From: Greg Kroah-Hartman Date: Thu, 15 Jan 2026 11:51:39 +0000 (+0100) Subject: 6.1-stable patches X-Git-Tag: v6.6.121~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=83e14830f2571f08d9ae0a3f84e16322537a12af;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches added patches: alsa-ac97-fix-a-double-free-in-snd_ac97_controller_register.patch alsa-ac97bus-use-guard-for-mutex-locks.patch ksm-use-range-walk-function-to-jump-over-holes-in-scan_get_next_rmap_item.patch mm-pagewalk-add-walk_page_range_vma.patch nfs-trace-show-timedout-instead-of-0x6e.patch nfs_common-factor-out-nfs_errtbl-and-nfs_stat_to_errno.patch nfsd-provide-locking-for-v4_end_grace.patch nfsd-remove-nfserr_eagain.patch pinctrl-qcom-lpass-lpi-mark-the-gpio-controller-as-sleeping.patch --- diff --git a/queue-6.1/alsa-ac97-fix-a-double-free-in-snd_ac97_controller_register.patch b/queue-6.1/alsa-ac97-fix-a-double-free-in-snd_ac97_controller_register.patch new file mode 100644 index 0000000000..ca8dbf23db --- /dev/null +++ b/queue-6.1/alsa-ac97-fix-a-double-free-in-snd_ac97_controller_register.patch @@ -0,0 +1,67 @@ +From stable+bounces-208187-greg=kroah.com@vger.kernel.org Mon Jan 12 18:35:39 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 12:31:09 -0500 +Subject: ALSA: ac97: fix a double free in snd_ac97_controller_register() +To: stable@vger.kernel.org +Cc: Haoxiang Li , Takashi Iwai , Sasha Levin +Message-ID: <20260112173109.826518-2-sashal@kernel.org> + +From: Haoxiang Li + +[ Upstream commit 830988b6cf197e6dcffdfe2008c5738e6c6c3c0f ] + +If ac97_add_adapter() fails, put_device() is the correct way to drop +the device reference. kfree() is not required. +Add kfree() if idr_alloc() fails and in ac97_adapter_release() to do +the cleanup. + +Found by code review. + +Fixes: 74426fbff66e ("ALSA: ac97: add an ac97 bus") +Cc: stable@vger.kernel.org +Signed-off-by: Haoxiang Li +Link: https://patch.msgid.link/20251219162845.657525-1-lihaoxiang@isrc.iscas.ac.cn +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/ac97/bus.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/sound/ac97/bus.c ++++ b/sound/ac97/bus.c +@@ -299,6 +299,7 @@ static void ac97_adapter_release(struct + idr_remove(&ac97_adapter_idr, ac97_ctrl->nr); + dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n", + dev_name(ac97_ctrl->parent)); ++ kfree(ac97_ctrl); + } + + static const struct device_type ac97_adapter_type = { +@@ -320,7 +321,9 @@ static int ac97_add_adapter(struct ac97_ + ret = device_register(&ac97_ctrl->adap); + if (ret) + put_device(&ac97_ctrl->adap); +- } ++ } else ++ kfree(ac97_ctrl); ++ + if (!ret) { + list_add(&ac97_ctrl->controllers, &ac97_controllers); + dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n", +@@ -361,14 +364,11 @@ struct ac97_controller *snd_ac97_control + ret = ac97_add_adapter(ac97_ctrl); + + if (ret) +- goto err; ++ return ERR_PTR(ret); + ac97_bus_reset(ac97_ctrl); + ac97_bus_scan(ac97_ctrl); + + return ac97_ctrl; +-err: +- kfree(ac97_ctrl); +- return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(snd_ac97_controller_register); + diff --git a/queue-6.1/alsa-ac97bus-use-guard-for-mutex-locks.patch b/queue-6.1/alsa-ac97bus-use-guard-for-mutex-locks.patch new file mode 100644 index 0000000000..d61c918dd8 --- /dev/null +++ b/queue-6.1/alsa-ac97bus-use-guard-for-mutex-locks.patch @@ -0,0 +1,92 @@ +From stable+bounces-208186-greg=kroah.com@vger.kernel.org Mon Jan 12 18:35:37 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 12:31:08 -0500 +Subject: ALSA: ac97bus: Use guard() for mutex locks +To: stable@vger.kernel.org +Cc: Takashi Iwai , Sasha Levin +Message-ID: <20260112173109.826518-1-sashal@kernel.org> + +From: Takashi Iwai + +[ Upstream commit c07824a14d99c10edd4ec4c389d219af336ecf20 ] + +Replace the manual mutex lock/unlock pairs with guard() for code +simplification. + +Only code refactoring, and no behavior change. + +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20250829151335.7342-18-tiwai@suse.de +Stable-dep-of: 830988b6cf19 ("ALSA: ac97: fix a double free in snd_ac97_controller_register()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/ac97/bus.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +--- a/sound/ac97/bus.c ++++ b/sound/ac97/bus.c +@@ -242,10 +242,9 @@ static ssize_t cold_reset_store(struct d + { + struct ac97_controller *ac97_ctrl; + +- mutex_lock(&ac97_controllers_mutex); ++ guard(mutex)(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->reset(ac97_ctrl); +- mutex_unlock(&ac97_controllers_mutex); + return len; + } + static DEVICE_ATTR_WO(cold_reset); +@@ -259,10 +258,9 @@ static ssize_t warm_reset_store(struct d + if (!dev) + return -ENODEV; + +- mutex_lock(&ac97_controllers_mutex); ++ guard(mutex)(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->warm_reset(ac97_ctrl); +- mutex_unlock(&ac97_controllers_mutex); + return len; + } + static DEVICE_ATTR_WO(warm_reset); +@@ -285,10 +283,10 @@ static const struct attribute_group *ac9 + + static void ac97_del_adapter(struct ac97_controller *ac97_ctrl) + { +- mutex_lock(&ac97_controllers_mutex); +- ac97_ctrl_codecs_unregister(ac97_ctrl); +- list_del(&ac97_ctrl->controllers); +- mutex_unlock(&ac97_controllers_mutex); ++ scoped_guard(mutex, &ac97_controllers_mutex) { ++ ac97_ctrl_codecs_unregister(ac97_ctrl); ++ list_del(&ac97_ctrl->controllers); ++ } + + device_unregister(&ac97_ctrl->adap); + } +@@ -312,7 +310,7 @@ static int ac97_add_adapter(struct ac97_ + { + int ret; + +- mutex_lock(&ac97_controllers_mutex); ++ guard(mutex)(&ac97_controllers_mutex); + ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL); + ac97_ctrl->nr = ret; + if (ret >= 0) { +@@ -323,13 +321,11 @@ static int ac97_add_adapter(struct ac97_ + if (ret) + put_device(&ac97_ctrl->adap); + } +- if (!ret) ++ if (!ret) { + list_add(&ac97_ctrl->controllers, &ac97_controllers); +- mutex_unlock(&ac97_controllers_mutex); +- +- if (!ret) + dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n", + dev_name(ac97_ctrl->parent)); ++ } + return ret; + } + diff --git a/queue-6.1/ksm-use-range-walk-function-to-jump-over-holes-in-scan_get_next_rmap_item.patch b/queue-6.1/ksm-use-range-walk-function-to-jump-over-holes-in-scan_get_next_rmap_item.patch new file mode 100644 index 0000000000..5dc8925b1b --- /dev/null +++ b/queue-6.1/ksm-use-range-walk-function-to-jump-over-holes-in-scan_get_next_rmap_item.patch @@ -0,0 +1,241 @@ +From stable+bounces-208267-greg=kroah.com@vger.kernel.org Tue Jan 13 14:23:13 2026 +From: Pedro Demarchi Gomes +Date: Tue, 13 Jan 2026 10:01:56 -0300 +Subject: ksm: use range-walk function to jump over holes in scan_get_next_rmap_item +To: stable@vger.kernel.org +Cc: Zhi.Yang@windriver.com, Pedro Demarchi Gomes , David Hildenbrand , craftfever , Chengming Zhou , xu xin , Andrew Morton +Message-ID: <20260113130156.220078-2-pedrodemargomes@gmail.com> + +From: Pedro Demarchi Gomes + +[ Upstream commit f5548c318d6520d4fa3c5ed6003eeb710763cbc5 ] + +Currently, scan_get_next_rmap_item() walks every page address in a VMA to +locate mergeable pages. This becomes highly inefficient when scanning +large virtual memory areas that contain mostly unmapped regions, causing +ksmd to use large amount of cpu without deduplicating much pages. + +This patch replaces the per-address lookup with a range walk using +walk_page_range(). The range walker allows KSM to skip over entire +unmapped holes in a VMA, avoiding unnecessary lookups. This problem was +previously discussed in [1]. + +Consider the following test program which creates a 32 TiB mapping in the +virtual address space but only populates a single page: + +/* 32 TiB */ +const size_t size = 32ul * 1024 * 1024 * 1024 * 1024; + +int main() { + char *area = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); + + if (area == MAP_FAILED) { + perror("mmap() failed\n"); + return -1; + } + + /* Populate a single page such that we get an anon_vma. */ + *area = 0; + + /* Enable KSM. */ + madvise(area, size, MADV_MERGEABLE); + pause(); + return 0; +} + +$ ./ksm-sparse & +$ echo 1 > /sys/kernel/mm/ksm/run + +Without this patch ksmd uses 100% of the cpu for a long time (more then 1 +hour in my test machine) scanning all the 32 TiB virtual address space +that contain only one mapped page. This makes ksmd essentially deadlocked +not able to deduplicate anything of value. With this patch ksmd walks +only the one mapped page and skips the rest of the 32 TiB virtual address +space, making the scan fast using little cpu. + +Link: https://lkml.kernel.org/r/20251023035841.41406-1-pedrodemargomes@gmail.com +Link: https://lkml.kernel.org/r/20251022153059.22763-1-pedrodemargomes@gmail.com +Link: https://lore.kernel.org/linux-mm/423de7a3-1c62-4e72-8e79-19a6413e420c@redhat.com/ [1] +Fixes: 31dbd01f3143 ("ksm: Kernel SamePage Merging") +Signed-off-by: Pedro Demarchi Gomes +Co-developed-by: David Hildenbrand +Signed-off-by: David Hildenbrand +Reported-by: craftfever +Closes: https://lkml.kernel.org/r/020cf8de6e773bb78ba7614ef250129f11a63781@murena.io +Suggested-by: David Hildenbrand +Acked-by: David Hildenbrand +Cc: Chengming Zhou +Cc: xu xin +Cc: +Signed-off-by: Andrew Morton +[ replace pmdp_get_lockless with pmd_read_atomic and pmdp_get with + READ_ONCE(*pmdp) ] +Signed-off-by: Pedro Demarchi Gomes +Signed-off-by: Greg Kroah-Hartman +--- + mm/ksm.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 113 insertions(+), 13 deletions(-) + +--- a/mm/ksm.c ++++ b/mm/ksm.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + #include + #include "internal.h" +@@ -2223,6 +2224,94 @@ static struct ksm_rmap_item *get_next_rm + return rmap_item; + } + ++struct ksm_next_page_arg { ++ struct folio *folio; ++ struct page *page; ++ unsigned long addr; ++}; ++ ++static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned long end, ++ struct mm_walk *walk) ++{ ++ struct ksm_next_page_arg *private = walk->private; ++ struct vm_area_struct *vma = walk->vma; ++ pte_t *start_ptep = NULL, *ptep, pte; ++ struct mm_struct *mm = walk->mm; ++ struct folio *folio; ++ struct page *page; ++ spinlock_t *ptl; ++ pmd_t pmd; ++ ++ if (ksm_test_exit(mm)) ++ return 0; ++ ++ cond_resched(); ++ ++ pmd = pmd_read_atomic(pmdp); ++ if (!pmd_present(pmd)) ++ return 0; ++ ++ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && pmd_leaf(pmd)) { ++ ptl = pmd_lock(mm, pmdp); ++ pmd = READ_ONCE(*pmdp); ++ ++ if (!pmd_present(pmd)) { ++ goto not_found_unlock; ++ } else if (pmd_leaf(pmd)) { ++ page = vm_normal_page_pmd(vma, addr, pmd); ++ if (!page) ++ goto not_found_unlock; ++ folio = page_folio(page); ++ ++ if (folio_is_zone_device(folio) || !folio_test_anon(folio)) ++ goto not_found_unlock; ++ ++ page += ((addr & (PMD_SIZE - 1)) >> PAGE_SHIFT); ++ goto found_unlock; ++ } ++ spin_unlock(ptl); ++ } ++ ++ start_ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl); ++ if (!start_ptep) ++ return 0; ++ ++ for (ptep = start_ptep; addr < end; ptep++, addr += PAGE_SIZE) { ++ pte = ptep_get(ptep); ++ ++ if (!pte_present(pte)) ++ continue; ++ ++ page = vm_normal_page(vma, addr, pte); ++ if (!page) ++ continue; ++ folio = page_folio(page); ++ ++ if (folio_is_zone_device(folio) || !folio_test_anon(folio)) ++ continue; ++ goto found_unlock; ++ } ++ ++not_found_unlock: ++ spin_unlock(ptl); ++ if (start_ptep) ++ pte_unmap(start_ptep); ++ return 0; ++found_unlock: ++ folio_get(folio); ++ spin_unlock(ptl); ++ if (start_ptep) ++ pte_unmap(start_ptep); ++ private->page = page; ++ private->folio = folio; ++ private->addr = addr; ++ return 1; ++} ++ ++static struct mm_walk_ops ksm_next_page_ops = { ++ .pmd_entry = ksm_next_page_pmd_entry, ++}; ++ + static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page) + { + struct mm_struct *mm; +@@ -2307,32 +2396,43 @@ next_mm: + ksm_scan.address = vma->vm_end; + + while (ksm_scan.address < vma->vm_end) { ++ struct ksm_next_page_arg ksm_next_page_arg; ++ struct page *tmp_page = NULL; ++ struct folio *folio; ++ + if (ksm_test_exit(mm)) + break; +- *page = follow_page(vma, ksm_scan.address, FOLL_GET); +- if (IS_ERR_OR_NULL(*page)) { +- ksm_scan.address += PAGE_SIZE; +- cond_resched(); +- continue; ++ ++ int found; ++ ++ found = walk_page_range_vma(vma, ksm_scan.address, ++ vma->vm_end, ++ &ksm_next_page_ops, ++ &ksm_next_page_arg); ++ ++ if (found > 0) { ++ folio = ksm_next_page_arg.folio; ++ tmp_page = ksm_next_page_arg.page; ++ ksm_scan.address = ksm_next_page_arg.addr; ++ } else { ++ VM_WARN_ON_ONCE(found < 0); ++ ksm_scan.address = vma->vm_end - PAGE_SIZE; + } +- if (is_zone_device_page(*page)) +- goto next_page; +- if (PageAnon(*page)) { +- flush_anon_page(vma, *page, ksm_scan.address); +- flush_dcache_page(*page); ++ if (tmp_page) { ++ flush_anon_page(vma, tmp_page, ksm_scan.address); ++ flush_dcache_page(tmp_page); + rmap_item = get_next_rmap_item(mm_slot, + ksm_scan.rmap_list, ksm_scan.address); + if (rmap_item) { + ksm_scan.rmap_list = + &rmap_item->rmap_list; + ksm_scan.address += PAGE_SIZE; ++ *page = tmp_page; + } else +- put_page(*page); ++ folio_put(folio); + mmap_read_unlock(mm); + return rmap_item; + } +-next_page: +- put_page(*page); + ksm_scan.address += PAGE_SIZE; + cond_resched(); + } diff --git a/queue-6.1/mm-pagewalk-add-walk_page_range_vma.patch b/queue-6.1/mm-pagewalk-add-walk_page_range_vma.patch new file mode 100644 index 0000000000..5658b7cba4 --- /dev/null +++ b/queue-6.1/mm-pagewalk-add-walk_page_range_vma.patch @@ -0,0 +1,77 @@ +From stable+bounces-208266-greg=kroah.com@vger.kernel.org Tue Jan 13 14:22:18 2026 +From: Pedro Demarchi Gomes +Date: Tue, 13 Jan 2026 10:01:55 -0300 +Subject: mm/pagewalk: add walk_page_range_vma() +To: stable@vger.kernel.org +Cc: Zhi.Yang@windriver.com, David Hildenbrand , Andrea Arcangeli , Hugh Dickins , Jason Gunthorpe , John Hubbard , Matthew Wilcox , Peter Xu , Shuah Khan , Vlastimil Babka , Andrew Morton , Pedro Demarchi Gomes +Message-ID: <20260113130156.220078-1-pedrodemargomes@gmail.com> + +From: David Hildenbrand + +[ Upstream commit e07cda5f232fac4de0925d8a4c92e51e41fa2f6e ] + +Let's add walk_page_range_vma(), which is similar to walk_page_vma(), +however, is only interested in a subset of the VMA range. + +To be used in KSM code to stop using follow_page() next. + +Link: https://lkml.kernel.org/r/20221021101141.84170-8-david@redhat.com +Signed-off-by: David Hildenbrand +Cc: Andrea Arcangeli +Cc: Hugh Dickins +Cc: Jason Gunthorpe +Cc: John Hubbard +Cc: Matthew Wilcox (Oracle) +Cc: Peter Xu +Cc: Shuah Khan +Cc: Vlastimil Babka +Signed-off-by: Andrew Morton +Stable-dep-of: f5548c318d6 ("ksm: use range-walk function to jump over holes in scan_get_next_rmap_item") +Signed-off-by: Pedro Demarchi Gomes +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/pagewalk.h | 3 +++ + mm/pagewalk.c | 20 ++++++++++++++++++++ + 2 files changed, 23 insertions(+) + +--- a/include/linux/pagewalk.h ++++ b/include/linux/pagewalk.h +@@ -99,6 +99,9 @@ int walk_page_range_novma(struct mm_stru + unsigned long end, const struct mm_walk_ops *ops, + pgd_t *pgd, + void *private); ++int walk_page_range_vma(struct vm_area_struct *vma, unsigned long start, ++ unsigned long end, const struct mm_walk_ops *ops, ++ void *private); + int walk_page_vma(struct vm_area_struct *vma, const struct mm_walk_ops *ops, + void *private); + int walk_page_mapping(struct address_space *mapping, pgoff_t first_index, +--- a/mm/pagewalk.c ++++ b/mm/pagewalk.c +@@ -517,6 +517,26 @@ int walk_page_range_novma(struct mm_stru + return walk_pgd_range(start, end, &walk); + } + ++int walk_page_range_vma(struct vm_area_struct *vma, unsigned long start, ++ unsigned long end, const struct mm_walk_ops *ops, ++ void *private) ++{ ++ struct mm_walk walk = { ++ .ops = ops, ++ .mm = vma->vm_mm, ++ .vma = vma, ++ .private = private, ++ }; ++ ++ if (start >= end || !walk.mm) ++ return -EINVAL; ++ if (start < vma->vm_start || end > vma->vm_end) ++ return -EINVAL; ++ ++ mmap_assert_locked(walk.mm); ++ return __walk_page_range(start, end, &walk); ++} ++ + int walk_page_vma(struct vm_area_struct *vma, const struct mm_walk_ops *ops, + void *private) + { diff --git a/queue-6.1/nfs-trace-show-timedout-instead-of-0x6e.patch b/queue-6.1/nfs-trace-show-timedout-instead-of-0x6e.patch new file mode 100644 index 0000000000..79cf2f4ec7 --- /dev/null +++ b/queue-6.1/nfs-trace-show-timedout-instead-of-0x6e.patch @@ -0,0 +1,42 @@ +From stable+bounces-208127-greg=kroah.com@vger.kernel.org Mon Jan 12 15:45:35 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 09:39:08 -0500 +Subject: NFS: trace: show TIMEDOUT instead of 0x6e +To: stable@vger.kernel.org +Cc: Chen Hanxiao , Jeff Layton , Chuck Lever , Sasha Levin +Message-ID: <20260112143910.714632-1-sashal@kernel.org> + +From: Chen Hanxiao + +[ Upstream commit cef48236dfe55fa266d505e8a497963a7bc5ef2a ] + +__nfs_revalidate_inode may return ETIMEDOUT. + +print symbol of ETIMEDOUT in nfs trace: + +before: +cat-5191 [005] 119.331127: nfs_revalidate_inode_exit: error=-110 (0x6e) + +after: +cat-1738 [004] 44.365509: nfs_revalidate_inode_exit: error=-110 (TIMEDOUT) + +Signed-off-by: Chen Hanxiao +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Stable-dep-of: c6c209ceb87f ("NFSD: Remove NFSERR_EAGAIN") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/trace/misc/nfs.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/trace/misc/nfs.h ++++ b/include/trace/misc/nfs.h +@@ -52,6 +52,7 @@ TRACE_DEFINE_ENUM(NFSERR_JUKEBOX); + { NFSERR_IO, "IO" }, \ + { NFSERR_NXIO, "NXIO" }, \ + { ECHILD, "CHILD" }, \ ++ { ETIMEDOUT, "TIMEDOUT" }, \ + { NFSERR_EAGAIN, "AGAIN" }, \ + { NFSERR_ACCES, "ACCES" }, \ + { NFSERR_EXIST, "EXIST" }, \ diff --git a/queue-6.1/nfs_common-factor-out-nfs_errtbl-and-nfs_stat_to_errno.patch b/queue-6.1/nfs_common-factor-out-nfs_errtbl-and-nfs_stat_to_errno.patch new file mode 100644 index 0000000000..0b08261be8 --- /dev/null +++ b/queue-6.1/nfs_common-factor-out-nfs_errtbl-and-nfs_stat_to_errno.patch @@ -0,0 +1,531 @@ +From stable+bounces-208128-greg=kroah.com@vger.kernel.org Mon Jan 12 15:52:46 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 09:39:09 -0500 +Subject: nfs_common: factor out nfs_errtbl and nfs_stat_to_errno +To: stable@vger.kernel.org +Cc: Mike Snitzer , Jeff Layton , NeilBrown , Anna Schumaker , Sasha Levin +Message-ID: <20260112143910.714632-2-sashal@kernel.org> + +From: Mike Snitzer + +[ Upstream commit 4806ded4c14c5e8fdc6ce885d83221a78c06a428 ] + +Common nfs_stat_to_errno() is used by both fs/nfs/nfs2xdr.c and +fs/nfs/nfs3xdr.c + +Will also be used by fs/nfsd/localio.c + +Signed-off-by: Mike Snitzer +Reviewed-by: Jeff Layton +Reviewed-by: NeilBrown +Signed-off-by: Anna Schumaker +Stable-dep-of: c6c209ceb87f ("NFSD: Remove NFSERR_EAGAIN") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/Kconfig | 1 + fs/nfs/nfs2xdr.c | 70 ----------------------------- + fs/nfs/nfs3xdr.c | 108 ++++++++------------------------------------- + fs/nfs/nfs4xdr.c | 4 - + fs/nfs_common/Makefile | 2 + fs/nfs_common/common.c | 67 +++++++++++++++++++++++++++ + fs/nfsd/Kconfig | 1 + include/linux/nfs_common.h | 16 ++++++ + 8 files changed, 109 insertions(+), 160 deletions(-) + create mode 100644 fs/nfs_common/common.c + create mode 100644 include/linux/nfs_common.h + +--- a/fs/nfs/Kconfig ++++ b/fs/nfs/Kconfig +@@ -5,6 +5,7 @@ config NFS_FS + select CRC32 + select LOCKD + select SUNRPC ++ select NFS_COMMON + select NFS_ACL_SUPPORT if NFS_V3_ACL + help + Choose Y here if you want to access files residing on other +--- a/fs/nfs/nfs2xdr.c ++++ b/fs/nfs/nfs2xdr.c +@@ -22,14 +22,12 @@ + #include + #include + #include ++#include + #include "nfstrace.h" + #include "internal.h" + + #define NFSDBG_FACILITY NFSDBG_XDR + +-/* Mapping from NFS error code to "errno" error code. */ +-#define errno_NFSERR_IO EIO +- + /* + * Declare the space requirements for NFS arguments and replies as + * number of 32bit-words +@@ -64,8 +62,6 @@ + #define NFS_readdirres_sz (1+NFS_pagepad_sz) + #define NFS_statfsres_sz (1+NFS_info_sz) + +-static int nfs_stat_to_errno(enum nfs_stat); +- + /* + * Encode/decode NFSv2 basic data types + * +@@ -1054,70 +1050,6 @@ out_default: + return nfs_stat_to_errno(status); + } + +- +-/* +- * We need to translate between nfs status return values and +- * the local errno values which may not be the same. +- */ +-static const struct { +- int stat; +- int errno; +-} nfs_errtbl[] = { +- { NFS_OK, 0 }, +- { NFSERR_PERM, -EPERM }, +- { NFSERR_NOENT, -ENOENT }, +- { NFSERR_IO, -errno_NFSERR_IO}, +- { NFSERR_NXIO, -ENXIO }, +-/* { NFSERR_EAGAIN, -EAGAIN }, */ +- { NFSERR_ACCES, -EACCES }, +- { NFSERR_EXIST, -EEXIST }, +- { NFSERR_XDEV, -EXDEV }, +- { NFSERR_NODEV, -ENODEV }, +- { NFSERR_NOTDIR, -ENOTDIR }, +- { NFSERR_ISDIR, -EISDIR }, +- { NFSERR_INVAL, -EINVAL }, +- { NFSERR_FBIG, -EFBIG }, +- { NFSERR_NOSPC, -ENOSPC }, +- { NFSERR_ROFS, -EROFS }, +- { NFSERR_MLINK, -EMLINK }, +- { NFSERR_NAMETOOLONG, -ENAMETOOLONG }, +- { NFSERR_NOTEMPTY, -ENOTEMPTY }, +- { NFSERR_DQUOT, -EDQUOT }, +- { NFSERR_STALE, -ESTALE }, +- { NFSERR_REMOTE, -EREMOTE }, +-#ifdef EWFLUSH +- { NFSERR_WFLUSH, -EWFLUSH }, +-#endif +- { NFSERR_BADHANDLE, -EBADHANDLE }, +- { NFSERR_NOT_SYNC, -ENOTSYNC }, +- { NFSERR_BAD_COOKIE, -EBADCOOKIE }, +- { NFSERR_NOTSUPP, -ENOTSUPP }, +- { NFSERR_TOOSMALL, -ETOOSMALL }, +- { NFSERR_SERVERFAULT, -EREMOTEIO }, +- { NFSERR_BADTYPE, -EBADTYPE }, +- { NFSERR_JUKEBOX, -EJUKEBOX }, +- { -1, -EIO } +-}; +- +-/** +- * nfs_stat_to_errno - convert an NFS status code to a local errno +- * @status: NFS status code to convert +- * +- * Returns a local errno value, or -EIO if the NFS status code is +- * not recognized. This function is used jointly by NFSv2 and NFSv3. +- */ +-static int nfs_stat_to_errno(enum nfs_stat status) +-{ +- int i; +- +- for (i = 0; nfs_errtbl[i].stat != -1; i++) { +- if (nfs_errtbl[i].stat == (int)status) +- return nfs_errtbl[i].errno; +- } +- dprintk("NFS: Unrecognized nfs status value: %u\n", status); +- return nfs_errtbl[i].errno; +-} +- + #define PROC(proc, argtype, restype, timer) \ + [NFSPROC_##proc] = { \ + .p_proc = NFSPROC_##proc, \ +--- a/fs/nfs/nfs3xdr.c ++++ b/fs/nfs/nfs3xdr.c +@@ -21,14 +21,13 @@ + #include + #include + #include ++#include ++ + #include "nfstrace.h" + #include "internal.h" + + #define NFSDBG_FACILITY NFSDBG_XDR + +-/* Mapping from NFS error code to "errno" error code. */ +-#define errno_NFSERR_IO EIO +- + /* + * Declare the space requirements for NFS arguments and replies as + * number of 32bit-words +@@ -91,8 +90,6 @@ + NFS3_pagepad_sz) + #define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz) + +-static int nfs3_stat_to_errno(enum nfs_stat); +- + /* + * Map file type to S_IFMT bits + */ +@@ -1406,7 +1403,7 @@ static int nfs3_xdr_dec_getattr3res(stru + out: + return error; + out_default: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1445,7 +1442,7 @@ static int nfs3_xdr_dec_setattr3res(stru + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1495,7 +1492,7 @@ out_default: + error = decode_post_op_attr(xdr, result->dir_attr, userns); + if (unlikely(error)) + goto out; +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1537,7 +1534,7 @@ static int nfs3_xdr_dec_access3res(struc + out: + return error; + out_default: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1578,7 +1575,7 @@ static int nfs3_xdr_dec_readlink3res(str + out: + return error; + out_default: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1658,7 +1655,7 @@ static int nfs3_xdr_dec_read3res(struct + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1728,7 +1725,7 @@ static int nfs3_xdr_dec_write3res(struct + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1795,7 +1792,7 @@ out_default: + error = decode_wcc_data(xdr, result->dir_attr, userns); + if (unlikely(error)) + goto out; +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1835,7 +1832,7 @@ static int nfs3_xdr_dec_remove3res(struc + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1881,7 +1878,7 @@ static int nfs3_xdr_dec_rename3res(struc + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -1926,7 +1923,7 @@ static int nfs3_xdr_dec_link3res(struct + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /** +@@ -2101,7 +2098,7 @@ out_default: + error = decode_post_op_attr(xdr, result->dir_attr, rpc_rqst_userns(req)); + if (unlikely(error)) + goto out; +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -2167,7 +2164,7 @@ static int nfs3_xdr_dec_fsstat3res(struc + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -2243,7 +2240,7 @@ static int nfs3_xdr_dec_fsinfo3res(struc + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -2304,7 +2301,7 @@ static int nfs3_xdr_dec_pathconf3res(str + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + /* +@@ -2350,7 +2347,7 @@ static int nfs3_xdr_dec_commit3res(struc + out: + return error; + out_status: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + #ifdef CONFIG_NFS_V3_ACL +@@ -2416,7 +2413,7 @@ static int nfs3_xdr_dec_getacl3res(struc + out: + return error; + out_default: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req, +@@ -2435,76 +2432,11 @@ static int nfs3_xdr_dec_setacl3res(struc + out: + return error; + out_default: +- return nfs3_stat_to_errno(status); ++ return nfs_stat_to_errno(status); + } + + #endif /* CONFIG_NFS_V3_ACL */ + +- +-/* +- * We need to translate between nfs status return values and +- * the local errno values which may not be the same. +- */ +-static const struct { +- int stat; +- int errno; +-} nfs_errtbl[] = { +- { NFS_OK, 0 }, +- { NFSERR_PERM, -EPERM }, +- { NFSERR_NOENT, -ENOENT }, +- { NFSERR_IO, -errno_NFSERR_IO}, +- { NFSERR_NXIO, -ENXIO }, +-/* { NFSERR_EAGAIN, -EAGAIN }, */ +- { NFSERR_ACCES, -EACCES }, +- { NFSERR_EXIST, -EEXIST }, +- { NFSERR_XDEV, -EXDEV }, +- { NFSERR_NODEV, -ENODEV }, +- { NFSERR_NOTDIR, -ENOTDIR }, +- { NFSERR_ISDIR, -EISDIR }, +- { NFSERR_INVAL, -EINVAL }, +- { NFSERR_FBIG, -EFBIG }, +- { NFSERR_NOSPC, -ENOSPC }, +- { NFSERR_ROFS, -EROFS }, +- { NFSERR_MLINK, -EMLINK }, +- { NFSERR_NAMETOOLONG, -ENAMETOOLONG }, +- { NFSERR_NOTEMPTY, -ENOTEMPTY }, +- { NFSERR_DQUOT, -EDQUOT }, +- { NFSERR_STALE, -ESTALE }, +- { NFSERR_REMOTE, -EREMOTE }, +-#ifdef EWFLUSH +- { NFSERR_WFLUSH, -EWFLUSH }, +-#endif +- { NFSERR_BADHANDLE, -EBADHANDLE }, +- { NFSERR_NOT_SYNC, -ENOTSYNC }, +- { NFSERR_BAD_COOKIE, -EBADCOOKIE }, +- { NFSERR_NOTSUPP, -ENOTSUPP }, +- { NFSERR_TOOSMALL, -ETOOSMALL }, +- { NFSERR_SERVERFAULT, -EREMOTEIO }, +- { NFSERR_BADTYPE, -EBADTYPE }, +- { NFSERR_JUKEBOX, -EJUKEBOX }, +- { -1, -EIO } +-}; +- +-/** +- * nfs3_stat_to_errno - convert an NFS status code to a local errno +- * @status: NFS status code to convert +- * +- * Returns a local errno value, or -EIO if the NFS status code is +- * not recognized. This function is used jointly by NFSv2 and NFSv3. +- */ +-static int nfs3_stat_to_errno(enum nfs_stat status) +-{ +- int i; +- +- for (i = 0; nfs_errtbl[i].stat != -1; i++) { +- if (nfs_errtbl[i].stat == (int)status) +- return nfs_errtbl[i].errno; +- } +- dprintk("NFS: Unrecognized nfs status value: %u\n", status); +- return nfs_errtbl[i].errno; +-} +- +- + #define PROC(proc, argtype, restype, timer) \ + [NFS3PROC_##proc] = { \ + .p_proc = NFS3PROC_##proc, \ +--- a/fs/nfs/nfs4xdr.c ++++ b/fs/nfs/nfs4xdr.c +@@ -52,6 +52,7 @@ + #include + #include + #include ++#include + + #include "nfs4_fs.h" + #include "nfs4trace.h" +@@ -63,9 +64,6 @@ + + #define NFSDBG_FACILITY NFSDBG_XDR + +-/* Mapping from NFS error code to "errno" error code. */ +-#define errno_NFSERR_IO EIO +- + struct compound_hdr; + static int nfs4_stat_to_errno(int); + static void encode_layoutget(struct xdr_stream *xdr, +--- a/fs/nfs_common/Makefile ++++ b/fs/nfs_common/Makefile +@@ -8,3 +8,5 @@ nfs_acl-objs := nfsacl.o + + obj-$(CONFIG_GRACE_PERIOD) += grace.o + obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o ++ ++obj-$(CONFIG_NFS_COMMON) += common.o +--- /dev/null ++++ b/fs/nfs_common/common.c +@@ -0,0 +1,67 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++ ++/* ++ * We need to translate between nfs status return values and ++ * the local errno values which may not be the same. ++ */ ++static const struct { ++ int stat; ++ int errno; ++} nfs_errtbl[] = { ++ { NFS_OK, 0 }, ++ { NFSERR_PERM, -EPERM }, ++ { NFSERR_NOENT, -ENOENT }, ++ { NFSERR_IO, -errno_NFSERR_IO}, ++ { NFSERR_NXIO, -ENXIO }, ++/* { NFSERR_EAGAIN, -EAGAIN }, */ ++ { NFSERR_ACCES, -EACCES }, ++ { NFSERR_EXIST, -EEXIST }, ++ { NFSERR_XDEV, -EXDEV }, ++ { NFSERR_NODEV, -ENODEV }, ++ { NFSERR_NOTDIR, -ENOTDIR }, ++ { NFSERR_ISDIR, -EISDIR }, ++ { NFSERR_INVAL, -EINVAL }, ++ { NFSERR_FBIG, -EFBIG }, ++ { NFSERR_NOSPC, -ENOSPC }, ++ { NFSERR_ROFS, -EROFS }, ++ { NFSERR_MLINK, -EMLINK }, ++ { NFSERR_NAMETOOLONG, -ENAMETOOLONG }, ++ { NFSERR_NOTEMPTY, -ENOTEMPTY }, ++ { NFSERR_DQUOT, -EDQUOT }, ++ { NFSERR_STALE, -ESTALE }, ++ { NFSERR_REMOTE, -EREMOTE }, ++#ifdef EWFLUSH ++ { NFSERR_WFLUSH, -EWFLUSH }, ++#endif ++ { NFSERR_BADHANDLE, -EBADHANDLE }, ++ { NFSERR_NOT_SYNC, -ENOTSYNC }, ++ { NFSERR_BAD_COOKIE, -EBADCOOKIE }, ++ { NFSERR_NOTSUPP, -ENOTSUPP }, ++ { NFSERR_TOOSMALL, -ETOOSMALL }, ++ { NFSERR_SERVERFAULT, -EREMOTEIO }, ++ { NFSERR_BADTYPE, -EBADTYPE }, ++ { NFSERR_JUKEBOX, -EJUKEBOX }, ++ { -1, -EIO } ++}; ++ ++/** ++ * nfs_stat_to_errno - convert an NFS status code to a local errno ++ * @status: NFS status code to convert ++ * ++ * Returns a local errno value, or -EIO if the NFS status code is ++ * not recognized. This function is used jointly by NFSv2 and NFSv3. ++ */ ++int nfs_stat_to_errno(enum nfs_stat status) ++{ ++ int i; ++ ++ for (i = 0; nfs_errtbl[i].stat != -1; i++) { ++ if (nfs_errtbl[i].stat == (int)status) ++ return nfs_errtbl[i].errno; ++ } ++ return nfs_errtbl[i].errno; ++} ++EXPORT_SYMBOL_GPL(nfs_stat_to_errno); +--- a/fs/nfsd/Kconfig ++++ b/fs/nfsd/Kconfig +@@ -8,6 +8,7 @@ config NFSD + select LOCKD + select SUNRPC + select EXPORTFS ++ select NFS_COMMON + select NFS_ACL_SUPPORT if NFSD_V2_ACL + select NFS_ACL_SUPPORT if NFSD_V3_ACL + depends on MULTIUSER +--- /dev/null ++++ b/include/linux/nfs_common.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This file contains constants and methods used by both NFS client and server. ++ */ ++#ifndef _LINUX_NFS_COMMON_H ++#define _LINUX_NFS_COMMON_H ++ ++#include ++#include ++ ++/* Mapping from NFS error code to "errno" error code. */ ++#define errno_NFSERR_IO EIO ++ ++int nfs_stat_to_errno(enum nfs_stat status); ++ ++#endif /* _LINUX_NFS_COMMON_H */ diff --git a/queue-6.1/nfsd-provide-locking-for-v4_end_grace.patch b/queue-6.1/nfsd-provide-locking-for-v4_end_grace.patch new file mode 100644 index 0000000000..4a9858f8c0 --- /dev/null +++ b/queue-6.1/nfsd-provide-locking-for-v4_end_grace.patch @@ -0,0 +1,186 @@ +From stable+bounces-208171-greg=kroah.com@vger.kernel.org Mon Jan 12 16:33:57 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 10:33:51 -0500 +Subject: nfsd: provide locking for v4_end_grace +To: stable@vger.kernel.org +Cc: NeilBrown , Li Lingfeng , Jeff Layton , Chuck Lever , Sasha Levin +Message-ID: <20260112153351.742670-1-sashal@kernel.org> + +From: NeilBrown + +[ Upstream commit 2857bd59feb63fcf40fe4baf55401baea6b4feb4 ] + +Writing to v4_end_grace can race with server shutdown and result in +memory being accessed after it was freed - reclaim_str_hashtbl in +particularly. + +We cannot hold nfsd_mutex across the nfsd4_end_grace() call as that is +held while client_tracking_op->init() is called and that can wait for +an upcall to nfsdcltrack which can write to v4_end_grace, resulting in a +deadlock. + +nfsd4_end_grace() is also called by the landromat work queue and this +doesn't require locking as server shutdown will stop the work and wait +for it before freeing anything that nfsd4_end_grace() might access. + +However, we must be sure that writing to v4_end_grace doesn't restart +the work item after shutdown has already waited for it. For this we +add a new flag protected with nn->client_lock. It is set only while it +is safe to make client tracking calls, and v4_end_grace only schedules +work while the flag is set with the spinlock held. + +So this patch adds a nfsd_net field "client_tracking_active" which is +set as described. Another field "grace_end_forced", is set when +v4_end_grace is written. After this is set, and providing +client_tracking_active is set, the laundromat is scheduled. +This "grace_end_forced" field bypasses other checks for whether the +grace period has finished. + +This resolves a race which can result in use-after-free. + +Reported-by: Li Lingfeng +Closes: https://lore.kernel.org/linux-nfs/20250623030015.2353515-1-neil@brown.name/T/#t +Fixes: 7f5ef2e900d9 ("nfsd: add a v4_end_grace file to /proc/fs/nfsd") +Cc: stable@vger.kernel.org +Signed-off-by: NeilBrown +Tested-by: Li Lingfeng +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +[ Adjust context ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfsd/netns.h | 2 ++ + fs/nfsd/nfs4state.c | 42 ++++++++++++++++++++++++++++++++++++++++-- + fs/nfsd/nfsctl.c | 3 +-- + fs/nfsd/state.h | 2 +- + 4 files changed, 44 insertions(+), 5 deletions(-) + +--- a/fs/nfsd/netns.h ++++ b/fs/nfsd/netns.h +@@ -64,6 +64,8 @@ struct nfsd_net { + + struct lock_manager nfsd4_manager; + bool grace_ended; ++ bool grace_end_forced; ++ bool client_tracking_active; + time64_t boot_time; + + struct dentry *nfsd_client_dir; +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -84,7 +84,7 @@ static u64 current_sessionid = 1; + /* forward declarations */ + static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner); + static void nfs4_free_ol_stateid(struct nfs4_stid *stid); +-void nfsd4_end_grace(struct nfsd_net *nn); ++static void nfsd4_end_grace(struct nfsd_net *nn); + static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps); + static void nfsd4_file_hash_remove(struct nfs4_file *fi); + +@@ -5882,7 +5882,7 @@ nfsd4_renew(struct svc_rqst *rqstp, stru + return nfs_ok; + } + +-void ++static void + nfsd4_end_grace(struct nfsd_net *nn) + { + /* do nothing if grace period already ended */ +@@ -5915,6 +5915,33 @@ nfsd4_end_grace(struct nfsd_net *nn) + */ + } + ++/** ++ * nfsd4_force_end_grace - forcibly end the NFSv4 grace period ++ * @nn: network namespace for the server instance to be updated ++ * ++ * Forces bypass of normal grace period completion, then schedules ++ * the laundromat to end the grace period immediately. Does not wait ++ * for the grace period to fully terminate before returning. ++ * ++ * Return values: ++ * %true: Grace termination schedule ++ * %false: No action was taken ++ */ ++bool nfsd4_force_end_grace(struct nfsd_net *nn) ++{ ++ if (!nn->client_tracking_ops) ++ return false; ++ spin_lock(&nn->client_lock); ++ if (nn->grace_ended || !nn->client_tracking_active) { ++ spin_unlock(&nn->client_lock); ++ return false; ++ } ++ WRITE_ONCE(nn->grace_end_forced, true); ++ mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); ++ spin_unlock(&nn->client_lock); ++ return true; ++} ++ + /* + * If we've waited a lease period but there are still clients trying to + * reclaim, wait a little longer to give them a chance to finish. +@@ -5924,6 +5951,8 @@ static bool clients_still_reclaiming(str + time64_t double_grace_period_end = nn->boot_time + + 2 * nn->nfsd4_lease; + ++ if (READ_ONCE(nn->grace_end_forced)) ++ return false; + if (nn->track_reclaim_completes && + atomic_read(&nn->nr_reclaim_complete) == + nn->reclaim_str_hashtbl_size) +@@ -8131,6 +8160,8 @@ static int nfs4_state_create_net(struct + nn->unconf_name_tree = RB_ROOT; + nn->boot_time = ktime_get_real_seconds(); + nn->grace_ended = false; ++ nn->grace_end_forced = false; ++ nn->client_tracking_active = false; + nn->nfsd4_manager.block_opens = true; + INIT_LIST_HEAD(&nn->nfsd4_manager.list); + INIT_LIST_HEAD(&nn->client_lru); +@@ -8207,6 +8238,10 @@ nfs4_state_start_net(struct net *net) + return ret; + locks_start_grace(net, &nn->nfsd4_manager); + nfsd4_client_tracking_init(net); ++ /* safe for laundromat to run now */ ++ spin_lock(&nn->client_lock); ++ nn->client_tracking_active = true; ++ spin_unlock(&nn->client_lock); + if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) + goto skip_grace; + printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n", +@@ -8253,6 +8288,9 @@ nfs4_state_shutdown_net(struct net *net) + + unregister_shrinker(&nn->nfsd_client_shrinker); + cancel_work_sync(&nn->nfsd_shrinker_work); ++ spin_lock(&nn->client_lock); ++ nn->client_tracking_active = false; ++ spin_unlock(&nn->client_lock); + cancel_delayed_work_sync(&nn->laundromat_work); + locks_end_grace(&nn->nfsd4_manager); + +--- a/fs/nfsd/nfsctl.c ++++ b/fs/nfsd/nfsctl.c +@@ -1117,9 +1117,8 @@ static ssize_t write_v4_end_grace(struct + case 'Y': + case 'y': + case '1': +- if (!nn->nfsd_serv) ++ if (!nfsd4_force_end_grace(nn)) + return -EBUSY; +- nfsd4_end_grace(nn); + break; + default: + return -EINVAL; +--- a/fs/nfsd/state.h ++++ b/fs/nfsd/state.h +@@ -719,7 +719,7 @@ static inline void get_nfs4_file(struct + struct nfsd_file *find_any_file(struct nfs4_file *f); + + /* grace period management */ +-void nfsd4_end_grace(struct nfsd_net *nn); ++bool nfsd4_force_end_grace(struct nfsd_net *nn); + + /* nfs4recover operations */ + extern int nfsd4_client_tracking_init(struct net *net); diff --git a/queue-6.1/nfsd-remove-nfserr_eagain.patch b/queue-6.1/nfsd-remove-nfserr_eagain.patch new file mode 100644 index 0000000000..7583dd7e8c --- /dev/null +++ b/queue-6.1/nfsd-remove-nfserr_eagain.patch @@ -0,0 +1,101 @@ +From stable+bounces-208129-greg=kroah.com@vger.kernel.org Mon Jan 12 15:45:37 2026 +From: Sasha Levin +Date: Mon, 12 Jan 2026 09:39:10 -0500 +Subject: NFSD: Remove NFSERR_EAGAIN +To: stable@vger.kernel.org +Cc: Chuck Lever , NeilBrown , Jeff Layton , Sasha Levin +Message-ID: <20260112143910.714632-3-sashal@kernel.org> + +From: Chuck Lever + +[ Upstream commit c6c209ceb87f64a6ceebe61761951dcbbf4a0baa ] + +I haven't found an NFSERR_EAGAIN in RFCs 1094, 1813, 7530, or 8881. +None of these RFCs have an NFS status code that match the numeric +value "11". + +Based on the meaning of the EAGAIN errno, I presume the use of this +status in NFSD means NFS4ERR_DELAY. So replace the one usage of +nfserr_eagain, and remove it from NFSD's NFS status conversion +tables. + +As far as I can tell, NFSERR_EAGAIN has existed since the pre-git +era, but was not actually used by any code until commit f4e44b393389 +("NFSD: delay unmount source's export after inter-server copy +completed."), at which time it become possible for NFSD to return +a status code of 11 (which is not valid NFS protocol). + +Fixes: f4e44b393389 ("NFSD: delay unmount source's export after inter-server copy completed.") +Cc: stable@vger.kernel.org +Reviewed-by: NeilBrown +Reviewed-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs_common/common.c | 1 - + fs/nfsd/nfs4proc.c | 2 +- + fs/nfsd/nfsd.h | 1 - + include/trace/misc/nfs.h | 2 -- + include/uapi/linux/nfs.h | 1 - + 5 files changed, 1 insertion(+), 6 deletions(-) + +--- a/fs/nfs_common/common.c ++++ b/fs/nfs_common/common.c +@@ -16,7 +16,6 @@ static const struct { + { NFSERR_NOENT, -ENOENT }, + { NFSERR_IO, -errno_NFSERR_IO}, + { NFSERR_NXIO, -ENXIO }, +-/* { NFSERR_EAGAIN, -EAGAIN }, */ + { NFSERR_ACCES, -EACCES }, + { NFSERR_EXIST, -EEXIST }, + { NFSERR_XDEV, -EXDEV }, +--- a/fs/nfsd/nfs4proc.c ++++ b/fs/nfsd/nfs4proc.c +@@ -1321,7 +1321,7 @@ try_again: + (schedule_timeout(20*HZ) == 0)) { + finish_wait(&nn->nfsd_ssc_waitq, &wait); + kfree(work); +- return nfserr_eagain; ++ return nfserr_jukebox; + } + finish_wait(&nn->nfsd_ssc_waitq, &wait); + goto try_again; +--- a/fs/nfsd/nfsd.h ++++ b/fs/nfsd/nfsd.h +@@ -201,7 +201,6 @@ void nfsd_lockd_shutdown(void); + #define nfserr_noent cpu_to_be32(NFSERR_NOENT) + #define nfserr_io cpu_to_be32(NFSERR_IO) + #define nfserr_nxio cpu_to_be32(NFSERR_NXIO) +-#define nfserr_eagain cpu_to_be32(NFSERR_EAGAIN) + #define nfserr_acces cpu_to_be32(NFSERR_ACCES) + #define nfserr_exist cpu_to_be32(NFSERR_EXIST) + #define nfserr_xdev cpu_to_be32(NFSERR_XDEV) +--- a/include/trace/misc/nfs.h ++++ b/include/trace/misc/nfs.h +@@ -16,7 +16,6 @@ TRACE_DEFINE_ENUM(NFSERR_PERM); + TRACE_DEFINE_ENUM(NFSERR_NOENT); + TRACE_DEFINE_ENUM(NFSERR_IO); + TRACE_DEFINE_ENUM(NFSERR_NXIO); +-TRACE_DEFINE_ENUM(NFSERR_EAGAIN); + TRACE_DEFINE_ENUM(NFSERR_ACCES); + TRACE_DEFINE_ENUM(NFSERR_EXIST); + TRACE_DEFINE_ENUM(NFSERR_XDEV); +@@ -53,7 +52,6 @@ TRACE_DEFINE_ENUM(NFSERR_JUKEBOX); + { NFSERR_NXIO, "NXIO" }, \ + { ECHILD, "CHILD" }, \ + { ETIMEDOUT, "TIMEDOUT" }, \ +- { NFSERR_EAGAIN, "AGAIN" }, \ + { NFSERR_ACCES, "ACCES" }, \ + { NFSERR_EXIST, "EXIST" }, \ + { NFSERR_XDEV, "XDEV" }, \ +--- a/include/uapi/linux/nfs.h ++++ b/include/uapi/linux/nfs.h +@@ -49,7 +49,6 @@ + NFSERR_NOENT = 2, /* v2 v3 v4 */ + NFSERR_IO = 5, /* v2 v3 v4 */ + NFSERR_NXIO = 6, /* v2 v3 v4 */ +- NFSERR_EAGAIN = 11, /* v2 v3 */ + NFSERR_ACCES = 13, /* v2 v3 v4 */ + NFSERR_EXIST = 17, /* v2 v3 v4 */ + NFSERR_XDEV = 18, /* v3 v4 */ diff --git a/queue-6.1/pinctrl-qcom-lpass-lpi-mark-the-gpio-controller-as-sleeping.patch b/queue-6.1/pinctrl-qcom-lpass-lpi-mark-the-gpio-controller-as-sleeping.patch new file mode 100644 index 0000000000..5284348d92 --- /dev/null +++ b/queue-6.1/pinctrl-qcom-lpass-lpi-mark-the-gpio-controller-as-sleeping.patch @@ -0,0 +1,88 @@ +From ebc18e9854e5a2b62a041fb57b216a903af45b85 Mon Sep 17 00:00:00 2001 +From: Bartosz Golaszewski +Date: Wed, 26 Nov 2025 13:22:19 +0100 +Subject: pinctrl: qcom: lpass-lpi: mark the GPIO controller as sleeping + +From: Bartosz Golaszewski + +commit ebc18e9854e5a2b62a041fb57b216a903af45b85 upstream. + +The gpio_chip settings in this driver say the controller can't sleep +but it actually uses a mutex for synchronization. This triggers the +following BUG(): + +[ 9.233659] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:281 +[ 9.233665] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 554, name: (udev-worker) +[ 9.233669] preempt_count: 1, expected: 0 +[ 9.233673] RCU nest depth: 0, expected: 0 +[ 9.233688] Tainted: [W]=WARN +[ 9.233690] Hardware name: Dell Inc. Latitude 7455/0FK7MX, BIOS 2.10.1 05/20/2025 +[ 9.233694] Call trace: +[ 9.233696] show_stack+0x24/0x38 (C) +[ 9.233709] dump_stack_lvl+0x40/0x88 +[ 9.233716] dump_stack+0x18/0x24 +[ 9.233722] __might_resched+0x148/0x160 +[ 9.233731] __might_sleep+0x38/0x98 +[ 9.233736] mutex_lock+0x30/0xd8 +[ 9.233749] lpi_config_set+0x2e8/0x3c8 [pinctrl_lpass_lpi] +[ 9.233757] lpi_gpio_direction_output+0x58/0x90 [pinctrl_lpass_lpi] +[ 9.233761] gpiod_direction_output_raw_commit+0x110/0x428 +[ 9.233772] gpiod_direction_output_nonotify+0x234/0x358 +[ 9.233779] gpiod_direction_output+0x38/0xd0 +[ 9.233786] gpio_shared_proxy_direction_output+0xb8/0x2a8 [gpio_shared_proxy] +[ 9.233792] gpiod_direction_output_raw_commit+0x110/0x428 +[ 9.233799] gpiod_direction_output_nonotify+0x234/0x358 +[ 9.233806] gpiod_configure_flags+0x2c0/0x580 +[ 9.233812] gpiod_find_and_request+0x358/0x4f8 +[ 9.233819] gpiod_get_index+0x7c/0x98 +[ 9.233826] devm_gpiod_get+0x34/0xb0 +[ 9.233829] reset_gpio_probe+0x58/0x128 [reset_gpio] +[ 9.233836] auxiliary_bus_probe+0xb0/0xf0 +[ 9.233845] really_probe+0x14c/0x450 +[ 9.233853] __driver_probe_device+0xb0/0x188 +[ 9.233858] driver_probe_device+0x4c/0x250 +[ 9.233863] __driver_attach+0xf8/0x2a0 +[ 9.233868] bus_for_each_dev+0xf8/0x158 +[ 9.233872] driver_attach+0x30/0x48 +[ 9.233876] bus_add_driver+0x158/0x2b8 +[ 9.233880] driver_register+0x74/0x118 +[ 9.233886] __auxiliary_driver_register+0x94/0xe8 +[ 9.233893] init_module+0x34/0xfd0 [reset_gpio] +[ 9.233898] do_one_initcall+0xec/0x300 +[ 9.233903] do_init_module+0x64/0x260 +[ 9.233910] load_module+0x16c4/0x1900 +[ 9.233915] __arm64_sys_finit_module+0x24c/0x378 +[ 9.233919] invoke_syscall+0x4c/0xe8 +[ 9.233925] el0_svc_common+0x8c/0xf0 +[ 9.233929] do_el0_svc+0x28/0x40 +[ 9.233934] el0_svc+0x38/0x100 +[ 9.233938] el0t_64_sync_handler+0x84/0x130 +[ 9.233943] el0t_64_sync+0x17c/0x180 + +Mark the controller as sleeping. + +Fixes: 6e261d1090d6 ("pinctrl: qcom: Add sm8250 lpass lpi pinctrl driver") +Cc: stable@vger.kernel.org +Reported-by: Val Packett +Closes: https://lore.kernel.org/all/98c0f185-b0e0-49ea-896c-f3972dd011ca@packett.cool/ +Signed-off-by: Bartosz Golaszewski +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Bjorn Andersson +Signed-off-by: Linus Walleij +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c ++++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +@@ -435,7 +435,7 @@ int lpi_pinctrl_probe(struct platform_de + pctrl->chip.ngpio = data->npins; + pctrl->chip.label = dev_name(dev); + pctrl->chip.of_gpio_n_cells = 2; +- pctrl->chip.can_sleep = false; ++ pctrl->chip.can_sleep = true; + + mutex_init(&pctrl->lock); + diff --git a/queue-6.1/series b/queue-6.1/series index 20f9aae319..0eefb78846 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -49,3 +49,12 @@ net-sched-sch_qfq-fix-null-deref-when-deactivating-i.patch net-usb-pegasus-fix-memory-leak-in-update_eth_regs_a.patch net-enetc-fix-build-warning-when-page_size-is-greate.patch arp-do-not-assume-dev_hard_header-does-not-change-sk.patch +pinctrl-qcom-lpass-lpi-mark-the-gpio-controller-as-sleeping.patch +mm-pagewalk-add-walk_page_range_vma.patch +ksm-use-range-walk-function-to-jump-over-holes-in-scan_get_next_rmap_item.patch +alsa-ac97bus-use-guard-for-mutex-locks.patch +alsa-ac97-fix-a-double-free-in-snd_ac97_controller_register.patch +nfsd-provide-locking-for-v4_end_grace.patch +nfs-trace-show-timedout-instead-of-0x6e.patch +nfs_common-factor-out-nfs_errtbl-and-nfs_stat_to_errno.patch +nfsd-remove-nfserr_eagain.patch