From 4beba210bb7dfe672edff02503187b30ff8517de Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 6 Aug 2026 13:40:38 -0400 Subject: [PATCH] Fixes for all trees Signed-off-by: Sasha Levin --- ...10-svc-fix-memory-leaks-and-list-cor.patch | 62 ++++ ...raw_spinlock_t-for-the-register-lock.patch | 179 ++++++++++ queue-5.10/series | 2 + ...10-svc-fix-memory-leaks-and-list-cor.patch | 62 ++++ ...raw_spinlock_t-for-the-register-lock.patch | 179 ++++++++++ queue-5.15/series | 2 + ...10-svc-fix-memory-leaks-and-list-cor.patch | 71 ++++ ...raw_spinlock_t-for-the-register-lock.patch | 179 ++++++++++ queue-6.1/series | 2 + ...raw_spinlock_t-for-the-register-lock.patch | 179 ++++++++++ ...troduce-mem_alloc_profiling_permanen.patch | 83 +++++ ...unbounded-recursion-in-free-path-wit.patch | 331 ++++++++++++++++++ queue-6.12/series | 3 + ...10-svc-fix-memory-leaks-and-list-cor.patch | 71 ++++ ...raw_spinlock_t-for-the-register-lock.patch | 179 ++++++++++ queue-6.6/series | 2 + 16 files changed, 1586 insertions(+) create mode 100644 queue-5.10/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch create mode 100644 queue-5.10/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch create mode 100644 queue-5.15/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch create mode 100644 queue-5.15/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch create mode 100644 queue-6.1/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch create mode 100644 queue-6.1/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch create mode 100644 queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch create mode 100644 queue-6.12/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch create mode 100644 queue-6.12/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch create mode 100644 queue-6.6/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch create mode 100644 queue-6.6/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch diff --git a/queue-5.10/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch b/queue-5.10/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch new file mode 100644 index 0000000000..19a9604ea1 --- /dev/null +++ b/queue-5.10/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch @@ -0,0 +1,62 @@ +From 9ca609a97c3a89e9d3617fee21cb543affcf9512 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Aug 2026 01:59:01 -0700 +Subject: firmware: stratix10-svc: fix memory leaks and list corruption bugs + +From: Tze Yee Ng + +[ Upstream commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c ] + +Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error +path. Switch pmem allocation from devm_kzalloc() to kzalloc() with +explicit kfree() in the free path to match its list-managed lifetime. +Remove the erroneous list_del(&svc_data_mem) which corrupted the list head +on failed lookups. + +Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") +Cc: stable@vger.kernel.org#5.0+ +Signed-off-by: Tze Yee Ng +Signed-off-by: Dinh Nguyen +(cherry picked from commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c) +Signed-off-by: Sasha Levin +--- + drivers/firmware/stratix10-svc.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c +index 6b6a819fcddfa..ae6d942ab2d75 100644 +--- a/drivers/firmware/stratix10-svc.c ++++ b/drivers/firmware/stratix10-svc.c +@@ -911,13 +911,15 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, + struct gen_pool *genpool = chan->ctrl->genpool; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + +- pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); ++ pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); + if (!pmem) + return ERR_PTR(-ENOMEM); + + va = gen_pool_alloc(genpool, s); +- if (!va) ++ if (!va) { ++ kfree(pmem); + return ERR_PTR(-ENOMEM); ++ } + + memset((void *)va, 0, s); + pa = gen_pool_virt_to_phys(genpool, va); +@@ -950,10 +952,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + (unsigned long)kaddr, pmem->size); + pmem->vaddr = NULL; + list_del(&pmem->node); ++ kfree(pmem); + return; + } +- +- list_del(&svc_data_mem); + } + EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); + +-- +2.53.0 + diff --git a/queue-5.10/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-5.10/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch new file mode 100644 index 0000000000..48885864f9 --- /dev/null +++ b/queue-5.10/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -0,0 +1,179 @@ +From 1143a6fed4e2728d2942e115bdbdd004ec997584 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 10:17:25 +0800 +Subject: gpio: pch: use raw_spinlock_t for the register lock + +From: Junjie Cao + +[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ] + +pch_irq_type() is registered as the irq_chip .irq_set_type callback and +takes chip->spinlock with spin_lock_irqsave(). This callback is reached +from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while +the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. +That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is +an rtmutex-backed sleeping lock, so acquiring it there is invalid. + +This was confirmed on a PREEMPT_RT kernel with lockdep +(PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored +pch_irq_type()'s locking and drove it through the real genirq carrier +irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. +the same __irq_set_trigger() edge that __setup_irq() takes for a +requested IRQ. With the original spin_lock_irqsave() edge lockdep +reported an invalid wait context, immediately followed by: + + BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 + in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod + hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 + rt_spin_lock+0x3a/0x1c0 + repro_irq_set_type+0x64/0xa0 [pch_repro] + __irq_set_trigger+0x69/0x140 + irq_set_irq_type+0x78/0xd0 + +Switching the mirrored lock to raw_spinlock_t made both splats go away. + +Convert the register lock to raw_spinlock_t. The same lock also +serializes the GPIO direction/value callbacks and the suspend/resume +register save/restore, but all of those critical sections only perform +MMIO register accesses (ioread32()/iowrite32()) and +irq_set_handler_locked(); none of them contain sleepable operations. +Keeping this register lock non-sleeping is therefore appropriate for the +irqchip callbacks and does not change the GPIO-side locking contract. + +This is the same class of issue and fix as recently addressed for other +GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t +in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use +raw_spinlock_t in the irq startup path"). + +Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") +Cc: stable@vger.kernel.org +Signed-off-by: Junjie Cao +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com +Signed-off-by: Bartosz Golaszewski +(cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c +index a552df298a974..af4b3b7f3033d 100644 +--- a/drivers/gpio/gpio-pch.c ++++ b/drivers/gpio/gpio-pch.c +@@ -92,7 +92,7 @@ struct pch_gpio { + struct pch_gpio_reg_data pch_gpio_reg; + int irq_base; + enum pch_type_t ioh; +- spinlock_t spinlock; ++ raw_spinlock_t spinlock; + }; + + static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -101,7 +101,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + struct pch_gpio *chip = gpiochip_get_data(gpio); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= BIT(nr); +@@ -109,7 +109,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + reg_val &= ~BIT(nr); + + iowrite32(reg_val, &chip->reg->po); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + } + + static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) +@@ -127,7 +127,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + u32 reg_val; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + reg_val = ioread32(&chip->reg->po); + if (val) +@@ -141,7 +141,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + pm |= BIT(nr); + iowrite32(pm, &chip->reg->pm); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -152,12 +152,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) + u32 pm; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pm = ioread32(&chip->reg->pm); + pm &= BIT(gpio_pins[chip->ioh]) - 1; + pm &= ~BIT(nr); + iowrite32(pm, &chip->reg->pm); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -259,7 +259,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + return 0; + } + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); +@@ -271,7 +271,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + return 0; + } + +@@ -378,7 +378,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, + + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); +- spin_lock_init(&chip->spinlock); ++ raw_spin_lock_init(&chip->spinlock); + pch_gpio_setup(chip); + + ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip); +@@ -415,9 +415,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pch_gpio_save_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -427,11 +427,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index c2315769f4..87beecc297 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -304,3 +304,5 @@ mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch bluetooth-sco-fix-uaf-on-sco_sock_timeout.patch bluetooth-sco-fix-use-after-free-in-sco_recv_frame-d.patch net-openvswitch-fix-skb-leak-on-flow-key-update-fail.patch +firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch +gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch diff --git a/queue-5.15/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch b/queue-5.15/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch new file mode 100644 index 0000000000..eb83700014 --- /dev/null +++ b/queue-5.15/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch @@ -0,0 +1,62 @@ +From 3e5b3c5cdff66e252bdf1fc3f696e98d3ddf49b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 4 Aug 2026 04:17:00 -0700 +Subject: firmware: stratix10-svc: fix memory leaks and list corruption bugs + +From: Tze Yee Ng + +[ Upstream commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c ] + +Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error +path. Switch pmem allocation from devm_kzalloc() to kzalloc() with +explicit kfree() in the free path to match its list-managed lifetime. +Remove the erroneous list_del(&svc_data_mem) which corrupted the list head +on failed lookups. + +Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") +Cc: stable@vger.kernel.org#5.0+ +Signed-off-by: Tze Yee Ng +Signed-off-by: Dinh Nguyen +(cherry picked from commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c) +Signed-off-by: Sasha Levin +--- + drivers/firmware/stratix10-svc.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c +index 993ef210169c0..c5ddd20fe4b75 100644 +--- a/drivers/firmware/stratix10-svc.c ++++ b/drivers/firmware/stratix10-svc.c +@@ -911,13 +911,15 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, + struct gen_pool *genpool = chan->ctrl->genpool; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + +- pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); ++ pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); + if (!pmem) + return ERR_PTR(-ENOMEM); + + va = gen_pool_alloc(genpool, s); +- if (!va) ++ if (!va) { ++ kfree(pmem); + return ERR_PTR(-ENOMEM); ++ } + + memset((void *)va, 0, s); + pa = gen_pool_virt_to_phys(genpool, va); +@@ -950,10 +952,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + (unsigned long)kaddr, pmem->size); + pmem->vaddr = NULL; + list_del(&pmem->node); ++ kfree(pmem); + return; + } +- +- list_del(&svc_data_mem); + } + EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); + +-- +2.53.0 + diff --git a/queue-5.15/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-5.15/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch new file mode 100644 index 0000000000..1e8678bbeb --- /dev/null +++ b/queue-5.15/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -0,0 +1,179 @@ +From 4e724bf3933b6bc35d9a63b202456372732b3586 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 10:18:16 +0800 +Subject: gpio: pch: use raw_spinlock_t for the register lock + +From: Junjie Cao + +[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ] + +pch_irq_type() is registered as the irq_chip .irq_set_type callback and +takes chip->spinlock with spin_lock_irqsave(). This callback is reached +from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while +the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. +That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is +an rtmutex-backed sleeping lock, so acquiring it there is invalid. + +This was confirmed on a PREEMPT_RT kernel with lockdep +(PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored +pch_irq_type()'s locking and drove it through the real genirq carrier +irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. +the same __irq_set_trigger() edge that __setup_irq() takes for a +requested IRQ. With the original spin_lock_irqsave() edge lockdep +reported an invalid wait context, immediately followed by: + + BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 + in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod + hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 + rt_spin_lock+0x3a/0x1c0 + repro_irq_set_type+0x64/0xa0 [pch_repro] + __irq_set_trigger+0x69/0x140 + irq_set_irq_type+0x78/0xd0 + +Switching the mirrored lock to raw_spinlock_t made both splats go away. + +Convert the register lock to raw_spinlock_t. The same lock also +serializes the GPIO direction/value callbacks and the suspend/resume +register save/restore, but all of those critical sections only perform +MMIO register accesses (ioread32()/iowrite32()) and +irq_set_handler_locked(); none of them contain sleepable operations. +Keeping this register lock non-sleeping is therefore appropriate for the +irqchip callbacks and does not change the GPIO-side locking contract. + +This is the same class of issue and fix as recently addressed for other +GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t +in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use +raw_spinlock_t in the irq startup path"). + +Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") +Cc: stable@vger.kernel.org +Signed-off-by: Junjie Cao +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com +Signed-off-by: Bartosz Golaszewski +(cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c +index a552df298a974..af4b3b7f3033d 100644 +--- a/drivers/gpio/gpio-pch.c ++++ b/drivers/gpio/gpio-pch.c +@@ -92,7 +92,7 @@ struct pch_gpio { + struct pch_gpio_reg_data pch_gpio_reg; + int irq_base; + enum pch_type_t ioh; +- spinlock_t spinlock; ++ raw_spinlock_t spinlock; + }; + + static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -101,7 +101,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + struct pch_gpio *chip = gpiochip_get_data(gpio); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= BIT(nr); +@@ -109,7 +109,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + reg_val &= ~BIT(nr); + + iowrite32(reg_val, &chip->reg->po); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + } + + static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) +@@ -127,7 +127,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + u32 reg_val; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + reg_val = ioread32(&chip->reg->po); + if (val) +@@ -141,7 +141,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + pm |= BIT(nr); + iowrite32(pm, &chip->reg->pm); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -152,12 +152,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) + u32 pm; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pm = ioread32(&chip->reg->pm); + pm &= BIT(gpio_pins[chip->ioh]) - 1; + pm &= ~BIT(nr); + iowrite32(pm, &chip->reg->pm); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -259,7 +259,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + return 0; + } + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); +@@ -271,7 +271,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + return 0; + } + +@@ -378,7 +378,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, + + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); +- spin_lock_init(&chip->spinlock); ++ raw_spin_lock_init(&chip->spinlock); + pch_gpio_setup(chip); + + ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip); +@@ -415,9 +415,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pch_gpio_save_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -427,11 +427,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 139cb65a17..d9ae4cba12 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -361,3 +361,5 @@ drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch hid-logitech-dj-fix-maxfield-check-in-dj-short-report-validation.patch mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch net-openvswitch-fix-skb-leak-on-flow-key-update-fail.patch +firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch +gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch diff --git a/queue-6.1/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch b/queue-6.1/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch new file mode 100644 index 0000000000..dc8656e287 --- /dev/null +++ b/queue-6.1/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch @@ -0,0 +1,71 @@ +From c14428e91b64a997686abea279fe36671271a76d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 03:18:07 -0700 +Subject: firmware: stratix10-svc: fix memory leaks and list corruption bugs + +From: Tze Yee Ng + +[ Upstream commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c ] + +Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error +path. Switch pmem allocation from devm_kzalloc() to kzalloc() with +explicit kfree() in the free path to match its list-managed lifetime. +Remove the erroneous list_del(&svc_data_mem) which corrupted the list head +on failed lookups. + +Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") +Cc: stable@vger.kernel.org#5.0+ +Signed-off-by: Tze Yee Ng +Signed-off-by: Dinh Nguyen +(cherry picked from commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c) +Signed-off-by: Sasha Levin +--- + drivers/firmware/stratix10-svc.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c +index b25d793805ce0..c4a709f2bbc7a 100644 +--- a/drivers/firmware/stratix10-svc.c ++++ b/drivers/firmware/stratix10-svc.c +@@ -1065,14 +1065,16 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, + struct gen_pool *genpool = chan->ctrl->genpool; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + +- pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); ++ pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); + if (!pmem) + return ERR_PTR(-ENOMEM); + + guard(mutex)(&svc_mem_lock); + va = gen_pool_alloc(genpool, s); +- if (!va) ++ if (!va) { ++ kfree(pmem); + return ERR_PTR(-ENOMEM); ++ } + + memset((void *)va, 0, s); + pa = gen_pool_virt_to_phys(genpool, va); +@@ -1098,6 +1100,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory); + void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + { + struct stratix10_svc_data_mem *pmem; ++ + guard(mutex)(&svc_mem_lock); + + list_for_each_entry(pmem, &svc_data_mem, node) +@@ -1106,10 +1109,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + (unsigned long)kaddr, pmem->size); + pmem->vaddr = NULL; + list_del(&pmem->node); ++ kfree(pmem); + return; + } +- +- list_del(&svc_data_mem); + } + EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); + +-- +2.53.0 + diff --git a/queue-6.1/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-6.1/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch new file mode 100644 index 0000000000..7bb14a892f --- /dev/null +++ b/queue-6.1/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -0,0 +1,179 @@ +From 6151578f2afb40fb19fa30af507f24cf1a280744 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 10:18:46 +0800 +Subject: gpio: pch: use raw_spinlock_t for the register lock + +From: Junjie Cao + +[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ] + +pch_irq_type() is registered as the irq_chip .irq_set_type callback and +takes chip->spinlock with spin_lock_irqsave(). This callback is reached +from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while +the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. +That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is +an rtmutex-backed sleeping lock, so acquiring it there is invalid. + +This was confirmed on a PREEMPT_RT kernel with lockdep +(PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored +pch_irq_type()'s locking and drove it through the real genirq carrier +irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. +the same __irq_set_trigger() edge that __setup_irq() takes for a +requested IRQ. With the original spin_lock_irqsave() edge lockdep +reported an invalid wait context, immediately followed by: + + BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 + in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod + hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 + rt_spin_lock+0x3a/0x1c0 + repro_irq_set_type+0x64/0xa0 [pch_repro] + __irq_set_trigger+0x69/0x140 + irq_set_irq_type+0x78/0xd0 + +Switching the mirrored lock to raw_spinlock_t made both splats go away. + +Convert the register lock to raw_spinlock_t. The same lock also +serializes the GPIO direction/value callbacks and the suspend/resume +register save/restore, but all of those critical sections only perform +MMIO register accesses (ioread32()/iowrite32()) and +irq_set_handler_locked(); none of them contain sleepable operations. +Keeping this register lock non-sleeping is therefore appropriate for the +irqchip callbacks and does not change the GPIO-side locking contract. + +This is the same class of issue and fix as recently addressed for other +GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t +in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use +raw_spinlock_t in the irq startup path"). + +Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") +Cc: stable@vger.kernel.org +Signed-off-by: Junjie Cao +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com +Signed-off-by: Bartosz Golaszewski +(cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c +index ee37ecb615cb1..77e84e0e6c1ba 100644 +--- a/drivers/gpio/gpio-pch.c ++++ b/drivers/gpio/gpio-pch.c +@@ -97,7 +97,7 @@ struct pch_gpio { + struct pch_gpio_reg_data pch_gpio_reg; + int irq_base; + enum pch_type_t ioh; +- spinlock_t spinlock; ++ raw_spinlock_t spinlock; + }; + + static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -106,7 +106,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + struct pch_gpio *chip = gpiochip_get_data(gpio); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= BIT(nr); +@@ -114,7 +114,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + reg_val &= ~BIT(nr); + + iowrite32(reg_val, &chip->reg->po); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + } + + static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) +@@ -132,7 +132,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + u32 reg_val; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + reg_val = ioread32(&chip->reg->po); + if (val) +@@ -146,7 +146,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + pm |= BIT(nr); + iowrite32(pm, &chip->reg->pm); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -157,12 +157,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) + u32 pm; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pm = ioread32(&chip->reg->pm); + pm &= BIT(gpio_pins[chip->ioh]) - 1; + pm &= ~BIT(nr); + iowrite32(pm, &chip->reg->pm); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -264,7 +264,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + return 0; + } + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); +@@ -276,7 +276,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + return 0; + } + +@@ -373,7 +373,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, + chip->ioh = id->driver_data; + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); +- spin_lock_init(&chip->spinlock); ++ raw_spin_lock_init(&chip->spinlock); + pch_gpio_setup(chip); + + ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); +@@ -406,9 +406,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pch_gpio_save_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -418,11 +418,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 2775ac515f..f881727b93 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -478,3 +478,5 @@ ata-ahci-make-ahci_ignore_port-handle-empty-mask_port_map.patch mm-hugetlb-fix-swap-entry-corruption-when-clearing-u.patch mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch net-openvswitch-fix-skb-leak-on-flow-key-update-fail.patch +firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch +gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch diff --git a/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch new file mode 100644 index 0000000000..f6e0f4fa94 --- /dev/null +++ b/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -0,0 +1,179 @@ +From a2b29507b1841de58a32ba7a27e922f99b5517c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 10:20:31 +0800 +Subject: gpio: pch: use raw_spinlock_t for the register lock + +From: Junjie Cao + +[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ] + +pch_irq_type() is registered as the irq_chip .irq_set_type callback and +takes chip->spinlock with spin_lock_irqsave(). This callback is reached +from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while +the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. +That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is +an rtmutex-backed sleeping lock, so acquiring it there is invalid. + +This was confirmed on a PREEMPT_RT kernel with lockdep +(PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored +pch_irq_type()'s locking and drove it through the real genirq carrier +irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. +the same __irq_set_trigger() edge that __setup_irq() takes for a +requested IRQ. With the original spin_lock_irqsave() edge lockdep +reported an invalid wait context, immediately followed by: + + BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 + in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod + hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 + rt_spin_lock+0x3a/0x1c0 + repro_irq_set_type+0x64/0xa0 [pch_repro] + __irq_set_trigger+0x69/0x140 + irq_set_irq_type+0x78/0xd0 + +Switching the mirrored lock to raw_spinlock_t made both splats go away. + +Convert the register lock to raw_spinlock_t. The same lock also +serializes the GPIO direction/value callbacks and the suspend/resume +register save/restore, but all of those critical sections only perform +MMIO register accesses (ioread32()/iowrite32()) and +irq_set_handler_locked(); none of them contain sleepable operations. +Keeping this register lock non-sleeping is therefore appropriate for the +irqchip callbacks and does not change the GPIO-side locking contract. + +This is the same class of issue and fix as recently addressed for other +GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t +in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use +raw_spinlock_t in the irq startup path"). + +Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") +Cc: stable@vger.kernel.org +Signed-off-by: Junjie Cao +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com +Signed-off-by: Bartosz Golaszewski +(cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c +index 63f25c72eac2f..75dd65957e4a2 100644 +--- a/drivers/gpio/gpio-pch.c ++++ b/drivers/gpio/gpio-pch.c +@@ -96,7 +96,7 @@ struct pch_gpio { + struct pch_gpio_reg_data pch_gpio_reg; + int irq_base; + enum pch_type_t ioh; +- spinlock_t spinlock; ++ raw_spinlock_t spinlock; + }; + + static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -105,7 +105,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + struct pch_gpio *chip = gpiochip_get_data(gpio); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= BIT(nr); +@@ -113,7 +113,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + reg_val &= ~BIT(nr); + + iowrite32(reg_val, &chip->reg->po); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + } + + static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) +@@ -131,7 +131,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + u32 reg_val; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + reg_val = ioread32(&chip->reg->po); + if (val) +@@ -145,7 +145,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + pm |= BIT(nr); + iowrite32(pm, &chip->reg->pm); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -156,12 +156,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) + u32 pm; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pm = ioread32(&chip->reg->pm); + pm &= BIT(gpio_pins[chip->ioh]) - 1; + pm &= ~BIT(nr); + iowrite32(pm, &chip->reg->pm); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -263,7 +263,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + return 0; + } + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); +@@ -275,7 +275,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + return 0; + } + +@@ -372,7 +372,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, + chip->ioh = id->driver_data; + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); +- spin_lock_init(&chip->spinlock); ++ raw_spin_lock_init(&chip->spinlock); + pch_gpio_setup(chip); + + ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); +@@ -405,9 +405,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pch_gpio_save_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -417,11 +417,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.12/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch b/queue-6.12/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch new file mode 100644 index 0000000000..09cd0b731a --- /dev/null +++ b/queue-6.12/lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch @@ -0,0 +1,83 @@ +From 56ae8d866ff9ed7dc7ef05a843e99c0124c07da3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 05:37:41 +0000 +Subject: lib/alloc_tag: introduce mem_alloc_profiling_permanently_disabled() + +From: Harry Yoo (Oracle) + +commit a37b0066a10aabf3c968b4566706fb866eaf9a85 upstream. + +mem_alloc_profiling_enabled() tells whether memalloc profiling is +currently enabled. However, even when this function returns false, +it can be enabled later. + +However, this is not enough. Some optimizations can be applied only when +memalloc profiling is permanently disabled. For example, to skip the +creation of KMALLOC_NO_OBJ_EXT caches at boot time, mem_profiling must +be set to "never", "0" w/ debugging on, or have been shutdown so that +it can no longer be enabled. + +Introduce mem_alloc_profiling_permanently_disabled() for this purpose. + +Signed-off-by: Harry Yoo (Oracle) +Acked-by: Suren Baghdasaryan +Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-3-47c7bd138de7@kernel.org +Signed-off-by: Vlastimil Babka (SUSE) +[ harry@kernel.org: Move the definition of + mem_alloc_profiling_permanently_disabled() after + mem_profiling_support. + + Unlike 6.18 and later kernels, mem_profiling_support is marked + __init and gets freed after boot. Since the function is needed + only when creating kmalloc caches, mark it __init as well. ] +Signed-off-by: Harry Yoo +Signed-off-by: Sasha Levin +--- + include/linux/alloc_tag.h | 3 +++ + lib/alloc_tag.c | 9 +++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h +index 6073a8f13c413..70025f8e24990 100644 +--- a/include/linux/alloc_tag.h ++++ b/include/linux/alloc_tag.h +@@ -105,6 +105,8 @@ static inline bool mem_alloc_profiling_enabled(void) + &mem_alloc_profiling_key); + } + ++bool __init mem_alloc_profiling_permanently_disabled(void); ++ + static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) + { + struct alloc_tag_counters v = { 0, 0 }; +@@ -198,6 +200,7 @@ static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) + + #define DEFINE_ALLOC_TAG(_alloc_tag) + static inline bool mem_alloc_profiling_enabled(void) { return false; } ++static inline bool mem_alloc_profiling_permanently_disabled(void) { return true; } + static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, + size_t bytes) {} + static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} +diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c +index e76c40bf29d06..a0d11582029ef 100644 +--- a/lib/alloc_tag.c ++++ b/lib/alloc_tag.c +@@ -183,6 +183,15 @@ static bool mem_profiling_support __meminitdata = true; + static bool mem_profiling_support __meminitdata; + #endif + ++/* ++ * Memory allocation profiling is permanently disabled and cannot be enabled. ++ * Must be called after setup_early_mem_profiling(). ++ */ ++bool __init mem_alloc_profiling_permanently_disabled(void) ++{ ++ return !mem_profiling_support; ++} ++ + static int __init setup_early_mem_profiling(char *str) + { + bool enable; +-- +2.53.0 + diff --git a/queue-6.12/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch b/queue-6.12/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch new file mode 100644 index 0000000000..3c3b057318 --- /dev/null +++ b/queue-6.12/mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch @@ -0,0 +1,331 @@ +From ce2954db3148d17ce91142f50673053cbf55b38b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 05:37:42 +0000 +Subject: mm/slab: prevent unbounded recursion in free path with new kmalloc + type + +From: Harry Yoo (Oracle) + +commit d9e6a7623938968e3752b67e37eaff097e559a54 upstream. + +Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from +its own slab") avoided recursive allocation of obj_exts from kmalloc +caches of the same size, by bumping the obj_exts array's allocation +size whenever the array size equals the size of the object being +allocated. + +However, as reported by Danielle Costantino and Shakeel Butt, +even slabs from kmalloc caches of different sizes can form a cycle +by allocating obj_exts arrays from each other [1]: + + What happened: a KMALLOC_NORMAL slab's obj_exts array (used by + allocation profiling / memcg accounting) is itself kmalloc()'d from a + KMALLOC_NORMAL cache, so the "slab holds another slab's obj_exts array" + relation can form cycles. With sizeof(struct slabobj_ext) == 16 and + the host's geometry: + + - kmalloc-512 has 64 objects/slab -> array is 64*16 == 1024 bytes, + served from kmalloc-1k; + - kmalloc-1k has 32 objects/slab -> array is 32*16 == 512 bytes, + served from kmalloc-512. + + A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's + obj_exts array. Discarding one frees the other's array, which empties + and discards that slab, which frees the first's array, and so on: + __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> + __free_slab() recurses along the cycle until the stack is exhausted. + +With memory allocation profiling, this allows unbounded recursion +in the free path and led to a stack overflow on a production host in +the Meta fleet [1]: + + BUG: TASK stack guard page was hit + Oops: stack guard page + RIP: 0010:kfree+0x8/0x5d0 + Call Trace: + __free_slab+0x66/0xc0 + kfree+0x3f0/0x5d0 + ... ( ~125x __free_slab <-> kfree ) ... + + do_syscall_64 + +It is proposed [1] to resolve this issue by always serving the obj_exts +array allocation from kmalloc caches (or large kmalloc) of sizes larger +than the object size. However, as pointed out by Vlastimil Babka [2], +this can waste an excessive amount of memory as slabs from large +kmalloc sizes (e.g. kmalloc-8k) generally need obj_exts arrays much +smaller than the object size. + +Therefore, rather than bumping the size, let us take a different +approach; disallow formation of cycles between kmalloc types when +allocating obj_exts arrays. Currently, all obj_exts arrays are served +from normal kmalloc caches. Cycles cannot be created if obj_exts arrays +of normal kmalloc caches are served from a special kmalloc type that can +never have obj_exts arrays. + +To achieve this, create a new kmalloc type called KMALLOC_NO_OBJ_EXT. +KMALLOC_NO_OBJ_EXT caches are created with SLAB_NO_OBJ_EXT flag when +either 1) memory allocation profiling is not permanently disabled, +or 2) kmalloc types with a priority higher than KMALLOC_CGROUP are +aliased with KMALLOC_NORMAL. + +Sheaf bootstrapping for KMALLOC_NO_OBJ_EXT caches now must be deferred +because allocation of a barn can trigger obj_exts array allocation of +normal kmalloc caches when the KMALLOC_NO_OBJ_EXT cache for that size +is not ready yet. For simplicity, perform bootstrapping of sheaves for +all kmalloc caches later. + +Introduce a new slab alloc flag, SLAB_ALLOC_NO_OBJ_EXT, to prevent +allocation of obj_exts arrays, and let kmalloc_slab() override the type +to KMALLOC_NO_OBJ_EXT when specified. Note that kmalloc_type() remains +unchanged because kmalloc_flags() bypasses the kmalloc fastpath. + +Do not pass SLAB_ALLOC_NO_RECURSE to kmalloc_flags() in +alloc_slab_obj_exts() and instead use SLAB_ALLOC_NO_OBJ_EXT only when +the objects are allocated from normal kmalloc caches. While this +prevents unbounded recursive allocation of obj_exts, it allows +KMALLOC_NO_OBJ_EXT caches to have sheaves. + +Since sheaf allocations specify SLAB_ALLOC_NO_RECURSE that prevents +allocation of both sheaves and obj_exts arrays, the recursion depth +is bounded. + +obj_exts arrays for non-kmalloc-normal caches can now have a valid tag. +Do not call mark_obj_codetag_empty() when freeing an obj_exts array to +avoid false warnings. KMALLOC_NO_OBJ_EXT don't need this as they never +allocate those arrays. + +Reported-by: Danielle Costantino +Reported-by: Shakeel Butt +Closes: https://lore.kernel.org/linux-mm/20260625230029.703750-1-shakeel.butt@linux.dev [1] +Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allocation and free paths") +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/linux-mm/c5c4208d-a6f0-413e-bad9-49be12f12d55@kernel.org [2] +Signed-off-by: Harry Yoo (Oracle) +Reviewed-by: Suren Baghdasaryan +Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org +Signed-off-by: Vlastimil Babka (SUSE) +[harry@kernel.org: Backport notes: + - Fix a minor conflict due to missing partitioned + kmalloc caches in 6.12. + + - Use __GFP_NO_OBJ_EXT instead of SLAB_ALLOC_NO_OBJ_EXT + since slab's internal alloc_flags do not exist in 6.12. + + - Deferring sheaf bootstrapping for kmalloc caches is not applied + as 6.12 doesn't have sheaves. + + - Adjust the comment for SLAB_NO_OBJ_EXT, like in the commit + 982e31382d9a ("mm/slab: decouple SLAB_NO_SHEAVES from + SLAB_NO_OBJ_EXT"). The rest of that commit is a no-op in 6.12 as + sheaves are not supported. Thus only adjust the comment. + + - Resolve conflicts due to missing kmalloc_nolock() support in 6.12. + + - Mark need_kmalloc_no_objext() __always_inline to make sure + the compiler does not generate a out-of-line function that could + access mem_profiling_support (which is marked __init) ] +Signed-off-by: Harry Yoo +Signed-off-by: Sasha Levin +--- + include/linux/slab.h | 8 +++++++- + mm/slab.h | 28 ++++++++++++++++++++++++++-- + mm/slab_common.c | 13 +++++++++++++ + mm/slub.c | 34 +++++++++++++++++++++++----------- + 4 files changed, 69 insertions(+), 14 deletions(-) + +diff --git a/include/linux/slab.h b/include/linux/slab.h +index 773843a71960d..28eba6cbb7670 100644 +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -206,7 +206,7 @@ enum _slab_flag_bits { + #endif + #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ + +-/* Slab created using create_boot_cache */ ++/* Slab caches without obj_exts array */ + #ifdef CONFIG_SLAB_OBJ_EXT + #define SLAB_NO_OBJ_EXT __SLAB_FLAG_BIT(_SLAB_NO_OBJ_EXT) + #else +@@ -578,6 +578,9 @@ enum kmalloc_cache_type { + #endif + #ifndef CONFIG_MEMCG + KMALLOC_CGROUP = KMALLOC_NORMAL, ++#endif ++#ifndef CONFIG_SLAB_OBJ_EXT ++ KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, + #endif + KMALLOC_RANDOM_START = KMALLOC_NORMAL, + KMALLOC_RANDOM_END = KMALLOC_RANDOM_START + RANDOM_KMALLOC_CACHES_NR, +@@ -591,6 +594,9 @@ enum kmalloc_cache_type { + #endif + #ifdef CONFIG_MEMCG + KMALLOC_CGROUP, ++#endif ++#ifdef CONFIG_SLAB_OBJ_EXT ++ KMALLOC_NO_OBJ_EXT, + #endif + NR_KMALLOC_TYPES + }; +diff --git a/mm/slab.h b/mm/slab.h +index b65d2462b3fdb..34a3d65b4ef5f 100644 +--- a/mm/slab.h ++++ b/mm/slab.h +@@ -413,9 +413,13 @@ static inline struct kmem_cache * + kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, unsigned long caller) + { + unsigned int index; ++ enum kmalloc_cache_type type = kmalloc_type(flags, caller); ++ ++ if (flags & __GFP_NO_OBJ_EXT) ++ type = KMALLOC_NO_OBJ_EXT; + + if (!b) +- b = &kmalloc_caches[kmalloc_type(flags, caller)]; ++ b = &kmalloc_caches[type]; + if (size <= 192) + index = kmalloc_size_index[size_index_elem(size)]; + else +@@ -454,7 +458,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) + { + if (!is_kmalloc_cache(s)) + return false; +- return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); ++ ++ return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); + } + + /* Legal flag mask for kmem_cache_create(), for various configurations */ +@@ -557,6 +562,25 @@ bool slab_in_kunit_test(void); + static inline bool slab_in_kunit_test(void) { return false; } + #endif + ++/* ++ * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. ++ * ++ * Memory allocation profiling requires obj_exts for all caches. ++ * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types ++ * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. ++ */ ++static __always_inline inline bool need_kmalloc_no_objext(void) ++{ ++ if (!mem_alloc_profiling_permanently_disabled()) ++ return true; ++ ++ if (!mem_cgroup_kmem_disabled() && ++ (KMALLOC_NORMAL == KMALLOC_RECLAIM)) ++ return true; ++ ++ return false; ++} ++ + #ifdef CONFIG_SLAB_OBJ_EXT + + /* +diff --git a/mm/slab_common.c b/mm/slab_common.c +index 477fa471da185..4cc96c3ea18e0 100644 +--- a/mm/slab_common.c ++++ b/mm/slab_common.c +@@ -793,6 +793,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); + #define KMALLOC_RANDOM_NAME(N, sz) + #endif + ++#ifdef CONFIG_SLAB_OBJ_EXT ++#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, ++#else ++#define KMALLOC_NO_OBJ_EXT_NAME(sz) ++#endif ++ + #define INIT_KMALLOC_INFO(__size, __short_size) \ + { \ + .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ +@@ -800,6 +806,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); + KMALLOC_CGROUP_NAME(__short_size) \ + KMALLOC_DMA_NAME(__short_size) \ + KMALLOC_RANDOM_NAME(RANDOM_KMALLOC_CACHES_NR, __short_size) \ ++ KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ + .size = __size, \ + } + +@@ -907,6 +914,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) + return; + } + flags |= SLAB_ACCOUNT; ++ } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { ++ if (!need_kmalloc_no_objext()) { ++ kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; ++ return; ++ } ++ flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; + } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { + flags |= SLAB_CACHE_DMA; + } +diff --git a/mm/slub.c b/mm/slub.c +index cf678f4617258..b61225c3503bf 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -1997,8 +1997,13 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + struct slabobj_ext *vec; + + gfp &= ~OBJCGS_CLEAR_MASK; +- /* Prevent recursive extension vector allocation */ +- gfp |= __GFP_NO_OBJ_EXT; ++ /* ++ * In most cases, obj_exts arrays are allocated from normal kmalloc. ++ * However, normal kmalloc caches must allocate them from ++ * KMALLOC_NO_OBJ_EXT caches to prevent recursion. ++ */ ++ if (is_kmalloc_normal(s)) ++ gfp |= __GFP_NO_OBJ_EXT; + vec = kcalloc_node(objects, sizeof(struct slabobj_ext), gfp, + slab_nid(slab)); + if (!vec) { +@@ -2014,6 +2019,22 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + return -ENOMEM; + } + ++ if (IS_ENABLED(CONFIG_DEBUG_VM)) { ++ struct kmem_cache *exts_cache; ++ struct slab *exts_slab; ++ ++ exts_slab = virt_to_slab(vec); ++ if (exts_slab) { ++ /* ++ * The vector must be allocated from either normal or ++ * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. ++ */ ++ exts_cache = exts_slab->slab_cache; ++ WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && ++ !(exts_cache->flags & SLAB_NO_OBJ_EXT)); ++ } ++ } ++ + new_exts = (unsigned long)vec; + #ifdef CONFIG_MEMCG + new_exts |= MEMCG_DATA_OBJEXTS; +@@ -2034,7 +2055,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, + * assign slabobj_exts in parallel. In this case the existing + * objcg vector should be reused. + */ +- mark_objexts_empty(vec); + kfree(vec); + return 0; + } else if (cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { +@@ -2061,14 +2081,6 @@ static inline void free_slab_obj_exts(struct slab *slab) + return; + } + +- /* +- * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its +- * corresponding extension will be NULL. alloc_tag_sub() will throw a +- * warning if slab has extensions but the extension of an object is +- * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that +- * the extension for obj_exts is expected to be NULL. +- */ +- mark_objexts_empty(obj_exts); + kfree(obj_exts); + slab->obj_exts = 0; + } +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 33c2465854..80d4091bb4 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -286,3 +286,6 @@ drm-xe-rtp-ensure-locking-ref-counting-for-oa-whitel.patch mm-hugetlb-fix-swap-entry-corruption-when-clearing-u.patch fs-proc-task_mmu-fix-pagemap_scan-written-state-for-.patch mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch +lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch +mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch +gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch diff --git a/queue-6.6/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch b/queue-6.6/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch new file mode 100644 index 0000000000..ac499ce25b --- /dev/null +++ b/queue-6.6/firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch @@ -0,0 +1,71 @@ +From b07c2bbdfb55bb7cabab647ee55751f5a5456dea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 03:24:58 -0700 +Subject: firmware: stratix10-svc: fix memory leaks and list corruption bugs + +From: Tze Yee Ng + +[ Upstream commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c ] + +Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error +path. Switch pmem allocation from devm_kzalloc() to kzalloc() with +explicit kfree() in the free path to match its list-managed lifetime. +Remove the erroneous list_del(&svc_data_mem) which corrupted the list head +on failed lookups. + +Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") +Cc: stable@vger.kernel.org#5.0+ +Signed-off-by: Tze Yee Ng +Signed-off-by: Dinh Nguyen +(cherry picked from commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c) +Signed-off-by: Sasha Levin +--- + drivers/firmware/stratix10-svc.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c +index 2f263d1a6bd12..f723869c5b2db 100644 +--- a/drivers/firmware/stratix10-svc.c ++++ b/drivers/firmware/stratix10-svc.c +@@ -1083,14 +1083,16 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, + struct gen_pool *genpool = chan->ctrl->genpool; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + +- pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); ++ pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); + if (!pmem) + return ERR_PTR(-ENOMEM); + + guard(mutex)(&svc_mem_lock); + va = gen_pool_alloc(genpool, s); +- if (!va) ++ if (!va) { ++ kfree(pmem); + return ERR_PTR(-ENOMEM); ++ } + + memset((void *)va, 0, s); + pa = gen_pool_virt_to_phys(genpool, va); +@@ -1116,6 +1118,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory); + void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + { + struct stratix10_svc_data_mem *pmem; ++ + guard(mutex)(&svc_mem_lock); + + list_for_each_entry(pmem, &svc_data_mem, node) +@@ -1124,10 +1127,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) + (unsigned long)kaddr, pmem->size); + pmem->vaddr = NULL; + list_del(&pmem->node); ++ kfree(pmem); + return; + } +- +- list_del(&svc_data_mem); + } + EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); + +-- +2.53.0 + diff --git a/queue-6.6/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-6.6/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch new file mode 100644 index 0000000000..c583f2ed45 --- /dev/null +++ b/queue-6.6/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -0,0 +1,179 @@ +From d05dcacf4c2ca535b5782c51058f3e91dec30a7d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Aug 2026 10:19:34 +0800 +Subject: gpio: pch: use raw_spinlock_t for the register lock + +From: Junjie Cao + +[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ] + +pch_irq_type() is registered as the irq_chip .irq_set_type callback and +takes chip->spinlock with spin_lock_irqsave(). This callback is reached +from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while +the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. +That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is +an rtmutex-backed sleeping lock, so acquiring it there is invalid. + +This was confirmed on a PREEMPT_RT kernel with lockdep +(PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored +pch_irq_type()'s locking and drove it through the real genirq carrier +irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. +the same __irq_set_trigger() edge that __setup_irq() takes for a +requested IRQ. With the original spin_lock_irqsave() edge lockdep +reported an invalid wait context, immediately followed by: + + BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 + in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod + hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 + rt_spin_lock+0x3a/0x1c0 + repro_irq_set_type+0x64/0xa0 [pch_repro] + __irq_set_trigger+0x69/0x140 + irq_set_irq_type+0x78/0xd0 + +Switching the mirrored lock to raw_spinlock_t made both splats go away. + +Convert the register lock to raw_spinlock_t. The same lock also +serializes the GPIO direction/value callbacks and the suspend/resume +register save/restore, but all of those critical sections only perform +MMIO register accesses (ioread32()/iowrite32()) and +irq_set_handler_locked(); none of them contain sleepable operations. +Keeping this register lock non-sleeping is therefore appropriate for the +irqchip callbacks and does not change the GPIO-side locking contract. + +This is the same class of issue and fix as recently addressed for other +GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t +in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use +raw_spinlock_t in the irq startup path"). + +Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") +Cc: stable@vger.kernel.org +Signed-off-by: Junjie Cao +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com +Signed-off-by: Bartosz Golaszewski +(cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c +index ee37ecb615cb1..77e84e0e6c1ba 100644 +--- a/drivers/gpio/gpio-pch.c ++++ b/drivers/gpio/gpio-pch.c +@@ -97,7 +97,7 @@ struct pch_gpio { + struct pch_gpio_reg_data pch_gpio_reg; + int irq_base; + enum pch_type_t ioh; +- spinlock_t spinlock; ++ raw_spinlock_t spinlock; + }; + + static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -106,7 +106,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + struct pch_gpio *chip = gpiochip_get_data(gpio); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + reg_val = ioread32(&chip->reg->po); + if (val) + reg_val |= BIT(nr); +@@ -114,7 +114,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) + reg_val &= ~BIT(nr); + + iowrite32(reg_val, &chip->reg->po); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + } + + static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) +@@ -132,7 +132,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + u32 reg_val; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + reg_val = ioread32(&chip->reg->po); + if (val) +@@ -146,7 +146,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, + pm |= BIT(nr); + iowrite32(pm, &chip->reg->pm); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -157,12 +157,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) + u32 pm; + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pm = ioread32(&chip->reg->pm); + pm &= BIT(gpio_pins[chip->ioh]) - 1; + pm &= ~BIT(nr); + iowrite32(pm, &chip->reg->pm); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -264,7 +264,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + return 0; + } + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); +@@ -276,7 +276,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + return 0; + } + +@@ -373,7 +373,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, + chip->ioh = id->driver_data; + chip->reg = chip->base; + pci_set_drvdata(pdev, chip); +- spin_lock_init(&chip->spinlock); ++ raw_spin_lock_init(&chip->spinlock); + pch_gpio_setup(chip); + + ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); +@@ -406,9 +406,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + pch_gpio_save_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +@@ -418,11 +418,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) + struct pch_gpio *chip = dev_get_drvdata(dev); + unsigned long flags; + +- spin_lock_irqsave(&chip->spinlock, flags); ++ raw_spin_lock_irqsave(&chip->spinlock, flags); + iowrite32(0x01, &chip->reg->reset); + iowrite32(0x00, &chip->reg->reset); + pch_gpio_restore_reg_conf(chip); +- spin_unlock_irqrestore(&chip->spinlock, flags); ++ raw_spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index b286c4f48e..88f5d8f2a4 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -208,3 +208,5 @@ ata-ahci-make-ahci_ignore_port-handle-empty-mask_port_map.patch iommu-sva-move-x86-disable-check-before-allocation.patch mm-hugetlb-fix-swap-entry-corruption-when-clearing-u.patch mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch +firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch +gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch -- 2.47.3