6.18.51-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 429/583 usb: xhci: simplify handling of Structural Parameters 1 values
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Neronin <[email protected]>
[ Upstream commit df08973556851b29bd78e79db696d992ed1b43f0 ]
The 32-bit read-only HCSPARAMS1 register contains the following fields:
Bits 7:0 - Number of Device Slots (MaxSlots) Bits 18:8 - Number of Interrupters (MaxIntrs) Bits 23:19 - Reserved Bits 31:24 - Number of Ports (MaxPorts)
Since the register value is constant for the lifetime of the controller, it is cached in 'xhci->hcs_params1'. However, platform drivers may override the number of interrupters through a separate variable, 'xhci->max_interrupters', leaving only the maximum slots and ports values still derived from the cached register.
To simplify the code and improve readability, replace 'xhci->hcs_params1' with two dedicated 'u8' fields: 'xhci->max_slots' and 'xhci->max_ports'. These values are initialized once and used directly instead of calling 'HCS_MAX_SLOTS()' and 'HCS_MAX_PORTS()' macros.
This change reduces code clutter without increasing memory usage.
[ Sasha: context conflict in xhci_gen_setup() only. 6.18.y carries
fe7892d46921 ("usb: xhci-pci: Limit VIA VL805 DMA addressing to 36 bits") out of order -- it landed upstream after this commit -- so the "xhci->dma_mask_bits = 64;" line sits exactly where the new max_slots and max_ports assignments are inserted. Kept both, with dma_mask_bits first, which matches the resulting upstream ordering. No functional change; every other hunk is verbatim. ]
Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")
--- drivers/usb/host/xhci-debugfs.c | 15 ++++----------- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci-mem.c | 31 +++++++++++++------------------ drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci-ring.c | 6 ++---- drivers/usb/host/xhci.c | 21 ++++++++++----------- drivers/usb/host/xhci.h | 3 ++- 7 files changed, 33 insertions(+), 47 deletions(-)
drivers/usb/host/xhci-debugfs.c +4 -11
@@ -613,20 +613,16 @@ void xhci_debugfs_remove_slot(struct xhcstatic void xhci_debugfs_create_ports (struct xhci_hcd *xhci,struct dentry *parent) { -unsigned int num_ports;char port_name[8 ];struct xhci_port *port;struct dentry *dir; - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); - parent =debugfs_create_dir ("ports" , parent); -while (num_ports--) { -scnprintf (port_name,sizeof (port_name),"port%02d" , - num_ports +1 ); +for (int i =0 ; i < xhci->max_ports; i++) { +scnprintf (port_name,sizeof (port_name),"port%02d" , i +1 ); dir =debugfs_create_dir (port_name, parent); - port = &xhci->hw_ports[num_ports]; + port = &xhci->hw_ports[i];debugfs_create_file ("portsc" ,0644 , dir, port, &port_fops); } }
@@ -634,7 +630,6 @@ static void xhci_debugfs_create_ports(ststatic int xhci_port_bw_show (struct xhci_hcd *xhci,u8 dev_speed,struct seq_file *s) { -unsigned int num_ports;unsigned int i;int ret;struct xhci_container_ctx *ctx;
@@ -645,8 +640,6 @@ static int xhci_port_bw_show(struct xhciif (ret <0 )return ret; - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); - ctx =xhci_alloc_port_bw_ctx (xhci,0 );if (!ctx) {pm_runtime_put_sync (dev);
@@ -661,7 +654,7 @@ static int xhci_port_bw_show(struct xhci/* print all roothub ports available bandwidth * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved */ -for (i =1 ; i < num_ports+1 ; i++) +for (i =1 ; i <= xhci->max_ports; i++)seq_printf (s,"port[%d] available bw: %d%%.\n" , i, ctx->bytes[i]); err_out:
drivers/usb/host/xhci-hub.c +1 -1
@@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct x/* Disable all Device Slots */ xhci_dbg (xhci,"Disable all slots\n" );spin_unlock_irqrestore (&xhci->lock, *flags); -for (i =1 ; i <=HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =1 ; i <= xhci->max_slots; i++) {if (!xhci->devs[i])continue ;
drivers/usb/host/xhci-mem.c +13 -18
@@ -951,7 +951,7 @@ static void xhci_free_virt_devices_depth/* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) {/* are any devices using this tt_info? */ -for (i =1 ; i <HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =1 ; i < xhci->max_slots; i++) { vdev = xhci->devs[i];if (vdev && (vdev->tt_info == tt_info)) xhci_free_virt_devices_depth_first(
@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_void xhci_mem_cleanup (struct xhci_hcd *xhci) {struct device *dev =xhci_to_hcd (xhci)->self.sysdev; -int i, j, num_ports; +int i, j;cancel_delayed_work_sync (&xhci->cmd_timer);
@@ -1918,8 +1918,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xxhci_dbg_trace (xhci, trace_xhci_dbg_init,"Freed command ring" );xhci_cleanup_command_queue (xhci); - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); -for (i =0 ; i < num_ports && xhci->rh_bw; i++) { +for (i =0 ; i < xhci->max_ports && xhci->rh_bw; i++) {struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;for (j =0 ; j <XHCI_MAX_INTERVAL ; j++) {struct list_head *ep = &bwt->interval_bw[j].endpoints;
@@ -1928,7 +1927,7 @@ void xhci_mem_cleanup(struct xhci_hcd *x } } -for (i =HCS_MAX_SLOTS (xhci->hcs_params1); i >0 ; i--) +for (i = xhci->max_slots; i >0 ; i--)xhci_free_virt_devices_depth_first (xhci, i);dma_pool_destroy (xhci->segment_pool);
@@ -1964,7 +1963,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xif (!xhci->rh_bw) goto no_bw; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) {struct xhci_tt_bw_info *tt, *n;list_for_each_entry_safe (tt, n, &xhci->rh_bw[i].tts, tt_list) {list_del (&tt->tt_list);
@@ -2165,7 +2164,7 @@ static void xhci_create_rhub_port_array(if (!rhub->ports)return ; -for (i =0 ; i <HCS_MAX_PORTS (xhci->hcs_params1); i++) { +for (i =0 ; i < xhci->max_ports; i++) {if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum ==DUPLICATE_ENTRY )continue ;
@@ -2188,19 +2187,17 @@ static int xhci_setup_port_arrays(struct {void __iomem *base;u32 offset; -unsigned int num_ports;int i, j;int cap_count =0 ;u32 cap_start;struct device *dev =xhci_to_hcd (xhci)->self.sysdev; - num_ports =HCS_MAX_PORTS (xhci->hcs_params1); - xhci->hw_ports =kcalloc_node (num_ports,sizeof (*xhci->hw_ports), - flags,dev_to_node (dev)); + xhci->hw_ports =kcalloc_node (xhci->max_ports,sizeof (*xhci->hw_ports), + flags,dev_to_node (dev));if (!xhci->hw_ports)return -ENOMEM ; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) { xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i]; xhci->hw_ports[i].hw_portnum = i;
@@ -2208,11 +2205,10 @@ static int xhci_setup_port_arrays(structinit_completion (&xhci->hw_ports[i].u3exit_done); } - xhci->rh_bw =kcalloc_node (num_ports,sizeof (*xhci->rh_bw), flags, -dev_to_node (dev)); + xhci->rh_bw =kcalloc_node (xhci->max_ports,sizeof (*xhci->rh_bw), flags,dev_to_node (dev));if (!xhci->rh_bw)return -ENOMEM ; -for (i =0 ; i < num_ports; i++) { +for (i =0 ; i < xhci->max_ports; i++) {struct xhci_interval_bw_table *bw_table;INIT_LIST_HEAD (&xhci->rh_bw[i].tts);
@@ -2244,9 +2240,8 @@ static int xhci_setup_port_arrays(struct offset = cap_start;while (offset) { -xhci_add_in_port (xhci, num_ports, base + offset, cap_count); -if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == - num_ports) +xhci_add_in_port (xhci, xhci->max_ports, base + offset, cap_count); +if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)break ; offset =xhci_find_next_ext_cap (base, offset,XHCI_EXT_CAPS_PROTOCOL );
drivers/usb/host/xhci-pci.c +1 -1
@@ -897,7 +897,7 @@ static int xhci_pci_poweroff_late(structif (!(xhci->quirks &XHCI_RESET_TO_DEFAULT ))return 0 ; -for (i =0 ; i <HCS_MAX_PORTS (xhci->hcs_params1); i++) { +for (i =0 ; i < xhci->max_ports; i++) { port = &xhci->hw_ports[i]; portsc =xhci_portsc_readl (port);
drivers/usb/host/xhci-ring.c +2 -4
@@ -1409,7 +1409,7 @@ void xhci_hc_died(struct xhci_hcd *xhci)xhci_cleanup_command_queue (xhci);/* return any pending urbs, remove may be waiting for them */ -for (i =0 ; i <=HCS_MAX_SLOTS (xhci->hcs_params1); i++) { +for (i =0 ; i <= xhci->max_slots; i++) {if (!xhci->devs[i])continue ;for (j =0 ; j <31 ; j++)
@@ -2010,7 +2010,6 @@ static void handle_port_status(struct xhstruct usb_hcd *hcd;u32 port_id;u32 portsc, cmd_reg; -int max_ports;unsigned int hcd_portnum;struct xhci_bus_state *bus_state;bool bogus_port_status = false;
@@ -2022,9 +2021,8 @@ static void handle_port_status(struct xh "WARN: xHC returned failed port status event\n"); port_id =GET_PORT_ID (le32_to_cpu (event->generic.field[0 ])); - max_ports =HCS_MAX_PORTS (xhci->hcs_params1); -if ((port_id <=0 ) || (port_id > max_ports)) { +if ((port_id <=0 ) || (port_id > xhci->max_ports)) {xhci_warn (xhci,"Port change event with invalid port ID %d\n" , port_id);return ;
drivers/usb/host/xhci.c +10 -11
@@ -291,8 +291,7 @@ static void xhci_zero_64b_regs(struct xhif (upper_32_bits (val))xhci_write_64 (xhci,0 , &xhci->op_regs->cmd_ring); - intrs =min_t (u32,HCS_MAX_INTRS (xhci->hcs_params1), -ARRAY_SIZE (xhci->run_regs->ir_set)); + intrs =min_t (u32, xhci->max_interrupters,ARRAY_SIZE (xhci->run_regs->ir_set));for (i =0 ; i < intrs; i++) {struct xhci_intr_reg __iomem *ir;
@@ -484,15 +483,13 @@ static void xhci_hcd_page_size(struct xhstatic void xhci_enable_max_dev_slots (struct xhci_hcd *xhci) {u32 config_reg; -u32 max_slots; - max_slots =HCS_MAX_SLOTS (xhci->hcs_params1);xhci_dbg_trace (xhci, trace_xhci_dbg_init,"xHC can handle at most %d device slots" , - max_slots); + xhci->max_slots); config_reg =readl (&xhci->op_regs->config_reg); config_reg &= ~HCS_SLOTS_MASK ; - config_reg |= max_slots; + config_reg |= xhci->max_slots;xhci_dbg_trace (xhci, trace_xhci_dbg_init,"Setting Max device slots reg = 0x%x" , config_reg);
@@ -4253,7 +4250,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd,xhci_err (xhci,"Error while assigning device slot ID: %s\n" ,xhci_trb_comp_code_string (command->status));xhci_err (xhci,"Max number of devices this xHCI host supports is %u.\n" , -HCS_MAX_SLOTS (xhci->hcs_params1)); + xhci->max_slots);xhci_free_command (xhci, command);return 0 ; }
@@ -5437,6 +5434,7 @@ int xhci_gen_setup(struct usb_hcd *hcd,*/ struct device *dev = hcd->self.sysdev;int retval; +u32 hcs_params1;/* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0 ;
@@ -5462,7 +5460,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci->run_regs = hcd->regs + (readl (&xhci->cap_regs->run_regs_off) &RTSOFF_MASK );/* Cache read-only capability registers */ - xhci->hcs_params1 =readl (&xhci->cap_regs->hcs_params1); + hcs_params1 =readl (&xhci->cap_regs->hcs_params1); xhci->hcs_params2 =readl (&xhci->cap_regs->hcs_params2); xhci->hcs_params3 =readl (&xhci->cap_regs->hcs_params3); xhci->hci_version =HC_VERSION (readl (&xhci->cap_regs->hc_capbase));
@@ -5471,10 +5469,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci->hcc_params2 =readl (&xhci->cap_regs->hcc_params2); xhci->dma_mask_bits =64 ; + xhci->max_slots =HCS_MAX_SLOTS (hcs_params1); + xhci->max_ports =HCS_MAX_PORTS (hcs_params1);/* xhci-plat or xhci-pci might have set max_interrupters already */ -if ((!xhci->max_interrupters) || - xhci->max_interrupters >HCS_MAX_INTRS (xhci->hcs_params1)) - xhci->max_interrupters =HCS_MAX_INTRS (xhci->hcs_params1); +if ((!xhci->max_interrupters) || xhci->max_interrupters >HCS_MAX_INTRS (hcs_params1)) + xhci->max_interrupters =HCS_MAX_INTRS (hcs_params1); xhci->quirks |= quirks;
drivers/usb/host/xhci.h +2 -1
@@ -1501,7 +1501,6 @@ struct xhci_hcd {struct xhci_doorbell_array __iomem *dba;/* Cached register copies of read-only HC data */ -__u32 hcs_params1;__u32 hcs_params2;__u32 hcs_params3;__u32 hcc_params;
@@ -1512,6 +1511,8 @@ struct xhci_hcd {/* packed release number */ u16 hci_version;u16 max_interrupters; +u8 max_slots; +u8 max_ports;/* imod_interval in ns (I * 250ns) */ u32 imod_interval;u32 page_size;
Greg Kroah-Hartman
maintainerPATCH 430/583 usb: xhci: bail out of setup if the controller is inaccessible
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 78203d5b54a40f0e36196ebf31c9c7a380fc8811 ]
xhci_gen_setup() locates the operational registers using the capability length read from the very first register:
xhci->op_regs = hcd->regs + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
If the controller is dead or has dropped off the bus, that read returns ~0, HC_LENGTH() truncates it to 0xff, and op_regs ends up 0xff bytes past the page-aligned MMIO base, i.e. unaligned. The first access through it, xhci_halt() -> xhci_handshake() reading op_regs->status, is then an unaligned readl() on device memory. arm64 faults on unaligned device accesses, so instead of xhci_handshake() catching the all-ones value and returning -ENODEV, setup oopses:
xhci-pci-renesas 0005:08:00.0: Unable to change power state from D3cold to D0, device inaccessible xhci-pci-renesas 0005:08:00.0: xHCI Host Controller
xhci-pci-renesas 0005:08:00.0: new USB bus registered, assigned bus number 1
Unable to handle kernel paging request at virtual address ffff80030a770103
ESR = 0x0000000096000021
FSC = 0x21: alignment fault
Internal error: Oops: 0000000096000021 [#1] SMP
pc : xhci_halt [xhci_hcd]
Call trace:
xhci_halt
xhci_gen_setup
xhci_pci_setup
usb_add_hcd
usb_hcd_pci_probe
xhci_pci_common_probe
xhci_pci_renesas_probeThis was hit with a Renesas uPD720201 that failed to power up ("Unable to change power state from D3cold to D0, device inaccessible") yet still reached the HCD probe path.
Read the capability register once, and if it reads back the all-ones value (as xhci_handshake() and xhci_reset() already test for), abort setup with -ENODEV before op_regs is derived from it. Reading it once also avoids re-reading a register that may change under a concurrent hot-removal.
--- drivers/usb/host/xhci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/usb/host/xhci.c +8 -3
@@ -5435,6 +5435,7 @@ int xhci_gen_setup(struct usb_hcd *hcd,struct device *dev = hcd->self.sysdev;int retval;u32 hcs_params1; +u32 hc_capbase;/* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0 ;
@@ -5455,15 +5456,19 @@ int xhci_gen_setup(struct usb_hcd *hcd,mutex_init (&xhci->mutex); xhci->main_hcd = hcd; xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + -HC_LENGTH (readl (&xhci->cap_regs->hc_capbase)); + hc_capbase =readl (&xhci->cap_regs->hc_capbase); +if (hc_capbase ==U32_MAX ) { +xhci_warn (xhci,"Host controller not accessible, removed?\n" ); +return -ENODEV ; + } + xhci->op_regs = hcd->regs +HC_LENGTH (hc_capbase); xhci->run_regs = hcd->regs + (readl (&xhci->cap_regs->run_regs_off) &RTSOFF_MASK );/* Cache read-only capability registers */ hcs_params1 =readl (&xhci->cap_regs->hcs_params1); xhci->hcs_params2 =readl (&xhci->cap_regs->hcs_params2); xhci->hcs_params3 =readl (&xhci->cap_regs->hcs_params3); - xhci->hci_version =HC_VERSION (readl (&xhci->cap_regs->hc_capbase)); + xhci->hci_version =HC_VERSION (hc_capbase); xhci->hcc_params =readl (&xhci->cap_regs->hcc_params);if (xhci->hci_version >0x100 ) xhci->hcc_params2 =readl (&xhci->cap_regs->hcc_params2);
Greg Kroah-Hartman
maintainerPATCH 431/583 fuse: publish io-uring queues with release semantics
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit 42df916e5a5f8fb4b60c8cefb54318d1ec02c580 ]
fuse_uring_create_queue() initializes a fuse_ring_queue and then publishes the pointer into ring->queues[qid] with WRITE_ONCE() under the fch->lock. There are several readers that may concurrently be fetching that pointer locklessly and then deferencing it.
WRITE_ONCE() doesn't ensure ordering of the queue's field initialization before the ring->queues[qid] pointer assignment. The queue must be published with smp_store_release() so the field initialization is guaranteed to happen before.
Readers in paths where the read may happen concurrently with the store need to use READ_ONCE() because any race involving a plain access is undefined.
--- fs/fuse/dev_uring.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
fs/fuse/dev_uring.c +11 -9
@@ -194,7 +194,7 @@ void fuse_uring_destruct(struct fuse_conreturn ;for (qid =0 ; qid < ring->nr_queues; qid++) { -struct fuse_ring_queue *queue = ring->queues[qid]; +struct fuse_ring_queue *queue =READ_ONCE (ring->queues[qid]);struct fuse_ring_ent *ent, *next;if (!queue)
@@ -213,7 +213,7 @@ void fuse_uring_destruct(struct fuse_conkfree (queue->fpq.processing);kfree (queue); - ring->queues[qid] =NULL ; +WRITE_ONCE (ring->queues[qid],NULL ); }kfree (ring->queues);
@@ -307,9 +307,11 @@ static struct fuse_ring_queue *fuse_urin }/* -* write_once and lock as the caller mostly doesn't take the lock at all +* fc->lock serializes concurrent creators for this qid. +* smp_store_release() are for the lockless readers who must see a +* fully initialized queue after &ring->queues[qid] is set */ -WRITE_ONCE (ring->queues[qid], queue); +smp_store_release (&ring->queues[qid], queue);spin_unlock (&fc->lock);return queue;
@@ -406,7 +408,7 @@ static void fuse_uring_log_ent_state(strstruct fuse_ring_ent *ent;for (qid =0 ; qid < ring->nr_queues; qid++) { -struct fuse_ring_queue *queue = ring->queues[qid]; +struct fuse_ring_queue *queue =READ_ONCE (ring->queues[qid]);if (!queue)continue ;
@@ -911,7 +913,7 @@ static int fuse_uring_commit_fetch(strucif (qid >= ring->nr_queues)return -EINVAL ; - queue = ring->queues[qid]; + queue =READ_ONCE (ring->queues[qid]);if (!queue)return err; fpq = &queue->fpq;
@@ -980,7 +982,7 @@ static bool is_ring_ready(struct fuse_riif (current_qid == qid)continue ; - queue = ring->queues[qid]; + queue =READ_ONCE (ring->queues[qid]);if (!queue) { ready = false;break ;
@@ -1134,7 +1136,7 @@ static int fuse_uring_register(struct ioreturn -EINVAL ; } - queue = ring->queues[qid]; + queue =READ_ONCE (ring->queues[qid]);if (!queue) { queue =fuse_uring_create_queue (ring, qid);if (!queue)
@@ -1286,7 +1288,7 @@ static struct fuse_ring_queue *fuse_urin ring->nr_queues)) qid =0 ; - queue = ring->queues[qid]; + queue =READ_ONCE (ring->queues[qid]);WARN_ONCE (!queue,"Missing queue for qid %d\n" , qid);return queue;
Greg Kroah-Hartman
maintainerPATCH 432/583 fuse: split off fuse_args and related definitions into a separate header
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
[ Upstream commit 229f9b9b66ab5be9e015422cf30b97740cfbdc8d ]
This is going to be used by both layers (transport and filesystem)
Stable-dep-of: ed9c881f3b49 ("fuse: fix race between interrupt and resend")
--- fs/fuse/args.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 53 -------------------------------------------- 2 files changed, 66 insertions(+), 52 deletions(-) create mode 100644 fs/fuse/args.h
fs/fuse/args.h new file +65 -0
@@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FS_FUSE_ARGS_H +#define _FS_FUSE_ARGS_H + +#include <linux/types.h> + +struct fuse_mount ; + +/** One input argument of a request */ +struct fuse_in_arg { +unsigned size; +const void *value; +}; + +/** One output argument of a request */ +struct fuse_arg { +unsigned size; +void *value; +}; + +struct fuse_args { +u64 nodeid; +u32 opcode; +u32 uid; +u32 gid; +u32 pid; +u8 in_numargs; +u8 out_numargs; +u8 ext_idx; +bool force:1 ; +bool noreply:1 ; +bool nocreds:1 ; +bool in_pages:1 ; +bool out_pages:1 ; +bool user_pages:1 ; +bool out_argvar:1 ; +bool page_zeroing:1 ; +bool page_replace:1 ; +bool may_block:1 ; +bool is_ext:1 ; +bool is_pinned:1 ; +bool invalidate_vmap:1 ; +bool abort_on_kill:1 ; +struct fuse_in_arg in_args[4 ]; +struct fuse_arg out_args[2 ]; +void (*end)(struct fuse_mount *fm,struct fuse_args *args,int error); +/* Used for kvec iter backed by vmalloc address */ +void *vmap_base; +}; + +/** FUSE folio descriptor */ +struct fuse_folio_desc { +unsigned int length; +unsigned int offset; +}; + +struct fuse_args_pages { +struct fuse_args args; +struct folio **folios; +struct fuse_folio_desc *descs; +unsigned int num_folios; +}; + +#endif /* _FS_FUSE_ARGS_H */
fs/fuse/fuse_i.h +1 -52
65 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 433/583 fuse: remove fm arg of args->end callback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
[ Upstream commit a697d95fcdbb3bbe25cdc29db5542ddcebb831c1 ]
Only used by FUSE_INIT and CUSE_INIT, these can store the relevant pointer in their structs derived from fuse_args.
Stable-dep-of: ed9c881f3b49 ("fuse: fix race between interrupt and resend")
--- fs/fuse/args.h | 2 +- fs/fuse/cuse.c | 7 ++++--- fs/fuse/dev.c | 7 +++---- fs/fuse/file.c | 22 +++++++++------------- fs/fuse/inode.c | 10 ++++++---- 5 files changed, 23 insertions(+), 25 deletions(-)
fs/fuse/args.h +1 -1
@@ -44,7 +44,7 @@ struct fuse_args { bool abort_on_kill:1;struct fuse_in_arg in_args[4 ];struct fuse_arg out_args[2 ]; -void (*end)(struct fuse_mount *fm,struct fuse_args *args,int error); +void (*end)(struct fuse_args *args,int error);/* Used for kvec iter backed by vmalloc address */ void *vmap_base; };
fs/fuse/cuse.c +4 -3
@@ -306,6 +306,7 @@ struct cuse_init_args {struct cuse_init_out out;struct folio *folio;struct fuse_folio_desc desc; +struct fuse_conn *fc; };/**
@@ -319,11 +320,10 @@ struct cuse_init_args {* required data structures for it. Please read the comment at the * top of this file for high level overview. */ -static void cuse_process_init_reply (struct fuse_mount *fm, -struct fuse_args *args,int error) +static void cuse_process_init_reply (struct fuse_args *args,int error) { -struct fuse_conn *fc = fm->fc;struct cuse_init_args *ia =container_of (args,typeof (*ia), ap.args); +struct fuse_conn *fc = ia->fc;struct fuse_args_pages *ap = &ia->ap;struct cuse_conn *cc =fc_to_cc (fc), *pos;struct cuse_init_out *arg = &ia->out;
@@ -468,6 +468,7 @@ static int cuse_send_init(struct cuse_co ap->descs = &ia->desc; ia->folio = folio; ia->desc.length = ap->args.out_args[1 ].size; + ia->fc = &cc->fc; ap->args.end = cuse_process_init_reply; rc =fuse_simple_background (fm, &ap->args,GFP_KERNEL );
fs/fuse/dev.c +3 -4
@@ -512,7 +512,7 @@ void fuse_request_end(struct fuse_req *r }if (test_bit (FR_ASYNC , &req->flags)) - req->args->end (fm, req->args, req->out.h.error); + req->args->end (req->args, req->out.h.error); put_request:fuse_put_request (req); }
@@ -1860,8 +1860,7 @@ struct fuse_retrieve_args {struct fuse_notify_retrieve_in inarg; }; -static void fuse_retrieve_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_retrieve_end (struct fuse_args *args,int error) {struct fuse_retrieve_args *ra =container_of (args,typeof (*ra), ap.args);
@@ -1956,7 +1955,7 @@ static int fuse_retrieve(struct fuse_mou err =fuse_simple_notify_reply (fm, args, outarg->notify_unique);if (err) -fuse_retrieve_end (fm, args, err); +fuse_retrieve_end (args, err);return err; }
fs/fuse/file.c +9 -13
@@ -91,8 +91,7 @@ static struct fuse_file *fuse_file_get(sreturn ff; } -static void fuse_release_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_release_end (struct fuse_args *args,int error) {struct fuse_release_args *ra =container_of (args,typeof (*ra), args);
@@ -112,15 +111,15 @@ static void fuse_file_put(struct fuse_fiif (!args) {/* Do nothing when server does not implement 'opendir' */ }else if (args->opcode ==FUSE_RELEASE && ff->fm->fc->no_open) { -fuse_release_end (ff->fm, args,0 ); +fuse_release_end (args,0 ); }else if (sync) {fuse_simple_request (ff->fm, args); -fuse_release_end (ff->fm, args,0 ); +fuse_release_end (args,0 ); }else { args->end = fuse_release_end;if (fuse_simple_background (ff->fm, args,GFP_KERNEL | __GFP_NOFAIL)) -fuse_release_end (ff->fm, args, -ENOTCONN ); +fuse_release_end (args, -ENOTCONN ); }kfree (ff); }
@@ -709,8 +708,7 @@ static void fuse_io_free(struct fuse_io_kfree (ia); } -static void fuse_aio_complete_req (struct fuse_mount *fm,struct fuse_args *args, -int err) +static void fuse_aio_complete_req (struct fuse_args *args,int err) {struct fuse_io_args *ia =container_of (args,typeof (*ia), ap.args);struct fuse_io_priv *io = ia->io;
@@ -758,7 +756,7 @@ static ssize_t fuse_async_req_send(struc ia->ap.args.may_block = io->should_dirty; err =fuse_simple_background (fm, &ia->ap.args,GFP_KERNEL );if (err) -fuse_aio_complete_req (fm, &ia->ap.args, err); +fuse_aio_complete_req (&ia->ap.args, err);return num_bytes; }
@@ -881,8 +879,7 @@ static int fuse_iomap_read_folio_range(creturn fuse_do_readfolio (file, folio, off, len); } -static void fuse_readpages_end (struct fuse_mount *fm,struct fuse_args *args, -int err) +static void fuse_readpages_end (struct fuse_args *args,int err) {int i;struct fuse_io_args *ia =container_of (args,typeof (*ia), ap.args);
@@ -947,7 +944,7 @@ static void fuse_send_readpages(struct f res =fuse_simple_request (fm, &ap->args); err = res <0 ? res :0 ; } -fuse_readpages_end (fm, &ap->args, err); +fuse_readpages_end (&ap->args, err); }static void fuse_readahead (struct readahead_control *rac)
@@ -1950,8 +1947,7 @@ __acquires(fi->lock) } } -static void fuse_writepage_end (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void fuse_writepage_end (struct fuse_args *args,int error) {struct fuse_writepage_args *wpa =container_of (args,typeof (*wpa), ia.ap.args);
fs/fuse/inode.c +6 -4
@@ -1311,13 +1311,14 @@ struct fuse_init_args {struct fuse_args args;struct fuse_init_in in;struct fuse_init_out out; +struct fuse_mount *fm; }; -static void process_init_reply (struct fuse_mount *fm,struct fuse_args *args, -int error) +static void process_init_reply (struct fuse_args *args,int error) { -struct fuse_conn *fc = fm->fc;struct fuse_init_args *ia =container_of (args,typeof (*ia), args); +struct fuse_mount *fm = ia->fm; +struct fuse_conn *fc = fm->fc;struct fuse_init_out *arg = &ia->out;bool ok = true;
@@ -1486,6 +1487,7 @@ static struct fuse_init_args *fuse_new_i ia =kzalloc (sizeof (*ia),GFP_KERNEL | __GFP_NOFAIL); + ia->fm = fm; ia->in.major =FUSE_KERNEL_VERSION ; ia->in.minor =FUSE_KERNEL_MINOR_VERSION ; ia->in.max_readahead = fm->sb->s_bdi->ra_pages *PAGE_SIZE ;
@@ -1559,7 +1561,7 @@ int fuse_send_init(struct fuse_mount *fmif (!err)return 0 ; } -process_init_reply (fm, &ia->args, err); +process_init_reply (&ia->args, err);if (fm->fc->conn_error)return -ENOTCONN ;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 434/583 fuse: fix race between interrupt and resend
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
[ Upstream commit ed9c881f3b498383f73c42712b359419da42a7b0 ]
After commit f8fce75fedf7 ("fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req") the WARN_ON(!list_empty(&req->intr_entry)) in fuse_request_free() still triggers due to the following race:
In request_wait_answer()
if (test_bit(FR_SENT, &req->flags)) -> returns true
In fuse_chan_resend()
clear_bit(FR_SENT, &req->flags)
In request_wait_answer()
queue_interrupt(req)
Fix by:
- move clearing FR_SENT inside fpq->lock
- move setting FR_PENDING inside fiq->lock
- recheck FR_SENT after acquiring fiq->lock in fuse_dev_queue_interrupt()
--- fs/fuse/dev.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-)
fs/fuse/dev.c +14 -13
@@ -353,7 +353,8 @@ void fuse_dev_queue_forget(struct fuse_ivoid fuse_dev_queue_interrupt (struct fuse_iqueue *fiq,struct fuse_req *req) {spin_lock (&fiq->lock); -if (list_empty (&req->intr_entry)) { +/* Repeat FR_SENT test after obtaining the lock to prevent race with fuse_resend() */ +if (list_empty (&req->intr_entry) &&test_bit (FR_SENT , &req->flags)) {list_add_tail (&req->intr_entry, &fiq->interrupts);/* * Pairs with smp_mb() implied by test_and_set_bit()
@@ -2011,7 +2012,7 @@ static int fuse_notify_retrieve(struct fstatic void fuse_resend (struct fuse_conn *fc) {struct fuse_dev *fud; -struct fuse_req *req, *next; +struct fuse_req *req;struct fuse_iqueue *fiq = &fc->iq;LIST_HEAD (to_queue);unsigned int i;
@@ -2026,24 +2027,20 @@ static void fuse_resend(struct fuse_connstruct fuse_pqueue *fpq = &fud->pq;spin_lock (&fpq->lock); -for (i =0 ; i <FUSE_PQ_HASH_SIZE ; i++) -list_splice_tail_init (&fpq->processing[i], &to_queue); +for (i =0 ; i <FUSE_PQ_HASH_SIZE ; i++) { +struct list_head *this_queue = &fpq->processing[i]; + +list_for_each_entry (req, this_queue, list) +clear_bit (FR_SENT , &req->flags); +list_splice_tail_init (this_queue, &to_queue); + }spin_unlock (&fpq->lock); }spin_unlock (&fc->lock); -list_for_each_entry_safe (req, next, &to_queue, list) { -set_bit (FR_PENDING , &req->flags); -clear_bit (FR_SENT , &req->flags); -/* mark the request as resend request */ - req->in.h.unique |=FUSE_UNIQUE_RESEND ; - } -spin_lock (&fiq->lock);if (!fiq->connected) {spin_unlock (&fiq->lock); -list_for_each_entry (req, &to_queue, list) -clear_bit (FR_PENDING , &req->flags);fuse_dev_end_requests (&to_queue);return ; }
@@ -2052,6 +2049,10 @@ static void fuse_resend(struct fuse_conn* intr_entry on fiq->interrupts after the request is re-queued. */ list_for_each_entry (req, &to_queue, list) { +set_bit (FR_PENDING , &req->flags); +/* mark the request as resend request */ + req->in.h.unique |=FUSE_UNIQUE_RESEND ; +if (test_bit (FR_INTERRUPTED , &req->flags))list_del_init (&req->intr_entry); }
Greg Kroah-Hartman
maintainerPATCH 435/583 net/packet: defer vmalloc TX_RING free until skbs finish
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit 992cc9f94ca924089a506ba9b327caa9af797529 ]
AF_PACKET TX_RING skbs keep a raw pointer to their ring frame. The skb page references preserve page-backed ring blocks after pg_vec is freed, but they do not preserve a vmalloc mapping.
tpacket_destruct_skb() currently drops the pending reference before writing the timestamp and TP_STATUS_AVAILABLE to the frame. Move the decrement after those stores. The smp_wmb() in __packet_set_status() orders the frame stores before the decrement.
Also recheck pending TX frames under pg_vec_lock before non-closing ring replacement, so a racing send cannot add a pending skb between the initial check and the ring swap.
Ring allocation can produce a mixture of page-backed and vmalloc-backed blocks. Allocate deferred-work storage during TX ring setup when the first vmalloc-backed block is encountered, and keep its pointer in the pg_vec allocation header. If allocation fails, return -ENOMEM from ring setup. On socket close, a non-NULL pointer identifies a vmalloc-backed vector without a scan. If TX skbs remain, defer the whole vector to system_long_wq.
After pg_vec is detached, a late destructor can skip the pending decrement. Use socket write-memory accounting as the deferred lifetime gate instead: an skb remains charged through its final sock_wfree(), after all ring-frame accesses. The delayed work retains a socket reference and reschedules itself until no TX skbs remain.
Move pending_refcnt release to packet_sock_destruct() so late skb destructors and deferred cleanup can safely use it after packet_release(). Page-backed teardown remains synchronous, and no lock is added to the TX completion hot path.
--- net/packet/af_packet.c | 96 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 9 deletions(-)
net/packet/af_packet.c +87 -9
@@ -87,6 +87,7 @@#include <linux/errqueue.h> #include <linux/net_tstamp.h> #include <linux/percpu.h> +#include <linux/workqueue.h> #ifdef CONFIG_INET #include <net/inet_common.h> #endif
@@ -1339,6 +1340,8 @@ static void packet_sock_destruct(structWARN_ON (atomic_read (&sk->sk_rmem_alloc));WARN_ON (refcount_read (&sk->sk_wmem_alloc)); +packet_free_pending (pkt_sk (sk)); +if (!sock_flag (sk,SOCK_DEAD )) {pr_err ("Attempt to release alive packet socket: %p\n" , sk);return ;
@@ -2533,11 +2536,11 @@ static void tpacket_destruct_skb(struct__u32 ts; ph =skb_zcopy_get_nouarg (skb); -packet_dec_pending (&po->tx_ring); ts =__packet_set_timestamp (po, ph, skb);__packet_set_status (po, ph,TP_STATUS_AVAILABLE | ts); +packet_dec_pending (&po->tx_ring);complete (&po->skb_completion); }
@@ -3206,7 +3209,6 @@ static int packet_release(struct socket/* Purge queues */ skb_queue_purge (&sk->sk_receive_queue); -packet_free_pending (po);sock_put (sk);return 0 ;
@@ -4370,11 +4372,26 @@ static const struct vm_operations_struct .close = packet_mm_close, }; +struct packet_pg_vec { +struct packet_pg_vec_free *deferred; +unsigned int order; +unsigned int len; + struct pgv pg_vec[] __counted_by(len); +}; + +struct packet_pg_vec_free { +struct delayed_work work; +struct sock *sk; +struct packet_pg_vec *vec; +}; +static void free_pg_vec (struct pgv *pg_vec,unsigned int order,unsigned int len) { +struct packet_pg_vec *vec;int i; + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]);for (i =0 ; i < len; i++) {if (likely (pg_vec[i].buffer)) {if (is_vmalloc_addr (pg_vec[i].buffer))
@@ -4385,7 +4402,46 @@ static void free_pg_vec(struct pgv *pg_v pg_vec[i].buffer =NULL ; } } -kfree (pg_vec); +kfree (vec->deferred); +kfree (vec); +} + +static void packet_free_pg_vec_work (struct work_struct *work) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; +struct sock *sk; + + deferred =container_of_const (to_delayed_work (work), + struct packet_pg_vec_free, work); + vec = deferred->vec; + sk = deferred->sk; +if (sk_wmem_alloc_get (sk)) { +queue_delayed_work (system_long_wq, &deferred->work,1 ); +return ; + } + +free_pg_vec (vec->pg_vec, vec->order, vec->len); +sock_put (sk); +} + +static void packet_free_tx_ring (struct sock *sk,struct pgv *pg_vec, +unsigned int order,unsigned int len) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; + + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]); + deferred = vec->deferred; +if (!deferred || !sk_wmem_alloc_get (sk)) { +free_pg_vec (pg_vec, order, len); +return ; + } + +/* A detached ring's pending count can miss late skb destructors. */ + deferred->sk = sk; +sock_hold (sk); +queue_delayed_work (system_long_wq, &deferred->work,0 ); }static char *alloc_one_pg_vec_page (unsigned long order)
@@ -4413,20 +4469,35 @@ static char *alloc_one_pg_vec_page(unsigreturn NULL ; } -static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order) +static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order,bool tx_ring) {unsigned int block_nr = req->tp_block_nr; +struct packet_pg_vec *vec;struct pgv *pg_vec;int i; - pg_vec =kcalloc (block_nr,sizeof (struct pgv ),GFP_KERNEL | __GFP_NOWARN); -if (unlikely (!pg_vec)) - goto out; + vec =kzalloc_flex (*vec, pg_vec, block_nr,GFP_KERNEL | __GFP_NOWARN); +if (unlikely (!vec)) +return NULL ; + vec->order = order; + vec->len = block_nr; + pg_vec = vec->pg_vec;for (i =0 ; i < block_nr; i++) { pg_vec[i].buffer =alloc_one_pg_vec_page (order);if (unlikely (!pg_vec[i].buffer)) goto out_free_pgvec; + +if (tx_ring && !vec->deferred && +is_vmalloc_addr (pg_vec[i].buffer)) { + vec->deferred =kzalloc_obj (*vec->deferred, +GFP_KERNEL | __GFP_NOWARN); +if (!vec->deferred) + goto out_free_pgvec; + vec->deferred->vec = vec; +INIT_DELAYED_WORK (&vec->deferred->work, + packet_free_pg_vec_work); + } } out:
@@ -4509,7 +4580,7 @@ static int packet_set_ring(struct sock * err = -ENOMEM ; order =get_order (req->tp_block_size); - pg_vec =alloc_pg_vec (req, order); + pg_vec =alloc_pg_vec (req, order, tx_ring);if (unlikely (!pg_vec)) goto out;switch (po->tp_version) {
@@ -4561,6 +4632,9 @@ static int packet_set_ring(struct sock * err = -EBUSY ;mutex_lock (&po->pg_vec_lock);if (closing ||atomic_long_read (&po->mapped) ==0 ) { +if (tx_ring && !closing &&packet_read_pending (rb)) + goto out_unlock; + err =0 ;spin_lock_bh (&rb_queue->lock);swap (rb->pg_vec, pg_vec);
@@ -4582,6 +4656,7 @@ static int packet_set_ring(struct sock *pr_err ("packet_mmap: vma is busy: %ld\n" ,atomic_long_read (&po->mapped)); } +out_unlock:mutex_unlock (&po->pg_vec_lock);spin_lock (&po->bind_lock);
@@ -4603,7 +4678,10 @@ static int packet_set_ring(struct sock * out_free_pg_vec:if (pg_vec) {bitmap_free (rx_owner_map); -free_pg_vec (pg_vec, order, req->tp_block_nr); +if (tx_ring && closing) +packet_free_tx_ring (sk, pg_vec, order, req->tp_block_nr); +else +free_pg_vec (pg_vec, order, req->tp_block_nr); } out:return err;
Greg Kroah-Hartman
maintainerPATCH 436/583 vlan: fix skb_under_panic and races when toggling HW VLAN offload
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 447cbe95ebb95392b5d8f6a01c0556826919ce23 ]
Toggling hardware VLAN TX offload (NETIF_F_HW_VLAN_CTAG_TX or NETIF_F_HW_VLAN_STAG_TX) on a lower device invokes vlan_transfer_features(), which dynamically changed vlandev->hard_header_len.
This causes two issues:
1. Lockless TX paths (e.g. packet_snd in af_packet.c, ip6_finish_output2) read dev->hard_header_len without holding RTNL lock. Mutating hard_header_len dynamically under RTNL creates a data race where upper layers reserve insufficient headroom based on a stale hard_header_len, resulting in skb_under_panic when vlan_dev_hard_header() is called.
2. In addition, vlan_transfer_features() updated hard_header_len without updating header_ops, causing a mismatch between allocated headroom and header creation.
Always setting dev->hard_header_len = real_dev->hard_header_len and dev->needed_headroom = real_dev->needed_headroom + VLAN_HLEN unconditionally ensures:
- dev->hard_header_len remains 100% static and immutable at real_dev->hard_header_len, eliminating all dynamic runtime updates and data races on hard_header_len.
- Upper layers allocating skbs via LL_RESERVED_SPACE() will always reserve sufficient headroom for software VLAN tag insertion (real_dev->hard_header_len + real_dev->needed_headroom + VLAN_HLEN).
- vlandev inherits real_dev->needed_tailroom so underlying trailer/padding/ICV requirements are honored.
- AF_PACKET SOCK_RAW network header offsets remain correctly aligned at real_dev->hard_header_len.
- vlan_header_ops is used unconditionally.
Note to stable teams: Make sure to backport these commits:
e16e960d55a4 ("ipvlan: inherit needed_headroom and needed_tailroom from phy_dev") cef51860becd ("macvlan: inherit needed_headroom and needed_tailroom from lowerdev")
--- net/8021q/vlan.c | 7 +++---- net/8021q/vlan_dev.c | 32 ++++---------------------------- 2 files changed, 7 insertions(+), 32 deletions(-)
net/8021q/vlan.c +3 -4
@@ -323,10 +323,9 @@ static void vlan_transfer_features(strucnetif_inherit_tso_max (vlandev, dev); -if (vlan_hw_offload_capable (dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; -else - vlandev->hard_header_len = dev->hard_header_len +VLAN_HLEN ; + vlandev->needed_headroom = dev->needed_headroom +VLAN_HLEN ; + vlandev->needed_tailroom = dev->needed_tailroom; + vlandev->hard_header_len = dev->hard_header_len;#if IS_ENABLED (CONFIG_FCOE ) vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
net/8021q/vlan_dev.c +4 -28
@@ -493,26 +493,6 @@ static const struct header_ops vlan_head .parse_protocol = vlan_parse_protocol, }; -static int vlan_passthru_hard_header (struct sk_buff *skb,struct net_device *dev, -unsigned short type, -const void *daddr,const void *saddr, -unsigned int len) -{ -struct vlan_dev_priv *vlan =vlan_dev_priv (dev); -struct net_device *real_dev = vlan->real_dev; - -if (saddr ==NULL ) - saddr = dev->dev_addr; - -return dev_hard_header (skb, real_dev, type, daddr, saddr, len); -} - -static const struct header_ops vlan_passthru_header_ops = { - .create = vlan_passthru_hard_header, - .parse = eth_header_parse, - .parse_protocol = vlan_parse_protocol, -}; -static const struct device_type vlan_type = { .name ="vlan" , };
@@ -571,14 +551,10 @@ static int vlan_dev_init(struct net_devi dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid;#endif - dev->needed_headroom = real_dev->needed_headroom; -if (vlan_hw_offload_capable (real_dev->features, vlan->vlan_proto)) { - dev->header_ops = &vlan_passthru_header_ops; - dev->hard_header_len = real_dev->hard_header_len; - }else { - dev->header_ops = &vlan_header_ops; - dev->hard_header_len = real_dev->hard_header_len +VLAN_HLEN ; - } + dev->needed_headroom = real_dev->needed_headroom +VLAN_HLEN ; + dev->needed_tailroom = real_dev->needed_tailroom; + dev->header_ops = &vlan_header_ops; + dev->hard_header_len = real_dev->hard_header_len; dev->netdev_ops = &vlan_netdev_ops;
Greg Kroah-Hartman
maintainerPATCH 437/583 crypto: iaa - fall back to software for multi-entry scatterlists
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Giovanni Cabiddu <[email protected]>
[ Upstream commit c7fdfd2bee1cf1448e5244da1a734e680f634b02 ]
IAA cannot process source or destination scatterlists with more than one entry directly. Instead of failing these requests, route them through a separate deflate acomp transform and keep the request alive in software.
The IAA driver has never handled multi-entry scatterlists, but the limitation was latent until commit e2c3b6b21c77 ("mm: zswap: use SG list decompression APIs from zsmalloc") made zswap pass the raw zsmalloc SG list directly to crypto drivers, so objects spanning multiple pages now reach IAA as multi-entry sources and would otherwise fail decompression.
Fallback to the generic DEFLATE implementation for scatterlists with more than one entry. After the multi-entry cases fall back early, simplify the DMA mapping path to a single scatterlist entry and fall back on mapping failure as well.
Add counters to track the number of requests processed by the software implementation on the compression direction.
Stable-dep-of: 94a259304771 ("crypto: iaa - unmap dst before software fallback on decompress")
--- drivers/crypto/intel/iaa/iaa_crypto_main.c | 111 +++++++++++++++------------- drivers/crypto/intel/iaa/iaa_crypto_stats.c | 9 ++ drivers/crypto/intel/iaa/iaa_crypto_stats.h | 2 3 files changed, 71 insertions(+), 51 deletions(-)
drivers/crypto/intel/iaa/iaa_crypto_main.c +60 -51
@@ -2,6 +2,7 @@/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */ #include <linux/init.h> +#include <linux/crypto.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h>
@@ -1001,6 +1002,19 @@ static int deflate_generic_decompress(streturn ret; } +static int deflate_generic_compress (struct acomp_req *req) +{ +ACOMP_FBREQ_ON_STACK (fbreq, req); +int ret; + + ret =crypto_acomp_compress (fbreq); + req->dlen = fbreq->dlen; + +update_total_sw_comp_calls (); + +return ret; +} +static int iaa_remap_for_verify (struct device *dev,struct iaa_wq *iaa_wq,struct acomp_req *req,dma_addr_t *src_addr,dma_addr_t *dst_addr);
@@ -1477,7 +1491,7 @@ static int iaa_comp_acompress(struct acostruct iaa_compression_ctx *compression_ctx;struct crypto_tfm *tfm = req->base.tfm;dma_addr_t src_addr, dst_addr; -int nr_sgs, cpu, ret =0 ; +int cpu, ret =0 ;struct iaa_wq *iaa_wq;struct idxd_wq *wq;struct device *dev;
@@ -1489,11 +1503,15 @@ static int iaa_comp_acompress(struct acoreturn -ENODEV ; } -if (!req->src || !req->slen) { -pr_debug ("invalid src, not compressing\n" ); +if (!req->src || !req->slen || !req->dst) { +pr_debug ("invalid req, not compressing\n" );return -EINVAL ; } +/* Fall back to software if src or dst has multiple sg entries */ +if (sg_nents (req->src) >1 ||sg_nents (req->dst) >1 ) +return deflate_generic_compress (req); + cpu =get_cpu (); wq =wq_table_next_wq (cpu);put_cpu ();
@@ -1512,30 +1530,25 @@ static int iaa_comp_acompress(struct aco dev = &wq->idxd->pdev->dev; - nr_sgs =dma_map_sg (dev, req->src,sg_nents (req->src),DMA_TO_DEVICE ); -if (nr_sgs <=0 || nr_sgs >1 ) { -dev_dbg (dev,"couldn't map src sg for iaa device %d," -" wq %d: ret=%d\n" , iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO ; - goto out; +if (!dma_map_sg (dev, req->src,1 ,DMA_TO_DEVICE )) { +dev_dbg (dev,"couldn't map src sg for iaa device %d, wq %d\n" , + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); +iaa_wq_put (wq); +return deflate_generic_compress (req); } src_addr =sg_dma_address (req->src); -dev_dbg (dev,"dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," -" req->slen %d, sg_dma_len(sg) %d\n" , src_addr, nr_sgs, +dev_dbg (dev,"map src %llx req->src %p slen %d sg_len %d\n" , src_addr, req->src, req->slen,sg_dma_len (req->src)); - nr_sgs =dma_map_sg (dev, req->dst,sg_nents (req->dst),DMA_FROM_DEVICE ); -if (nr_sgs <=0 || nr_sgs >1 ) { -dev_dbg (dev,"couldn't map dst sg for iaa device %d," -" wq %d: ret=%d\n" , iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO ; - goto err_map_dst; +if (!dma_map_sg (dev, req->dst,1 ,DMA_FROM_DEVICE )) { +dev_dbg (dev,"couldn't map dst sg for iaa device %d, wq %d\n" , + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); +dma_unmap_sg (dev, req->src,1 ,DMA_TO_DEVICE ); +iaa_wq_put (wq); +return deflate_generic_compress (req); } dst_addr =sg_dma_address (req->dst); -dev_dbg (dev,"dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," -" req->dlen %d, sg_dma_len(sg) %d\n" , dst_addr, nr_sgs, +dev_dbg (dev,"map dst %llx req->dst %p dlen %d sg_len %d\n" , dst_addr, req->dst, req->dlen,sg_dma_len (req->dst)); ret = iaa_compress(tfm, req, wq, src_addr, req->slen, dst_addr,
@@ -1555,8 +1568,8 @@ static int iaa_comp_acompress(struct acoif (ret)dev_dbg (dev,"asynchronous compress verification failed ret=%d\n" , ret); -dma_unmap_sg (dev, req->dst,sg_nents (req->dst),DMA_TO_DEVICE ); -dma_unmap_sg (dev, req->src,sg_nents (req->src),DMA_FROM_DEVICE ); +dma_unmap_sg (dev, req->dst,1 ,DMA_TO_DEVICE ); +dma_unmap_sg (dev, req->src,1 ,DMA_FROM_DEVICE ); goto out; }
@@ -1564,9 +1577,8 @@ static int iaa_comp_acompress(struct acoif (ret)dev_dbg (dev,"asynchronous compress failed ret=%d\n" , ret); -dma_unmap_sg (dev, req->dst,sg_nents (req->dst),DMA_FROM_DEVICE ); -err_map_dst: -dma_unmap_sg (dev, req->src,sg_nents (req->src),DMA_TO_DEVICE ); +dma_unmap_sg (dev, req->dst,1 ,DMA_FROM_DEVICE ); +dma_unmap_sg (dev, req->src,1 ,DMA_TO_DEVICE ); out:iaa_wq_put (wq);
@@ -1577,7 +1589,7 @@ static int iaa_comp_adecompress(struct a {struct crypto_tfm *tfm = req->base.tfm;dma_addr_t src_addr, dst_addr; -int nr_sgs, cpu, ret =0 ; +int cpu, ret =0 ;struct iaa_wq *iaa_wq;struct device *dev;struct idxd_wq *wq;
@@ -1587,11 +1599,15 @@ static int iaa_comp_adecompress(struct areturn -ENODEV ; } -if (!req->src || !req->slen) { -pr_debug ("invalid src, not decompressing\n" ); +if (!req->src || !req->slen || !req->dst) { +pr_debug ("invalid req, not decompressing\n" );return -EINVAL ; } +/* Fall back to software if src or dst has multiple sg entries */ +if (sg_nents (req->src) >1 ||sg_nents (req->dst) >1 ) +return deflate_generic_decompress (req); + cpu =get_cpu (); wq =wq_table_next_wq (cpu);put_cpu ();
@@ -1610,30 +1626,25 @@ static int iaa_comp_adecompress(struct a dev = &wq->idxd->pdev->dev; - nr_sgs =dma_map_sg (dev, req->src,sg_nents (req->src),DMA_TO_DEVICE ); -if (nr_sgs <=0 || nr_sgs >1 ) { -dev_dbg (dev,"couldn't map src sg for iaa device %d," -" wq %d: ret=%d\n" , iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO ; - goto out; +if (!dma_map_sg (dev, req->src,1 ,DMA_TO_DEVICE )) { +dev_dbg (dev,"couldn't map src sg for iaa device %d, wq %d\n" , + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); +iaa_wq_put (wq); +return deflate_generic_decompress (req); } src_addr =sg_dma_address (req->src); -dev_dbg (dev,"dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," -" req->slen %d, sg_dma_len(sg) %d\n" , src_addr, nr_sgs, +dev_dbg (dev,"map src %llx req->src %p slen %d sg_len %d\n" , src_addr, req->src, req->slen,sg_dma_len (req->src)); - nr_sgs =dma_map_sg (dev, req->dst,sg_nents (req->dst),DMA_FROM_DEVICE ); -if (nr_sgs <=0 || nr_sgs >1 ) { -dev_dbg (dev,"couldn't map dst sg for iaa device %d," -" wq %d: ret=%d\n" , iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO ; - goto err_map_dst; +if (!dma_map_sg (dev, req->dst,1 ,DMA_FROM_DEVICE )) { +dev_dbg (dev,"couldn't map dst sg for iaa device %d, wq %d\n" , + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); +dma_unmap_sg (dev, req->src,1 ,DMA_TO_DEVICE ); +iaa_wq_put (wq); +return deflate_generic_decompress (req); } dst_addr =sg_dma_address (req->dst); -dev_dbg (dev,"dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," -" req->dlen %d, sg_dma_len(sg) %d\n" , dst_addr, nr_sgs, +dev_dbg (dev,"map dst %llx req->dst %p dlen %d sg_len %d\n" , dst_addr, req->dst, req->dlen,sg_dma_len (req->dst)); ret = iaa_decompress(tfm, req, wq, src_addr, req->slen,
@@ -1644,10 +1655,8 @@ static int iaa_comp_adecompress(struct aif (ret !=0 )dev_dbg (dev,"asynchronous decompress failed ret=%d\n" , ret); -dma_unmap_sg (dev, req->dst,sg_nents (req->dst),DMA_FROM_DEVICE ); -err_map_dst: -dma_unmap_sg (dev, req->src,sg_nents (req->src),DMA_TO_DEVICE ); -out: +dma_unmap_sg (dev, req->dst,1 ,DMA_FROM_DEVICE ); +dma_unmap_sg (dev, req->src,1 ,DMA_TO_DEVICE );iaa_wq_put (wq);return ret;
drivers/crypto/intel/iaa/iaa_crypto_stats.c +9 -0
@@ -19,6 +19,7 @@static atomic64_t total_comp_calls;static atomic64_t total_decomp_calls; +static atomic64_t total_sw_comp_calls;static atomic64_t total_sw_decomp_calls;static atomic64_t total_comp_bytes_out;static atomic64_t total_decomp_bytes_in;
@@ -43,6 +44,11 @@ void update_total_decomp_calls(void)atomic64_inc (&total_decomp_calls); } +void update_total_sw_comp_calls (void ) +{ +atomic64_inc (&total_sw_comp_calls); +} +void update_total_sw_decomp_calls (void ) {atomic64_inc (&total_sw_decomp_calls);
@@ -104,6 +110,7 @@ static void reset_iaa_crypto_stats(void) {atomic64_set (&total_comp_calls,0 );atomic64_set (&total_decomp_calls,0 ); +atomic64_set (&total_sw_comp_calls,0 );atomic64_set (&total_sw_decomp_calls,0 );atomic64_set (&total_comp_bytes_out,0 );atomic64_set (&total_decomp_bytes_in,0 );
@@ -174,6 +181,8 @@ static int global_stats_show(struct seq_ atomic64_read(&total_comp_calls));seq_printf (m," total_decomp_calls: %llu\n" ,atomic64_read (&total_decomp_calls)); +seq_printf (m," total_sw_comp_calls: %llu\n" , +atomic64_read (&total_sw_comp_calls));seq_printf (m," total_sw_decomp_calls: %llu\n" ,atomic64_read (&total_sw_decomp_calls)); seq_printf(m, " total_comp_bytes_out: %llu\n",
drivers/crypto/intel/iaa/iaa_crypto_stats.h +2 -0
@@ -11,6 +11,7 @@ void iaa_crypto_debugfs_cleanup(void);void update_total_comp_calls (void );void update_total_comp_bytes_out (int n);void update_total_decomp_calls (void ); +void update_total_sw_comp_calls (void );void update_total_sw_decomp_calls (void );void update_total_decomp_bytes_in (int n);void update_completion_einval_errs (void );
@@ -29,6 +30,7 @@ static inline void iaa_crypto_debugfs_clstatic inline void update_total_comp_calls (void ) {}static inline void update_total_comp_bytes_out (int n) {}static inline void update_total_decomp_calls (void ) {} +static inline void update_total_sw_comp_calls (void ) {}static inline void update_total_sw_decomp_calls (void ) {}static inline void update_total_decomp_bytes_in (int n) {}static inline void update_completion_einval_errs (void ) {}
Greg Kroah-Hartman
maintainerPATCH 438/583 crypto: iaa - unmap dst before software fallback on decompress
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinicius Costa Gomes <[email protected]>
[ Upstream commit 94a25930477113730372e0fa2985da4c5ac95c9a ]
On a hardware analytics error, decompress retries through the software fallback, which writes req->dst with the CPU while it is still mapped DMA_FROM_DEVICE. With SWIOTLB active the later dma_unmap_sg() copies the stale bounce buffer over req->dst, corrupting the result.
Unmap before the fallback runs. The async path unmaps inline; the sync path signals the retry with -EAGAIN so iaa_comp_adecompress() runs the fallback after unmapping.
--- drivers/crypto/intel/iaa/iaa_crypto_main.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-)
drivers/crypto/intel/iaa/iaa_crypto_main.c +12 -15
@@ -1063,13 +1063,17 @@ static void iaa_desc_complete(struct idxpr_warn ("%s: falling back to deflate-generic decompress, " "analytics error code %x\n" , __func__, idxd_desc->iax_completion->error_code); +dma_unmap_sg (dev, ctx->req->dst,sg_nents (ctx->req->dst), +DMA_FROM_DEVICE ); +dma_unmap_sg (dev, ctx->req->src,1 ,DMA_TO_DEVICE ); + ret =deflate_generic_decompress (ctx->req);if (ret) {dev_dbg (dev,"%s: deflate-generic failed ret=%d\n" , __func__, ret); err = -EIO ; - goto err; } + goto out; }else { err = -EIO ; goto err;
@@ -1452,19 +1456,9 @@ static int iaa_decompress(struct crypto_ ret =check_completion (dev, idxd_desc->iax_completion, false, false);if (ret) {dev_dbg (dev,"%s: check_completion failed ret=%d\n" , __func__, ret); -if (idxd_desc->iax_completion->status ==IAA_ANALYTICS_ERROR ) { -pr_warn ("%s: falling back to deflate-generic decompress, " -"analytics error code %x\n" , __func__, - idxd_desc->iax_completion->error_code); - ret =deflate_generic_decompress (req); -if (ret) { -dev_dbg (dev,"%s: deflate-generic failed ret=%d\n" , - __func__, ret); - goto err; - } - }else { - goto err; - } +if (idxd_desc->iax_completion->status ==IAA_ANALYTICS_ERROR ) + ret = -EAGAIN ; + goto err; }else { req->dlen = idxd_desc->iax_completion->output_size; }
@@ -1652,13 +1646,16 @@ static int iaa_comp_adecompress(struct aif (ret == -EINPROGRESS )return ret; -if (ret !=0 ) +if (ret !=0 && ret != -EAGAIN )dev_dbg (dev,"asynchronous decompress failed ret=%d\n" , ret);dma_unmap_sg (dev, req->dst,1 ,DMA_FROM_DEVICE );dma_unmap_sg (dev, req->src,1 ,DMA_TO_DEVICE );iaa_wq_put (wq); +if (ret == -EAGAIN ) + ret =deflate_generic_decompress (req); +return ret; }
Greg Kroah-Hartman
maintainerPATCH 439/583 crypto: atmel-ecc - replace min_t with min
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 5b085b2a038a1458f9398cb3b3b03cba6e38e1e0 ]
Use the simpler min() macro since the values are all unsigned and compatible.
Stable-dep-of: f07a0d251db7 ("crypto: atmel-ecc - avoid stale fallback key after set_secret failure")
--- drivers/crypto/atmel-ecc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/crypto/atmel-ecc.c +2 -2
@@ -56,7 +56,7 @@ static void atmel_ecdh_done(struct atmel goto free_work_data;/* might want less than we've got */ - n_sz =min_t (size_t,ATMEL_ECC_NIST_P256_N_SIZE , req->dst_len); + n_sz =min (ATMEL_ECC_NIST_P256_N_SIZE , req->dst_len);/* copy the shared secret */ copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, n_sz),
@@ -150,7 +150,7 @@ static int atmel_ecdh_generate_public_kereturn -EINVAL ;/* might want less than we've got */ - nbytes =min_t (size_t,ATMEL_ECC_PUBKEY_SIZE , req->dst_len); + nbytes =min (ATMEL_ECC_PUBKEY_SIZE , req->dst_len);/* public key was saved at private key generation */ copied = sg_copy_from_buffer(req->dst,
Greg Kroah-Hartman
maintainerPATCH 440/583 crypto: atmel-ecc - clean up and improve ECDH comments
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 3e84fb698abada239d3e35ed3d52a24dbfda5f6a ]
Improve the kerneldoc for struct atmel_ecdh_ctx by removing the stale "unsupported curves" wording, since the device only supports a single curve (P-256), and move the set_secret() constraint to the description.
In atmel_ecdh_set_secret(), clarify that the device generates the private key, and drop the redundant "only supports NIST P256" comment.
In atmel_ecdh_done() and atmel_ecdh_generate_public_key(), clarify the truncation comments. Also note that a P-256 public key consists of two 32-byte coordinates in atmel_ecdh_compute_shared_secret(), and remove the unnecessary fall-through comment and other redundant comments.
Stable-dep-of: f07a0d251db7 ("crypto: atmel-ecc - avoid stale fallback key after set_secret failure")
--- drivers/crypto/atmel-ecc.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-)
drivers/crypto/atmel-ecc.c +14 -24
@@ -27,15 +27,14 @@ static struct atmel_ecc_driver_data driv/** * struct atmel_ecdh_ctx - transformation context -* @client : pointer to i2c client device -* @fallback : used for unsupported curves or when user wants to use its own -* private key. -* @public_key : generated when calling set_secret(). It's the responsibility -* of the user to not call set_secret() while -* generate_public_key() or compute_shared_secret() are in flight. -* @curve_id : elliptic curve id -* @do_fallback: true when the device doesn't support the curve or when the user -* wants to use its own private key. +* @client: I2C client device +* @fallback: ECDH fallback used for caller-provided private keys +* @public_key: cached public key for the device-generated private key +* @curve_id: elliptic curve id +* @do_fallback: true when ECDH operations should use @fallback +* +* The caller must not invoke set_secret() while generate_public_key() +* or compute_shared_secret() are in flight. */ struct atmel_ecdh_ctx {struct i2c_client *client;
@@ -55,7 +54,7 @@ static void atmel_ecdh_done(struct atmelif (status) goto free_work_data; -/* might want less than we've got */ +/* copy only as much as requested, capped at 32 bytes */ n_sz =min (ATMEL_ECC_NIST_P256_N_SIZE , req->dst_len);/* copy the shared secret */
@@ -64,15 +63,15 @@ static void atmel_ecdh_done(struct atmelif (copied != n_sz) status = -EINVAL ; -/* fall through */ free_work_data:kfree_sensitive (work_data);kpp_request_complete (req, status); }/* -* A random private key is generated and stored in the device. The device -* returns the pair public key. +* If no private key is provided, generate one in the device and cache +* the corresponding public key. The generated private key never leaves +* the device. */ static int atmel_ecdh_set_secret (struct crypto_kpp *tfm,const void *buf,unsigned int len)
@@ -83,9 +82,7 @@ static int atmel_ecdh_set_secret(structstruct ecdh params;int ret = -ENOMEM ; -/* free the old public key, if any */ kfree (ctx->public_key); -/* make sure you don't free the old public key twice */ ctx->public_key =NULL ;if (crypto_ecdh_decode_key (buf, len, ¶ms) <0 ) {
@@ -94,7 +91,6 @@ static int atmel_ecdh_set_secret(struct }if (params.key_size) { -/* fallback to ecdh software implementation */ ctx->do_fallback = true;return crypto_kpp_set_secret (ctx->fallback, buf, len); }
@@ -103,11 +99,6 @@ static int atmel_ecdh_set_secret(structif (!cmd)return -ENOMEM ; -/* -* The device only supports NIST P256 ECC keys. The public key size will -* always be the same. Use a macro for the key size to avoid unnecessary -* computations. -*/ public_key =kmalloc (ATMEL_ECC_PUBKEY_SIZE ,GFP_KERNEL );if (!public_key) goto free_cmd;
@@ -120,7 +111,6 @@ static int atmel_ecdh_set_secret(structif (ret) goto free_public_key; -/* save the public key */ memcpy (public_key, &cmd->data[RSP_DATA_IDX ],ATMEL_ECC_PUBKEY_SIZE ); ctx->public_key = public_key;
@@ -149,7 +139,7 @@ static int atmel_ecdh_generate_public_keif (!ctx->public_key)return -EINVAL ; -/* might want less than we've got */ +/* copy only as much as requested, capped at 64 bytes */ nbytes =min (ATMEL_ECC_PUBKEY_SIZE , req->dst_len);/* public key was saved at private key generation */
@@ -175,7 +165,7 @@ static int atmel_ecdh_compute_shared_secreturn crypto_kpp_compute_shared_secret (req); } -/* must have exactly two points to be on the curve */ +/* A P-256 public key must contain two 32-byte coordinates */ if (req->src_len !=ATMEL_ECC_PUBKEY_SIZE )return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 441/583 crypto: atmel-ecc - avoid stale fallback key after set_secret failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit f07a0d251db7606e4792d2610788fbcc7b2c0d12 ]
Clear ->do_fallback before decoding a new ECDH secret and enable it only after the software fallback accepts a caller-provided private key. This avoids using a stale fallback key should crypto_kpp_set_secret() fail.
--- drivers/crypto/atmel-ecc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/crypto/atmel-ecc.c +4 -4
@@ -84,6 +84,7 @@ static int atmel_ecdh_set_secret(structkfree (ctx->public_key); ctx->public_key =NULL ; + ctx->do_fallback = false;if (crypto_ecdh_decode_key (buf, len, ¶ms) <0 ) {dev_err (&ctx->client->dev,"crypto_ecdh_decode_key failed\n" );
@@ -91,8 +92,9 @@ static int atmel_ecdh_set_secret(struct }if (params.key_size) { - ctx->do_fallback = true; -return crypto_kpp_set_secret (ctx->fallback, buf, len); + ret =crypto_kpp_set_secret (ctx->fallback, buf, len); + ctx->do_fallback = !ret; +return ret; } cmd =kmalloc (sizeof (*cmd),GFP_KERNEL );
@@ -103,8 +105,6 @@ static int atmel_ecdh_set_secret(structif (!public_key) goto free_cmd; - ctx->do_fallback = false; -atmel_i2c_init_genkey_cmd (cmd,DATA_SLOT_2 ); ret =atmel_i2c_send_receive (ctx->client, cmd);
Greg Kroah-Hartman
maintainerPATCH 442/583 ovl: fix double end_creating() on the casefold-mismatch path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vivek Parikh <[email protected]>
[ Upstream commit 077ab8985ee278c3d8618182d335b0f0cd919e16 ]
ovl_create_real() releases the new dentry twice when the casefold consistency check fails. The S_IFDIR branch calls end_creating() and sets err, then falls through to the common out: label which calls end_creating() on the same dentry again:
case S_IFDIR:
newdentry = ovl_do_mkdir(ofs, dir, newdentry, attr->mode);
err = PTR_ERR_OR_ZERO(newdentry);
if (!err && ofs->casefold != ovl_dentry_casefolded(newdentry)) {
pr_warn_ratelimited(...);
end_creating(newdentry); /* first */
err = -EINVAL;
}
break;
...
if (err)
goto out;
...
out:
if (err) {
end_creating(newdentry); /* second, same dentry */
return ERR_PTR(err);
}end_creating() is end_dirop(), which does inode_unlock() on the parent and dput() on the dentry, so the parent directory's i_rwsem is unlocked twice and the dentry is put twice. The second unlock releases a lock that is not held, which is what wedges every later creation under that parent, and the second dput() drops a reference that was never taken.
The branch was added by commit dfc7da402ccc ("ovl: Check for casefold consistency when creating new dentries") as a bare dput(), which already released the reference twice; commit fe497f0759e0 ("VFS: change vfs_mkdir() to unlock on failure.") converted both sites to end_creating(), adding the double unlock.
This is reachable by an unprivileged user. The casefold consistency of the layers is validated at mount time in ovl_parse_layer(), and again on every lookup in ovl_lookup_single(), but ofs->workdir is the internal "work" subdirectory created inside the user-supplied workdir, and that subdirectory is not re-checked. Marking it casefolded after the mount therefore makes every ovl_create_temp() inherit the wrong state - and that path reaches ovl_create_real() through ovl_start_creating_temp(), which uses start_creating() with a generated name and so never runs the lookup-time check.
unshare -Urm
mount -t tmpfs -o casefold=utf8-12.1.0 tmpfs mnt
mkdir -p mnt/lower/d mnt/upper mnt/work mnt/merged
mount -t overlay ovl -o lowerdir=mnt/lower,\
upperdir=mnt/upper,workdir=mnt/work mnt/merged
chattr +F mnt/work/work
mkdir mnt/merged/d/sub # directory copy-upoverlayfs: wrong inherited casefold (work/#5)
and the next copy-up blocks forever on the parent's i_rwsem:
mkdir D start_creating+0x65/0xb0
ovl_start_creating_temp+0xb0/0xe0 [overlay]
ovl_create_temp+0xa3/0x1d0 [overlay]
ovl_copy_up_one+0x1f1c/0x21c0 [overlay]
ovl_copy_up_flags+0xf5/0x140 [overlay]
ovl_create_object+0xb7/0x220 [overlay]
ovl_mkdir+0x23/0x40 [overlay]Drop the end_creating() from the branch and let out: own the cleanup, which is what every other error path in this function already does.
--- fs/overlayfs/dir.c | 1 - 1 file changed, 1 deletion(-)
fs/overlayfs/dir.c +0 -1
@@ -191,7 +191,6 @@ struct dentry *ovl_create_real(struct ovif (!err && ofs->casefold !=ovl_dentry_casefolded (newdentry)) {pr_warn_ratelimited ("wrong inherited casefold (%pd2)\n" , newdentry); -dput (newdentry); err = -EINVAL ; }break ;
Greg Kroah-Hartman
maintainerPATCH 443/583 pidfs: simplify PIDFD_GET_<type>_NAMESPACE ioctls
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit a71e4f103aed69e7a11ea913312726bb194c76ee ]
We have reworked namespaces sufficiently that all this special-casing shouldn't be needed anymore
Stable-dep-of: 9688a4680293 ("pidfd: hold exec_update_lock around namespace ioctl")
--- fs/pidfs.c | 75 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 36 insertions(+), 39 deletions(-)
fs/pidfs.c +36 -39
@@ -456,7 +456,6 @@ static long pidfd_ioctl(struct file *fil struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL;struct ns_common *ns_common =NULL ; -struct pid_namespace *pid_ns;if (!pidfs_ioctl_valid (cmd))return -ENOIOCTLCMD ;
@@ -498,66 +497,64 @@ static long pidfd_ioctl(struct file *filswitch (cmd) {/* Namespaces that hang of nsproxy. */ case PIDFD_GET_CGROUP_NAMESPACE : -if (IS_ENABLED (CONFIG_CGROUPS )) { -get_cgroup_ns (nsp->cgroup_ns); - ns_common =to_ns_common (nsp->cgroup_ns); - } +if (!ns_ref_get (nsp->cgroup_ns)) +break ; + ns_common =to_ns_common (nsp->cgroup_ns);break ;case PIDFD_GET_IPC_NAMESPACE : -if (IS_ENABLED (CONFIG_IPC_NS )) { -get_ipc_ns (nsp->ipc_ns); - ns_common =to_ns_common (nsp->ipc_ns); - } +if (!ns_ref_get (nsp->ipc_ns)) +break ; + ns_common =to_ns_common (nsp->ipc_ns);break ;case PIDFD_GET_MNT_NAMESPACE : -get_mnt_ns (nsp->mnt_ns); +if (!ns_ref_get (nsp->mnt_ns)) +break ; ns_common =to_ns_common (nsp->mnt_ns);break ;case PIDFD_GET_NET_NAMESPACE : -if (IS_ENABLED (CONFIG_NET_NS )) { - ns_common =to_ns_common (nsp->net_ns); -get_net_ns (ns_common); - } +if (!ns_ref_get (nsp->net_ns)) +break ; + ns_common =to_ns_common (nsp->net_ns);break ;case PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE : -if (IS_ENABLED (CONFIG_PID_NS )) { -get_pid_ns (nsp->pid_ns_for_children); - ns_common =to_ns_common (nsp->pid_ns_for_children); - } +if (!ns_ref_get (nsp->pid_ns_for_children)) +break ; + ns_common =to_ns_common (nsp->pid_ns_for_children);break ;case PIDFD_GET_TIME_NAMESPACE : -if (IS_ENABLED (CONFIG_TIME_NS )) { -get_time_ns (nsp->time_ns); - ns_common =to_ns_common (nsp->time_ns); - } +if (!ns_ref_get (nsp->time_ns)) +break ; + ns_common =to_ns_common (nsp->time_ns);break ;case PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE : -if (IS_ENABLED (CONFIG_TIME_NS )) { -get_time_ns (nsp->time_ns_for_children); - ns_common =to_ns_common (nsp->time_ns_for_children); - } +if (!ns_ref_get (nsp->time_ns_for_children)) +break ; + ns_common =to_ns_common (nsp->time_ns_for_children);break ;case PIDFD_GET_UTS_NAMESPACE : -if (IS_ENABLED (CONFIG_UTS_NS )) { -get_uts_ns (nsp->uts_ns); - ns_common =to_ns_common (nsp->uts_ns); - } +if (!ns_ref_get (nsp->uts_ns)) +break ; + ns_common =to_ns_common (nsp->uts_ns);break ;/* Namespaces that don't hang of nsproxy. */ case PIDFD_GET_USER_NAMESPACE : -if (IS_ENABLED (CONFIG_USER_NS )) { -rcu_read_lock (); - ns_common =to_ns_common (get_user_ns (task_cred_xxx (task, user_ns))); -rcu_read_unlock (); + scoped_guard(rcu) { +struct user_namespace *user_ns; + + user_ns =task_cred_xxx (task, user_ns); +if (!ns_ref_get (user_ns)) +break ; + ns_common =to_ns_common (user_ns); }break ;case PIDFD_GET_PID_NAMESPACE : -if (IS_ENABLED (CONFIG_PID_NS )) { -rcu_read_lock (); + scoped_guard(rcu) { +struct pid_namespace *pid_ns; + pid_ns =task_active_pid_ns (task); -if (pid_ns) - ns_common =to_ns_common (get_pid_ns (pid_ns)); -rcu_read_unlock (); +if (!ns_ref_get (pid_ns)) +break ; + ns_common =to_ns_common (pid_ns); }break ;default :
Greg Kroah-Hartman
maintainerPATCH 444/583 pidfd: hold exec_update_lock around namespace ioctl
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chen Linxuan <[email protected]>
[ Upstream commit 9688a46802939da28f00cb40e8129615d5d4af39 ]
The PIDFD_GET_*_NAMESPACE ioctls in pidfd_ioctl() perform a filesystem credentials ptrace access check before handing out a namespace file descriptor. The accompanying comment states that the code "mirrors nsfs behavior", but, unlike the corresponding procfs paths, it does so without holding the target task's exec_update_lock.
proc_ns_get_link() and proc_ns_readlink() both take exec_update_lock for reading around the ptrace check and the namespace lookup, so that the credentials used for the access decision match those of the task when its namespace is read. Without it, a caller can pass the check against the target's old credentials and then read the namespace after the target has execve()'d a setuid binary and committed new credentials -- accessing namespace information it should have been denied.
Hold exec_update_lock for reading around the ptrace check and the namespace lookup so that pidfd truly mirrors nsfs behavior, as the comment already claims. open_namespace() itself runs outside the lock: once a namespace reference is obtained it carries its own refcount and is opened with the caller's own credentials, so a concurrent execve() on the target can no longer affect the outcome.
--- fs/pidfs.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-)
fs/pidfs.c +31 -12
@@ -456,6 +456,7 @@ static long pidfd_ioctl(struct file *fil struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL;struct ns_common *ns_common =NULL ; +int error;if (!pidfs_ioctl_valid (cmd))return -ENOIOCTLCMD ;
@@ -479,20 +480,33 @@ static long pidfd_ioctl(struct file *filif (arg)return -EINVAL ; +/* +* We're trying to open a file descriptor to the namespace so perform a +* filesystem cred ptrace check. Hold @task's exec_update_lock for the +* duration of the ptrace check and the namespace lookup so that the +* credentials used for the access decision match those of @task at the +* time its namespace is read, preventing a concurrent execve() from +* swapping the task's credentials in between the check and the use. We +* mirror nsfs behavior. +*/ + error =down_read_killable (&task->signal->exec_update_lock); +if (error) +return error; + +if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { + error = -EACCES ; + goto out_unlock; + } + scoped_guard(task_lock, task) { nsp = task->nsproxy;if (nsp)get_nsproxy (nsp); } -if (!nsp) -return -ESRCH ;/* just pretend it didn't exist */ - -/* -* We're trying to open a file descriptor to the namespace so perform a -* filesystem cred ptrace check. Also, we mirror nsfs behavior. -*/ -if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) -return -EACCES ; +if (!nsp) { + error = -ESRCH ;/* just pretend it didn't exist */ + goto out_unlock; + }switch (cmd) {/* Namespaces that hang of nsproxy. */
@@ -558,11 +572,16 @@ static long pidfd_ioctl(struct file *fil }break ;default : -return -ENOIOCTLCMD ; + error = -ENOIOCTLCMD ; } -if (!ns_common) -return -EOPNOTSUPP ; +if (!error && !ns_common) + error = -EOPNOTSUPP ; + +out_unlock: +up_read (&task->signal->exec_update_lock); +if (error) +return error;/* open_namespace() unconditionally consumes the reference */ return open_namespace (ns_common);
Greg Kroah-Hartman
maintainerPATCH 445/583 rust: devres: fix race condition due to nesting
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Danilo Krummrich <[email protected]>
[ Upstream commit ba268514ea14b44570030e8ed2aef92a38679e85 ]
Commit f5d3ef25d238 ("rust: devres: get rid of Devres' inner Arc") did attempt to optimize away the internal reference count of Devres.
However, without an internal reference count, we can't support cases where Devres is indirectly nested, resulting into a deadlock.
Such indirect nesting easily happens in the following way:
A registration object (which is guarded by devres) hold a reference count of an object that holds a device resource guarded by devres itself.
For instance a drm::Registration holds a reference of a drm::Device. The drm::Device itself holds a device resource in its private data.
When the drm::Registration is dropped by devres, and it happens that it did hold the last reference count of the drm::Device, it also drops the device resource, which is guarded by devres itself.
Thus, resulting into a deadlock in the Devres destructor of the device resource, as in the following backtrace.
sysrq: Show Blocked State task:rmmod state:D stack:0 pid:1331 tgid:1331 ppid:1330 task_flags:0x400100 flags:0x00000010 Call trace: __switch_to+0x190/0x294 (T) __schedule+0x878/0xf10 schedule+0x4c/0xcc schedule_timeout+0x44/0x118 wait_for_common+0xc0/0x18c wait_for_completion+0x18/0x24 _RINvNtCs4gKlGRWyJ5S_4core3ptr13drop_in_placeINtNtNtCsgzhNYVB7wSz_6kernel4sync3arc3ArcINtNtBN_6devres6DevresmEEECsRdyc7Hyps3_15rust_driver_pci+0x68/0xe8 [rust_driver_pci] _RINvNvNtCsgzhNYVB7wSz_6kernel6devres16register_foreign8callbackINtNtCs4gKlGRWyJ5S_4core3pin3PinINtNtNtB6_5alloc4kbox3BoxINtNtNtB6_4sync3arc3ArcINtB4_6DevresmEENtNtB1A_9allocator7KmallocEEECsRdyc7Hyps3_15rust_driver_pci+0x34/0xc8 [rust_driver_pci] devm_action_release+0x14/0x20 devres_release_all+0xb8/0x118 device_release_driver_internal+0x1c4/0x28c driver_detach+0x94/0xd4 bus_remove_driver+0xdc/0x11c driver_unregister+0x34/0x58 pci_unregister_driver+0x20/0x80 __arm64_sys_delete_module+0x1d8/0x254 invoke_syscall+0x40/0xcc el0_svc_common+0x8c/0xd8 do_el0_svc+0x1c/0x28 el0_svc+0x54/0x1d4 el0t_64_sync_handler+0x84/0x12c el0t_64_sync+0x198/0x19c
In order to fix this, re-introduce the internal reference count.
Stable-dep-of: acc516dfa197 ("rust: devres: fix race between concurrent revokers")
--- rust/kernel/devres.rs | 157 +++++++++++++++++--------------------------------- 1 file changed, 55 insertions(+), 102 deletions(-)
rust/kernel/devres.rs +55 -102
@@ -12,24 +12,22 @@ use crate::{ error::{to_result, Error, Result}, ffi::c_void, prelude::*, - revocable::{Revocable, RevocableGuard}, - sync::{aref::ARef, rcu, Completion}, - types::{ForeignOwnable, Opaque, ScopeGuard}, + revocable::{ + Revocable, + RevocableGuard, // + }, + sync::{ + aref::ARef, + rcu, + Arc, // + }, + types::ForeignOwnable, }; -use pin_init::Wrapper ; - -/// [`Devres`] inner data accessed from [`Devres::callback`]. #[pin_data] struct Inner <T :Send > {#[pin] data:Revocable <T >, -/// Tracks whether [`Devres::callback`] has been completed. -#[pin] - devm:Completion , -/// Tracks whether revoking [`Self::data`] has been completed. -#[pin] - revoke:Completion , }/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
@@ -105,18 +103,13 @@ struct Inner<T: Send> {/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? }; -/// let devres = KBox::pin_init(Devres::new(dev, iomem), GFP_KERNEL)?; +/// let devres = Devres::new(dev, iomem)?; /// /// let res = devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); /// # Ok(()) /// # } /// ``` -/// -/// # Invariants -/// -/// `Self::inner` is guaranteed to be initialized and is always accessed read-only. -#[pin_data( PinnedDrop )] pub struct Devres <T :Send > { dev:ARef <Device >,/// Pointer to [`Self::devres_callback`].
@@ -124,14 +117,7 @@ pub struct Devres<T: Send> {/// Has to be stored, since Rust does not guarantee to always return the same address for a /// function. However, the C API uses the address as a key. callback: unsafe extern "C" fn(*mut c_void), -/// Contains all the fields shared with [`Self::callback`]. -// TODO: Replace with `UnsafePinned`, once available. -// -// Subsequently, the `drop_in_place()` in `Devres::drop` and `Devres::new` as well as the -// explicit `Send` and `Sync' impls can be removed. -#[pin] - inner: Opaque<Inner<T>>, - _add_action: (), + inner: Arc<Inner<T>>, }impl <T :Send >Devres <T > {
@@ -139,74 +125,53 @@ impl<T: Send> Devres<T> {/// /// The `data` encapsulated within the returned `Devres` instance' `data` will be /// (revoked)[`Revocable`] once the device is detached. -pub fn new <'a,E >( - dev: &'aDevice <Bound >, - data:impl PinInit <T ,E > + 'a, - ) ->impl PinInit <Self ,Error > + 'a +pub fn new <E >(dev: &Device <Bound >, data:impl PinInit <T ,E >) ->Result <Self >where -T : 'a,Error :From <E >, { -try_pin_init! (&this inSelf { +let callback =Self ::devres_callback; +let inner =Arc ::pin_init ( +try_pin_init! (Inner { + data <-Revocable ::new (data), + }), +GFP_KERNEL , + )?; +let devres_inner = inner.clone (); + +// SAFETY: +// - `dev.as_raw()` is a pointer to a valid bound device. +// - `inner` is guaranteed to be valid for the duration of the lifetime of `Self`. +// - `devm_add_action()` is guaranteed not to call `callback` for the entire lifetime of +// `dev`. +to_result (unsafe { + bindings::devm_add_action ( + dev.as_raw (), +Some (callback), +Arc ::as_ptr (&inner).cast_mut ().cast (), + ) + })?; + +// `devm_add_action()` was successful and has consumed the reference count. + core::mem::forget (devres_inner); + +Ok (Self { dev: dev.into (), - callback:Self ::devres_callback, -// INVARIANT: `inner` is properly initialized. - inner <-Opaque ::pin_init (try_pin_init! (Inner { - devm <-Completion ::new (), - revoke <-Completion ::new (), - data <-Revocable ::new (data), - })), -// TODO: Replace with "initializer code blocks" [1] once available. -// -// [1] https://github.com/Rust-for-Linux/pin-init/pull/69 - _add_action: { -// SAFETY: `this` is a valid pointer to uninitialized memory. -let inner =unsafe { &raw mut (*this.as_ptr ()).inner }; - -// SAFETY: -// - `dev.as_raw()` is a pointer to a valid bound device. -// - `inner` is guaranteed to be a valid for the duration of the lifetime of `Self`. -// - `devm_add_action()` is guaranteed not to call `callback` until `this` has been -// properly initialized, because we require `dev` (i.e. the *bound* device) to -// live at least as long as the returned `impl PinInit<Self, Error>`. -to_result (unsafe { - bindings::devm_add_action (dev.as_raw (),Some (*callback), inner.cast ()) - }).inspect_err (|_| { -let inner =Opaque ::cast_into (inner); - -// SAFETY: `inner` is a valid pointer to an `Inner<T>` and valid for both reads -// and writes. -unsafe { core::ptr::drop_in_place (inner) }; - })?; - }, + callback, + inner, }) } -fn inner (&self) -> &Inner <T > { -// SAFETY: By the type invairants of `Self`, `inner` is properly initialized and always -// accessed read-only. -unsafe { &*self.inner.get () } - } -fn data (&self) -> &Revocable <T > { - &self.inner ().data + &self.inner.data }#[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback (ptr: *mut kernel::ffi::c_void ) { -// SAFETY: In `Self::new` we've passed a valid pointer to `Inner` to `devm_add_action()`, -// hence `ptr` must be a valid pointer to `Inner`. -let inner =unsafe { &*ptr.cast ::<Inner <T >>() }; - -// Ensure that `inner` can't be used anymore after we signal completion of this callback. -let inner =ScopeGuard ::new_with_data (inner, |inner| inner.devm.complete_all ()); - -if !inner.data.revoke () { -// If `revoke()` returns false, it means that `Devres::drop` already started revoking -// `data` for us. Hence we have to wait until `Devres::drop` signals that it -// completed revoking `data`. - inner.revoke.wait_for_completion (); - } +// SAFETY: In `Self::new` we've passed a valid pointer of `Inner<T>` to +// `devm_add_action()`, hence `ptr` must be a valid pointer to `Inner<T>`. +let inner =unsafe {Arc ::from_raw (ptr.cast ::<Inner <T >>()) }; + + inner.data.revoke (); }fn remove_action (&self) ->bool {
@@ -218,7 +183,7 @@ impl<T: Send> Devres<T> { bindings::devm_remove_action_nowarn ( self.dev.as_raw (),Some (self.callback), - core::ptr::from_ref (self.inner ()).cast_mut ().cast (), +Arc ::as_ptr (&self.inner).cast_mut ().cast (), ) } == 0) }
@@ -289,31 +254,19 @@ unsafe impl<T: Send> Send for Devres<T>// SAFETY: `Devres` can be shared with any task, if `T: Sync`. unsafe impl <T :Send +Sync >Sync for Devres <T > {} -#[pinned_drop] -impl <T :Send >PinnedDrop for Devres <T > { -fn drop (self:Pin <&mut Self >) { +impl <T :Send >Drop for Devres <T > { +fn drop (&mut self) {// SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. if unsafe { self.data ().revoke_nosync () } {// We revoked `self.data` before the devres action did, hence try to remove it. -if !self.remove_action () { -// We could not remove the devres action, which means that it now runs concurrently, -// hence signal that `self.data` has been revoked by us successfully. - self.inner ().revoke.complete_all (); - -// Wait for `Self::devres_callback` to be done using this object. - self.inner ().devm.wait_for_completion (); +if self.remove_action () { +// SAFETY: In `Self::new` we have taken an additional reference count of `self.data` +// for `devm_add_action()`. Since `remove_action()` was successful, we have to drop +// this additional reference count. +drop (unsafe {Arc ::from_raw (Arc ::as_ptr (&self.inner)) }); } - }else { -// `Self::devres_callback` revokes `self.data` for us, hence wait for it to be done -// using this object. - self.inner ().devm.wait_for_completion (); } - -// INVARIANT: At this point it is guaranteed that `inner` can't be accessed any more. -// -// SAFETY: `inner` is valid for dropping. -unsafe { core::ptr::drop_in_place (self.inner.get ()) }; } }
Re: [PATCH 6.18 445/583] rust: devres: fix race condition due to nesting
rust/kernel/devres.rs impl<T: Send> Devres<T> { · line 139
Quoted 10 lines · On Wed, 09 Sep 2026 15:42:10 +0200 Greg Kroah-Hartman…
rust/kernel/devres.rs +8
-1-try_pin_init! (&this inSelf { +let callback =Self ::devres_callback; +let inner =Arc ::pin_init ( +try_pin_init! (Inner { + data <-Revocable ::new (data), + }), +GFP_KERNEL , + )?; +let devres_inner = inner.clone ();
The compiler complains here:
error[E0277]: the trait bound `impl pin_init::PinInit<Inner<T>, error::Error>: pin_init::PinInit<_, E>` is not satisfied
--> rust/kernel/devres.rs:141:13
|
140 | let inner = Arc::pin_init(
| ------------- required by a bound introduced by this call
141 | / try_pin_init!(Inner {
142 | | data <- Revocable::new(data),
143 | | revocation <- Completion::new(),
144 | | }),
| | ^
| | |
| |______________the trait `pin_init::PinInit<_, E>` is not implemented for `impl pin_init::PinInit<Inner<T>, error::Error>`
| this tail expression is of type `impl PinInit<Inner<T>, Error>`
|
= help: the following other types implement trait `pin_init::PinInit<T, E>`:
<AlwaysFail<T> as pin_init::PinInit<T, ()>>
<ChainPinInit<I, F, T, E> as pin_init::PinInit<T, E>>
<ChainInit<I, F, T, E> as pin_init::PinInit<T, E>>
<core::result::Result<T, E> as pin_init::PinInit<T, E>>
note: required by a bound in `init::InPlaceInit::pin_init`
--> rust/kernel/init.rs:155:31
|
155 | fn pin_init<E>(init: impl PinInit<T, E>, flags: Flags) -> error::Result<Self::PinnedSelf>
| ^^^^^^^^^^^^^ required by this bound in `InPlaceInit::pin_init`
error[E0282]: type annotations needed for `Arc<T>`
--> rust/kernel/devres.rs:140:13
|
140 | let inner = Arc::pin_init(
| ^^^^^
...
147 | let devres_inner = inner.clone();
| ----- type must be known at this point
|
help: consider giving `inner` an explicit type, where the type for type parameter `T` is specified
|
140 | let inner: Arc<T> = Arc::pin_init(
| ++++++++
error[E0282]: type annotations needed
--> rust/kernel/devres.rs:158:17
|
158 | Arc::as_ptr(&inner).cast_mut().cast(),
| ^^^^^^^^^^^ -------- type must be known at this point
| |
| cannot infer type of the type parameter `T` declared on the struct `Arc`
|
help: consider specifying the generic argument
|
158 | Arc::<T>::as_ptr(&inner).cast_mut().cast(),
| +++++This diff would work, similar to commit 9aa64d2503c6 ("rust: devres:
embed struct devres_node directly"), but I wonder if the sequence of backports was not intended like this:
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 67471946f2b5..0cafe7ba28c8 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -137,7 +137,7 @@ pub fn new<E>(dev: &Device<Bound>, data: impl PinInit<T, E>) -> Result<Self>
Error: From<E>,
{
let callback = Self::devres_callback;
- let inner = Arc::pin_init(
+ let inner = Arc::pin_init::<Error>(
try_pin_init!(Inner {
data <- Revocable::new(data),
revocation <- Completion::new(),I hope that helps!
Cheers,
Miguel
Sasha Levin
maintainerRe: [PATCH 6.18 445/583] rust: devres: fix race condition due to nesting
Quoted 4 lines
The compiler complains here:
error[E0277]: the trait bound `impl
pin_init::PinInit
[...]
Dropped both offending commits from 6.18, thanks!
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 446/583 rust: devres: fix race between concurrent revokers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Danilo Krummrich <[email protected]>
[ Upstream commit acc516dfa1972d31836b50abc0115216cd0fccc5 ]
There is a potential race condition when two paths try to revoke a Devres concurrently.
The driver core's devres_release_all() calls Revocable::revoke() via the release callback, while Devres::drop() calls revoke_nosync() on another CPU.
The revoker that does not claim the is_available swap returns immediately, but the revoker that did may still be executing drop_in_place() on the inner data. This can cause a use-after-free when the other revoker's caller proceeds to drop adjacent resources that drop_in_place() still references (e.g., Devres<DmaMappedSgt> racing with SGTable freeing the backing sg_table and pages).
Fix this by adding a Completion. The release callback signals the Completion after revoke() finishes, and Devres::drop() waits for it when it loses the is_available swap. This ensures the wrapped object is fully torn down before Devres::drop() returns.
--- rust/kernel/devres.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
rust/kernel/devres.rs +16 -2
@@ -19,7 +19,8 @@ use crate::{ sync::{ aref::ARef, rcu, - Arc, // + Arc, + Completion, // }, types::ForeignOwnable, };
@@ -28,6 +29,8 @@ use crate::{struct Inner <T :Send > {#[pin] data:Revocable <T >, +#[pin] + revocation:Completion , }/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
@@ -44,6 +47,10 @@ struct Inner<T: Send> {/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource /// anymore. /// +/// When a [`Devres`] is dropped, it is guaranteed that `T` has been fully dropped by the time +/// [`Devres::drop`] returns, even if a concurrent revocation through the release callback is in +/// progress. +/// /// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s /// [`Drop`] implementation. ///
@@ -133,6 +140,7 @@ impl<T: Send> Devres<T> {let inner =Arc ::pin_init (try_pin_init! (Inner { data <-Revocable ::new (data), + revocation <-Completion ::new (), }),GFP_KERNEL , )?;
@@ -171,7 +179,9 @@ impl<T: Send> Devres<T> {// `devm_add_action()`, hence `ptr` must be a valid pointer to `Inner<T>`. let inner =unsafe {Arc ::from_raw (ptr.cast ::<Inner <T >>()) }; - inner.data.revoke (); +if inner.data.revoke () { + inner.revocation.complete_all (); + } }fn remove_action (&self) ->bool {
@@ -266,6 +276,10 @@ impl<T: Send> Drop for Devres<T> {// this additional reference count. drop (unsafe {Arc ::from_raw (Arc ::as_ptr (&self.inner)) }); } + }else { +// The release callback is concurrently revoking; wait for it to finish +// `drop_in_place()` of the wrapped object before returning. + self.inner.revocation.wait_for_completion (); } } }
Greg Kroah-Hartman
maintainerPATCH 447/583 rust: bug: prevent dead_code warning from warn_on!s flags constant
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: FUJITA Tomonori <[email protected]>
[ Upstream commit e66cfc29e0d06fec34c06bb40d4d281f595677b1 ]
Fix the following dead_code warning on some configurations in an atomic development branch:
warning: constant WARN_ON_FLAGS is never used
--> linux/rust/kernel/bug.rs:126:19
|
126 | const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
| ^^^^^^^^^^^^^
|
::: linux/rust/kernel/sync/srcu.rs:106:12
|
106 | if crate::warn_on!(
| ____________-
107 | | // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
108 | | // and `srcu_readers_active()` only checks the active reader count.
109 | | unsafe { bindings::srcu_readers_active(ptr) }
110 | | ) {
| |_________- in this macro invocation
| = note: #[warn(dead_code)] (part of #[warn(unused)]) on by default
= note: this warning originates in the macro crate::warn_on (in Nightly builds, run with -Z macro-backtrace for more info)
The warn_on! macro always defines a WARN_ON_FLAGS constant and hands it to warn_flags!. On configurations where warn_flags! does not reference its flags argument (the LOONGARCH/ARM variant, which only calls WARN_ON(), and the !CONFIG_BUG no-op variant), the constant is left unused and triggers a dead_code warning.
warn_flags! is the macro that accepts (and here discards) the flags argument, so make it responsible for the argument it drops.
Also rename _COND_STR to COND_STR and consume $file for consistency.
--- rust/kernel/bug.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
rust/kernel/bug.rs +9 -1
@@ -98,6 +98,10 @@ macro_rules! warn_flags {#[cfg(all( CONFIG_BUG , any( CONFIG_LOONGARCH , CONFIG_ARM )))] macro_rules! warn_flags { ($flags:expr) => { +if false { + _ = $flags; + } +// SAFETY: It is always safe to call `WARN_ON()`. unsafe { $crate::bindings::WARN_ON (true ) } };
@@ -107,7 +111,11 @@ macro_rules! warn_flags {#[doc(hidden)] #[cfg(any(testlib, not( CONFIG_BUG )))] macro_rules! warn_flags { - ($flags:expr) => {}; + ($flags:expr) => { +if false { + _ = $flags; + } + }; }#[doc(hidden)]
Greg Kroah-Hartman
maintainerPATCH 448/583 mm/hugetlb: defer vmemmap population for bootmem hugepages
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Kiryl Shutsemau (Meta)" <[email protected]>
[ Upstream commit 209e6d9eb13aaf1b6e0fc6f76afc00d055e5ba12 ]
Currently, the vmemmap for bootmem-allocated gigantic pages is populated early in hugetlb_vmemmap_init_early(). However, the zone information is only available after zones are initialized. If it is later discovered that a page spans multiple zones, the HVO mapping must be undone and replaced with a normal mapping using vmemmap_undo_hvo().
Defer the actual vmemmap population to hugetlb_vmemmap_init_late(). At this stage, zones are already initialized, so it can be checked if the page is valid for HVO before deciding how to populate the vmemmap.
This allows us to remove vmemmap_undo_hvo() and the complex logic required to rollback HVO mappings.
In hugetlb_vmemmap_init_late(), if HVO population fails or if the zones are invalid, fall back to a normal vmemmap population.
Postponing population until hugetlb_vmemmap_init_late() also makes zone information available from within vmemmap_populate_hvo().
Stable-dep-of: b1b7c045e808 ("mm/hugetlb: initialize gigantic bootmem hugepage struct pages earlier")
--- include/linux/mm.h | 2 - mm/hugetlb_vmemmap.c | 37 +++++++++++++++++------------------ mm/sparse-vmemmap.c | 53 --------------------------------------------------- 3 files changed, 18 insertions(+), 74 deletions(-)
include/linux/mm.h +0 -2
@@ -4101,8 +4101,6 @@ int vmemmap_populate(unsigned long start struct vmem_altmap *altmap);int vmemmap_populate_hvo (unsigned long start,unsigned long end,int node,unsigned long headsize); -int vmemmap_undo_hvo (unsigned long start,unsigned long end,int node, -unsigned long headsize);void vmemmap_wrprotect_hvo (unsigned long start,unsigned long end,int node,unsigned long headsize);void vmemmap_populate_print_last (void );
mm/hugetlb_vmemmap.c +18 -19
@@ -789,7 +789,6 @@ void __init hugetlb_vmemmap_init_early(i {unsigned long psize, paddr, section_size;unsigned long ns, i, pnum, pfn, nr_pages; -unsigned long start, end;struct huge_bootmem_page *m =NULL ;void *map;
@@ -815,14 +814,6 @@ void __init hugetlb_vmemmap_init_early(i paddr =virt_to_phys (m); pfn =PHYS_PFN (paddr); map =pfn_to_page (pfn); - start = (unsigned long )map; - end = start + nr_pages *sizeof (struct page ); - -if (vmemmap_populate_hvo (start, end, nid, -HUGETLB_VMEMMAP_RESERVE_SIZE ) <0 ) -continue ; - -memmap_boot_pages_add (HUGETLB_VMEMMAP_RESERVE_SIZE /PAGE_SIZE ); pnum =pfn_to_section_nr (pfn); ns = psize / section_size;
@@ -860,28 +851,36 @@ void __init hugetlb_vmemmap_init_late(in h = m->hstate; pfn =PHYS_PFN (phys); nr_pages =pages_per_huge_page (h); + map =pfn_to_page (pfn); + start = (unsigned long )map; + end = start + nr_pages *sizeof (struct page );if (!hugetlb_bootmem_page_zones_valid (nid, m)) {/* * Oops, the hugetlb page spans multiple zones. -* Remove it from the list, and undo HVO. +* Remove it from the list, and populate it normally. */ list_del (&m->list); - map =pfn_to_page (pfn); - - start = (unsigned long )map; - end = start + nr_pages *sizeof (struct page ); - -vmemmap_undo_hvo (start, end, nid, -HUGETLB_VMEMMAP_RESERVE_SIZE ); - nr_mmap = end - start -HUGETLB_VMEMMAP_RESERVE_SIZE ; +vmemmap_populate (start, end, nid,NULL ); + nr_mmap = end - start;memmap_boot_pages_add (DIV_ROUND_UP (nr_mmap,PAGE_SIZE ));memblock_phys_free (phys,huge_page_size (h));continue ; - }else + } + +if (vmemmap_populate_hvo (start, end, nid, +HUGETLB_VMEMMAP_RESERVE_SIZE ) <0 ) { +/* Fallback if HVO population fails */ +vmemmap_populate (start, end, nid,NULL ); + nr_mmap = end - start; + }else { m->flags |=HUGE_BOOTMEM_ZONES_VALID ; + nr_mmap =HUGETLB_VMEMMAP_RESERVE_SIZE ; + } + + memmap_boot_pages_add(DIV_ROUND_UP(nr_mmap, PAGE_SIZE)); } }#endif
mm/sparse-vmemmap.c +0 -53
59 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 449/583 mm/hugetlb: refactor code around vmemmap_walk
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau <[email protected]>
[ Upstream commit c0b495b91a47b6c5ee54cf00e620dbadeb884253 ]
To prepare for removing fake head pages, the vmemmap_walk code is being reworked.
The reuse_page and reuse_addr variables are being eliminated. There will no longer be an expectation regarding the reuse address in relation to the operated range. Instead, the caller will provide head and tail vmemmap pages.
Currently, vmemmap_head and vmemmap_tail are set to the same page, but this will change in the future.
The only functional change is that __hugetlb_vmemmap_optimize_folio() will abandon optimization if memory allocation fails.
Stable-dep-of: b1b7c045e808 ("mm/hugetlb: initialize gigantic bootmem hugepage struct pages earlier")
--- mm/hugetlb_vmemmap.c | 224 ++++++++++++++++++++------------------------------- 1 file changed, 89 insertions(+), 135 deletions(-)
mm/hugetlb_vmemmap.c +89 -135
@@ -24,8 +24,8 @@* * @remap_pte: called for each lowest-level entry (PTE). * @nr_walked: the number of walked pte. -* @reuse_page: the page which is reused for the tail vmemmap pages. -* @reuse_addr: the virtual address of the @reuse_page page. +* @vmemmap_head: the page to be installed as first in the vmemmap range +* @vmemmap_tail: the page to be installed as non-first in the vmemmap range * @vmemmap_pages: the list head of the vmemmap pages that can be freed * or is mapped from. * @flags: used to modify behavior in vmemmap page table walking
@@ -34,11 +34,13 @@struct vmemmap_remap_walk {void (*remap_pte)(pte_t *pte,unsigned long addr,struct vmemmap_remap_walk *walk); +unsigned long nr_walked; -struct page *reuse_page; -unsigned long reuse_addr; +struct page *vmemmap_head; +struct page *vmemmap_tail;struct list_head *vmemmap_pages; +/* Skip the TLB flush when we split the PMD */ #define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)/* Skip the TLB flush when we remap the PTE */
@@ -140,14 +142,7 @@ static int vmemmap_pte_entry(pte_t *pte, {struct vmemmap_remap_walk *vmemmap_walk = walk->private; -/* -* The reuse_page is found 'first' in page table walking before -* starting remapping. -*/ -if (!vmemmap_walk->reuse_page) - vmemmap_walk->reuse_page =pte_page (ptep_get (pte)); -else - vmemmap_walk->remap_pte (pte, addr, vmemmap_walk); + vmemmap_walk->remap_pte (pte, addr, vmemmap_walk); vmemmap_walk->nr_walked++;return 0 ;
@@ -207,18 +202,12 @@ static void free_vmemmap_page_list(strucstatic void vmemmap_remap_pte (pte_t *pte,unsigned long addr,struct vmemmap_remap_walk *walk) { -/* -* Remap the tail pages as read-only to catch illegal write operation -* to the tail pages. -*/ -pgprot_t pgprot =PAGE_KERNEL_RO ;struct page *page =pte_page (ptep_get (pte));pte_t entry;/* Remapping the head page requires r/w */ -if (unlikely (addr == walk->reuse_addr)) { - pgprot =PAGE_KERNEL ; -list_del (&walk->reuse_page->lru); +if (unlikely (walk->nr_walked ==0 && walk->vmemmap_head)) { +list_del (&walk->vmemmap_head->lru);/* * Makes sure that preceding stores to the page contents from
@@ -226,53 +215,50 @@ static void vmemmap_remap_pte(pte_t *pte* write. */ smp_wmb (); + + entry =mk_pte (walk->vmemmap_head,PAGE_KERNEL ); + }else { +/* +* Remap the tail pages as read-only to catch illegal write +* operation to the tail pages. +*/ + entry =mk_pte (walk->vmemmap_tail,PAGE_KERNEL_RO ); } - entry =mk_pte (walk->reuse_page, pgprot);list_add (&page->lru, walk->vmemmap_pages);set_pte_at (&init_mm, addr, pte, entry); } -/* -* How many struct page structs need to be reset. When we reuse the head -* struct page, the special metadata (e.g. page->flags or page->mapping) -* cannot copy to the tail struct page structs. The invalid value will be -* checked in the free_tail_page_prepare(). In order to avoid the message -* of "corrupted mapping in tail page". We need to reset at least 4 (one -* head struct page struct and three tail struct page structs) struct page -* structs. -*/ -#define NR_RESET_STRUCT_PAGE 4 - -static inline void reset_struct_pages (struct page *start) -{ -struct page *from = start +NR_RESET_STRUCT_PAGE ; - -BUILD_BUG_ON (NR_RESET_STRUCT_PAGE *2 >PAGE_SIZE /sizeof (struct page )); -memcpy (start, from,sizeof (*from) *NR_RESET_STRUCT_PAGE ); -} -static void vmemmap_restore_pte (pte_t *pte,unsigned long addr,struct vmemmap_remap_walk *walk) { -pgprot_t pgprot =PAGE_KERNEL ;struct page *page; -void *to; - -BUG_ON (pte_page (ptep_get (pte)) != walk->reuse_page); +struct page *from, *to; page = list_first_entry(walk->vmemmap_pages, struct page, lru);list_del (&page->lru); + +/* +* Initialize tail pages in the newly allocated vmemmap page. +* +* There is folio-scope metadata that is encoded in the first few +* tail pages. +* +* Use the value last tail page in the page with the head page +* to initialize the rest of tail pages. +*/ + from =compound_head ((struct page *)addr) + +PAGE_SIZE /sizeof (struct page ) -1 ; to =page_to_virt (page); -copy_page (to, (void *)walk->reuse_addr); -reset_struct_pages (to); +for (int i =0 ; i <PAGE_SIZE /sizeof (struct page ); i++, to++) + *to = *from;/* * Makes sure that preceding stores to the page contents become visible * before the set_pte_at() write. */ smp_wmb (); -set_pte_at (&init_mm, addr, pte,mk_pte (page, pgprot)); +set_pte_at (&init_mm, addr, pte,mk_pte (page,PAGE_KERNEL )); }/**
@@ -282,33 +268,28 @@ static void vmemmap_restore_pte(pte_t *p* to remap. * @end: end address of the vmemmap virtual address range that we want to * remap. -* @reuse: reuse address. -* * Return: %0 on success, negative error code otherwise. */ -static int vmemmap_remap_split (unsigned long start,unsigned long end, -unsigned long reuse) +static int vmemmap_remap_split (unsigned long start,unsigned long end) {struct vmemmap_remap_walk walk = { .remap_pte =NULL , .flags =VMEMMAP_SPLIT_NO_TLB_FLUSH , }; -/* See the comment in the vmemmap_remap_free(). */ -BUG_ON (start - reuse !=PAGE_SIZE ); - -return vmemmap_remap_range (reuse, end, &walk); +return vmemmap_remap_range (start, end, &walk); }/** * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end) -* to the page which @reuse is mapped to, then free vmemmap -* which the range are mapped to. +* to use @vmemmap_head/tail, then free vmemmap which +* the range are mapped to. * @start: start address of the vmemmap virtual address range that we want * to remap. * @end: end address of the vmemmap virtual address range that we want to * remap. -* @reuse: reuse address. +* @vmemmap_head: the page to be installed as first in the vmemmap range +* @vmemmap_tail: the page to be installed as non-first in the vmemmap range * @vmemmap_pages: list to deposit vmemmap pages to be freed. It is callers * responsibility to free pages. * @flags: modifications to vmemmap_remap_walk flags
@@ -316,69 +297,38 @@ static int vmemmap_remap_split(unsigned* Return: %0 on success, negative error code otherwise. */ static int vmemmap_remap_free (unsigned long start,unsigned long end, -unsigned long reuse, +struct page *vmemmap_head, +struct page *vmemmap_tail,struct list_head *vmemmap_pages,unsigned long flags) {int ret;struct vmemmap_remap_walk walk = { .remap_pte = vmemmap_remap_pte, - .reuse_addr = reuse, + .vmemmap_head = vmemmap_head, + .vmemmap_tail = vmemmap_tail, .vmemmap_pages = vmemmap_pages, .flags = flags, }; -int nid =page_to_nid ((struct page *)reuse); -gfp_t gfp_mask =GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN; -/* -* Allocate a new head vmemmap page to avoid breaking a contiguous -* block of struct page memory when freeing it back to page allocator -* in free_vmemmap_page_list(). This will allow the likely contiguous -* struct page backing memory to be kept contiguous and allowing for -* more allocations of hugepages. Fallback to the currently -* mapped head page in case should it fail to allocate. -*/ - walk.reuse_page =alloc_pages_node (nid, gfp_mask,0 ); -if (walk.reuse_page) { -copy_page (page_to_virt (walk.reuse_page), - (void *)walk.reuse_addr); -list_add (&walk.reuse_page->lru, vmemmap_pages); -memmap_pages_add (1 ); - } + ret =vmemmap_remap_range (start, end, &walk); +if (!ret || !walk.nr_walked) +return ret; + + end = start + walk.nr_walked *PAGE_SIZE ;/* -* In order to make remapping routine most efficient for the huge pages, -* the routine of vmemmap page table walking has the following rules -* (see more details from the vmemmap_pte_range()): -* -* - The range [@start, @end) and the range [@reuse, @reuse + PAGE_SIZE) -* should be continuous. -* - The @reuse address is part of the range [@reuse, @end) that we are -* walking which is passed to vmemmap_remap_range(). -* - The @reuse address is the first in the complete range. -* -* So we need to make sure that @start and @reuse meet the above rules. +* vmemmap_pages contains pages from the previous vmemmap_remap_range() +* call which failed. These are pages which were removed from +* the vmemmap. They will be restored in the following call. */ -BUG_ON (start - reuse !=PAGE_SIZE ); - - ret =vmemmap_remap_range (reuse, end, &walk); -if (ret && walk.nr_walked) { - end = reuse + walk.nr_walked *PAGE_SIZE ; -/* -* vmemmap_pages contains pages from the previous -* vmemmap_remap_range call which failed. These -* are pages which were removed from the vmemmap. -* They will be restored in the following call. -*/ - walk = (struct vmemmap_remap_walk ) { - .remap_pte = vmemmap_restore_pte, - .reuse_addr = reuse, - .vmemmap_pages = vmemmap_pages, - .flags =0 , - }; + walk = (struct vmemmap_remap_walk ) { + .remap_pte = vmemmap_restore_pte, + .vmemmap_pages = vmemmap_pages, + .flags =0 , + }; -vmemmap_remap_range (reuse, end, &walk); - } +vmemmap_remap_range (start, end, &walk);return ret; }
@@ -415,29 +365,24 @@ out:* to remap. * @end: end address of the vmemmap virtual address range that we want to * remap. -* @reuse: reuse address. * @flags: modifications to vmemmap_remap_walk flags * * Return: %0 on success, negative error code otherwise. */ static int vmemmap_remap_alloc (unsigned long start,unsigned long end, -unsigned long reuse,unsigned long flags) +unsigned long flags) {LIST_HEAD (vmemmap_pages);struct vmemmap_remap_walk walk = { .remap_pte = vmemmap_restore_pte, - .reuse_addr = reuse, .vmemmap_pages = &vmemmap_pages, .flags = flags, }; -/* See the comment in the vmemmap_remap_free(). */ -BUG_ON (start - reuse !=PAGE_SIZE ); -if (alloc_vmemmap_page_list (start, end, &vmemmap_pages))return -ENOMEM ; -return vmemmap_remap_range (reuse, end, &walk); +return vmemmap_remap_range (start, end, &walk); }DEFINE_STATIC_KEY_FALSE (hugetlb_optimize_vmemmap_key);
@@ -454,8 +399,7 @@ static int __hugetlb_vmemmap_restore_fol struct folio *folio, unsigned long flags) { int ret; -unsigned long vmemmap_start = (unsigned long )&folio->page, vmemmap_end; -unsigned long vmemmap_reuse; +unsigned long vmemmap_start, vmemmap_end;VM_WARN_ON_ONCE_FOLIO (!folio_test_hugetlb (folio), folio);VM_WARN_ON_ONCE_FOLIO (folio_ref_count (folio), folio);
@@ -466,18 +410,18 @@ static int __hugetlb_vmemmap_restore_folif (flags &VMEMMAP_SYNCHRONIZE_RCU )synchronize_rcu (); + vmemmap_start = (unsigned long )&folio->page; vmemmap_end = vmemmap_start +hugetlb_vmemmap_size (h); - vmemmap_reuse = vmemmap_start; + vmemmap_start +=HUGETLB_VMEMMAP_RESERVE_SIZE ;/* * The pages which the vmemmap virtual address range [@vmemmap_start, -* @vmemmap_end) are mapped to are freed to the buddy allocator, and -* the range is mapped to the page which @vmemmap_reuse is mapped to. +* @vmemmap_end) are mapped to are freed to the buddy allocator. * When a HugeTLB page is freed to the buddy allocator, previously * discarded vmemmap pages must be allocated and remapping. */ - ret =vmemmap_remap_alloc (vmemmap_start, vmemmap_end, vmemmap_reuse, flags); + ret =vmemmap_remap_alloc (vmemmap_start, vmemmap_end, flags);if (!ret) {folio_clear_hugetlb_vmemmap_optimized (folio);static_branch_dec (&hugetlb_optimize_vmemmap_key);
@@ -565,9 +509,9 @@ static int __hugetlb_vmemmap_optimize_fo struct list_head *vmemmap_pages, unsigned long flags) { - int ret = 0; -unsigned long vmemmap_start = (unsigned long )&folio->page, vmemmap_end; -unsigned long vmemmap_reuse; +unsigned long vmemmap_start, vmemmap_end; +struct page *vmemmap_head, *vmemmap_tail; +int nid, ret =0 ;VM_WARN_ON_ONCE_FOLIO (!folio_test_hugetlb (folio), folio);VM_WARN_ON_ONCE_FOLIO (folio_ref_count (folio), folio);
@@ -592,18 +536,30 @@ static int __hugetlb_vmemmap_optimize_fo*/ folio_set_hugetlb_vmemmap_optimized (folio); + nid =folio_nid (folio); + vmemmap_head =alloc_pages_node (nid,GFP_KERNEL ,0 ); +if (!vmemmap_head) { + ret = -ENOMEM ; + goto out; + } + +copy_page (page_to_virt (vmemmap_head), folio); +list_add (&vmemmap_head->lru, vmemmap_pages); +memmap_pages_add (1 ); + + vmemmap_tail = vmemmap_head; + vmemmap_start = (unsigned long )&folio->page; vmemmap_end = vmemmap_start +hugetlb_vmemmap_size (h); - vmemmap_reuse = vmemmap_start; - vmemmap_start +=HUGETLB_VMEMMAP_RESERVE_SIZE ;/* -* Remap the vmemmap virtual address range [@vmemmap_start, @vmemmap_end) -* to the page which @vmemmap_reuse is mapped to. Add pages previously -* mapping the range to vmemmap_pages list so that they can be freed by -* the caller. +* Remap the vmemmap virtual address range [@vmemmap_start, @vmemmap_end). +* Add pages previously mapping the range to vmemmap_pages list so that +* they can be freed by the caller. */ - ret =vmemmap_remap_free (vmemmap_start, vmemmap_end, vmemmap_reuse, + ret =vmemmap_remap_free (vmemmap_start, vmemmap_end, + vmemmap_head, vmemmap_tail, vmemmap_pages, flags); +out:if (ret) {static_branch_dec (&hugetlb_optimize_vmemmap_key);folio_clear_hugetlb_vmemmap_optimized (folio);
@@ -632,21 +588,19 @@ void hugetlb_vmemmap_optimize_folio(consstatic int hugetlb_vmemmap_split_folio (const struct hstate *h,struct folio *folio) { -unsigned long vmemmap_start = (unsigned long )&folio->page, vmemmap_end; -unsigned long vmemmap_reuse; +unsigned long vmemmap_start, vmemmap_end;if (!vmemmap_should_optimize_folio (h, folio))return 0 ; + vmemmap_start = (unsigned long )&folio->page; vmemmap_end = vmemmap_start +hugetlb_vmemmap_size (h); - vmemmap_reuse = vmemmap_start; - vmemmap_start +=HUGETLB_VMEMMAP_RESERVE_SIZE ;/* * Split PMDs on the vmemmap virtual address range [@vmemmap_start, * @vmemmap_end] */ -return vmemmap_remap_split (vmemmap_start, vmemmap_end, vmemmap_reuse); +return vmemmap_remap_split (vmemmap_start, vmemmap_end); } static void __hugetlb_vmemmap_optimize_folios(struct hstate *h,
Greg Kroah-Hartman
maintainerPATCH 450/583 mm/hugetlb: initialize gigantic bootmem hugepage struct pages earlier
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
[ Upstream commit b1b7c045e808c761b1cc8c19b3040fadedda3fef ]
Gigantic bootmem HugeTLB pages are currently initialized from hugetlb_init(), but page_alloc_init_late() runs earlier and walks pageblocks to determine zone contiguity.
If a bootmem HugeTLB region is marked noinit, set_zone_contiguous() can observe still-uninitialized struct pages through __pageblock_pfn_to_page(). This may not trigger an immediate failure, but it can make set_zone_contiguous() compute the wrong zone contiguity state. If extra poisoned-page checks are added in this path, such as PF_POISONED_CHECK() in page_zone_id(), it can also trigger an early boot panic.
Initialize gigantic bootmem HugeTLB struct pages from page_alloc_init_late(), before zone contiguity is evaluated, so later page allocator setup only sees valid struct page state. This also makes the initialization order more natural, as struct pages should be initialized before later code inspects them.
--- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 5 ++--- mm/mm_init.c | 1 + 3 files changed, 8 insertions(+), 3 deletions(-)
include/linux/hugetlb.h +5 -0
@@ -173,6 +173,7 @@ struct address_space *hugetlb_folio_mappextern int sysctl_hugetlb_shm_group;extern struct list_head huge_boot_pages[MAX_NUMNODES ]; +void hugetlb_bootmem_struct_page_init (void );void hugetlb_bootmem_alloc (void );bool hugetlb_bootmem_allocated (void );extern nodemask_t hugetlb_bootmem_nodes;
@@ -1316,6 +1317,10 @@ static inline bool hugetlb_bootmem_alloc {return false; } + +static inline void hugetlb_bootmem_struct_page_init (void ) +{ +}#endif /* CONFIG_HUGETLB_PAGE */ static inline spinlock_t *huge_pte_lock(struct hstate *h,
mm/hugetlb.c +2 -3
@@ -3453,7 +3453,7 @@ static void __init gather_bootmem_preallgather_bootmem_prealloc_node (nid); } -static void __init gather_bootmem_prealloc (void ) +void __init hugetlb_bootmem_struct_page_init (void ) {struct padata_mt_job job = { .thread_fn = gather_bootmem_prealloc_parallel,
@@ -3650,7 +3650,7 @@ static unsigned long __init hugetlb_page* - For gigantic pages, this is called early in the boot process and * pages are allocated from memblock allocated or something similar. * Gigantic pages are actually added to pools later with the routine -* gather_bootmem_prealloc. +* hugetlb_bootmem_struct_page_init. * - For non-gigantic pages, this is called later in the boot process after * all of mm is up and functional. Pages are allocated from buddy and * then added to hugetlb pools.
@@ -4717,7 +4717,6 @@ static int __init hugetlb_init(void)hugetlb_cma_check ();hugetlb_init_hstates (); -gather_bootmem_prealloc ();report_hugepages ();hugetlb_sysfs_init ();
mm/mm_init.c +1 -0
@@ -2357,6 +2357,7 @@ void __init page_alloc_init_late(void)/* Reinit limits that are based on free pages after the kernel is up */ files_maxfiles_init ();#endif +hugetlb_bootmem_struct_page_init ();/* Accounting of total+free memory is stable at this point. */ mem_init_print_info ();
Greg Kroah-Hartman
maintainerPATCH 451/583 mm/mglru: use the common routine for dirty/writeback reactivation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kairui Song <[email protected]>
[ Upstream commit 75d4c3f5fb980de1b620adede47e43dff4d6a5f3 ]
Currently MGLRU will move the dirty writeback folios to the second oldest gen instead of reactivate them like the classical LRU. This might help to reduce the LRU contention as it skipped the isolation. But as a result we will see these folios at the LRU tail more frequently leading to inefficient reclaim.
Besides, the dirty / writeback check after isolation in shrink_folio_list is more accurate and covers more cases. So instead, just drop the special handling for dirty writeback, use the common routine and re-activate it like the classical LRU.
This should in theory improve the scan efficiency. These folios will be rotated back to LRU tail once writeback is done so there is no risk of hotness inversion. And now each reclaim loop will have a higher success rate. This also prepares for unifying the writeback and throttling mechanism with classical LRU, we keep these folios far from tail so detecting the tail batch will have a similar pattern with classical LRU.
The micro optimization that avoids LRU contention by skipping the isolation is gone, which should be fine. Compared to IO and writeback cost, the isolation overhead is trivial.
And using the common routine also keeps the folio's referenced bits (tier bits), which could improve metrics in the long term. Also no more need to clean reclaim bit as the common routine will make use of it.
Note the common routine updates a few throttling and writeback counters, which are not used, and never have been for the MGLRU case. We will start making use of these in later commits.
Stable-dep-of: f7e698e326b2 ("mm/mglru: fix and remove redundant unevictable folio handling")
--- mm/vmscan.c | 19 ------------------- 1 file changed, 19 deletions(-)
mm/vmscan.c +0 -19
@@ -4469,7 +4469,6 @@ static bool sort_folio(struct lruvec *lr int tier_idx) {bool success; -bool dirty, writeback;int gen =folio_lru_gen (folio);int type =folio_is_file_lru (folio);int zone =folio_zonenum (folio);
@@ -4519,21 +4518,6 @@ static bool sort_folio(struct lruvec *lrreturn true; } - dirty =folio_test_dirty (folio); - writeback =folio_test_writeback (folio); -if (type ==LRU_GEN_FILE && dirty) { - sc->nr.file_taken += delta; -if (!writeback) - sc->nr.unqueued_dirty += delta; - } - -/* waiting for writeback */ -if (writeback || (type ==LRU_GEN_FILE && dirty)) { - gen =folio_inc_gen (lruvec, folio, true); -list_move (&folio->lru, &lrugen->folios[gen][type][zone]); -return true; - } -return false; }
@@ -4561,9 +4545,6 @@ static bool isolate_folio(struct lruvecif (!folio_test_referenced (folio))set_mask_bits (&folio->flags.f,LRU_REFS_MASK ,0 ); -/* for shrink_folio_list() */ -folio_clear_reclaim (folio); - success =lru_gen_del_folio (lruvec, folio, true);VM_WARN_ON_ONCE_FOLIO (!success, folio);
Greg Kroah-Hartman
maintainerPATCH 452/583 mm/mglru: fix and remove redundant unevictable folio handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kairui Song <[email protected]>
[ Upstream commit f7e698e326b239a91ea15844817551921209e826 ]
sort_folio() has a shortcut for moving folios that are no longer evictable but are still sitting on a generation list. However, this shortcut is buggy. It does not follow the PG_lru usage convention, and it has a more serious issue.
Unevictable folios are not threaded on lists[LRU_UNEVICTABLE], so that folio->lru can be reused to hold folio->mlock_count (see the comment in lruvec_init()). Hence lruvec_add_folio() skips the list_add() for them, and every other place that turns a folio unevictable initialises mlock_count explicitly: lru_add() sets it to 0, __mlock_folio() and __mlock_new_folio() set it to !!folio_test_mlocked(folio). sort_folio() sets nothing, and the lru_gen_del_folio() right above it may have already poisoned folio->lru via list_del(), so mlock_count ends up aliasing LIST_POISON2, which reads as 0x122, i.e. 290. The result is user visible. On munlock, __munlock_folio() decrements that bogus count, finds it still non-zero and bails out before clearing PG_mlocked, so the folio remains unevictable and the Mlocked accounting stays inflated until the folio is freed.
The shortcut also touches the LRU flags in the wrong order. It calls lru_gen_del_folio() while PG_lru is still set, so a concurrent folio_test_clear_lru() (e.g. compaction, folio_isolate_lru()) can succeed on a folio that has already been taken off the generation list, which may lead to unexpected behavior.
So fix it by isolating them as common folios and letting the generic shrink path cull them. This matches the classical LRU behavior, and there should be no visible effect on the generic eviction or isolation behavior.
There is no performance concern either, such a folio goes through this once, and then it is off the generation lists for good.
--- mm/vmscan.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-)
mm/vmscan.c +5 -14
@@ -4468,7 +4468,6 @@ void lru_gen_soft_reclaim(struct mem_cgrstatic bool sort_folio (struct lruvec *lruvec,struct folio *folio,struct scan_control *sc,int tier_idx) { -bool success;int gen =folio_lru_gen (folio);int type =folio_is_file_lru (folio);int zone =folio_zonenum (folio);
@@ -4480,15 +4479,9 @@ static bool sort_folio(struct lruvec *lrVM_WARN_ON_ONCE_FOLIO (gen >=MAX_NR_GENS , folio); -/* unevictable */ -if (!folio_evictable (folio)) { - success =lru_gen_del_folio (lruvec, folio, true); -VM_WARN_ON_ONCE_FOLIO (!success, folio); -folio_set_unevictable (folio); -lruvec_add_folio (lruvec, folio); -__count_vm_events (UNEVICTABLE_PGCULLED , delta); -return true; - } +/* unevictable: let it through and the generic path will cull it */ +if (!folio_evictable (folio)) +return false;/* promoted */ if (gen !=lru_gen_from_seq (lrugen->min_seq[type])) {
@@ -4738,11 +4731,9 @@ retry:list_for_each_entry_safe_reverse (folio, next, &list, lru) {DEFINE_MIN_SEQ (lruvec); -if (!folio_evictable (folio)) { -list_del (&folio->lru); -folio_putback_lru (folio); +/* move_folios_to_lru() culls unevictable folios via folio_putback_lru() */ +if (!folio_evictable (folio))continue ; - }/* retry folios that may have missed folio_rotate_reclaimable() */ if (!skip_retry && !folio_test_active(folio) && !folio_mapped(folio) &&
Greg Kroah-Hartman
maintainerPATCH 453/583 mm/rmap: use huge_ptep_get() in try_to_migrate_one()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dev Jain <[email protected]>
[ Upstream commit 9be12ffa78cd087d41086f937a156435d4e588e9 ]
try_to_migrate_one() is used by folio migration to replace a present mapping with a migration entry. For hugetlb folios, page_vma_mapped_walk() returns the pte pointer to the hugetlb folio in pvmw.pte, but the code reads the huge pte entry with ptep_get().
On arches which provide their own huge_ptep_get() to dereference a huge pte pointer, accessing via ptep_get() would cause pte_pfn(), pte_present() etc to misbehave.
It is not clear whether this has a trivially visible effect to userspace.
Use huge_ptep_get() to dereference a huge pte pointer.
Commit a98a2f0c8ce1 copied the bug from try_to_unmap_one into try_to_migrate_one.
--- mm/rmap.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
mm/rmap.c +9 -6
@@ -2384,20 +2384,23 @@ static bool try_to_migrate_one(struct fo/* Unexpected PMD-mapped THP? */ VM_BUG_ON_FOLIO (!pvmw.pte, folio); -/* -* Handle PFN swap PTEs, such as device-exclusive ones, that -* actually map pages. -*/ - pteval =ptep_get (pvmw.pte); + address = pvmw.address; +if (folio_test_hugetlb (folio)) + pteval =huge_ptep_get (mm, address, pvmw.pte); +else + pteval =ptep_get (pvmw.pte);if (likely (pte_present (pteval))) { pfn =pte_pfn (pteval); }else { +/* +* Handle PFN swap PTEs, such as device-exclusive ones, +* that actually map pages. +*/ pfn =swp_offset_pfn (pte_to_swp_entry (pteval));VM_WARN_ON_FOLIO (folio_test_hugetlb (folio), folio); } subpage =folio_page (folio, pfn -folio_pfn (folio)); - address = pvmw.address; anon_exclusive =folio_test_anon (folio) &&PageAnonExclusive (subpage);
Greg Kroah-Hartman
maintainerPATCH 454/583 mm/slab: move and refactor __kmem_cache_alias()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vlastimil Babka <[email protected]>
[ Upstream commit 8598351edc42f38d2a1eaed9abca39c98e7b0bbf ]
Move __kmem_cache_alias() to slab_common.c since it's called by __kmem_cache_create_args() and calls find_mergeable() that both are in this file. We can remove two slab.h declarations and make them static. Instead declare sysfs_slab_alias() from slub.c so that __kmem_cache_alias() can keep calling it.
Add args parameter to __kmem_cache_alias() and find_mergeable() instead of align and ctor. With that we can also move the checks for usersize and sheaf_capacity there from __kmem_cache_create_args() and make the result more symmetric with slab_unmergeable().
No functional changes intended.
Stable-dep-of: 7e98f8563956 ("mm/slub: fix missing debugfs entries for caches created before sysfs init")
--- mm/slab.h | 8 +++----- mm/slab_common.c | 44 +++++++++++++++++++++++++++++++++++++------- mm/slub.c | 30 +----------------------------- 3 files changed, 41 insertions(+), 41 deletions(-)
mm/slab.h +3 -5
@@ -299,9 +299,12 @@ struct kmem_cache {#define SLAB_SUPPORTS_SYSFS 1void sysfs_slab_unlink (struct kmem_cache *s);void sysfs_slab_release (struct kmem_cache *s); +int sysfs_slab_alias (struct kmem_cache *s,const char *name);#else static inline void sysfs_slab_unlink (struct kmem_cache *s) { }static inline void sysfs_slab_release (struct kmem_cache *s) { } +static inline int sysfs_slab_alias (struct kmem_cache *s,const char *name) + {return 0 ; }#endif void *fixup_red_left (struct kmem_cache *s,void *p);
@@ -422,11 +425,6 @@ extern void create_boot_cache(struct kme unsigned int useroffset, unsigned int usersize);int slab_unmergeable (struct kmem_cache *s); -struct kmem_cache *find_mergeable (unsigned size,unsigned align, -slab_flags_t flags,const char *name,void (*ctor)(void *)); -struct kmem_cache * -__kmem_cache_alias (const char *name,unsigned int size,unsigned int align, -slab_flags_t flags,void (*ctor)(void *));slab_flags_t kmem_cache_flags (slab_flags_t flags,const char *name);
mm/slab_common.c +37 -7
74 lines not on this page · raw message
mm/slub.c +1 -29
@@ -351,11 +351,8 @@ enum track_item { TRACK_ALLOC, TRACK_FRE#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add (struct kmem_cache *); -static int sysfs_slab_alias (struct kmem_cache *,const char *);#else static inline int sysfs_slab_add (struct kmem_cache *s) {return 0 ; } -static inline int sysfs_slab_alias (struct kmem_cache *s,const char *p) - {return 0 ; }#endif #if defined(CONFIG_DEBUG_FS ) && defined(CONFIG_SLUB_DEBUG )
@@ -8533,31 +8530,6 @@ void __init kmem_cache_init_late(void)#endif } -struct kmem_cache * -__kmem_cache_alias (const char *name,unsigned int size,unsigned int align, -slab_flags_t flags,void (*ctor)(void *)) -{ -struct kmem_cache *s; - - s =find_mergeable (size, align, flags, name, ctor); -if (s) { -if (sysfs_slab_alias (s, name)) -pr_err ("SLUB: Unable to add cache alias %s to sysfs\n" , - name); - - s->refcount++; - -/* -* Adjust the object sizes so that we clear -* the complete object on kzalloc. -*/ - s->object_size =max (s->object_size, size); - s->inuse =max (s->inuse,ALIGN (size,sizeof (void *))); - } - -return s; -} -int do_kmem_cache_create (struct kmem_cache *s,const char *name,unsigned int size,struct kmem_cache_args *args,slab_flags_t flags)
@@ -9790,7 +9762,7 @@ struct saved_alias {static struct saved_alias *alias_list; -static int sysfs_slab_alias (struct kmem_cache *s,const char *name) +int sysfs_slab_alias (struct kmem_cache *s,const char *name) {struct saved_alias *al;
Greg Kroah-Hartman
maintainerPATCH 455/583 mm/slub: fix missing debugfs entries for caches created before sysfs init
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Xiasong <[email protected]>
[ Upstream commit 7e98f856395618011c517f767fb80ac3fe90de2b ]
slab_debugfs_init() creates the slab debugfs root at device initcall time, while slab_sysfs_init() moves slab_state to FULL at late initcall time. SLAB_STORE_USER caches created in this window miss their debugfs entries because do_kmem_cache_create() skips debugfs_slab_add() when slab_state <= UP. This was observed with MPTCP's request_sock_subflow_v6 cache, whose slab debugfs directory was missing.
The affected window is:
slab_debugfs_init()
slab_debugfs_root = debugfs_create_dir(...)
list_for_each_entry(s, &slab_caches, list)
debugfs_slab_add(s)
kmem_cache_create(..., SLAB_STORE_USER, ...)
do_kmem_cache_create()
if (slab_state <= UP)
return without debugfs entries
slab_sysfs_init()
slab_state = FULL
Initialize the debugfs root and add debugfs entries while holding slab_mutex, walking slab_caches exactly once and handling both sysfs and debugfs entries in the same pass. This gives the sysfs and debugfs initialization an explicit order and prevents caches from being created between the debugfs scan and slab_state reaching FULL.
Gate the new slab_late_init() on either sysfs or debugfs being enabled, with the slab_kset creation and alias_list processing factored into helpers that have empty no-sysfs variants, as suggested by Vlastimil Babka. On slab_kset_init() failure, slab_state stays below FULL so kmem_cache_create() keeps taking the early-boot path, matching prior behavior.
Guard debugfs_slab_release() against an uninitialized debugfs root, since the root is now created later and a cache may be released before it exists.
--- mm/slub.c | 76 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 28 deletions(-)
mm/slub.c +48 -28
@@ -351,14 +351,20 @@ enum track_item { TRACK_ALLOC, TRACK_FRE#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add (struct kmem_cache *); +static int __init slab_kset_init (void ); +static void __init slab_sysfs_process_aliases (void );#else static inline int sysfs_slab_add (struct kmem_cache *s) {return 0 ; } +static inline int slab_kset_init (void ) {return 0 ; } +static inline void slab_sysfs_process_aliases (void ) { }#endif #if defined(CONFIG_DEBUG_FS ) && defined(CONFIG_SLUB_DEBUG )static void debugfs_slab_add (struct kmem_cache *); +static void __init slab_debugfs_root_init (void );#else static inline void debugfs_slab_add (struct kmem_cache *s) { } +static inline void slab_debugfs_root_init (void ) { }#endif enum stat_item {
@@ -9791,28 +9797,20 @@ int sysfs_slab_alias(struct kmem_cache *return 0 ; } -static int __init slab_sysfs_init (void ) +static int __init slab_kset_init (void ) { -struct kmem_cache *s; -int err; - -mutex_lock (&slab_mutex); - slab_kset =kset_create_and_add ("slab" ,NULL , kernel_kobj);if (!slab_kset) { -mutex_unlock (&slab_mutex);pr_err ("Cannot register slab subsystem.\n" );return -ENOMEM ; } - slab_state =FULL ; +return 0 ; +} -list_for_each_entry (s, &slab_caches, list) { - err =sysfs_slab_add (s); -if (err) -pr_err ("SLUB: Unable to add boot slab %s to sysfs\n" , - s->name); - } +static void __init slab_sysfs_process_aliases (void ) +{ +int err;while (alias_list) {struct saved_alias *al = alias_list;
@@ -9824,12 +9822,41 @@ static int __init slab_sysfs_init(void) al->name); kfree(al); } +} +#endif /* SLAB_SUPPORTS_SYSFS */ + +#if defined(SLAB_SUPPORTS_SYSFS ) || \ + (defined(CONFIG_SLUB_DEBUG ) && defined(CONFIG_DEBUG_FS )) +static int __init slab_late_init (void ) +{ +struct kmem_cache *s; +int err; +mutex_lock (&slab_mutex); + + err =slab_kset_init (); +if (err) + goto out; + +slab_debugfs_root_init (); + slab_state =FULL ; + +list_for_each_entry (s, &slab_caches, list) { +if (sysfs_slab_add (s)) +pr_err ("SLUB: Unable to add boot slab %s to sysfs\n" , + s->name); + +if (s->flags &SLAB_STORE_USER ) +debugfs_slab_add (s); + } + +slab_sysfs_process_aliases (); +out:mutex_unlock (&slab_mutex); -return 0 ; +return err; } -late_initcall (slab_sysfs_init); -#endif /* SLAB_SUPPORTS_SYSFS */ +late_initcall (slab_late_init); +#endif #if defined(CONFIG_SLUB_DEBUG ) && defined(CONFIG_DEBUG_FS )static int slab_debugfs_show (struct seq_file *seq,void *v)
@@ -10021,23 +10048,16 @@ static void debugfs_slab_add(struct kmemvoid debugfs_slab_release (struct kmem_cache *s) { +if (unlikely (!slab_debugfs_root)) +return ; +debugfs_lookup_and_remove (s->name, slab_debugfs_root); } -static int __init slab_debugfs_init (void ) +static void __init slab_debugfs_root_init (void ) { -struct kmem_cache *s; - slab_debugfs_root =debugfs_create_dir ("slab" ,NULL ); - -list_for_each_entry (s, &slab_caches, list) -if (s->flags &SLAB_STORE_USER ) -debugfs_slab_add (s); - -return 0 ; - } -__initcall (slab_debugfs_init);#endif /* * The /proc/slabinfo ABI
Greg Kroah-Hartman
maintainerPATCH 456/583 xen/balloon: improve accuracy of initial balloon target for dom0
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roger Pau Monne <[email protected]>
[ Upstream commit b13cd24c15d74f6dfcddf0d53ae6efb58d3f0461 ]
The dom0 balloon target set by the toolstack is the value returned by XENMEM_current_reservation. Do the same in the kernel balloon driver and set the current allocation to the value returned by XENMEM_current_reservation. On my test system this causes the kernel balloon driver target to exactly match the value set by the toolstack in xenstore.
Note this approach can be used by both PV and PVH dom0s, as the toolstack always uses XENMEM_current_reservation to set the initial target regardless of the dom0 type.
Message-ID: <[email protected]>
Stable-dep-of: 5d00a11d484e ("x86/xen: fix init of balloon stats again")
--- drivers/xen/balloon.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
drivers/xen/balloon.c +17 -10
@@ -724,7 +724,8 @@ static int __init balloon_add_regions(vostatic int __init balloon_init (void ) {struct task_struct *task; -unsigned long current_pages; +long current_pages =0 ; +domid_t domid =DOMID_SELF ;int rc;if (!xen_domain ())
@@ -732,15 +733,21 @@ static int __init balloon_init(void)pr_info ("Initialising balloon driver\n" ); -if (xen_pv_domain ()) { -if (xen_released_pages >= xen_start_info->nr_pages) - goto underflow; - current_pages =min (xen_start_info->nr_pages - - xen_released_pages, max_pfn); - }else { -if (xen_unpopulated_pages >=get_num_physpages ()) - goto underflow; - current_pages =get_num_physpages () - xen_unpopulated_pages; +if (xen_initial_domain ()) + current_pages =HYPERVISOR_memory_op (XENMEM_current_reservation, + &domid); +if (current_pages <=0 ) { +if (xen_pv_domain ()) { +if (xen_released_pages >= xen_start_info->nr_pages) + goto underflow; + current_pages =min (xen_start_info->nr_pages - + xen_released_pages, max_pfn); + }else { +if (xen_unpopulated_pages >=get_num_physpages ()) + goto underflow; + current_pages =get_num_physpages () - + xen_unpopulated_pages; + } } balloon_stats.current_pages = current_pages;
Greg Kroah-Hartman
maintainerPATCH 457/583 x86/xen: fix init of balloon stats again
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roger Pau Monne <[email protected]>
[ Upstream commit 5d00a11d484e7b31a358cd81afebccb4602dbe8c ]
The handling of extra memory regions done in balloon_add_regions() is not correct for PV guests, since the initial target is set to reflect the real memory the system has, not what's described on the memory map, which can be higher if memory != maxmem.
Introduce separate logic for addition vs subtraction in balloon_add_regions() and handle extra regions correctly by adding them to the total amount of pages, instead of subtracting from the current and target pages amounts.
In the common case PV domU/dom0 and PVH dom0 will use the addition path, since the initial target reflects the real assigned memory. HVM and PVH domUs use the subtraction path, since the target is set based on the amount of memory reported in the memory map, without accounting for released regions.
Message-ID: <[email protected]>
--- drivers/xen/balloon.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
drivers/xen/balloon.c +19 -10
@@ -679,7 +679,7 @@ void xen_free_ballooned_pages(unsigned i }EXPORT_SYMBOL (xen_free_ballooned_pages); -static int __init balloon_add_regions (void ) +static int __init balloon_add_regions (bool append) {unsigned long start_pfn, pages;unsigned long pfn, extra_pfn_end;
@@ -703,19 +703,26 @@ static int __init balloon_add_regions(voballoon_append (pfn_to_page (pfn));/* -* Extra regions are accounted for in the physmap, but need -* decreasing from current_pages and target_pages to balloon -* down the initial allocation, because they are already -* accounted for in total_pages. +* There are two different use-cases depending on how the +* initial memory target is fetched. For PVH dom0 and PV the +* target is usually set to reflect the domain assigned memory, +* and hence extra regions need adding. +* +* OTOH for HVM and PVH domU the target is set to the amount of +* RAM reported in the memory map, and hence extra regions need +* subtracting to reflect the real memory usage. */ pages = extra_pfn_end - start_pfn; -if (pages >= balloon_stats.current_pages || - pages >= balloon_stats.target_pages) { +if (append) { + balloon_stats.total_pages += pages; + }else if (pages >= balloon_stats.current_pages || + pages >= balloon_stats.target_pages) {WARN (1 ,"Extra pages underflow current target" );return -ERANGE ; + }else { + balloon_stats.current_pages -= pages; + balloon_stats.target_pages -= pages; } - balloon_stats.current_pages -= pages; - balloon_stats.target_pages -= pages; }return 0 ;
@@ -726,6 +733,7 @@ static int __init balloon_init(void)struct task_struct *task;long current_pages =0 ;domid_t domid =DOMID_SELF ; +bool append = true;int rc;if (!xen_domain ())
@@ -745,6 +753,7 @@ static int __init balloon_init(void) }else {if (xen_unpopulated_pages >=get_num_physpages ()) goto underflow; + append = false; current_pages =get_num_physpages () - xen_unpopulated_pages; }
@@ -767,7 +776,7 @@ static int __init balloon_init(void)register_sysctl_init ("xen/balloon" , balloon_table);#endif - rc =balloon_add_regions (); + rc =balloon_add_regions (append);if (rc)return rc;
Greg Kroah-Hartman
maintainerPATCH 458/583 mm/page_alloc: dont spin_trylock() in NMI on UP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Brendan Jackman <[email protected]>
[ Upstream commit 3105ae628fb785d48b49256468be4f21a7b3cfc0 ]
Patch series "mm/page_alloc: fixes for free_pages_nolock() on RT/UP".
Pre-existing bugs found by Sashiko during review of this other series:
https://lore.kernel.org/all/[email protected]/
I have not reproduced these bugs, and I suspect there is no real-world user that is affected by them.
This patch (of 2):
As noted in can_spin_trylock(), using this is unsafe in this context. commit 620b46ed6ae17 ("mm/page_alloc: return NULL early from alloc_frozen_pages_nolock() in NMI on UP") fixed this on the alloc side but missed the free side.
Impact: If BPF programs using these features in NMI (probably tracing) are present on non-SMP builds this might crash the kernel and is probably exploitable by local attackers for privilege escalation.
--- mm/page_alloc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
mm/page_alloc.c +4 -2
@@ -2965,8 +2965,10 @@ static void __free_frozen_pages(struct p migratetype =MIGRATE_MOVABLE ; } -if (unlikely ((fpi_flags &FPI_TRYLOCK ) &&IS_ENABLED (CONFIG_PREEMPT_RT ) - && (in_nmi () ||in_hardirq ()))) { +if (unlikely ((fpi_flags &FPI_TRYLOCK ) && + ((IS_ENABLED (CONFIG_PREEMPT_RT ) && + (in_nmi () ||in_hardirq ())) || + (!IS_ENABLED (CONFIG_SMP ) &&in_nmi ())))) {add_page_to_zone_llist (zone, page, order);return ; }
Greg Kroah-Hartman
maintainerPATCH 459/583 USB: gadget: ffs: fix mm lifetime handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabriel Prostitis <[email protected]>
[ Upstream commit 5eb5c72c72fef76cb765ef1669b62b6a3ba1bfc8 ]
io_data stores a pointer to the submitting task's mm_struct, but does not currently hold a reference to it while async requests are pending.
This can result in a use-after-free if the task exits before completion handling finishes.
Take a reference with mmgrab() when queuing the read request and release it with mmdrop() on request completion.
Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path")
--- drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-)
drivers/usb/gadget/function/f_fs.c +20 -7
@@ -863,9 +863,15 @@ static void ffs_user_copy_worker(structbool kiocb_has_eventfd = io_data->kiocb->ki_flags &IOCB_EVENTFD ;if (io_data->read && ret >0 ) { -kthread_use_mm (io_data->mm); - ret =ffs_copy_to_iter (io_data->buf, ret, &io_data->data); -kthread_unuse_mm (io_data->mm); +if (mmget_not_zero (io_data->mm)) { +kthread_use_mm (io_data->mm); + ret =ffs_copy_to_iter (io_data->buf, ret, &io_data->data); +kthread_unuse_mm (io_data->mm); +mmput (io_data->mm); + }else { + ret = -EFAULT ; + } +mmdrop (io_data->mm); } io_data->kiocb->ki_complete (io_data->kiocb, ret);
@@ -1246,16 +1252,20 @@ static ssize_t ffs_epfile_write_iter(str kiocb->private = p; -if (p->aio) +if (p->aio) { +mmgrab (p->mm);kiocb_set_cancel_fn (kiocb, ffs_aio_cancel); + } res =ffs_epfile_io (kiocb->ki_filp, p);if (res == -EIOCBQUEUED )return res; -if (p->aio) +if (p->aio) { +mmdrop (p->mm);kfree (p); -else + }else { *from = p->data; + }return res; }
@@ -1290,14 +1300,17 @@ static ssize_t ffs_epfile_read_iter(stru kiocb->private = p; -if (p->aio) +if (p->aio) { +mmgrab (p->mm);kiocb_set_cancel_fn (kiocb, ffs_aio_cancel); + } res =ffs_epfile_io (kiocb->ki_filp, p);if (res == -EIOCBQUEUED )return res;if (p->aio) { +mmdrop (p->mm);kfree (p->to_free);kfree (p); }else {
Greg Kroah-Hartman
maintainerPATCH 460/583 usb: gadget: f_fs: Fix Use-After-Free in AIO error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Neill Kapron <[email protected]>
[ Upstream commit e78dcb1f7ec271449c54984dc90c62a5ba272de7 ]
In ffs_epfile_write_iter() and ffs_epfile_read_iter(), when ffs_epfile_io() fails with an error other than -EIOCBQUEUED, the io_data structure (p) is freed. However, for AIO operations, the kiocb cancel function was already armed and kiocb->private was set to p.
If a concurrent cancel operation (such as sys_io_cancel()) executes after ffs_epfile_io() fails but before the function frees p, a Use-After-Free can occur when the cancellation handler accesses the freed pointer.
To securely fix this race condition, we must properly un-arm the cancellation. Invoking kiocb->ki_complete() does exactly this by acquiring ctx->ctx_lock and safely removing the kiocb from the active sequence. In doing so, it ensures that a parallel io_cancel can no longer discover the kiocb, effectively closing the race window.
We then return -EIOCBQUEUED to notify the VFS layer that the kiocb has been consumed and it should avoid attempting to complete the request again or triggering subsequent completion handlers.
--- drivers/usb/gadget/function/f_fs.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/function/f_fs.c +4 -0
@@ -1261,8 +1261,10 @@ static ssize_t ffs_epfile_write_iter(strif (res == -EIOCBQUEUED )return res;if (p->aio) { + kiocb->ki_complete (kiocb, res);mmdrop (p->mm);kfree (p); +return -EIOCBQUEUED ; }else { *from = p->data; }
@@ -1310,9 +1312,11 @@ static ssize_t ffs_epfile_read_iter(strureturn res;if (p->aio) { + kiocb->ki_complete (kiocb, res);mmdrop (p->mm);kfree (p->to_free);kfree (p); +return -EIOCBQUEUED ; }else { *to = p->data; }
Greg Kroah-Hartman
maintainerPATCH 461/583 cxl/pci: Move CXL drivers RCH error handling into core/ras_rch.c
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit 0ff60f2ec3e4043a442e805f80f8a2445113ec8f ]
Restricted CXL Host (RCH) protocol error handling uses a procedure distinct from the CXL Virtual Hierarchy (VH) handling. This is because of the differences in the RCH and VH topologies. Improve the maintainability and add ability to enable/disable RCH handling.
Move and combine the RCH handling code into a single block conditionally compiled with the CONFIG_CXL_RCH_RAS kernel config.
Stable-dep-of: 29458e62d082 ("cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read")
--- drivers/cxl/core/Makefile | 1 drivers/cxl/core/core.h | 11 +--- drivers/cxl/core/pci.c | 115 ------------------------------------------ drivers/cxl/core/ras_rch.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ tools/testing/cxl/Kbuild | 1 5 files changed, 126 insertions(+), 123 deletions(-) create mode 100644 drivers/cxl/core/ras_rch.c
drivers/cxl/core/Makefile +1 -0
@@ -20,3 +20,4 @@ cxl_core-$(CONFIG_CXL_MCE) += mce.o cxl_core-$(CONFIG_CXL_FEATURES) += features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o cxl_core-$(CONFIG_CXL_RAS) += ras.o +cxl_core-$(CONFIG_CXL_RAS) += ras_rch.o
drivers/cxl/core/core.h +3 -8
@@ -149,6 +149,9 @@ int cxl_ras_init(void);void cxl_ras_exit (void );bool cxl_handle_ras (struct cxl_dev_state *cxlds,void __iomem *ras_base);void cxl_handle_cor_ras (struct cxl_dev_state *cxlds,void __iomem *ras_base); +void cxl_dport_map_rch_aer (struct cxl_dport *dport); +void cxl_disable_rch_root_ints (struct cxl_dport *dport); +void cxl_handle_rdport_errors (struct cxl_dev_state *cxlds);#else static inline int cxl_ras_init (void ) {
@@ -164,14 +167,6 @@ static inline bool cxl_handle_ras(structreturn false; }static inline void cxl_handle_cor_ras (struct cxl_dev_state *cxlds,void __iomem *ras_base) { } -#endif /* CONFIG_CXL_RAS */ - -/* Restricted CXL Host specific RAS functions */ -#ifdef CONFIG_CXL_RAS -void cxl_dport_map_rch_aer (struct cxl_dport *dport); -void cxl_disable_rch_root_ints (struct cxl_dport *dport); -void cxl_handle_rdport_errors (struct cxl_dev_state *cxlds); -#else static inline void cxl_dport_map_rch_aer (struct cxl_dport *dport) { }static inline void cxl_disable_rch_root_ints (struct cxl_dport *dport) { }static inline void cxl_handle_rdport_errors (struct cxl_dev_state *cxlds) { }
drivers/cxl/core/pci.c +0 -115
121 lines not on this page · raw message
drivers/cxl/core/ras_rch.c new file +121 -0
121 lines not on this page · raw message
tools/testing/cxl/Kbuild +1 -0
@@ -64,6 +64,7 @@ cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o cxl_core-$(CONFIG_CXL_RAS) += $(CXL_CORE_SRC)/ras.o +cxl_core-$(CONFIG_CXL_RAS) += $(CXL_CORE_SRC)/ras_rch.o cxl_core-y += config_check.o cxl_core-y += cxl_core_test.o cxl_core-y += cxl_core_exports.o
Greg Kroah-Hartman
maintainerPATCH 462/583 cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit 29458e62d0829cbc99435f3e44fd560f9bbf1da7 ]
cxl_rch_get_aer_info() copies the RCH Downstream Port AER capability from the RCRB MMIO block using a readl() loop bounded by sizeof(struct aer_capability_regs). This struct is a software layout and its embedded struct pcie_tlp_log is larger than the on-wire AER capability. As a result the loop reads past the mapped AER register block.
The over-read also populates the software-only tail fields including header_log.header_len. An out-of-range header_len passed to pcie_print_tlp_log() can then loop past the header log buffer and cause a second out-of-bounds read.
The read was correct when introduced, but struct pcie_tlp_log has since grown (Header Log and TLP Prefix Log sizes, header_len and flit fields), so sizeof(struct aer_capability_regs) no longer matches the physical AER capability.
Bound the read to the physical AER registers, header through the 16 byte Header Log. Zero the destination first so the software-only fields are deterministic.
--- drivers/cxl/core/ras_rch.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
drivers/cxl/core/ras_rch.c +16 -1
@@ -58,13 +58,28 @@ void cxl_disable_rch_root_ints(struct cxstatic bool cxl_rch_get_aer_info (void __iomem *aer_base,struct aer_capability_regs *aer_regs) { -int read_cnt =sizeof (struct aer_capability_regs ) /sizeof (u32); +/* +* Bound the copy to the physically-defined AER registers (header +* through the 16-byte Header Log). struct aer_capability_regs is a +* software layout whose embedded struct pcie_tlp_log is larger than +* the on-wire AER capability; copying sizeof(*aer_regs) would +* over-read the RCRB-mapped MMIO block. +*/ +int read_cnt = (PCI_ERR_HEADER_LOG +16 ) /sizeof (u32);u32 *aer_regs_buf = (u32 *)aer_regs;int n;if (!aer_base)return false; +/* +* Zero the destination so the software-only tail fields +* (e.g. header_log.header_len) are deterministic rather than +* left as uninitialized stack, which could drive a bogus loop +* length in pcie_print_tlp_log(). +*/ +memset (aer_regs,0 ,sizeof (*aer_regs)); +/* Use readl() to guarantee 32-bit accesses */ for (n =0 ; n < read_cnt; n++) aer_regs_buf[n] =readl (aer_base + n *sizeof (u32));
Greg Kroah-Hartman
maintainerPATCH 463/583 ceph: cap delegated inode count in ceph_parse_deleg_inos()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 4bd3158bd62466d57ed72a3f7bc5f205fedd6919 ]
ceph_parse_deleg_inos() decodes interval sets of delegated inode numbers from an MDS create-with-delegation reply. For each set it reads a 64-bit start and a 64-bit len with ceph_decode_64_safe(), which only validates that the eight bytes are present in the message, not the value, and then loops over len while inserting entries into s_delegated_inos.
len is fully attacker controlled. A malicious or compromised MDS can send one huge interval, many intervals in one reply, duplicate intervals, or repeated replies that accumulate delegated inodes on the same session. The original code bounded none of these and could spin the insert loop or grow the xarray without limit.
Bound both dimensions with a single enforcement point. Track the number of delegated inodes held by each MDS session in an atomic counter and grow it only in ceph_insert_deleg_ino(), which uses atomic_add_unless() to refuse to push the count past CEPH_MAX_DELEG_INOS. Because that helper is the only place the counter grows, the per-session population can never exceed the cap, so no separate per-session pre-check is needed. The counter is decremented when async create consumes a delegated inode or when an insert fails, incremented when a delegated inode is restored, initialized with the session xarray, and reset when reconnect destroys the xarray.
A per-session cap alone still lets one reply spin the insert loop on duplicate ranges without growing the counter, so also cap the aggregate interval length accepted from a single reply. Together these bound both the loop trip count per reply and the xarray population across replies.
The cap is a fixed, client-chosen constant rather than a value derived from the MDS. mds_client_prealloc_inos is a userspace MDS configuration option; it is never sent to the kernel client on the wire, and a server-supplied bound could not be trusted for a defensive limit in any case. The constant is set well above that option's documented default of 1000 (a generous multiple), so legitimate refill behavior is unaffected while the CPU and xarray memory a malformed delegation stream can consume stays bounded.
Impact: a malicious or compromised Ceph MDS can no longer make a client spin through an unbounded delegated-inode interval or grow one session's delegated-inode xarray without limit.
--- fs/ceph/mds_client.c | 59 +++++++++++++++++++++++++++++++++++++++++++++------ fs/ceph/mds_client.h | 1 fs/ceph/super.h | 9 +++++++ 3 files changed, 63 insertions(+), 6 deletions(-)
fs/ceph/mds_client.c +53 -6
@@ -570,10 +570,36 @@ bad:#define DELEGATED_INO_AVAILABLE xa_mk_value(1) +static int ceph_insert_deleg_ino (struct ceph_mds_session *s,u64 ino) +{ +struct ceph_client *cl = s->s_mdsc->fsc->client; +int err; + +/* +* Cap how many delegated inodes a single session may hold. This is +* the only place that grows the count, so atomic_add_unless() bounds +* it at exactly CEPH_MAX_DELEG_INOS; s_num_deleg_inos can never exceed +* that. +*/ +if (!atomic_add_unless (&s->s_num_deleg_inos,1 ,CEPH_MAX_DELEG_INOS )) { +pr_warn_ratelimited_client (cl, +"MDS session already holds %d delegated inodes\n" , +CEPH_MAX_DELEG_INOS ); +return -EOVERFLOW ; + } + + err =xa_insert (&s->s_delegated_inos, ino,DELEGATED_INO_AVAILABLE , +GFP_KERNEL ); +if (err) +atomic_dec (&s->s_num_deleg_inos); +return err; +} +static int ceph_parse_deleg_inos (void **p,void *end,struct ceph_mds_session *s) {struct ceph_client *cl = s->s_mdsc->fsc->client; +u64 msg_deleg_inos =0 ;u32 sets;ceph_decode_32_safe (p, end, sets, bad);
@@ -591,16 +617,34 @@ static int ceph_parse_deleg_inos(void ** start, len);continue ; } + +/* +* Bound the number of inodes one reply may delegate. +* ceph_insert_deleg_ino() separately caps the per-session +* population, so this only has to stop one reply from spinning +* the insert loop under an attacker-controlled len. +*/ +if (len > (u64 )CEPH_MAX_DELEG_INOS || + msg_deleg_inos > (u64 )CEPH_MAX_DELEG_INOS - len) { +pr_warn_ratelimited_client (cl, +"MDS reply delegates too many inodes (have %llu, +%llu, max %d)\n" , + msg_deleg_inos, len,CEPH_MAX_DELEG_INOS ); +return -EIO ; + } + msg_deleg_inos += len; +while (len--) { -int err =xa_insert (&s->s_delegated_inos, start++, -DELEGATED_INO_AVAILABLE , -GFP_KERNEL ); +int err =ceph_insert_deleg_ino (s, start++); +if (!err) {doutc (cl,"added delegated inode 0x%llx\n" , start -1 ); }else if (err == -EBUSY ) {pr_warn_client (cl,"MDS delegated inode 0x%llx more than once.\n" , start -1 ); + }else if (err == -EOVERFLOW ) { +/* ceph_insert_deleg_ino() already warned. */ +return -EIO ; }else {return err; }
@@ -618,16 +662,17 @@ u64 ceph_get_deleg_ino(struct ceph_mds_sxa_for_each (&s->s_delegated_inos, ino, val) { val =xa_erase (&s->s_delegated_inos, ino); -if (val ==DELEGATED_INO_AVAILABLE ) +if (val ==DELEGATED_INO_AVAILABLE ) { +atomic_dec (&s->s_num_deleg_inos);return ino; + } } return 0; }int ceph_restore_deleg_ino (struct ceph_mds_session *s,u64 ino) { -return xa_insert (&s->s_delegated_inos, ino,DELEGATED_INO_AVAILABLE , -GFP_KERNEL ); +return ceph_insert_deleg_ino (s, ino); }#else /* BITS_PER_LONG == 64 */ /*
@@ -1012,6 +1057,7 @@ static struct ceph_mds_session *registerINIT_LIST_HEAD (&s->s_waiting);INIT_LIST_HEAD (&s->s_unsafe);xa_init (&s->s_delegated_inos); +atomic_set (&s->s_num_deleg_inos,0 );INIT_LIST_HEAD (&s->s_cap_releases);INIT_WORK (&s->s_cap_release_work, ceph_cap_release_work);
@@ -4913,6 +4959,7 @@ static void send_mds_reconnect(struct ce goto fail_nomsg;xa_destroy (&session->s_delegated_inos); +atomic_set (&session->s_num_deleg_inos,0 );mutex_lock (&session->s_mutex); session->s_state =CEPH_MDS_SESSION_RECONNECTING ;
fs/ceph/mds_client.h +1 -0
@@ -252,6 +252,7 @@ struct ceph_mds_session {struct list_head s_waiting;/* waiting requests */ struct list_head s_unsafe;/* unsafe requests */ struct xarray s_delegated_inos; +atomic_t s_num_deleg_inos; };/*
fs/ceph/super.h +9 -0
@@ -624,6 +624,15 @@ static inline int ceph_ino_compare(struc#define CEPH_MDS_INO_LOG_OFFSET (2 * CEPH_MAX_MDS)#define CEPH_INO_SYSTEM_BASE ((6*CEPH_MAX_MDS) + (CEPH_MAX_MDS * CEPH_NUM_STRAY)) +/* +* Upper bound on the number of delegated inodes a single MDS session may +* hold. The MDS normally hands out a small preallocation window (the +* userspace mds_client_prealloc_inos option defaults to 1000) and refills +* it as the client consumes entries. This leaves generous headroom while +* bounding the CPU and memory a malformed delegation interval can consume. +*/ +#define CEPH_MAX_DELEG_INOS 8192 +static inline bool ceph_vino_is_reserved (const struct ceph_vino vino) { if (vino.ino >= CEPH_INO_SYSTEM_BASE ||
Greg Kroah-Hartman
maintainerPATCH 464/583 ceph: force a cap message when a deferred revoke cant be acked immediately
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 8fdf946445732c2bcd685abc8bd0e509d2ebc158 ]
When the MDS revokes capabilities, handle_cap_grant() normally guarantees a response by setting CHECK_CAPS_FLUSH_FORCE (see commit 31634d7597d8 ("ceph: force sending a cap update msg back to MDS for revoke op")), so ceph_check_caps() sends a cap message even if the client would otherwise decide it has nothing to do. That guarantee is skipped whenever the revoke has to be deferred (via revoke_wait):
revoking Fb while dirty data is still buffered (writeback is queued first) or revoking Fc while pages are cached (async invalidation is queued first).
In those cases, the ack is left to the deferred completion (ceph_put_wrbuffer_cap_refs() after writeback, or the invalidate worker after invalidation); both of which call ceph_check_caps(ci,0) i.e. without CHECK_CAPS_FLUSH_FORCE. Nothing gets sent under one of the following conditions:
- the inode is retaining caps because the file was used recently (file_wanted != 0; retain |= CEPH_CAP_ANY)
- the revoked cap is still used because the page was re-cached (e.g. a file being re-read)
- the MDS has meanwhile re-granted, so issued==implemented and the client sees nothing being revoked
The client then never emits the cap message which the MDS is waiting for. The MDS blocks on the revoke indefinitely and logs, for minutes or hours:
client.NNN isn't responding to mclientcaps(revoke), ino 0x... pending pAsxLsXsxFsxcrwb issued pAsxLsXsxFsxcrwb, sent 964.899182 seconds ago
The client-side state at that point shows the full cap set still issued, nothing in the revoking/flushing sets. Thus nothing gets sent.
This patch fixes it by remembering that a forced response is expected. When a revoke is deferred, set CEPH_I_FLUSH_FORCE on the inode. ceph_check_caps() replays it as CHECK_CAPS_FLUSH_FORCE, so whichever path re-checks the inode next (the writeback/invalidate completion, the delayed worker, or any other caller) is guaranteed to send a cap message to the MDS. __prep_cap() clears the flag once a message is actually built.
This is the deferred-path counterpart of the existing CHECK_CAPS_FLUSH_FORCE handling; a normal (non-deferred) revoke still forces the response inline as before.
--- fs/ceph/caps.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++------- fs/ceph/super.h | 4 +++ 2 files changed, 60 insertions(+), 7 deletions(-)
fs/ceph/caps.c +56 -7
@@ -978,6 +978,27 @@ int __ceph_caps_revoking_other(struct cereturn 0 ; } +/* +* Return true if any cap of this inode holds caps which the MDS has +* revoked, but which we have not released yet. +*/ +static bool __ceph_is_any_revoking (const struct ceph_inode_info *ci) +{ +const struct rb_node *p; + +lockdep_assert_held (&ci->i_ceph_lock); + +for (p =rb_first (&ci->i_caps); p; p =rb_next (p)) { +const struct ceph_cap *cap = + rb_entry(p, struct ceph_cap, ci_node); + +if (cap->implemented & ~cap->issued) +return true; + } + +return false; +} +int __ceph_caps_used (struct ceph_inode_info *ci) {int used =0 ;
@@ -1420,6 +1441,9 @@ static void __prep_cap(struct cap_msg_ar cap->implemented &= cap->issued | used; cap->mds_wanted = want; +if ((ci->i_ceph_flags &CEPH_I_FLUSH_FORCE ) !=0 && !__ceph_is_any_revoking (ci)) + ci->i_ceph_flags &= ~CEPH_I_FLUSH_FORCE ; + arg->session = cap->session; arg->ino =ceph_vino (inode).ino; arg->cid = cap->cap_id;
@@ -2034,6 +2058,14 @@ void ceph_check_caps(struct ceph_inode_iif (ci->i_ceph_flags &CEPH_I_FLUSH ) flags |=CHECK_CAPS_FLUSH ; +/* +* A revoke whose response was deferred (see handle_cap_grant()) must +* still be acknowledged. Replay the forced flush here so that even a +* check triggered by writeback/invalidation completion sends a cap +* message to the MDS. +*/ +if (ci->i_ceph_flags &CEPH_I_FLUSH_FORCE ) + flags |=CHECK_CAPS_FLUSH_FORCE ; retry:/* Caps wanted by virtue of active open files. */ file_wanted =__ceph_caps_file_wanted (ci);
@@ -3760,13 +3792,30 @@ static void handle_cap_grant(struct inodBUG_ON (cap->issued & ~cap->implemented);/* don't let check_caps skip sending a response to MDS for revoke msgs */ -if (!revoke_wait &&le32_to_cpu (grant->op) ==CEPH_CAP_OP_REVOKE ) { - cap->mds_wanted =0 ; - flags |=CHECK_CAPS_FLUSH_FORCE ; -if (cap == ci->i_auth_cap) - check_caps =1 ;/* check auth cap only */ -else - check_caps =2 ;/* check all caps */ +if (le32_to_cpu (grant->op) ==CEPH_CAP_OP_REVOKE ) { +if (revoke_wait) { +/* +* We can't ack the revoke yet: the response is deferred +* until the writeback or cache invalidation queued above +* completes. Set the CEPH_I_FLUSH_FORCE flag to remember +* that a forced cap message is owed so that deferred +* completion (ceph_put_wrbuffer_cap_refs() or the +* invalidate worker, both of which call ceph_check_caps()) +* actually sends one, even if by then the revoked caps look +* unused, the inode is retaining caps, or the MDS has +* re-granted them. Without this, the cap message is never +* sent and the MDS hangs ("isn't responding to +* mclientcaps(revoke)"). +*/ + ci->i_ceph_flags |=CEPH_I_FLUSH_FORCE ; + }else { + cap->mds_wanted =0 ; + flags |=CHECK_CAPS_FLUSH_FORCE ; +if (cap == ci->i_auth_cap) + check_caps =1 ;/* check auth cap only */ +else + check_caps =2 ;/* check all caps */ + } } if (extra_info->inline_version > 0 &&
fs/ceph/super.h +4 -0
@@ -681,6 +681,10 @@ static inline struct inode *ceph_find_in#define CEPH_I_SHUTDOWN (1 << 13)/* inode is no longer usable */ #define CEPH_I_ASYNC_CHECK_CAPS (1 << 14)/* check caps immediately after async creating finishes */ +#define CEPH_I_FLUSH_FORCE (1 << 15)/* a revoke's response was deferred; +* force a cap message to the MDS once +* the deferred work completes +*/ /* * Masks of ceph inode work.
Greg Kroah-Hartman
maintainerPATCH 465/583 NFSD: Guard admin state-revocation walks with NFSD_NET_UP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 2f3e6638aebc0ab8afb8b4e9816ea9a1cad85378 ]
Writing to /proc/fs/nfsd/unlock_filesystem, or sending the NFSD_CMD_UNLOCK_FILESYSTEM or NFSD_CMD_UNLOCK_EXPORT netlink command, walks the NFSv4 client hash tables to revoke open state and cancel async COPY operations. All three handlers gate that walk on nn->nfsd_serv, but a listener added via portlist or netlink listener_set sets nn->nfsd_serv before any nfsd thread starts. nfsd_startup_net() has not yet allocated nn->conf_id_hashtbl, so the walkers dereference a NULL table. A local administrator with CAP_SYS_ADMIN can crash the kernel this way without ever starting the server.
nn->nfsd_serv is set when the service is created, which precedes table allocation. NFSD_NET_UP instead brackets the window where the tables are live: set at the end of nfsd_startup_net() and cleared in nfsd_shutdown_net() after they are freed, both under nfsd_mutex. Gating the three unlock paths on NFSD_NET_UP fixes the startup-time NULL dereference while preserving the earlier post-shutdown use-after-free fix.
--- fs/nfsd/nfsctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/nfsd/nfsctl.c +1 -1
@@ -298,7 +298,7 @@ static ssize_t write_unlock_fs(struct fi error =nlmsvc_unlock_all_by_sb (path.dentry->d_sb);mutex_lock (&nfsd_mutex); nn =net_generic (netns (file), nfsd_net_id); -if (nn->nfsd_serv) +if (nn->nfsd_net_up)nfsd4_revoke_states (nn, path.dentry->d_sb);else error = -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 466/583 ceph: properly decrypt filenames in vmalloc() buffers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sam Edwards <[email protected]>
[ Upstream commit e939fc6a7bd969a58a150b7f188c1047138403e3 ]
The fscrypt subsystem uses the scatterlist crypto API, inheriting its requirement that any buffers are in the linear mapping region. However, the messenger client uses kvmalloc() to create buffers for messages, which will occasionally place those buffers in the vmalloc() region when physical memory fragmentation doesn't permit a large enough kmalloc(). The various callers of ceph_fname_to_usr() directly pass (slices of) raw messages from the MDS without considering that the messages may be in vmalloc() buffers, resulting in oopses especially on non-x86 platforms (see 'Closes:' for more details and a reproducer).
Make ceph_fname_to_usr() explicitly tolerant of vmalloc()-allocated fname->ctext, fname->name, and/or oname->name buffers, using tname (which, when non-null, must be a linear address; when null, is briefly allocated as necessary) as a bounce buffer to avoid passing any inappropriate addresses to fscrypt_fname_disk_to_usr().
Additionally change parse_reply_info_readdir() -- the only function to supply its own tname -- to follow the new "tname must never come from vmalloc()" rule by passing NULL when the message is not in the linear region. Though this causes a per-dentry kmalloc()+kfree(), this overhead exists only when processing the minority of messages that spill into vmalloc(). My (crude) testing puts this at only about 1 in 8,000 readdir messages. Still, if the overhead proves unreasonable in the future, it is easy enough to mitigate: a future change could allocate a bounce buffer in parse_reply_info_readdir() and use that as tname instead.
--- fs/ceph/crypto.c | 46 +++++++++++++++++++++++++++++++++++++--------- fs/ceph/mds_client.c | 8 ++++++-- 2 files changed, 43 insertions(+), 11 deletions(-)
fs/ceph/crypto.c +37 -9
@@ -351,6 +351,11 @@ out:* Otherwise, base64 decode the string, and then ask fscrypt to format it * for userland presentation. * +* Though the fscrypt/crypto subsystems broadly expect all buffers to be in the +* linear-mapped region, this function slightly relaxes those requirements: +* fname->ctext, fname->name, and oname->name may be vmalloc(), but not +* tname->name. +* * Returns 0 on success or negative error code on error. */ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
@@ -358,11 +363,15 @@ int ceph_fname_to_usr(const struct ceph_ {struct inode *dir = fname->dir;struct fscrypt_str _tname =FSTR_INIT (NULL ,0 ); +struct fscrypt_str _oname;struct fscrypt_str iname;char *name = fname->name;int name_len = fname->name_len;int ret; +if (WARN_ON_ONCE (tname &&is_vmalloc_addr (tname->name))) +return -EIO ; +/* Sanity check that the resulting name will fit in the buffer */ if (fname->name_len >NAME_MAX || fname->ctext_len >NAME_MAX )return -EIO ;
@@ -403,16 +412,19 @@ int ceph_fname_to_usr(const struct ceph_ goto out_inode; } +if ((!tname || !tname->name) && + (fname->ctext_len ==0 || +unlikely (is_vmalloc_addr (fname->ctext)) || +unlikely (is_vmalloc_addr (oname->name)))) { + ret =fscrypt_fname_alloc_buffer (NAME_MAX , &_tname); +if (ret) + goto out_inode; + tname = &_tname; + } +if (fname->ctext_len ==0 ) {int declen; -if (!tname) { - ret =fscrypt_fname_alloc_buffer (NAME_MAX , &_tname); -if (ret) - goto out_inode; - tname = &_tname; - } - declen =ceph_base64_decode (name, name_len, tname->name);if (declen <=0 ) { ret = -EIO ;
@@ -420,13 +432,29 @@ int ceph_fname_to_usr(const struct ceph_ } iname.name = tname->name; iname.len = declen; + }else if (unlikely (is_vmalloc_addr (fname->ctext))) { +memcpy (tname->name, fname->ctext, fname->ctext_len); + + iname.name = tname->name; + iname.len = fname->ctext_len; }else { iname.name = fname->ctext; iname.len = fname->ctext_len; } - ret =fscrypt_fname_disk_to_usr (dir,0 ,0 , &iname, oname); -if (!ret && (dir != fname->dir)) { + _oname.name =unlikely (is_vmalloc_addr (oname->name)) ? + tname->name : oname->name; + _oname.len = oname->len; + + ret =fscrypt_fname_disk_to_usr (dir,0 ,0 , &iname, &_oname); +if (ret) + goto out; + +if (unlikely (is_vmalloc_addr (oname->name))) +memcpy (oname->name, _oname.name, _oname.len); + oname->len = _oname.len; + +if (dir != fname->dir) {char tmp_buf[CEPH_BASE64_CHARS (NAME_MAX )]; name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
fs/ceph/mds_client.c +6 -2
@@ -496,9 +496,13 @@ static int parse_reply_info_readdir(void* to do the base64_decode in-place. It's * safe because the decoded string should * always be shorter, which is 3/4 of origin -* string. +* string. If this message was allocated with +* vmalloc() (happens, but rarely), leave it +* NULL and let ceph_fname_to_usr() allocate +* suitable temporary working space instead. */ - tname.name = _name; +if (likely (!is_vmalloc_addr (_name))) + tname.name = _name;/* * Set oname to _name too, and this will be
Greg Kroah-Hartman
maintainerPATCH 467/583 smb: move some definitions from common/smb2pdu.h into common/fscc.h
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit a5e581093b1d9321cbb627dd8c209d0d4e0a988a ]
These definitions are specified in MS-FSCC, so move them into fscc.h.
Only add some documentation references, no other changes.
The original header relocation is not applicable to this stable tree: fscc.h is absent and the relevant definitions are already kept in smb2pdu.h.
For this stable-only dependency adaptation, prepare the existing fallocate paths for EOF-extending ranges and allocation metadata refresh, and align the cifs declarations with the target patch context. This lets the fscache resize locking fix apply without adding its new helper in the dependency.
[ sashal: Reduced backport -- upstream a5e581093b1d9 touches 2 file(s), this
backport carries 2. Not backported here:
fs/smb/common/fscc.h
fs/smb/common/smb2pdu.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 32a7af68df73 ("cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem")
--- fs/smb/client/cifsfs.h | 5 +- fs/smb/client/smb2ops.c | 89 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 14 deletions(-)
fs/smb/client/cifsfs.h +3 -2
@@ -133,9 +133,10 @@ extern ssize_t cifs_file_copychunk_range struct file *dst_file, loff_t destoff, size_t len, unsigned int flags); -extern long cifs_ioctl (struct file *filep,unsigned int cmd,unsigned long arg); -extern void cifs_setsize (struct inode *inode,loff_t offset); +long cifs_ioctl (struct file *filep,unsigned int command,unsigned long arg); +void cifs_setsize (struct inode *inode,loff_t offset); +struct fs_context ;struct smb3_fs_context ;extern struct dentry *cifs_smb3_do_mount (struct file_system_type *fs_type,int flags,struct smb3_fs_context *ctx);
fs/smb/client/smb2ops.c +77 -12
140 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 468/583 smb/client: reduce fallocate zero buffer allocation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 9e4ec3be67af41ab859302d7109b34976efd9258 ]
The fallocate emulation allocates a 1 MiB zero-filled buffer even though each SMB2_write request is limited to SMB2_MAX_BUFFER_SIZE, which is 64 KiB. A high-order 1 MiB allocation is more likely to fail on a fragmented system.
Allocate only the smaller of the requested range and SMB2_MAX_BUFFER_SIZE, and reuse that zero-filled buffer for every write request. Also reject a successful write that makes no progress to avoid looping indefinitely.
This reduces the contiguous allocation required by fallocate emulation without changing the written data or range semantics.
Stable-dep-of: 32a7af68df73 ("cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem")
--- fs/smb/client/smb2ops.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
fs/smb/client/smb2ops.c +4 -3
@@ -3508,7 +3508,7 @@ static int smb3_simple_fallocate_write_r char *buf) {struct cifs_io_parms io_parms = {0 }; -int nbytes; +unsigned int nbytes;int rc =0 ;struct kvec iov[2 ];
@@ -3529,9 +3529,10 @@ static int smb3_simple_fallocate_write_r rc =SMB2_write (xid, &io_parms, &nbytes, iov,1 );if (rc)break ; +if (!nbytes) +return -EIO ;if (nbytes > len)return -EINVAL ; - buf += nbytes; off += nbytes; len -= nbytes; }
@@ -3551,7 +3552,7 @@ static int smb3_simple_fallocate_range(uloff_t l;int rc; - buf =kvzalloc (1024 *1024 ,GFP_KERNEL ); + buf =kvzalloc (min_t (loff_t, len,SMB2_MAX_BUFFER_SIZE ),GFP_KERNEL );if (buf ==NULL ) { rc = -ENOMEM ; goto out;
Greg Kroah-Hartman
maintainerPATCH 469/583 smb/client: emulate small EOF-extending mode 0 fallocate ranges
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huiwen He <[email protected]>
[ Upstream commit 7a06d3b816d73448b4e38b83d65049f090b7b201 ]
When a mode 0 fallocate extends EOF from 1G to 2G + 1M, the client currently sends SetEOF for 2G + 1M. This can make fallocate return success without allocating the requested range, or allocate extra space before that range.
For example, on a fresh file:
xfs_io -f \
-c "falloc 0 1G" \
-c "falloc 2G 1M" \
-c "truncate 3G" testThe second fallocate should allocate [2G, 2G + 1M), leaving [1G, 2G) as a hole.
Before this change, the result depended on the server allocation policy. With Samba "strict allocate = no", SetEOF could return success without allocating [2G, 2G + 1M). With "strict allocate = yes":
# filefrag -v test
[0, 1G) allocated
[1G, 2G) allocated unexpectedly
[2G, 2G + 1M) allocatedSMB cannot allocate that arbitrary range, so write zeroes to small EOF-extending ranges instead. Limit this to 1 MiB to bound the client-side I/O cost.
With "strict allocate = no", the requested range [2G, 2G + 1M) is allocated by the writes. With "strict allocate = yes":
# filefrag -v test
[0, 1G) allocated
[1G, 2G) hole
[2G, 2G + 1M) allocatedThis fixes the small EOF-extending range case exercised by generic/213.
Stable-dep-of: 32a7af68df73 ("cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem")
--- fs/smb/client/smb2ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/smb2ops.c +1 -1
@@ -3553,7 +3553,7 @@ static int smb3_simple_fallocate_range(uint rc; buf =kvzalloc (min_t (loff_t, len,SMB2_MAX_BUFFER_SIZE ),GFP_KERNEL ); -if (buf ==NULL ) { +if (!buf) { rc = -ENOMEM ; goto out; }
Greg Kroah-Hartman
maintainerPATCH 470/583 cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Sorenson <[email protected]>
[ Upstream commit 32a7af68df7361fe7cf153cf36124d04b94aec00 ]
cifs_setsize() calls fscache_resize_cookie() without holding i_rwsem. When the fscache cookie is active (FSCACHE_COOKIE_IS_CACHING is set), fscache_resize_cookie() performs a real resize that requires i_rwsem held exclusively. If another file descriptor has the same inode open, fscache_use_cookie() was already called from that cifs_open(), making the cookie active. In that case, calling cifs_setsize() from cifs_do_truncate() (invoked from cifs_open() without i_rwsem) races against concurrent fscache I/O.
Strip fscache_resize_cookie() from cifs_setsize(), making it a pure size/page-cache helper. Add cifs_resize_file_locked() for callers that already hold i_rwsem: it calls netfs_resize_file() and cifs_setsize(), then temporarily activates the cookie with fscache_use_cookie() to perform the resize under the lock, then deactivates it with cifs_fscache_unuse_inode_cookie(). Using fscache_use_cookie() before the resize ensures correctness whether or not another fd already holds the cookie active.
Switch cifs_file_set_size(), smb2_duplicate_extents(), and both size- extension branches of smb3_simple_falloc() to the new wrapper; those paths already hold i_rwsem via VFS setattr, lock_two_nondirectories(), or cifs_fallocate() respectively. cifs_do_truncate() continues to call cifs_setsize() followed by cifs_invalidate_cache(), since it runs without i_rwsem.
--- fs/smb/client/cifsfs.h | 1 + fs/smb/client/inode.c | 24 +++++++++++++++++++----- fs/smb/client/smb2ops.c | 9 +++------ 3 files changed, 23 insertions(+), 11 deletions(-)
fs/smb/client/cifsfs.h +1 -0
@@ -135,6 +135,7 @@ extern ssize_t cifs_file_copychunk_rangelong cifs_ioctl (struct file *filep,unsigned int command,unsigned long arg);void cifs_setsize (struct inode *inode,loff_t offset); +void cifs_resize_file_locked (struct inode *inode,loff_t offset);struct fs_context ;struct smb3_fs_context ;
fs/smb/client/inode.c +19 -5
@@ -3039,7 +3039,23 @@ void cifs_setsize(struct inode *inode, linode_set_mtime_to_ts (inode,inode_set_ctime_current (inode));truncate_pagecache (inode, offset);netfs_wait_for_outstanding_io (inode); -fscache_resize_cookie (cifs_inode_cookie (inode), offset); +} + +void cifs_resize_file_locked (struct inode *inode,loff_t offset) +{ +struct fscache_cookie *cookie =cifs_inode_cookie (inode); + +lockdep_assert_held_write (&inode->i_rwsem); + +netfs_resize_file (netfs_inode (inode), offset, true); +cifs_setsize (inode, offset); + +if (!cookie) +return ; + +fscache_use_cookie (cookie, true); +fscache_resize_cookie (cookie, offset); +cifs_fscache_unuse_inode_cookie (inode, true); } int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
@@ -3106,10 +3122,8 @@ int cifs_file_set_size(const unsigned incifs_put_tlink (tlink); set_size_out: -if (rc ==0 ) { -netfs_resize_file (&cifsInode->netfs, size, true); -cifs_setsize (inode, size); - } +if (rc ==0 ) +cifs_resize_file_locked (inode, size);return rc; }
fs/smb/client/smb2ops.c +3 -6
@@ -2197,8 +2197,7 @@ smb2_duplicate_extents(const unsigned in rc =smb2_set_file_size (xid, tcon, trgtfile, dest_off + len, false);if (rc) goto duplicate_extents_out; -netfs_resize_file (netfs_inode (inode), dest_off + len, true); -cifs_setsize (inode, dest_off + len); +cifs_resize_file_locked (inode, dest_off + len); } rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, trgtfile->fid.volatile_fid,
@@ -3702,8 +3701,7 @@ static long smb3_simple_falloc(struct fi } new_eof = off + len; -netfs_resize_file (&cifsi->netfs, new_eof, true); -cifs_setsize (inode, new_eof); +cifs_resize_file_locked (inode, new_eof); qrc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
@@ -3728,8 +3726,7 @@ static long smb3_simple_falloc(struct fiif (rc) goto out; -netfs_resize_file (&cifsi->netfs, new_eof, true); -cifs_setsize (inode, new_eof); +cifs_resize_file_locked (inode, new_eof); qrc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
Greg Kroah-Hartman
maintainerPATCH 471/583 HID: sony: use guard() and scoped_guard()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosalie Wanders <[email protected]>
[ Upstream commit da4f817ad273bca9aefd8636d347a8c101069111 ]
This replaces the spin_lock_irqsave() and spin_unlock_irqrestore() calls with the RAII guard() and scoped_guard().
Stable-dep-of: 7c65699a3a31 ("HID: sony: clean up device list on probe failure")
--- drivers/hid/hid-sony.c | 53 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 31 deletions(-)
drivers/hid/hid-sony.c +22 -31
@@ -27,6 +27,7 @@* There will be no PIN request from the device. */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h>
@@ -520,14 +521,12 @@ static void sony_set_leds(struct sony_scstatic inline void sony_schedule_work (struct sony_sc *sc,enum sony_worker which) { -unsigned long flags; -switch (which) {case SONY_WORKER_STATE : -spin_lock_irqsave (&sc->lock, flags); -if (!sc->defer_initialization && sc->state_worker_initialized) -schedule_work (&sc->state_worker); -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { +if (!sc->defer_initialization && sc->state_worker_initialized) +schedule_work (&sc->state_worker); + }break ; } }
@@ -796,7 +795,6 @@ static const u8 *sony_report_fixup(strucstatic void sixaxis_parse_report (struct sony_sc *sc,u8 *rd,int size) {static const u8 sixaxis_battery_capacity[] = {0 ,1 ,25 ,50 ,75 ,100 }; -unsigned long flags;int offset;u8 battery_capacity;int battery_status;
@@ -818,10 +816,10 @@ static void sixaxis_parse_report(struct battery_status =POWER_SUPPLY_STATUS_DISCHARGING ; } -spin_lock_irqsave (&sc->lock, flags); - sc->battery_capacity = battery_capacity; - sc->battery_status = battery_status; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->battery_capacity = battery_capacity; + sc->battery_status = battery_status; + }if (sc->quirks &SIXAXIS_CONTROLLER ) {int val;
@@ -1625,15 +1623,14 @@ static int sony_battery_get_property(str union power_supply_propval *val) {struct sony_sc *sc =power_supply_get_drvdata (psy); -unsigned long flags;int ret =0 ;u8 battery_capacity;int battery_status; -spin_lock_irqsave (&sc->lock, flags); - battery_capacity = sc->battery_capacity; - battery_status = sc->battery_status; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + battery_capacity = sc->battery_capacity; + battery_status = sc->battery_status; + }switch (psp) {case POWER_SUPPLY_PROP_PRESENT :
@@ -1715,10 +1712,9 @@ static inline int sony_compare_connectiostatic int sony_check_add_dev_list (struct sony_sc *sc) {struct sony_sc *entry; -unsigned long flags;int ret; -spin_lock_irqsave (&sony_dev_list_lock, flags); +guard (spinlock_irqsave)(&sony_dev_list_lock);list_for_each_entry (entry, &sony_device_list, list_node) { ret = memcmp(sc->mac_address, entry->mac_address,
@@ -1732,26 +1728,23 @@ static int sony_check_add_dev_list(struc"controller with MAC address %pMR already connected\n" , sc->mac_address); } - goto unlock; + goto out; } } ret =0 ;list_add (&(sc->list_node), &sony_device_list); -unlock: -spin_unlock_irqrestore (&sony_dev_list_lock, flags); +out:return ret; }static void sony_remove_dev_list (struct sony_sc *sc) { -unsigned long flags; -if (sc->list_node.next) { -spin_lock_irqsave (&sony_dev_list_lock, flags); -list_del (&(sc->list_node)); -spin_unlock_irqrestore (&sony_dev_list_lock, flags); + scoped_guard(spinlock_irqsave, &sony_dev_list_lock) { +list_del (&(sc->list_node)); + } } }
@@ -1879,12 +1872,10 @@ static inline void sony_init_output_repostatic inline void sony_cancel_work_sync (struct sony_sc *sc) { -unsigned long flags; -if (sc->state_worker_initialized) { -spin_lock_irqsave (&sc->lock, flags); - sc->state_worker_initialized =0 ; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->state_worker_initialized =0 ; + }cancel_work_sync (&sc->state_worker); } }
Greg Kroah-Hartman
maintainerPATCH 472/583 HID: sony: clean up device list on probe failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit 7c65699a3a311198a07659a614fe64d45924839e ]
sony_input_configured() adds some controllers to sony_device_list before HID core registers their input devices. input_register_device() can fail after the callback returns successfully. sony_probe() then observes that HID_CLAIMED_INPUT is clear and unwinds, but only stops the HID hardware. The devres-managed sony_sc is freed while its list node remains linked, so the next matching controller traverses freed memory.
Initialize the list node and device ID to inactive states. Make list removal idempotent and run the driver-private cleanup on every probe failure path. This also makes a second cleanup safe when sony_input_configured() already unwound a partial initialization before sony_probe() handles the missing input claim.
Found by 0sec (https://0sec.ai) using automated source analysis; verified against the HID input registration and probe unwind paths.
--- drivers/hid/hid-sony.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-)
drivers/hid/hid-sony.c +17 -16
@@ -1741,11 +1741,10 @@ out:static void sony_remove_dev_list (struct sony_sc *sc) { -if (sc->list_node.next) { - scoped_guard(spinlock_irqsave, &sony_dev_list_lock) { -list_del (&(sc->list_node)); - } - } +guard (spinlock_irqsave)(&sony_dev_list_lock); + +if (!list_empty (&sc->list_node)) +list_del_init (&sc->list_node); }static int sony_get_bt_devaddr (struct sony_sc *sc)
@@ -1880,6 +1879,13 @@ static inline void sony_cancel_work_sync } } +static void sony_cleanup (struct sony_sc *sc) +{ +sony_cancel_work_sync (sc); +sony_remove_dev_list (sc); +sony_release_device_id (sc); +} +static int sony_input_configured (struct hid_device *hdev,struct hid_input *hidinput) {
@@ -2038,9 +2044,7 @@ static int sony_input_configured(struct err_close:hid_hw_close (hdev); err_stop: -sony_cancel_work_sync (sc); -sony_remove_dev_list (sc); -sony_release_device_id (sc); +sony_cleanup (sc);return ret; }
@@ -2066,6 +2070,8 @@ static int sony_probe(struct hid_device }spin_lock_init (&sc->lock); +INIT_LIST_HEAD (&sc->list_node); + sc->device_id =-1 ; sc->quirks = quirks;hid_set_drvdata (hdev, sc);
@@ -2094,6 +2100,7 @@ static int sony_probe(struct hid_device ret =hid_hw_start (hdev, connect_mask);if (ret) {hid_err (hdev,"hw start failed\n" ); +sony_cleanup (sc);return ret; }
@@ -2145,7 +2152,7 @@ static int sony_probe(struct hid_device err:usb_free_urb (sc->ghl_urb); - +sony_cleanup (sc);hid_hw_stop (hdev);return ret; }
@@ -2162,13 +2169,7 @@ static void sony_remove(struct hid_devic }hid_hw_close (hdev); - -sony_cancel_work_sync (sc); - -sony_remove_dev_list (sc); - -sony_release_device_id (sc); - +sony_cleanup (sc);hid_hw_stop (hdev); }
Greg Kroah-Hartman
maintainerPATCH 473/583 HID: mcp2221: fix OOB write in mcp2221_raw_event()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Pradines <[email protected]>
[ Upstream commit f097d246677b03db814c5862f368cea341b76a00 ]
mcp2221_raw_event() copies device-supplied data into mcp->rxbuf at offset rxbuf_idx without checking that the copy fits within the destination buffer. A device responding with up to 60 bytes to a small I2C/SMBus read can overflow the buffer.
Add a rxbuf_size field to struct mcp2221, set it alongside rxbuf in mcp_i2c_smbus_read(), and check rxbuf_idx + data[3] <= rxbuf_size before the memcpy.
Stable-dep-of: db2333f88729 ("HID: mcp2221: clear rxbuf after I2C/SMBus transfer completes")
--- drivers/hid/hid-mcp2221.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/hid/hid-mcp2221.c +7 -0
@@ -121,6 +121,7 @@ struct mcp2221 {u8 *rxbuf;u8 txbuf[64 ];int rxbuf_idx; +int rxbuf_size;int status;u8 cur_i2c_clk_div;struct gpio_chip *gc;
@@ -323,12 +324,14 @@ static int mcp_i2c_smbus_read(struct mcp mcp->txbuf[3 ] = (u8)(msg->addr <<1 ); total_len = msg->len; mcp->rxbuf = msg->buf; + mcp->rxbuf_size = msg->len; }else { mcp->txbuf[1 ] = smbus_len; mcp->txbuf[2 ] =0 ; mcp->txbuf[3 ] = (u8)(smbus_addr <<1 ); total_len = smbus_len; mcp->rxbuf = smbus_buf; + mcp->rxbuf_size = smbus_len; } ret =mcp_send_data_req_status (mcp, mcp->txbuf,4 );
@@ -915,6 +918,10 @@ static int mcp2221_raw_event(struct hid_ mcp->status = -EINVAL ;break ; } +if (mcp->rxbuf_idx + data[3 ] > mcp->rxbuf_size) { + mcp->status = -EINVAL ; +break ; + }if (4 + data[3 ] > size) { mcp->status = -EINVAL ;break ;
Greg Kroah-Hartman
maintainerPATCH 474/583 HID: mcp2221: clear rxbuf after I2C/SMBus transfer completes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
[ Upstream commit db2333f88729c8aae062cb171ed058725ff5c901 ]
mcp_i2c_smbus_read() stores the caller-supplied buffer pointer in mcp->rxbuf for the duration of a transfer but never clears it when the transfer finishes or times out. Once the caller frees or reuses the buffer, mcp->rxbuf becomes a dangling pointer. A delayed or spurious MCP2221_I2C_GET_DATA report can then drive mcp2221_raw_event() to memcpy device data into the freed memory, causing a write use-after-free.
Route all return paths through a single exit point that clears mcp->rxbuf and mcp->rxbuf_size, so that the existing !mcp->rxbuf guard in the raw_event handler can reject any report arriving after the transfer has ended.
--- drivers/hid/hid-mcp2221.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/hid/hid-mcp2221.c +6 -2
@@ -336,7 +336,7 @@ static int mcp_i2c_smbus_read(struct mcp ret =mcp_send_data_req_status (mcp, mcp->txbuf,4 );if (ret) -return ret; + goto out; mcp->rxbuf_idx =0 ;
@@ -358,7 +358,7 @@ static int mcp_i2c_smbus_read(struct mcp }else {usleep_range (980 ,1000 );mcp_cancel_last_cmd (mcp); -return ret; + goto out; } }else { retries =0 ;
@@ -368,6 +368,10 @@ static int mcp_i2c_smbus_read(struct mcpusleep_range (980 ,1000 ); ret =mcp_chk_last_cmd_status_free_bus (mcp); +out: + mcp->rxbuf =NULL ; + mcp->rxbuf_size =0 ; +return ret; }
Greg Kroah-Hartman
maintainerPATCH 475/583 erofs: skip sufficiently large global buffers when resizing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil Gurudasani <[email protected]>
[ Upstream commit a7d097cf01301c5da37927c8f26123d006f0fd8a ]
z_erofs_gbuf_nrpages is advanced only after every global buffer has been grown. If a resize fails after some buffers were enlarged, a retry revisits those enlarged buffers.
Retrying the same size then returns -ENOMEM because alloc_pages_bulk() has no pages to add and the unchanged return value is treated as a failure. Retrying an intermediate size allocates a temporary pointer array smaller than gbuf->nrpages and copies more existing pointers than the array can hold.
Skip buffers that already satisfy the request. Once all remaining buffers have caught up, advancing z_erofs_gbuf_nrpages again describes the guaranteed minimum size across the pool.
--- fs/erofs/zutil.c | 2 ++ 1 file changed, 2 insertions(+)
fs/erofs/zutil.c +2 -0
@@ -79,6 +79,8 @@ int z_erofs_gbuf_growsize(unsigned int nfor (i =0 ; i < z_erofs_gbuf_count; ++i) { gbuf = &z_erofs_gbufpool[i]; +if (gbuf->nrpages >= nrpages) +continue ; tmp_pages =kcalloc (nrpages,sizeof (*tmp_pages),GFP_KERNEL );if (!tmp_pages) goto out;
Greg Kroah-Hartman
maintainerPATCH 476/583 ACPI: CPPC: Reject desired_perf reads on _CPC revision 4+
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Loehle <[email protected]>
[ Upstream commit df5a1d4a8cdfda20eb2581a85e81c7d436866534 ]
When CPPC feedback counters cannot provide a usable sample, cppc-cpufreq calls cppc_get_desired_perf() because some platforms repurpose Desired Performance to report actual delivered performance.
ACPI 6.5 defines _CPC revision 3 and lists Read/Write as the Optional Attribute of Desired Performance. ACPI 6.6 advances _CPC to revision 4 and lists only Write, so invoking that workaround for revision 4 or later would require a register read that the interface no longer specifies.
Make cppc_get_desired_perf() return -EOPNOTSUPP for _CPC revision 4 or later. Use the revision retained in the per-CPU CPC descriptor rather than the platform-wide FADT revision.
The _CPC revision may still not accurately describe the implemented register semantics. If a nominally revision 3 platform implements a non-readable Desired Performance register, a read may return zero and make cppc_cpufreq_get_rate() report 0 kHz. Treat a zero read as unusable and fall back to the cached OSPM request, just as for a failed read.
--- drivers/acpi/cppc_acpi.c | 13 +++++++++++-- drivers/cpufreq/cppc_cpufreq.c | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-)
drivers/acpi/cppc_acpi.c +11 -2
@@ -768,7 +768,6 @@ int acpi_cppc_processor_probe(struct acp }if (cpc_rev >CPPC_V3_REV ) { num_ent =CPPC_V3_NUM_ENT ; - cpc_rev =CPPC_V3_REV ; } cpc_ptr->num_entries = num_ent;
@@ -1289,10 +1288,20 @@ static int cppc_set_reg_val(int cpu, enu* @cpunum: CPU from which to get desired performance. * @desired_perf: Return address. * -* Return: 0 for success, -EIO otherwise. +* Return: 0 for success, -EOPNOTSUPP for _CPC revision 4 or later, and a +* negative errno otherwise. */ int cppc_get_desired_perf (int cpunum,u64 *desired_perf) { +struct cpc_desc *cpc_desc =per_cpu (cpc_desc_ptr, cpunum); + +if (!cpc_desc) +return -ENODEV ; + +/* _CPC revision 4 no longer specifies Desired Performance as readable. */ +if (cpc_desc->version >CPPC_V3_REV ) +return -EOPNOTSUPP ; +return cppc_get_reg_val (cpunum,DESIRED_PERF , desired_perf); }EXPORT_SYMBOL_GPL (cppc_get_desired_perf);
drivers/cpufreq/cppc_cpufreq.c +1 -1
@@ -766,7 +766,7 @@ out_invalid_counters:* value first as some platforms may update the actual delivered perf * there; if failed, resort to the cached desired perf. */ -if (cppc_get_desired_perf (cpu, &delivered_perf)) +if (cppc_get_desired_perf (cpu, &delivered_perf) || !delivered_perf) delivered_perf = cpu_data->perf_ctrls.desired_perf;return cppc_perf_to_khz (&cpu_data->perf_caps, delivered_perf);
Greg Kroah-Hartman
maintainerPATCH 477/583 ACPI: x86: cmos_rtc: Create a CMOS RTC platform device
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit 1ae2f435350ec05224a39995c3a680aa6fdae5a5 ]
Make the CMOS RTC ACPI scan handler create a platform device that will be used subsequently by rtc-cmos for driver binding on x86 systems with ACPI and update add_rtc_cmos() to skip registering a fallback platform device for the CMOS RTC when the above one has been registered.
Stable-dep-of: a3df8bbe0a70 ("ACPI: TAD: Add locking around AML evaluations")
--- arch/x86/kernel/rtc.c | 4 ++++ drivers/acpi/x86/cmos_rtc.c | 8 ++++++++ include/linux/acpi.h | 4 ++++ 3 files changed, 16 insertions(+)
arch/x86/kernel/rtc.c +4 -0
@@ -2,6 +2,7 @@/* * RTC related functions */ +#include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/mc146818rtc.h> #include <linux/export.h>
@@ -146,6 +147,9 @@ static __init int add_rtc_cmos(void) } }#endif +if (cmos_rtc_platform_device_present) +return 0 ; +if (!x86_platform.legacy.rtc)return -ENODEV ;
drivers/acpi/x86/cmos_rtc.c +8 -0
@@ -24,6 +24,8 @@ static const struct acpi_device_id acpi_ {} }; +bool cmos_rtc_platform_device_present; +static bool cmos_rtc_space_handler_present__read_mostly ; static acpi_status acpi_cmos_rtc_space_handler(u32 function,
@@ -103,6 +105,12 @@ static int acpi_cmos_rtc_attach(struct aif (ret <0 )return ret; +if (IS_ERR_OR_NULL (acpi_create_platform_device (adev,NULL ))) { +pr_err ("Failed to create CMOS-RTC platform device\n" ); +return 0 ; + }else { + cmos_rtc_platform_device_present = true; + }return 1 ; }
include/linux/acpi.h +4 -0
@@ -779,6 +779,8 @@ const char *acpi_get_subsystem_id(acpi_hint acpi_mrrm_max_mem_region (void );#endif +extern bool cmos_rtc_platform_device_present; +#else /* !CONFIG_ACPI */ #define acpi_disabled 1
@@ -1104,6 +1106,8 @@ static inline int acpi_mrrm_max_mem_regi return 1; } +#define cmos_rtc_platform_device_present false +#endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI_HMAT
Greg Kroah-Hartman
maintainerPATCH 478/583 ACPI: x86/rtc-cmos: Use platform device for driver binding
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit 2a78e42104444f948698f1225deaf515e9b7224d ]
Modify the rtc-cmos driver to bind to a platform device on systems with ACPI via acpi_match_table and advertise the CMOST RTC ACPI device IDs for driver auto-loading. Note that adding the requisite device IDs to it and exposing them via MODULE_DEVICE_TABLE() is sufficient for this purpose.
Since the ACPI device IDs in question are the same as for the CMOS RTC ACPI scan handler, put them into a common header file and use the definition from there in both places.
Additionally, to prevent a PNP device from being created for the CMOS RTC if a platform one is present already, make is_cmos_rtc_device() check cmos_rtc_platform_device_present introduced previously.
Stable-dep-of: a3df8bbe0a70 ("ACPI: TAD: Add locking around AML evaluations")
--- drivers/acpi/acpi_pnp.c | 2 +- drivers/acpi/x86/cmos_rtc.c | 5 +---- drivers/rtc/rtc-cmos.c | 10 ++++++++++ include/linux/acpi.h | 6 ++++++ 4 files changed, 18 insertions(+), 5 deletions(-)
drivers/acpi/acpi_pnp.c +1 -1
@@ -385,7 +385,7 @@ static int is_cmos_rtc_device(struct acp { "PNP0B02" }, {""}, }; -return !acpi_match_device_ids (adev, ids); +return !cmos_rtc_platform_device_present && !acpi_match_device_ids (adev, ids); }bool acpi_is_pnp_device (struct acpi_device *adev)
drivers/acpi/x86/cmos_rtc.c +1 -4
@@ -18,10 +18,7 @@#include "../internal.h" static const struct acpi_device_id acpi_cmos_rtc_ids[] = { - {"PNP0B00" }, - {"PNP0B01" }, - {"PNP0B02" }, - {} +ACPI_CMOS_RTC_IDS };bool cmos_rtc_platform_device_present;
drivers/rtc/rtc-cmos.c +10 -0
@@ -27,6 +27,7 @@#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h>
@@ -1482,6 +1483,14 @@ static __init void cmos_of_init(struct p#else static inline void cmos_of_init (struct platform_device *pdev) {}#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { +ACPI_CMOS_RTC_IDS +}; +MODULE_DEVICE_TABLE (acpi, acpi_cmos_rtc_ids); +#endif +/*----------------------------------------------------------------*/ /* Platform setup should have set up an RTC device, when PNP is
@@ -1536,6 +1545,7 @@ static struct platform_driver cmos_platf .name = driver_name, .pm = &cmos_pm_ops, .of_match_table = of_match_ptr(of_cmos_match), + .acpi_match_table = ACPI_PTR(acpi_cmos_rtc_ids), } };
include/linux/acpi.h +6 -0
@@ -779,6 +779,12 @@ const char *acpi_get_subsystem_id(acpi_hint acpi_mrrm_max_mem_region (void );#endif +#define ACPI_CMOS_RTC_IDS \ + { "PNP0B00", }, \ + { "PNP0B01", }, \ + { "PNP0B02", }, \ + { "", } +extern bool cmos_rtc_platform_device_present;#else /* !CONFIG_ACPI */
Greg Kroah-Hartman
maintainerPATCH 479/583 ACPI: TAD: Rearrange RT data validation checking
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit 3329a1416c3350449081ca5daaa94802a65b2992 ]
Move RT data validation checks from acpi_tad_set_real_time() to a separate function called acpi_tad_rt_is_invalid() and use it also in acpi_tad_get_real_time() to validate data coming from the platform firmware.
Also make acpi_tad_set_real_time() return -EINVAL when the RT data passed to it is invalid (instead of -ERANGE which is somewhat confusing) and introduce ACPI_TAD_TZ_UNSPEC to represent the "unspecified timezone" value.
Stable-dep-of: a3df8bbe0a70 ("ACPI: TAD: Add locking around AML evaluations")
--- drivers/acpi/acpi_tad.c | 142 ++++++++++++++++++++++++++++++++---------------- include/linux/acpi.h | 4 + 2 files changed, 101 insertions(+), 45 deletions(-)
drivers/acpi/acpi_tad.c +97 -45
@@ -49,6 +49,9 @@ MODULE_AUTHOR("Rafael J. Wysocki");/* Special value for disabled timer or expired timer wake policy. */ #define ACPI_TAD_WAKE_DISABLED (~(u32)0) +/* ACPI TAD RTC */ +#define ACPI_TAD_TZ_UNSPEC 2047 +struct acpi_tad_driver_data {u32 capabilities; };
@@ -67,6 +70,16 @@ struct acpi_tad_rt {u8 padding[3 ];/* must be 0 */ } __packed; +static bool acpi_tad_rt_is_invalid (struct acpi_tad_rt *rt) +{ +return rt->year <1900 || rt->year >9999 || + rt->month <1 || rt->month >12 || + rt->hour >23 || rt->minute >59 || rt->second >59 || + rt->tz <-1440 || + (rt->tz >1440 && rt->tz !=ACPI_TAD_TZ_UNSPEC ) || + rt->daylight >3 ; +} +static int acpi_tad_set_real_time (struct device *dev,struct acpi_tad_rt *rt) {acpi_handle handle =ACPI_HANDLE (dev);
@@ -80,12 +93,8 @@ static int acpi_tad_set_real_time(structunsigned long long retval;acpi_status status; -if (rt->year <1900 || rt->year >9999 || - rt->month <1 || rt->month >12 || - rt->hour >23 || rt->minute >59 || rt->second >59 || - rt->tz <-1440 || (rt->tz >1440 && rt->tz !=2047 ) || - rt->daylight >3 ) -return -ERANGE ; +if (acpi_tad_rt_is_invalid (rt)) +return -EINVAL ; args[0 ].buffer.pointer = (u8 *)rt; args[0 ].buffer.length =sizeof (*rt);
@@ -102,7 +111,7 @@ static int acpi_tad_set_real_time(structreturn 0 ; } -static int acpi_tad_get_real_time (struct device *dev,struct acpi_tad_rt *rt) +static int acpi_tad_evaluate_grt (struct device *dev,struct acpi_tad_rt *rt) {acpi_handle handle =ACPI_HANDLE (dev);struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER };
@@ -111,12 +120,7 @@ static int acpi_tad_get_real_time(structacpi_status status;int ret = -EIO ; -pm_runtime_get_sync (dev); - status =acpi_evaluate_object (handle,"_GRT" ,NULL , &output); - -pm_runtime_put_sync (dev); -if (ACPI_FAILURE (status)) goto out_free;
@@ -139,6 +143,80 @@ out_free:return ret; } +static int __acpi_tad_get_real_time (struct device *dev,struct acpi_tad_rt *rt) +{ +int ret; + + ret =acpi_tad_evaluate_grt (dev, rt); +if (ret) +return ret; + +if (acpi_tad_rt_is_invalid (rt)) +return -ENODATA ; + +return 0 ; +} + +static int acpi_tad_get_real_time (struct device *dev,struct acpi_tad_rt *rt) +{ +int ret; + +pm_runtime_get_sync (dev); + + ret =__acpi_tad_get_real_time (dev, rt); + +pm_runtime_put_sync (dev); + +return ret; +} + +static int __acpi_tad_wake_set (struct device *dev,char *method,u32 timer_id, +u32 value) +{ +acpi_handle handle =ACPI_HANDLE (dev); +union acpi_object args[] = { + { .type =ACPI_TYPE_INTEGER , }, + { .type =ACPI_TYPE_INTEGER , }, + }; +struct acpi_object_list arg_list = { + .pointer = args, + .count =ARRAY_SIZE (args), + }; +unsigned long long retval; +acpi_status status; + + args[0 ].integer.value = timer_id; + args[1 ].integer.value = value; + + status =acpi_evaluate_integer (handle, method, &arg_list, &retval); +if (ACPI_FAILURE (status) || retval) +return -EIO ; + +return 0 ; +} + +static int __acpi_tad_wake_read (struct device *dev,char *method,u32 timer_id, +unsigned long long *retval) +{ +acpi_handle handle =ACPI_HANDLE (dev); +union acpi_object args[] = { + { .type =ACPI_TYPE_INTEGER , }, + }; +struct acpi_object_list arg_list = { + .pointer = args, + .count =ARRAY_SIZE (args), + }; +acpi_status status; + + args[0 ].integer.value = timer_id; + + status =acpi_evaluate_integer (handle, method, &arg_list, retval); +if (ACPI_FAILURE (status)) +return -EIO ; + +return 0 ; +} +static char *acpi_tad_rt_next_field (char *s,int *val) {char *p;
@@ -251,31 +329,15 @@ static const struct attribute_group acpistatic int acpi_tad_wake_set (struct device *dev,char *method,u32 timer_id,u32 value) { -acpi_handle handle =ACPI_HANDLE (dev); -union acpi_object args[] = { - { .type =ACPI_TYPE_INTEGER , }, - { .type =ACPI_TYPE_INTEGER , }, - }; -struct acpi_object_list arg_list = { - .pointer = args, - .count =ARRAY_SIZE (args), - }; -unsigned long long retval; -acpi_status status; - - args[0 ].integer.value = timer_id; - args[1 ].integer.value = value; +int ret;pm_runtime_get_sync (dev); - status =acpi_evaluate_integer (handle, method, &arg_list, &retval); + ret =__acpi_tad_wake_set (dev, method, timer_id, value);pm_runtime_put_sync (dev); -if (ACPI_FAILURE (status) || retval) -return -EIO ; - -return 0 ; +return ret; } static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,
@@ -301,27 +363,17 @@ static int acpi_tad_wake_write(struct destatic ssize_t acpi_tad_wake_read (struct device *dev,char *buf,char *method,u32 timer_id,const char *specval) { -acpi_handle handle =ACPI_HANDLE (dev); -union acpi_object args[] = { - { .type =ACPI_TYPE_INTEGER , }, - }; -struct acpi_object_list arg_list = { - .pointer = args, - .count =ARRAY_SIZE (args), - };unsigned long long retval; -acpi_status status; - - args[0 ].integer.value = timer_id; +int ret;pm_runtime_get_sync (dev); - status =acpi_evaluate_integer (handle, method, &arg_list, &retval); + ret =__acpi_tad_wake_read (dev, method, timer_id, &retval);pm_runtime_put_sync (dev); -if (ACPI_FAILURE (status)) -return -EIO ; +if (ret) +return ret;if ((u32 )retval ==ACPI_TAD_WAKE_DISABLED )return sprintf (buf,"%s\n" , specval);
include/linux/acpi.h +4 -0
@@ -785,7 +785,11 @@ int acpi_mrrm_max_mem_region(void); { "PNP0B02", }, \ { "", } +#ifdef CONFIG_X86 extern bool cmos_rtc_platform_device_present; +#else +#define cmos_rtc_platform_device_present false +#endif #else /* !CONFIG_ACPI */
Greg Kroah-Hartman
maintainerPATCH 480/583 ACPI: TAD: Add locking around AML evaluations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit a3df8bbe0a704fa5c1609b9666b594f350558fe0 ]
In the ACPI TAD driver, there are hidden assumptions that the ACPI control methods used by it will not be evaluated concurrently due to ACPICA namespace and interpreter locking.
However, that may not be the case since ACPICA may drop and re-acquire the namespace and interpreter locks during the evaluation of a given object in a few cases, including the one in which the AML in question sleeps causing acpi_ex_system_do_sleep() to be called. In that case, the evaluation of one control method may be started while the evaluation of another one is still in progress.
For this reason, add a global lock to the ACPI TAD driver and acquire it every time before evaluating an ACPI control method, except for the initial evaluation of _GCP in acpi_tad_probe().
--- drivers/acpi/acpi_tad.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-)
drivers/acpi/acpi_tad.c +29 -19
@@ -23,6 +23,7 @@#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/suspend.h>
@@ -80,6 +81,8 @@ static bool acpi_tad_rt_is_invalid(struc rt->daylight >3 ; } +static DEFINE_MUTEX (acpi_tad_aml_lock); +static int acpi_tad_set_real_time (struct device *dev,struct acpi_tad_rt *rt) {acpi_handle handle =ACPI_HANDLE (dev);
@@ -101,6 +104,8 @@ static int acpi_tad_set_real_time(structpm_runtime_get_sync (dev); +guard (mutex)(&acpi_tad_aml_lock); + status =acpi_evaluate_integer (handle,"_SRT" , &arg_list, &retval);pm_runtime_put_sync (dev);
@@ -115,30 +120,27 @@ static int acpi_tad_evaluate_grt(struct {acpi_handle handle =ACPI_HANDLE (dev);struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER }; -union acpi_object *out_obj; -struct acpi_tad_rt *data;acpi_status status;int ret = -EIO ; - status =acpi_evaluate_object (handle,"_GRT" ,NULL , &output); -if (ACPI_FAILURE (status)) - goto out_free; - - out_obj = output.pointer; -if (out_obj->type !=ACPI_TYPE_BUFFER ) - goto out_free; - -if (out_obj->buffer.length !=sizeof (*rt)) - goto out_free; - - data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); -if (!data->valid) - goto out_free; +guard (mutex)(&acpi_tad_aml_lock); -memcpy (rt, data,sizeof (*rt)); - ret =0 ; + status =acpi_evaluate_object (handle,"_GRT" ,NULL , &output); +if (ACPI_SUCCESS (status)) { +union acpi_object *out_obj; -out_free: + out_obj = output.pointer; +if (out_obj->type ==ACPI_TYPE_BUFFER && + out_obj->buffer.length ==sizeof (*rt)) { +struct acpi_tad_rt *data; + + data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); +if (data->valid) { +memcpy (rt, data,sizeof (*rt)); + ret =0 ; + } + } + }ACPI_FREE (output.pointer);return ret; }
@@ -188,6 +190,8 @@ static int __acpi_tad_wake_set(struct de args[0 ].integer.value = timer_id; args[1 ].integer.value = value; +guard (mutex)(&acpi_tad_aml_lock); + status =acpi_evaluate_integer (handle, method, &arg_list, &retval);if (ACPI_FAILURE (status) || retval)return -EIO ;
@@ -210,6 +214,8 @@ static int __acpi_tad_wake_read(struct d args[0 ].integer.value = timer_id; +guard (mutex)(&acpi_tad_aml_lock); + status =acpi_evaluate_integer (handle, method, &arg_list, retval);if (ACPI_FAILURE (status))return -EIO ;
@@ -424,6 +430,8 @@ static int acpi_tad_clear_status(structpm_runtime_get_sync (dev); +guard (mutex)(&acpi_tad_aml_lock); + status =acpi_evaluate_integer (handle,"_CWS" , &arg_list, &retval);pm_runtime_put_sync (dev);
@@ -465,6 +473,8 @@ static ssize_t acpi_tad_status_read(strupm_runtime_get_sync (dev); +guard (mutex)(&acpi_tad_aml_lock); + status =acpi_evaluate_integer (handle,"_GWS" , &arg_list, &retval);pm_runtime_put_sync (dev);
Greg Kroah-Hartman
maintainerPATCH 481/583 cpufreq: apple-soc: Fix OPP table cleanup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit d87cb889dc7ab1f2deecadf2a5e9023184bd7900 ]
apple_soc_cpufreq_init() adds OPP tables from firmware, but some failure paths do not remove them. The driver also uses dev_pm_opp_remove_all_dynamic(), which is not the right cleanup helper for OPP tables loaded from firmware.
Use the cpumask OPP helper after the policy CPU mask has been populated. Pair it with the matching cpumask remove helper on failure paths and in apple_soc_cpufreq_exit(). This also removes the separate dev_pm_opp_set_sharing_cpus() call, as the cpumask helper loads the DT OPP tables for all CPUs in the policy.
--- drivers/cpufreq/apple-soc-cpufreq.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-)
drivers/cpufreq/apple-soc-cpufreq.c +14 -22
@@ -251,21 +251,19 @@ static int apple_soc_cpufreq_init(structreturn -ENODEV ; } - ret =dev_pm_opp_of_add_table (cpu_dev); -if (ret <0 ) { -dev_err (cpu_dev,"%s: failed to add OPP table: %d\n" , __func__, ret); -return ret; - } + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; ret =apple_soc_cpufreq_find_cluster (policy, ®_base, &info);if (ret) {dev_err (cpu_dev,"%s: failed to get cluster info: %d\n" , __func__, ret); -return ret; + goto out_free_priv; } - ret =dev_pm_opp_set_sharing_cpus (cpu_dev, policy->cpus); -if (ret) { -dev_err (cpu_dev,"%s: failed to mark OPPs as shared: %d\n" , __func__, ret); + ret =dev_pm_opp_of_cpumask_add_table (policy->cpus); +if (ret <0 ) { +dev_err (cpu_dev,"%s: failed to add OPP table: %d\n" , __func__, ret); goto out_iounmap; }
@@ -273,19 +271,13 @@ static int apple_soc_cpufreq_init(structif (ret <=0 ) {dev_dbg (cpu_dev,"OPP table is not ready, deferring probe\n" ); ret = -EPROBE_DEFER ; - goto out_free_opp; - } - - priv =kzalloc (sizeof (*priv),GFP_KERNEL ); -if (!priv) { - ret = -ENOMEM ; - goto out_free_opp; + goto out_free_table; } ret =dev_pm_opp_init_cpufreq_table (cpu_dev, &freq_table);if (ret) {dev_err (cpu_dev,"failed to init cpufreq table: %d\n" , ret); - goto out_free_priv; + goto out_free_table; }/* Get OPP levels (p-state indexes) and stash them in driver_data */
@@ -320,12 +312,12 @@ static int apple_soc_cpufreq_init(struct out_free_cpufreq_table:dev_pm_opp_free_cpufreq_table (cpu_dev, &freq_table); -out_free_priv: -kfree (priv); -out_free_opp: -dev_pm_opp_remove_all_dynamic (cpu_dev); +out_free_table: +dev_pm_opp_of_cpumask_remove_table (policy->cpus); out_iounmap:iounmap (reg_base); +out_free_priv: +kfree (priv);return ret; }
@@ -334,7 +326,7 @@ static void apple_soc_cpufreq_exit(strucstruct apple_cpu_priv *priv = policy->driver_data;dev_pm_opp_free_cpufreq_table (priv->cpu_dev, &policy->freq_table); -dev_pm_opp_remove_all_dynamic (priv->cpu_dev); +dev_pm_opp_of_cpumask_remove_table (policy->cpus);iounmap (priv->reg_base);kfree (priv); }
Greg Kroah-Hartman
maintainerPATCH 482/583 bpf: Factor stackid_init function from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 15b837759a97237d647962f9943afe0d55af615a ]
The new stackid_init function stores all the necessary bits for stackid trace and it will be used by other functions in following changes.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 95 +++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 36 deletions(-)
kernel/bpf/stackmap.c +59 -36
@@ -246,33 +246,54 @@ get_callchain_entry_for_task(struct task#endif } -static long __bpf_get_stackid (struct bpf_map *map, -struct perf_callchain_entry *trace,u64 flags) +struct stackid { +struct stack_map_bucket *bucket; +u64 *ips; +u32 nr; +u32 len; +u32 hash; +u32 id; +}; + +static int stackid_init (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); -struct stack_map_bucket *bucket, *new_bucket, *old_bucket; -u32 hash, id, trace_nr, trace_len, i, max_depth;u32 skip = flags &BPF_F_SKIP_FIELD_MASK ; -bool user = flags &BPF_F_USER_STACK ; -u64 *ips; -bool hash_matches; +u32 max_depth;if (trace->nr <= skip)/* skipping more than usable stack trace */ return -EFAULT ; max_depth =stack_map_calculate_max_depth (map->value_size,stack_map_data_size (map), flags); - trace_nr =min_t (u32, trace->nr - skip, max_depth - skip); - trace_len = trace_nr *sizeof (u64); - ips = trace->ip + skip; - hash =jhash2 ((u32 *)ips, trace_len /sizeof (u32),0 ); - id = hash & (smap->n_buckets -1 ); - bucket =READ_ONCE (smap->buckets[id]); + stackid->nr =min_t (u32, trace->nr - skip, max_depth - skip); + stackid->len = stackid->nr *sizeof (u64); + stackid->ips = trace->ip + skip; + stackid->hash =jhash2 ((u32 *)stackid->ips, stackid->len /sizeof (u32),0 ); + stackid->id = stackid->hash & (smap->n_buckets -1 ); + stackid->bucket =READ_ONCE (smap->buckets[stackid->id]); +return 0 ; +} + +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); +struct stack_map_bucket *new_bucket, *old_bucket; +bool user = flags &BPF_F_USER_STACK ; +bool hash_matches; +u32 trace_len, i; +int err; + + err =stackid_init (stackid, map, trace, flags); +if (err) +return err; - hash_matches = bucket && bucket->hash == hash; + hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash;/* fast cmp */ if (hash_matches && flags &BPF_F_FAST_STACK_CMP ) -return id; +return stackid->id;if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -282,42 +303,42 @@ static long __bpf_get_stackid(struct bpfpcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))return -ENOMEM ; - new_bucket->nr = trace_nr; + new_bucket->nr = stackid->nr; id_offs = (struct bpf_stack_build_id *)new_bucket->data; -for (i =0 ; i < trace_nr; i++) - id_offs[i].ip = ips[i]; -stack_map_get_build_id_offset (id_offs, trace_nr, user, false/* !may_fault */ ); - trace_len = trace_nr *sizeof (struct bpf_stack_build_id ); -if (hash_matches && bucket->nr == trace_nr && -memcmp (bucket->data, new_bucket->data, trace_len) ==0 ) { +for (i =0 ; i < stackid->nr; i++) + id_offs[i].ip = stackid->ips[i]; +stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); + trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); +if (hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, new_bucket->data, trace_len) ==0 ) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode); -return id; +return stackid->id; } -if (bucket && !(flags &BPF_F_REUSE_STACKID )) { +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return -EEXIST ; } }else { -if (hash_matches && bucket->nr == trace_nr && -memcmp (bucket->data, ips, trace_len) ==0 ) -return id; -if (bucket && !(flags &BPF_F_REUSE_STACKID )) +if (hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) +return stackid->id; +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID ))return -EEXIST ; new_bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))return -ENOMEM ; -memcpy (new_bucket->data, ips, trace_len); +memcpy (new_bucket->data, stackid->ips, stackid->len); } - new_bucket->hash = hash; - new_bucket->nr = trace_nr; + new_bucket->hash = stackid->hash; + new_bucket->nr = stackid->nr; - old_bucket =xchg (&smap->buckets[id], new_bucket); + old_bucket =xchg (&smap->buckets[stackid->id], new_bucket);if (old_bucket)pcpu_freelist_push (&smap->freelist, &old_bucket->fnode); -return id; +return stackid->id; } BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
@@ -326,6 +347,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_reu32 elem_size =stack_map_data_size (map);bool user = flags &BPF_F_USER_STACK ;struct perf_callchain_entry *trace; +struct stackid stackid;bool kernel = !user;u32 max_depth;
@@ -341,7 +363,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re/* couldn't fetch the stack trace */ return -EFAULT ; -return __bpf_get_stackid (map, trace, flags); +return __bpf_get_stackid (&stackid, map, trace, flags); }const struct bpf_func_proto bpf_get_stackid_proto = {
@@ -370,6 +392,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bp {struct perf_event *event = ctx->event;struct perf_callchain_entry *trace; +struct stackid stackid;bool kernel, user;__u64 nr_kernel;int ret;
@@ -395,7 +418,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpif (kernel) { trace->nr = nr_kernel; - ret =__bpf_get_stackid (map, trace, flags); + ret =__bpf_get_stackid (&stackid, map, trace, flags); }else {/* user */ u64 skip = flags &BPF_F_SKIP_FIELD_MASK ;
@@ -404,7 +427,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpreturn -EFAULT ; flags = (flags & ~BPF_F_SKIP_FIELD_MASK ) | skip; - ret =__bpf_get_stackid (map, trace, flags); + ret =__bpf_get_stackid (&stackid, map, trace, flags); }/* restore nr */
Greg Kroah-Hartman
maintainerPATCH 483/583 bpf: Factor stackid_fastpath function from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 0ca56befcffec3a6c9d1842eae06c74e1cf41f11 ]
The new stackid_fastpath does the fast stack hash and trace check, that does not need new bucket allocation. It covers both just-ip and buildid code paths.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-)
kernel/bpf/stackmap.c +28 -15
@@ -253,6 +253,7 @@ struct stackid {u32 len;u32 hash;u32 id; +bool hash_matches; }; static int stackid_init(struct stackid *stackid, struct bpf_map *map,
@@ -273,28 +274,46 @@ static int stackid_init(struct stackid * stackid->hash =jhash2 ((u32 *)stackid->ips, stackid->len /sizeof (u32),0 ); stackid->id = stackid->hash & (smap->n_buckets -1 ); stackid->bucket =READ_ONCE (smap->buckets[stackid->id]); + stackid->hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash;return 0 ; } +static int stackid_fastpath (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) +{ +int err; + + err =stackid_init (stackid, map, trace, flags); +if (err) +return err; + +/* fast cmp */ +if (stackid->hash_matches && flags &BPF_F_FAST_STACK_CMP ) +return stackid->id; + +if (stack_map_use_build_id (map)) +return -ENOENT ; +if (stackid->hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) +return stackid->id; +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) +return -EEXIST ; +return -ENOENT ; +} +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map,struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; -bool hash_matches;u32 trace_len, i;int err; - err =stackid_init (stackid, map, trace, flags); -if (err) + err =stackid_fastpath (stackid, map, trace, flags); +if (err != -ENOENT )return err; - hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash; -/* fast cmp */ -if (hash_matches && flags &BPF_F_FAST_STACK_CMP ) -return stackid->id; -if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -309,7 +328,7 @@ static long __bpf_get_stackid(struct sta id_offs[i].ip = stackid->ips[i];stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); -if (hash_matches && stackid->bucket->nr == stackid->nr && +if (stackid->hash_matches && stackid->bucket->nr == stackid->nr &&memcmp (stackid->bucket->data, new_bucket->data, trace_len) ==0 ) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return stackid->id;
@@ -319,12 +338,6 @@ static long __bpf_get_stackid(struct stareturn -EEXIST ; } }else { -if (hash_matches && stackid->bucket->nr == stackid->nr && -memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) -return stackid->id; -if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) -return -EEXIST ; - new_bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))
Greg Kroah-Hartman
maintainerPATCH 484/583 bpf: Factor stackid_new_bucket from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit bb4e6f4e1b68fe60c04ca04c564c6624e837dbf4 ]
The new stackid_new_bucket allocates the new bucket and initializes it with the trace data.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-)
kernel/bpf/stackmap.c +30 -18
@@ -301,31 +301,52 @@ static int stackid_fastpath(struct stackreturn -ENOENT ; } +static struct stack_map_bucket * +stackid_new_bucket (struct stackid *stackid,struct bpf_map *map) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); +struct bpf_stack_build_id *id_offs; +struct stack_map_bucket *bucket; +u32 i; + + bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist); +if (unlikely (!bucket)) +return NULL ; + +if (stack_map_use_build_id (map)) { + id_offs = (struct bpf_stack_build_id *)bucket->data; +for (i =0 ; i < stackid->nr; i++) + id_offs[i].ip = stackid->ips[i]; + }else { +memcpy (bucket->data, stackid->ips, stackid->len); + } + + bucket->hash = stackid->hash; + bucket->nr = stackid->nr; +return bucket; +} +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map,struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; -u32 trace_len, i; +u32 trace_len;int err; err =stackid_fastpath (stackid, map, trace, flags);if (err != -ENOENT )return err; + new_bucket =stackid_new_bucket (stackid, map); +if (!new_bucket) +return -ENOMEM ; +if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs; -/* for build_id+offset, pop a bucket before slow cmp */ - new_bucket = (struct stack_map_bucket *) -pcpu_freelist_pop (&smap->freelist); -if (unlikely (!new_bucket)) -return -ENOMEM ; - new_bucket->nr = stackid->nr; id_offs = (struct bpf_stack_build_id *)new_bucket->data; -for (i =0 ; i < stackid->nr; i++) - id_offs[i].ip = stackid->ips[i];stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); if (stackid->hash_matches && stackid->bucket->nr == stackid->nr &&
@@ -337,17 +358,8 @@ static long __bpf_get_stackid(struct stapcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return -EEXIST ; } - }else { - new_bucket = (struct stack_map_bucket *) -pcpu_freelist_pop (&smap->freelist); -if (unlikely (!new_bucket)) -return -ENOMEM ; -memcpy (new_bucket->data, stackid->ips, stackid->len); } - new_bucket->hash = stackid->hash; - new_bucket->nr = stackid->nr; - old_bucket =xchg (&smap->buckets[stackid->id], new_bucket);if (old_bucket)pcpu_freelist_push (&smap->freelist, &old_bucket->fnode);
Greg Kroah-Hartman
maintainerPATCH 485/583 bpf: Use stack id functions instead of __bpf_get_stackid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 09b3fd6caa0b57f8a39254ee5db3af30bdd53c18 ]
Replacing __bpf_get_stackid calls with sequence of following functions:
stackid_fastpath
stackid_new_bucket
stackid_install
This makes code more structured and allows us to easily disable preemption only in bpf_get_stackid in following changes.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-)
kernel/bpf/stackmap.c +27 -16
@@ -326,22 +326,13 @@ stackid_new_bucket(struct stackid *stackreturn bucket; } -static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map, -struct perf_callchain_entry *trace,u64 flags) +static long stackid_install (struct stackid *stackid,struct bpf_map *map, +struct stack_map_bucket *new_bucket,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); -struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; +struct stack_map_bucket *old_bucket;u32 trace_len; -int err; - - err =stackid_fastpath (stackid, map, trace, flags); -if (err != -ENOENT ) -return err; - - new_bucket =stackid_new_bucket (stackid, map); -if (!new_bucket) -return -ENOMEM ;if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -371,10 +362,12 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re {u32 elem_size =stack_map_data_size (map);bool user = flags &BPF_F_USER_STACK ; +struct stack_map_bucket *new_bucket;struct perf_callchain_entry *trace;struct stackid stackid;bool kernel = !user;u32 max_depth; +int err;if (unlikely (flags & ~(BPF_F_SKIP_FIELD_MASK |BPF_F_USER_STACK |BPF_F_FAST_STACK_CMP |BPF_F_REUSE_STACKID )))
@@ -388,7 +381,15 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re/* couldn't fetch the stack trace */ return -EFAULT ; -return __bpf_get_stackid (&stackid, map, trace, flags); + err =stackid_fastpath (&stackid, map, trace, flags); +if (err != -ENOENT ) +return err; + + new_bucket =stackid_new_bucket (&stackid, map); +if (!new_bucket) +return -ENOMEM ; + +return stackid_install (&stackid, map, new_bucket, flags); }const struct bpf_func_proto bpf_get_stackid_proto = {
@@ -416,6 +417,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bp struct bpf_map *, map, u64, flags) {struct perf_event *event = ctx->event; +struct stack_map_bucket *new_bucket;struct perf_callchain_entry *trace;struct stackid stackid;bool kernel, user;
@@ -443,7 +445,6 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpif (kernel) { trace->nr = nr_kernel; - ret =__bpf_get_stackid (&stackid, map, trace, flags); }else {/* user */ u64 skip = flags &BPF_F_SKIP_FIELD_MASK ;
@@ -452,12 +453,22 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpreturn -EFAULT ; flags = (flags & ~BPF_F_SKIP_FIELD_MASK ) | skip; - ret =__bpf_get_stackid (&stackid, map, trace, flags); } + ret =stackid_fastpath (&stackid, map, trace, flags); +if (ret != -ENOENT ) + goto out; + + new_bucket =stackid_new_bucket (&stackid, map); +if (new_bucket) { + trace->nr = nr; +return stackid_install (&stackid, map, new_bucket, flags); + } + ret = -ENOMEM ; + +out:/* restore nr */ trace->nr = nr; -return ret; }
Greg Kroah-Hartman
maintainerPATCH 486/583 bpf: Disable preemption in bpf_get_stackid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 15f1bd8574662f1b7b26aaa2e23ebf4066f0117d ]
The get_perf_callchain call needs disabled preemption plus we need it disabled as long as we access its returned trace entries buffer.
Note the bpf_get_stackid_pe function is executed already with preemption disabled.
--- kernel/bpf/stackmap.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
kernel/bpf/stackmap.c +13 -11
@@ -374,20 +374,22 @@ BPF_CALL_3(bpf_get_stackid, struct pt_rereturn -EINVAL ; max_depth =stack_map_calculate_max_depth (map->value_size, elem_size, flags); - trace =get_perf_callchain (regs, kernel, user, max_depth, - false, false); -if (unlikely (!trace)) -/* couldn't fetch the stack trace */ -return -EFAULT ; +scoped_guard (preempt) { + trace =get_perf_callchain (regs, kernel, user, max_depth, + false, false); +if (unlikely (!trace)) +/* couldn't fetch the stack trace */ +return -EFAULT ; - err =stackid_fastpath (&stackid, map, trace, flags); -if (err != -ENOENT ) -return err; + err =stackid_fastpath (&stackid, map, trace, flags); +if (err != -ENOENT ) +return err; - new_bucket =stackid_new_bucket (&stackid, map); -if (!new_bucket) -return -ENOMEM ; + new_bucket =stackid_new_bucket (&stackid, map); +if (!new_bucket) +return -ENOMEM ; + }return stackid_install (&stackid, map, new_bucket, flags); }
Greg Kroah-Hartman
maintainerPATCH 487/583 cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit 9d399526128bae184b153deab42b49f5f3eea2ed ]
cxl_rch_get_aer_severity() classifies RCH Downstream Port uncorrectable errors as fatal or non-fatal by ANDing uncorrectable status with PCI_ERR_ROOT_FATAL_RCV. This is wrong because PCI_ERR_ROOT_FATAL_RCV is a Root Error Status register bit (bit 6), not a severity bit. ANDing it against uncorrectable status tests a reserved bit and produces incorrect severity classification.
Fix by ANDing the unmasked uncor_status against uncor_severity. Per PCIe Base Spec r6.0 Section 7.8.4.4, each bit in the Uncorrectable Error Severity register indicates whether the corresponding error is fatal (1) or non-fatal (0).
--- drivers/cxl/core/ras_rch.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/cxl/core/ras_rch.c +5 -5
@@ -94,11 +94,11 @@ static bool cxl_rch_get_aer_info(void __static bool cxl_rch_get_aer_severity (struct aer_capability_regs *aer_regs,int *severity) { -if (aer_regs->uncor_status & ~aer_regs->uncor_mask) { -if (aer_regs->uncor_status &PCI_ERR_ROOT_FATAL_RCV ) - *severity =AER_FATAL ; -else - *severity =AER_NONFATAL ; +u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask; + +if (uncor_status) { + *severity = (uncor_status & aer_regs->uncor_severity) ? +AER_FATAL :AER_NONFATAL ;return true; }
Greg Kroah-Hartman
maintainerPATCH 488/583 dax/cxl, hmem: Initialize hmem early and defer dax_cxl binding
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Williams <[email protected]>
[ Upstream commit 39aa1d4be12bf9f685adaa06aa2d997c1c611b16 ]
Move hmem/ earlier in the dax Makefile so that hmem_init() runs before dax_cxl.
In addition, defer registration of the dax_cxl driver to a workqueue instead of using module_cxl_driver(). This ensures that dax_hmem has an opportunity to initialize and register its deferred callback and make ownership decisions before dax_cxl begins probing and claiming Soft Reserved ranges.
Mark the dax_cxl driver as PROBE_PREFER_ASYNCHRONOUS so its probe runs out of line from other synchronous probing avoiding ordering dependencies while coordinating ownership decisions with dax_hmem.
Stable-dep-of: 775d0f4558f4 ("cxl/mce: Make the MCE notifier per-region")
--- drivers/dax/Makefile | 3 +-- drivers/dax/cxl.c | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-)
drivers/dax/Makefile +1 -2
@@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-y += hmem/ obj-$(CONFIG_DAX) += dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o
@@ -10,5 +11,3 @@ dax-y += bus.o device_dax-y := device.o dax_pmem-y := pmem.o dax_cxl-y := cxl.o - -obj-y += hmem/
drivers/dax/cxl.c +26 -1
@@ -38,10 +38,35 @@ static struct cxl_driver cxl_dax_region_ .id = CXL_DEVICE_DAX_REGION, .drv = { .suppress_bind_attrs = true, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; -module_cxl_driver (cxl_dax_region_driver); +static void cxl_dax_region_driver_register (struct work_struct *work) +{ +cxl_driver_register (&cxl_dax_region_driver); +} + +static DECLARE_WORK(cxl_dax_region_driver_work, cxl_dax_region_driver_register); + +static int __init cxl_dax_region_init (void ) +{ +/* +* Need to resolve a race with dax_hmem wanting to drive regions +* instead of CXL +*/ +queue_work (system_long_wq, &cxl_dax_region_driver_work); +return 0 ; +} +module_init (cxl_dax_region_init); + +static void __exit cxl_dax_region_exit(void) +{ +flush_work (&cxl_dax_region_driver_work); +cxl_driver_unregister (&cxl_dax_region_driver); +} +module_exit (cxl_dax_region_exit); +MODULE_ALIAS_CXL (CXL_DEVICE_DAX_REGION );MODULE_DESCRIPTION ("CXL DAX: direct access to CXL regions" );MODULE_LICENSE ("GPL" );
Greg Kroah-Hartman
maintainerPATCH 489/583 cxl/region: Add helper to check Soft Reserved containment by CXL regions
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Smita Koralahalli <[email protected]>
[ Upstream commit 8e65f99b525b3f49b87db0db0d0e0fc1a0c53e40 ]
Add a helper to determine whether a given Soft Reserved memory range is fully contained within the committed CXL region.
This helper provides a primitive for policy decisions in subsequent patches such as co-ordination with dax_hmem to determine whether CXL has fully claimed ownership of Soft Reserved memory ranges.
[ sashal: Reduced backport -- upstream 8e65f99b525b3 touches 2 file(s), this
backport carries 2. Not backported here:
include/cxl/cxl.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 775d0f4558f4 ("cxl/mce: Make the MCE notifier per-region")
--- drivers/cxl/core/region.c | 83 ++++++++++++++++++++++++++++++++-------------- drivers/cxl/cxl.h | 5 ++ 2 files changed, 63 insertions(+), 25 deletions(-)
drivers/cxl/core/region.c +58 -25
@@ -3751,6 +3751,61 @@ static int cxl_region_debugfs_poison_cleDEFINE_DEBUGFS_ATTRIBUTE (cxl_poison_clear_fops,NULL , cxl_region_debugfs_poison_clear,"%llx\n" ); +static int cxl_region_setup_poison (struct cxl_region *cxlr) +{ +struct device *dev = &cxlr->dev; +struct cxl_region_params *p = &cxlr->params; +struct dentry *dentry; + +/* Create poison attributes if all memdevs support the capabilities */ +for (int i =0 ; i < p->nr_targets; i++) { +struct cxl_endpoint_decoder *cxled = p->targets[i]; +struct cxl_memdev *cxlmd =cxled_to_memdev (cxled); + +if (!cxl_memdev_has_poison_cmd (cxlmd,CXL_POISON_ENABLED_INJECT ) || + !cxl_memdev_has_poison_cmd (cxlmd,CXL_POISON_ENABLED_CLEAR )) +return 0 ; + } + + dentry =cxl_debugfs_create_dir (dev_name (dev)); +debugfs_create_file ("inject_poison" ,0200 , dentry, cxlr, + &cxl_poison_inject_fops); +debugfs_create_file ("clear_poison" ,0200 , dentry, cxlr, + &cxl_poison_clear_fops); + +return devm_add_action_or_reset (dev, remove_debugfs, dentry); +} + +static int region_contains_resource (struct device *dev,const void *data) +{ +const struct resource *res = data; +struct cxl_region *cxlr; +struct cxl_region_params *p; + +if (!is_cxl_region (dev)) +return 0 ; + + cxlr =to_cxl_region (dev); + p = &cxlr->params; + +if (p->state !=CXL_CONFIG_COMMIT ) +return 0 ; + +if (!p->res) +return 0 ; + +return resource_contains (p->res, res) ?1 :0 ; +} + +bool cxl_region_contains_resource (const struct resource *res) +{ +guard (rwsem_read)(&cxl_rwsem.region); + struct device *dev __free(put_device) = bus_find_device( + &cxl_bus_type,NULL , res, region_contains_resource); +return !!dev; +} +EXPORT_SYMBOL_FOR_MODULES (cxl_region_contains_resource,"dax_hmem" ); +static int cxl_region_can_probe (struct cxl_region *cxlr) {struct cxl_region_params *p = &cxlr->params;
@@ -3780,7 +3835,6 @@ static int cxl_region_probe(struct devic {struct cxl_region *cxlr =to_cxl_region (dev);struct cxl_region_params *p = &cxlr->params; -bool poison_supported = true;int rc; rc =cxl_region_can_probe (cxlr);
@@ -3804,30 +3858,9 @@ static int cxl_region_probe(struct devicif (rc)return rc; -/* Create poison attributes if all memdevs support the capabilities */ -for (int i =0 ; i < p->nr_targets; i++) { -struct cxl_endpoint_decoder *cxled = p->targets[i]; -struct cxl_memdev *cxlmd =cxled_to_memdev (cxled); - -if (!cxl_memdev_has_poison_cmd (cxlmd,CXL_POISON_ENABLED_INJECT ) || - !cxl_memdev_has_poison_cmd (cxlmd,CXL_POISON_ENABLED_CLEAR )) { - poison_supported = false; -break ; - } - } - -if (poison_supported) { -struct dentry *dentry; - - dentry =cxl_debugfs_create_dir (dev_name (dev)); -debugfs_create_file ("inject_poison" ,0200 , dentry, cxlr, - &cxl_poison_inject_fops); -debugfs_create_file ("clear_poison" ,0200 , dentry, cxlr, - &cxl_poison_clear_fops); - rc =devm_add_action_or_reset (dev, remove_debugfs, dentry); -if (rc) -return rc; - } + rc =cxl_region_setup_poison (cxlr); +if (rc) +return rc;switch (cxlr->mode) {case CXL_PARTMODE_PMEM :
drivers/cxl/cxl.h +5 -0
@@ -887,6 +887,7 @@ struct cxl_pmem_region *to_cxl_pmem_regiint cxl_add_to_region (struct cxl_endpoint_decoder *cxled);struct cxl_dax_region *to_cxl_dax_region (struct device *dev);u64 cxl_port_get_spa_cache_alias (struct cxl_port *endpoint,u64 spa); +bool cxl_region_contains_resource (const struct resource *res);#else static inline bool is_cxl_pmem_region (struct device *dev) {
@@ -909,6 +910,10 @@ static inline u64 cxl_port_get_spa_cache {return 0 ; } +static inline bool cxl_region_contains_resource (const struct resource *res) +{ +return false; +}#endif void cxl_endpoint_parse_cdat (struct cxl_port *port);
Greg Kroah-Hartman
maintainerPATCH 490/583 cxl/mce: Make the MCE notifier per-region
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Jiang <[email protected]>
[ Upstream commit 775d0f4558f4cec0ee0c8966595d1add1791f36e ]
Flavien Solt reported lifetime issues with the CXL MCE notifier, which can lead to NULL dereferences and use-after-free in the MCE handler. The notifier was registered per memory device and stored in 'struct cxl_memdev_state', even though it only needs the region state (the region's SPA range and its extended linear cache size).
Instead of keeping the memory device and endpoint alive, the correct fix is to move the notifier into 'struct cxl_region' and register it from cxl_region_probe() as it should be a per-region notifier. Setup the registration to only happen for regions that have an extended linear cache as that is the only current usage.
Remove cxl_port_get_spa_cache_alias() as it is now dead code.
[ dj: Update dev_warn() when notifier fails due to kconfig. (Ben) ]
--- drivers/cxl/core/mbox.c | 8 -------- drivers/cxl/core/mce.c | 27 +++++++++++++-------------- drivers/cxl/core/region.c | 43 +++++++++++++++---------------------------- drivers/cxl/cxl.h | 8 ++------ drivers/cxl/cxlmem.h | 2 -- 5 files changed, 30 insertions(+), 58 deletions(-)
drivers/cxl/core/mbox.c +0 -8
@@ -11,7 +11,6 @@#include "core.h" #include "trace.h" -#include "mce.h" static bool cxl_raw_allow_all;
@@ -1524,7 +1523,6 @@ EXPORT_SYMBOL_NS_GPL(cxl_mailbox_init, "struct cxl_memdev_state *cxl_memdev_state_create (struct device *dev) {struct cxl_memdev_state *mds; -int rc; mds =devm_kzalloc (dev,sizeof (*mds),GFP_KERNEL );if (!mds) {
@@ -1539,12 +1537,6 @@ struct cxl_memdev_state *cxl_memdev_stat mds->cxlds.reg_map.resource =CXL_RESOURCE_NONE ; mds->cxlds.type =CXL_DEVTYPE_CLASSMEM ; - rc =devm_cxl_register_mce_notifier (dev, &mds->mce_notifier); -if (rc == -EOPNOTSUPP ) -dev_warn (dev,"CXL MCE unsupported\n" ); -else if (rc) -return ERR_PTR (rc); -return mds; }EXPORT_SYMBOL_NS_GPL (cxl_memdev_state_create,"CXL" );
drivers/cxl/core/mce.c +13 -14
@@ -4,16 +4,16 @@#include <linux/notifier.h> #include <linux/set_memory.h> #include <asm/mce.h> -#include <cxlmem.h> +#include <cxl.h> +#include "core.h" #include "mce.h" static int cxl_handle_mce (struct notifier_block *nb,unsigned long val,void *data) { - struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state, - mce_notifier); -struct cxl_memdev *cxlmd = mds->cxlds.cxlmd; -struct cxl_port *endpoint = cxlmd->endpoint; + struct cxl_region *cxlr = container_of(nb, struct cxl_region, + mce_notifier); +struct cxl_region_params *p = &cxlr->params;struct mce *mce = data;u64 spa, spa_alias;unsigned long pfn;
@@ -21,26 +21,25 @@ static int cxl_handle_mce(struct notifieif (!mce || !mce_usable_address (mce))return NOTIFY_DONE ; -if (!endpoint) -return NOTIFY_DONE ; - spa = mce->addr &MCI_ADDR_PHYSADDR ; - pfn = spa >>PAGE_SHIFT ; -if (!pfn_valid (pfn)) +if (!cxl_resource_contains_addr (p->res, spa))return NOTIFY_DONE ; - spa_alias =cxl_port_get_spa_cache_alias (endpoint, spa); -if (spa_alias == ~0ULL ) -return NOTIFY_DONE ; +if (spa >= p->res->start + p->cache_size) + spa_alias = spa - p->cache_size; +else + spa_alias = spa + p->cache_size; pfn = spa_alias >>PAGE_SHIFT ; +if (!pfn_valid (pfn)) +return NOTIFY_DONE ;/* * Take down the aliased memory page. The original memory page flagged * by the MCE will be taken cared of by the standard MCE handler. */ -dev_emerg (mds->cxlds.dev,"Offlining aliased SPA address0: %#llx\n" , +dev_emerg (&cxlr->dev,"Offlining aliased SPA address0: %#llx\n" , spa_alias);if (!memory_failure (pfn,0 ))set_mce_nospec (pfn);
drivers/cxl/core/region.c +15 -28
61 lines not on this page · raw message
drivers/cxl/cxl.h +2 -6
@@ -540,6 +540,7 @@ enum cxl_partition_mode {* @coord: QoS access coordinates for the region * @node_notifier: notifier for setting the access coordinates to node * @adist_notifier: notifier for calculating the abstract distance of node +* @mce_notifier: notifier for MCE */ struct cxl_region {struct device dev;
@@ -553,6 +554,7 @@ struct cxl_region {struct access_coordinate coord[ACCESS_COORDINATE_MAX ];struct notifier_block node_notifier;struct notifier_block adist_notifier; +struct notifier_block mce_notifier; };struct cxl_nvdimm_bridge {
@@ -886,7 +888,6 @@ bool is_cxl_pmem_region(struct device *dstruct cxl_pmem_region *to_cxl_pmem_region (struct device *dev);int cxl_add_to_region (struct cxl_endpoint_decoder *cxled);struct cxl_dax_region *to_cxl_dax_region (struct device *dev); -u64 cxl_port_get_spa_cache_alias (struct cxl_port *endpoint,u64 spa);bool cxl_region_contains_resource (const struct resource *res);#else static inline bool is_cxl_pmem_region (struct device *dev)
@@ -905,11 +906,6 @@ static inline struct cxl_dax_region *to_ {return NULL ; } -static inline u64 cxl_port_get_spa_cache_alias (struct cxl_port *endpoint, -u64 spa) -{ -return 0 ; -}static inline bool cxl_region_contains_resource (const struct resource *res) {return false;
drivers/cxl/cxlmem.h +0 -2
@@ -484,7 +484,6 @@ static inline struct cxl_dev_state *mbox* @poison: poison driver state info * @security: security driver state info * @fw: firmware upload / activation state -* @mce_notifier: MCE notifier * * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for * details on capacity parameters.
@@ -504,7 +503,6 @@ struct cxl_memdev_state {struct cxl_poison_state poison;struct cxl_security_state security;struct cxl_fw_state fw; -struct notifier_block mce_notifier; }; static inline struct cxl_memdev_state *
Greg Kroah-Hartman
maintainerPATCH 491/583 SUNRPC: fix gssx_dec_option_array error path bugs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
[ Upstream commit 5e9a94539b1ec17a89177d952badfd0d844d694a ]
Four coupled defects in the gssx XDR option-array decoder make the error paths unsafe: a NULL deref in the caller, a refcount leak on the decoded group_info, and a latent use-after-free that the leak fix would otherwise expose.
gssx_dec_option_array() sets oa->count = 1 before allocating oa->data. If that allocation fails, -ENOMEM is returned with oa->count == 1 and oa->data == NULL. All other error paths jump to free_oa: which frees oa->data and NULLs it but also leaves oa->count == 1. The caller trusts the count:
gssp_accept_sec_context_upcall()
gssx_dec_accept_sec_context()
gssx_dec_option_array() /* fails, count=1 data=NULL */
data = res.options.data[0].value /* NULL deref */
Independently, free_creds: releases the partially decoded svc_cred with a bare kfree(creds). gssx_dec_linux_creds() installs a groups_alloc() result into creds->cr_group_info; that object is kvmalloc-backed and refcounted, and only put_group_info() reaches kvfree(). A plain kfree(creds) drops the wrapper and leaks the group_info allocation.
The natural fix for the leak is to call free_svc_cred(creds) before kfree(creds), but free_svc_cred() invokes put_group_info() on creds->cr_group_info unconditionally when non-NULL. The existingout_free_groups: path in gssx_dec_linux_creds() already called groups_free() on that pointer without clearing it, so once free_svc_cred() is wired in, the subsequent put_group_info() would touch freed memory.
Fix all four together:
- Move the oa->count = 1 assignment below the oa->data allocation
so it is never set when oa->data is NULL.
- Reset oa->count to 0 at free_oa: so count and data stay coherent and the caller sees an empty option array.
- Call free_svc_cred(creds) before kfree(creds) at free_creds:
so the refcounted cr_group_info is released. free_svc_cred() either NULL-guards each field explicitly (cr_group_info has an if() check) or delegates to a helper that is NULL-safe itself (kfree for the string fields, gss_mech_put() which guards with if(gm) at gss_mech_switch.c:342), so it is safe to call on a partially decoded svc_cred where only cr_uid/cr_gid/cr_group_info have been written and everything else is zero from kzalloc.
- In gssx_dec_linux_creds()'s out_free_groups: path, release cr_group_info with put_group_info() rather than groups_free() so the teardown matches free_svc_cred()'s refcount-aware path, and clear the pointer so a later free_svc_cred() on the same creds does not release it a second time.
--- net/sunrpc/auth_gss/gss_rpc_xdr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
net/sunrpc/auth_gss/gss_rpc_xdr.c +6 -3
@@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xreturn 0 ; out_free_groups: -groups_free (creds->cr_group_info); +put_group_info (creds->cr_group_info); + creds->cr_group_info =NULL ;return err; }
@@ -243,12 +244,12 @@ static int gssx_dec_option_array(structreturn 0 ;/* we recognize only 1 currently: CREDS_VALUE */ - oa->count =1 ; - oa->data =kmalloc (sizeof (struct gssx_option ),GFP_KERNEL );if (!oa->data)return -ENOMEM ; + oa->count =1 ; + creds =kzalloc (sizeof (struct svc_cred ),GFP_KERNEL );if (!creds) { err = -ENOMEM ;
@@ -300,8 +301,10 @@ static int gssx_dec_option_array(structreturn 0 ; free_creds: +free_svc_cred (creds);kfree (creds); free_oa: + oa->count =0 ;kfree (oa->data); oa->data =NULL ;return err;
Greg Kroah-Hartman
maintainerPATCH 492/583 svcrdma: Release transport resources synchronously
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit bf94dea7fd4e6708d1a784be23db65eff84d82f1 ]
NFSD has always supported added network listeners. The new netlink protocol now enables the removal of listeners.
Olga noticed that if an RDMA listener is removed and immediately re-added, the deferred __svc_rdma_free() function might not have run yet, so some or all of the old listener's RDMA resources linger, which prevents a new listener on the same address from being created.
Also, svc_xprt_free() does a module_put() just after calling ->xpo_free(). That means if there is deferred work going on, the module could be unloaded before that work is even started, resulting in a UAF.
Neil asks:
Quoted 6 lines
What particular part of
__svc_rdma_free()needs to run in order for a subsequent registration to succeed?
Can that bit be run directory fromsvc_rdma_free()rather than be delayed?
(I know almost nothing about rdma so forgive me if the answers to these questions seems obvious)
The reasons I can recall are:
- Some of the transport tear-down work can sleep
- Releasing a cm_id is tricky and can deadlock
We might be able to mitigate the second issue with judicious application of transport reference counting.
Stable-dep-of: 4488e9129737 ("svcrdma: Reorder rpcrdma_rn_unregister before rdma_destroy_id")
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +8 -11
@@ -613,12 +613,18 @@ static void svc_rdma_detach(struct svc_xrdma_disconnect (rdma->sc_cm_id); } -static void __svc_rdma_free (struct work_struct *work) +/** +* svc_rdma_free - Release class-specific transport resources +* @xprt: Generic svc transport object +*/ +static void svc_rdma_free (struct svc_xprt *xprt) {struct svcxprt_rdma *rdma = - container_of(work, struct svcxprt_rdma, sc_work); + container_of(xprt, struct svcxprt_rdma, sc_xprt);struct ib_device *device = rdma->sc_cm_id->device; +might_sleep (); +/* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR (rdma->sc_qp))ib_drain_qp (rdma->sc_qp);
@@ -651,15 +657,6 @@ static void __svc_rdma_free(struct work_kfree (rdma); } -static void svc_rdma_free (struct svc_xprt *xprt) -{ -struct svcxprt_rdma *rdma = - container_of(xprt, struct svcxprt_rdma, sc_xprt); - -INIT_WORK (&rdma->sc_work, __svc_rdma_free); -schedule_work (&rdma->sc_work); -} -static int svc_rdma_has_wspace (struct svc_xprt *xprt) { struct svcxprt_rdma *rdma =
Greg Kroah-Hartman
maintainerPATCH 493/583 svcrdma: Reorder rpcrdma_rn_unregister before rdma_destroy_id
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 4488e912973773d64368828acf3b8e39d93650ae ]
svc_rdma_free() caches rdma->sc_cm_id->device before teardown, then calls rdma_destroy_id(sc_cm_id) which frees the cm_id. rpcrdma_rn_unregister() follows, but between those two calls the transport's sc_rn entry is still installed in the device's rd_xa. A concurrent ib_unregister_device walk can dispatch svc_rdma_xprt_done() against the now-freed sc_cm_id.
Move rpcrdma_rn_unregister() before rdma_destroy_id() so the transport's notification entry is removed from the xarray before the cm_id it references is destroyed.
Also guard the sc_cm_id dereference with a NULL check: the following patches introduce paths that reach svc_rdma_free() with sc_cm_id == NULL (listener create failure, ADDR_CHANGE replacement failure).
--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_transport.c +10 -3
@@ -621,10 +621,15 @@ static void svc_rdma_free(struct svc_xpr {struct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); -struct ib_device *device = rdma->sc_cm_id->device; +struct ib_device *device;might_sleep (); +if (!rdma->sc_cm_id) + goto out_free; + + device = rdma->sc_cm_id->device; +/* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR (rdma->sc_qp))ib_drain_qp (rdma->sc_qp);
@@ -649,11 +654,13 @@ static void svc_rdma_free(struct svc_xprif (rdma->sc_pd && !IS_ERR (rdma->sc_pd))ib_dealloc_pd (rdma->sc_pd); +if (!test_bit (XPT_LISTENER , &rdma->sc_xprt.xpt_flags)) +rpcrdma_rn_unregister (device, &rdma->sc_rn); +/* Destroy the CM ID */ rdma_destroy_id (rdma->sc_cm_id); -if (!test_bit (XPT_LISTENER , &rdma->sc_xprt.xpt_flags)) -rpcrdma_rn_unregister (device, &rdma->sc_rn); +out_free:kfree (rdma); }
Greg Kroah-Hartman
maintainerPATCH 494/583 rpcrdma: arm rn_done before publishing the notification
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 5b06f706374c37375bdff9d21cc10e61df925a92 ]
rpcrdma_rn_register() inserts @rn into rd_xa with xa_alloc() before storing the caller's callback in rn->rn_done. The xarray makes @rn reachable to rpcrdma_remove_one(), which walks rd_xa and invokes rn->rn_done(rn) for every registered notification. A device removal that races a fresh registration can therefore observe @rn with rn_done still NULL, because the notification objects are zero allocated by their owners, and call through a NULL function pointer.
Store rn->rn_done before xa_alloc() publishes @rn. The xarray's store-side and load-side ordering then guarantees that any CPU which finds @rn in rd_xa also observes the armed callback.
rpcrdma_rn_unregister() treats a non-NULL rn_done as the sentinel for a completed registration, so the early store must not survive a failed registration. Clear rn_done again when xa_alloc() fails. Were it left set, the failed-accept cleanup path would call rpcrdma_rn_unregister() on an @rn that was never inserted, erasing an unrelated rd_xa slot and underflowing rd_kref.
--- net/sunrpc/xprtrdma/ib_client.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
net/sunrpc/xprtrdma/ib_client.c +18 -6
@@ -52,8 +52,8 @@ static struct rpcrdma_device *rpcrdma_ge* is unregistered first. * * On failure, a negative errno is returned. rn->rn_done is left -* NULL on every failure path (it is assigned only after xa_alloc -* and kref_get have both succeeded), so the @rn may safely be +* NULL on every failure path (it is armed before xa_alloc but +* cleared again if xa_alloc fails), so the @rn may safely be * passed to rpcrdma_rn_unregister() without a separate * registered/unregistered flag in the caller. */
@@ -66,10 +66,21 @@ int rpcrdma_rn_register(struct ib_deviceif (!rd ||test_bit (RPCRDMA_RD_F_REMOVING , &rd->rd_flags))return -ENETUNREACH ; -if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) +/* +* Arm rn_done before xa_alloc() publishes @rn: once @rn is +* visible in rd_xa, a concurrent rpcrdma_remove_one() can +* call rn->rn_done(), so the pointer must already be set. +* +* Restore NULL if xa_alloc() fails. rn_done doubles as the +* registration sentinel for rpcrdma_rn_unregister(); a stale +* value would unregister an @rn that was never inserted. +*/ + rn->rn_done = done; +if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) { + rn->rn_done =NULL ;return -ENOMEM ; + }kref_get (&rd->rd_kref); - rn->rn_done = done;trace_rpcrdma_client_register (device, rn);return 0 ; }
@@ -102,8 +113,9 @@ void rpcrdma_rn_unregister(struct ib_dev/* * rn_done is the registration sentinel: rpcrdma_rn_register -* assigns it last, after xa_alloc and kref_get have both -* succeeded. A NULL rn_done means this notification was +* leaves it NULL on every failure path, clearing it again if +* xa_alloc fails, so a non-NULL rn_done marks a completed +* registration. A NULL rn_done means this notification was * never registered (or its registration failed) or has * already been unregistered, and the call is a no-op. * Without this guard, rn_index == 0 from a kzalloc'd
Greg Kroah-Hartman
maintainerPATCH 495/583 svcrdma: Reject oversized Read segments at decode time
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit af6f0e06bed818ee7fc8b869915964410020a1c5 ]
The RPC/RDMA Read list decoder stores wire-supplied segment lengths without validation. xdr_count_read_segments() checks 4-byte alignment for non-zero position values but does not cap the segment length.
An oversized rs_length reaches svc_rdma_build_read_segment(), which derives nr_bvec from it and can drive a large dynamic bvec allocation before verifying that enough rq_pages remain. If the post-allocation page-overrun guard fires, the freshly acquired rw context is not returned, leaking the resource.
Reject any segment whose length exceeds the receive context's page budget during Read list decoding, consistent with how xdr_check_write_chunk() bounds Write segment counts against rc_maxpages. Also return the rw context on the existing post-allocation overrun path in svc_rdma_build_read_segment(), keeping that defensive guard balanced.
Stable-dep-of: 0ca487abb3bd ("svcrdma: Reject Read lists that exceed the page budget")
--- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 2 ++ net/sunrpc/xprtrdma/svc_rdma_rw.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +2 -0
@@ -448,6 +448,8 @@ static bool xdr_count_read_segments(struxdr_decode_read_segment (p, &position, &handle, &length, &offset); +if (length > rctxt->rc_maxpages <<PAGE_SHIFT ) +return false;if (position) {if (position &3 )return false;
net/sunrpc/xprtrdma/svc_rdma_rw.c +3 -2
@@ -766,7 +766,7 @@ static int svc_rdma_build_read_segment(s len -= seg_len;if (len && ((head->rc_curpage +1 ) > rqstp->rq_maxpages)) - goto out_overrun; + goto out_put; } ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
@@ -779,7 +779,8 @@ static int svc_rdma_build_read_segment(s cc->cc_sqecount += ret;return 0 ; -out_overrun: +out_put: +svc_rdma_put_rw_ctxt (rdma, ctxt);trace_svcrdma_page_overrun_err (&cc->cc_cid, head->rc_curpage);return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 496/583 svcrdma: Reject Read lists that exceed the page budget
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 0ca487abb3bdf581851664b5db21f364caf57682 ]
Individual Read segment lengths are validated at decode time, but nothing prevents a requester from sending multiple segments whose cumulative length exceeds the rq_pages array budget. When one segment fills the page array exactly, the runtime guard in svc_rdma_build_read_segment() is bypassed because len reaches zero. A subsequent segment then accesses the NULL sentinel slot at rq_pages[rq_maxpages], resulting in a NULL pointer dereference during DMA mapping.
Accumulate pages across all Read segments and reject the message at decode time when the total would overflow the page budget.
--- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +9 -3
@@ -430,11 +430,14 @@ static void svc_rdma_build_arg_xdr(struc* to the first byte past the Read list. rc_read_pcl and * rc_call_pcl cl_count fields are set to the number of * Read segments in the list. -* %false: Read list is corrupt. @rctxt's xdr_stream is left in an -* unknown state. +* %false: Read list is corrupt or exceeds the page budget. @rctxt's +* xdr_stream is left in an unknown state. */ static bool xdr_count_read_segments (struct svc_rdma_recv_ctxt *rctxt,__be32 *p) { +unsigned int maxlen = rctxt->rc_maxpages <<PAGE_SHIFT ; +unsigned int total_len =0 ; + rctxt->rc_call_pcl.cl_count =0 ; rctxt->rc_read_pcl.cl_count =0 ;while (xdr_item_is_present (p)) {
@@ -448,7 +451,10 @@ static bool xdr_count_read_segments(struxdr_decode_read_segment (p, &position, &handle, &length, &offset); -if (length > rctxt->rc_maxpages <<PAGE_SHIFT ) +if (length > maxlen) +return false; + total_len += length; +if (PAGE_ALIGN (total_len) > maxlen)return false;if (position) {if (position &3 )
Greg Kroah-Hartman
maintainerPATCH 497/583 sched_ext: Fix exit_task leak on fork failure during enable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: fangqiurong <[email protected]>
[ Upstream commit 03506edca637a8465dba9f635c50e9884fbcaf4e ]
scx_fork() initializes tasks when scx_init_task_enabled is set, but scx_cancel_fork() only exits them when scx_enabled() is true. A fork that fails in the enable window (between releasing scx_fork_rwsem and setting __scx_enabled) runs ops.init_task() but never ops.exit_task().
Gate scx_cancel_fork() on scx_init_task_enabled.
--- kernel/sched/ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/sched/ext.c +1 -1
@@ -3007,7 +3007,7 @@ void scx_post_fork(struct task_struct *pvoid scx_cancel_fork (struct task_struct *p) { -if (scx_enabled ()) { +if (scx_init_task_enabled) {struct rq *rq;struct rq_flags rf;
Greg Kroah-Hartman
maintainerPATCH 498/583 RDMA/ionic: Embed counter driver data in rdma_counter allocation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abhijit Gangurde <[email protected]>
[ Upstream commit cf3ebd89e754015625fee90aa938f6bc79a2c974 ]
Commit 7e53b31acc7f ("RDMA/core: Create and destroy rdma_counter using rdma_zalloc_drv_obj()") requires drivers implementing counter ops to embed struct rdma_counter in a driver-specific struct, register its size via INIT_RDMA_OBJ_SIZE, and provide a counter_init callback.
The ionic driver was merged without this adaptation, causing a NULL pointer dereference in alloc_and_bind() since rdma_zalloc_drv_obj() allocates zero bytes when size_rdma_counter is unset.
Consolidate struct ionic_counter into a new struct ionic_rdma_counter that embeds struct rdma_counter, replace the xarray with a lightweight ida for ID allocation, and add the required counter_init and INIT_RDMA_OBJ_SIZE declarations.
--- drivers/infiniband/hw/ionic/ionic_hw_stats.c | 101 ++++++++++++--------------- drivers/infiniband/hw/ionic/ionic_ibdev.h | 11 ++ 2 files changed, 54 insertions(+), 58 deletions(-)
drivers/infiniband/hw/ionic/ionic_hw_stats.c +45 -56
@@ -237,35 +237,34 @@ err_dma:static struct rdma_hw_stats *ionic_counter_alloc_stats (struct rdma_counter *counter) { +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter);struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); -struct ionic_counter *cntr; -int err; +struct rdma_hw_stats *stats; +int id; - cntr =kzalloc (sizeof (*cntr),GFP_KERNEL ); -if (!cntr) -return NULL ; - -/* buffer for current values from the device */ cntr->vals =kzalloc (PAGE_SIZE ,GFP_KERNEL );if (!cntr->vals) - goto err_vals; - - err =xa_alloc (&dev->counter_stats->xa_counters, &counter->id, - cntr, -XA_LIMIT (0 ,IONIC_MAX_QPID ), -GFP_KERNEL ); -if (err) - goto err_xa; - -INIT_LIST_HEAD (&cntr->qp_list); +return NULL ; -return rdma_alloc_hw_stats_struct (dev->counter_stats->stats_hdrs, - dev->counter_stats->queue_stats_count, -RDMA_HW_STATS_DEFAULT_LIFESPAN ); -err_xa: + id =ida_alloc_max (&dev->counter_stats->counter_ida, +IONIC_MAX_QPID ,GFP_KERNEL ); +if (id <0 ) + goto err_ida; + + counter->id = id; + + stats =rdma_alloc_hw_stats_struct (dev->counter_stats->stats_hdrs, + dev->counter_stats->queue_stats_count, +RDMA_HW_STATS_DEFAULT_LIFESPAN ); +if (!stats) + goto err_hw_stats; + +return stats; + +err_hw_stats: +ida_free (&dev->counter_stats->counter_ida, id); +err_ida:kfree (cntr->vals); -err_vals: -kfree (cntr);return NULL ; }
@@ -273,14 +272,10 @@ err_vals:static int ionic_counter_dealloc (struct rdma_counter *counter) {struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); -struct ionic_counter *cntr; - - cntr =xa_erase (&dev->counter_stats->xa_counters, counter->id); -if (!cntr) -return -EINVAL ; +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); +ida_free (&dev->counter_stats->counter_ida, counter->id);kfree (cntr->vals); -kfree (cntr);return 0 ; }
@@ -289,13 +284,8 @@ static int ionic_counter_bind_qp(struct struct ib_qp *ibqp, u32 port) { -struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter);struct ionic_qp *qp =to_ionic_qp (ibqp); -struct ionic_counter *cntr; - - cntr =xa_load (&dev->counter_stats->xa_counters, counter->id); -if (!cntr) -return -EINVAL ;list_add_tail (&qp->qp_list_counter, &cntr->qp_list); ibqp->counter = counter;
@@ -315,29 +305,23 @@ static int ionic_counter_unbind_qp(strucreturn 0 ; } -static int ionic_get_qp_stats (struct ib_device *ibdev, -struct rdma_hw_stats *hw_stats, -u32 counter_id) -{ -struct ionic_ibdev *dev =to_ionic_ibdev (ibdev); -struct ionic_counter_stats *cs; -struct ionic_counter *cntr; +static int ionic_counter_update_stats (struct rdma_counter *counter) +{ +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); +struct ionic_ibdev *dev =to_ionic_ibdev (counter->device); +struct ionic_counter_stats *cs = dev->counter_stats;dma_addr_t hw_stats_dma;struct ionic_qp *qp;int rc, stat_i =0 ; - cs = dev->counter_stats; - cntr =xa_load (&cs->xa_counters, counter_id); -if (!cntr) -return -EINVAL ; - hw_stats_dma =dma_map_single (dev->lif_cfg.hwdev, cntr->vals,PAGE_SIZE ,DMA_FROM_DEVICE ); rc =dma_mapping_error (dev->lif_cfg.hwdev, hw_stats_dma);if (rc)return rc; -memset (hw_stats->value,0 ,sizeof (u64) * hw_stats->num_counters); +memset (counter->stats->value,0 , +sizeof (u64) * counter->stats->num_counters);list_for_each_entry (qp, &cntr->qp_list, qp_list_counter) { rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE,
@@ -347,7 +331,7 @@ static int ionic_get_qp_stats(struct ib_ goto err_cmd;for (stat_i =0 ; stat_i < cs->queue_stats_count; ++stat_i) - hw_stats->value[stat_i] += + counter->stats->value[stat_i] +=ionic_v1_stat_val (&cs->hdr[stat_i], cntr->vals,PAGE_SIZE );
@@ -362,11 +346,6 @@ err_cmd:return rc; } -static int ionic_counter_update_stats (struct rdma_counter *counter) -{ -return ionic_get_qp_stats (counter->device, counter->stats, counter->id); -} -static int ionic_alloc_counters (struct ionic_ibdev *dev) {struct ionic_counter_stats *cs = dev->counter_stats;
@@ -427,12 +406,22 @@ static const struct ib_device_ops ionic_ .get_hw_stats = ionic_get_hw_stats, }; +static void ionic_counter_init (struct rdma_counter *counter) +{ +struct ionic_rdma_counter *cntr =to_ionic_rdma_counter (counter); + +INIT_LIST_HEAD (&cntr->qp_list); +} +static const struct ib_device_ops ionic_counter_stats_ops = { .counter_alloc_stats = ionic_counter_alloc_stats, .counter_dealloc = ionic_counter_dealloc, .counter_bind_qp = ionic_counter_bind_qp, .counter_unbind_qp = ionic_counter_unbind_qp, .counter_update_stats = ionic_counter_update_stats, + .counter_init = ionic_counter_init, + +INIT_RDMA_OBJ_SIZE (rdma_counter, ionic_rdma_counter, rdma_counter), };void ionic_stats_init (struct ionic_ibdev *dev)
@@ -462,7 +451,7 @@ void ionic_stats_init(struct ionic_ibdevreturn ; } -xa_init_flags (&dev->counter_stats->xa_counters,XA_FLAGS_ALLOC ); +ida_init (&dev->counter_stats->counter_ida);ib_set_device_ops (&dev->ibdev, &ionic_counter_stats_ops); }
@@ -471,7 +460,7 @@ void ionic_stats_init(struct ionic_ibdevvoid ionic_stats_cleanup (struct ionic_ibdev *dev) {if (dev->counter_stats) { -xa_destroy (&dev->counter_stats->xa_counters); +ida_destroy (&dev->counter_stats->counter_ida);kfree (dev->counter_stats->hdr);kfree (dev->counter_stats->stats_hdrs);kfree (dev->counter_stats);
drivers/infiniband/hw/ionic/ionic_ibdev.h +9 -2
@@ -331,14 +331,21 @@ struct ionic_counter_stats {int queue_stats_count;struct ionic_v1_stat *hdr;struct rdma_stat_desc *stats_hdrs; -struct xarray xa_counters; +struct ida counter_ida; }; -struct ionic_counter { +struct ionic_rdma_counter { +struct rdma_counter rdma_counter;void *vals;struct list_head qp_list; }; +static inline struct ionic_rdma_counter * +to_ionic_rdma_counter (struct rdma_counter *counter) +{ + return container_of(counter, struct ionic_rdma_counter, rdma_counter); +} +static inline struct ionic_ibdev *to_ionic_ibdev (struct ib_device *ibdev) { return container_of(ibdev, struct ionic_ibdev, ibdev);
Greg Kroah-Hartman
maintainerPATCH 499/583 scsi: fnic: Use GFP_ATOMIC for VLAN alloc under spinlock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linkai Gong <[email protected]>
[ Upstream commit 9639c6324524ea3f934908bd51f02430000954ab ]
fnic_fcoe_process_vlan_resp() allocates a VLAN descriptor with kzalloc_obj() (default GFP_KERNEL) while holding vlans_lock via spin_lock_irqsave(). GFP_KERNEL may sleep, which is not allowed in this atomic context and can trigger a sleeping-from-invalid-context warning or deadlock.
Pass GFP_ATOMIC so the allocation is safe under the IRQ-safe spinlock.
--- drivers/scsi/fnic/fip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/fnic/fip.c +1 -1
@@ -139,7 +139,7 @@ void fnic_fcoe_process_vlan_resp(structFNIC_FIP_DBG (KERN_INFO , fnic->host, fnic->fnic_num,"process_vlan_resp: FIP VLAN %d\n" , vid); - vlan =kzalloc (sizeof (*vlan),GFP_KERNEL ); + vlan =kzalloc (sizeof (*vlan),GFP_ATOMIC );if (!vlan) {/* retry from timer */
Greg Kroah-Hartman
maintainerPATCH 500/583 sched_ext: Fix inverted ops.core_sched_before() invocation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit f7b6d128dd49a6eec09066ecfd29095f12588786 ]
scx_prio_less() implements prio_less() semantics - %true means that @a is the lower priority and should run after @b. ops.core_sched_before() is documented to return %true when @a should run before @b. scx_prio_less() returns the op's value as-is, inverting the documented semantics at runtime.
Call the op with the arguments swapped.
scx_qmap followed the wiring instead of the documentation and returned %true for the younger task, so the two inversions canceled out and it behaved as intended. Flip its comparison to match. scx_qmap is likely the only current user in or out of the kernel tree. Any scheduler written the same way needs the same flip, while schedulers following the documentation are fixed by this change.
--- kernel/sched/ext.c | 10 ++++++++-- tools/sched_ext/scx_qmap.bpf.c | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-)
kernel/sched/ext.c +8 -2
@@ -2513,6 +2513,8 @@ static struct task_struct *pick_task_scx* * When ops.core_sched_before() is enabled, @p->scx.core_sched_at is used to * implement FIFO ordering within each local DSQ. See pick_task_scx(). +* +* Return: %true if @a should run after @b. */ bool scx_prio_less (const struct task_struct *a,const struct task_struct *b,bool in_fi)
@@ -2520,6 +2522,10 @@ bool scx_prio_less(const struct task_strstruct scx_sched *sch = scx_root;/* +* scx_prio_less() returns whether @a should run after @b while +* ops.core_sched_before() returns whether its first argument should run +* before the second. Swap the arguments. +* * The const qualifiers are dropped from task_struct pointers when * calling ops.core_sched_before(). Accesses are controlled by the * verifier.
@@ -2528,8 +2534,8 @@ bool scx_prio_less(const struct task_str !scx_rq_bypassing(task_rq(a)))return SCX_CALL_OP_2TASKS_RET (sch,SCX_KF_REST , core_sched_before,task_rq (a), - (struct task_struct *)a, - (struct task_struct *)b); + (struct task_struct *)b, + (struct task_struct *)a); else return time_after64(a->scx.core_sched_at, b->scx.core_sched_at); }
tools/sched_ext/scx_qmap.bpf.c +1 -1
@@ -530,7 +530,7 @@ static s64 task_qdist(struct task_structbool BPF_STRUCT_OPS (qmap_core_sched_before ,struct task_struct *a,struct task_struct *b) { -return task_qdist (a) >task_qdist (b); +return task_qdist (a) <task_qdist (b); }void BPF_STRUCT_OPS (qmap_cpu_release ,s32 cpu,struct scx_cpu_release_args *args)
Greg Kroah-Hartman
maintainerPATCH 501/583 sched: Add assert_balance_callbacks_empty helper
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Stultz <[email protected]>
[ Upstream commit f9530b3183358bbf945f7c20d4a6e2048061ec50 ]
With proxy-exec utilizing pick-again logic, we can end up having balance callbacks set by the preivous pick_next_task() call left on the list.
So pull the warning out into a helper function, and make sure we check it when we pick again.
Stable-dep-of: f3629c63a4af ("sched/core: Make core-sched flips wait for in-flight selections")
--- kernel/sched/core.c | 1 + kernel/sched/sched.h | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
kernel/sched/core.c +1 -0
@@ -6880,6 +6880,7 @@ static void __sched notrace __schedule(i } pick_again: +assert_balance_callbacks_empty (rq); next =pick_next_task (rq, rq->donor, &rf);rq_set_donor (rq, next);if (unlikely (task_is_blocked (next))) {
kernel/sched/sched.h +8 -1
@@ -1787,6 +1787,13 @@ static inline void scx_rq_clock_update(sstatic inline void scx_rq_clock_invalidate (struct rq *rq) {}#endif /* !CONFIG_SCHED_CLASS_EXT */ +static inline void assert_balance_callbacks_empty (struct rq *rq) +{ +WARN_ON_ONCE (IS_ENABLED (CONFIG_PROVE_LOCKING ) && + rq->balance_callback && + rq->balance_callback != &balance_push_callback); +} +/* * Lockdep annotation that avoids accidental unlocks; it's like a * sticky/continuous lockdep_assert_held().
@@ -1803,7 +1810,7 @@ static inline void rq_pin_lock(struct rq rq->clock_update_flags &= (RQCF_REQ_SKIP |RQCF_ACT_SKIP ); rf->clock_update_flags =0 ; -WARN_ON_ONCE (rq->balance_callback && rq->balance_callback != &balance_push_callback); +assert_balance_callbacks_empty (rq); }static inline void rq_unpin_lock (struct rq *rq,struct rq_flags *rf)
Greg Kroah-Hartman
maintainerPATCH 502/583 sched: Rework prev_balance() to avoid stale prev references
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Stultz <[email protected]>
[ Upstream commit 7a3a6bfbd62a2ba3e0ef1e92d6b71abb66890825 ]
Historically, the prev value from __schedule() was the rq->curr. This prev value is passed down through numerous functions, and used in the class scheduler implementations. The fact that prev was on_cpu until the end of __schedule(), meant it was stable across the rq lock drops that the class->balance() implementations often do.
However, with proxy-exec, the prev passed to functions called by __schedule() is rq->donor, which may not be the same as rq->curr and may not be on_cpu, this makes the prev value potentially unstable across rq lock drops.
A recently found issue with proxy-exec, is when we begin doing return migration from try_to_wake_up(), its possible we may be waking up the rq->donor. When we do this, we proxy_resched_idle() to put_prev_set_next() setting the rq->donor to rq->idle, allowing the rq->donor to be return migrated and allowed to run.
This however runs into trouble, as on another cpu we might be in the middle of calling __schedule(). Conceptually the rq lock is held for the majority of the time, but in calling prev_balance() its possible the class->balance() handler call may briefly drop the rq lock. This opens a window for try_to_wake_up() to wake and return migrate the rq->donor before the class logic reacquires the rq lock.
Unfortunately prev_balance() pass in a prev argument, to which we pass rq->donor. However this prev value can now become stale and incorrect across a rq lock drop.
So, to correct this, rework the prev_balance() call so that it does not take a "prev" argument.
Backport adaptation for 6.18:
Keep the existing sched_class::pick_next_task callbacks, pick_task interfaces, SCX balancing setup and proxy-execution flow. The later upstream scheduler refactors and lock annotations are not prerequisites for this dependency.
Convert all six balance callbacks, including the fair and SCX callbacks still present here, to the new signature. Read rq->donor at the existing selection and balance call sites, and refresh prev after new-idle balancing in pick_next_task_fair() because that path still drops the rq lock inside the stable pick_next_task implementation.
This preserves the existing functions and supplies the selection context needed for f3629c63a4af (sched/core: Make core-sched flips wait for in-flight selections) to apply without changes.
Stable-dep-of: f3629c63a4af ("sched/core: Make core-sched flips wait for in-flight selections")
--- kernel/sched/core.c | 37 ++++++++++++++++++------------------- kernel/sched/deadline.c | 8 +++++++- kernel/sched/ext.c | 5 ++--- kernel/sched/fair.c | 4 +++- kernel/sched/idle.c | 2 +- kernel/sched/rt.c | 8 +++++++- kernel/sched/sched.h | 2 +- kernel/sched/stop_task.c | 2 +- 8 files changed, 40 insertions(+), 28 deletions(-)
kernel/sched/core.c +18 -19
@@ -5923,10 +5923,9 @@ static inline void schedule_debug(structschedstat_inc (this_rq ()->sched_count); } -static void prev_balance (struct rq *rq,struct task_struct *prev, -struct rq_flags *rf) +static void prev_balance (struct rq *rq,struct rq_flags *rf) { -const struct sched_class *start_class = prev->sched_class; +const struct sched_class *start_class = rq->donor->sched_class;const struct sched_class *class;#ifdef CONFIG_SCHED_CLASS_EXT
@@ -5951,7 +5950,7 @@ static void prev_balance(struct rq *rq,* a runnable task of @class priority or higher. */ for_active_class_range(class, start_class, &idle_sched_class) { -if (class->balance && class->balance (rq, prev, rf)) +if (class->balance && class->balance (rq, rf))break ; } }
@@ -5960,7 +5959,7 @@ static void prev_balance(struct rq *rq,* Pick up the highest-prio task: */ static inline struct task_struct * -__pick_next_task (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +__pick_next_task (struct rq *rq,struct rq_flags *rf) {const struct sched_class *class;struct task_struct *p;
@@ -5976,34 +5975,34 @@ __pick_next_task(struct rq *rq, struct t* higher scheduling class, because otherwise those lose the * opportunity to pull in more work from other CPUs. */ -if (likely (!sched_class_above (prev->sched_class, &fair_sched_class) && +if (likely (!sched_class_above (rq->donor->sched_class, &fair_sched_class) && rq->nr_running == rq->cfs.h_nr_queued)) { - p =pick_next_task_fair (rq, prev, rf); + p =pick_next_task_fair (rq, rq->donor, rf);if (unlikely (p ==RETRY_TASK )) goto restart;/* Assume the next prioritized class is idle_sched_class */ if (!p) { p =pick_task_idle (rq); -put_prev_set_next_task (rq, prev, p); +put_prev_set_next_task (rq, rq->donor, p); }return p; } restart: -prev_balance (rq, prev, rf); +prev_balance (rq, rf);for_each_active_class (class) {if (class->pick_next_task) { - p = class->pick_next_task (rq, prev); + p = class->pick_next_task (rq, rq->donor);if (p)return p; }else { p = class->pick_task (rq);if (p) { -put_prev_set_next_task (rq, prev, p); +put_prev_set_next_task (rq, rq->donor, p);return p; } }
@@ -6052,7 +6051,7 @@ extern void task_vruntime_update(structstatic void queue_core_balance (struct rq *rq);static struct task_struct * -pick_next_task (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +pick_next_task (struct rq *rq,struct rq_flags *rf) {struct task_struct *next, *p, *max =NULL ;const struct cpumask *smt_mask;
@@ -6064,7 +6063,7 @@ pick_next_task(struct rq *rq, struct tasbool need_sync;if (!sched_core_enabled (rq)) -return __pick_next_task (rq, prev, rf); +return __pick_next_task (rq, rf); cpu =cpu_of (rq);
@@ -6077,7 +6076,7 @@ pick_next_task(struct rq *rq, struct tas*/ rq->core_pick =NULL ; rq->core_dl_server =NULL ; -return __pick_next_task (rq, prev, rf); +return __pick_next_task (rq, rf); }/*
@@ -6101,7 +6100,7 @@ pick_next_task(struct rq *rq, struct tas goto out_set_next; } -prev_balance (rq, prev, rf); +prev_balance (rq, rf); smt_mask =cpu_smt_mask (cpu); need_sync = !!rq->core->core_cookie;
@@ -6274,7 +6273,7 @@ pick_next_task(struct rq *rq, struct tas } out_set_next: -put_prev_set_next_task (rq, prev, next); +put_prev_set_next_task (rq, rq->donor, next);if (rq->core->core_forceidle_count && next == rq->idle)queue_core_balance (rq);
@@ -6496,9 +6495,9 @@ static inline void sched_core_cpu_deactistatic inline void sched_core_cpu_dying (unsigned int cpu) {}static struct task_struct * -pick_next_task (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +pick_next_task (struct rq *rq,struct rq_flags *rf) { -return __pick_next_task (rq, prev, rf); +return __pick_next_task (rq, rf); }#endif /* !CONFIG_SCHED_CORE */
@@ -6881,7 +6880,7 @@ static void __sched notrace __schedule(i pick_again:assert_balance_callbacks_empty (rq); - next =pick_next_task (rq, rq->donor, &rf); + next =pick_next_task (rq, &rf);rq_set_donor (rq, next);if (unlikely (task_is_blocked (next))) { next =find_proxy_task (rq, next, &rf);
kernel/sched/deadline.c +7 -1
@@ -2474,8 +2474,14 @@ static void check_preempt_equal_dl(strucresched_curr (rq); } -static int balance_dl (struct rq *rq,struct task_struct *p,struct rq_flags *rf) +static int balance_dl (struct rq *rq,struct rq_flags *rf) { +/* +* Note, rq->donor may change during rq lock drops, +* so don't re-use p across lock drops +*/ +struct task_struct *p = rq->donor; +if (!on_dl_rq (&p->dl) &&need_pull_dl_task (rq, p)) {/* * This is OK, because current is on_cpu, which avoids it being
kernel/sched/ext.c +2 -3
@@ -2200,14 +2200,13 @@ has_tasks:return true; } -static int balance_scx (struct rq *rq,struct task_struct *prev, -struct rq_flags *rf) +static int balance_scx (struct rq *rq,struct rq_flags *rf) {int ret;rq_unpin_lock (rq, rf); - ret =balance_one (rq, prev); + ret =balance_one (rq, rq->donor);#ifdef CONFIG_SCHED_SMT /*
kernel/sched/fair.c +3 -1
@@ -8879,7 +8879,7 @@ static void set_cpus_allowed_fair(struct }static int -balance_fair (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +balance_fair (struct rq *rq,struct rq_flags *rf) {if (sched_fair_runnable (rq))return 1 ;
@@ -9200,6 +9200,8 @@ simple: idle:if (rf) { new_tasks =sched_balance_newidle (rq, rf); +/* The donor may have changed while the rq lock was dropped. */ + prev = rq->donor;/* * Because sched_balance_newidle() releases (and re-acquires)
kernel/sched/idle.c +1 -1
@@ -460,7 +460,7 @@ select_task_rq_idle(struct task_struct * }static int -balance_idle (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +balance_idle (struct rq *rq,struct rq_flags *rf) {return WARN_ON_ONCE (1 ); }
kernel/sched/rt.c +7 -1
@@ -1591,8 +1591,14 @@ static void check_preempt_equal_prio(strresched_curr (rq); } -static int balance_rt (struct rq *rq,struct task_struct *p,struct rq_flags *rf) +static int balance_rt (struct rq *rq,struct rq_flags *rf) { +/* +* Note, rq->donor may change during rq lock drops, +* so don't re-use p across lock drops +*/ +struct task_struct *p = rq->donor; +if (!on_rt_rq (&p->rt) &&need_pull_rt_task (rq, p)) {/* * This is OK, because current is on_cpu, which avoids it being
kernel/sched/sched.h +1 -1
@@ -2422,7 +2422,7 @@ struct sched_class {void (*wakeup_preempt)(struct rq *rq,struct task_struct *p,int flags); -int (*balance)(struct rq *rq,struct task_struct *prev,struct rq_flags *rf); +int (*balance)(struct rq *rq,struct rq_flags *rf);struct task_struct *(*pick_task)(struct rq *rq);/* * Optional! When implemented pick_next_task() should be equivalent to:
kernel/sched/stop_task.c +1 -1
@@ -16,7 +16,7 @@ select_task_rq_stop(struct task_struct * }static int -balance_stop (struct rq *rq,struct task_struct *prev,struct rq_flags *rf) +balance_stop (struct rq *rq,struct rq_flags *rf) {return sched_stop_runnable (rq); }
Greg Kroah-Hartman
maintainerPATCH 503/583 sched/core: Make core-sched flips wait for in-flight selections
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit f3629c63a4af3e491381780bc6c123cb498c4c40 ]
Core scheduling's pick_next_task() operates on all sibling rqs under one acquisition of the shared core-wide lock. A ->pick_task() that releases the rq lock leaves every sibling __lock momentarily free, letting __sched_core_flip(false) complete mid-selection and rebind rq_lockp() under it. The selection resumes on the split locks, touching sibling state it no longer protects, and __schedule() finally releases a lock that was never taken while leaking the one that was.
Count in-flight core-wide selections in the leader's rq->core_pick_in_flight and make __sched_core_flip() wait for the count to drain. The count only changes under the shared lock, which the flip holds while sampling, so no other ordering is needed. The wait can repeat while selections overlap, but the flip backs off between samples and flips are rare cookie-lifetime events.
sched_core_cpu_deactivate() moves the count to the new leader - a stale copy left behind would bias it forever if that CPU later returns as its own leader.
--- kernel/sched/core.c | 22 ++++++++++++++++++++++ kernel/sched/sched.h | 1 + 2 files changed, 23 insertions(+)
kernel/sched/core.c +22 -0
@@ -434,6 +434,17 @@ static void __sched_core_flip(bool enablsched_core_lock (cpu, &flags); +/* +* A core-wide selection may have the shared rq lock temporarily +* released by a lock-dropping ->pick_task(). Flipping would +* rebind rq_lockp() under it. Wait it out. +*/ +while (cpu_rq (cpu)->core->core_pick_in_flight) { +sched_core_unlock (cpu, &flags); +cpu_relax (); +sched_core_lock (cpu, &flags); + } +for_each_cpu (t, smt_mask)cpu_rq (t)->core_enabled = enabled;
@@ -6079,6 +6090,8 @@ pick_next_task(struct rq *rq, struct rq_return __pick_next_task (rq, rf); } + rq->core->core_pick_in_flight++; +/* * If there were no {en,de}queues since we picked (IOW, the task * pointers are all still valid), and we haven't scheduled the last
@@ -6273,6 +6286,7 @@ pick_next_task(struct rq *rq, struct rq_ } out_set_next: + rq->core->core_pick_in_flight--;put_prev_set_next_task (rq, rq->donor, next);if (rq->core->core_forceidle_count && next == rq->idle)queue_core_balance (rq);
@@ -6467,6 +6481,13 @@ static void sched_core_cpu_deactivate(un core_rq->core_forceidle_occupation = rq->core_forceidle_occupation;/* +* A stale leftover would bias the count forever if this CPU later +* returns as its own leader. Move, don't copy. +*/ + core_rq->core_pick_in_flight = rq->core_pick_in_flight; + rq->core_pick_in_flight =0 ; + +/* * Accounting edge for forced idle is handled in pick_next_task(). * Don't need another one here, since the hotplug thread shouldn't * have a cookie.
@@ -8774,6 +8795,7 @@ void __init sched_init(void) rq->core_forceidle_count =0 ; rq->core_forceidle_occupation =0 ; rq->core_forceidle_start =0 ; + rq->core_pick_in_flight =0 ; rq->core_cookie =0UL ;#endif
kernel/sched/sched.h +1 -0
@@ -1310,6 +1310,7 @@ struct rq {unsigned int core_forceidle_seq;unsigned int core_forceidle_occupation;u64 core_forceidle_start; +unsigned int core_pick_in_flight;#endif /* CONFIG_SCHED_CORE */ /* Scratch cpumask to be temporarily used under rq_lock */
Greg Kroah-Hartman
maintainerPATCH 504/583 firmware: qcom_scm: Rename peripheral as pas_id
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mukesh Ojha <[email protected]>
[ Upstream commit 69054348cc1c2d87acad90aec5e6e0d191012aff ]
Peripheral and pas_id refers to unique id for a subsystem and used only when peripheral authentication service from secure world is utilized.
Lets rename peripheral to pas_id to reflect closer to its meaning.
Stable-dep-of: c06c5ab49453 ("remoteproc: qcom: pas: Guard dtb metadata release with dtb_pas_id check")
--- drivers/firmware/qcom/qcom_scm.c | 30 +++++++++++++++--------------- include/linux/firmware/qcom/qcom_scm.h | 10 +++++----- 2 files changed, 20 insertions(+), 20 deletions(-)
drivers/firmware/qcom/qcom_scm.c +15 -15
@@ -562,7 +562,7 @@ static void qcom_scm_set_download_mode(u* qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the * metadata -* @peripheral: peripheral id +* @pas_id: peripheral authentication service id * @metadata: pointer to memory containing ELF header, program header table * and optional blob of data used for authenticating the metadata * and the rest of the firmware
@@ -575,7 +575,7 @@ static void qcom_scm_set_download_mode(u* track the metadata allocation, this needs to be released by invoking * qcom_scm_pas_metadata_release() by the caller. */ -int qcom_scm_pas_init_image (u32 peripheral,const void *metadata,size_t size, +int qcom_scm_pas_init_image (u32 pas_id,const void *metadata,size_t size,struct qcom_scm_pas_metadata *ctx) {dma_addr_t mdata_phys;
@@ -585,7 +585,7 @@ int qcom_scm_pas_init_image(u32 peripher .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, };struct qcom_scm_res res;
@@ -658,20 +658,20 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_/** * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral * for firmware loading -* @peripheral: peripheral id +* @pas_id: peripheral authentication service id * @addr: start address of memory area to prepare * @size: size of the memory area to prepare * * Returns 0 on success. */ -int qcom_scm_pas_mem_setup (u32 peripheral,phys_addr_t addr,phys_addr_t size) +int qcom_scm_pas_mem_setup (u32 pas_id,phys_addr_t addr,phys_addr_t size) {int ret;struct qcom_scm_desc desc = { .svc =QCOM_SCM_SVC_PIL , .cmd =QCOM_SCM_PIL_PAS_MEM_SETUP , .arginfo =QCOM_SCM_ARGS (3 ), - .args[0 ] = peripheral, + .args[0 ] = pas_id, .args[1 ] = addr, .args[2 ] = size, .owner =ARM_SMCCC_OWNER_SIP ,
@@ -699,18 +699,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup/** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware * and reset the remote processor -* @peripheral: peripheral id +* @pas_id: peripheral authentication service id * * Return 0 on success. */ -int qcom_scm_pas_auth_and_reset (u32 peripheral) +int qcom_scm_pas_auth_and_reset (u32 pas_id) {int ret;struct qcom_scm_desc desc = { .svc =QCOM_SCM_SVC_PIL , .cmd =QCOM_SCM_PIL_PAS_AUTH_AND_RESET , .arginfo =QCOM_SCM_ARGS (1 ), - .args[0 ] = peripheral, + .args[0 ] = pas_id, .owner =ARM_SMCCC_OWNER_SIP , };struct qcom_scm_res res;
@@ -735,18 +735,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_/** * qcom_scm_pas_shutdown() - Shut down the remote processor -* @peripheral: peripheral id +* @pas_id: peripheral authentication service id * * Returns 0 on success. */ -int qcom_scm_pas_shutdown (u32 peripheral) +int qcom_scm_pas_shutdown (u32 pas_id) {int ret;struct qcom_scm_desc desc = { .svc =QCOM_SCM_SVC_PIL , .cmd =QCOM_SCM_PIL_PAS_SHUTDOWN , .arginfo =QCOM_SCM_ARGS (1 ), - .args[0 ] = peripheral, + .args[0 ] = pas_id, .owner =ARM_SMCCC_OWNER_SIP , };struct qcom_scm_res res;
@@ -772,18 +772,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown)/** * qcom_scm_pas_supported() - Check if the peripheral authentication service is * available for the given peripherial -* @peripheral: peripheral id +* @pas_id: peripheral authentication service id * * Returns true if PAS is supported for this peripheral, otherwise false. */ -bool qcom_scm_pas_supported (u32 peripheral) +bool qcom_scm_pas_supported (u32 pas_id) {int ret;struct qcom_scm_desc desc = { .svc =QCOM_SCM_SVC_PIL , .cmd =QCOM_SCM_PIL_PAS_IS_SUPPORTED , .arginfo =QCOM_SCM_ARGS (1 ), - .args[0 ] = peripheral, + .args[0 ] = pas_id, .owner =ARM_SMCCC_OWNER_SIP , };struct qcom_scm_res res;
include/linux/firmware/qcom/qcom_scm.h +5 -5
@@ -72,13 +72,13 @@ struct qcom_scm_pas_metadata {ssize_t size; }; -int qcom_scm_pas_init_image (u32 peripheral,const void *metadata,size_t size, +int qcom_scm_pas_init_image (u32 pas_id,const void *metadata,size_t size,struct qcom_scm_pas_metadata *ctx);void qcom_scm_pas_metadata_release (struct qcom_scm_pas_metadata *ctx); -int qcom_scm_pas_mem_setup (u32 peripheral,phys_addr_t addr,phys_addr_t size); -int qcom_scm_pas_auth_and_reset (u32 peripheral); -int qcom_scm_pas_shutdown (u32 peripheral); -bool qcom_scm_pas_supported (u32 peripheral); +int qcom_scm_pas_mem_setup (u32 pas_id,phys_addr_t addr,phys_addr_t size); +int qcom_scm_pas_auth_and_reset (u32 pas_id); +int qcom_scm_pas_shutdown (u32 pas_id); +bool qcom_scm_pas_supported (u32 pas_id);int qcom_scm_io_readl (phys_addr_t addr,unsigned int *val);int qcom_scm_io_writel (phys_addr_t addr,unsigned int val);
Greg Kroah-Hartman
maintainerPATCH 505/583 remoteproc: qcom: pas: Guard dtb metadata release with dtb_pas_id check
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mukesh Ojha <[email protected]>
[ Upstream commit c06c5ab4945392d2c2aded6d832ab6b58cabe351 ]
All other call sites of qcom_scm_pas_metadata_release() for the DTB context are guarded by a check on pas->dtb_pas_id, but the call inside qcom_pas_load() was not. Fix this by moving the call to the guarded block.
--- drivers/remoteproc/qcom_q6v5_pas.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/remoteproc/qcom_q6v5_pas.c +4 -5
@@ -248,15 +248,14 @@ static int qcom_pas_load(struct rproc *r ret =qcom_mdt_load_no_init (pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, pas->dtb_mem_region, pas->dtb_mem_phys, pas->dtb_mem_size, &pas->dtb_mem_reloc); -if (ret) - goto release_dtb_metadata; +if (ret) { +qcom_scm_pas_metadata_release (&pas->dtb_pas_metadata); + goto release_dtb_firmware; + } }return 0 ; -release_dtb_metadata: -qcom_scm_pas_metadata_release (&pas->dtb_pas_metadata); - release_dtb_firmware:release_firmware (pas->dtb_firmware);
Greg Kroah-Hartman
maintainerPATCH 506/583 ASoC: codecs: aw88261: reduce log spam
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Val Packett <[email protected]>
[ Upstream commit d90c361af215a9fa2a986d9f47d554d0cf3401dd ]
This driver would create a wall of logspam during initialization due to e.g. the PLL not being ready while waiting for it to stabilize. Change intermediate dev_err() calls to dev_dbg() to reduce the noise.
While here, log the detected chip ID when that check fails.
Stable-dep-of: 06b6f1245567 ("ASoC: codecs: aw88261: only check PLL and clock state at power-up")
--- sound/soc/codecs/aw88261.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
sound/soc/codecs/aw88261.c +14 -10
@@ -150,7 +150,7 @@ static int aw88261_dev_get_iis_status(stif (ret)return ret;if ((reg_val &AW88261_BIT_PLL_CHECK ) !=AW88261_BIT_PLL_CHECK ) { -dev_err (aw_dev->dev,"check pll lock fail,reg_val:0x%04x" , reg_val); +dev_dbg (aw_dev->dev,"check pll lock fail,reg_val:0x%04x" , reg_val);return -EINVAL ; }
@@ -164,7 +164,7 @@ static int aw88261_dev_check_mode1_pll(sfor (i =0 ; i <AW88261_DEV_SYSST_CHECK_MAX ; i++) { ret =aw88261_dev_get_iis_status (aw_dev);if (ret) { -dev_err (aw_dev->dev,"mode1 iis signal check error" ); +dev_dbg (aw_dev->dev,"mode1 iis signal check error" );usleep_range (AW88261_2000_US ,AW88261_2000_US +10 ); }else {return ret;
@@ -255,7 +255,7 @@ static int aw88261_dev_check_sysst(struc check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK ) &AW88261_BIT_SYSST_CHECK ;if (check_val !=AW88261_BIT_SYSST_CHECK ) { -dev_err (aw_dev->dev,"check sysst fail, reg_val=0x%04x, check:0x%x" , +dev_dbg (aw_dev->dev,"check sysst fail, reg_val=0x%04x, check:0x%x" , reg_val,AW88261_BIT_SYSST_CHECK );usleep_range (AW88261_2000_US ,AW88261_2000_US +10 ); }else {
@@ -549,7 +549,7 @@ static int aw88261_dev_start(struct aw88int ret;if (aw_dev->status ==AW88261_DEV_PW_ON ) { -dev_info (aw_dev->dev,"already power on" ); +dev_dbg (aw_dev->dev,"already power on" );return 0 ; }
@@ -559,7 +559,7 @@ static int aw88261_dev_start(struct aw88 ret =aw88261_dev_check_syspll (aw_dev);if (ret) { -dev_err (aw_dev->dev,"pll check failed cannot start" ); +dev_dbg (aw_dev->dev,"pll check failed" ); goto pll_check_fail; }
@@ -570,7 +570,7 @@ static int aw88261_dev_start(struct aw88/* check i2s status */ ret =aw88261_dev_check_sysst (aw_dev);if (ret) { -dev_err (aw_dev->dev,"sysst check failed" ); +dev_dbg (aw_dev->dev,"sysst check failed" ); goto sysst_check_fail; }
@@ -671,18 +671,22 @@ static void aw88261_start_pa(struct aw88for (i =0 ; i <AW88261_START_RETRIES ; i++) { ret =aw88261_reg_update (aw88261, aw88261->phase_sync);if (ret) { -dev_err (aw88261->aw_pa->dev,"fw update failed, cnt:%d\n" , i); +dev_dbg (aw88261->aw_pa->dev, +"aw88261_reg_update failed, cnt:%d, ret:%d\n" , i, ret);continue ; } ret =aw88261_dev_start (aw88261);if (ret) { -dev_err (aw88261->aw_pa->dev,"aw88261 device start failed. retry = %d" , i); +dev_dbg (aw88261->aw_pa->dev, +"aw88261_dev_start failed, cnt:%d, ret:%d\n" , i, ret);continue ; }else { -dev_info (aw88261->aw_pa->dev,"start success\n" ); +dev_dbg (aw88261->aw_pa->dev,"start success\n" );break ; } } +if (ret !=0 ) +dev_err (aw88261->aw_pa->dev,"start failure (%d)\n" , ret); }static void aw88261_startup_work (struct work_struct *work)
@@ -1198,7 +1202,7 @@ static int aw88261_init(struct aw88261 *return ret; }if (chip_id !=AW88261_CHIP_ID ) { -dev_err (&i2c->dev,"unsupported device" ); +dev_err (&i2c->dev,"unsupported device id = %x" , chip_id);return -ENXIO ; }
Greg Kroah-Hartman
maintainerPATCH 507/583 ASoC: codecs: aw88261: only check PLL and clock state at power-up
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jorijn van der Graaf <[email protected]>
[ Upstream commit 06b6f1245567a4be862c3e1cc74577922ceb05fb ]
The SYSST check performed during device start requires SWS (amplifier switching, bit 8) and BSTS (boost finished, bit 9) on top of PLL lock and clock stability. Those bits cannot be asserted at this point in the
sequence: the check runs after amppd release but before the hmute/ULS-hmute release, and the amplifier neither switches nor finishes ramping its boost converter while it is still muted. With the Fairphone (Gen. 6) firmware profile, aw88261_dev_start() therefore always fails with
check sysst fail, reg_val=0x0011, check:0x311
and playback aborts, even though the amplifier is fine and PLL lock and stable clocks are present.
Check only PLL lock and clock stability, for which a definition already exists; this still re-validates the clocks after amppd release (aw88261_dev_check_syspll() checked them before it). This matches the vendor aw882xx driver, which only validates PLL lock and clock stability at this stage, and the in-tree aw88399 driver, which skips the SWS check whenever the amplifier may legitimately not be switching (AW88399_BIT_SYSST_NOSWS_CHECK).
--- sound/soc/codecs/aw88261.c | 6 +++--- sound/soc/codecs/aw88261.h | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-)
sound/soc/codecs/aw88261.c +3 -3
@@ -253,10 +253,10 @@ static int aw88261_dev_check_sysst(strucreturn ret; check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK ) - &AW88261_BIT_SYSST_CHECK ; -if (check_val !=AW88261_BIT_SYSST_CHECK ) { + &AW88261_BIT_PLL_CHECK ; +if (check_val !=AW88261_BIT_PLL_CHECK ) {dev_dbg (aw_dev->dev,"check sysst fail, reg_val=0x%04x, check:0x%x" , - reg_val,AW88261_BIT_SYSST_CHECK ); + reg_val,AW88261_BIT_PLL_CHECK );usleep_range (AW88261_2000_US ,AW88261_2000_US +10 ); }else {return 0 ;
sound/soc/codecs/aw88261.h +0 -6
@@ -181,12 +181,6 @@ AW88261_OTHS_OT_VALUE | \ AW88261_PLLS_LOCKED_VALUE)) -#define AW88261_BIT_SYSST_CHECK \ - (AW88261_BSTS_FINISHED_VALUE | \ - AW88261_SWS_SWITCHING_VALUE | \ - AW88261_CLKS_STABLE_VALUE | \ - AW88261_PLLS_LOCKED_VALUE) -#define AW88261_ULS_HMUTE_START_BIT (14)#define AW88261_ULS_HMUTE_BITS_LEN (1)#define AW88261_ULS_HMUTE_MASK \
Greg Kroah-Hartman
maintainerPATCH 508/583 ring-buffer: Make cpu_buffer::free_page a buffer_data_read_page
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit 7a1fb95de5404134f8758c1295ce88986bdf117c ]
Discarding a cached reader page after a concurrent ring buffer resize uses the new global subbuf_order for the free_pages() call. This mismatched order may crashes the kernel or leaks memory because the cached page was allocated under the old size.
Save the actual free_page order alongside the page address to ensure we always refer to the correct value and do not rely on the potentially stalled cpu_buffer->subbuf_order value. The simplest is to make free_page a buffer_data_read_page which already covers exactly what we
need: a page address and a page order.
--- kernel/trace/ring_buffer.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
kernel/trace/ring_buffer.c +10 -10
@@ -531,7 +531,7 @@ struct ring_buffer_per_cpu {raw_spinlock_t reader_lock;/* serialize readers */ arch_spinlock_t lock;struct lock_class_key lock_key; -struct buffer_data_page *free_page; +struct buffer_data_read_page free_page;unsigned long nr_pages;unsigned int current_context;struct list_head *pages;
@@ -2514,7 +2514,7 @@ static void rb_free_cpu_buffer(struct rifree_buffer_page (bpage); } -free_pages ((unsigned long )cpu_buffer->free_page, cpu_buffer->buffer->subbuf_order); +free_pages ((unsigned long )cpu_buffer->free_page.data, cpu_buffer->free_page.order);kfree (cpu_buffer); }
@@ -6570,9 +6570,9 @@ ring_buffer_alloc_read_page(struct tracelocal_irq_save (flags);arch_spin_lock (&cpu_buffer->lock); -if (cpu_buffer->free_page) { - bpage->data = cpu_buffer->free_page; - cpu_buffer->free_page =NULL ; +if (cpu_buffer->free_page.data) { + *bpage = cpu_buffer->free_page; + cpu_buffer->free_page.data =NULL ; }arch_spin_unlock (&cpu_buffer->lock);
@@ -6624,8 +6624,8 @@ void ring_buffer_free_read_page(struct tlocal_irq_save (flags);arch_spin_lock (&cpu_buffer->lock); -if (!cpu_buffer->free_page) { - cpu_buffer->free_page = bpage; +if (!cpu_buffer->free_page.data) { + cpu_buffer->free_page = *data_page; bpage =NULL ; }
@@ -6978,7 +6978,7 @@ int ring_buffer_subbuf_order_set(struct }for_each_buffer_cpu (buffer, cpu) { -struct buffer_data_page *old_free_data_page; +struct buffer_data_read_page old_free_data_page;struct list_head old_pages;unsigned long flags;
@@ -7021,7 +7021,7 @@ int ring_buffer_subbuf_order_set(structarch_spin_lock (&cpu_buffer->lock); old_free_data_page = cpu_buffer->free_page; - cpu_buffer->free_page =NULL ; + cpu_buffer->free_page.data =NULL ;arch_spin_unlock (&cpu_buffer->lock);rb_head_page_activate (cpu_buffer);
@@ -7033,7 +7033,7 @@ int ring_buffer_subbuf_order_set(structlist_del_init (&bpage->list);free_buffer_page (bpage); } -free_pages ((unsigned long )old_free_data_page, old_order); +free_pages ((unsigned long )old_free_data_page.data, old_free_data_page.order);rb_check_pages (cpu_buffer); }
Greg Kroah-Hartman
maintainerPATCH 509/583 ocfs2: validate dx_root extent list fields during block read
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
[ Upstream commit 775c17386a6fd695f999d4cda90e3931386570dd ]
Patch series "ocfs2: consolidate extent list validation into block read callbacks".
ocfs2 validates extent list fields (l_count, l_next_free_rec) at various points during extent tree traversal. This is fragile because each caller must remember to check for corrupted on-disk data before using it.
This series moves those checks into the block read validation callbacks (ocfs2_validate_dx_root and ocfs2_validate_extent_block), so corrupted fields are caught early at block read time. Redundant post-read checks are then removed.
This patch (of 4):
Move the extent list l_count validation from ocfs2_dx_dir_lookup_rec() into ocfs2_validate_dx_root(), so that corrupted on-disk fields are caught early at block read time rather than during directory lookups.
Additionally, add a l_next_free_rec <= l_count check to prevent out-of-bounds access when iterating over extent records.
Both checks are skipped for inline dx roots (OCFS2_DX_FLAG_INLINE), which use dr_entries instead of dr_list.
Stable-dep-of: bc70726ddad5 ("ocfs2: validate directory-index entry counts when reading metadata")
--- fs/ocfs2/dir.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
fs/ocfs2/dir.c +25 -1
@@ -580,7 +580,7 @@ static int ocfs2_validate_dx_root(structmlog (ML_ERROR ,"Checksum failed for dir index root block %llu\n" , (unsigned long long )bh->b_blocknr); -return ret; + goto bail; }if (!OCFS2_IS_VALID_DX_ROOT (dx_root)) {
@@ -588,8 +588,32 @@ static int ocfs2_validate_dx_root(struct"Dir Index Root # %llu has bad signature %.*s\n" , (unsigned long long )le64_to_cpu (dx_root->dr_blkno), 7, dx_root->dr_signature); + goto bail; } +if (!(dx_root->dr_flags &OCFS2_DX_FLAG_INLINE )) { +struct ocfs2_extent_list *el = &dx_root->dr_list; + +if (le16_to_cpu (el->l_count) !=ocfs2_extent_recs_per_dx_root (sb)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid l_count %u (expected %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (el->l_count), +ocfs2_extent_recs_per_dx_root (sb)); + goto bail; + } + +if (le16_to_cpu (el->l_next_free_rec) >le16_to_cpu (el->l_count)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid l_next_free_rec %u (l_count %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (el->l_next_free_rec), +le16_to_cpu (el->l_count)); + goto bail; + } + } + +bail:return ret; }
Greg Kroah-Hartman
maintainerPATCH 510/583 ocfs2: validate directory-index entry counts when reading metadata
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit bc70726ddad53c7e9a9a85915bf2415b0d4f42f9 ]
ocfs2_validate_dx_leaf() and ocfs2_validate_dx_root() check the ECC and signature of an indexed-directory block before it reaches higher-level callers, but neither validator bounds the ocfs2_dx_entry_list counts against the capacity of the block that holds them.
ocfs2_dx_dir_search() then walks
for (i = 0; i < le16_to_cpu(entry_list->de_num_used); i++) dx_entry = &entry_list->de_entries[i];
over de_num_used entries with no bounds check. entry_list is either dx_leaf->dl_list (from ocfs2_read_dx_leaf) or, for an inline root, dx_root->dr_entries. A crafted on-disk image can set de_num_used (and de_count, which is the __counted_by_le() bound of de_entries) to 0xffff and make the walk read far past the end of the 4KB metadata block, giving a slab out-of-bounds read reachable from any path lookup, stat() or open() on an indexed directory once the image is mounted.
Commit 775c17386a6f ("ocfs2: validate dx_root extent list fields during block read") already bounds dr_list for the non-inline dx_root, but left the inline dr_entries path and the dx_leaf dl_list unchecked. Add the same read-time validation for both entry lists: de_count must equal the capacity of the block (ocfs2_dx_entries_per_leaf()/per_root()) and de_num_used must not exceed de_count, rejecting corrupted metadata with -EFSCORRUPTED before ocfs2_dx_dir_search() can walk an out-of-range entry array.
de_count is always written as exactly the block capacity when a leaf or inline root is formatted, so the equality check does not reject any valid image.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- fs/ocfs2/dir.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-)
fs/ocfs2/dir.c +41 -4
@@ -611,6 +611,28 @@ static int ocfs2_validate_dx_root(struct le16_to_cpu(el->l_count)); goto bail; } + }else { +struct ocfs2_dx_entry_list *dl_list = &dx_root->dr_entries; + +if (le16_to_cpu (dl_list->de_count) != +ocfs2_dx_entries_per_root (sb)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid de_count %u (expected %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (dl_list->de_count), +ocfs2_dx_entries_per_root (sb)); + goto bail; + } + +if (le16_to_cpu (dl_list->de_num_used) > +le16_to_cpu (dl_list->de_count)) { + ret =ocfs2_error (sb, +"Dir Index Root # %llu has invalid de_num_used %u (de_count %u)\n" , + (unsigned long long )le64_to_cpu (dx_root->dr_blkno), +le16_to_cpu (dl_list->de_num_used), +le16_to_cpu (dl_list->de_count)); + goto bail; + } } bail:
@@ -650,10 +672,25 @@ static int ocfs2_validate_dx_leaf(structreturn ret; } -if (!OCFS2_IS_VALID_DX_LEAF (dx_leaf)) { - ret =ocfs2_error (sb,"Dir Index Leaf has bad signature %.*s\n" , -7 , dx_leaf->dl_signature); - } +if (!OCFS2_IS_VALID_DX_LEAF (dx_leaf)) +return ocfs2_error (sb,"Dir Index Leaf has bad signature %.*s\n" , +7 , dx_leaf->dl_signature); + +if (le16_to_cpu (dx_leaf->dl_list.de_count) != +ocfs2_dx_entries_per_leaf (sb)) +return ocfs2_error (sb, +"Dir Index Leaf # %llu has invalid de_count %u (expected %u)\n" , + (unsigned long long )le64_to_cpu (dx_leaf->dl_blkno), +le16_to_cpu (dx_leaf->dl_list.de_count), +ocfs2_dx_entries_per_leaf (sb)); + +if (le16_to_cpu (dx_leaf->dl_list.de_num_used) > +le16_to_cpu (dx_leaf->dl_list.de_count)) +return ocfs2_error (sb, +"Dir Index Leaf # %llu has invalid de_num_used %u (de_count %u)\n" , + (unsigned long long )le64_to_cpu (dx_leaf->dl_blkno), +le16_to_cpu (dx_leaf->dl_list.de_num_used), +le16_to_cpu (dx_leaf->dl_list.de_count));return ret; }
Greg Kroah-Hartman
maintainerPATCH 511/583 lockd: fix swapped arguments in nlmsvc_match_ip()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oscar Ou <[email protected]>
[ Upstream commit b9060689f49dc663e9a3d069c4a65ff63a836e66 ]
When releasing locks by server IP address via /proc/fs/nfsd/unlock_ip, nlmsvc_unlock_all_by_ip() calls nlm_traverse_files() with the server sockaddr as the opaque @data argument:
nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
The match callback is later invoked from nlm_traverse_locks() as:
match(lockhost, host);
where the first argument is the nlm_host that owns the lock, and the second argument is the @data that was originally passed down (here the server sockaddr). This is the convention every other match callback relies on (nlmsvc_mark_host(), nlmsvc_same_host(), nlmsvc_is_client()):
arg1 is the real nlm_host, arg2 is the caller-supplied reference value.
nlmsvc_match_ip() has had these two arguments reversed ever since the unlock-by-IP feature was introduced in commit 4373ea84c84d ("lockd:
unlock lockd locks associated with a given server ip"):
return rpc_cmp_addr(nlm_srcaddr(host), datap);
Here @host is actually the server sockaddr, so nlm_srcaddr(host) dereferences a struct sockaddr as a struct nlm_host and reads garbage at the offset of h_srcaddr; meanwhile @datap is actually the lock owner's nlm_host but is compared as a sockaddr. As a result the comparison practically never matches and locks are not released for the requested IP.
Swap the arguments so the lock owner's source address is compared against the requested server address:
return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host);
--- fs/lockd/svcsubs.c | 2 +- include/linux/lockd/lockd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
fs/lockd/svcsubs.c +1 -1
@@ -508,7 +508,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sstatic int nlmsvc_match_ip (void *datap,struct nlm_host *host) { -return rpc_cmp_addr (nlm_srcaddr (host), datap); +return rpc_cmp_addr (nlm_srcaddr (datap), (struct sockaddr *)host); }/**
include/linux/lockd/lockd.h +1 -1
@@ -268,7 +268,7 @@ void nsm_release(struct nsm_handle *n* This is used in garbage collection and resource reclaim * A return value != 0 means destroy the lock/block/share */ -typedef int (*nlm_host_match_fn_t )(void *cur,struct nlm_host *ref); +typedef int (*nlm_host_match_fn_t )(void *owner,struct nlm_host *ref);/* * Server-side lock handling
Greg Kroah-Hartman
maintainerPATCH 512/583 PCI: starfive: Use regulator APIs to control the 3v3 power supply of PCIe slots
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hal Feng <[email protected]>
[ Upstream commit 05a75df4182e301a1b0059606f77b65c74deaa9b ]
The driver has been using the "enable-gpios" property to control the 3v3 power supply of PCIe slots. But it is not documented in the dt-bindings and also using GPIO APIs is not a standard way to control PCIe slot power, so use the documented "vpcie3v3-supply" property and regulator APIs to control the slot supply.
This change will break the DTs which used "enable-gpio" or "enable-gpios" property under the controller node. Since these properties were not defined in the bindings, it is safe to switch to "vpcie3v3-supply". Any out-of-tree DTS impacted by this change should migrate to "vpcie3v3-supply" instead.
Stable-dep-of: 22877a061f81 ("PCI: starfive: Fix resource leaks on error paths in host_init()")
--- drivers/pci/controller/plda/pcie-starfive.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
drivers/pci/controller/plda/pcie-starfive.c +15 -10
@@ -55,7 +55,7 @@ struct starfive_jh7110_pcie {struct reset_control *resets;struct clk_bulk_data *clks;struct regmap *reg_syscon; -struct gpio_desc *power_gpio; +struct regulator *vpcie3v3;struct gpio_desc *reset_gpio;struct phy *phy;
@@ -153,11 +153,13 @@ static int starfive_pcie_parse_dt(structreturn dev_err_probe (dev,PTR_ERR (pcie->reset_gpio),"failed to get perst-gpio\n" ); - pcie->power_gpio =devm_gpiod_get_optional (dev,"enable" , -GPIOD_OUT_LOW ); -if (IS_ERR (pcie->power_gpio)) -return dev_err_probe (dev,PTR_ERR (pcie->power_gpio), -"failed to get power-gpio\n" ); + pcie->vpcie3v3 =devm_regulator_get_optional (dev,"vpcie3v3" ); +if (IS_ERR (pcie->vpcie3v3)) { +if (PTR_ERR (pcie->vpcie3v3) != -ENODEV ) +return dev_err_probe (dev,PTR_ERR (pcie->vpcie3v3), +"failed to get vpcie3v3 regulator\n" ); + pcie->vpcie3v3 =NULL ; + }return 0 ; }
@@ -270,8 +272,8 @@ static void starfive_pcie_host_deinit(st container_of(plda, struct starfive_jh7110_pcie, plda);starfive_pcie_clk_rst_deinit (pcie); -if (pcie->power_gpio) -gpiod_set_value_cansleep (pcie->power_gpio,0 ); +if (pcie->vpcie3v3) +regulator_disable (pcie->vpcie3v3);starfive_pcie_disable_phy (pcie); }
@@ -304,8 +306,11 @@ static int starfive_pcie_host_init(strucif (ret)return ret; -if (pcie->power_gpio) -gpiod_set_value_cansleep (pcie->power_gpio,1 ); +if (pcie->vpcie3v3) { + ret =regulator_enable (pcie->vpcie3v3); +if (ret) +dev_err_probe (dev, ret,"failed to enable vpcie3v3 regulator\n" ); + }if (pcie->reset_gpio)gpiod_set_value_cansleep (pcie->reset_gpio,1 );
Greg Kroah-Hartman
maintainerPATCH 513/583 PCI: starfive: Fix resource leaks on error paths in host_init()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Tariq <[email protected]>
[ Upstream commit 22877a061f81c5d58041e384b3131684bec636b9 ]
starfive_pcie_host_init() acquires the PHY, clocks/resets, and an optional regulator in sequence, but does not correctly unwind these resources when a later step fails.
If starfive_pcie_clk_rst_init() fails after the PHY has already been successfully enabled, the function returns directly without disabling the PHY, leaking it and leaving it powered.
If regulator_enable() fails for the optional vpcie3v3 regulator, the failure is only logged; the function falls through and returns success, leaving the driver believing the regulator is enabled while continuing to configure PCIe hardware that may be unpowered. This also leaves the clocks and PHY enabled with nothing to clean them up.
Disable the PHY on the clk/reset failure path, and disable the clocks/resets and PHY, then return the error, if the regulator fails to enable.
Build-tested and boot-tested on StarFive VisionFive 2 v1.2A
--- drivers/pci/controller/plda/pcie-starfive.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/pci/controller/plda/pcie-starfive.c +11 -2
@@ -304,12 +304,14 @@ static int starfive_pcie_host_init(struc ret =starfive_pcie_clk_rst_init (pcie);if (ret) -return ret; + goto err_disable_phy;if (pcie->vpcie3v3) { ret =regulator_enable (pcie->vpcie3v3); -if (ret) +if (ret) {dev_err_probe (dev, ret,"failed to enable vpcie3v3 regulator\n" ); + goto err_clk_rst; + } }if (pcie->reset_gpio)
@@ -379,6 +381,13 @@ static int starfive_pcie_host_init(strucdev_info (dev,"port link down\n" );return 0 ; + +err_clk_rst: +starfive_pcie_clk_rst_deinit (pcie); +err_disable_phy: +starfive_pcie_disable_phy (pcie); + +return ret; }static const struct plda_pcie_host_ops sf_host_ops = {
Greg Kroah-Hartman
maintainerPATCH 514/583 fuse: fix missing barrier when checking io-uring readiness
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit edb310bc27f0ad83e7fd558a3caf1a94ca511654 upstream.
fuse_block_alloc() reads fch->initialized and then fch->io_uring. fch->io_uring is set before fch->initialized, ordered by the smp_wmb() in fuse_chan_set_intialized(), but fuse_block_alloc() has no matching read barrier between the two loads.
This may lead a CPU to observe fch->initialized=1 but fch->io_uring=0, and skip the check that blocks request allocation until the io-uring queues are ready. This can reintroduce the lock-order inversion deadlock that commit 3393ff964e0f prevents.
Add an smp_rmb() barrier to pair with the smp_wmb() in fuse_chan_set_initialized() to prevent this.
--- fs/fuse/dev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
fs/fuse/dev.c +7 -3
@@ -172,7 +172,13 @@ void fuse_set_initialized(struct fuse_costatic bool fuse_block_alloc (struct fuse_conn *fc,bool for_background) { -return !fc->initialized || (for_background && fc->blocked) || +if (!fc->initialized) +return true; + +/* Pairs with smp_wmb() in fuse_set_initialized() */ +smp_rmb (); + +return (for_background && fc->blocked) || (fc->io_uring && fc->connected && !fuse_uring_ready (fc)); }
@@ -212,8 +218,6 @@ static struct fuse_req *fuse_get_req(str (TASK_KILLABLE | TASK_FREEZABLE))) goto out; } -/* Matches smp_wmb() in fuse_set_initialized() */ -smp_rmb (); err = -ENOTCONN ;if (!fc->connected)
Greg Kroah-Hartman
maintainerPATCH 515/583 fuse-uring: refactor io-uring header copying to ring
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit 6582f8a06698403dccf8a01b7eef176b2c6dd7ff ]
Move header copying to ring logic into a new copy_header_to_ring() function. This makes the copy_to_user() logic more clear and centralizes error handling / rate-limited logging.
Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")
--- fs/fuse/dev_uring.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-)
fs/fuse/dev_uring.c +21 -18
@@ -581,6 +581,18 @@ err:return err; } +static __always_inline int copy_header_to_ring(void __user *ring, +const void *header, +size_t header_size) +{ +if (copy_to_user (ring, header, header_size)) { +pr_info_ratelimited ("Copying header to ring failed.\n" ); +return -EFAULT ; + } + +return 0 ; +} +static int fuse_uring_copy_from_ring (struct fuse_ring *ring,struct fuse_req *req,struct fuse_ring_ent *ent)
@@ -643,13 +655,11 @@ static int fuse_uring_args_to_ring(struc* Some op code have that as zero size. */ if (args->in_args[0 ].size >0 ) { - err =copy_to_user (&ent->headers->op_in, in_args->value, - in_args->size); -if (err) { -pr_info_ratelimited ( -"Copying the header failed.\n" ); -return -EFAULT ; - } + err =copy_header_to_ring (&ent->headers->op_in, + in_args->value, + in_args->size); +if (err) +return err; } in_args++; num_args--;
@@ -665,9 +675,8 @@ static int fuse_uring_args_to_ring(struc } ent_in_out.payload_sz = cs.ring.copied_sz; - err =copy_to_user (&ent->headers->ring_ent_in_out, &ent_in_out, -sizeof (ent_in_out)); -return err ? -EFAULT :0 ; +return copy_header_to_ring (&ent->headers->ring_ent_in_out, &ent_in_out, +sizeof (ent_in_out)); } static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
@@ -696,14 +705,8 @@ static int fuse_uring_copy_to_ring(struc }/* copy fuse_in_header */ - err =copy_to_user (&ent->headers->in_out, &req->in.h, -sizeof (req->in.h)); -if (err) { - err = -EFAULT ; -return err; - } - -return 0 ; +return copy_header_to_ring (&ent->headers->in_out, &req->in.h, +sizeof (req->in.h)); } static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
Greg Kroah-Hartman
maintainerPATCH 516/583 fuse-uring: refactor io-uring header copying from ring
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit ba7d47897fd895533c19af436ca7fc4f6b171238 ]
Move header copying from ring logic into a new copy_header_from_ring() function. This makes the copy_from_user() logic more clear and centralizes error handling / rate-limited logging.
Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")
--- fs/fuse/dev_uring.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
fs/fuse/dev_uring.c +17 -5
@@ -593,6 +593,18 @@ static __always_inline int copy_header_treturn 0 ; } +static __always_inline int copy_header_from_ring(void *header, +const void __user *ring, +size_t header_size) +{ +if (copy_from_user (header, ring, header_size)) { +pr_info_ratelimited ("Copying header from ring failed.\n" ); +return -EFAULT ; + } + +return 0 ; +} +static int fuse_uring_copy_from_ring (struct fuse_ring *ring,struct fuse_req *req,struct fuse_ring_ent *ent)
@@ -603,10 +615,10 @@ static int fuse_uring_copy_from_ring(strint err;struct fuse_uring_ent_in_out ring_in_out; - err =copy_from_user (&ring_in_out, &ent->headers->ring_ent_in_out, -sizeof (ring_in_out)); + err =copy_header_from_ring (&ring_in_out, &ent->headers->ring_ent_in_out, +sizeof (ring_in_out));if (err) -return -EFAULT ; +return err; err =import_ubuf (ITER_SOURCE , ent->payload, ring->max_payload_sz, &iter);
@@ -842,8 +854,8 @@ static void fuse_uring_commit(struct fusstruct fuse_conn *fc = ring->fc;ssize_t err = -EFAULT ; -if (copy_from_user (&req->out.h, &ent->headers->in_out, -sizeof (req->out.h))) +if (copy_header_from_ring (&req->out.h, &ent->headers->in_out, +sizeof (req->out.h))) goto out; err =fuse_uring_out_header_has_err (&req->out.h, req, fc);
Greg Kroah-Hartman
maintainerPATCH 517/583 fuse-uring: use enum types for header copying
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit b2bbd7dcd2433e29b7e9a726aaa9571a78fa8d5f ]
Use enum types to identify which part of the header needs to be copied. This improves the interface and will simplify both kernel-space and user-space header addresses copying when buffer rings are added.
Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")
--- fs/fuse/dev_uring.c | 66 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 13 deletions(-)
fs/fuse/dev_uring.c +53 -13
@@ -31,6 +31,15 @@ struct fuse_uring_pdu {static const struct fuse_iqueue_ops fuse_io_uring_ops; +enum fuse_uring_header_type { +/* struct fuse_in_header / struct fuse_out_header */ +FUSE_URING_HEADER_IN_OUT , +/* per op code header */ +FUSE_URING_HEADER_OP , +/* struct fuse_uring_ent_in_out header */ +FUSE_URING_HEADER_RING_ENT , +}; +static void uring_cmd_set_ring_ent (struct io_uring_cmd *cmd,struct fuse_ring_ent *ring_ent) {
@@ -581,10 +590,33 @@ err:return err; } -static __always_inline int copy_header_to_ring(void __user *ring, -const void *header, -size_t header_size) +static int ring_header_type_offset (enum fuse_uring_header_type type) +{ +switch (type) { +case FUSE_URING_HEADER_IN_OUT : +return 0 ; +case FUSE_URING_HEADER_OP : +return offsetof(struct fuse_uring_req_header , op_in); +case FUSE_URING_HEADER_RING_ENT : +return offsetof(struct fuse_uring_req_header , ring_ent_in_out); +default : +WARN_ONCE (1 ,"Invalid header type: %d\n" , type); +return -EINVAL ; + } +} + +static int copy_header_to_ring (struct fuse_ring_ent *ent, +enum fuse_uring_header_type type, +const void *header,size_t header_size) { +int offset =ring_header_type_offset (type); +void __user *ring; + +if (offset <0 ) +return offset; + + ring = (void __user *)ent->headers + offset; +if (copy_to_user (ring, header, header_size)) {pr_info_ratelimited ("Copying header to ring failed.\n" );return -EFAULT ;
@@ -593,10 +625,18 @@ static __always_inline int copy_header_treturn 0 ; } -static __always_inline int copy_header_from_ring(void *header, -const void __user *ring, -size_t header_size) +static int copy_header_from_ring (struct fuse_ring_ent *ent, +enum fuse_uring_header_type type,void *header, +size_t header_size) { +int offset =ring_header_type_offset (type); +const void __user *ring; + +if (offset <0 ) +return offset; + + ring = (void __user *)ent->headers + offset; +if (copy_from_user (header, ring, header_size)) {pr_info_ratelimited ("Copying header from ring failed.\n" );return -EFAULT ;
@@ -615,8 +655,8 @@ static int fuse_uring_copy_from_ring(strint err;struct fuse_uring_ent_in_out ring_in_out; - err =copy_header_from_ring (&ring_in_out, &ent->headers->ring_ent_in_out, -sizeof (ring_in_out)); + err =copy_header_from_ring (ent,FUSE_URING_HEADER_RING_ENT , + &ring_in_out,sizeof (ring_in_out));if (err)return err;
@@ -667,7 +707,7 @@ static int fuse_uring_args_to_ring(struc* Some op code have that as zero size. */ if (args->in_args[0 ].size >0 ) { - err =copy_header_to_ring (&ent->headers->op_in, + err =copy_header_to_ring (ent,FUSE_URING_HEADER_OP , in_args->value, in_args->size);if (err)
@@ -687,8 +727,8 @@ static int fuse_uring_args_to_ring(struc } ent_in_out.payload_sz = cs.ring.copied_sz; -return copy_header_to_ring (&ent->headers->ring_ent_in_out, &ent_in_out, -sizeof (ent_in_out)); +return copy_header_to_ring (ent,FUSE_URING_HEADER_RING_ENT , + &ent_in_out,sizeof (ent_in_out)); } static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
@@ -717,7 +757,7 @@ static int fuse_uring_copy_to_ring(struc }/* copy fuse_in_header */ -return copy_header_to_ring (&ent->headers->in_out, &req->in.h, +return copy_header_to_ring (ent,FUSE_URING_HEADER_IN_OUT , &req->in.h,sizeof (req->in.h)); }
@@ -854,7 +894,7 @@ static void fuse_uring_commit(struct fusstruct fuse_conn *fc = ring->fc;ssize_t err = -EFAULT ; -if (copy_header_from_ring (&req->out.h, &ent->headers->in_out, +if (copy_header_from_ring (ent,FUSE_URING_HEADER_IN_OUT , &req->out.h,sizeof (req->out.h))) goto out;
Greg Kroah-Hartman
maintainerPATCH 518/583 fuse-uring: refactor setting up copy state for payload copying
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit c0f9203732fc70de8d20697270bfe405481eac14 ]
Add a new helper function setup_fuse_copy_state() to contain the logic for setting up the copy state for payload copying.
Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")
--- fs/fuse/dev_uring.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
fs/fuse/dev_uring.c +24 -14
@@ -645,6 +645,27 @@ static int copy_header_from_ring(structreturn 0 ; } +static int setup_fuse_copy_state (struct fuse_copy_state *cs, +struct fuse_ring *ring,struct fuse_req *req, +struct fuse_ring_ent *ent,int dir, +struct iov_iter *iter) +{ +int err; + + err =import_ubuf (dir, ent->payload, ring->max_payload_sz, iter); +if (err) { +pr_info_ratelimited ("fuse: Import of user buffer failed\n" ); +return err; + } + +fuse_copy_init (cs, dir ==ITER_DEST , iter); + + cs->is_uring = true; + cs->req = req; + +return 0 ; +} +static int fuse_uring_copy_from_ring (struct fuse_ring *ring,struct fuse_req *req,struct fuse_ring_ent *ent)
@@ -660,15 +681,10 @@ static int fuse_uring_copy_from_ring(strif (err)return err; - err =import_ubuf (ITER_SOURCE , ent->payload, ring->max_payload_sz, - &iter); + err =setup_fuse_copy_state (&cs, ring, req, ent,ITER_SOURCE , &iter);if (err)return err; -fuse_copy_init (&cs, false, &iter); - cs.is_uring = true; - cs.req = req; - err =fuse_copy_out_args (&cs, args, ring_in_out.payload_sz);fuse_copy_finish (&cs);return err;
@@ -691,15 +707,9 @@ static int fuse_uring_args_to_ring(struc .commit_id = req->in.h.unique, }; - err =import_ubuf (ITER_DEST , ent->payload, ring->max_payload_sz, &iter); -if (err) { -pr_info_ratelimited ("fuse: Import of user buffer failed\n" ); + err =setup_fuse_copy_state (&cs, ring, req, ent,ITER_DEST , &iter); +if (err)return err; - } - -fuse_copy_init (&cs, true, &iter); - cs.is_uring = true; - cs.req = req;if (num_args >0 ) {/*
Greg Kroah-Hartman
maintainerPATCH 519/583 fuse-uring: use named constants for io-uring iovec indices
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
[ Upstream commit 8bbb2ad1f687633a991839bd3efae04ccfb29e19 ]
Replace magic indices 0 and 1 for the iovec array with named constants FUSE_URING_IOV_HEADERS and FUSE_URING_IOV_PAYLOAD. This makes the usages self-documenting and prepares for buffer ring support which will also reference these iovec slots by index.
Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")
--- fs/fuse/dev_uring.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
fs/fuse/dev_uring.c +13 -11
@@ -18,7 +18,8 @@ MODULE_PARM_DESC(enable_uring, "Enable userspace communication through io-uring");#define FUSE_URING_IOV_SEGS 2/* header and payload */ - +#define FUSE_URING_IOV_HEADERS 0 +#define FUSE_URING_IOV_PAYLOAD 1bool fuse_uring_enabled (void ) {
@@ -1105,8 +1106,8 @@ static int fuse_uring_do_register(struct }/* -* sqe->addr is a ptr to an iovec array, iov[0] has the headers, iov[1] -* the payload +* sqe->addr is a ptr to an iovec array, iov[FUSE_URING_IOV_HEADERS] has the +* headers, iov[FUSE_URING_IOV_PAYLOAD] the payload */ static int fuse_uring_get_iovec_from_sqe (const struct io_uring_sqe *sqe,struct iovec iov[FUSE_URING_IOV_SEGS ])
@@ -1136,8 +1137,8 @@ fuse_uring_create_ring_ent(struct io_uri {struct fuse_ring *ring = queue->ring;struct fuse_ring_ent *ent; -size_t payload_size;struct iovec iov[FUSE_URING_IOV_SEGS ]; +struct iovec *headers, *payload;int err; err =fuse_uring_get_iovec_from_sqe (cmd->sqe, iov);
@@ -1148,15 +1149,16 @@ fuse_uring_create_ring_ent(struct io_uri } err = -EINVAL ; -if (iov[0 ].iov_len <sizeof (struct fuse_uring_req_header )) { -pr_info_ratelimited ("Invalid header len %zu\n" , iov[0 ].iov_len); + headers = &iov[FUSE_URING_IOV_HEADERS ]; +if (headers->iov_len <sizeof (struct fuse_uring_req_header )) { +pr_info_ratelimited ("Invalid header len %zu\n" , headers->iov_len);return ERR_PTR (err); } - payload_size = iov[1 ].iov_len; -if (payload_size < ring->max_payload_sz) { + payload = &iov[FUSE_URING_IOV_PAYLOAD ]; +if (payload->iov_len < ring->max_payload_sz) {pr_info_ratelimited ("Invalid req payload len %zu\n" , - payload_size); + payload->iov_len);return ERR_PTR (err); }
@@ -1168,8 +1170,8 @@ fuse_uring_create_ring_ent(struct io_uriINIT_LIST_HEAD (&ent->list); ent->queue = queue; - ent->headers = iov[0 ].iov_base; - ent->payload = iov[1 ].iov_base; + ent->headers = headers->iov_base; + ent->payload = payload->iov_base;atomic_inc (&ring->queue_refs);return ent;
Greg Kroah-Hartman
maintainerPATCH 520/583 fuse: copy request headers via a stack buffer for io-uring
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit fd10f40af314f07b6d6e028b1ca25c8b49903aab ]
The fuse-io-uring transport copies req->in.h out to the ring in fuse_uring_copy_to_ring() and req->out.h back in fuse_uring_commit(). Both headers live inside the fuse_request slab object, whose cache (fuse_req_cachep) is created without a usercopy whitelist, so copying them directly to/from userspace trips CONFIG_HARDENED_USERCOPY and panics:
usercopy: Kernel memory exposure attempt detected from SLUB object 'fuse_request' (offset 56, size 40)!
kernel BUG at mm/usercopy.c:102!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:usercopy_abort (mm/usercopy.c:90) Call Trace: __check_heap_object (mm/slub.c:8268) __check_object_size (mm/usercopy.c:197 mm/usercopy.c:258 mm/usercopy.c:223) copy_header_to_ring (fs/fuse/dev_uring.c:618) fuse_uring_prepare_send (fs/fuse/dev_uring.c:776 fs/fuse/dev_uring.c:785) fuse_uring_send_in_task (fs/fuse/dev_uring.c:1306) tctx_task_work_run (io_uring/tw.c:96) task_work_run (kernel/task_work.c:233) io_run_task_work (io_uring/tw.h:84) io_cqring_wait (io_uring/wait.c:278) __do_sys_io_uring_enter (io_uring/io_uring.c:2685) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Bounce both headers through an on-stack copy so the usercopy touches stack memory, not the slab object.
--- fs/fuse/dev_uring.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
fs/fuse/dev_uring.c +8 -4
@@ -747,6 +747,7 @@ static int fuse_uring_copy_to_ring(struc {struct fuse_ring_queue *queue = ent->queue;struct fuse_ring *ring = queue->ring; +struct fuse_in_header in_header;int err; err = -EIO ;
@@ -768,8 +769,9 @@ static int fuse_uring_copy_to_ring(struc }/* copy fuse_in_header */ -return copy_header_to_ring (ent,FUSE_URING_HEADER_IN_OUT , &req->in.h, -sizeof (req->in.h)); + in_header = req->in.h; +return copy_header_to_ring (ent,FUSE_URING_HEADER_IN_OUT , &in_header, +sizeof (in_header)); } static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
@@ -903,11 +905,13 @@ static void fuse_uring_commit(struct fus {struct fuse_ring *ring = ent->queue->ring;struct fuse_conn *fc = ring->fc; +struct fuse_out_header out_header;ssize_t err = -EFAULT ; -if (copy_header_from_ring (ent,FUSE_URING_HEADER_IN_OUT , &req->out.h, -sizeof (req->out.h))) +if (copy_header_from_ring (ent,FUSE_URING_HEADER_IN_OUT , &out_header, +sizeof (out_header))) goto out; + req->out.h = out_header; err =fuse_uring_out_header_has_err (&req->out.h, req, fc);if (err) {
Greg Kroah-Hartman
maintainerPATCH 521/583 ipv6: pass proto by value to ipv6_push_nfrag_opts() and ipv6_push_frag_opts()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1bc46dd209667346c5aa56c351995e7c260696d0 ]
With CONFIG_STACKPROTECTOR_STRONG=y, it is better to avoid passing a pointer to an automatic variable.
Change these exported functions to return 'u8 proto' instead of void.
- ipv6_push_nfrag_opts()
- ipv6_push_frag_opts()
For instance, replace
ipv6_push_frag_opts(skb, opt, &proto); with: proto = ipv6_push_frag_opts(skb, opt, proto);
Note that even after this change, ip6_xmit() has to use a stack canary because of @first_hop variable.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- include/net/ipv6.h | 10 ++++----- net/ipv6/exthdrs.c | 55 ++++++++++++++++++++++++++------------------------ net/ipv6/ip6_output.c | 19 ++++++++++------- net/ipv6/ip6_tunnel.c | 2 - 4 files changed, 46 insertions(+), 40 deletions(-)
include/net/ipv6.h +5 -5
@@ -1147,11 +1147,11 @@ int ip6_local_out(struct net *net, struc* Extension header (options) processing */ -void ipv6_push_nfrag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, -u8 *proto,struct in6_addr **daddr_p, -struct in6_addr *saddr); -void ipv6_push_frag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, -u8 *proto); +u8 ipv6_push_nfrag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, +u8 proto,struct in6_addr **daddr_p, +struct in6_addr *saddr); +u8 ipv6_push_frag_opts (struct sk_buff *skb,struct ipv6_txoptions *opt, +u8 proto);int ipv6_skip_exthdr (const struct sk_buff *,int start,u8 *nexthdrp,__be16 *frag_offp);
net/ipv6/exthdrs.c +29 -26
109 lines not on this page · raw message
net/ipv6/ip6_output.c +11 -8
@@ -307,11 +307,12 @@ int ip6_xmit(const struct sock *sk, stru seg_len += opt->opt_nflen + opt->opt_flen;if (opt->opt_flen) -ipv6_push_frag_opts (skb, opt, &proto); + proto =ipv6_push_frag_opts (skb, opt, proto);if (opt->opt_nflen) -ipv6_push_nfrag_opts (skb, opt, &proto, &first_hop, - &fl6->saddr); + proto =ipv6_push_nfrag_opts (skb, opt, proto, + &first_hop, + &fl6->saddr); }if (unlikely (seg_len >IPV6_MAXPLEN )) {
@@ -1941,11 +1942,13 @@ struct sk_buff *__ip6_make_skb(struct so__skb_pull (skb,skb_network_header_len (skb)); final_dst = &fl6->daddr; -if (opt && opt->opt_flen) -ipv6_push_frag_opts (skb, opt, &proto); -if (opt && opt->opt_nflen) -ipv6_push_nfrag_opts (skb, opt, &proto, &final_dst, &fl6->saddr); - +if (opt) { +if (opt->opt_flen) + proto =ipv6_push_frag_opts (skb, opt, proto); +if (opt->opt_nflen) + proto =ipv6_push_nfrag_opts (skb, opt, proto, + &final_dst, &fl6->saddr); + }skb_push (skb,sizeof (struct ipv6hdr ));skb_reset_network_header (skb); hdr =ipv6_hdr (skb);
net/ipv6/ip6_tunnel.c +1 -1
@@ -1262,7 +1262,7 @@ route_lookup:if (encap_limit >=0 ) {init_tel_txopt (&opt, encap_limit); -ipv6_push_frag_opts (skb, &opt.ops, &proto); + proto =ipv6_push_frag_opts (skb, &opt.ops, proto); }skb_push (skb,sizeof (struct ipv6hdr ));
Greg Kroah-Hartman
maintainerPATCH 522/583 ipv6: add some unlikely()/likely() clauses in ip6_output.c
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 2855e4925416c5900a9c6cece11f4a2e56bf6e63 ]
1) daddr is unlikely a multicast in ip6_finish_output2().
2) ip6_finish_output_gso_slowpath_drop() should not be called often.
3) ip6_fragment() should not be called often.
4) opt is unlikely to be set.
5) ip6_xmit() and ip6_forward() mostly sends not too big packets.
6) Most __ip6_make_skb() calls are for UDP packets, not ICMPV6 ones.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv6/ip6_output.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
net/ipv6/ip6_output.c +12 -12
@@ -80,7 +80,7 @@ static int ip6_finish_output2(struct net hdr =ipv6_hdr (skb); daddr = &hdr->daddr; -if (ipv6_addr_is_multicast (daddr)) { +if (unlikely (ipv6_addr_is_multicast (daddr))) { if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && ((mroute6_is_socket(net, skb) && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
@@ -181,8 +181,8 @@ ip6_finish_output_gso_slowpath_drop(strustatic int ip6_finish_output_gso (struct net *net,struct sock *sk,struct sk_buff *skb,unsigned int mtu) { -if (!(IP6CB (skb)->flags &IP6SKB_FAKEJUMBO ) && - !skb_gso_validate_network_len (skb, mtu)) +if (unlikely (!(IP6CB (skb)->flags &IP6SKB_FAKEJUMBO ) && + !skb_gso_validate_network_len (skb, mtu)))return ip6_finish_output_gso_slowpath_drop (net, sk, skb, mtu);return ip6_finish_output2 (net, sk, skb);
@@ -204,8 +204,8 @@ static int __ip6_finish_output(struct neif (skb_is_gso (skb))return ip6_finish_output_gso (net, sk, skb, mtu); -if (skb->len > mtu || - (IP6CB (skb)->frag_max_size && skb->len >IP6CB (skb)->frag_max_size)) +if (unlikely (skb->len > mtu || + (IP6CB (skb)->frag_max_size && skb->len >IP6CB (skb)->frag_max_size)))return ip6_fragment (net, sk, skb, ip6_finish_output2);return ip6_finish_output2 (net, sk, skb);
@@ -303,7 +303,7 @@ int ip6_xmit(const struct sock *sk, stru } } -if (opt) { +if (unlikely (opt)) { seg_len += opt->opt_nflen + opt->opt_flen;if (opt->opt_flen)
@@ -356,7 +356,7 @@ int ip6_xmit(const struct sock *sk, stru skb->mark = mark; mtu =dst_mtu (dst); -if ((skb->len <= mtu) || skb->ignore_df ||skb_is_gso (skb)) { +if (likely ((skb->len <= mtu) || skb->ignore_df ||skb_is_gso (skb))) {IP6_INC_STATS (net, idev,IPSTATS_MIB_OUTREQUESTS );/* if egress device is enslaved to an L3 master device pass the
@@ -656,7 +656,7 @@ int ip6_forward(struct sk_buff *skb)if (mtu <IPV6_MIN_MTU ) mtu =IPV6_MIN_MTU ; -if (ip6_pkt_too_big (skb, mtu)) { +if (unlikely (ip6_pkt_too_big (skb, mtu))) {/* Again, force OUTPUT device used as source address */ skb->dev = dev;icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu);
@@ -1370,7 +1370,7 @@ static int ip6_setup_cork(struct sock *s/* * setup for corking */ -if (opt) { +if (unlikely (opt)) {if (WARN_ON (v6_cork->opt))return -EINVAL ;
@@ -1886,7 +1886,7 @@ static void ip6_cork_steal_dst(struct skstatic void ip6_cork_release (struct inet_cork_full *cork,struct inet6_cork *v6_cork) { -if (v6_cork->opt) { +if (unlikely (v6_cork->opt)) {struct ipv6_txoptions *opt = v6_cork->opt;kfree (opt->dst0opt);
@@ -1942,7 +1942,7 @@ struct sk_buff *__ip6_make_skb(struct so__skb_pull (skb,skb_network_header_len (skb)); final_dst = &fl6->daddr; -if (opt) { +if (unlikely (opt)) {if (opt->opt_flen) proto =ipv6_push_frag_opts (skb, opt, proto);if (opt->opt_nflen)
@@ -1970,7 +1970,7 @@ struct sk_buff *__ip6_make_skb(struct soip6_cork_steal_dst (skb, cork);IP6_INC_STATS (net, rt->rt6i_idev,IPSTATS_MIB_OUTREQUESTS ); -if (proto ==IPPROTO_ICMPV6 ) { +if (unlikely (proto ==IPPROTO_ICMPV6 )) {struct inet6_dev *idev =ip6_dst_idev (skb_dst (skb));u8 icmp6_type;
Greg Kroah-Hartman
maintainerPATCH 523/583 inet: add dst4_mtu() and dst6_mtu() helpers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 8776c4ef3ab22b95f55713f00a7a576e6402507d ]
With CONFIG_MITIGATION_RETPOLINE=y dst_mtu() is a bit fat, because it is generic.
Indeed, clang does not always inline it.
Add dst4_mtu() and dst6_mtu() helpers for callers that expect either ipv4_mtu() or ip6_mtu() to be called.
These helpers are always inlined.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- include/net/dst.h | 6 ++++++ include/net/ip6_route.h | 6 ++++++ 2 files changed, 12 insertions(+)
include/net/dst.h +6 -0
@@ -219,6 +219,12 @@ static inline u32 dst_mtu(const struct dreturn INDIRECT_CALL_INET (dst->ops->mtu, ip6_mtu, ipv4_mtu, dst); } +/* Variant of dst_mtu() for IPv4 users. */ +static inline u32 dst4_mtu (const struct dst_entry *dst) +{ +return INDIRECT_CALL_1 (dst->ops->mtu, ipv4_mtu, dst); +} +/* RTT metrics are stored in milliseconds for user ABI, but used as jiffies */ static inline unsigned long dst_metric_rtt (const struct dst_entry *dst,int metric) {
include/net/ip6_route.h +6 -0
@@ -266,6 +266,12 @@ static inline bool ipv6_anycast_destinatint ip6_fragment (struct net *net,struct sock *sk,struct sk_buff *skb,int (*output)(struct net *,struct sock *,struct sk_buff *)); +/* Variant of dst_mtu() for IPv6 users */ +static inline u32 dst6_mtu (const struct dst_entry *dst) +{ +return INDIRECT_CALL_1 (dst->ops->mtu, ip6_mtu, dst); +} +static inline unsigned int ip6_skb_dst_mtu (const struct sk_buff *skb) { const struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
Greg Kroah-Hartman
maintainerPATCH 524/583 ipv6: use dst6_mtu() instead of dst_mtu()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit b40f0130a23b6bab8b359e158a40af81f11f14be ]
When we expect an IPv6 dst, use dst6_mtu() instead of dst_mtu() to save some code space.
Due to current dst6_mtu() implementation, only convert users in IPv6 stack.
Stable-dep-of: 870a9e42ecc6 ("tcp: clamp route advmss to TCP_MIN_MSS")
--- net/ipv6/ip6_gre.c | 2 +- net/ipv6/ip6_output.c | 6 +++--- net/ipv6/ip6_tunnel.c | 4 ++-- net/ipv6/ipv6_sockglue.c | 4 ++-- net/ipv6/route.c | 10 ++++++---- net/ipv6/tcp_ipv6.c | 9 +++++---- 6 files changed, 19 insertions(+), 16 deletions(-)
net/ipv6/ip6_gre.c +1 -1
@@ -1057,7 +1057,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit/* TooBig packet may have updated dst->dev's mtu */ if (!t->parms.collect_md && dst) { mtu =READ_ONCE (dst_dev (dst)->mtu); -if (dst_mtu (dst) > mtu) +if (dst6_mtu (dst) > mtu) dst->ops->update_pmtu (dst,NULL , skb, mtu, false); } err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
net/ipv6/ip6_output.c +3 -3
@@ -355,7 +355,7 @@ int ip6_xmit(const struct sock *sk, stru skb->priority = priority; skb->mark = mark; - mtu =dst_mtu (dst); + mtu =dst6_mtu (dst);if (likely ((skb->len <= mtu) || skb->ignore_df ||skb_is_gso (skb))) {IP6_INC_STATS (net, idev,IPSTATS_MIB_OUTREQUESTS );
@@ -1405,10 +1405,10 @@ static int ip6_setup_cork(struct sock *s v6_cork->dontfrag = ipc6->dontfrag;if (rt->dst.flags &DST_XFRM_TUNNEL ) mtu =READ_ONCE (np->pmtudisc) >=IPV6_PMTUDISC_PROBE ? -READ_ONCE (rt->dst.dev->mtu) :dst_mtu (&rt->dst); +READ_ONCE (rt->dst.dev->mtu) :dst6_mtu (&rt->dst);else mtu =READ_ONCE (np->pmtudisc) >=IPV6_PMTUDISC_PROBE ? -READ_ONCE (rt->dst.dev->mtu) :dst_mtu (xfrm_dst_path (&rt->dst)); +READ_ONCE (rt->dst.dev->mtu) :dst6_mtu (xfrm_dst_path (&rt->dst)); frag_size =READ_ONCE (np->frag_size);if (frag_size && frag_size < mtu)
net/ipv6/ip6_tunnel.c +2 -2
@@ -643,7 +643,7 @@ ip4ip6_err(struct sk_buff *skb, struct i/* change mtu on this route */ if (rel_type ==ICMP_DEST_UNREACH && rel_code ==ICMP_FRAG_NEEDED ) { -if (rel_info >dst_mtu (skb_dst (skb2))) +if (rel_info >dst6_mtu (skb_dst (skb2))) goto out;skb_dst_update_pmtu_no_confirm (skb2, rel_info);
@@ -1195,7 +1195,7 @@ route_lookup: t->parms.name); goto tx_err_dst_release; } - mtu =dst_mtu (dst) - eth_hlen - psh_hlen - t->tun_hlen; + mtu =dst6_mtu (dst) - eth_hlen - psh_hlen - t->tun_hlen;if (encap_limit >=0 ) { max_headroom +=8 ; mtu -=8 ;
net/ipv6/ipv6_sockglue.c +2 -2
@@ -1184,7 +1184,7 @@ int do_ipv6_getsockopt(struct sock *sk,rcu_read_lock (); dst =__sk_dst_get (sk);if (dst) - val =dst_mtu (dst); + val =dst6_mtu (dst);rcu_read_unlock ();if (!val)return -ENOTCONN ;
@@ -1283,7 +1283,7 @@ int do_ipv6_getsockopt(struct sock *sk,rcu_read_lock (); dst =__sk_dst_get (sk);if (dst) - mtuinfo.ip6m_mtu =dst_mtu (dst); + mtuinfo.ip6m_mtu =dst6_mtu (dst);rcu_read_unlock ();if (!mtuinfo.ip6m_mtu)return -ENOTCONN ;
net/ipv6/route.c +6 -4
@@ -2053,6 +2053,8 @@ unlock:static bool rt6_mtu_change_route_allowed (struct inet6_dev *idev,struct rt6_info *rt,int mtu) { +u32 dmtu =dst6_mtu (&rt->dst); +/* If the new MTU is lower than the route PMTU, this new MTU will be the * lowest MTU in the path: always allow updating the route PMTU to * reflect PMTU decreases.
@@ -2063,10 +2065,10 @@ static bool rt6_mtu_change_route_allowed* handle this. */ -if (dst_mtu (&rt->dst) >= mtu) +if (dmtu >= mtu)return true; -if (dst_mtu (&rt->dst) == idev->cnf.mtu6) +if (dmtu == idev->cnf.mtu6)return true;return false;
@@ -2936,7 +2938,7 @@ static void __ip6_rt_update_pmtu(structif (mtu <IPV6_MIN_MTU )return ; -if (mtu >=dst_mtu (dst)) +if (mtu >=dst6_mtu (dst))return ;if (!rt6_cache_allowed_for_pmtu (rt6)) {
@@ -3252,7 +3254,7 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);static unsigned int ip6_default_advmss (const struct dst_entry *dst) { -unsigned int mtu =dst_mtu (dst); +unsigned int mtu =dst6_mtu (dst);struct net *net; mtu -=sizeof (struct ipv6hdr ) +sizeof (struct tcphdr );
net/ipv6/tcp_ipv6.c +5 -4
@@ -351,7 +351,7 @@ failure:static void tcp_v6_mtu_reduced (struct sock *sk) {struct dst_entry *dst; -u32 mtu; +u32 mtu, dmtu;if ((1 << sk->sk_state) & (TCPF_LISTEN |TCPF_CLOSE ))return ;
@@ -368,8 +368,9 @@ static void tcp_v6_mtu_reduced(struct soif (!dst)return ; -if (inet_csk (sk)->icsk_pmtu_cookie >dst_mtu (dst)) { -tcp_sync_mss (sk,dst_mtu (dst)); + dmtu =dst6_mtu (dst); +if (inet_csk (sk)->icsk_pmtu_cookie > dmtu) { +tcp_sync_mss (sk, dmtu);tcp_simple_retransmit (sk); } }
@@ -1499,7 +1500,7 @@ static struct sock *tcp_v6_syn_recv_socktcp_ca_openreq_child (newsk, dst); -tcp_sync_mss (newsk,dst_mtu (dst)); +tcp_sync_mss (newsk,dst6_mtu (dst)); newtp->advmss =tcp_mss_clamp (tcp_sk (sk),dst_metric_advmss (dst));tcp_initialize_rcv_mss (newsk);