6.12.111-rc1 review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 988/1102 mm: move hugetlb specific things in folio to page[3]
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Hildenbrand <[email protected]>
[ Upstream commit 4eeec8c89a0c4a8c20fb13a4e7093cc8efce383d ]
Let's just move the hugetlb specific stuff to a separate page, and stop letting it overlay other fields for now.
This frees up some space in page[2], which we will use on 32bit to free up some space in page[1]. While we could move these things to page[3] instead, it's cleaner to just move the hugetlb specific things out of the way and pack the core-folio stuff as tight as possible. ... and we can minimize the work required in dump_folio.
We can now avoid re-initializing &folio->_deferred_list in hugetlb code.
Hopefully dynamically allocating "strut folio" in the future will further clean this up.
Stable-dep-of: 078e1a0fc41a ("mm/gup: fix always draining LRU caches in collect_longterm_unpinnable_folios()")
--- include/linux/mm_types.h | 27 +++++++++++++++++---------- mm/hugetlb.c | 1 - mm/page_alloc.c | 5 +++++ 3 files changed, 22 insertions(+), 11 deletions(-)
include/linux/mm_types.h +17 -10
@@ -385,20 +385,23 @@ struct folio {unsigned long _flags_2;unsigned long _head_2;/* public: */ -void *_hugetlb_subpool; -void *_hugetlb_cgroup; -void *_hugetlb_cgroup_rsvd; -void *_hugetlb_hwpoison; +struct list_head _deferred_list;/* private: the union with struct page is transitional */ }; +struct page __page_2; + }; +union {struct { -unsigned long _flags_2a; -unsigned long _head_2a; +unsigned long _flags_3; +unsigned long _head_3;/* public: */ -struct list_head _deferred_list; +void *_hugetlb_subpool; +void *_hugetlb_cgroup; +void *_hugetlb_cgroup_rsvd; +void *_hugetlb_hwpoison;/* private: the union with struct page is transitional */ }; -struct page __page_2; +struct page __page_3; }; };
@@ -433,8 +436,12 @@ FOLIO_MATCH(compound_head, _head_1); offsetof(struct page, pg) + 2 * sizeof(struct page))FOLIO_MATCH (flags, _flags_2);FOLIO_MATCH (compound_head, _head_2); -FOLIO_MATCH (flags, _flags_2a); -FOLIO_MATCH (compound_head, _head_2a); +#undef FOLIO_MATCH +#define FOLIO_MATCH (pg, fl) \ + static_assert(offsetof(struct folio, fl) == \ + offsetof(struct page, pg) + 3 * sizeof(struct page)) +FOLIO_MATCH (flags, _flags_3); +FOLIO_MATCH (compound_head, _head_3);#undef FOLIO_MATCH/**
mm/hugetlb.c +0 -1
@@ -1702,7 +1702,6 @@ static void __update_and_free_hugetlb_fofolio_ref_unfreeze (folio,1 ); -INIT_LIST_HEAD (&folio->_deferred_list);hugetlb_free_folio (folio); }
mm/page_alloc.c +5 -0
@@ -1005,6 +1005,11 @@ static int free_tail_page_prepare(struct goto out; }break ; +case 3 : +/* the third tail page: hugetlb specifics overlap ->mappings */ +if (IS_ENABLED (CONFIG_HUGETLB_PAGE )) +break ; + fallthrough;default :if (page->mapping !=TAIL_MAPPING ) {bad_page (page,"corrupted mapping in tail page" );
Greg Kroah-Hartman
maintainerPATCH 989/1102 mm: move _pincount in folio to page[2] on 32bit
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Hildenbrand <[email protected]>
[ Upstream commit 31a31da8a6187f1e5448ec73222e01d7d3fed4aa ]
Let's free up some space on 32bit in page[1] by moving the _pincount to page[2].
For order-1 folios (never anon folios!) on 32bit, we will now also use the GUP_PIN_COUNTING_BIAS approach. A fully-mapped order-1 folio requires 2 references. With GUP_PIN_COUNTING_BIAS being 1024, we'd detect such folios as "maybe pinned" with 512 full mappings, instead of 1024 for order-0. As anon folios are out of the picture (which are the most relevant users of checking for pinnings on *mapped* pages) and we are talking about 32bit, this is not expected to cause any trouble.
In __dump_page(), copy one additional folio page if we detect a folio with an order > 1, so we can dump the pincount on order > 1 folios reliably.
Note that THPs on 32bit are not particularly common (and we don't care too much about performance), but we want to keep it working reliably, because likely we want to use large folios there as well in the future, independent of PMD leaf support.
Once we dynamically allocate "struct folio", fortunately the 32bit specifics will likely go away again; even small folios could then have a pincount and folio_has_pincount() would essentially always return "true".
[ sashal: Reduced backport -- upstream 31a31da8a6187 touches 6 file(s), this
backport carries 1. Not backported here:
include/linux/mm_types.h
mm/debug.c
mm/gup.c
mm/internal.h
mm/page_alloc.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 078e1a0fc41a ("mm/gup: fix always draining LRU caches in collect_longterm_unpinnable_folios()")
--- include/linux/mm.h | 2 ++ 1 file changed, 2 insertions(+)
include/linux/mm.h +2 -0
@@ -1905,6 +1905,8 @@ static inline struct folio *pfn_folio(unreturn page_folio (pfn_to_page (pfn)); } +#define folio_has_pincount (folio) folio_test_large(folio) +/** * folio_maybe_dma_pinned - Report if a folio may be pinned for DMA. * @folio: The folio.
Greg Kroah-Hartman
maintainerPATCH 990/1102 mm/gup: fix always draining LRU caches in collect_longterm_unpinnable_folios()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "David Hildenbrand (Arm)" <[email protected]>
[ Upstream commit 078e1a0fc41a42baaf113383b52dab8874c0b967 ]
folio_may_be_lru_cached() is currently only true for small folios, and for small folios FOLL_PIN adds GUP_PIN_COUNTING_BIAS references instead of 1 in try_grab_folio()/try_grab_folio_fast().
Consequently, our
folio_ref_count(folio) != folio_expected_ref_count(folio) + 1
check in collect_longterm_unpinnable_folios() will currently always identify "reference mismatch" and first drain the local LRU cache to then drain the LRU cache on all CPUs, as collect_longterm_unpinnable_folios() is really called after pinning the folios with FOLL_PIN.
Add a comment because the current code is not quite intuitive: we used to drain only to make sure the folio_isolate_lru() would succeed. But then we also started draining to make later migration more reliable.
We'll refactor that code soon a bit, to also make it usable in other context where we really want to remove any references from LRU caches.
Let's add CC stable, because having an easy way for excessive LRU cache draining on all CPUs does not sound right. In common scenarios we don't expect to ever have to drain.
--- mm/gup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
mm/gup.c +8 -2
@@ -2362,6 +2362,7 @@ static unsigned long collect_longterm_unfor (folio =pofs_get_folio (pofs, i); folio; folio =pofs_next_folio (folio, pofs, &i)) { +const int pin_refs =folio_has_pincount (folio) ?1 :GUP_PIN_COUNTING_BIAS ;if (folio_is_longterm_pinnable (folio))continue ;
@@ -2376,15 +2377,20 @@ static unsigned long collect_longterm_uncontinue ; } +/* +* We drain not only to make the folio_isolate_lru() succeed, +* but also to remove any other folio references from LRU +* caches. +*/ if (drained ==0 &&folio_may_be_lru_cached (folio) &&folio_ref_count (folio) != -folio_expected_ref_count (folio) +1 ) { +folio_expected_ref_count (folio) + pin_refs) {lru_add_drain (); drained =1 ; }if (drained ==1 &&folio_may_be_lru_cached (folio) &&folio_ref_count (folio) != -folio_expected_ref_count (folio) +1 ) { +folio_expected_ref_count (folio) + pin_refs) {lru_add_drain_all (); drained =2 ; }
Greg Kroah-Hartman
maintainerPATCH 991/1102 nscommon: simplify initialization
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 5612ff3ec588be09f11a9424db6d1186bcdeb3fa ]
There's a lot of information that namespace implementers don't need to know about at all. Encapsulate this all in the initialization helper.
[ sashal: Reduced backport -- upstream 5612ff3ec588b touches 10 file(s), this
backport carries 1. Not backported here:
fs/namespace.c
include/linux/ns_common.h
ipc/namespace.c
kernel/cgroup/namespace.c
kernel/nscommon.c
kernel/pid_namespace.c
kernel/time/namespace.c
kernel/user_namespace.c
... and 2 more
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 9688a4680293 ("pidfd: hold exec_update_lock around namespace ioctl")
--- fs/pidfs.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
fs/pidfs.c +16 -6
@@ -115,21 +115,27 @@ static __poll_t pidfd_poll(struct file *return poll_flags; } +#define pidfs_ioctl_valid (cmd) \ + (_IOC_TYPE(cmd) == PIDFS_IOCTL_MAGIC && _IOC_DIR(cmd) == _IOC_NONE && \ + _IOC_NR(cmd) >= _IOC_NR(PIDFD_GET_CGROUP_NAMESPACE) && \ + _IOC_NR(cmd) <= _IOC_NR(PIDFD_GET_UTS_NAMESPACE)) +static long pidfd_ioctl (struct file *file,unsigned int cmd,unsigned long arg) { struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; -struct pid *pid =pidfd_pid (file);struct ns_common *ns_common =NULL ; -struct pid_namespace *pid_ns; -if (arg) -return -EINVAL ; +if (!pidfs_ioctl_valid (cmd)) +return -ENOIOCTLCMD ; - task =get_pid_task (pid,PIDTYPE_PID ); + task =get_pid_task (pidfd_pid (file),PIDTYPE_PID );if (!task)return -ESRCH ; +if (arg) +return -EINVAL ; + scoped_guard(task_lock, task) { nsp = task->nsproxy;if (nsp)
@@ -202,13 +208,17 @@ static long pidfd_ioctl(struct file *fil }break ;case PIDFD_GET_PID_NAMESPACE : -if (IS_ENABLED (CONFIG_PID_NS )) { +#ifdef CONFIG_PID_NS + { +struct pid_namespace *pid_ns; +rcu_read_lock (); pid_ns =task_active_pid_ns (task);if (pid_ns) ns_common =to_ns_common (get_pid_ns (pid_ns));rcu_read_unlock (); } +#endif break ;default :return -ENOIOCTLCMD ;
Re: [PATCH 6.12 0991/1102] nscommon: simplify initialization
Quoted 14 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit
5612ff3ec588be09f11a9424db6d1186bcdeb3fa]There's a lot of information that namespace implementers don't need to know about at all. Encapsulate this all in the initialization helper.
Reviewed-by Jan Kara <[email protected]>Signed-off-by Christian Brauner <[email protected]>
Quoted 14 lines
[ sashal: Reduced backport -- upstream
5612ff3ec588btouches 10 file(s), this
backport carries 1. Not backported here:
fs/namespace.c
include/linux/ns_common.h
ipc/namespace.c
kernel/cgroup/namespace.c
kernel/nscommon.c
kernel/pid_namespace.c
kernel/time/namespace.c
kernel/user_namespace.c
... and 2 more
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Hi Greg/Sasha,
I ran an AI-assisted review of 0f71b995d44b and verified that the recorded backport identity does not match its payload.
Claimed upstream has changes such as:
ret = ns_common_init(new_ns, &mntns_operations);
err = ns_common_init(ns, &pidns_operations);
The recorded 6.12.y backport instead has unrelated pidfs code:
#define pidfs_ioctl_valid(cmd) \
(_IOC_TYPE(cmd) == PIDFS_IOCTL_MAGIC && \
_IOC_DIR(cmd) == _IOC_NONE)
...
if (!pidfs_ioctl_valid(cmd))
return -ENOIOCTLCMD;The commit claims to backport 5612ff3ec588
("nscommon: simplify initialization"). That upstream commit changes ten namespace initialization files. The stable commit changes only fs/pidfs.c, so the two changed-file sets are disjoint.
The pidfs code resembles later ioctl validation and CONFIG_PID_NS changes and appears defensive, but none of it comes from 5612ff3ec588. The nscommon refactor itself is not needed on this older branch because its helper framework is absent and direct initialization remains valid.
I think we should drop the change. Thoughts ?
thanks,
Harshit
Quoted 64 lines
Stable-dep-of:
9688a4680293("pidfd: holdexec_update_lockaround namespace ioctl")Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- fs/pidfs.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)fs/pidfs.c +16
-6@@ -115,21 +115,27 @@ static __poll_t pidfd_poll(struct file *return poll_flags; } +#define pidfs_ioctl_valid (cmd) \ + (_IOC_TYPE(cmd) == PIDFS_IOCTL_MAGIC && _IOC_DIR(cmd) == _IOC_NONE && \ + _IOC_NR(cmd) >= _IOC_NR(PIDFD_GET_CGROUP_NAMESPACE) && \ + _IOC_NR(cmd) <= _IOC_NR(PIDFD_GET_UTS_NAMESPACE)) +static long pidfd_ioctl (struct file *file,unsigned int cmd,unsigned long arg) { struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; -struct pid *pid =pidfd_pid (file);struct ns_common *ns_common =NULL ; -struct pid_namespace *pid_ns; -if (arg) -return -EINVAL ; +if (!pidfs_ioctl_valid (cmd)) +return -ENOIOCTLCMD ; - task =get_pid_task (pid,PIDTYPE_PID ); + task =get_pid_task (pidfd_pid (file),PIDTYPE_PID );if (!task)return -ESRCH ; +if (arg) +return -EINVAL ; + scoped_guard(task_lock, task) { nsp = task->nsproxy;if (nsp)@@ -202,13 +208,17 @@ static long pidfd_ioctl(struct file *fil }break ;case PIDFD_GET_PID_NAMESPACE : -if (IS_ENABLED (CONFIG_PID_NS )) { +#ifdef CONFIG_PID_NS + { +struct pid_namespace *pid_ns; +rcu_read_lock (); pid_ns =task_active_pid_ns (task);if (pid_ns) ns_common =to_ns_common (get_pid_ns (pid_ns));rcu_read_unlock (); } +#endif break ;default :return -ENOIOCTLCMD ;
Sasha Levin
maintainerRe: [PATCH 6.12 0991/1102] nscommon: simplify initialization
Quoted 4 lines · Harshit Mogalapalli
The commit claims to backport
5612ff3ec588
("nscommon: simplify initialization"). That upstream commit changes ten namespace initialization files. The stable commit changes onlyfs/pidfs.c, so the two changed-file sets are disjoint.
[...]
Quoted 1 line
I think we should drop the change. Thoughts ?
I'll drop it.
Thanks for catching this one.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 992/1102 pidfd: hold exec_update_lock around namespace ioctl
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chen Linxuan <[email protected]>
[ Upstream commit 9688a46802939da28f00cb40e8129615d5d4af39 ]
The PIDFD_GET_*_NAMESPACE ioctls in pidfd_ioctl() perform a filesystem credentials ptrace access check before handing out a namespace file descriptor. The accompanying comment states that the code "mirrors nsfs behavior", but, unlike the corresponding procfs paths, it does so without holding the target task's exec_update_lock.
proc_ns_get_link() and proc_ns_readlink() both take exec_update_lock for reading around the ptrace check and the namespace lookup, so that the credentials used for the access decision match those of the task when its namespace is read. Without it, a caller can pass the check against the target's old credentials and then read the namespace after the target has execve()'d a setuid binary and committed new credentials -- accessing namespace information it should have been denied.
Hold exec_update_lock for reading around the ptrace check and the namespace lookup so that pidfd truly mirrors nsfs behavior, as the comment already claims. open_namespace() itself runs outside the lock: once a namespace reference is obtained it carries its own refcount and is opened with the caller's own credentials, so a concurrent execve() on the target can no longer affect the outcome.
--- fs/pidfs.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-)
fs/pidfs.c +31 -12
@@ -125,6 +125,7 @@ static long pidfd_ioctl(struct file *fil struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL;struct ns_common *ns_common =NULL ; +int error;if (!pidfs_ioctl_valid (cmd))return -ENOIOCTLCMD ;
@@ -136,20 +137,33 @@ static long pidfd_ioctl(struct file *filif (arg)return -EINVAL ; +/* +* We're trying to open a file descriptor to the namespace so perform a +* filesystem cred ptrace check. Hold @task's exec_update_lock for the +* duration of the ptrace check and the namespace lookup so that the +* credentials used for the access decision match those of @task at the +* time its namespace is read, preventing a concurrent execve() from +* swapping the task's credentials in between the check and the use. We +* mirror nsfs behavior. +*/ + error =down_read_killable (&task->signal->exec_update_lock); +if (error) +return error; + +if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { + error = -EACCES ; + goto out_unlock; + } + scoped_guard(task_lock, task) { nsp = task->nsproxy;if (nsp)get_nsproxy (nsp); } -if (!nsp) -return -ESRCH ;/* just pretend it didn't exist */ - -/* -* We're trying to open a file descriptor to the namespace so perform a -* filesystem cred ptrace check. Also, we mirror nsfs behavior. -*/ -if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) -return -EACCES ; +if (!nsp) { + error = -ESRCH ;/* just pretend it didn't exist */ + goto out_unlock; + }switch (cmd) {/* Namespaces that hang of nsproxy. */
@@ -221,11 +235,16 @@ static long pidfd_ioctl(struct file *fil#endif break ;default : -return -ENOIOCTLCMD ; + error = -ENOIOCTLCMD ; } -if (!ns_common) -return -EOPNOTSUPP ; +if (!error && !ns_common) + error = -EOPNOTSUPP ; + +out_unlock: +up_read (&task->signal->exec_update_lock); +if (error) +return error;/* open_namespace() unconditionally consumes the reference */ return open_namespace (ns_common);
Greg Kroah-Hartman
maintainerPATCH 993/1102 mm/hugetlb: initialize gigantic bootmem hugepage struct pages earlier
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muchun Song <[email protected]>
[ Upstream commit b1b7c045e808c761b1cc8c19b3040fadedda3fef ]
Gigantic bootmem HugeTLB pages are currently initialized from hugetlb_init(), but page_alloc_init_late() runs earlier and walks pageblocks to determine zone contiguity.
If a bootmem HugeTLB region is marked noinit, set_zone_contiguous() can observe still-uninitialized struct pages through __pageblock_pfn_to_page(). This may not trigger an immediate failure, but it can make set_zone_contiguous() compute the wrong zone contiguity state. If extra poisoned-page checks are added in this path, such as PF_POISONED_CHECK() in page_zone_id(), it can also trigger an early boot panic.
Initialize gigantic bootmem HugeTLB struct pages from page_alloc_init_late(), before zone contiguity is evaluated, so later page allocator setup only sees valid struct page state. This also makes the initialization order more natural, as struct pages should be initialized before later code inspects them.
--- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 5 ++--- mm/mm_init.c | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-)
include/linux/hugetlb.h +5 -0
@@ -172,6 +172,7 @@ struct address_space *hugetlb_folio_mappextern int sysctl_hugetlb_shm_group;extern struct list_head huge_boot_pages[MAX_NUMNODES ]; +void hugetlb_bootmem_struct_page_init (void );/* arch callbacks */ #ifndef CONFIG_HIGHPTE
@@ -1265,6 +1266,10 @@ static inline bool hugetlbfs_pagecache_p {return false; } + +static inline void hugetlb_bootmem_struct_page_init (void ) +{ +}#endif /* CONFIG_HUGETLB_PAGE */ static inline spinlock_t *huge_pte_lock(struct hstate *h,
mm/hugetlb.c +2 -3
@@ -3276,7 +3276,7 @@ static void __init gather_bootmem_preallgather_bootmem_prealloc_node (nid); } -static void __init gather_bootmem_prealloc (void ) +void __init hugetlb_bootmem_struct_page_init (void ) {struct padata_mt_job job = { .thread_fn = gather_bootmem_prealloc_parallel,
@@ -3431,7 +3431,7 @@ static unsigned long __init hugetlb_page* - For gigantic pages, this is called early in the boot process and * pages are allocated from memblock allocated or something similar. * Gigantic pages are actually added to pools later with the routine -* gather_bootmem_prealloc. +* hugetlb_bootmem_struct_page_init. * - For non-gigantic pages, this is called later in the boot process after * all of mm is up and functional. Pages are allocated from buddy and * then added to hugetlb pools.
@@ -4470,7 +4470,6 @@ static int __init hugetlb_init(void)hugetlb_cma_check ();hugetlb_init_hstates (); -gather_bootmem_prealloc ();report_hugepages ();hugetlb_sysfs_init ();
mm/mm_init.c +2 -0
@@ -30,6 +30,7 @@#include <linux/crash_dump.h> #include <linux/execmem.h> #include <linux/vmstat.h> +#include <linux/hugetlb.h> #include "internal.h" #include "slab.h" #include "shuffle.h"
@@ -2320,6 +2321,7 @@ void __init page_alloc_init_late(void)/* Reinit limits that are based on free pages after the kernel is up */ files_maxfiles_init ();#endif +hugetlb_bootmem_struct_page_init ();/* Accounting of total+free memory is stable at this point. */ mem_init_print_info ();
Greg Kroah-Hartman
maintainerPATCH 994/1102 mm/mglru: reset page lru tier bits when activating
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Xu <[email protected]>
[ Upstream commit f1001f3d3b6868998cab73d10fda1a5c99ddf963 ]
When a folio is activated, lru_gen_add_folio() moves the folio to the youngest generation. But unlike folio_update_gen()/folio_inc_gen(), lru_gen_add_folio() doesn't reset the folio lru tier bits (LRU_REFS_MASK | LRU_REFS_FLAGS). This inconsistency can affect how pages are aged via folio_mark_accessed() (e.g. fd accesses), though no user visible impact related to this has been detected yet.
Note that lru_gen_add_folio() cannot clear PG_workingset if the activation is due to workingset refault, otherwise PSI accounting will be skipped. So fix lru_gen_add_folio() to clear the lru tier bits other than PG_workingset when activating a folio, and also clear all the lru tier bits when a folio is activated via folio_activate() in lru_gen_look_around().
Stable-dep-of: f7e698e326b2 ("mm/mglru: fix and remove redundant unevictable folio handling")
--- include/linux/mm_inline.h | 15 ++++++++++++++- include/linux/mmzone.h | 2 ++ mm/vmscan.c | 8 ++++---- 3 files changed, 20 insertions(+), 5 deletions(-)
include/linux/mm_inline.h +14 -1
@@ -155,6 +155,11 @@ static inline int folio_lru_refs(structreturn ((flags &LRU_REFS_MASK ) >>LRU_REFS_PGOFF ) + workingset; } +static inline void folio_clear_lru_refs (struct folio *folio) +{ +set_mask_bits (&folio->flags,LRU_REFS_MASK |LRU_REFS_FLAGS ,0 ); +} +static inline int folio_lru_gen (struct folio *folio) {unsigned long flags =READ_ONCE (folio->flags);
@@ -222,6 +227,7 @@ static inline bool lru_gen_add_folio(str {unsigned long seq;unsigned long flags; +unsigned long mask;int gen =folio_lru_gen (folio);int type =folio_is_file_lru (folio);int zone =folio_zonenum (folio);
@@ -257,7 +263,14 @@ static inline bool lru_gen_add_folio(str gen =lru_gen_from_seq (seq); flags = (gen +1UL ) <<LRU_GEN_PGOFF ;/* see the comment on MIN_NR_GENS about PG_active */ -set_mask_bits (&folio->flags,LRU_GEN_MASK |BIT (PG_active), flags); + mask =LRU_GEN_MASK ; +/* +* Don't clear PG_workingset here because it can affect PSI accounting +* if the activation is due to workingset refault. +*/ +if (folio_test_active (folio)) + mask |=LRU_REFS_MASK |BIT (PG_referenced) |BIT (PG_active); +set_mask_bits (&folio->flags, mask, flags);lru_gen_update_size (lruvec, folio,-1 , gen);/* for folio_rotate_reclaimable() */
include/linux/mmzone.h +2 -0
@@ -403,6 +403,8 @@ enum { NR_LRU_GEN_CAPS }; +#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset)) +#define MIN_LRU_BATCH BITS_PER_LONG#define MAX_LRU_BATCH (MIN_LRU_BATCH * 64)
mm/vmscan.c +4 -4
@@ -2625,8 +2625,6 @@ static bool should_clear_pmd_young(void)* shorthand helpers ******************************************************************************/ -#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset)) -#define DEFINE_MAX_SEQ (lruvec) \ unsigned long max_seq = READ_ONCE((lruvec)->lrugen.max_seq)
@@ -4157,8 +4155,10 @@ bool lru_gen_look_around(struct page_vma old_gen =folio_lru_gen (folio);if (old_gen <0 )folio_set_referenced (folio); -else if (old_gen != new_gen) +else if (old_gen != new_gen) { +folio_clear_lru_refs (folio);folio_activate (folio); + } }arch_leave_lazy_mmu_mode ();
@@ -4400,7 +4400,7 @@ static bool isolate_folio(struct lruvec/* see the comment on MAX_NR_TIERS */ if (!folio_test_referenced (folio)) -set_mask_bits (&folio->flags,LRU_REFS_MASK |LRU_REFS_FLAGS ,0 ); +folio_clear_lru_refs (folio);/* for shrink_folio_list() */ folio_clear_reclaim (folio);
Greg Kroah-Hartman
maintainerPATCH 995/1102 mm/mglru: optimize deactivation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Zhao <[email protected]>
[ Upstream commit cc8ec7be78ffaa29f6e0781fd12602974970eb53 ]
Do not shuffle a folio in the deactivation paths if it is already in the oldest generation. This reduces the LRU lock contention.
Before this patch, the contention is reproducible by FIO, e.g.,
fio -filename=/dev/nvme1n1p2 -direct=0 -thread -size=1024G \
-rwmixwrite=30 --norandommap --randrepeat=0 -ioengine=sync \
-bs=4k -numjobs=400 -runtime=25000 --time_based \
-group_reporting -name=mglru98.96%--_raw_spin_lock_irqsave
folio_lruvec_lock_irqsave
|
--98.78%--folio_batch_move_lru
|
--98.63%--deactivate_file_folio
mapping_try_invalidate
invalidate_mapping_pages
invalidate_bdev
blkdev_common_ioctl
blkdev_ioctlAfter this patch, deactivate_file_folio() bails out early without taking the LRU lock.
A side effect is that a folio can be left at the head of the oldest generation, rather than the tail. If reclaim happens at the same time, it cannot reclaim this folio immediately. Since there is no known correlation between truncation and reclaim, this side effect is considered insignificant.
Stable-dep-of: f7e698e326b2 ("mm/mglru: fix and remove redundant unevictable folio handling")
--- mm/swap.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- mm/vmscan.c | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 16 deletions(-)
mm/swap.c +41 -8
@@ -411,7 +411,8 @@ static void __lru_cache_activate_folio(s }#ifdef CONFIG_LRU_GEN -static void folio_inc_refs (struct folio *folio) + +static void lru_gen_inc_refs (struct folio *folio) {unsigned long new_flags, old_flags =READ_ONCE (folio->flags);
@@ -438,10 +439,34 @@ static void folio_inc_refs(struct folio new_flags |= old_flags & ~LRU_REFS_MASK; }while (!try_cmpxchg (&folio->flags, &old_flags, new_flags)); } -#else -static void folio_inc_refs (struct folio *folio) + +static bool lru_gen_clear_refs (struct folio *folio) { +struct lru_gen_folio *lrugen; +int gen =folio_lru_gen (folio); +int type =folio_is_file_lru (folio); + +if (gen <0 ) +return true; + +set_mask_bits (&folio->flags,LRU_REFS_MASK |LRU_REFS_FLAGS ,0 ); + + lrugen = &folio_lruvec (folio)->lrugen; +/* whether can do without shuffling under the LRU lock */ +return gen ==lru_gen_from_seq (READ_ONCE (lrugen->min_seq[type])); } + +#else /* !CONFIG_LRU_GEN */ + +static void lru_gen_inc_refs (struct folio *folio) +{ +} + +static bool lru_gen_clear_refs (struct folio *folio) +{ +return false; +} +#endif /* CONFIG_LRU_GEN */ /**
@@ -460,7 +485,7 @@ static void folio_inc_refs(struct foliovoid folio_mark_accessed (struct folio *folio) {if (lru_gen_enabled ()) { -folio_inc_refs (folio); +lru_gen_inc_refs (folio);return ; }
@@ -556,7 +581,7 @@ void folio_add_lru_vma(struct folio *fol*/ static void lru_deactivate_file (struct lruvec *lruvec,struct folio *folio) { -bool active =folio_test_active (folio); +bool active =folio_test_active (folio) ||lru_gen_enabled ();long nr_pages =folio_nr_pages (folio);if (folio_test_unevictable (folio))
@@ -621,7 +646,10 @@ static void lru_lazyfree(struct lruvec *lruvec_del_folio (lruvec, folio);folio_clear_active (folio); -folio_clear_referenced (folio); +if (lru_gen_enabled ()) +lru_gen_clear_refs (folio); +else +folio_clear_referenced (folio);/* * Lazyfree folios are clean anonymous folios. They have * the swapbacked flag cleared, to distinguish them from normal
@@ -689,6 +717,9 @@ void deactivate_file_folio(struct folioif (folio_test_unevictable (folio) || !folio_test_lru (folio))return ; +if (lru_gen_enabled () &&lru_gen_clear_refs (folio)) +return ; +folio_batch_add_and_move (folio, lru_deactivate_file); }
@@ -702,8 +733,10 @@ void deactivate_file_folio(struct folio*/ void folio_deactivate (struct folio *folio) { -if (folio_test_unevictable (folio) || !folio_test_lru (folio) || - !(folio_test_active (folio) ||lru_gen_enabled ())) +if (folio_test_unevictable (folio) || !folio_test_lru (folio)) +return ; + +if (lru_gen_enabled () ?lru_gen_clear_refs (folio) : !folio_test_active (folio))return ;folio_batch_add_and_move (folio, lru_deactivate);
mm/vmscan.c +30 -8
@@ -4313,7 +4313,6 @@ static bool sort_folio(struct lruvec *lr int tier_idx) {bool success; -bool dirty, writeback;int gen =folio_lru_gen (folio);int type =folio_is_file_lru (folio);int zone =folio_zonenum (folio);
@@ -4359,8 +4358,9 @@ static bool sort_folio(struct lruvec *lrreturn true; } - dirty =folio_test_dirty (folio); - writeback =folio_test_writeback (folio); +bool dirty =folio_test_dirty (folio); +bool writeback =folio_test_writeback (folio); +if (type ==LRU_GEN_FILE && dirty) { sc->nr.file_taken += delta;if (!writeback)
@@ -4627,12 +4627,23 @@ retry: type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);list_for_each_entry_safe_reverse (folio, next, &list, lru) { +DEFINE_MIN_SEQ (lruvec); +if (!folio_evictable (folio)) {list_del (&folio->lru);folio_putback_lru (folio);continue ; } +/* retry folios that may have missed folio_rotate_reclaimable() */ +if (!skip_retry && !folio_test_active (folio) && !folio_mapped (folio) && + !folio_test_dirty (folio) && !folio_test_writeback (folio)) { +if (!folio_test_referenced (folio) && !folio_test_locked (folio)) { +list_move (&folio->lru, &clean); +continue ; + } + } +if (folio_test_reclaim (folio) && (folio_test_dirty (folio) ||folio_test_writeback (folio))) {/* restore LRU_REFS_FLAGS cleared by isolate_folio() */
@@ -4644,14 +4655,25 @@ retry:if (skip_retry ||folio_test_active (folio) ||folio_test_referenced (folio) ||folio_mapped (folio) ||folio_test_locked (folio) ||folio_test_dirty (folio) ||folio_test_writeback (folio)) { +unsigned long seq =READ_ONCE (lruvec->lrugen.max_seq); +/* don't add rejected folios to the oldest generation */ -set_mask_bits (&folio->flags,LRU_REFS_MASK |LRU_REFS_FLAGS , -BIT (PG_active)); +if (!folio_test_active (folio)) { +if ((type ==LRU_GEN_ANON && !folio_test_swapcache (folio)) || + (folio_test_reclaim (folio) && + (folio_test_dirty (folio) ||folio_test_writeback (folio)))) + seq--; +else if (min_seq[type] +MIN_NR_GENS < seq) + seq = min_seq[type] +1 ; +else + seq = min_seq[type]; + } + +if (seq == min_seq[type]) +set_mask_bits (&folio->flags,LRU_REFS_MASK |LRU_REFS_FLAGS , +BIT (PG_active));continue ; } - -/* retry folios that may have missed folio_rotate_reclaimable() */ -list_move (&folio->lru, &clean); }spin_lock_irq (&lruvec->lru_lock);
Greg Kroah-Hartman
maintainerPATCH 996/1102 mm/mglru: use the common routine for dirty/writeback reactivation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kairui Song <[email protected]>
[ Upstream commit 75d4c3f5fb980de1b620adede47e43dff4d6a5f3 ]
Currently MGLRU will move the dirty writeback folios to the second oldest gen instead of reactivate them like the classical LRU. This might help to reduce the LRU contention as it skipped the isolation. But as a result we will see these folios at the LRU tail more frequently leading to inefficient reclaim.
Besides, the dirty / writeback check after isolation in shrink_folio_list is more accurate and covers more cases. So instead, just drop the special handling for dirty writeback, use the common routine and re-activate it like the classical LRU.
This should in theory improve the scan efficiency. These folios will be rotated back to LRU tail once writeback is done so there is no risk of hotness inversion. And now each reclaim loop will have a higher success rate. This also prepares for unifying the writeback and throttling mechanism with classical LRU, we keep these folios far from tail so detecting the tail batch will have a similar pattern with classical LRU.
The micro optimization that avoids LRU contention by skipping the isolation is gone, which should be fine. Compared to IO and writeback cost, the isolation overhead is trivial.
And using the common routine also keeps the folio's referenced bits (tier bits), which could improve metrics in the long term. Also no more need to clean reclaim bit as the common routine will make use of it.
Note the common routine updates a few throttling and writeback counters, which are not used, and never have been for the MGLRU case. We will start making use of these in later commits.
Stable-dep-of: f7e698e326b2 ("mm/mglru: fix and remove redundant unevictable folio handling")
--- mm/vmscan.c | 21 --------------------- 1 file changed, 21 deletions(-)
mm/vmscan.c +0 -21
@@ -4358,23 +4358,6 @@ static bool sort_folio(struct lruvec *lrreturn true; } -bool dirty =folio_test_dirty (folio); -bool writeback =folio_test_writeback (folio); - -if (type ==LRU_GEN_FILE && dirty) { - sc->nr.file_taken += delta; -if (!writeback) - sc->nr.unqueued_dirty += delta; - } - -/* waiting for writeback */ -if (folio_test_locked (folio) || writeback || - (type ==LRU_GEN_FILE && dirty)) { - gen =folio_inc_gen (lruvec, folio, true); -list_move (&folio->lru, &lrugen->folios[gen][type][zone]); -return true; - } -return false; }
@@ -4402,10 +4385,6 @@ static bool isolate_folio(struct lruvecif (!folio_test_referenced (folio))folio_clear_lru_refs (folio); -/* for shrink_folio_list() */ -folio_clear_reclaim (folio); -folio_clear_referenced (folio); - success =lru_gen_del_folio (lruvec, folio, true);VM_WARN_ON_ONCE_FOLIO (!success, folio);
Re: [PATCH 6.12 0996/1102] mm/mglru: use the common routine for dirty/writeback reactivation
Quoted 94 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kairui Song <[email protected]>
[ Upstream commit
75d4c3f5fb980de1b620adede47e43dff4d6a5f3]Currently MGLRU will move the dirty writeback folios to the second oldest gen instead of reactivate them like the classical LRU. This might help to reduce the LRU contention as it skipped the isolation. But as a result we will see these folios at the LRU tail more frequently leading to inefficient reclaim.
Besides, the dirty / writeback check after isolation in
shrink_folio_listis more accurate and covers more cases. So instead, just drop the special handling for dirty writeback, use the common routine and re-activate it like the classical LRU.This should in theory improve the scan efficiency. These folios will be rotated back to LRU tail once writeback is done so there is no risk of hotness inversion. And now each reclaim loop will have a higher success rate. This also prepares for unifying the writeback and throttling mechanism with classical LRU, we keep these folios far from tail so detecting the tail batch will have a similar pattern with classical LRU.
The micro optimization that avoids LRU contention by skipping the isolation is gone, which should be fine. Compared to IO and writeback cost, the isolation overhead is trivial.
And using the common routine also keeps the folio's referenced bits (tier bits), which could improve metrics in the long term. Also no more need to clean reclaim bit as the common routine will make use of it.
Note the common routine updates a few throttling and writeback counters, which are not used, and never have been for the MGLRU case. We will start making use of these in later commits.
Signed-off-by Kairui Song <[email protected]>Reviewed-by Axel Rasmussen <[email protected]>Reviewed-by Barry Song <[email protected]>Reviewed-by Baolin Wang <[email protected]>Cc Chen Ridong <[email protected]>Cc Chris Li <[email protected]>Cc David Hildenbrand <[email protected]>Cc David Stevens <[email protected]>Cc Johannes Weiner <[email protected]>Cc Kalesh Singh <[email protected]>Cc Leno Hou <[email protected]>Cc Lorenzo Stoakes <[email protected]>Cc Michal Hocko <[email protected]>Cc Shakeel Butt <[email protected]>Cc Suren Baghdasaryan <[email protected]>Cc Vernon Yang <[email protected]>Cc Wei Xu <[email protected]>Cc Yafang <[email protected]>Cc Yuanchu Xie <[email protected]>Cc Yu Zhao <[email protected]>Signed-off-by Andrew Morton <[email protected]>Stable-dep-of:
f7e698e326b2("mm/mglru: fix and remove redundant unevictable folio handling")Signed-off-by Sasha Levin <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- mm/vmscan.c | 21 --------------------- 1 file changed, 21 deletions(-)mm/vmscan.c +0
-17@@ -4358,23 +4358,6 @@ static bool sort_folio(struct lruvec *lrreturn true; } -bool dirty =folio_test_dirty (folio); -bool writeback =folio_test_writeback (folio); - -if (type ==LRU_GEN_FILE && dirty) { - sc->nr.file_taken += delta; -if (!writeback) - sc->nr.unqueued_dirty += delta; - } - -/* waiting for writeback */ -if (folio_test_locked (folio) || writeback || - (type ==LRU_GEN_FILE && dirty)) { - gen =folio_inc_gen (lruvec, folio, true); -list_move (&folio->lru, &lrugen->folios[gen][type][zone]); -return true; - } -return false; }
mm/vmscan.c static bool isolate_folio(struct lruvec · line 4407
Quoted 6 lines
mm/vmscan.c +0
-3@@ -4402,10 +4385,6 @@ static bool isolate_folio(struct lruvecif (!folio_test_referenced (folio))folio_clear_lru_refs (folio); -/* for shrink_folio_list() */ -folio_clear_reclaim (folio); -folio_clear_referenced (folio);
^^^^
Hi Greg/Sasha,
I checked the AI-assisted report for 0ad8312612a0 against the older 6.12.y MGLRU reference-state protocol.
Upstream has, after 4d5d14a01e2c:
if (!sc->may_unmap && folio_mapped(folio))
goto keep_locked;
/* Continue into common dirty and writeback handling. */
6.12.y has:
if (!sc->may_unmap && folio_mapped(folio))
goto keep_locked;
if (lru_gen_enabled() && !ignore_references &&
folio_mapped(folio) && folio_test_referenced(folio))
goto keep_locked;The backport removes folio_clear_referenced() as upstream 75d4c3f5fb98 does, but upstream first applied:
4d5d14a01e2c9091b128fb46e1d07475e9a7bb72
("mm/mglru: rework workingset protection")
That redesign removed shrink_folio_list()'s old PG_referenced shortcut. The destination retains the shortcut, so a referenced mapped folio can be kept before it reaches the common dirty/writeback handling which the posted patch was intended to use.
The missing prerequisite is a broad workingset change rather than a small stable dependency.
Maybe we should drop this series of commits ?
thanks,
Harshit
Quoted 7 lines
mm/vmscan.c +0
-1- success =lru_gen_del_folio (lruvec, folio, true);VM_WARN_ON_ONCE_FOLIO (!success, folio);
Sasha Levin
maintainerRe: [PATCH 6.12 0996/1102] mm/mglru: use the common routine for dirty/writeback reactivation
Quoted 5 lines · Harshit Mogalapalli
The backport removes
folio_clear_referenced()as upstream75d4c3f5fb98does, but upstream first applied:
4d5d14a01e2c9091b128fb46e1d07475e9a7bb72
("mm/mglru: rework workingset protection")
[...]
Quoted 1 line
Maybe we should drop this series of commits ?
Dropped from 6.12, thanks.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 997/1102 mm/mglru: fix and remove redundant unevictable folio handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kairui Song <[email protected]>
[ Upstream commit f7e698e326b239a91ea15844817551921209e826 ]
sort_folio() has a shortcut for moving folios that are no longer evictable but are still sitting on a generation list. However, this shortcut is buggy. It does not follow the PG_lru usage convention, and it has a more serious issue.
Unevictable folios are not threaded on lists[LRU_UNEVICTABLE], so that folio->lru can be reused to hold folio->mlock_count (see the comment in lruvec_init()). Hence lruvec_add_folio() skips the list_add() for them, and every other place that turns a folio unevictable initialises mlock_count explicitly: lru_add() sets it to 0, __mlock_folio() and __mlock_new_folio() set it to !!folio_test_mlocked(folio). sort_folio() sets nothing, and the lru_gen_del_folio() right above it may have already poisoned folio->lru via list_del(), so mlock_count ends up aliasing LIST_POISON2, which reads as 0x122, i.e. 290. The result is user visible. On munlock, __munlock_folio() decrements that bogus count, finds it still non-zero and bails out before clearing PG_mlocked, so the folio remains unevictable and the Mlocked accounting stays inflated until the folio is freed.
The shortcut also touches the LRU flags in the wrong order. It calls lru_gen_del_folio() while PG_lru is still set, so a concurrent folio_test_clear_lru() (e.g. compaction, folio_isolate_lru()) can succeed on a folio that has already been taken off the generation list, which may lead to unexpected behavior.
So fix it by isolating them as common folios and letting the generic shrink path cull them. This matches the classical LRU behavior, and there should be no visible effect on the generic eviction or isolation behavior.
There is no performance concern either, such a folio goes through this once, and then it is off the generation lists for good.
--- mm/vmscan.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-)
mm/vmscan.c +5 -14
@@ -4312,7 +4312,6 @@ void lru_gen_soft_reclaim(struct mem_cgrstatic bool sort_folio (struct lruvec *lruvec,struct folio *folio,struct scan_control *sc,int tier_idx) { -bool success;int gen =folio_lru_gen (folio);int type =folio_is_file_lru (folio);int zone =folio_zonenum (folio);
@@ -4323,15 +4322,9 @@ static bool sort_folio(struct lruvec *lrVM_WARN_ON_ONCE_FOLIO (gen >=MAX_NR_GENS , folio); -/* unevictable */ -if (!folio_evictable (folio)) { - success =lru_gen_del_folio (lruvec, folio, true); -VM_WARN_ON_ONCE_FOLIO (!success, folio); -folio_set_unevictable (folio); -lruvec_add_folio (lruvec, folio); -__count_vm_events (UNEVICTABLE_PGCULLED , delta); -return true; - } +/* unevictable: let it through and the generic path will cull it */ +if (!folio_evictable (folio)) +return false;/* promoted */ if (gen !=lru_gen_from_seq (lrugen->min_seq[type])) {
@@ -4608,11 +4601,9 @@ retry:list_for_each_entry_safe_reverse (folio, next, &list, lru) {DEFINE_MIN_SEQ (lruvec); -if (!folio_evictable (folio)) { -list_del (&folio->lru); -folio_putback_lru (folio); +/* move_folios_to_lru() culls unevictable folios via folio_putback_lru() */ +if (!folio_evictable (folio))continue ; - }/* retry folios that may have missed folio_rotate_reclaimable() */ if (!skip_retry && !folio_test_active(folio) && !folio_mapped(folio) &&
Greg Kroah-Hartman
maintainerPATCH 998/1102 mm/migrate_device: clear stale mapping after freeing swapcache
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arvind Yadav <[email protected]>
[ Upstream commit 34a00895d032a414830d41106a09329ae6c251b6 ]
__migrate_device_pages() reads the folio mapping before calling folio_free_swap(). When folio_free_swap() succeeds, the folio is removed from the swap cache, but the saved mapping still points to swap_space.
Passing the stale mapping to folio_migrate_mapping() makes it use the mapped-folio path for a folio that is no longer in swapcache. It can then operate on swap_space.i_pages with invalid reference accounting, eventually triggering a folio reference count BUG.
After a successful split, nr still contains the number of pages in the original large folio, although each resulting page is now a separate order-0 folio. Reset nr to 1 so each split folio is processed separately, including its own swapcache removal and mapping lookup.
Refresh the saved mapping after folio_free_swap() so the current folio state is used during migration.
--- mm/migrate_device.c | 6 ++++++ 1 file changed, 6 insertions(+)
mm/migrate_device.c +6 -0
@@ -746,6 +746,12 @@ static void __migrate_device_pages(unsig src_pfns[i] &= ~MIGRATE_PFN_MIGRATE ;continue ; } + +/* +* folio_free_swap() removed the folio from the swap +* cache. Refresh the saved mapping before migration. +*/ + mapping =folio_mapping (folio); } }else if (folio_is_zone_device (newfolio)) {/*
Greg Kroah-Hartman
maintainerPATCH 999/1102 mm/slab: move and refactor __kmem_cache_alias()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vlastimil Babka <[email protected]>
[ Upstream commit 8598351edc42f38d2a1eaed9abca39c98e7b0bbf ]
Move __kmem_cache_alias() to slab_common.c since it's called by __kmem_cache_create_args() and calls find_mergeable() that both are in this file. We can remove two slab.h declarations and make them static. Instead declare sysfs_slab_alias() from slub.c so that __kmem_cache_alias() can keep calling it.
Add args parameter to __kmem_cache_alias() and find_mergeable() instead of align and ctor. With that we can also move the checks for usersize and sheaf_capacity there from __kmem_cache_create_args() and make the result more symmetric with slab_unmergeable().
No functional changes intended.
Stable-dep-of: 7e98f8563956 ("mm/slub: fix missing debugfs entries for caches created before sysfs init")
--- mm/slab.h | 8 +++----- mm/slab_common.c | 41 +++++++++++++++++++++++++++++++++-------- mm/slub.c | 29 +---------------------------- 3 files changed, 37 insertions(+), 41 deletions(-)
mm/slab.h +3 -5
@@ -319,9 +319,12 @@ struct kmem_cache {#define SLAB_SUPPORTS_SYSFS 1void sysfs_slab_unlink (struct kmem_cache *s);void sysfs_slab_release (struct kmem_cache *s); +int sysfs_slab_alias (struct kmem_cache *s,const char *name);#else static inline void sysfs_slab_unlink (struct kmem_cache *s) { }static inline void sysfs_slab_release (struct kmem_cache *s) { } +static inline int sysfs_slab_alias (struct kmem_cache *s,const char *name) + {return 0 ; }#endif void *fixup_red_left (struct kmem_cache *s,void *p);
@@ -442,11 +445,6 @@ extern void create_boot_cache(struct kme unsigned int useroffset, unsigned int usersize);int slab_unmergeable (struct kmem_cache *s); -struct kmem_cache *find_mergeable (unsigned size,unsigned align, -slab_flags_t flags,const char *name,void (*ctor)(void *)); -struct kmem_cache * -__kmem_cache_alias (const char *name,unsigned int size,unsigned int align, -slab_flags_t flags,void (*ctor)(void *));slab_flags_t kmem_cache_flags (slab_flags_t flags,const char *name);
mm/slab_common.c +33 -8
74 lines not on this page · raw message
mm/slub.c +1 -28
@@ -333,11 +333,8 @@ enum track_item { TRACK_ALLOC, TRACK_FRE#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add (struct kmem_cache *); -static int sysfs_slab_alias (struct kmem_cache *,const char *);#else static inline int sysfs_slab_add (struct kmem_cache *s) {return 0 ; } -static inline int sysfs_slab_alias (struct kmem_cache *s,const char *p) - {return 0 ; }#endif #if defined(CONFIG_DEBUG_FS ) && defined(CONFIG_SLUB_DEBUG )
@@ -6061,30 +6058,6 @@ void __init kmem_cache_init_late(void)#endif } -struct kmem_cache * -__kmem_cache_alias (const char *name,unsigned int size,unsigned int align, -slab_flags_t flags,void (*ctor)(void *)) -{ -struct kmem_cache *s; - - s =find_mergeable (size, align, flags, name, ctor); -if (s) { -if (sysfs_slab_alias (s, name)) -return NULL ; - - s->refcount++; - -/* -* Adjust the object sizes so that we clear -* the complete object on kzalloc. -*/ - s->object_size =max (s->object_size, size); - s->inuse =max (s->inuse,ALIGN (size,sizeof (void *))); - } - -return s; -} -int do_kmem_cache_create (struct kmem_cache *s,const char *name,unsigned int size,struct kmem_cache_args *args,slab_flags_t flags)
@@ -7250,7 +7223,7 @@ struct saved_alias {static struct saved_alias *alias_list; -static int sysfs_slab_alias (struct kmem_cache *s,const char *name) +int sysfs_slab_alias (struct kmem_cache *s,const char *name) {struct saved_alias *al;
Greg Kroah-Hartman
maintainerPATCH 1000/1102 mm/slub: fix missing debugfs entries for caches created before sysfs init
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Xiasong <[email protected]>
[ Upstream commit 7e98f856395618011c517f767fb80ac3fe90de2b ]
slab_debugfs_init() creates the slab debugfs root at device initcall time, while slab_sysfs_init() moves slab_state to FULL at late initcall time. SLAB_STORE_USER caches created in this window miss their debugfs entries because do_kmem_cache_create() skips debugfs_slab_add() when slab_state <= UP. This was observed with MPTCP's request_sock_subflow_v6 cache, whose slab debugfs directory was missing.
The affected window is:
slab_debugfs_init()
slab_debugfs_root = debugfs_create_dir(...)
list_for_each_entry(s, &slab_caches, list)
debugfs_slab_add(s)
kmem_cache_create(..., SLAB_STORE_USER, ...)
do_kmem_cache_create()
if (slab_state <= UP)
return without debugfs entries
slab_sysfs_init()
slab_state = FULL
Initialize the debugfs root and add debugfs entries while holding slab_mutex, walking slab_caches exactly once and handling both sysfs and debugfs entries in the same pass. This gives the sysfs and debugfs initialization an explicit order and prevents caches from being created between the debugfs scan and slab_state reaching FULL.
Gate the new slab_late_init() on either sysfs or debugfs being enabled, with the slab_kset creation and alias_list processing factored into helpers that have empty no-sysfs variants, as suggested by Vlastimil Babka. On slab_kset_init() failure, slab_state stays below FULL so kmem_cache_create() keeps taking the early-boot path, matching prior behavior.
Guard debugfs_slab_release() against an uninitialized debugfs root, since the root is now created later and a cache may be released before it exists.
--- mm/slub.c | 76 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 28 deletions(-)
mm/slub.c +48 -28
@@ -333,14 +333,20 @@ enum track_item { TRACK_ALLOC, TRACK_FRE#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add (struct kmem_cache *); +static int __init slab_kset_init (void ); +static void __init slab_sysfs_process_aliases (void );#else static inline int sysfs_slab_add (struct kmem_cache *s) {return 0 ; } +static inline int slab_kset_init (void ) {return 0 ; } +static inline void slab_sysfs_process_aliases (void ) { }#endif #if defined(CONFIG_DEBUG_FS ) && defined(CONFIG_SLUB_DEBUG )static void debugfs_slab_add (struct kmem_cache *); +static void __init slab_debugfs_root_init (void );#else static inline void debugfs_slab_add (struct kmem_cache *s) { } +static inline void slab_debugfs_root_init (void ) { }#endif enum stat_item {
@@ -7247,28 +7253,20 @@ int sysfs_slab_alias(struct kmem_cache *return 0 ; } -static int __init slab_sysfs_init (void ) +static int __init slab_kset_init (void ) { -struct kmem_cache *s; -int err; - -mutex_lock (&slab_mutex); - slab_kset =kset_create_and_add ("slab" ,NULL , kernel_kobj);if (!slab_kset) { -mutex_unlock (&slab_mutex);pr_err ("Cannot register slab subsystem.\n" );return -ENOMEM ; } - slab_state =FULL ; +return 0 ; +} -list_for_each_entry (s, &slab_caches, list) { - err =sysfs_slab_add (s); -if (err) -pr_err ("SLUB: Unable to add boot slab %s to sysfs\n" , - s->name); - } +static void __init slab_sysfs_process_aliases (void ) +{ +int err;while (alias_list) {struct saved_alias *al = alias_list;
@@ -7280,12 +7278,41 @@ static int __init slab_sysfs_init(void) al->name); kfree(al); } +} +#endif /* SLAB_SUPPORTS_SYSFS */ + +#if defined(SLAB_SUPPORTS_SYSFS ) || \ + (defined(CONFIG_SLUB_DEBUG ) && defined(CONFIG_DEBUG_FS )) +static int __init slab_late_init (void ) +{ +struct kmem_cache *s; +int err; +mutex_lock (&slab_mutex); + + err =slab_kset_init (); +if (err) + goto out; + +slab_debugfs_root_init (); + slab_state =FULL ; + +list_for_each_entry (s, &slab_caches, list) { +if (sysfs_slab_add (s)) +pr_err ("SLUB: Unable to add boot slab %s to sysfs\n" , + s->name); + +if (s->flags &SLAB_STORE_USER ) +debugfs_slab_add (s); + } + +slab_sysfs_process_aliases (); +out:mutex_unlock (&slab_mutex); -return 0 ; +return err; } -late_initcall (slab_sysfs_init); -#endif /* SLAB_SUPPORTS_SYSFS */ +late_initcall (slab_late_init); +#endif #if defined(CONFIG_SLUB_DEBUG ) && defined(CONFIG_DEBUG_FS )static int slab_debugfs_show (struct seq_file *seq,void *v)
@@ -7483,23 +7510,16 @@ static void debugfs_slab_add(struct kmemvoid debugfs_slab_release (struct kmem_cache *s) { +if (unlikely (!slab_debugfs_root)) +return ; +debugfs_lookup_and_remove (s->name, slab_debugfs_root); } -static int __init slab_debugfs_init (void ) +static void __init slab_debugfs_root_init (void ) { -struct kmem_cache *s; - slab_debugfs_root =debugfs_create_dir ("slab" ,NULL ); - -list_for_each_entry (s, &slab_caches, list) -if (s->flags &SLAB_STORE_USER ) -debugfs_slab_add (s); - -return 0 ; - } -__initcall (slab_debugfs_init);#endif /* * The /proc/slabinfo ABI
Greg Kroah-Hartman
maintainerPATCH 1001/1102 x86/locking: Remove semicolon from "lock" prefix
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uros Bizjak <[email protected]>
[ Upstream commit 023f3290b02552ea006c1a2013e373750d2cbff6 ]
Minimum version of binutils required to compile the kernel is 2.25. This version correctly handles the "lock" prefix, so it is possible to remove the semicolon, which was used to support ancient versions of GNU as.
Due to the semicolon, the compiler considers "lock; insn" as two separate instructions. Removing the semicolon makes asm length calculations more accurate, consequently making scheduling and inlining decisions of the compiler more accurate.
Removing the semicolon also enables assembler checks involving lock prefix. Trying to assemble e.g. "lock andl %eax, %ebx" results in:
Error: expecting lockable instruction after `lock'
Stable-dep-of: d824ed130768 ("x86/locking: Use sfence for wmb() if SSE is available")
--- arch/x86/include/asm/alternative.h | 2 +- arch/x86/include/asm/barrier.h | 8 ++++---- arch/x86/include/asm/cmpxchg.h | 4 ++-- arch/x86/include/asm/cmpxchg_32.h | 4 ++-- arch/x86/include/asm/edac.h | 2 +- arch/x86/include/asm/sync_bitops.h | 12 ++++++------ 6 files changed, 16 insertions(+), 16 deletions(-)
arch/x86/include/asm/alternative.h +1 -1
@@ -48,7 +48,7 @@".popsection\n" \ "671:" -#define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; " +#define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock "#else /* ! CONFIG_SMP */ #define LOCK_PREFIX_HERE ""
arch/x86/include/asm/barrier.h +4 -4
@@ -12,11 +12,11 @@*/ #ifdef CONFIG_X86_32 -#define mb () asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "mfence", \ +#define mb () asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "mfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") -#define rmb () asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "lfence", \ +#define rmb () asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") -#define wmb () asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "sfence", \ +#define wmb () asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \ X86_FEATURE_XMM2) ::: "memory", "cc")#else #define __mb () asm volatile("mfence":::"memory")
@@ -50,7 +50,7 @@#define __dma_rmb () barrier()#define __dma_wmb () barrier() -#define __smp_mb () asm volatile("lock; addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc") +#define __smp_mb () asm volatile("lock addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc")#define __smp_rmb () dma_rmb()#define __smp_wmb () barrier()
arch/x86/include/asm/cmpxchg.h +2 -2
@@ -134,7 +134,7 @@ extern void __add_wrong_size(void) __raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)#define __sync_cmpxchg (ptr, old, new, size) \ - __raw_cmpxchg((ptr), (old), (new), (size), "lock; ") + __raw_cmpxchg((ptr), (old), (new), (size), "lock ")#define __cmpxchg_local (ptr, old, new, size) \ __raw_cmpxchg((ptr), (old), (new), (size), "")
@@ -222,7 +222,7 @@ extern void __add_wrong_size(void) __raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX)#define __sync_try_cmpxchg (ptr, pold, new, size) \ - __raw_try_cmpxchg((ptr), (pold), (new), (size), "lock; ") + __raw_try_cmpxchg((ptr), (pold), (new), (size), "lock ")#define __try_cmpxchg_local (ptr, pold, new, size) \ __raw_try_cmpxchg((ptr), (pold), (new), (size), "")
arch/x86/include/asm/cmpxchg_32.h +2 -2
@@ -103,7 +103,7 @@ static __always_inline bool __try_cmpxch static __always_inline u64 arch_cmpxchg64(volatile u64 *ptr, u64 old, u64 new) { -return __arch_cmpxchg64_emu (ptr, old, new,LOCK_PREFIX_HERE ,"lock; " ); +return __arch_cmpxchg64_emu (ptr, old, new,LOCK_PREFIX_HERE ,"lock " ); }#define arch_cmpxchg64 arch_cmpxchg64
@@ -136,7 +136,7 @@ static __always_inline u64 arch_cmpxchg6 static __always_inline bool arch_try_cmpxchg64(volatile u64 *ptr, u64 *oldp, u64 new) { -return __arch_try_cmpxchg64_emu (ptr, oldp, new,LOCK_PREFIX_HERE ,"lock; " ); +return __arch_try_cmpxchg64_emu (ptr, oldp, new,LOCK_PREFIX_HERE ,"lock " ); }#define arch_try_cmpxchg64 arch_try_cmpxchg64
arch/x86/include/asm/edac.h +1 -1
@@ -13,7 +13,7 @@ static inline void edac_atomic_scrub(voi* are interrupt, DMA and SMP safe. */ for (i =0 ; i < size /4 ; i++, virt_addr++) - asm volatile("lock; addl $0, %0"::"m" (*virt_addr)); + asm volatile("lock addl $0, %0"::"m" (*virt_addr)); }#endif /* _ASM_X86_EDAC_H */
arch/x86/include/asm/sync_bitops.h +6 -6
@@ -31,7 +31,7 @@*/ static inline void sync_set_bit (long nr,volatile unsigned long *addr) { - asm volatile("lock; " __ASM_SIZE(bts) " %1,%0" + asm volatile("lock " __ASM_SIZE(bts) " %1,%0" : "+m" (ADDR) : "Ir" (nr) : "memory");
@@ -49,7 +49,7 @@ static inline void sync_set_bit(long nr,*/ static inline void sync_clear_bit (long nr,volatile unsigned long *addr) { - asm volatile("lock; " __ASM_SIZE(btr) " %1,%0" + asm volatile("lock " __ASM_SIZE(btr) " %1,%0" : "+m" (ADDR) : "Ir" (nr) : "memory");
@@ -66,7 +66,7 @@ static inline void sync_clear_bit(long n*/ static inline void sync_change_bit (long nr,volatile unsigned long *addr) { - asm volatile("lock; " __ASM_SIZE(btc) " %1,%0" + asm volatile("lock " __ASM_SIZE(btc) " %1,%0" : "+m" (ADDR) : "Ir" (nr) : "memory");
@@ -82,7 +82,7 @@ static inline void sync_change_bit(long*/ static inline bool sync_test_and_set_bit (long nr,volatile unsigned long *addr) { -return GEN_BINARY_RMWcc ("lock; " __ASM_SIZE(bts), *addr, c,"Ir" , nr); +return GEN_BINARY_RMWcc ("lock " __ASM_SIZE(bts), *addr, c,"Ir" , nr); }/**
@@ -95,7 +95,7 @@ static inline bool sync_test_and_set_bit*/ static inline int sync_test_and_clear_bit (long nr,volatile unsigned long *addr) { -return GEN_BINARY_RMWcc ("lock; " __ASM_SIZE(btr), *addr, c,"Ir" , nr); +return GEN_BINARY_RMWcc ("lock " __ASM_SIZE(btr), *addr, c,"Ir" , nr); }/**
@@ -108,7 +108,7 @@ static inline int sync_test_and_clear_bi*/ static inline int sync_test_and_change_bit (long nr,volatile unsigned long *addr) { -return GEN_BINARY_RMWcc ("lock; " __ASM_SIZE(btc), *addr, c,"Ir" , nr); +return GEN_BINARY_RMWcc ("lock " __ASM_SIZE(btc), *addr, c,"Ir" , nr); }#define sync_test_bit (nr, addr) test_bit(nr, addr)
Greg Kroah-Hartman
maintainerPATCH 1002/1102 x86/locking: Use sfence for wmb() if SSE is available
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yao Zi <[email protected]>
[ Upstream commit d824ed1307680dd482f607b0e707c575f70668c4 ]
When adding cc clobber to wmb()'s definition, the alternative() condition to use sfence was incorrectly raised from X86_FEATURE_XMM to X86_FEATURE_XMM2.
Restore the correct constraint for potential better performance on machines without SSE2.
--- arch/x86/include/asm/barrier.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/include/asm/barrier.h +1 -1
@@ -17,7 +17,7 @@#define rmb () asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \ X86_FEATURE_XMM2) ::: "memory", "cc")#define wmb () asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \ - X86_FEATURE_XMM2) ::: "memory", "cc") + X86_FEATURE_XMM) ::: "memory", "cc")#else #define __mb () asm volatile("mfence":::"memory")#define __rmb () asm volatile("lfence":::"memory")
Greg Kroah-Hartman
maintainerPATCH 1003/1102 xen/balloon: improve accuracy of initial balloon target for dom0
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roger Pau Monne <[email protected]>
[ Upstream commit b13cd24c15d74f6dfcddf0d53ae6efb58d3f0461 ]
The dom0 balloon target set by the toolstack is the value returned by XENMEM_current_reservation. Do the same in the kernel balloon driver and set the current allocation to the value returned by XENMEM_current_reservation. On my test system this causes the kernel balloon driver target to exactly match the value set by the toolstack in xenstore.
Note this approach can be used by both PV and PVH dom0s, as the toolstack always uses XENMEM_current_reservation to set the initial target regardless of the dom0 type.
Message-ID: <[email protected]>
Stable-dep-of: 5d00a11d484e ("x86/xen: fix init of balloon stats again")
--- drivers/xen/balloon.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
drivers/xen/balloon.c +17 -10
@@ -720,7 +720,8 @@ static int __init balloon_add_regions(vostatic int __init balloon_init (void ) {struct task_struct *task; -unsigned long current_pages; +long current_pages =0 ; +domid_t domid =DOMID_SELF ;int rc;if (!xen_domain ())
@@ -728,15 +729,21 @@ static int __init balloon_init(void)pr_info ("Initialising balloon driver\n" ); -if (xen_pv_domain ()) { -if (xen_released_pages >= xen_start_info->nr_pages) - goto underflow; - current_pages =min (xen_start_info->nr_pages - - xen_released_pages, max_pfn); - }else { -if (xen_unpopulated_pages >=get_num_physpages ()) - goto underflow; - current_pages =get_num_physpages () - xen_unpopulated_pages; +if (xen_initial_domain ()) + current_pages =HYPERVISOR_memory_op (XENMEM_current_reservation, + &domid); +if (current_pages <=0 ) { +if (xen_pv_domain ()) { +if (xen_released_pages >= xen_start_info->nr_pages) + goto underflow; + current_pages =min (xen_start_info->nr_pages - + xen_released_pages, max_pfn); + }else { +if (xen_unpopulated_pages >=get_num_physpages ()) + goto underflow; + current_pages =get_num_physpages () - + xen_unpopulated_pages; + } } balloon_stats.current_pages = current_pages;
Greg Kroah-Hartman
maintainerPATCH 1004/1102 x86/xen: fix init of balloon stats again
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roger Pau Monne <[email protected]>
[ Upstream commit 5d00a11d484e7b31a358cd81afebccb4602dbe8c ]
The handling of extra memory regions done in balloon_add_regions() is not correct for PV guests, since the initial target is set to reflect the real memory the system has, not what's described on the memory map, which can be higher if memory != maxmem.
Introduce separate logic for addition vs subtraction in balloon_add_regions() and handle extra regions correctly by adding them to the total amount of pages, instead of subtracting from the current and target pages amounts.
In the common case PV domU/dom0 and PVH dom0 will use the addition path, since the initial target reflects the real assigned memory. HVM and PVH domUs use the subtraction path, since the target is set based on the amount of memory reported in the memory map, without accounting for released regions.
Message-ID: <[email protected]>
--- drivers/xen/balloon.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
drivers/xen/balloon.c +19 -10
@@ -675,7 +675,7 @@ void xen_free_ballooned_pages(unsigned i }EXPORT_SYMBOL (xen_free_ballooned_pages); -static int __init balloon_add_regions (void ) +static int __init balloon_add_regions (bool append) {unsigned long start_pfn, pages;unsigned long pfn, extra_pfn_end;
@@ -699,19 +699,26 @@ static int __init balloon_add_regions(voballoon_append (pfn_to_page (pfn));/* -* Extra regions are accounted for in the physmap, but need -* decreasing from current_pages and target_pages to balloon -* down the initial allocation, because they are already -* accounted for in total_pages. +* There are two different use-cases depending on how the +* initial memory target is fetched. For PVH dom0 and PV the +* target is usually set to reflect the domain assigned memory, +* and hence extra regions need adding. +* +* OTOH for HVM and PVH domU the target is set to the amount of +* RAM reported in the memory map, and hence extra regions need +* subtracting to reflect the real memory usage. */ pages = extra_pfn_end - start_pfn; -if (pages >= balloon_stats.current_pages || - pages >= balloon_stats.target_pages) { +if (append) { + balloon_stats.total_pages += pages; + }else if (pages >= balloon_stats.current_pages || + pages >= balloon_stats.target_pages) {WARN (1 ,"Extra pages underflow current target" );return -ERANGE ; + }else { + balloon_stats.current_pages -= pages; + balloon_stats.target_pages -= pages; } - balloon_stats.current_pages -= pages; - balloon_stats.target_pages -= pages; }return 0 ;
@@ -722,6 +729,7 @@ static int __init balloon_init(void)struct task_struct *task;long current_pages =0 ;domid_t domid =DOMID_SELF ; +bool append = true;int rc;if (!xen_domain ())
@@ -741,6 +749,7 @@ static int __init balloon_init(void) }else {if (xen_unpopulated_pages >=get_num_physpages ()) goto underflow; + append = false; current_pages =get_num_physpages () - xen_unpopulated_pages; }
@@ -763,7 +772,7 @@ static int __init balloon_init(void)register_sysctl_init ("xen/balloon" , balloon_table);#endif - rc =balloon_add_regions (); + rc =balloon_add_regions (append);if (rc)return rc;
Greg Kroah-Hartman
maintainerPATCH 1005/1102 cxl/pci: Remove unnecessary CXL Endpoint handling helper functions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit ca3d1a53e62093d17436abd447463da9c0f4e56b ]
The CXL driver's cxl_handle_endpoint_cor_ras()/cxl_handle_endpoint_ras() are unnecessary helper functions used only for Endpoints. Remove these functions as they are not common for all CXL devices and do not provide value for EP handling.
Rename __cxl_handle_ras to cxl_handle_ras() and __cxl_handle_cor_ras() to cxl_handle_cor_ras().
Stable-dep-of: 29458e62d082 ("cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read")
--- drivers/cxl/core/pci.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-)
drivers/cxl/core/pci.c +8 -18
@@ -650,8 +650,8 @@ err: }EXPORT_SYMBOL_NS_GPL (read_cdat_data,CXL ); -static void __cxl_handle_cor_ras (struct cxl_dev_state *cxlds, -void __iomem *ras_base) +static void cxl_handle_cor_ras (struct cxl_dev_state *cxlds, +void __iomem *ras_base) {void __iomem *addr;u32 status;
@@ -667,11 +667,6 @@ static void __cxl_handle_cor_ras(struct } } -static void cxl_handle_endpoint_cor_ras (struct cxl_dev_state *cxlds) -{ -return __cxl_handle_cor_ras (cxlds, cxlds->regs.ras); -} -/* CXL spec rev3.0 8.2.4.16.1 */ static void header_log_copy (void __iomem *ras_base,u32 *log) {
@@ -693,8 +688,8 @@ static void header_log_copy(void __iomem* Log the state of the RAS status registers and prepare them to log the * next error status. Return 1 if reset needed. */ -static bool __cxl_handle_ras (struct cxl_dev_state *cxlds, -void __iomem *ras_base) +static bool cxl_handle_ras (struct cxl_dev_state *cxlds, +void __iomem *ras_base) {u32 hl[CXL_HEADERLOG_SIZE_U32 ];void __iomem *addr;
@@ -727,11 +722,6 @@ static bool __cxl_handle_ras(struct cxl_return true; } -static bool cxl_handle_endpoint_ras (struct cxl_dev_state *cxlds) -{ -return __cxl_handle_ras (cxlds, cxlds->regs.ras); -} -#ifdef CONFIG_PCIEAER_CXL static void cxl_dport_map_rch_aer (struct cxl_dport *dport)
@@ -810,13 +800,13 @@ EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_static void cxl_handle_rdport_cor_ras (struct cxl_dev_state *cxlds,struct cxl_dport *dport) { -return __cxl_handle_cor_ras (cxlds, dport->regs.ras); +return cxl_handle_cor_ras (cxlds, dport->regs.ras); }static bool cxl_handle_rdport_ras (struct cxl_dev_state *cxlds,struct cxl_dport *dport) { -return __cxl_handle_ras (cxlds, dport->regs.ras); +return cxl_handle_ras (cxlds, dport->regs.ras); }/*
@@ -913,7 +903,7 @@ void cxl_cor_error_detected(struct pci_dif (cxlds->rcd)cxl_handle_rdport_errors (cxlds); -cxl_handle_endpoint_cor_ras (cxlds); +cxl_handle_cor_ras (cxlds, cxlds->regs.ras); } }EXPORT_SYMBOL_NS_GPL (cxl_cor_error_detected,CXL );
@@ -942,7 +932,7 @@ pci_ers_result_t cxl_error_detected(stru* chance the situation is recoverable dump the status of the RAS * capability registers and bounce the active state of the memdev. */ - ue =cxl_handle_endpoint_ras (cxlds); + ue =cxl_handle_ras (cxlds, cxlds->regs.ras); }
Greg Kroah-Hartman
maintainerPATCH 1006/1102 cxl/pci: Remove unnecessary CXL RCH handling helper functions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit eb78ef4d6f0e51243c1ee117f801dbc503e886ab ]
cxl_handle_rdport_cor_ras() and cxl_handle_rdport_ras() are specific to Restricted CXL Host (RCH) handling. Improve readability and maintainability by replacing these and instead using the common cxl_handle_cor_ras() and cxl_handle_ras() functions.
Stable-dep-of: 29458e62d082 ("cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read")
--- drivers/cxl/core/pci.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-)
drivers/cxl/core/pci.c +2 -14
@@ -797,18 +797,6 @@ void cxl_dport_init_ras_reporting(struct }EXPORT_SYMBOL_NS_GPL (cxl_dport_init_ras_reporting,CXL ); -static void cxl_handle_rdport_cor_ras (struct cxl_dev_state *cxlds, -struct cxl_dport *dport) -{ -return cxl_handle_cor_ras (cxlds, dport->regs.ras); -} - -static bool cxl_handle_rdport_ras (struct cxl_dev_state *cxlds, -struct cxl_dport *dport) -{ -return cxl_handle_ras (cxlds, dport->regs.ras); -} -/* * Copy the AER capability registers using 32 bit read accesses. * This is necessary because RCRB AER capability is MMIO mapped. Clear the
@@ -878,9 +866,9 @@ static void cxl_handle_rdport_errors(strpci_print_aer (pdev, severity, &aer_regs);if (severity ==AER_CORRECTABLE ) -cxl_handle_rdport_cor_ras (cxlds, dport); +cxl_handle_cor_ras (cxlds, dport->regs.ras);else - cxl_handle_rdport_ras(cxlds, dport); + cxl_handle_ras(cxlds, dport->regs.ras); }#else
Greg Kroah-Hartman
maintainerPATCH 1007/1102 cxl/pci: Remove CXL VH handling in CONFIG_PCIEAER_CXL conditional blocks from core/pci.c
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Jiang <[email protected]>
[ Upstream commit 7ff8b1d60881c5f97b5ae426e14d2822917d3b69 ]
Create new config CONFIG_CXL_RAS and put all CXL RAS items behind the config. The config will depend on CPER and PCIE AER to build. Move the related VH RAS code from core/pci.c to core/ras.c.
Restricted CXL host (RCH) RAS functions will be moved in a future patch.
[ sashal: Reduced backport -- upstream 7ff8b1d60881c touches 8 file(s), this
backport carries 6. Not backported here:
drivers/cxl/cxl.h
drivers/cxl/cxlpci.h
drivers/cxl/Kconfig
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 29458e62d082 ("cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read")
--- drivers/cxl/core/Makefile | 2 drivers/cxl/core/core.h | 12 + drivers/cxl/core/pci.c | 296 --------------------------------------------- drivers/cxl/core/ras.c | 186 ++++++++++++++++++++++++++++ drivers/cxl/core/ras_rch.c | 121 ++++++++++++++++++ tools/testing/cxl/Kbuild | 2 6 files changed, 323 insertions(+), 296 deletions(-) create mode 100644 drivers/cxl/core/ras.c create mode 100644 drivers/cxl/core/ras_rch.c
drivers/cxl/core/Makefile +2 -0
@@ -14,5 +14,7 @@ cxl_core-y += pci.o cxl_core-y += hdm.o cxl_core-y += pmu.o cxl_core-y += cdat.o +cxl_core-y += ras.o +cxl_core-$(CONFIG_PCIEAER_CXL) += ras_rch.o cxl_core-$(CONFIG_TRACING) += trace.o cxl_core-$(CONFIG_CXL_REGION) += region.o
drivers/cxl/core/core.h +12 -0
@@ -112,4 +112,16 @@ bool cxl_need_node_perf_attrs_update(intint cxl_port_get_switch_dport_bandwidth (struct cxl_port *port,struct access_coordinate *c); +struct cxl_dev_state ; +bool cxl_handle_ras (struct cxl_dev_state *cxlds,void __iomem *ras_base); +void cxl_handle_cor_ras (struct cxl_dev_state *cxlds,void __iomem *ras_base); + +#ifdef CONFIG_PCIEAER_CXL +void cxl_dport_map_rch_aer (struct cxl_dport *dport); +void cxl_disable_rch_root_ints (struct cxl_dport *dport); +void cxl_handle_rdport_errors (struct cxl_dev_state *cxlds); +#else +static inline void cxl_handle_rdport_errors (struct cxl_dev_state *cxlds) { } +#endif +#endif /* __CXL_CORE_H__ */
drivers/cxl/core/pci.c +0 -296
302 lines not on this page · raw message
drivers/cxl/core/ras.c new file +186 -0
186 lines not on this page · raw message
drivers/cxl/core/ras_rch.c new file +121 -0
121 lines not on this page · raw message
tools/testing/cxl/Kbuild +2 -0
@@ -61,6 +61,8 @@ cxl_core-y += $(CXL_CORE_SRC)/pci.o cxl_core-y += $(CXL_CORE_SRC)/hdm.o cxl_core-y += $(CXL_CORE_SRC)/pmu.o cxl_core-y += $(CXL_CORE_SRC)/cdat.o +cxl_core-y += $(CXL_CORE_SRC)/ras.o +cxl_core-$(CONFIG_PCIEAER_CXL) += $(CXL_CORE_SRC)/ras_rch.o cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o cxl_core-y += config_check.o
Greg Kroah-Hartman
maintainerPATCH 1008/1102 cxl/ras: Fix cxl_rch_get_aer_info() out-of-bounds AER register read
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit 29458e62d0829cbc99435f3e44fd560f9bbf1da7 ]
cxl_rch_get_aer_info() copies the RCH Downstream Port AER capability from the RCRB MMIO block using a readl() loop bounded by sizeof(struct aer_capability_regs). This struct is a software layout and its embedded struct pcie_tlp_log is larger than the on-wire AER capability. As a result the loop reads past the mapped AER register block.
The over-read also populates the software-only tail fields including header_log.header_len. An out-of-range header_len passed to pcie_print_tlp_log() can then loop past the header log buffer and cause a second out-of-bounds read.
The read was correct when introduced, but struct pcie_tlp_log has since grown (Header Log and TLP Prefix Log sizes, header_len and flit fields), so sizeof(struct aer_capability_regs) no longer matches the physical AER capability.
Bound the read to the physical AER registers, header through the 16 byte Header Log. Zero the destination first so the software-only fields are deterministic.
--- drivers/cxl/core/ras_rch.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
drivers/cxl/core/ras_rch.c +16 -1
@@ -58,13 +58,28 @@ void cxl_disable_rch_root_ints(struct cxstatic bool cxl_rch_get_aer_info (void __iomem *aer_base,struct aer_capability_regs *aer_regs) { -int read_cnt =sizeof (struct aer_capability_regs ) /sizeof (u32); +/* +* Bound the copy to the physically-defined AER registers (header +* through the 16-byte Header Log). struct aer_capability_regs is a +* software layout whose embedded struct pcie_tlp_log is larger than +* the on-wire AER capability; copying sizeof(*aer_regs) would +* over-read the RCRB-mapped MMIO block. +*/ +int read_cnt = (PCI_ERR_HEADER_LOG +16 ) /sizeof (u32);u32 *aer_regs_buf = (u32 *)aer_regs;int n;if (!aer_base)return false; +/* +* Zero the destination so the software-only tail fields +* (e.g. header_log.header_len) are deterministic rather than +* left as uninitialized stack, which could drive a bogus loop +* length in pcie_print_tlp_log(). +*/ +memset (aer_regs,0 ,sizeof (*aer_regs)); +/* Use readl() to guarantee 32-bit accesses */ for (n =0 ; n < read_cnt; n++) aer_regs_buf[n] =readl (aer_base + n *sizeof (u32));
Greg Kroah-Hartman
maintainerPATCH 1009/1102 USB: gadget: ffs: fix mm lifetime handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabriel Prostitis <[email protected]>
[ Upstream commit 5eb5c72c72fef76cb765ef1669b62b6a3ba1bfc8 ]
io_data stores a pointer to the submitting task's mm_struct, but does not currently hold a reference to it while async requests are pending.
This can result in a use-after-free if the task exits before completion handling finishes.
Take a reference with mmgrab() when queuing the read request and release it with mmdrop() on request completion.
Stable-dep-of: e78dcb1f7ec2 ("usb: gadget: f_fs: Fix Use-After-Free in AIO error path")
--- drivers/usb/gadget/function/f_fs.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-)
drivers/usb/gadget/function/f_fs.c +20 -7
@@ -866,9 +866,15 @@ static void ffs_user_copy_worker(structunsigned long flags;if (io_data->read && ret >0 ) { -kthread_use_mm (io_data->mm); - ret =ffs_copy_to_iter (io_data->buf, ret, &io_data->data); -kthread_unuse_mm (io_data->mm); +if (mmget_not_zero (io_data->mm)) { +kthread_use_mm (io_data->mm); + ret =ffs_copy_to_iter (io_data->buf, ret, &io_data->data); +kthread_unuse_mm (io_data->mm); +mmput (io_data->mm); + }else { + ret = -EFAULT ; + } +mmdrop (io_data->mm); } io_data->kiocb->ki_complete (io_data->kiocb, ret);
@@ -1258,16 +1264,20 @@ static ssize_t ffs_epfile_write_iter(str kiocb->private = p; -if (p->aio) +if (p->aio) { +mmgrab (p->mm);kiocb_set_cancel_fn (kiocb, ffs_aio_cancel); + } res =ffs_epfile_io (kiocb->ki_filp, p);if (res == -EIOCBQUEUED )return res; -if (p->aio) +if (p->aio) { +mmdrop (p->mm);kfree (p); -else + }else { *from = p->data; + }return res; }
@@ -1302,14 +1312,17 @@ static ssize_t ffs_epfile_read_iter(stru kiocb->private = p; -if (p->aio) +if (p->aio) { +mmgrab (p->mm);kiocb_set_cancel_fn (kiocb, ffs_aio_cancel); + } res =ffs_epfile_io (kiocb->ki_filp, p);if (res == -EIOCBQUEUED )return res;if (p->aio) { +mmdrop (p->mm);kfree (p->to_free);kfree (p); }else {
Greg Kroah-Hartman
maintainerPATCH 1010/1102 usb: gadget: f_fs: Fix Use-After-Free in AIO error path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Neill Kapron <[email protected]>
[ Upstream commit e78dcb1f7ec271449c54984dc90c62a5ba272de7 ]
In ffs_epfile_write_iter() and ffs_epfile_read_iter(), when ffs_epfile_io() fails with an error other than -EIOCBQUEUED, the io_data structure (p) is freed. However, for AIO operations, the kiocb cancel function was already armed and kiocb->private was set to p.
If a concurrent cancel operation (such as sys_io_cancel()) executes after ffs_epfile_io() fails but before the function frees p, a Use-After-Free can occur when the cancellation handler accesses the freed pointer.
To securely fix this race condition, we must properly un-arm the cancellation. Invoking kiocb->ki_complete() does exactly this by acquiring ctx->ctx_lock and safely removing the kiocb from the active sequence. In doing so, it ensures that a parallel io_cancel can no longer discover the kiocb, effectively closing the race window.
We then return -EIOCBQUEUED to notify the VFS layer that the kiocb has been consumed and it should avoid attempting to complete the request again or triggering subsequent completion handlers.
--- drivers/usb/gadget/function/f_fs.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/function/f_fs.c +4 -0
@@ -1273,8 +1273,10 @@ static ssize_t ffs_epfile_write_iter(strif (res == -EIOCBQUEUED )return res;if (p->aio) { + kiocb->ki_complete (kiocb, res);mmdrop (p->mm);kfree (p); +return -EIOCBQUEUED ; }else { *from = p->data; }
@@ -1322,9 +1324,11 @@ static ssize_t ffs_epfile_read_iter(strureturn res;if (p->aio) { + kiocb->ki_complete (kiocb, res);mmdrop (p->mm);kfree (p->to_free);kfree (p); +return -EIOCBQUEUED ; }else { *to = p->data; }
Greg Kroah-Hartman
maintainerPATCH 1011/1102 zram: fixup read_block_state()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 8b05d2d8af817c6a1e23032df51e7ad83030d543 ]
ac_time is now in seconds, do not use ktime_to_timespec64()
[ Stable tree note: The ac_time conversion described above is not present
here, so retain the ktime_t conversion and microsecond output. Move the timespec64 temporary into the scan loop instead, allowing the follow-up bounds fix to move nr_pages initialization under init_lock cleanly. ]
Stable-dep-of: 391f057f44a5 ("zram: fix out-of-bounds access in read_block_state()")
--- drivers/block/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/block/zram/zram_drv.c +1 -1
@@ -869,7 +869,6 @@ static ssize_t read_block_state(struct fssize_t index, written =0 ;struct zram *zram = file->private_data;unsigned long nr_pages = zram->disksize >>PAGE_SHIFT ; -struct timespec64 ts; kbuf =kvmalloc (count,GFP_KERNEL );if (!kbuf)
@@ -884,6 +883,7 @@ static ssize_t read_block_state(struct ffor (index = *ppos; index < nr_pages; index++) {int copied; +struct timespec64 ts;zram_slot_lock (zram, index);if (!zram_allocated (zram, index))
Greg Kroah-Hartman
maintainerPATCH 1012/1102 zram: fix out-of-bounds access in read_block_state()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Longlong Xia <[email protected]>
[ Upstream commit 391f057f44a51cc9418da5cba78b014324174264 ]
read_block_state() calculates nr_pages before taking dev_lock. If the device is reset and reinitialized with a smaller disksize before lock acquisition, nr_pages still describes the old table. The subsequent loop can then call slot_lock() past the end of the newly allocated table.
Read disksize after acquiring dev_lock and checking that the device is initialized. The read lock then keeps the table and its bound stable for the duration of the scan.
--- drivers/block/zram/zram_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/block/zram/zram_drv.c +3 -1
@@ -868,7 +868,7 @@ static ssize_t read_block_state(struct fchar *kbuf;ssize_t index, written =0 ;struct zram *zram = file->private_data; -unsigned long nr_pages = zram->disksize >>PAGE_SHIFT ; +unsigned long nr_pages; kbuf =kvmalloc (count,GFP_KERNEL );if (!kbuf)
@@ -881,6 +881,8 @@ static ssize_t read_block_state(struct freturn -EINVAL ; } + nr_pages = zram->disksize >>PAGE_SHIFT ; +for (index = *ppos; index < nr_pages; index++) {int copied;struct timespec64 ts;
Greg Kroah-Hartman
maintainerPATCH 1013/1102 zram: remove entry element member
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit b4444a849f1827c04df81a6f1ef26ddb6f072a94 ]
Element is in the same anon union as handle and hence holds the same value, which makes code below sort of confusing
handle = zram_get_handle()
if (!handle)
element = zram_get_element()
Element doesn't really simplify the code, let's just remove it. We already re-purpose handle to store the block id a written back page.
Stable-dep-of: 894913e2d35c ("zram: fix out-of-bounds access in writeback_store()")
--- drivers/block/zram/zram_drv.c | 23 +++++------------------ drivers/block/zram/zram_drv.h | 5 +---- 2 files changed, 6 insertions(+), 22 deletions(-)
drivers/block/zram/zram_drv.c +5 -18
@@ -112,17 +112,6 @@ static void zram_clear_flag(struct zram zram->table[index].flags &= ~BIT (flag); } -static inline void zram_set_element (struct zram *zram,u32 index, -unsigned long element) -{ - zram->table[index].element = element; -} - -static unsigned long zram_get_element (struct zram *zram,u32 index) -{ -return zram->table[index].element; -} -static size_t zram_get_obj_size (struct zram *zram,u32 index) {return zram->table[index].flags & (BIT (ZRAM_FLAG_SHIFT ) -1 );
@@ -762,7 +751,7 @@ static ssize_t writeback_store(struct dezram_free_page (zram, index);zram_clear_flag (zram, index,ZRAM_UNDER_WB );zram_set_flag (zram, index,ZRAM_WB ); -zram_set_element (zram, index, blk_idx); +zram_set_handle (zram, index, blk_idx); blk_idx =0 ;atomic64_inc (&zram->stats.pages_stored);spin_lock (&zram->wb_limit_lock);
@@ -1390,7 +1379,7 @@ static void zram_free_page(struct zram *if (zram_test_flag (zram, index,ZRAM_WB )) {zram_clear_flag (zram, index,ZRAM_WB ); -free_block_bdev (zram,zram_get_element (zram, index)); +free_block_bdev (zram,zram_get_handle (zram, index)); goto out; }
@@ -1436,12 +1425,10 @@ static int zram_read_from_zspool(struct handle =zram_get_handle (zram, index);if (!handle ||zram_test_flag (zram, index,ZRAM_SAME )) { -unsigned long value;void *mem; - value = handle ?zram_get_element (zram, index) :0 ; mem =kmap_local_page (page); -zram_fill_page (mem,PAGE_SIZE , value); +zram_fill_page (mem,PAGE_SIZE , handle);kunmap_local (mem);return 0 ; }
@@ -1487,7 +1474,7 @@ static int zram_read_page(struct zram *z*/ zram_slot_unlock (zram, index); - ret =read_from_bdev (zram, page,zram_get_element (zram, index), + ret =read_from_bdev (zram, page,zram_get_handle (zram, index), parent); }
@@ -1639,7 +1626,7 @@ out:if (flags) {zram_set_flag (zram, index, flags); -zram_set_element (zram, index, element); +zram_set_handle (zram, index, element); }else {zram_set_handle (zram, index, handle);zram_set_obj_size (zram, index, comp_len);
drivers/block/zram/zram_drv.h +1 -4
@@ -62,10 +62,7 @@ enum zram_pageflags {/* Allocated for each disk page */ struct zram_table_entry { -union { -unsigned long handle; -unsigned long element; - }; +unsigned long handle;unsigned int flags;spinlock_t lock;#ifdef CONFIG_ZRAM_TRACK_ENTRY_ACTIME
Greg Kroah-Hartman
maintainerPATCH 1014/1102 zram: use zram_read_from_zspool() in writeback
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit b8d3ff7bb5111c6a88d5672469de445d9b46cf22 ]
We only can read pages from zspool in writeback, zram_read_page() is not really right in that context not only because it's a more generic function that handles ZRAM_WB pages, but also because it requires us to unlock slot between slot flag check and actual page read. Use zram_read_from_zspool() instead and do slot flags check and page read under the same slot lock.
Stable-dep-of: 894913e2d35c ("zram: fix out-of-bounds access in writeback_store()")
--- drivers/block/zram/zram_drv.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
drivers/block/zram/zram_drv.c +5 -7
@@ -55,8 +55,8 @@ static size_t huge_class_size;static const struct block_device_operations zram_devops;static void zram_free_page (struct zram *zram,size_t index); -static int zram_read_page (struct zram *zram,struct page *page,u32 index, -struct bio *parent); +static int zram_read_from_zspool (struct zram *zram,struct page *page, +u32 index);static int zram_slot_trylock (struct zram *zram,u32 index) {
@@ -694,14 +694,12 @@ static ssize_t writeback_store(struct dezram_set_flag (zram, index,ZRAM_UNDER_WB );/* Need for hugepage writeback racing */ zram_set_flag (zram, index,ZRAM_IDLE ); -zram_slot_unlock (zram, index); -if (zram_read_page (zram, page, index,NULL )) { -zram_slot_lock (zram, index); +if (zram_read_from_zspool (zram, page, index)) {zram_clear_flag (zram, index,ZRAM_UNDER_WB );zram_clear_flag (zram, index,ZRAM_IDLE ); -zram_slot_unlock (zram, index); -continue ; + goto next; } +zram_slot_unlock (zram, index);bio_init (&bio, zram->bdev, &bio_vec,1 ,REQ_OP_WRITE |REQ_SYNC );
Greg Kroah-Hartman
maintainerPATCH 1015/1102 zram: fix out-of-bounds access in writeback_store()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Longlong Xia <[email protected]>
[ Upstream commit 894913e2d35c46ff19a77530907771ae57862b96 ]
Patch series "zram: fix stale scan bounds after reinitialization".
Both writeback_store() and read_block_state() derive their table scan bounds from zram->disksize before acquiring dev_lock. If the device is reset and reinitialized with a smaller disksize between that read and lock acquisition, the bound can describe the old table while the scan operates on the new one. This can lead to out-of-bounds slot accesses.
Move both bound calculations under dev_lock so each bound remains consistent with the table throughout its scan. Keep the fixes separate because the affected interfaces originate from different commits and can be backported independently.
This patch (of 2):
writeback_store() calculates the table scan bounds before taking dev_lock. A reset followed by reconfiguration with a smaller disksize can therefore replace zram->table while writeback_store() is waiting for the lock. Once it acquires the lock, it sees an initialized device but scans the new table using the old upper bound, resulting in an out-of-bounds access.
Calculate the number of pages while holding dev_lock so the scan bound matches the table protected by the lock.
--- drivers/block/zram/zram_drv.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
drivers/block/zram/zram_drv.c +11 -4
@@ -599,7 +599,7 @@ static ssize_t writeback_store(struct de struct device_attribute *attr, const char *buf, size_t len) { struct zram *zram = dev_to_zram(dev); -unsigned long nr_pages = zram->disksize >>PAGE_SHIFT ; +unsigned long nr_pages;unsigned long index =0 ;struct bio bio;struct bio_vec bio_vec;
@@ -620,11 +620,9 @@ static ssize_t writeback_store(struct deif (strncmp (buf,PAGE_WB_SIG ,sizeof (PAGE_WB_SIG ) -1 ))return -EINVAL ; -if (kstrtol (buf +sizeof (PAGE_WB_SIG ) -1 ,10 , &index) || - index >= nr_pages) +if (kstrtol (buf +sizeof (PAGE_WB_SIG ) -1 ,10 , &index))return -EINVAL ; - nr_pages =1 ; mode =PAGE_WRITEBACK ; }
@@ -634,6 +632,15 @@ static ssize_t writeback_store(struct de goto release_init_lock; } + nr_pages = zram->disksize >>PAGE_SHIFT ; +if (mode ==PAGE_WRITEBACK ) { +if (index >= nr_pages) { +up_read (&zram->init_lock); +return -EINVAL ; + } + nr_pages =1 ; + } +/* Do not permit concurrent post-processing actions. */ if (atomic_xchg (&zram->pp_in_progress,1 )) {up_read (&zram->init_lock);
Greg Kroah-Hartman
maintainerPATCH 1016/1102 zram: switch to guard() for init_lock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 0d38260c2a11de147f0c4701b344fdfa6bcdd04c ]
Use init_lock guard() in sysfs store/show handlers, in order to simplify and, more importantly, to modernize the code.
While at it, fix up more coding styles.
For the 6.12 stable backport, retain only the zram_reset_device() conversion needed by the follow-up fix. The remaining upstream hunks apply to zram paths that have significantly diverged in this tree.
Stable-dep-of: dde75313eed0 ("zram: set default primary compressor in zram_destroy_comps()")
--- drivers/block/zram/zram_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/block/zram/zram_drv.c +1 -2
@@ -2167,7 +2167,7 @@ static void zram_destroy_comps(struct zrstatic void zram_reset_device (struct zram *zram) { -down_write (&zram->init_lock); +guard (rwsem_write)(&zram->init_lock); zram->limit_pages =0 ;
@@ -2183,7 +2183,6 @@ static void zram_reset_device(struct zrareset_bdev (zram);comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); -up_write (&zram->init_lock); } static ssize_t disksize_store(struct device *dev,
Greg Kroah-Hartman
maintainerPATCH 1017/1102 zram: set default primary compressor in zram_destroy_comps()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit dde75313eed0b014c437f48dd75c0308b592cbf9 ]
Patch series "zram: fix zram issues reported by sashiko".
Sashiko drove by and reported [1] a couple of zram issues:
a possible BUG_ON() in zlib code due to missing winbits range validation and one possible NULL-ptr dereference in zcomp. Both are low risk yet still worth fixing.
This patch (of 2):
zram_destroy_comps() resets all compressors and leaves them set to NULL, including the primary one, which is invalid device state, as now comp_algorithm_show()->strcmp() can be called on a NULL compressor. Set default primary compressor in zram_destroy_comps().
--- drivers/block/zram/zram_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/block/zram/zram_drv.c +1 -2
@@ -2163,6 +2163,7 @@ static void zram_destroy_comps(struct zr }zram_comp_params_reset (zram); +comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); }static void zram_reset_device (struct zram *zram)
@@ -2181,8 +2182,6 @@ static void zram_reset_device(struct zramemset (&zram->stats,0 ,sizeof (zram->stats));atomic_set (&zram->pp_in_progress,0 );reset_bdev (zram); - -comp_algorithm_set (zram,ZRAM_PRIMARY_COMP , default_compressor); } static ssize_t disksize_store(struct device *dev,
Greg Kroah-Hartman
maintainerPATCH 1018/1102 netlink: introduce type-checking attribute iteration for nlmsg
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit 42401c42389622424f2973ec57414f033ae6be8f ]
Add the nlmsg_for_each_attr_type() macro to simplify iteration over attributes of a specific type in a Netlink message.
Convert existing users in vxlan and nfsd to use the new macro.
Stable-dep-of: bdcc85c2b05a ("nfsd: validate sockaddr length per family in listener_set")
--- drivers/net/vxlan/vxlan_vnifilter.c | 13 ++++--------- fs/nfsd/nfsctl.c | 36 ++++++++++++++---------------------- include/net/netlink.h | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 31 deletions(-)
drivers/net/vxlan/vxlan_vnifilter.c +4 -9
@@ -978,15 +978,10 @@ static int vxlan_vnifilter_process(strucif (!(vxlan->cfg.flags &VXLAN_F_VNIFILTER ))return -EOPNOTSUPP ; -nlmsg_for_each_attr (attr, nlh,sizeof (*tmsg), rem) { -switch (nla_type (attr)) { -case VXLAN_VNIFILTER_ENTRY : - err =vxlan_process_vni_filter (vxlan, attr, - nlh->nlmsg_type, extack); -break ; -default : -continue ; - } +nlmsg_for_each_attr_type (attr,VXLAN_VNIFILTER_ENTRY , nlh, +sizeof (*tmsg), rem) { + err =vxlan_process_vni_filter (vxlan, attr, nlh->nlmsg_type, + extack); vnis++;if (err)break ;
fs/nfsd/nfsctl.c +14 -22
@@ -1682,10 +1682,9 @@ int nfsd_nl_threads_set_doit(struct sk_breturn -EINVAL ;/* count number of SERVER_THREADS values */ -nlmsg_for_each_attr (attr, info->nlhdr,GENL_HDRLEN , rem) { -if (nla_type (attr) ==NFSD_A_SERVER_THREADS ) - nrpools++; - } +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_THREADS , info->nlhdr, +GENL_HDRLEN , rem) + nrpools++;mutex_lock (&nfsd_mutex);
@@ -1696,12 +1695,11 @@ int nfsd_nl_threads_set_doit(struct sk_b } i =0 ; -nlmsg_for_each_attr (attr, info->nlhdr,GENL_HDRLEN , rem) { -if (nla_type (attr) ==NFSD_A_SERVER_THREADS ) { - nthreads[i++] =nla_get_u32 (attr); -if (i >= nrpools) -break ; - } +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_THREADS , info->nlhdr, +GENL_HDRLEN , rem) { + nthreads[i++] =nla_get_u32 (attr); +if (i >= nrpools) +break ; } if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
@@ -1842,14 +1840,12 @@ int nfsd_nl_version_set_doit(struct sk_bfor (i =0 ; i <=NFSD_SUPPORTED_MINOR_VERSION ; i++)nfsd_minorversion (nn, i,NFSD_CLEAR ); -nlmsg_for_each_attr (attr, info->nlhdr,GENL_HDRLEN , rem) { +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_PROTO_VERSION , info->nlhdr, +GENL_HDRLEN , rem) {struct nlattr *tb[NFSD_A_VERSION_MAX +1 ];u32 major, minor =0 ;bool enabled; -if (nla_type (attr) !=NFSD_A_SERVER_PROTO_VERSION ) -continue ; -if (nla_parse_nested (tb,NFSD_A_VERSION_MAX , attr, nfsd_version_nl_policy, info->extack) <0 )continue ;
@@ -2000,14 +1996,12 @@ int nfsd_nl_listener_set_doit(struct sk_* Walk the list of server_socks from userland and move any that match * back to sv_permsocks */ -nlmsg_for_each_attr (attr, info->nlhdr,GENL_HDRLEN , rem) { +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_SOCK_ADDR , info->nlhdr, +GENL_HDRLEN , rem) {struct nlattr *tb[NFSD_A_SOCK_MAX +1 ];const char *xcl_name;struct sockaddr *sa; -if (nla_type (attr) !=NFSD_A_SERVER_SOCK_ADDR ) -continue ; -if (nla_parse_nested (tb,NFSD_A_SOCK_MAX , attr, nfsd_sock_nl_policy, info->extack) <0 )continue ;
@@ -2062,15 +2056,13 @@ int nfsd_nl_listener_set_doit(struct sk_svc_xprt_destroy_all (serv, net);/* walk list of addrs again, open any that still don't exist */ -nlmsg_for_each_attr (attr, info->nlhdr,GENL_HDRLEN , rem) { +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_SOCK_ADDR , info->nlhdr, +GENL_HDRLEN , rem) {struct nlattr *tb[NFSD_A_SOCK_MAX +1 ];const char *xcl_name;struct sockaddr *sa;int ret; -if (nla_type (attr) !=NFSD_A_SERVER_SOCK_ADDR ) -continue ; -if (nla_parse_nested (tb,NFSD_A_SOCK_MAX , attr, nfsd_sock_nl_policy, info->extack) <0 )continue ;
include/net/netlink.h +14 -0
@@ -68,6 +68,8 @@* nlmsg_for_each_msg() loop over all messages * nlmsg_validate() validate netlink message incl. attrs * nlmsg_for_each_attr() loop over all attributes +* nlmsg_for_each_attr_type() loop over all attributes with the +* given type * * Misc: * nlmsg_report() report back to application?
@@ -941,6 +943,18 @@ static inline u32 nlmsg_seq(const struct nlmsg_attrlen(nlh, hdrlen), rem)/** +* nlmsg_for_each_attr_type - iterate over a stream of attributes +* @pos: loop counter, set to the current attribute +* @type: required attribute type for @pos +* @nlh: netlink message header +* @hdrlen: length of the family specific header +* @rem: initialized to len, holds bytes currently remaining in stream +*/ +#define nlmsg_for_each_attr_type (pos, type, nlh, hdrlen, rem) \ + nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ + if (nla_type(pos) == type) + +/** * nlmsg_put - Add a new netlink message to an skb * @skb: socket buffer to store message in * @portid: netlink PORTID of requesting application
Greg Kroah-Hartman
maintainerPATCH 1019/1102 nfsd: validate sockaddr length per family in listener_set
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit bdcc85c2b05a9378d8bd2d65f9fc41440a3cf464 ]
nfsd_sock_nl_policy declares NFSD_A_SOCK_ADDR as a bare NLA_BINARY attribute with no minimum length. A CAP_NET_ADMIN caller can send a 16-byte NFSD_A_SOCK_ADDR with sa_family=AF_INET6, causing a 12-byte OOB read across three consumers (rpc_cmp_addr_port, svc_find_listener, kernel_bind).
nfsd_nl_listener_set_doit() also parsed and validated each listener entry inline in two separate loops, interleaved with mutating the running listener configuration. The validation was duplicated, used an open-coded "nla_len < sizeof(struct sockaddr)" check that was too short for AF_INET6, and handled a malformed entry inconsistently depending on which loop noticed it.
Add an nfsd_nl_validate_listeners() helper that walks the entire list once and confirms each entry parses, carries both an address and a transport name, and is long enough for its address family (sizeof(struct sockaddr_in) for AF_INET, sizeof(struct sockaddr_in6) for AF_INET6, -EAFNOSUPPORT otherwise). Call it before taking nfsd_mutex or creating the serv, so a malformed request fails cleanly with no side effects.
Since every entry is known valid by the time the two existing loops run, drop the redundant presence and per-family length checks from both, leaving only the nla_parse_nested() call needed to extract the data.
--- fs/nfsd/nfsctl.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 12 deletions(-)
fs/nfsd/nfsctl.c +65 -12
@@ -1959,6 +1959,60 @@ err_free_msg: }/** +* nfsd_nl_validate_listeners - sanity-check the listener list from userland +* @info: netlink metadata and command arguments +* +* Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry +* is well-formed: it parses against the policy, carries both an address and +* a transport name, and the address is long enough for its family. Doing +* this up front lets the callers below assume every entry is valid and +* guarantees we make no changes when the request is malformed. +* +* Return: 0 if every entry is valid, or a negative errno otherwise. +*/ +static int nfsd_nl_validate_listeners (struct genl_info *info) +{ +const struct nlattr *attr; +int rem; + +nlmsg_for_each_attr_type (attr,NFSD_A_SERVER_SOCK_ADDR , info->nlhdr, +GENL_HDRLEN , rem) { +struct nlattr *tb[NFSD_A_SOCK_MAX +1 ]; +struct sockaddr *sa; +int err; + + err =nla_parse_nested (tb,NFSD_A_SOCK_MAX , attr, + nfsd_sock_nl_policy, info->extack); +if (err <0 ) +return err; + +if (!tb[NFSD_A_SOCK_ADDR ] || !tb[NFSD_A_SOCK_TRANSPORT_NAME ]) +return -EINVAL ; + + sa =nla_data (tb[NFSD_A_SOCK_ADDR ]); +if (nla_len (tb[NFSD_A_SOCK_ADDR ]) <sizeof (sa->sa_family)) +return -EINVAL ; + +switch (sa->sa_family) { +case AF_INET : +if (nla_len (tb[NFSD_A_SOCK_ADDR ]) < +sizeof (struct sockaddr_in )) +return -EINVAL ; +break ; +case AF_INET6 : +if (nla_len (tb[NFSD_A_SOCK_ADDR ]) < +sizeof (struct sockaddr_in6 )) +return -EINVAL ; +break ; +default : +return -EAFNOSUPPORT ; + } + } + +return 0 ; +} + +/** * nfsd_nl_listener_set_doit - set the nfs running sockets * @skb: reply buffer * @info: netlink metadata and command arguments
@@ -1976,6 +2030,15 @@ int nfsd_nl_listener_set_doit(struct sk_bool delete = false;int err, rem; +/* +* Validate the entire listener list before making any changes, so a +* malformed request fails cleanly without creating a serv or touching +* the existing listeners. +*/ + err =nfsd_nl_validate_listeners (info); +if (err) +return err; +mutex_lock (&nfsd_mutex); err =nfsd_create_serv (net);
@@ -2002,16 +2065,11 @@ int nfsd_nl_listener_set_doit(struct sk_const char *xcl_name;struct sockaddr *sa; +/* validated up front in nfsd_nl_validate_listeners() */ if (nla_parse_nested (tb,NFSD_A_SOCK_MAX , attr, nfsd_sock_nl_policy, info->extack) <0 )continue ; -if (!tb[NFSD_A_SOCK_ADDR ] || !tb[NFSD_A_SOCK_TRANSPORT_NAME ]) -continue ; - -if (nla_len (tb[NFSD_A_SOCK_ADDR ]) <sizeof (*sa)) -continue ; - xcl_name =nla_data (tb[NFSD_A_SOCK_TRANSPORT_NAME ]); sa =nla_data (tb[NFSD_A_SOCK_ADDR ]);
@@ -2063,16 +2121,11 @@ int nfsd_nl_listener_set_doit(struct sk_struct sockaddr *sa;int ret; +/* validated up front in nfsd_nl_validate_listeners() */ if (nla_parse_nested (tb,NFSD_A_SOCK_MAX , attr, nfsd_sock_nl_policy, info->extack) <0 )continue ; -if (!tb[NFSD_A_SOCK_ADDR ] || !tb[NFSD_A_SOCK_TRANSPORT_NAME ]) -continue ; - -if (nla_len (tb[NFSD_A_SOCK_ADDR ]) <sizeof (*sa)) -continue ; - xcl_name =nla_data (tb[NFSD_A_SOCK_TRANSPORT_NAME ]); sa =nla_data (tb[NFSD_A_SOCK_ADDR ]);
Greg Kroah-Hartman
maintainerPATCH 1020/1102 nfsd: check nfsd4_acl_to_attr() return value in nfsd4_create()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 2c7912732184773dbd371a411da87af1cc080b86 ]
nfsd4_create() stores the return value of nfsd4_acl_to_attr() in status, but the switch(create->cr_type) block unconditionally overwrites it in every branch. ACL translation errors are silently discarded, and the CREATE proceeds without the requested ACL.
Add an early exit check after nfsd4_acl_to_attr(), matching the pattern already used in nfsd4_setattr().
--- fs/nfsd/nfs4proc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
fs/nfsd/nfs4proc.c +17 -0
@@ -796,7 +796,23 @@ nfsd4_create(struct svc_rqst *rqstp, strif (status)return status; +/* Sanitize cr_type to avoid returning ATTRNOTSUPP. */ +switch (create->cr_type) { +case NF4LNK : +case NF4BLK : +case NF4CHR : +case NF4SOCK : +case NF4FIFO : +case NF4DIR : +break ; +default : + status = nfserr_badtype; + goto out_aftermask; + } + status =nfsd4_acl_to_attr (create->cr_type, create->cr_acl, &attrs); +if (status != nfs_ok) + goto out_aftermask; current->fs->umask = create->cr_umask;switch (create->cr_type) {case NF4LNK :
@@ -863,6 +879,7 @@ out:fh_put (&resfh); out_umask: current->fs->umask =0 ; +out_aftermask:nfsd_attrs_free (&attrs);return status; }
Greg Kroah-Hartman
maintainerPATCH 1021/1102 NFSD: Rename a function parameter
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 71bc2c6c413b78e5a47b4a62284f712d9f719007 ]
Clean up: A function parameter called "rqstp" typically refers to an object of type "struct svc_rqst", so it's confusing when such an parameter refers to a different struct type with field names that are very similar to svc_rqst.
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
fs/nfsd/nfsctl.c +14 -14
@@ -1494,7 +1494,7 @@ unsigned int nfsd_net_id;static int nfsd_genl_rpc_status_compose_msg (struct sk_buff *skb,struct netlink_callback *cb, -struct nfsd_genl_rqstp *rqstp) +struct nfsd_genl_rqstp *genl_rqstp) {void *hdr;u32 i;
@@ -1504,22 +1504,22 @@ static int nfsd_genl_rpc_status_compose_ if (!hdr) return -ENOBUFS; - if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, rqstp->rq_xid) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, rqstp->rq_flags) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, rqstp->rq_prog) || - nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, rqstp->rq_proc) || - nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, rqstp->rq_vers) || + if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, genl_rqstp->rq_xid) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, genl_rqstp->rq_flags) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, genl_rqstp->rq_prog) || + nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, genl_rqstp->rq_proc) || + nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, genl_rqstp->rq_vers) || nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME, - ktime_to_us(rqstp->rq_stime), + ktime_to_us(genl_rqstp->rq_stime), NFSD_A_RPC_STATUS_PAD)) return -ENOBUFS; - switch (rqstp->rq_saddr.sa_family) { + switch (genl_rqstp->rq_saddr.sa_family) { case AF_INET: { const struct sockaddr_in *s_in, *d_in; - s_in = (const struct sockaddr_in *)&rqstp->rq_saddr; - d_in = (const struct sockaddr_in *)&rqstp->rq_daddr; + s_in = (const struct sockaddr_in *)&genl_rqstp->rq_saddr; + d_in = (const struct sockaddr_in *)&genl_rqstp->rq_daddr; if (nla_put_in_addr(skb, NFSD_A_RPC_STATUS_SADDR4, s_in->sin_addr.s_addr) || nla_put_in_addr(skb, NFSD_A_RPC_STATUS_DADDR4,
@@ -1534,8 +1534,8 @@ static int nfsd_genl_rpc_status_compose_ case AF_INET6: { const struct sockaddr_in6 *s_in, *d_in; - s_in = (const struct sockaddr_in6 *)&rqstp->rq_saddr; - d_in = (const struct sockaddr_in6 *)&rqstp->rq_daddr; + s_in = (const struct sockaddr_in6 *)&genl_rqstp->rq_saddr; + d_in = (const struct sockaddr_in6 *)&genl_rqstp->rq_daddr; if (nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_SADDR6, &s_in->sin6_addr) || nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_DADDR6,
@@ -1549,9 +1549,9 @@ static int nfsd_genl_rpc_status_compose_ } } -for (i =0 ; i < rqstp->rq_opcnt; i++) +for (i =0 ; i < genl_rqstp->rq_opcnt; i++)if (nla_put_u32 (skb,NFSD_A_RPC_STATUS_COMPOUND_OPS , - rqstp->rq_opnum[i])) + genl_rqstp->rq_opnum[i]))return -ENOBUFS ;genlmsg_end (skb, hdr);
Greg Kroah-Hartman
maintainerPATCH 1022/1102 NFSD: Make nfsd_genl_rqstp::rq_ops array best-effort
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit a2d61427fb4b630b94ed9bd457dd8bc239b83e4b ]
To enable NFSD to handle NFSv4 COMPOUNDs of unrestricted size, resize the array in struct nfsd_genl_rqstp so it saves only up to 16 operations per COMPOUND.
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 3 ++- fs/nfsd/nfsd.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
fs/nfsd/nfsctl.c +2 -1
@@ -1627,7 +1627,8 @@ int nfsd_nl_rpc_status_get_dumpit(structint j; args = rqstp->rq_argp; - genl_rqstp.rq_opcnt = args->opcnt; + genl_rqstp.rq_opcnt =min_t (u32, args->opcnt, +ARRAY_SIZE (genl_rqstp.rq_opnum));for (j =0 ; j < genl_rqstp.rq_opcnt; j++) genl_rqstp.rq_opnum[j] = args->ops[j].opnum;
fs/nfsd/nfsd.h +1 -1
@@ -82,7 +82,7 @@ struct nfsd_genl_rqstp {/* NFSv4 compound */ u32 rq_opcnt; -u32 rq_opnum[NFSD_MAX_OPS_PER_COMPOUND ]; +u32 rq_opnum[16 ]; };extern struct svc_program nfsd_programs[];
Greg Kroah-Hartman
maintainerPATCH 1023/1102 Revert "NFSD: Remove the cap on number of operations per NFSv4 COMPOUND"
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 3e7f011c255582d7c914133785bbba1990441713 ]
I've found that pynfs COMP6 now leaves the connection or lease in a strange state, which causes CLOSE9 to hang indefinitely. I've dug into it a little, but I haven't been able to root-cause it yet. However, I bisected to commit 48aab1606fa8 ("NFSD: Remove the cap on number of operations per NFSv4 COMPOUND").
Tianshuo Han also reports a potential vulnerability when decoding an NFSv4 COMPOUND. An attacker can place an arbitrarily large op count in the COMPOUND header, which results in:
[ 51.410584] nfsd: vmalloc error: size 1209533382144, exceeds total
pages, mode:0xdc0(GFP_KERNEL|__GFP_ZERO),
nodemask=(null),cpuset=/,mems_allowed=0
when NFSD attempts to allocate the COMPOUND op array.
Let's restore the operation-per-COMPOUND limit, but increased to 200 for now.
The 6.12 tree already contains the functional changes from this revert and enforces a stricter 50-operation cap. Retain that cap and use this dependency to relocate struct nfsd_genl_rqstp into its sole user so the follow-up address-width fix applies at the same location as upstream.
[ sashal: Reduced backport -- upstream 3e7f011c25558 touches 5 file(s), this
backport carries 2. Not backported here:
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/xdr4.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: a99d720ed2a5 ("nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage")
--- fs/nfsd/nfsctl.c | 15 +++++++++++++++ fs/nfsd/nfsd.h | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-)
fs/nfsd/nfsctl.c +15 -0
@@ -1492,6 +1492,21 @@ static int create_proc_exports_entry(voiunsigned int nfsd_net_id; +struct nfsd_genl_rqstp { +struct sockaddr rq_daddr; +struct sockaddr rq_saddr; +unsigned long rq_flags; +ktime_t rq_stime; +__be32 rq_xid; +u32 rq_vers; +u32 rq_prog; +u32 rq_proc; + +/* NFSv4 compound */ +u32 rq_opcnt; +u32 rq_opnum[16 ]; +}; +static int nfsd_genl_rpc_status_compose_msg (struct sk_buff *skb,struct netlink_callback *cb,struct nfsd_genl_rqstp *genl_rqstp)
fs/nfsd/nfsd.h +0 -15
@@ -70,21 +70,6 @@ struct readdir_cd {/* Maximum number of operations per session compound */ #define NFSD_MAX_OPS_PER_COMPOUND 50 -struct nfsd_genl_rqstp { -struct sockaddr rq_daddr; -struct sockaddr rq_saddr; -unsigned long rq_flags; -ktime_t rq_stime; -__be32 rq_xid; -u32 rq_vers; -u32 rq_prog; -u32 rq_proc; - -/* NFSv4 compound */ -u32 rq_opcnt; -u32 rq_opnum[16 ]; -}; -extern struct svc_program nfsd_programs[];extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;extern struct mutex nfsd_mutex;
Greg Kroah-Hartman
maintainerPATCH 1024/1102 nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit a99d720ed2a5258564e5e9d5f39f3184a030d354 ]
struct nfsd_genl_rqstp declares rq_daddr and rq_saddr as plain "struct sockaddr" (16 bytes). When an IPv6 NFS client is connected, nfsd_genl_rpc_status_compose_msg() casts these fields to "struct sockaddr_in6 *" (28 bytes) and reads sin6_addr at offset 8..24, which extends 8 bytes past the end of the 16-byte sockaddr field into the adjacent rq_flags member. The 16-byte nla_put_in6_addr then ships 8 bytes of truncated IPv6 address followed by 8 bytes of rq_flags to userspace via the NFSD_A_RPC_STATUS_SADDR6/DADDR6 netlink attributes.
This is reachable by any unprivileged process in the network namespace because NFSD_CMD_RPC_STATUS_GET uses GENL_CMD_CAP_DUMP without GENL_ADMIN_PERM.
Fix by widening rq_daddr and rq_saddr to struct sockaddr_storage so the IPv6 casts operate within bounds, copying sizeof(struct sockaddr_storage) bytes in the memcpy calls so the full address is captured, and zero-initializing the genl_rqstp stack variable to prevent leaking uninitialized tail bytes through netlink.
--- fs/nfsd/nfsctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
fs/nfsd/nfsctl.c +6 -6
@@ -1493,8 +1493,8 @@ static int create_proc_exports_entry(voiunsigned int nfsd_net_id;struct nfsd_genl_rqstp { -struct sockaddr rq_daddr; -struct sockaddr rq_saddr; +struct sockaddr_storage rq_daddr; +struct sockaddr_storage rq_saddr;unsigned long rq_flags;ktime_t rq_stime;__be32 rq_xid;
@@ -1529,7 +1529,7 @@ static int nfsd_genl_rpc_status_compose_ NFSD_A_RPC_STATUS_PAD))return -ENOBUFS ; -switch (genl_rqstp->rq_saddr.sa_family) { +switch (genl_rqstp->rq_saddr.ss_family) {case AF_INET : {const struct sockaddr_in *s_in, *d_in;
@@ -1606,7 +1606,7 @@ int nfsd_nl_rpc_status_get_dumpit(structlist_for_each_entry_rcu (rqstp, &nn->nfsd_serv->sv_pools[i].sp_all_threads, rq_all) { -struct nfsd_genl_rqstp genl_rqstp; +struct nfsd_genl_rqstp genl_rqstp = {};unsigned int status_counter;if (rqstp_index++ < cb->args[1 ])/* already consumed */
@@ -1630,9 +1630,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct genl_rqstp.rq_stime = rqstp->rq_stime; genl_rqstp.rq_opcnt =0 ;memcpy (&genl_rqstp.rq_daddr,svc_daddr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));memcpy (&genl_rqstp.rq_saddr,svc_addr (rqstp), -sizeof (struct sockaddr )); +sizeof (struct sockaddr_storage ));#ifdef CONFIG_NFSD_V4 if (rqstp->rq_vers == NFS4_VERSION &&
Greg Kroah-Hartman
maintainerPATCH 1025/1102 nfsd: dedup nfs4_client_to_reclaim inserts
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 9b57793863f69c608691c98e57c2f9be2db9dfed ]
nfs4_client_to_reclaim() unconditionally allocates a new nfs4_client_reclaim, prepends it to reclaim_str_hashtbl[], and bumps reclaim_str_hashtbl_size with no check for an existing entry for the same client name. After a reboot with a populated recovery directory that inflates the counter by one for every client that reclaims:
boot: load_recdir()
nfs4_client_to_reclaim(name) /* entry #1, size++ */
grace: RECLAIM_COMPLETE
__nfsd4_create_reclaim_record_grace()
nfs4_client_to_reclaim(name) /* entry #2, size++ */inc_reclaim_complete() ends the grace period early only when
atomic_inc_return(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_sizeWith reclaim_str_hashtbl_size at 2N and nr_reclaim_complete capped at N, the equality never holds and the fast end-of-grace path is dead. The grace period always runs out the full 90-second laundromat timer, and the shadow entry left in the hash table carries a dangling cr_clp for any reader that walks it.
Fix nfs4_client_to_reclaim() to look the name up with nfsd4_find_reclaim_client() first and, on a hit, fold the new princhash into the existing record (if it lacks one) and return that record without allocating or touching reclaim_str_hashtbl_size. On kmemdup() failure during the fold-in, return NULL so __cld_pipe_inprogress_downcall() surfaces -EFAULT to nfsdcld, matching the miss-path contract.
Add an rw_semaphore (reclaim_str_hashtbl_lock) to struct nfsd_net that serialises all access to reclaim_str_hashtbl[] and reclaim_str_hashtbl_size. Writers (nfs4_client_to_reclaim, nfs4_remove_reclaim_record callers) hold the write side; readers (nfsd4_cld_check*, inc_reclaim_complete, clients_still_reclaiming, nfs4_has_reclaimed_state, nfsd4_check_legacy_client) hold the read side. All call sites are in sleepable context, and none is a hot path, so the rwsem cost is negligible.
--- fs/nfsd/netns.h | 6 ++++ fs/nfsd/nfs4recover.c | 39 ++++++++++++++++++++++++++----- fs/nfsd/nfs4state.c | 61 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 14 deletions(-)
fs/nfsd/netns.h +5 -1
@@ -84,6 +84,7 @@ struct nfsd_net {*/ struct list_head *reclaim_str_hashtbl;int reclaim_str_hashtbl_size; +struct rw_semaphore reclaim_str_hashtbl_lock;struct list_head *conf_id_hashtbl;struct rb_root conf_name_tree;struct list_head *unconf_id_hashtbl;
@@ -96,7 +97,10 @@ struct nfsd_net {* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. * -* All of the above fields are protected by the client_mutex. +* reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by +* reclaim_str_hashtbl_lock. +* +* All of the remaining fields are protected by the client_lock. */ struct list_head client_lru;struct list_head close_lru;
fs/nfsd/nfs4recover.c +32 -7
@@ -355,10 +355,12 @@ __nfsd4_remove_reclaim_record_grace(consreturn ; } name.len = len; +down_write (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp)nfs4_remove_reclaim_record (crp, nn); +up_write (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); } static void
@@ -559,6 +561,7 @@ nfs4_legacy_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -678,13 +681,16 @@ nfsd4_check_legacy_client(struct nfs4_cl goto out_enoent; } name.len =HEXDIR_LEN ; +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (name, nn); -kfree (name.data);if (crp) {set_bit (NFSD4_CLIENT_STABLE , &clp->cl_flags); crp->cr_clp = clp; -return 0 ; } +up_read (&nn->reclaim_str_hashtbl_lock); +kfree (name.data); +if (crp) +return 0 ; out_enoent:return -ENOENT ;
@@ -1285,6 +1291,7 @@ nfsd4_cld_check(struct nfs4_client *clp)return 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1296,13 +1303,16 @@ nfsd4_cld_check(struct nfs4_client *clp)struct xdr_netobj name; status =nfs4_make_rec_clidname (dname, &clp->cl_name); -if (status) +if (status) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } name.data =kmemdup (dname,HEXDIR_LEN ,GFP_KERNEL );if (!name.data) {dprintk ("%s: failed to allocate memory for name.data!\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1313,9 +1323,11 @@ nfsd4_cld_check(struct nfs4_client *clp) }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found: crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1335,6 +1347,7 @@ nfsd4_cld_check_v2(struct nfs4_client *creturn 0 ;/* look for it in the reclaim hashtable otherwise */ +down_read (&nn->reclaim_str_hashtbl_lock); crp =nfsd4_find_reclaim_client (clp->cl_name, nn);if (crp) goto found;
@@ -1345,13 +1358,16 @@ nfsd4_cld_check_v2(struct nfs4_client *cchar dname[HEXDIR_LEN ]; status =nfs4_make_rec_clidname (dname, &clp->cl_name); -if (status) +if (status) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } name.data =kmemdup (dname,HEXDIR_LEN ,GFP_KERNEL );if (!name.data) {dprintk ("%s: failed to allocate memory for name.data\n" , __func__); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; } name.len =HEXDIR_LEN ;
@@ -1362,6 +1378,7 @@ nfsd4_cld_check_v2(struct nfs4_client *c }#endif +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; found:if (crp->cr_princhash.len) {
@@ -1369,26 +1386,33 @@ found: principal = clp->cl_cred.cr_raw_principal; else if (clp->cl_cred.cr_principal) principal = clp->cl_cred.cr_principal; -if (principal ==NULL ) +if (principal ==NULL ) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } cksum.len =crypto_shash_digestsize (tfm); cksum.data =kmalloc (cksum.len,GFP_KERNEL ); -if (cksum.data ==NULL ) +if (cksum.data ==NULL ) { +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; + } status =crypto_shash_tfm_digest (tfm, principal,strlen (principal), cksum.data);if (status) {kfree (cksum.data); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; }if (memcmp (crp->cr_princhash.data, cksum.data, crp->cr_princhash.len)) {kfree (cksum.data); +up_read (&nn->reclaim_str_hashtbl_lock);return -ENOENT ; }kfree (cksum.data); } crp->cr_clp = clp; +up_read (&nn->reclaim_str_hashtbl_lock);return 0 ; }
@@ -1488,6 +1512,7 @@ nfs4_cld_state_init(struct net *net)for (i =0 ; i <CLIENT_HASH_SIZE ; i++)INIT_LIST_HEAD (&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size =0 ; +init_rwsem (&nn->reclaim_str_hashtbl_lock); nn->track_reclaim_completes = true;atomic_set (&nn->nr_reclaim_complete,0 );
fs/nfsd/nfs4state.c +55 -6
122 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 1026/1102 nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 01c5d5f58a5db9b0ee5afba2e49d3157788687b2 ]
After a DESTROY_SESSION the per-session teardown path can free a session while rpciod still holds an inflight callback rpc_task that dereferences clp->cl_cb_session. nfsd4_probe_callback_sync() flushes cl_callback_wq, but once nfsd4_run_cb_work() has called rpc_call_async() the rpc_task lives on rpciod; flushing the workqueue does not wait for it. rpc_shutdown_client() does drain rpciod tasks, but uses a 1-second wait_event_timeout — tasks stuck in rpc_delay() (e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain.
destroy path rpciod
------------ ------
unhash_session(ses)
nfsd4_probe_callback_sync(clp)
flush_workqueue(cl_callback_wq)
/* returns; rpc_task still live */
nfsd4_put_session_locked(ses)
free_session(ses) -> kfree(ses)
nfsd4_cb_sequence_done()
reads cb_clp->cl_cb_session
/* freed slab */A second window exists in nfsd4_process_cb_update(). When __nfsd4_find_backchannel() returns NULL because unhash_session() has already removed the destroyed session from cl_sessions, setup_callback_client() takes the v4.1 early return so clp->cl_cb_session = ses never fires and the field retains a pointer to the about-to-be-freed session.
Fix both by converting cl_cb_session to an RCU-protected pointer:
- Move the cl_cb_session = ses assignment in setup_callback_client() to after rpc_create() succeeds, so it is only published when a working backchannel exists. Clear cl_cb_session on the error return in nfsd4_process_cb_update(). Both stores use rcu_assign_pointer().
- Annotate cl_cb_session with __rcu. All rpciod-side readers use rcu_read_lock()/rcu_dereference() and check for NULL, bailing to the appropriate error or requeue path:
encode_cb_sequence4args(), decode_cb_sequence4resok(), nfsd41_cb_get_slot(), nfsd41_cb_release_slot(), nfsd4_cb_prepare(), and nfsd4_cb_sequence_done().
- Switch __free_session() from kfree() to kfree_rcu() so the session slab is not reclaimed until after an RCU grace period, guaranteeing that rpciod readers inside rcu_read_lock() never dereference freed memory.
- Pass the session pointer to the nfsd_cb_seq_status and nfsd_cb_free_slot tracepoints instead of having them re-read cl_cb_session.
- nfsd4_cb_prepare() calls rpc_exit() when the session is NULL, routing through the done/release path to requeue the callback.
--- fs/nfsd/nfs4callback.c | 78 ++++++++++++++++++++++++++++++++++++++++--------- fs/nfsd/nfs4state.c | 4 +- fs/nfsd/state.h | 3 + fs/nfsd/trace.h | 14 +++----- 4 files changed, 75 insertions(+), 24 deletions(-)
fs/nfsd/nfs4callback.c +65 -13
@@ -387,12 +387,19 @@ static void encode_cb_sequence4args(struconst struct nfsd4_callback *cb, struct nfs4_cb_compound_hdr *hdr) { - struct nfsd4_session *session = cb->cb_clp->cl_cb_session; + struct nfsd4_session *session;__be32 *p;if (hdr->minorversion ==0 )return ; +rcu_read_lock (); + session =rcu_dereference (cb->cb_clp->cl_cb_session); +if (!session) { +rcu_read_unlock (); +return ; + } +encode_nfs_cb_opnum4 (xdr,OP_CB_SEQUENCE );encode_sessionid4 (xdr, session);
@@ -404,6 +411,7 @@ static void encode_cb_sequence4args(struxdr_encode_empty_array (p);/* csa_referring_call_lists */ hdr->nops++; +rcu_read_unlock (); }/*
@@ -430,21 +438,32 @@ static void encode_cb_sequence4args(strustatic int decode_cb_sequence4resok (struct xdr_stream *xdr,struct nfsd4_callback *cb) { -struct nfsd4_session *session = cb->cb_clp->cl_cb_session; +struct nfsd4_session *session;int status = -ESERVERFAULT ;__be32 *p;u32 dummy; +rcu_read_lock (); + session =rcu_dereference (cb->cb_clp->cl_cb_session); +if (!session) { +rcu_read_unlock (); + cb->cb_seq_status = -NFS4ERR_BADSESSION ; +return -NFS4ERR_BADSESSION ; + } +/* * If the server returns different values for sessionID, slotID or * sequence number, the server is looney tunes. */ p =xdr_inline_decode (xdr,NFS4_MAX_SESSIONID_LEN +4 +4 +4 +4 ); -if (unlikely (p ==NULL )) +if (unlikely (p ==NULL )) { +rcu_read_unlock (); goto out_overflow; + }if (memcmp (p, session->se_sessionid.data,NFS4_MAX_SESSIONID_LEN )) {dprintk ("NFS: %s Invalid session id\n" , __func__); +rcu_read_unlock (); goto out; } p +=XDR_QUADLEN (NFS4_MAX_SESSIONID_LEN );
@@ -452,18 +471,21 @@ static int decode_cb_sequence4resok(stru dummy =be32_to_cpup (p++);if (dummy != session->se_cb_seq_nr) {dprintk ("NFS: %s Invalid sequence number\n" , __func__); +rcu_read_unlock (); goto out; } dummy =be32_to_cpup (p++);if (dummy !=0 ) {dprintk ("NFS: %s Invalid slotid\n" , __func__); +rcu_read_unlock (); goto out; }/* * FIXME: process highest slotid and target highest slotid */ +rcu_read_unlock (); status =0 ; out: cb->cb_seq_status = status;
@@ -1060,9 +1082,8 @@ static int setup_callback_client(struct }else {if (!conn->cb_xprt || !ses)return -EINVAL ; - clp->cl_cb_session = ses; args.bc_xprt = conn->cb_xprt; - args.prognumber = clp->cl_cb_session->se_cb_prog; + args.prognumber = ses->se_cb_prog; args.protocol = conn->cb_xprt->xpt_class->xcl_ident |XPRT_TRANSPORT_BC ; args.authflavor = ses->se_cb_sec.flavor;
@@ -1080,8 +1101,10 @@ static int setup_callback_client(structreturn -ENOMEM ; } -if (clp->cl_minorversion !=0 ) +if (clp->cl_minorversion !=0 ) { clp->cl_cb_conn.cb_xprt = conn->cb_xprt; +rcu_assign_pointer (clp->cl_cb_session, ses); + } clp->cl_cb_client = client; clp->cl_cb_cred = cred;rcu_read_lock ();
@@ -1226,15 +1249,21 @@ static void nfsd4_cb_prepare(struct rpc_trace_nfsd_cb_rpc_prepare (clp); cb->cb_seq_status =1 ; cb->cb_status =0 ; -if (minorversion && !nfsd41_cb_get_slot (cb, task)) -return ; +if (minorversion) { +if (!rcu_access_pointer (clp->cl_cb_session)) { +rpc_exit (task, -EIO ); +return ; + } +if (!nfsd41_cb_get_slot (cb, task)) +return ; + }rpc_call_start (task); }static bool nfsd4_cb_sequence_done (struct rpc_task *task,struct nfsd4_callback *cb) {struct nfs4_client *clp = cb->cb_clp; -struct nfsd4_session *session = clp->cl_cb_session; +struct nfsd4_session *session;bool ret = true;if (!clp->cl_minorversion) {
@@ -1256,8 +1285,15 @@ static bool nfsd4_cb_sequence_done(strucif (!cb->cb_holds_slot) goto need_restart; +rcu_read_lock (); + session =rcu_dereference (clp->cl_cb_session); +if (!session) { +rcu_read_unlock (); + goto need_restart; + } +/* This is the operation status code for CB_SEQUENCE */ -trace_nfsd_cb_seq_status (task, cb); +trace_nfsd_cb_seq_status (task, cb, session);switch (cb->cb_seq_status) {case 0 :/*
@@ -1285,19 +1321,25 @@ static bool nfsd4_cb_sequence_done(struccase -NFS4ERR_BADSESSION :nfsd4_mark_cb_fault (cb->cb_clp); ret = false; +rcu_read_unlock (); goto need_restart;case -NFS4ERR_DELAY : cb->cb_seq_status =1 ; -if (!rpc_restart_call (task)) +if (!rpc_restart_call (task)) { +rcu_read_unlock (); goto out; + }rpc_delay (task,2 *HZ ); +rcu_read_unlock ();return false;case -NFS4ERR_BADSLOT : +rcu_read_unlock (); goto retry_nowait;case -NFS4ERR_SEQ_MISORDERED :if (session->se_cb_seq_nr !=1 ) { session->se_cb_seq_nr =1 ; +rcu_read_unlock (); goto retry_nowait; }break ;
@@ -1306,7 +1348,8 @@ static bool nfsd4_cb_sequence_done(struc }nfsd41_cb_release_slot (cb); -trace_nfsd_cb_free_slot (task, cb); +trace_nfsd_cb_free_slot (task, cb, session); +rcu_read_unlock ();if (RPC_SIGNALLED (task)) goto need_restart;
@@ -1415,7 +1458,15 @@ static struct nfsd4_conn * __nfsd4_find_* Note there isn't a lot of locking in this code; instead we depend on * the fact that it is run from clp->cl_callback_wq, which won't run two * work items at once. So, for example, clp->cl_callback_wq handles all -* access of cl_cb_client and all calls to rpc_create or rpc_shutdown_client. +* access of cl_cb_client, and all calls to rpc_create or +* rpc_shutdown_client. +* +* cl_cb_session is written only from cl_callback_wq (via +* rcu_assign_pointer) and read from rpciod under rcu_read_lock (via +* rcu_dereference) by encode_cb_sequence4args(), decode_cb_sequence4resok(), +* and nfsd4_cb_sequence_done(). Sessions are freed with kfree_rcu() so that +* rpciod readers in an RCU read-side critical section never dereference a +* freed session. */ static void nfsd4_process_cb_update (struct nfsd4_callback *cb) {
@@ -1467,6 +1518,7 @@ static void nfsd4_process_cb_update(strunfsd4_mark_cb_down (clp);if (c)svc_xprt_put (c->cn_xprt); +rcu_assign_pointer (clp->cl_cb_session, ses);return ; } }
fs/nfsd/nfs4state.c +2 -2
@@ -2223,7 +2223,7 @@ static void nfsd4_del_conns(struct nfsd4static void __free_session (struct nfsd4_session *ses) {free_session_slots (ses); -kfree (ses); +kfree_rcu (ses, rcu_head); }static void free_session (struct nfsd4_session *ses)
@@ -3283,7 +3283,7 @@ static struct nfs4_client *create_clientclear_bit (0 , &clp->cl_cb_slot_busy);copy_verf (clp, verf);memcpy (&clp->cl_addr, sa,sizeof (struct sockaddr_storage )); - clp->cl_cb_session =NULL ; +RCU_INIT_POINTER (clp->cl_cb_session,NULL ); clp->net = net; clp->cl_nfsd_dentry = nfsd_client_mkdir( nn, &clp->cl_nfsdfs,
fs/nfsd/state.h +2 -1
@@ -310,6 +310,7 @@ struct nfsd4_session {struct list_head se_conns;u32 se_cb_prog;u32 se_cb_seq_nr; +struct rcu_head rcu_head;struct nfsd4_slot *se_slots[];/* forward channel slots */ };
@@ -422,7 +423,7 @@ struct nfs4_client {#define NFSD4_CB_FAULT 3int cl_cb_state;struct nfsd4_callback cl_cb_null; -struct nfsd4_session *cl_cb_session; +struct nfsd4_session __rcu *cl_cb_session;/* for all client information that callback code might need: */ spinlock_t cl_lock;
fs/nfsd/trace.h +6 -8
@@ -1619,9 +1619,10 @@ DEFINE_NFSD_CB_LIFETIME_EVENT(bc_shutdow TRACE_EVENT(nfsd_cb_seq_status, TP_PROTO( const struct rpc_task *task, - const struct nfsd4_callback *cb + const struct nfsd4_callback *cb, + const struct nfsd4_session *session ), - TP_ARGS(task, cb), + TP_ARGS(task, cb, session), TP_STRUCT__entry( __field(unsigned int, task_id) __field(unsigned int, client_id)
@@ -1633,8 +1634,6 @@ TRACE_EVENT(nfsd_cb_seq_status,__field (int, seq_status) ),TP_fast_assign ( - const struct nfs4_client *clp = cb->cb_clp; -const struct nfsd4_session *session = clp->cl_cb_session; const struct nfsd4_sessionid *sid = (struct nfsd4_sessionid *)&session->se_sessionid;
@@ -1660,9 +1659,10 @@ TRACE_EVENT(nfsd_cb_seq_status, TRACE_EVENT(nfsd_cb_free_slot, TP_PROTO( const struct rpc_task *task, - const struct nfsd4_callback *cb + const struct nfsd4_callback *cb, + const struct nfsd4_session *session ), - TP_ARGS(task, cb), + TP_ARGS(task, cb, session), TP_STRUCT__entry( __field(unsigned int, task_id) __field(unsigned int, client_id)
@@ -1673,8 +1673,6 @@ TRACE_EVENT(nfsd_cb_free_slot,__field (u32, slot_seqno) ),TP_fast_assign ( - const struct nfs4_client *clp = cb->cb_clp; -const struct nfsd4_session *session = clp->cl_cb_session; const struct nfsd4_sessionid *sid = (struct nfsd4_sessionid *)&session->se_sessionid;
Greg Kroah-Hartman
maintainerPATCH 1027/1102 nfsd: fix clock domain mismatch in clients_still_reclaiming()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 09ea3eb9a518565f5bca386e81b993ed8825f5e8 ]
clients_still_reclaiming() computes a deadline from nn->boot_time (CLOCK_REALTIME, ~1.7 billion) but compares it against ktime_get_boottime_seconds() (CLOCK_BOOTTIME, seconds since boot). The comparison is always false — it would take ~54 years of uptime for BOOTTIME to exceed the REALTIME-derived deadline.
This means any client can hold the server in grace indefinitely by sending CLAIM_PREVIOUS OPEN requests, blocking all non-reclaim operations for all other clients.
Add boot_time_bt (CLOCK_BOOTTIME) alongside the existing boot_time and use it for the deadline computation. boot_time (CLOCK_REALTIME) is preserved for its cl_boot clientid-nonce role.
--- fs/nfsd/netns.h | 1 + fs/nfsd/nfs4state.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
fs/nfsd/netns.h +1 -0
@@ -69,6 +69,7 @@ struct nfsd_net {bool grace_end_forced;bool client_tracking_active;time64_t boot_time; +time64_t boot_time_bt;/* same instant in CLOCK_BOOTTIME */ struct dentry *nfsd_client_dir;
fs/nfsd/nfs4state.c +2 -1
@@ -6469,7 +6469,7 @@ bool nfsd4_force_end_grace(struct nfsd_n*/ static bool clients_still_reclaiming (struct nfsd_net *nn) { -time64_t double_grace_period_end = nn->boot_time + +time64_t double_grace_period_end = nn->boot_time_bt +2 * nn->nfsd4_lease;if (READ_ONCE (nn->grace_end_forced))
@@ -8877,6 +8877,7 @@ static int nfs4_state_create_net(struct nn->conf_name_tree =RB_ROOT ; nn->unconf_name_tree =RB_ROOT ; nn->boot_time =ktime_get_real_seconds (); + nn->boot_time_bt =ktime_get_boottime_seconds (); nn->grace_ended = false; nn->grace_end_forced = false; nn->client_tracking_active = false;
Greg Kroah-Hartman
maintainerPATCH 1028/1102 nfsd: fix netlink dumpit error handling for rpc_status_get
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit e13d505af73a1e013aa81806652d4378b21cfca2 ]
nfsd_genl_rpc_status_compose_msg() returns -ENOBUFS on nla_put failure without calling genlmsg_cancel(), leaving a partial message in the skb. The caller then propagates -ENOBUFS directly, which the netlink dump infrastructure treats as a fatal error, aborting the entire dump.
The correct netlink dump convention is:
- Cancel any partial message with genlmsg_cancel()
- If prior messages were added to the skb (skb->len > 0), save the current iterator position and return skb->len to paginate
- Only return a negative errno when no messages fit at all
Fix compose_msg to cancel the partial message on all nla_put failure paths, and fix the caller to paginate when possible rather than returning a fatal error.
A second defect surfaces once pagination actually works: cb->args[1] records the resume index within the pool named by cb->args[0], but the inner loop applied it to every pool from cb->args[0] onward. After a mid-pool pause, a later dump call drains the resume pool and continues into subsequent pools within the same call, where the stale cb->args[1] caused the first N threads of each following pool to be skipped. On per-CPU or per-node pool configurations this silently dropped active requests from the dump. Apply the saved thread index only to the pool matching cb->args[0], and start every subsequent pool from thread 0.
--- fs/nfsd/nfsctl.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
fs/nfsd/nfsctl.c +26 -6
@@ -1527,7 +1527,7 @@ static int nfsd_genl_rpc_status_compose_nla_put_s64 (skb,NFSD_A_RPC_STATUS_SERVICE_TIME ,ktime_to_us (genl_rqstp->rq_stime), NFSD_A_RPC_STATUS_PAD)) - return -ENOBUFS; + goto out_cancel;switch (genl_rqstp->rq_saddr.ss_family) {case AF_INET : {
@@ -1543,7 +1543,7 @@ static int nfsd_genl_rpc_status_compose_ s_in->sin_port) || nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, d_in->sin_port)) - return -ENOBUFS; + goto out_cancel;break ; }case AF_INET6 : {
@@ -1559,7 +1559,7 @@ static int nfsd_genl_rpc_status_compose_ s_in->sin6_port) ||nla_put_be16 (skb,NFSD_A_RPC_STATUS_DPORT , d_in->sin6_port)) - return -ENOBUFS; + goto out_cancel;break ; } }
@@ -1567,10 +1567,14 @@ static int nfsd_genl_rpc_status_compose_for (i =0 ; i < genl_rqstp->rq_opcnt; i++)if (nla_put_u32 (skb,NFSD_A_RPC_STATUS_COMPOUND_OPS , genl_rqstp->rq_opnum[i])) -return -ENOBUFS ; + goto out_cancel;genlmsg_end (skb, hdr);return 0 ; + +out_cancel: +genlmsg_cancel (skb, hdr); +return -ENOBUFS ; }/**
@@ -1598,10 +1602,20 @@ int nfsd_nl_rpc_status_get_dumpit(structfor (i =0 ; i < nn->nfsd_serv->sv_nrpools; i++) {struct svc_rqst *rqstp; +long thread_skip =0 ;if (i < cb->args[0 ])/* already consumed */ continue ; +/* +* The saved thread index only applies to the pool the dump +* was resumed in. Subsequent pools must start from thread 0, +* otherwise their first cb->args[1] threads are silently +* skipped. +*/ +if (i == cb->args[0 ]) + thread_skip = cb->args[1 ]; + rqstp_index =0 ; list_for_each_entry_rcu(rqstp, &nn->nfsd_serv->sv_pools[i].sp_all_threads,
@@ -1609,7 +1623,7 @@ int nfsd_nl_rpc_status_get_dumpit(structstruct nfsd_genl_rqstp genl_rqstp = {};unsigned int status_counter; -if (rqstp_index++ < cb->args[1 ])/* already consumed */ +if (rqstp_index++ < thread_skip)/* already consumed */ continue ;/* * Acquire rq_status_counter before parsing the rqst
@@ -1663,8 +1677,14 @@ int nfsd_nl_rpc_status_get_dumpit(struct ret =nfsd_genl_rpc_status_compose_msg (skb, cb, &genl_rqstp); -if (ret) +if (ret) { +if (skb->len) { + cb->args[0 ] = i; + cb->args[1 ] = rqstp_index -1 ; + ret = skb->len; + } goto out; + } } }
Greg Kroah-Hartman
maintainerPATCH 1029/1102 nfsd: update mtime/ctime on COPY in presence of delegated attributes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Olga Kornievskaia <[email protected]>
[ Upstream commit 4183cf383b6faec17a0882b84cd2d901dba62b16 ]
When delegated attributes are given on open, the file is opened with NOCMTIME and modifying operations do not update mtime/ctime as to not get out-of-sync with the client's delegated view. However, for COPY operation, the server should update its view of mtime/ctime and reflect that in any GETATTR queries.
[ Stable adaptation: Linux 6.12 does not contain the Fixes commit or nfsd_update_cmtime_attr(), so omit the timestamp-update call sites. Keep the attr_update context needed by d0beaee498e1 ("nfsd: fix stale s2s_cp_stateids IDR entry for async COPY"), and alias its newer error label to the existing 6.12 cleanup path. ]
Stable-dep-of: d0beaee498e1 ("nfsd: fix stale s2s_cp_stateids IDR entry for async COPY")
--- fs/nfsd/nfs4proc.c | 8 ++++++-- fs/nfsd/xdr4.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-)
fs/nfsd/nfs4proc.c +6 -2
@@ -1870,12 +1870,15 @@ nfsd4_copy(struct svc_rqst *rqstp, struc goto out_err; async_copy->cp_src =kmalloc (sizeof (*async_copy->cp_src),GFP_KERNEL );if (!async_copy->cp_src) - goto out_err; + goto out_dec_async_copy_err;if (!nfs4_init_copy_state (nn, copy)) - goto out_err; + goto out_dec_async_copy_err;memcpy (&result->cb_stateid, ©->cp_stateid.cs_stid,sizeof (result->cb_stateid));dup_copy_fields (copy, async_copy); +if ((READ_ONCE (copy->nf_dst->nf_file->f_mode) & +FMODE_NOCMTIME ) !=0 ) + async_copy->attr_update = true; async_copy->copy_task =kthread_create (nfsd4_do_async_copy, async_copy,"%s" ,"copy thread" );if (IS_ERR (async_copy->copy_task))
@@ -1894,6 +1897,7 @@ out:trace_nfsd_copy_done (copy, status);release_copy_files (copy);return status; +out_dec_async_copy_err: out_err:if (nfsd4_ssc_is_inter (copy)) {/*
fs/nfsd/xdr4.h +1 -0
@@ -736,6 +736,7 @@ struct nfsd4_copy {struct nfsd_file *nf_src;struct nfsd_file *nf_dst; +bool attr_update;copy_stateid_t cp_stateid;
Greg Kroah-Hartman
maintainerPATCH 1030/1102 nfsd: fix stale s2s_cp_stateids IDR entry for async COPY
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit d0beaee498e11880e72826026db0e9c9890fc114 ]
For an async COPY, nfsd4_copy() called nfs4_init_copy_state() before dup_copy_fields(), so the s2s_cp_stateids IDR was pointed at &u->copy->cp_stateid -- memory in the per-rqstp COMPOUND buffer that is reused by the next request. dup_copy_fields() copies only the value into async_copy, so the IDR slot dangled at the transient buffer for the whole background copy. Any IDR walker then dereferences reused request memory:
the laundromat reads cs_type from it and, if the bytes look like an expired NFS4_COPYNOTIFY_STID, follows into refcount_dec()/idr_remove()/kfree() on garbage; manage_cpntf_state() has the same exposure via idr_find().
Duplicate the fields first, then register the stateid on the stable async_copy. result->cb_stateid is unchanged.
--- fs/nfsd/nfs4proc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
fs/nfsd/nfs4proc.c +4 -3
@@ -1871,11 +1871,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struc async_copy->cp_src =kmalloc (sizeof (*async_copy->cp_src),GFP_KERNEL );if (!async_copy->cp_src) goto out_dec_async_copy_err; -if (!nfs4_init_copy_state (nn, copy)) +dup_copy_fields (copy, async_copy); + +if (!nfs4_init_copy_state (nn, async_copy)) goto out_dec_async_copy_err; -memcpy (&result->cb_stateid, ©->cp_stateid.cs_stid, +memcpy (&result->cb_stateid, &async_copy->cp_stateid.cs_stid,sizeof (result->cb_stateid)); -dup_copy_fields (copy, async_copy);if ((READ_ONCE (copy->nf_dst->nf_file->f_mode) &FMODE_NOCMTIME ) !=0 ) async_copy->attr_update = true;
Greg Kroah-Hartman
maintainerPATCH 1031/1102 nfsd: close shrinker/GC/fsnotify vs per-net shutdown race in filecache
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 40162cfea79b9510380decfdd1795b754dc9f972 ]
The shrinker, GC worker, and fsnotify/lease callbacks can unhash an nfsd_file from the rhashtable and then call nfsd_file_dispose_list_delayed() to move it to the per-net dispose list. If nfsd_file_cache_shutdown_net() runs concurrently, its rhashtable walk misses the already-unhashed file, and its drain of the per-net dispose list can run before the file has been queued. The file then sits on the per-net list with no thread to drain it, leaking both the file and its associated state.
The GC worker and shrinker already hold nfsd_gc_lock while walking the LRU, but in the original code they release it before calling nfsd_file_dispose_list_delayed(). The fsnotify/lease path (nfsd_file_close_inode) has no synchronization at all.
Fix this by:
1. Widening nfsd_gc_lock in both nfsd_file_gc() and nfsd_file_lru_scan() to cover the nfsd_file_dispose_list_delayed() call.
2. Wrapping nfsd_file_close_inode() in nfsd_gc_lock so that all three callers of nfsd_file_dispose_list_delayed() hold the lock.
3. Adding a spin_lock/unlock(nfsd_gc_lock) barrier in nfsd_file_cache_shutdown_net() after the purge, so that any in-progress disposal has fully completed before the per-net list is drained.
All operations inside the lock are non-sleeping (rhashtable lookups, atomic bit/refcount ops, list moves, svc_wake_up), so the spinlock is appropriate.
--- fs/nfsd/filecache.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
fs/nfsd/filecache.c +31 -3
@@ -54,6 +54,12 @@/* We only care about NFSD_MAY_READ/WRITE for this cache */ #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO) +/* +* Serialize callers of nfsd_file_dispose_list_delayed() against per-net +* shutdown. +*/ +static DEFINE_SPINLOCK (nfsd_gc_lock); + static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits); static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions); static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations);
@@ -436,11 +442,17 @@ nfsd_file_dispose_list(struct list_head* * Transfers each file to the "freeme" list for its nfsd_net, to eventually * be disposed of by the per-net garbage collector. +* +* All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net() +* can synchronize against them before draining the per-net dispose list. +* This guarantees nf_net is still live when we call net_generic(). */ static void nfsd_file_dispose_list_delayed (struct list_head *dispose) { -while (!list_empty (dispose)) { +lockdep_assert_held (&nfsd_gc_lock); + +while (!list_empty (dispose)) {struct nfsd_file *nf =list_first_entry (dispose, struct nfsd_file, nf_gc);struct nfsd_net *nn =net_generic (nf->nf_net, nfsd_net_id);
@@ -565,10 +577,12 @@ nfsd_file_gc(void)LIST_HEAD (dispose);unsigned long ret; +spin_lock (&nfsd_gc_lock); ret =list_lru_walk (&nfsd_file_lru, nfsd_file_lru_cb, &dispose,list_lru_count (&nfsd_file_lru)); -trace_nfsd_file_gc_removed (ret,list_lru_count (&nfsd_file_lru));nfsd_file_dispose_list_delayed (&dispose); +spin_unlock (&nfsd_gc_lock); +trace_nfsd_file_gc_removed (ret,list_lru_count (&nfsd_file_lru)); } static void
@@ -591,10 +605,14 @@ nfsd_file_lru_scan(struct shrinker *s, sLIST_HEAD (dispose);unsigned long ret; +if (!spin_trylock (&nfsd_gc_lock)) +return SHRINK_STOP ; + ret =list_lru_shrink_walk (&nfsd_file_lru, sc, nfsd_file_lru_cb, &dispose); -trace_nfsd_file_shrinker_removed (ret,list_lru_count (&nfsd_file_lru));nfsd_file_dispose_list_delayed (&dispose); +spin_unlock (&nfsd_gc_lock); +trace_nfsd_file_shrinker_removed (ret,list_lru_count (&nfsd_file_lru));return ret; }
@@ -680,8 +698,10 @@ nfsd_file_close_inode(struct inode *inod {LIST_HEAD (dispose); +spin_lock (&nfsd_gc_lock);nfsd_file_queue_for_close (inode, &dispose);nfsd_file_dispose_list_delayed (&dispose); +spin_unlock (&nfsd_gc_lock); }/**
@@ -927,6 +947,14 @@ void nfsd_file_cache_shutdown_net(struct net *net) {nfsd_file_cache_purge (net); +/* +* Ensure any in-progress shrinker, GC, or fsnotify/lease callback +* (all of which hold nfsd_gc_lock while calling +* nfsd_file_dispose_list_delayed()) has fully completed before +* draining the per-net dispose list. +*/ +spin_lock (&nfsd_gc_lock); +spin_unlock (&nfsd_gc_lock);nfsd_free_fcache_disposal_net (net); }
Greg Kroah-Hartman
maintainerPATCH 1032/1102 NFSD: Prevent client use-after-free during admin state revocation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit e270e5a0778e5bff852c8862ce9576ce70359393 ]
A stateid holds only a bare pointer to its nfs4_client; a stateid reference does not pin it. The client survives only because __destroy_client() drains its stateids before free_client() runs.
nfsd4_revoke_states() drops nn->client_lock across revoke_one_stid(), which dereferences the client to revoke a stateid and read clp->cl_minorversion. A teardown racing the dropped lock can free the client first.
Pinning cl_rpc_users under client_lock blocks the DESTROY_CLIENTID and EXCHANGE_ID teardown, which refuses while cl_rpc_users is non-zero. force_expire_client() ignores it: once its wait for cl_rpc_users to reach zero has passed, a later pin goes unnoticed.
Under client_lock, skip a client whose cl_time is already zero -- force_expire_client() clears it there before waiting -- otherwise pin cl_rpc_users before dropping the lock. The walk then either sees the expiry and skips, or pins in time for that wait to cover the revoke.
--- fs/nfsd/nfs4state.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +20 -2
@@ -1840,13 +1840,23 @@ void nfsd4_revoke_states(struct nfsd_netstruct nfs4_client *clp; retry:list_for_each_entry (clp, head, cl_idhash) { -struct nfs4_stid *stid =find_one_sb_stid (clp, sb, - sc_types); +struct nfs4_stid *stid; + +/* +* force_expire_client() ignores cl_rpc_users once +* its wait_event() has passed, so pinning cannot +* keep an already-expiring client alive; the +* expiry path revokes its states instead. +*/ +if (is_client_expired (clp)) +continue ; + stid =find_one_sb_stid (clp, sb, sc_types);if (stid) {struct nfs4_ol_stateid *stp;struct nfs4_delegation *dp;struct nfs4_layout_stateid *ls; +atomic_inc (&clp->cl_rpc_users);spin_unlock (&nn->client_lock);switch (stid->sc_type) {case SC_TYPE_OPEN :
@@ -1930,6 +1940,9 @@ void nfsd4_revoke_states(struct nfsd_net*/ nn->nfs40_last_revoke =ktime_get_boottime_seconds (); +if (atomic_dec_and_test (&clp->cl_rpc_users) && +is_client_expired (clp)) +wake_up_all (&expiry_wq); goto retry; } }
@@ -3129,6 +3142,11 @@ static void force_expire_client(struct ntrace_nfsd_clid_admin_expired (&clp->cl_clientid); +/* +* cl_time is cleared under client_lock before the wait so a +* revocation walk pinning cl_rpc_users under it either skips +* this client or is seen by this wait_event(). +*/ spin_lock (&nn->client_lock); clp->cl_time =0 ;spin_unlock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 1033/1102 nfsd: disallow file locking and delegations for NFSv4 reexport
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mike Snitzer <[email protected]>
[ Upstream commit 9254c8ae9b8172d9ad26e620a4bbc604a1efa7fa ]
We do not and cannot support file locking with NFS reexport over NFSv4.x for the same reason we don't do it for NFSv3: NFS reexport server reboot cannot allow clients to recover locks because the source NFS server has not rebooted, and so it is not in grace. Since the source NFS server is not in grace, it cannot offer any guarantees that the file won't have been changed between the locks getting lost and any attempt to recover/reclaim them. The same applies to delegations and any associated locks, so disallow them too.
Clients are no longer allowed to get file locks or delegations from a reexport server, any attempts will fail with operation not supported.
Update the "Reboot recovery" section accordingly in Documentation/filesystems/nfs/reexport.rst
Stable-dep-of: 4683ca76b3b7 ("NFSD: Prevent client use-after-free during delegation revoke")
--- Documentation/filesystems/nfs/reexport.rst | 10 ++- fs/nfs/export.c | 3 - fs/nfsd/netns.h | 3 + fs/nfsd/nfs4state.c | 75 ++++++++++++++++++----------- fs/nfsd/state.h | 2 include/linux/exportfs.h | 1 6 files changed, 61 insertions(+), 33 deletions(-)
Documentation/filesystems/nfs/reexport.rst +7 -3
@@ -26,9 +26,13 @@ Reboot recovery --------------- The NFS protocol's normal reboot recovery mechanisms don't work for the -case when the reexport server reboots. Clients will lose any locks -they held before the reboot, and further IO will result in errors. -Closing and reopening files should clear the errors. +case when the reexport server reboots because the source server has not +rebooted, and so it is not in grace. Since the source server is not in +grace, it cannot offer any guarantees that the file won't have been +changed between the locks getting lost and any attempt to recover them. +The same applies to delegations and any associated locks. Clients are +not allowed to get file locks or delegations from a reexport server, any +attempts will fail with operation not supported. Filehandle limits -----------------
fs/nfs/export.c +2 -1
@@ -161,5 +161,6 @@ const struct export_operations nfs_expor EXPORT_OP_CLOSE_BEFORE_UNLINK | EXPORT_OP_REMOTE_FS | EXPORT_OP_NOATOMIC_ATTR | - EXPORT_OP_FLUSH_ON_CLOSE, + EXPORT_OP_FLUSH_ON_CLOSE | + EXPORT_OP_NOLOCKS, };
fs/nfsd/netns.h +3 -0
@@ -105,6 +105,9 @@ struct nfsd_net {*/ struct list_head client_lru;struct list_head close_lru; + +/* protects del_recall_lru and delegation hash/unhash */ +spinlock_t deleg_lock____cacheline_aligned ;struct list_head del_recall_lru;/* protected by blocked_locks_lock */
fs/nfsd/nfs4state.c +47 -28
210 lines not on this page · raw message
fs/nfsd/state.h +1 -1
@@ -101,7 +101,7 @@ struct nfs4_stid {#define SC_TYPE_LAYOUT BIT(3)unsigned short sc_type; -/* state_lock protects sc_status for delegation stateids. +/* nn->deleg_lock protects sc_status for delegation stateids. * ->cl_lock protects sc_status for open and lock stateids. * ->st_mutex also protect sc_status for open stateids. * ->ls_lock protects sc_status for layout stateids.
include/linux/exportfs.h +1 -0
@@ -247,6 +247,7 @@ struct export_operations {*/ #define EXPORT_OP_FLUSH_ON_CLOSE (0x20)/* fs flushes file data on close */ #define EXPORT_OP_ASYNC_LOCK (0x40)/* fs can do async lock request */ +#define EXPORT_OP_NOLOCKS (0x80)/* no file locking support */ unsigned long flags; };
Greg Kroah-Hartman
maintainerPATCH 1034/1102 NFSD: Prevent client use-after-free during delegation revoke
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 4683ca76b3b7e5808338491c6eb3c20e6b4894d5 ]
A delegation stateid holds only a bare pointer to its owning nfs4_client and does not keep it alive. The client survives its stateids only because __destroy_client() drains cl_delegations and cl_revoked before free_client() runs.
nfs4_laundromat() breaks that invariant: it unhashes an expired delegation from cl_delegations, drops deleg_lock, then revoke_delegation() relinks it onto cl_revoked under cl_lock. In that window the delegation is on neither list, so client_has_state() can report no remaining state.
Every teardown path first requires cl_rpc_users to be zero, but the laundromat holds no such reference. A client whose recalled delegation has just timed out can therefore reach free_client() while revoke_delegation() is still about to dereference cl_lock, a use-after-free.
Pin the client with cl_rpc_users across the revoke so teardown blocks until it completes, then reap the delegation from cl_revoked. A client already expiring reaps its own, so skip it and leave the delegation on del_recall_lru.
--- fs/nfsd/netns.h | 6 ++++-- fs/nfsd/nfs4state.c | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-)
fs/nfsd/netns.h +4 -2
@@ -106,7 +106,8 @@ struct nfsd_net {struct list_head client_lru;struct list_head close_lru; -/* protects del_recall_lru and delegation hash/unhash */ +/* protects del_recall_lru and delegation hash/unhash; +* nests outside client_lock */ spinlock_t deleg_lock____cacheline_aligned ;struct list_head del_recall_lru;
@@ -115,7 +116,8 @@ struct nfsd_net {struct delayed_work laundromat_work; -/* client_lock protects the client lru list and session hash table */ +/* client_lock protects the client lru list and session hash +* table; nests inside deleg_lock */ spinlock_t client_lock;/* protects blocked_locks_lru */
fs/nfsd/nfs4state.c +23 -0
@@ -6783,6 +6783,7 @@ nfs4_laundromat(struct nfsd_net *nn) .new_timeo = nn->nfsd4_lease };struct nfs4_cpntf_state *cps; +struct nfs4_client *clp;copy_stateid_t *cps_t;int i;
@@ -6810,6 +6811,18 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);if (!state_expired (<, dp->dl_time))break ; + clp = dp->dl_stid.sc_client; +spin_lock (&nn->client_lock); +if (is_client_expired (clp)) { +spin_unlock (&nn->client_lock); +continue ; + } +/* +* Pin without reviving: get_client_locked() would +* flip a courtesy client back to NFSD4_ACTIVE. +*/ +atomic_inc (&clp->cl_rpc_users); +spin_unlock (&nn->client_lock);refcount_inc (&dp->dl_stid.sc_count);unhash_delegation_locked (dp,SC_STATUS_REVOKED );list_add (&dp->dl_recall_lru, &reaplist);
@@ -6818,8 +6831,18 @@ nfs4_laundromat(struct nfsd_net *nn)while (!list_empty (&reaplist)) { dp = list_first_entry(&reaplist, struct nfs4_delegation, dl_recall_lru); + clp = dp->dl_stid.sc_client;list_del_init (&dp->dl_recall_lru);revoke_delegation (dp); +/* +* Unpin without renewing: put_client_renew() would +* renew the reaped client's lease. +*/ +if (atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) { +if (is_client_expired (clp)) +wake_up_all (&expiry_wq); +spin_unlock (&nn->client_lock); + } }spin_lock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 1035/1102 ceph: Remove fs/ceph deadcode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Dr. David Alan Gilbert" <[email protected]>
[ Upstream commit 6025b482e48041cd71111ab4f7cc28e0371b2e3e ]
ceph_caps_revoking() has been unused since 2017's commit 3fb99d483e61 ("ceph: nuke startsync op")
ceph_mdsc_open_export_target_sessions() has been unused since 2013's commit 11df2dfb610d ("ceph: add imported caps when handling cap export message")
Remove them.
Stable-dep-of: 8fdf94644573 ("ceph: force a cap message when a deferred revoke can't be acked immediately")
--- fs/ceph/caps.c | 14 -------------- fs/ceph/mds_client.c | 8 -------- fs/ceph/mds_client.h | 2 -- fs/ceph/super.h | 1 - 4 files changed, 25 deletions(-)
fs/ceph/caps.c +0 -14
@@ -978,20 +978,6 @@ int __ceph_caps_revoking_other(struct cereturn 0 ; } -int ceph_caps_revoking (struct ceph_inode_info *ci,int mask) -{ -struct inode *inode = &ci->netfs.inode; -struct ceph_client *cl =ceph_inode_to_client (inode); -int ret; - -spin_lock (&ci->i_ceph_lock); - ret =__ceph_caps_revoking_other (ci,NULL , mask); -spin_unlock (&ci->i_ceph_lock); -doutc (cl,"%p %llx.%llx %s = %d\n" , inode,ceph_vinop (inode), -ceph_cap_string (mask), ret); -return ret; -} -int __ceph_caps_used (struct ceph_inode_info *ci) {int used =0 ;
fs/ceph/mds_client.c +0 -8
@@ -1748,14 +1748,6 @@ static void __open_export_target_session } } -void ceph_mdsc_open_export_target_sessions (struct ceph_mds_client *mdsc, -struct ceph_mds_session *session) -{ -mutex_lock (&mdsc->mutex); -__open_export_target_sessions (mdsc, session); -mutex_unlock (&mdsc->mutex); -} -/* * session caps */
fs/ceph/mds_client.h +0 -2
@@ -646,8 +646,6 @@ extern void ceph_mdsc_handle_fsmap(strucextern struct ceph_mds_session *ceph_mdsc_open_export_target_session (struct ceph_mds_client *mdsc,int target); -extern void ceph_mdsc_open_export_target_sessions (struct ceph_mds_client *mdsc, -struct ceph_mds_session *session); extern int ceph_trim_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session,
fs/ceph/super.h +0 -1
@@ -822,7 +822,6 @@ extern int __ceph_mark_dirty_caps(structextern int __ceph_caps_revoking_other (struct ceph_inode_info *ci,struct ceph_cap *ocap,int mask); -extern int ceph_caps_revoking (struct ceph_inode_info *ci,int mask);extern int __ceph_caps_used (struct ceph_inode_info *ci);static inline bool __ceph_is_file_opened (struct ceph_inode_info *ci)
Greg Kroah-Hartman
maintainerPATCH 1036/1102 ceph: force a cap message when a deferred revoke cant be acked immediately
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 8fdf946445732c2bcd685abc8bd0e509d2ebc158 ]
When the MDS revokes capabilities, handle_cap_grant() normally guarantees a response by setting CHECK_CAPS_FLUSH_FORCE (see commit 31634d7597d8 ("ceph: force sending a cap update msg back to MDS for revoke op")), so ceph_check_caps() sends a cap message even if the client would otherwise decide it has nothing to do. That guarantee is skipped whenever the revoke has to be deferred (via revoke_wait):
revoking Fb while dirty data is still buffered (writeback is queued first) or revoking Fc while pages are cached (async invalidation is queued first).
In those cases, the ack is left to the deferred completion (ceph_put_wrbuffer_cap_refs() after writeback, or the invalidate worker after invalidation); both of which call ceph_check_caps(ci,0) i.e. without CHECK_CAPS_FLUSH_FORCE. Nothing gets sent under one of the following conditions:
- the inode is retaining caps because the file was used recently (file_wanted != 0; retain |= CEPH_CAP_ANY)
- the revoked cap is still used because the page was re-cached (e.g. a file being re-read)
- the MDS has meanwhile re-granted, so issued==implemented and the client sees nothing being revoked
The client then never emits the cap message which the MDS is waiting for. The MDS blocks on the revoke indefinitely and logs, for minutes or hours:
client.NNN isn't responding to mclientcaps(revoke), ino 0x... pending pAsxLsXsxFsxcrwb issued pAsxLsXsxFsxcrwb, sent 964.899182 seconds ago
The client-side state at that point shows the full cap set still issued, nothing in the revoking/flushing sets. Thus nothing gets sent.
This patch fixes it by remembering that a forced response is expected. When a revoke is deferred, set CEPH_I_FLUSH_FORCE on the inode. ceph_check_caps() replays it as CHECK_CAPS_FLUSH_FORCE, so whichever path re-checks the inode next (the writeback/invalidate completion, the delayed worker, or any other caller) is guaranteed to send a cap message to the MDS. __prep_cap() clears the flag once a message is actually built.
This is the deferred-path counterpart of the existing CHECK_CAPS_FLUSH_FORCE handling; a normal (non-deferred) revoke still forces the response inline as before.
--- fs/ceph/caps.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++------- fs/ceph/super.h | 4 +++ 2 files changed, 60 insertions(+), 7 deletions(-)
fs/ceph/caps.c +56 -7
@@ -978,6 +978,27 @@ int __ceph_caps_revoking_other(struct cereturn 0 ; } +/* +* Return true if any cap of this inode holds caps which the MDS has +* revoked, but which we have not released yet. +*/ +static bool __ceph_is_any_revoking (const struct ceph_inode_info *ci) +{ +const struct rb_node *p; + +lockdep_assert_held (&ci->i_ceph_lock); + +for (p =rb_first (&ci->i_caps); p; p =rb_next (p)) { +const struct ceph_cap *cap = + rb_entry(p, struct ceph_cap, ci_node); + +if (cap->implemented & ~cap->issued) +return true; + } + +return false; +} +int __ceph_caps_used (struct ceph_inode_info *ci) {int used =0 ;
@@ -1420,6 +1441,9 @@ static void __prep_cap(struct cap_msg_ar cap->implemented &= cap->issued | used; cap->mds_wanted = want; +if ((ci->i_ceph_flags &CEPH_I_FLUSH_FORCE ) !=0 && !__ceph_is_any_revoking (ci)) + ci->i_ceph_flags &= ~CEPH_I_FLUSH_FORCE ; + arg->session = cap->session; arg->ino =ceph_vino (inode).ino; arg->cid = cap->cap_id;
@@ -2034,6 +2058,14 @@ void ceph_check_caps(struct ceph_inode_iif (ci->i_ceph_flags &CEPH_I_FLUSH ) flags |=CHECK_CAPS_FLUSH ; +/* +* A revoke whose response was deferred (see handle_cap_grant()) must +* still be acknowledged. Replay the forced flush here so that even a +* check triggered by writeback/invalidation completion sends a cap +* message to the MDS. +*/ +if (ci->i_ceph_flags &CEPH_I_FLUSH_FORCE ) + flags |=CHECK_CAPS_FLUSH_FORCE ; retry:/* Caps wanted by virtue of active open files. */ file_wanted =__ceph_caps_file_wanted (ci);
@@ -3760,13 +3792,30 @@ static void handle_cap_grant(struct inodBUG_ON (cap->issued & ~cap->implemented);/* don't let check_caps skip sending a response to MDS for revoke msgs */ -if (!revoke_wait &&le32_to_cpu (grant->op) ==CEPH_CAP_OP_REVOKE ) { - cap->mds_wanted =0 ; - flags |=CHECK_CAPS_FLUSH_FORCE ; -if (cap == ci->i_auth_cap) - check_caps =1 ;/* check auth cap only */ -else - check_caps =2 ;/* check all caps */ +if (le32_to_cpu (grant->op) ==CEPH_CAP_OP_REVOKE ) { +if (revoke_wait) { +/* +* We can't ack the revoke yet: the response is deferred +* until the writeback or cache invalidation queued above +* completes. Set the CEPH_I_FLUSH_FORCE flag to remember +* that a forced cap message is owed so that deferred +* completion (ceph_put_wrbuffer_cap_refs() or the +* invalidate worker, both of which call ceph_check_caps()) +* actually sends one, even if by then the revoked caps look +* unused, the inode is retaining caps, or the MDS has +* re-granted them. Without this, the cap message is never +* sent and the MDS hangs ("isn't responding to +* mclientcaps(revoke)"). +*/ + ci->i_ceph_flags |=CEPH_I_FLUSH_FORCE ; + }else { + cap->mds_wanted =0 ; + flags |=CHECK_CAPS_FLUSH_FORCE ; +if (cap == ci->i_auth_cap) + check_caps =1 ;/* check auth cap only */ +else + check_caps =2 ;/* check all caps */ + } } if (extra_info->inline_version > 0 &&
fs/ceph/super.h +4 -0
@@ -670,6 +670,10 @@ static inline struct inode *ceph_find_in#define CEPH_I_SHUTDOWN (1 << 13)/* inode is no longer usable */ #define CEPH_I_ASYNC_CHECK_CAPS (1 << 14)/* check caps immediately after async creating finishes */ +#define CEPH_I_FLUSH_FORCE (1 << 15)/* a revoke's response was deferred; +* force a cap message to the MDS once +* the deferred work completes +*/ /* * Masks of ceph inode work.
Greg Kroah-Hartman
maintainerPATCH 1037/1102 NFSD: Guard admin state-revocation walks with NFSD_NET_UP
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 2f3e6638aebc0ab8afb8b4e9816ea9a1cad85378 ]
Writing to /proc/fs/nfsd/unlock_filesystem, or sending the NFSD_CMD_UNLOCK_FILESYSTEM or NFSD_CMD_UNLOCK_EXPORT netlink command, walks the NFSv4 client hash tables to revoke open state and cancel async COPY operations. All three handlers gate that walk on nn->nfsd_serv, but a listener added via portlist or netlink listener_set sets nn->nfsd_serv before any nfsd thread starts. nfsd_startup_net() has not yet allocated nn->conf_id_hashtbl, so the walkers dereference a NULL table. A local administrator with CAP_SYS_ADMIN can crash the kernel this way without ever starting the server.
nn->nfsd_serv is set when the service is created, which precedes table allocation. NFSD_NET_UP instead brackets the window where the tables are live: set at the end of nfsd_startup_net() and cleared in nfsd_shutdown_net() after they are freed, both under nfsd_mutex. Gating the three unlock paths on NFSD_NET_UP fixes the startup-time NULL dereference while preserving the earlier post-shutdown use-after-free fix.
--- fs/nfsd/nfsctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/nfsd/nfsctl.c +1 -1
@@ -301,7 +301,7 @@ static ssize_t write_unlock_fs(struct fi error =nlmsvc_unlock_all_by_sb (path.dentry->d_sb);mutex_lock (&nfsd_mutex); nn =net_generic (netns (file), nfsd_net_id); -if (nn->nfsd_serv) +if (nn->nfsd_net_up)nfsd4_revoke_states (nn, path.dentry->d_sb);else error = -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 1038/1102 ceph: properly decrypt filenames in vmalloc() buffers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sam Edwards <[email protected]>
[ Upstream commit e939fc6a7bd969a58a150b7f188c1047138403e3 ]
The fscrypt subsystem uses the scatterlist crypto API, inheriting its requirement that any buffers are in the linear mapping region. However, the messenger client uses kvmalloc() to create buffers for messages, which will occasionally place those buffers in the vmalloc() region when physical memory fragmentation doesn't permit a large enough kmalloc(). The various callers of ceph_fname_to_usr() directly pass (slices of) raw messages from the MDS without considering that the messages may be in vmalloc() buffers, resulting in oopses especially on non-x86 platforms (see 'Closes:' for more details and a reproducer).
Make ceph_fname_to_usr() explicitly tolerant of vmalloc()-allocated fname->ctext, fname->name, and/or oname->name buffers, using tname (which, when non-null, must be a linear address; when null, is briefly allocated as necessary) as a bounce buffer to avoid passing any inappropriate addresses to fscrypt_fname_disk_to_usr().
Additionally change parse_reply_info_readdir() -- the only function to supply its own tname -- to follow the new "tname must never come from vmalloc()" rule by passing NULL when the message is not in the linear region. Though this causes a per-dentry kmalloc()+kfree(), this overhead exists only when processing the minority of messages that spill into vmalloc(). My (crude) testing puts this at only about 1 in 8,000 readdir messages. Still, if the overhead proves unreasonable in the future, it is easy enough to mitigate: a future change could allocate a bounce buffer in parse_reply_info_readdir() and use that as tname instead.
--- fs/ceph/crypto.c | 46 +++++++++++++++++++++++++++++++++++++--------- fs/ceph/mds_client.c | 8 ++++++-- 2 files changed, 43 insertions(+), 11 deletions(-)
fs/ceph/crypto.c +37 -9
@@ -370,6 +370,11 @@ int ceph_encode_encrypted_fname(struct i* Otherwise, base64 decode the string, and then ask fscrypt to format it * for userland presentation. * +* Though the fscrypt/crypto subsystems broadly expect all buffers to be in the +* linear-mapped region, this function slightly relaxes those requirements: +* fname->ctext, fname->name, and oname->name may be vmalloc(), but not +* tname->name. +* * Returns 0 on success or negative error code on error. */ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
@@ -377,11 +382,15 @@ int ceph_fname_to_usr(const struct ceph_ {struct inode *dir = fname->dir;struct fscrypt_str _tname =FSTR_INIT (NULL ,0 ); +struct fscrypt_str _oname;struct fscrypt_str iname;char *name = fname->name;int name_len = fname->name_len;int ret; +if (WARN_ON_ONCE (tname &&is_vmalloc_addr (tname->name))) +return -EIO ; +/* Sanity check that the resulting name will fit in the buffer */ if (fname->name_len >NAME_MAX || fname->ctext_len >NAME_MAX )return -EIO ;
@@ -422,16 +431,19 @@ int ceph_fname_to_usr(const struct ceph_ goto out_inode; } +if ((!tname || !tname->name) && + (fname->ctext_len ==0 || +unlikely (is_vmalloc_addr (fname->ctext)) || +unlikely (is_vmalloc_addr (oname->name)))) { + ret =fscrypt_fname_alloc_buffer (NAME_MAX , &_tname); +if (ret) + goto out_inode; + tname = &_tname; + } +if (fname->ctext_len ==0 ) {int declen; -if (!tname) { - ret =fscrypt_fname_alloc_buffer (NAME_MAX , &_tname); -if (ret) - goto out_inode; - tname = &_tname; - } - declen =ceph_base64_decode (name, name_len, tname->name);if (declen <=0 ) { ret = -EIO ;
@@ -439,13 +451,29 @@ int ceph_fname_to_usr(const struct ceph_ } iname.name = tname->name; iname.len = declen; + }else if (unlikely (is_vmalloc_addr (fname->ctext))) { +memcpy (tname->name, fname->ctext, fname->ctext_len); + + iname.name = tname->name; + iname.len = fname->ctext_len; }else { iname.name = fname->ctext; iname.len = fname->ctext_len; } - ret =fscrypt_fname_disk_to_usr (dir,0 ,0 , &iname, oname); -if (!ret && (dir != fname->dir)) { + _oname.name =unlikely (is_vmalloc_addr (oname->name)) ? + tname->name : oname->name; + _oname.len = oname->len; + + ret =fscrypt_fname_disk_to_usr (dir,0 ,0 , &iname, &_oname); +if (ret) + goto out; + +if (unlikely (is_vmalloc_addr (oname->name))) +memcpy (oname->name, _oname.name, _oname.len); + oname->len = _oname.len; + +if (dir != fname->dir) {char tmp_buf[CEPH_BASE64_CHARS (NAME_MAX )]; name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
fs/ceph/mds_client.c +6 -2
@@ -496,9 +496,13 @@ static int parse_reply_info_readdir(void* to do the base64_decode in-place. It's * safe because the decoded string should * always be shorter, which is 3/4 of origin -* string. +* string. If this message was allocated with +* vmalloc() (happens, but rarely), leave it +* NULL and let ceph_fname_to_usr() allocate +* suitable temporary working space instead. */ - tname.name = _name; +if (likely (!is_vmalloc_addr (_name))) + tname.name = _name;/* * Set oname to _name too, and this will be
Greg Kroah-Hartman
maintainerPATCH 1039/1102 HID: apple: preserve keyboard backlight across T2 resume
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andre Eikmeyer <[email protected]>
[ Upstream commit 2b5d1495bd101f3d9c13caf3923754f48ee7371a ]
The T2 virtual USB host controller re-enumerates the internal keyboard after system resume. The butterfly keyboard backlight currently uses LED_CORE_SUSPENDRESUME, so the LED core sends a blocking request to the old HID device while it is disappearing. That request fails with -ENODEV and the newly probed device starts with its backlight off.
To fix this, we cache the requested brightness when the old HID device is removed and restore it when the replacement is probed. We let re-enumeration handle restoration instead of issuing a request through the stale device.
--- drivers/hid/hid-apple.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/hid/hid-apple.c +16 -2
@@ -89,6 +89,9 @@ struct apple_sc_backlight {struct hid_device *hdev; }; +/* T2 VHCI re-enumerates the internal keyboard across system resume. */ +static int apple_backlight_resume_brightness =-1 ; +struct apple_magic_backlight {struct led_classdev cdev;struct hid_report *brightness;
@@ -797,6 +800,7 @@ static int apple_backlight_led_set(strucstatic int apple_backlight_init (struct hid_device *hdev) {int ret; +int brightness;struct apple_sc *asc =hid_get_drvdata (hdev);struct apple_backlight_config_report *rep;
@@ -832,13 +836,20 @@ static int apple_backlight_init(struct h asc->backlight->cdev.name ="apple::kbd_backlight" ; asc->backlight->cdev.max_brightness = rep->backlight_on_max; asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set; - asc->backlight->cdev.flags =LED_CORE_SUSPENDRESUME ; +/* VHCI re-enumeration restores the cached brightness in the next probe. */ + + brightness =READ_ONCE (apple_backlight_resume_brightness); +if (brightness <0 ) + brightness =LED_OFF ; +else + brightness =min_t (int, brightness, rep->backlight_on_max); - ret =apple_backlight_set (hdev,0 ,0 ); + ret =apple_backlight_set (hdev, brightness,0 );if (ret <0 ) {hid_err (hdev,"backlight set request failed: %d\n" , ret); goto cleanup_and_exit; } + asc->backlight->cdev.brightness = brightness; ret =devm_led_classdev_register (&hdev->dev, &asc->backlight->cdev);
@@ -971,6 +982,9 @@ static void apple_remove(struct hid_deviif (asc->quirks &APPLE_RDESC_BATTERY )del_timer_sync (&asc->battery_timer); +if (asc->backlight) +WRITE_ONCE (apple_backlight_resume_brightness, + asc->backlight->cdev.brightness);hid_hw_stop (hdev); }
Greg Kroah-Hartman
maintainerPATCH 1040/1102 btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit a5b3f117daead61c3c9c88cd1159d38fa4ad1362 ]
It's a generic helper not specific to ioctls and used in several places, so move it out from ioctl.c and into fs.c. While at it change its return type from int to bool and declare the loop variable in the loop itself.
This also slightly reduces the module's size.
Before this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781492 161037 16920 1959449 1de619 fs/btrfs/btrfs.koAfter this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781340 161037 16920 1959297 1de581 fs/btrfs/btrfs.koStable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/fs.c | 9 +++++ fs/btrfs/fs.h | 2 + fs/btrfs/ioctl.c | 96 ++++++++++++++++++++++++------------------------------- fs/btrfs/ioctl.h | 1 4 files changed, 53 insertions(+), 55 deletions(-)
fs/btrfs/fs.c +9 -0
@@ -5,6 +5,15 @@#include "fs.h" #include "accessors.h" +bool __pure btrfs_is_empty_uuid (const u8 *uuid) +{ +for (int i =0 ; i <BTRFS_UUID_SIZE ; i++) { +if (uuid[i] !=0 ) +return false; + } +return true; +} +void __btrfs_set_fs_incompat (struct btrfs_fs_info *fs_info,u64 flag,const char *name) {
fs/btrfs/fs.h +2 -0
@@ -994,6 +994,8 @@ void btrfs_exclop_balance(struct btrfs_fint btrfs_check_ioctl_vol_args_path (const struct btrfs_ioctl_vol_args *vol_args); +bool __pure btrfs_is_empty_uuid (const u8 *uuid); +/* Compatibility and incompatibility defines */ void __btrfs_set_fs_incompat (struct btrfs_fs_info *fs_info,u64 flag,const char *name);
fs/btrfs/ioctl.c +42 -54
187 lines not on this page · raw message
fs/btrfs/ioctl.h +0 -1
@@ -19,7 +19,6 @@ int btrfs_fileattr_set(struct mnt_idmap struct dentry *dentry, struct fileattr *fa);int btrfs_ioctl_get_supported_features (void __user *arg);void btrfs_sync_inode_flags_to_i_flags (struct inode *inode); -int __pure btrfs_is_empty_uuid (const u8 *uuid);void btrfs_update_ioctl_balance_args (struct btrfs_fs_info *fs_info,struct btrfs_ioctl_balance_args *bargs);
Greg Kroah-Hartman
maintainerPATCH 1041/1102 btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit a4545b74e2de98989c1d14bc48c52c2f9fa734b6 ]
Currently BTRFS_BYTES_TO_BLKS() is defined in ctree.h but it's not related at all to the btree data structure, so move it into fs.h.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ctree.h | 3 --- fs/btrfs/fs.h | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-)
fs/btrfs/ctree.h +0 -3
@@ -506,9 +506,6 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(creturn BTRFS_MAX_ITEM_SIZE (info) -sizeof (struct btrfs_dir_item ); } -#define BTRFS_BYTES_TO_BLKS (fs_info, bytes) \ - ((bytes) >> (fs_info)->sectorsize_bits) -static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) {return mapping_gfp_constraint (mapping, ~__GFP_FS);
fs/btrfs/fs.h +2 -0
@@ -959,6 +959,8 @@ static inline u64 btrfs_calc_metadata_si#define BTRFS_MAX_EXTENT_ITEM_SIZE (r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \ sizeof(struct btrfs_item)) +#define BTRFS_BYTES_TO_BLKS (fs_info, bytes) ((bytes) >> (fs_info)->sectorsize_bits) +static inline bool btrfs_is_zoned (const struct btrfs_fs_info *fs_info) {return IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && fs_info->zone_size >0 ;
Greg Kroah-Hartman
maintainerPATCH 1042/1102 btrfs: move btrfs_alloc_write_mask() into fs.h
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 378f25d3fc429ad001fa686f615df5c0f9cd47e1 ]
Currently btrfs_alloc_write_mask() is defined in ctree.h but it's not related at all to the btree data structure, so move it into fs.h.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ctree.h | 6 ------ fs/btrfs/fs.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-)
fs/btrfs/ctree.h +0 -6
@@ -7,7 +7,6 @@#define BTRFS_CTREE_H #include "linux/cleanup.h" -#include <linux/pagemap.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/mutex.h>
@@ -506,11 +505,6 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(creturn BTRFS_MAX_ITEM_SIZE (info) -sizeof (struct btrfs_dir_item ); } -static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) -{ -return mapping_gfp_constraint (mapping, ~__GFP_FS); -} -void btrfs_error_unpin_extent_range (struct btrfs_fs_info *fs_info,u64 start,u64 end);int btrfs_discard_extent (struct btrfs_fs_info *fs_info,u64 bytenr,u64 num_bytes,u64 *actual_bytes);
fs/btrfs/fs.h +6 -0
@@ -18,6 +18,7 @@#include <linux/rwsem.h> #include <linux/semaphore.h> #include <linux/list.h> +#include <linux/pagemap.h> #include <linux/radix-tree.h> #include <linux/workqueue.h> #include <linux/wait.h>
@@ -893,6 +894,11 @@ struct btrfs_fs_info {#define inode_to_fs_info (_inode) (BTRFS_I(_Generic((_inode), \ struct inode *: (_inode)))->root->fs_info) +static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) +{ +return mapping_gfp_constraint (mapping, ~__GFP_FS); +} +static inline u64 btrfs_get_fs_generation (const struct btrfs_fs_info *fs_info) {return READ_ONCE (fs_info->generation);
Greg Kroah-Hartman
maintainerPATCH 1043/1102 btrfs: expose per-inode stable writes flag
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit ecde48a1a6b3256bd49db8780bf37556b157783c ]
The address space flag AS_STABLE_WRITES determine if FGP_STABLE for will wait for the folio to finish its writeback.
For btrfs, due to the default data checksum behavior, if we modify the folio while it's still under writeback, it will cause data checksum mismatch. Thus for quite some call sites we manually call folio_wait_writeback() to prevent such problem from happening.
Currently there is only one call site inside btrfs really utilizing FGP_STABLE, and in that case we also manually call folio_wait_writeback() to do the waiting.
But it's better to properly expose the stable writes flag to a per-inode basis, to allow call sites to fully benefit from FGP_STABLE flag. E.g. for inodes with NODATASUM allowing beginning dirtying the page without waiting for writeback.
This involves:
- Update the mapping's stable write flag when setting/clearing NODATASUM inode flag using ioctl
This only works for empty files, so it should be fine.
- Update the mapping's stable write flag when reading an inode from disk
- Remove the explicit folio_wait_writeback() for FGP_BEGINWRITE call site
[ sashal: Reduced backport -- upstream ecde48a1a6b32 touches 4 file(s), this
backport carries 3. Not backported here:
fs/btrfs/btrfs_inode.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/file.c | 3 +-- fs/btrfs/inode.c | 8 ++++++++ fs/btrfs/ioctl.c | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-)
fs/btrfs/file.c +1 -2
@@ -884,7 +884,7 @@ static int prepare_uptodate_page(structstatic fgf_t get_prepare_fgp_flags (bool nowait) { -fgf_t fgp_flags =FGP_LOCK |FGP_ACCESSED |FGP_CREAT ; +fgf_t fgp_flags =FGP_LOCK |FGP_ACCESSED |FGP_CREAT |FGP_STABLE ;if (nowait) fgp_flags |=FGP_NOWAIT ;
@@ -954,7 +954,6 @@ again: faili = i -1 ; goto fail; } -wait_on_page_writeback (pages[i]); }return 0 ;
fs/btrfs/inode.c +8 -0
@@ -3854,6 +3854,10 @@ static int btrfs_read_locked_inode(strucbtrfs_inode_split_flags (btrfs_inode_flags (leaf, inode_item), &BTRFS_I (inode)->flags, &BTRFS_I (inode)->ro_flags); +if (BTRFS_I (inode)->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->i_mapping); +else +mapping_set_stable_writes (inode->i_mapping); cache_index:/*
@@ -6313,6 +6317,10 @@ int btrfs_create_new_inode(struct btrfs_if (btrfs_test_opt (fs_info,NODATACOW ))BTRFS_I (inode)->flags |=BTRFS_INODE_NODATACOW |BTRFS_INODE_NODATASUM ; +if (BTRFS_I (inode)->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->i_mapping); +else +mapping_set_stable_writes (inode->i_mapping); } ret =btrfs_insert_inode_locked (inode);
fs/btrfs/ioctl.c +4 -0
@@ -391,6 +391,10 @@ int btrfs_fileattr_set(struct mnt_idmap update_flags: inode->flags = inode_flags; +if (inode->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->vfs_inode.i_mapping); +else +mapping_set_stable_writes (inode->vfs_inode.i_mapping);btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode);inode_inc_iversion (&inode->vfs_inode);inode_set_ctime_current (&inode->vfs_inode);
Greg Kroah-Hartman
maintainerPATCH 1044/1102 btrfs: update include and forward declarations in headers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Sterba <[email protected]>
[ Upstream commit 6149c82bdaff63203e58a4910d9245468d281f39 ]
Pass over all header files and add missing forward declarations, includes or fix include types.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/accessors.h | 1 + fs/btrfs/acl.h | 2 ++ fs/btrfs/ctree.h | 2 +- fs/btrfs/delayed-ref.h | 2 ++ fs/btrfs/dir-item.h | 1 + fs/btrfs/direct-io.h | 2 ++ fs/btrfs/discard.h | 1 + fs/btrfs/extent-tree.h | 1 - fs/btrfs/file-item.h | 2 ++ fs/btrfs/file.h | 2 ++ fs/btrfs/fs.c | 1 - fs/btrfs/ioctl.h | 2 ++ fs/btrfs/locking.c | 1 - fs/btrfs/ordered-data.h | 1 + fs/btrfs/print-tree.h | 2 ++ fs/btrfs/props.h | 1 + fs/btrfs/qgroup.h | 3 +++ fs/btrfs/raid-stripe-tree.h | 1 + fs/btrfs/send.c | 1 - fs/btrfs/space-info.c | 2 +- fs/btrfs/subpage.c | 1 - fs/btrfs/sysfs.h | 1 + fs/btrfs/volumes.h | 4 ++++ fs/btrfs/xattr.h | 2 ++ 24 files changed, 32 insertions(+), 7 deletions(-)
fs/btrfs/accessors.h +1 -0
@@ -12,6 +12,7 @@#include <linux/string.h> #include <linux/mm.h> #include <uapi/linux/btrfs_tree.h> +#include "extent_io.h" struct extent_buffer ;
fs/btrfs/acl.h +2 -0
@@ -3,6 +3,8 @@#ifndef BTRFS_ACL_H #define BTRFS_ACL_H +#include <linux/types.h> +struct posix_acl ;struct inode ;struct btrfs_trans_handle ;
fs/btrfs/ctree.h +1 -1
@@ -6,7 +6,7 @@#ifndef BTRFS_CTREE_H #define BTRFS_CTREE_H -#include "linux/cleanup.h" +#include <linux/cleanup.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/mutex.h>
fs/btrfs/delayed-ref.h +2 -0
@@ -14,6 +14,8 @@#include <linux/spinlock.h> #include <linux/slab.h> #include <uapi/linux/btrfs_tree.h> +#include "fs.h" +#include "messages.h" struct btrfs_trans_handle ;struct btrfs_fs_info ;
fs/btrfs/dir-item.h +1 -0
@@ -10,6 +10,7 @@ struct fscrypt_str;struct btrfs_fs_info ;struct btrfs_key ;struct btrfs_path ; +struct btrfs_inode ;struct btrfs_root ;struct btrfs_trans_handle ;
fs/btrfs/direct-io.h +2 -0
@@ -5,6 +5,8 @@#include <linux/types.h> +struct kiocb ; +int __init btrfs_init_dio (void );void __cold btrfs_destroy_dio (void );
fs/btrfs/discard.h +1 -0
@@ -3,6 +3,7 @@#ifndef BTRFS_DISCARD_H #define BTRFS_DISCARD_H +#include <linux/types.h> #include <linux/sizes.h> struct btrfs_fs_info ;
fs/btrfs/extent-tree.h +0 -1
@@ -4,7 +4,6 @@#define BTRFS_EXTENT_TREE_H #include <linux/types.h> -#include "misc.h" #include "block-group.h" #include "locking.h"
fs/btrfs/file-item.h +2 -0
@@ -3,8 +3,10 @@#ifndef BTRFS_FILE_ITEM_H #define BTRFS_FILE_ITEM_H +#include <linux/blk_types.h> #include <linux/list.h> #include <uapi/linux/btrfs_tree.h> +#include "ctree.h" #include "accessors.h" struct extent_map ;
fs/btrfs/file.h +2 -0
@@ -9,6 +9,8 @@ struct file;struct extent_state ;struct kiocb ;struct iov_iter ; +struct inode ; +struct folio ;struct page ;struct btrfs_ioctl_encoded_io_args ;struct btrfs_drop_extents_args ;
fs/btrfs/fs.c +0 -1
@@ -1,7 +1,6 @@// SPDX-License-Identifier: GPL-2.0 #include "messages.h" -#include "ctree.h" #include "fs.h" #include "accessors.h"
fs/btrfs/ioctl.h +2 -0
@@ -9,6 +9,8 @@ struct file;struct dentry ;struct mnt_idmap ;struct fileattr ; +struct inode ; +struct io_uring_cmd ;struct btrfs_fs_info ;struct btrfs_ioctl_balance_args ;
fs/btrfs/locking.c +0 -1
@@ -9,7 +9,6 @@#include <linux/page-flags.h> #include <asm/bug.h> #include <trace/events/btrfs.h> -#include "misc.h" #include "ctree.h" #include "extent_io.h" #include "locking.h"
fs/btrfs/ordered-data.h +1 -0
@@ -17,6 +17,7 @@struct inode ;struct page ;struct extent_state ; +struct btrfs_block_group ;struct btrfs_inode ;struct btrfs_root ;struct btrfs_fs_info ;
fs/btrfs/print-tree.h +2 -0
@@ -6,6 +6,8 @@#ifndef BTRFS_PRINT_TREE_H #define BTRFS_PRINT_TREE_H +#include <linux/types.h> +/* Buffer size to contain tree name and possibly additional data (offset) */ #define BTRFS_ROOT_NAME_BUF_LEN 48
fs/btrfs/props.h +1 -0
@@ -6,6 +6,7 @@#ifndef BTRFS_PROPS_H #define BTRFS_PROPS_H +#include <linux/types.h> #include <linux/compiler_types.h> struct inode ;
fs/btrfs/qgroup.h +3 -0
@@ -22,6 +22,9 @@ struct btrfs_ioctl_quota_ctl_args;struct btrfs_trans_handle ;struct btrfs_delayed_ref_root ;struct btrfs_inode ; +struct btrfs_transaction ; +struct btrfs_block_group ; +struct btrfs_qgroup_swapped_blocks ;/* * Btrfs qgroup overview
fs/btrfs/raid-stripe-tree.h +1 -0
@@ -9,6 +9,7 @@#include <linux/types.h> #include <uapi/linux/btrfs_tree.h> #include "fs.h" +#include "accessors.h" #define BTRFS_RST_SUPP_BLOCK_GROUP_MASK (BTRFS_BLOCK_GROUP_DUP | \ BTRFS_BLOCK_GROUP_RAID1_MASK | \
fs/btrfs/send.c +0 -1
@@ -17,7 +17,6 @@#include <linux/compat.h> #include <linux/crc32c.h> #include <linux/fsverity.h> -#include "send.h" #include "ctree.h" #include "backref.h"
fs/btrfs/space-info.c +1 -1
@@ -1,6 +1,6 @@// SPDX-License-Identifier: GPL-2.0 -#include "linux/spinlock.h" +#include <linux/spinlock.h> #include <linux/minmax.h> #include "misc.h" #include "ctree.h"
fs/btrfs/subpage.c +0 -1
@@ -2,7 +2,6 @@#include <linux/slab.h> #include "messages.h" -#include "ctree.h" #include "subpage.h" #include "btrfs_inode.h"
fs/btrfs/sysfs.h +1 -0
@@ -7,6 +7,7 @@#include <linux/compiler_types.h> #include <linux/kobject.h> +struct block_device ;struct btrfs_fs_info ;struct btrfs_device ;struct btrfs_fs_devices ;
fs/btrfs/volumes.h +4 -0
@@ -7,6 +7,7 @@#define BTRFS_VOLUMES_H #include <linux/blk_types.h> +#include <linux/blkdev.h> #include <linux/sizes.h> #include <linux/atomic.h> #include <linux/sort.h>
@@ -18,14 +19,17 @@#include <linux/completion.h> #include <linux/rbtree.h> #include <uapi/linux/btrfs.h> +#include <uapi/linux/btrfs_tree.h> #include "messages.h" #include "rcu-string.h" +#include "extent-io-tree.h" struct block_device ;struct bdev_handle ;struct btrfs_fs_info ;struct btrfs_block_group ;struct btrfs_trans_handle ; +struct btrfs_transaction ;struct btrfs_zoned_device_info ;#define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G)
fs/btrfs/xattr.h +2 -0
@@ -6,6 +6,8 @@#ifndef BTRFS_XATTR_H #define BTRFS_XATTR_H +#include <linux/types.h> +struct dentry ;struct inode ;struct qstr ;
Greg Kroah-Hartman
maintainerPATCH 1045/1102 btrfs: parameter constification in ioctl.c
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Sterba <[email protected]>
[ Upstream commit 5e54f9420fc9b8096d0a4b579a84806a3ef0bf88 ]
Add const to function parameters that are not changed.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ioctl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
fs/btrfs/ioctl.c +14 -14
@@ -117,8 +117,8 @@ struct btrfs_ioctl_encoded_io_args_32 {#endif /* Mask out flags that are inappropriate for the given type of inode. */ -static unsigned int btrfs_mask_fsflags_for_type (struct inode *inode, -unsigned int flags) +static unsigned int btrfs_mask_fsflags_for_type (const struct inode *inode, +unsigned int flags) {if (S_ISDIR (inode->i_mode))return flags;
@@ -132,7 +132,7 @@ static unsigned int btrfs_mask_fsflags_f* Export internal inode flags to the format expected by the FS_IOC_GETFLAGS * ioctl. */ -static unsigned int btrfs_inode_flags_to_fsflags (struct btrfs_inode *binode) +static unsigned int btrfs_inode_flags_to_fsflags (const struct btrfs_inode *binode) {unsigned int iflags =0 ;u32 flags = binode->flags;
@@ -218,7 +218,7 @@ static int check_fsflags(unsigned int olreturn 0 ; } -static int check_fsflags_compatible (struct btrfs_fs_info *fs_info, +static int check_fsflags_compatible (const struct btrfs_fs_info *fs_info,unsigned int flags) {if (btrfs_is_zoned (fs_info) && (flags &FS_NOCOW_FL ))
@@ -247,7 +247,7 @@ static int btrfs_check_ioctl_vol_args2_s*/ int btrfs_fileattr_get (struct dentry *dentry,struct fileattr *fa) { -struct btrfs_inode *binode =BTRFS_I (d_inode (dentry)); +const struct btrfs_inode *binode =BTRFS_I (d_inode (dentry));fileattr_fill_flags (fa,btrfs_inode_flags_to_fsflags (binode));return 0 ;
@@ -485,7 +485,7 @@ void btrfs_exclop_balance(struct btrfs_f } } -static int btrfs_ioctl_getversion (struct inode *inode,int __user *arg) +static int btrfs_ioctl_getversion (const struct inode *inode,int __user *arg) {return put_user (inode->i_generation, arg); }
@@ -557,7 +557,7 @@ static noinline int btrfs_ioctl_fitrim(s* Calculate the number of transaction items to reserve for creating a subvolume * or snapshot, not including the inode, directory entries, or parent directory. */ -static unsigned int create_subvol_num_items (struct btrfs_qgroup_inherit *inherit) +static unsigned int create_subvol_num_items (const struct btrfs_qgroup_inherit *inherit) {/* * 1 to add root block
@@ -967,7 +967,7 @@ static int btrfs_may_delete(struct mnt_i/* copy of may_create in fs/namei.c() */ static inline int btrfs_may_create (struct mnt_idmap *idmap, -struct inode *dir,struct dentry *child) +struct inode *dir,const struct dentry *child) {if (d_really_is_positive (child))return -EEXIST ;
@@ -1539,8 +1539,8 @@ out:return ret; } -static noinline int key_in_sk(struct btrfs_key *key, -struct btrfs_ioctl_search_key *sk) +static noinline int key_in_sk(const struct btrfs_key *key, +const struct btrfs_ioctl_search_key *sk) {struct btrfs_key test;int ret;
@@ -1565,7 +1565,7 @@ static noinline int key_in_sk(struct btr static noinline int copy_to_sk(struct btrfs_path *path, struct btrfs_key *key, - struct btrfs_ioctl_search_key *sk, + const struct btrfs_ioctl_search_key *sk, u64 *buf_size, char __user *ubuf, unsigned long *sk_offset,
@@ -2843,7 +2843,7 @@ out_free:return ret; } -static long btrfs_ioctl_fs_info (struct btrfs_fs_info *fs_info, +static long btrfs_ioctl_fs_info (const struct btrfs_fs_info *fs_info,void __user *arg) {struct btrfs_ioctl_fs_info_args *fi_args;
@@ -2897,7 +2897,7 @@ static long btrfs_ioctl_fs_info(struct breturn ret; } -static long btrfs_ioctl_dev_info (struct btrfs_fs_info *fs_info, +static long btrfs_ioctl_dev_info (const struct btrfs_fs_info *fs_info,void __user *arg) {BTRFS_DEV_LOOKUP_ARGS (args);
@@ -4355,7 +4355,7 @@ static int btrfs_ioctl_get_features(strureturn 0 ; } -static int check_feature_bits (struct btrfs_fs_info *fs_info, +static int check_feature_bits (const struct btrfs_fs_info *fs_info,enum btrfs_feature_set set,u64 change_mask,u64 flags,u64 supported_flags,u64 safe_set,u64 safe_clear)
Greg Kroah-Hartman
maintainerPATCH 1046/1102 btrfs: pass struct btrfs_inode to btrfs_sync_inode_flags_to_i_flags()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Sterba <[email protected]>
[ Upstream commit 4f27a693940bcf313d17792ac4ec13a83f71cf25 ]
Pass a struct btrfs_inode to btrfs_sync_inode_flags_to_i_flags() as it's an internal interface.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/inode.c | 4 ++-- fs/btrfs/ioctl.c | 19 +++++++++---------- fs/btrfs/ioctl.h | 4 ++-- fs/btrfs/verity.c | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-)
fs/btrfs/inode.c +2 -2
@@ -3975,7 +3975,7 @@ cache_acl:break ; } -btrfs_sync_inode_flags_to_i_flags (inode); +btrfs_sync_inode_flags_to_i_flags (BTRFS_I (inode));return 0 ; }
@@ -6212,7 +6212,7 @@ static void btrfs_inherit_iflags(struct inode->flags |=BTRFS_INODE_NODATASUM ; } -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); } int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
fs/btrfs/ioctl.c +9 -10
@@ -166,25 +166,24 @@ static unsigned int btrfs_inode_flags_to/* * Update inode->i_flags based on the btrfs internal flags. */ -void btrfs_sync_inode_flags_to_i_flags (struct inode *inode) +void btrfs_sync_inode_flags_to_i_flags (struct btrfs_inode *inode) { -struct btrfs_inode *binode =BTRFS_I (inode);unsigned int new_fl =0 ; -if (binode->flags &BTRFS_INODE_SYNC ) +if (inode->flags &BTRFS_INODE_SYNC ) new_fl |=S_SYNC ; -if (binode->flags &BTRFS_INODE_IMMUTABLE ) +if (inode->flags &BTRFS_INODE_IMMUTABLE ) new_fl |=S_IMMUTABLE ; -if (binode->flags &BTRFS_INODE_APPEND ) +if (inode->flags &BTRFS_INODE_APPEND ) new_fl |=S_APPEND ; -if (binode->flags &BTRFS_INODE_NOATIME ) +if (inode->flags &BTRFS_INODE_NOATIME ) new_fl |=S_NOATIME ; -if (binode->flags &BTRFS_INODE_DIRSYNC ) +if (inode->flags &BTRFS_INODE_DIRSYNC ) new_fl |=S_DIRSYNC ; -if (binode->ro_flags &BTRFS_INODE_RO_VERITY ) +if (inode->ro_flags &BTRFS_INODE_RO_VERITY ) new_fl |=S_VERITY ; -set_mask_bits (&inode->i_flags, +set_mask_bits (&inode->vfs_inode.i_flags,S_SYNC |S_APPEND |S_IMMUTABLE |S_NOATIME |S_DIRSYNC |S_VERITY , new_fl); }
@@ -395,7 +394,7 @@ update_flags:mapping_clear_stable_writes (inode->vfs_inode.i_mapping); else mapping_set_stable_writes(inode->vfs_inode.i_mapping); -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode);inode_inc_iversion (&inode->vfs_inode);inode_set_ctime_current (&inode->vfs_inode); ret =btrfs_update_inode (trans, inode);
fs/btrfs/ioctl.h +2 -2
@@ -9,8 +9,8 @@ struct file;struct dentry ;struct mnt_idmap ;struct fileattr ; -struct inode ;struct io_uring_cmd ; +struct btrfs_inode ;struct btrfs_fs_info ;struct btrfs_ioctl_balance_args ;
@@ -20,7 +20,7 @@ int btrfs_fileattr_get(struct dentry *deint btrfs_fileattr_set (struct mnt_idmap *idmap,struct dentry *dentry,struct fileattr *fa);int btrfs_ioctl_get_supported_features (void __user *arg); -void btrfs_sync_inode_flags_to_i_flags (struct inode *inode); +void btrfs_sync_inode_flags_to_i_flags (struct btrfs_inode *inode);void btrfs_update_ioctl_balance_args (struct btrfs_fs_info *fs_info,struct btrfs_ioctl_balance_args *bargs);
fs/btrfs/verity.c +2 -2
@@ -485,7 +485,7 @@ static int rollback_verity(struct btrfs_ goto out; } inode->ro_flags &= ~BTRFS_INODE_RO_VERITY ; -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); ret =btrfs_update_inode (trans, inode);if (ret) {btrfs_abort_transaction (trans, ret);
@@ -552,7 +552,7 @@ static int finish_verity(struct btrfs_in goto out; } inode->ro_flags |=BTRFS_INODE_RO_VERITY ; -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); ret =btrfs_update_inode (trans, inode);if (ret) goto end_trans;
Greg Kroah-Hartman
maintainerPATCH 1048/1102 btrfs: use BTRFS_PATH_AUTO_FREE in can_nocow_extent()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Sterba <[email protected]>
[ Upstream commit 5e8632035a173a8c54c17301ecff69c79babb4bf ]
This is the trivial pattern for path auto free, initialize at the beginning and free at the end with simple goto -> return conversions.
Stable-dep-of: 3f950867c307 ("btrfs: fix extent map leak in NOCOW direct I/O write")
--- fs/btrfs/inode.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-)
fs/btrfs/inode.c +15 -22
@@ -7054,7 +7054,7 @@ noinline int can_nocow_extent(struct ino {struct btrfs_fs_info *fs_info =inode_to_fs_info (inode);struct can_nocow_file_extent_args nocow_args = {0 }; -struct btrfs_path *path; +BTRFS_PATH_AUTO_FREE (path);int ret;struct extent_buffer *leaf;struct btrfs_root *root =BTRFS_I (inode)->root;
@@ -7071,13 +7071,12 @@ noinline int can_nocow_extent(struct ino ret =btrfs_lookup_file_extent (NULL , root, path,btrfs_ino (BTRFS_I (inode)), offset,0 );if (ret <0 ) - goto out; +return ret;if (ret ==1 ) {if (path->slots[0 ] ==0 ) { -/* can't find the item, must cow */ - ret =0 ; - goto out; +/* Can't find the item, must COW. */ +return 0 ; } path->slots[0 ]--; }
@@ -7086,17 +7085,17 @@ noinline int can_nocow_extent(struct inobtrfs_item_key_to_cpu (leaf, &key, path->slots[0 ]);if (key.objectid !=btrfs_ino (BTRFS_I (inode)) || key.type !=BTRFS_EXTENT_DATA_KEY ) { -/* not our file or wrong item type, must cow */ - goto out; +/* Not our file or wrong item type, must COW. */ +return 0 ; }if (key.offset > offset) { -/* Wrong offset, must cow */ - goto out; +/* Wrong offset, must COW. */ +return 0 ; }if (btrfs_file_extent_end (path) <= offset) - goto out; +return 0 ; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); found_type =btrfs_file_extent_type (leaf, fi);
@@ -7112,15 +7111,13 @@ noinline int can_nocow_extent(struct inoif (ret !=1 ) {/* Treat errors as not being able to NOCOW. */ - ret =0 ; - goto out; +return 0 ; } - ret =0 ;if (btrfs_extent_readonly (fs_info, nocow_args.file_extent.disk_bytenr + nocow_args.file_extent.offset)) - goto out; +return 0 ;if (!(BTRFS_I (inode)->flags &BTRFS_INODE_NODATACOW ) && found_type ==BTRFS_FILE_EXTENT_PREALLOC ) {
@@ -7129,20 +7126,16 @@ noinline int can_nocow_extent(struct ino range_end =round_up (offset + nocow_args.file_extent.num_bytes, root->fs_info->sectorsize) -1 ; ret =test_range_bit_exists (io_tree, offset, range_end,EXTENT_DELALLOC ); -if (ret) { - ret = -EAGAIN ; - goto out; - } +if (ret) +return -EAGAIN ; }if (file_extent)memcpy (file_extent, &nocow_args.file_extent,sizeof (*file_extent)); *len = nocow_args.file_extent.num_bytes; - ret =1 ; -out: -btrfs_free_path (path); -return ret; + +return 1 ; }/* The callers of this must take lock_extent() */
Greg Kroah-Hartman
maintainerPATCH 1049/1102 btrfs: add btrfs prefix to main lock, try lock and unlock extent functions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 242570e80bfdf3f98ca13031c723edd72c69bdc2 ]
These functions are exported so they should have a 'btrfs_' prefix by convention, to make it clear they are btrfs specific and to avoid collisions with functions from elsewhere in the kernel. So add a prefix to their name.
Stable-dep-of: 3f950867c307 ("btrfs: fix extent map leak in NOCOW direct I/O write")
--- fs/btrfs/compression.c | 12 ++-- fs/btrfs/defrag.c | 28 ++++----- fs/btrfs/direct-io.c | 20 +++--- fs/btrfs/extent-io-tree.h | 6 +- fs/btrfs/extent_io.c | 30 +++++----- fs/btrfs/extent_map.c | 44 +++++++------- fs/btrfs/extent_map.h | 2 fs/btrfs/fiemap.c | 4 - fs/btrfs/file.c | 60 ++++++++++---------- fs/btrfs/free-space-cache.c | 10 +-- fs/btrfs/inode.c | 112 +++++++++++++++++++------------------- fs/btrfs/ioctl.c | 2 fs/btrfs/ordered-data.c | 8 +- fs/btrfs/reflink.c | 8 +- fs/btrfs/relocation.c | 24 ++++---- fs/btrfs/tests/extent-io-tests.c | 6 +- fs/btrfs/tests/extent-map-tests.c | 42 +++++++------- fs/btrfs/tests/inode-tests.c | 40 ++++++------- fs/btrfs/tree-log.c | 8 +- fs/btrfs/zoned.c | 2 20 files changed, 234 insertions(+), 234 deletions(-)
fs/btrfs/compression.c +6 -6
@@ -499,7 +499,7 @@ static noinline int add_ra_bio_pages(str } page_end = (pg_index <<PAGE_SHIFT ) +folio_size (folio) -1 ; -lock_extent (tree, cur, page_end,NULL ); +btrfs_lock_extent (tree, cur, page_end,NULL );read_lock (&em_tree->lock); em =lookup_extent_mapping (em_tree, cur, page_end +1 - cur);read_unlock (&em_tree->lock);
@@ -513,15 +513,15 @@ static noinline int add_ra_bio_pages(str (cur + fs_info->sectorsize > extent_map_end(em)) || (extent_map_block_start(em) >> SECTOR_SHIFT) != orig_bio->bi_iter.bi_sector) { -free_extent_map (em); -unlock_extent (tree, cur, page_end,NULL ); +btrfs_free_extent_map (em); +btrfs_unlock_extent (tree, cur, page_end,NULL );folio_unlock (folio);folio_put (folio);break ; } add_size =min (em->start + em->len, page_end +1 ) - cur; -free_extent_map (em); -unlock_extent (tree, cur, page_end,NULL ); +btrfs_free_extent_map (em); +btrfs_unlock_extent (tree, cur, page_end,NULL );if (folio->index == end_index) {size_t zero_offset =offset_in_folio (folio, isize);
@@ -603,7 +603,7 @@ void btrfs_submit_compressed_read(struct cb->compress_type =extent_map_compression (em); cb->orig_bbio = bbio; -free_extent_map (em); +btrfs_free_extent_map (em); cb->nr_folios =DIV_ROUND_UP (compressed_len,PAGE_SIZE ); cb->compressed_folios =kcalloc (cb->nr_folios,sizeof (struct folio *),GFP_NOFS );
fs/btrfs/defrag.c +14 -14
@@ -734,12 +734,12 @@ next: not_found:btrfs_release_path (&path); -free_extent_map (em); +btrfs_free_extent_map (em);return NULL ; err:btrfs_release_path (&path); -free_extent_map (em); +btrfs_free_extent_map (em);return ERR_PTR (ret); }
@@ -769,7 +769,7 @@ static struct extent_map *defrag_lookup_* file extent items in the inode's subvolume tree). */ if (em && (em->flags &EXTENT_FLAG_MERGED )) { -free_extent_map (em); +btrfs_free_extent_map (em); em =NULL ; }
@@ -779,10 +779,10 @@ static struct extent_map *defrag_lookup_/* Get the big lock and read metadata off disk. */ if (!locked) -lock_extent (io_tree, start, end, &cached); +btrfs_lock_extent (io_tree, start, end, &cached); em =defrag_get_extent (BTRFS_I (inode), start, newer_than);if (!locked) -unlock_extent (io_tree, start, end, &cached); +btrfs_unlock_extent (io_tree, start, end, &cached);if (IS_ERR (em))return NULL ;
@@ -837,7 +837,7 @@ static bool defrag_check_next_extent(str ret = true; out: -free_extent_map (next); +btrfs_free_extent_map (next);return ret; }
@@ -894,9 +894,9 @@ again:while (1 ) {struct btrfs_ordered_extent *ordered; -lock_extent (&inode->io_tree, page_start, page_end, &cached_state); +btrfs_lock_extent (&inode->io_tree, page_start, page_end, &cached_state); ordered =btrfs_lookup_ordered_range (inode, page_start,PAGE_SIZE ); -unlock_extent (&inode->io_tree, page_start, page_end, +btrfs_unlock_extent (&inode->io_tree, page_start, page_end, &cached_state);if (!ordered)break ;
@@ -1099,7 +1099,7 @@ add:/* Allocate new defrag_target_range */ new =kmalloc (sizeof (*new),GFP_NOFS );if (!new) { -free_extent_map (em); +btrfs_free_extent_map (em); ret = -ENOMEM ;break ; }
@@ -1109,7 +1109,7 @@ add: next: cur =extent_map_end (em); -free_extent_map (em); +btrfs_free_extent_map (em); }if (ret <0 ) {struct defrag_target_range *entry;
@@ -1226,7 +1226,7 @@ static int defrag_one_range(struct btrfsfolio_wait_writeback (folios[i]);/* Lock the pages range */ -lock_extent (&inode->io_tree, start_index <<PAGE_SHIFT , +btrfs_lock_extent (&inode->io_tree, start_index <<PAGE_SHIFT , (last_index <<PAGE_SHIFT ) +PAGE_SIZE -1 , &cached_state);/*
@@ -1240,7 +1240,7 @@ static int defrag_one_range(struct btrfs newer_than, do_compress, true, &target_list, last_scanned_ret);if (ret <0 ) - goto unlock_extent; + goto btrfs_unlock_extent;list_for_each_entry (entry, &target_list, list) { ret = defrag_one_locked_target(inode, entry, folios, nr_pages,
@@ -1253,8 +1253,8 @@ static int defrag_one_range(struct btrfslist_del_init (&entry->list);kfree (entry); } -unlock_extent: -unlock_extent (&inode->io_tree, start_index <<PAGE_SHIFT , +btrfs_unlock_extent: +btrfs_unlock_extent (&inode->io_tree, start_index <<PAGE_SHIFT , (last_index <<PAGE_SHIFT ) +PAGE_SIZE -1 , &cached_state); free_folios:
fs/btrfs/direct-io.c +10 -10
@@ -52,13 +52,13 @@ static int lock_extent_direct(struct inowhile (1 ) {if (nowait) { -if (!try_lock_extent (io_tree, lockstart, lockend, +if (!btrfs_try_lock_extent (io_tree, lockstart, lockend, cached_state)) { ret = -EAGAIN ;break ; } }else { -lock_extent (io_tree, lockstart, lockend, cached_state); +btrfs_lock_extent (io_tree, lockstart, lockend, cached_state); }/* * We're concerned with the entire range that we're going to be
@@ -80,7 +80,7 @@ static int lock_extent_direct(struct ino lockstart, lockend)))break ; -unlock_extent (io_tree, lockstart, lockend, cached_state); +btrfs_unlock_extent (io_tree, lockstart, lockend, cached_state);if (ordered) {if (nowait) {
@@ -157,7 +157,7 @@ static struct extent_map *btrfs_create_d (1U << BTRFS_ORDERED_DIRECT));if (IS_ERR (ordered)) {if (em) { -free_extent_map (em); +btrfs_free_extent_map (em);btrfs_drop_extent_map_range (inode, start, start + file_extent->num_bytes -1 , false); }
@@ -268,7 +268,7 @@ static int btrfs_get_blocks_direct_write nowait);if (ret <0 ) {/* Our caller expects us to free the input extent map. */ -free_extent_map (em); +btrfs_free_extent_map (em); *map =NULL ;btrfs_dec_nocow_writers (bg);if (nowait && (ret == -ENOSPC || ret == -EDQUOT ))
@@ -281,7 +281,7 @@ static int btrfs_get_blocks_direct_write &file_extent, type);btrfs_dec_nocow_writers (bg);if (type ==BTRFS_ORDERED_PREALLOC ) { -free_extent_map (em); +btrfs_free_extent_map (em); *map = em2; em = em2; }
@@ -294,7 +294,7 @@ static int btrfs_get_blocks_direct_write dio_data->nocow_done = true; }else {/* Our caller expects us to free the input extent map. */ -free_extent_map (em); +btrfs_free_extent_map (em); *map =NULL ;if (nowait) {
@@ -484,7 +484,7 @@ static int btrfs_dio_iomap_begin(struct* the generic code. */ if (extent_map_is_compressed (em) || em->disk_bytenr ==EXTENT_MAP_INLINE ) { -free_extent_map (em); +btrfs_free_extent_map (em);/* * If we are in a NOWAIT context, return -EAGAIN in order to * fallback to buffered IO. This is not only because we can
@@ -525,7 +525,7 @@ static int btrfs_dio_iomap_begin(struct* after we have submitted bios for all the extents in the range. */ if ((flags &IOMAP_NOWAIT ) && len < length) { -free_extent_map (em); +btrfs_free_extent_map (em); ret = -EAGAIN ; goto unlock_err; }
@@ -573,7 +573,7 @@ static int btrfs_dio_iomap_begin(struct iomap->offset = start; iomap->bdev = fs_info->fs_devices->latest_dev->bdev; iomap->length = len; -free_extent_map (em); +btrfs_free_extent_map (em);/* * Reads will hold the EXTENT_DIO_LOCKED bit until the io is completed,
fs/btrfs/extent-io-tree.h +3 -3
@@ -142,13 +142,13 @@ int __lock_extent(struct extent_io_treebool __try_lock_extent (struct extent_io_tree *tree,u64 start,u64 end,u32 bits,struct extent_state **cached); -static inline int lock_extent (struct extent_io_tree *tree,u64 start,u64 end, +static inline int btrfs_lock_extent (struct extent_io_tree *tree,u64 start,u64 end,struct extent_state **cached) {return __lock_extent (tree, start, end,EXTENT_LOCKED , cached); } -static inline bool try_lock_extent (struct extent_io_tree *tree,u64 start, +static inline bool btrfs_try_lock_extent (struct extent_io_tree *tree,u64 start,u64 end,struct extent_state **cached) {return __try_lock_extent (tree, start, end,EXTENT_LOCKED , cached);
@@ -179,7 +179,7 @@ static inline int clear_extent_bit(strucreturn __clear_extent_bit (tree, start, end, bits, cached,NULL ); } -static inline int unlock_extent (struct extent_io_tree *tree,u64 start,u64 end, +static inline int btrfs_unlock_extent (struct extent_io_tree *tree,u64 start,u64 end,struct extent_state **cached) {return __clear_extent_bit (tree, start, end,EXTENT_LOCKED , cached,NULL );
fs/btrfs/extent_io.c +15 -15
@@ -409,13 +409,13 @@ again: }/* step three, lock the state bits for the whole range */ -lock_extent (tree, delalloc_start, delalloc_end, &cached_state); +btrfs_lock_extent (tree, delalloc_start, delalloc_end, &cached_state);/* then test to make sure it is all still delalloc */ ret =test_range_bit (tree, delalloc_start, delalloc_end,EXTENT_DELALLOC , cached_state); -unlock_extent (tree, delalloc_start, delalloc_end, &cached_state); +btrfs_unlock_extent (tree, delalloc_start, delalloc_end, &cached_state);if (!ret) {__unlock_for_delalloc (inode, locked_folio, delalloc_start, delalloc_end);
@@ -947,7 +947,7 @@ static struct extent_map *get_extent_mapreturn em; } -free_extent_map (em); +btrfs_free_extent_map (em); *em_cached =NULL ; }
@@ -1102,7 +1102,7 @@ static int btrfs_do_readpage(struct foli bio_ctrl->last_em_start = em->start; -free_extent_map (em); +btrfs_free_extent_map (em); em =NULL ;/* we've found a hole, just zero and go on */
@@ -1153,9 +1153,9 @@ int btrfs_read_folio(struct file *file,btrfs_lock_and_flush_ordered_range (inode, start, end, &cached_state); ret =btrfs_do_readpage (folio, &em_cached, &bio_ctrl); -unlock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_unlock_extent (&inode->io_tree, start, end, &cached_state); -free_extent_map (em_cached); +btrfs_free_extent_map (em_cached);/* * If btrfs_do_readpage() failed we will want to submit the assembled
@@ -1344,7 +1344,7 @@ static noinline_for_stack int writepage_* We've hit an error during previous delalloc range, * have to cleanup the remaining locked ranges. */ -unlock_extent (&inode->io_tree, found_start, +btrfs_unlock_extent (&inode->io_tree, found_start, found_start + found_len -1 ,NULL ); __unlock_for_delalloc(&inode->vfs_inode, folio, found_start,
@@ -1491,7 +1491,7 @@ static int submit_one_sector(struct btrfASSERT (block_start !=EXTENT_MAP_HOLE );ASSERT (block_start !=EXTENT_MAP_INLINE ); -free_extent_map (em); +btrfs_free_extent_map (em); em =NULL ;/*
@@ -2498,10 +2498,10 @@ void btrfs_readahead(struct readahead_cowhile ((folio =readahead_folio (rac)) !=NULL )btrfs_do_readpage (folio, &em_cached, &bio_ctrl); -unlock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_unlock_extent (&inode->io_tree, start, end, &cached_state);if (em_cached) -free_extent_map (em_cached); +btrfs_free_extent_map (em_cached);submit_one_bio (&bio_ctrl); }
@@ -2525,7 +2525,7 @@ int extent_invalidate_folio(struct extenif (start > end)return 0 ; -lock_extent (tree, start, end, &cached_state); +btrfs_lock_extent (tree, start, end, &cached_state);folio_wait_writeback (folio);/*
@@ -2533,7 +2533,7 @@ int extent_invalidate_folio(struct exten* so here we only need to unlock the extent range to free any * existing extent state. */ -unlock_extent (tree, start, end, &cached_state); +btrfs_unlock_extent (tree, start, end, &cached_state);return 0 ; }
@@ -2602,7 +2602,7 @@ bool try_release_extent_mapping(struct f }if ((em->flags &EXTENT_FLAG_PINNED ) || em->start != start) {write_unlock (&extent_tree->lock); -free_extent_map (em); +btrfs_free_extent_map (em);break ; } if (test_range_bit_exists(io_tree, em->start,
@@ -2635,13 +2635,13 @@ remove_em:*/ remove_extent_mapping (inode, em);/* Once for the inode's extent map tree. */ -free_extent_map (em); +btrfs_free_extent_map (em); next: start =extent_map_end (em);write_unlock (&extent_tree->lock);/* Once for us, for the lookup_extent_mapping() reference. */ -free_extent_map (em); +btrfs_free_extent_map (em);if (need_resched ()) {/*
fs/btrfs/extent_map.c +22 -22
147 lines not on this page · raw message
fs/btrfs/extent_map.h +1 -1
@@ -174,7 +174,7 @@ int split_extent_map(struct btrfs_inode u64 new_logical);struct extent_map *alloc_extent_map (void ); -void free_extent_map (struct extent_map *em); +void btrfs_free_extent_map (struct extent_map *em);int __init extent_map_init (void );void __cold extent_map_exit (void );int unpin_extent_cache (struct btrfs_inode *inode,u64 start,u64 len,u64 gen);
fs/btrfs/fiemap.c +2 -2
@@ -661,7 +661,7 @@ restart: range_end =round_up (start + len, sectorsize); prev_extent_end = range_start; -lock_extent (&inode->io_tree, range_start, range_end, &cached_state); +btrfs_lock_extent (&inode->io_tree, range_start, range_end, &cached_state); ret =fiemap_find_last_extent_offset (inode, path, &last_extent_end);if (ret <0 )
@@ -841,7 +841,7 @@ check_eof_delalloc: } out_unlock: -unlock_extent (&inode->io_tree, range_start, range_end, &cached_state); +btrfs_unlock_extent (&inode->io_tree, range_start, range_end, &cached_state);if (ret ==BTRFS_FIEMAP_FLUSH_CACHE ) {btrfs_release_path (path);
fs/btrfs/file.c +30 -30
234 lines not on this page · raw message
fs/btrfs/free-space-cache.c +5 -5
@@ -340,7 +340,7 @@ int btrfs_truncate_free_space_cache(strubtrfs_i_size_write (inode,0 );truncate_pagecache (vfs_inode,0 ); -lock_extent (&inode->io_tree,0 , (u64)-1 , &cached_state); +btrfs_lock_extent (&inode->io_tree,0 , (u64)-1 , &cached_state);btrfs_drop_extent_map_range (inode,0 , (u64)-1 , false);/*
@@ -352,7 +352,7 @@ int btrfs_truncate_free_space_cache(struinode_sub_bytes (&inode->vfs_inode, control.sub_bytes);btrfs_inode_safe_disk_i_size_write (inode, control.last_size); -unlock_extent (&inode->io_tree,0 , (u64)-1 , &cached_state); +btrfs_unlock_extent (&inode->io_tree,0 , (u64)-1 , &cached_state);if (ret) goto fail;
@@ -1294,7 +1294,7 @@ cleanup_write_cache_enospc(struct inode struct extent_state **cached_state) {io_ctl_drop_pages (io_ctl); -unlock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , +btrfs_unlock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , cached_state); }
@@ -1419,7 +1419,7 @@ static int __btrfs_write_out_cache(strucif (ret) goto out_unlock; -lock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , +btrfs_lock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , &cached_state);io_ctl_set_generation (io_ctl, trans->transid);
@@ -1475,7 +1475,7 @@ static int __btrfs_write_out_cache(strucio_ctl_drop_pages (io_ctl);io_ctl_free (io_ctl); -unlock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , +btrfs_unlock_extent (&BTRFS_I (inode)->io_tree,0 ,i_size_read (inode) -1 , &cached_state);/*
fs/btrfs/inode.c +56 -56
406 lines not on this page · raw message
fs/btrfs/ioctl.c +1 -1
@@ -4617,7 +4617,7 @@ static int btrfs_ioctl_encoded_read(stru args.compression, &unlocked);if (!unlocked) { -unlock_extent (io_tree, start, lockend, &cached_state); +btrfs_unlock_extent (io_tree, start, lockend, &cached_state);btrfs_inode_unlock (inode,BTRFS_ILOCK_SHARED ); } }
fs/btrfs/ordered-data.c +4 -4
@@ -1160,7 +1160,7 @@ void btrfs_lock_and_flush_ordered_range( cachedp = cached_state;while (1 ) { -lock_extent (&inode->io_tree, start, end, cachedp); +btrfs_lock_extent (&inode->io_tree, start, end, cachedp); ordered =btrfs_lookup_ordered_range (inode, start, end - start +1 );if (!ordered) {
@@ -1173,7 +1173,7 @@ void btrfs_lock_and_flush_ordered_range(refcount_dec (&cache->refs);break ; } -unlock_extent (&inode->io_tree, start, end, cachedp); +btrfs_unlock_extent (&inode->io_tree, start, end, cachedp);btrfs_start_ordered_extent (ordered);btrfs_put_ordered_extent (ordered); }
@@ -1191,7 +1191,7 @@ bool btrfs_try_lock_ordered_range(struct {struct btrfs_ordered_extent *ordered; -if (!try_lock_extent (&inode->io_tree, start, end, cached_state)) +if (!btrfs_try_lock_extent (&inode->io_tree, start, end, cached_state))return false; ordered =btrfs_lookup_ordered_range (inode, start, end - start +1 );
@@ -1199,7 +1199,7 @@ bool btrfs_try_lock_ordered_range(structreturn true;btrfs_put_ordered_extent (ordered); -unlock_extent (&inode->io_tree, start, end, cached_state); +btrfs_unlock_extent (&inode->io_tree, start, end, cached_state);return false; }
fs/btrfs/reflink.c +4 -4
@@ -701,9 +701,9 @@ static int btrfs_extent_same_range(struc* because we have already locked the inode's i_mmap_lock in exclusive * mode. */ -lock_extent (&BTRFS_I (dst)->io_tree, dst_loff, end, &cached_state); +btrfs_lock_extent (&BTRFS_I (dst)->io_tree, dst_loff, end, &cached_state); ret =btrfs_clone (src, dst, loff, len,ALIGN (len, bs), dst_loff,1 ); -unlock_extent (&BTRFS_I (dst)->io_tree, dst_loff, end, &cached_state); +btrfs_unlock_extent (&BTRFS_I (dst)->io_tree, dst_loff, end, &cached_state);btrfs_btree_balance_dirty (fs_info);
@@ -802,9 +802,9 @@ static noinline int btrfs_clone_files(st* mode. */ end = destoff + len -1 ; -lock_extent (&BTRFS_I (inode)->io_tree, destoff, end, &cached_state); +btrfs_lock_extent (&BTRFS_I (inode)->io_tree, destoff, end, &cached_state); ret =btrfs_clone (src, inode, off, olen, len, destoff,0 ); -unlock_extent (&BTRFS_I (inode)->io_tree, destoff, end, &cached_state); +btrfs_unlock_extent (&BTRFS_I (inode)->io_tree, destoff, end, &cached_state);/* * We may have copied an inline extent into a page of the destination
fs/btrfs/relocation.c +12 -12
@@ -1025,7 +1025,7 @@ int replace_file_extents(struct btrfs_tr/* Take mmap lock to serialize with reflinks. */ if (!down_read_trylock (&inode->i_mmap_lock))continue ; - ret =try_lock_extent (&inode->io_tree, key.offset, + ret =btrfs_try_lock_extent (&inode->io_tree, key.offset, end, &cached_state);if (!ret) {up_read (&inode->i_mmap_lock);
@@ -1033,7 +1033,7 @@ int replace_file_extents(struct btrfs_tr }btrfs_drop_extent_map_range (inode, key.offset, end, true); -unlock_extent (&inode->io_tree, key.offset, end, +btrfs_unlock_extent (&inode->io_tree, key.offset, end, &cached_state);up_read (&inode->i_mmap_lock); }
@@ -1497,10 +1497,10 @@ static int invalidate_extent_cache(struc end = (u64)-1 ; } -/* the lock_extent waits for read_folio to complete */ -lock_extent (&inode->io_tree, start, end, &cached_state); +/* the btrfs_lock_extent waits for read_folio to complete */ +btrfs_lock_extent (&inode->io_tree, start, end, &cached_state);btrfs_drop_extent_map_range (inode, start, end, true); -unlock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_unlock_extent (&inode->io_tree, start, end, &cached_state); }return 0 ; }
@@ -2873,13 +2873,13 @@ static noinline_for_stack int prealloc_felse end = cluster->end - offset; -lock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_lock_extent (&inode->io_tree, start, end, &cached_state); num_bytes = end +1 - start; ret =btrfs_prealloc_file_range (&inode->vfs_inode,0 , start, num_bytes, num_bytes, end +1 , &alloc_hint); cur_offset = end +1 ; -unlock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_unlock_extent (&inode->io_tree, start, end, &cached_state);if (ret)break ; }
@@ -2912,10 +2912,10 @@ static noinline_for_stack int setup_relo em->ram_bytes = em->len; em->flags |=EXTENT_FLAG_PINNED ; -lock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_lock_extent (&inode->io_tree, start, end, &cached_state); ret =btrfs_replace_extent_map_range (inode, em, false); -unlock_extent (&inode->io_tree, start, end, &cached_state); -free_extent_map (em); +btrfs_unlock_extent (&inode->io_tree, start, end, &cached_state); +btrfs_free_extent_map (em);return ret; }
@@ -3037,7 +3037,7 @@ again: goto release_folio;/* Mark the range delalloc and dirty for later writeback */ -lock_extent (&BTRFS_I (inode)->io_tree, clamped_start, clamped_end, +btrfs_lock_extent (&BTRFS_I (inode)->io_tree, clamped_start, clamped_end, &cached_state); ret =btrfs_set_extent_delalloc (BTRFS_I (inode), clamped_start, clamped_end,0 , &cached_state);
@@ -3071,7 +3071,7 @@ again: boundary_start, boundary_end, EXTENT_BOUNDARY, NULL); } -unlock_extent (&BTRFS_I (inode)->io_tree, clamped_start, clamped_end, +btrfs_unlock_extent (&BTRFS_I (inode)->io_tree, clamped_start, clamped_end, &cached_state);btrfs_delalloc_release_extents (BTRFS_I (inode), clamped_len); cur += clamped_len;
fs/btrfs/tests/extent-io-tests.c +3 -3
@@ -191,7 +191,7 @@ static int test_find_delalloc(u32 sector sectorsize - 1, start, end); goto out_bits; } -unlock_extent (tmp, start, end,NULL ); +btrfs_unlock_extent (tmp, start, end,NULL );unlock_page (locked_page);put_page (locked_page);
@@ -227,7 +227,7 @@ static int test_find_delalloc(u32 sectortest_err ("there were unlocked pages in the range" ); goto out_bits; } -unlock_extent (tmp, start, end,NULL ); +btrfs_unlock_extent (tmp, start, end,NULL );/* locked_page was unlocked above */ put_page (locked_page);
@@ -282,7 +282,7 @@ static int test_find_delalloc(u32 sectortest_err ("pages in range were not all locked" ); goto out_bits; } -unlock_extent (tmp, start, end,NULL ); +btrfs_unlock_extent (tmp, start, end,NULL );/* * Now to test where we run into a page that is no longer dirty in the
fs/btrfs/tests/extent-map-tests.c +21 -21
168 lines not on this page · raw message
fs/btrfs/tests/inode-tests.c +20 -20
@@ -268,7 +268,7 @@ static noinline int test_btrfs_get_extentest_err ("expected a hole, got %llu" , em->disk_bytenr); goto out; } -free_extent_map (em); +btrfs_free_extent_map (em);btrfs_drop_extent_map_range (BTRFS_I (inode),0 , (u64)-1 , false);/*
@@ -314,7 +314,7 @@ static noinline int test_btrfs_get_exten* this? */ offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -336,7 +336,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* Regular extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -363,7 +363,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* The next 3 are split extents */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -392,7 +392,7 @@ static noinline int test_btrfs_get_exten disk_bytenr =extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -414,7 +414,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -447,7 +447,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* Prealloc extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -475,7 +475,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* The next 3 are a half written prealloc extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -505,7 +505,7 @@ static noinline int test_btrfs_get_exten disk_bytenr =extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -537,7 +537,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -570,7 +570,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* Now for the compressed extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -603,7 +603,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* Split compressed extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);
@@ -638,7 +638,7 @@ static noinline int test_btrfs_get_exten disk_bytenr =extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -664,7 +664,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -698,7 +698,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em);/* A hole between regular extents but no hole extent */ em =btrfs_get_extent (BTRFS_I (inode),NULL , offset +6 , sectorsize);
@@ -725,7 +725,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset,SZ_4M );if (IS_ERR (em)) {
@@ -757,7 +757,7 @@ static noinline int test_btrfs_get_exten goto out; } offset = em->start + em->len; -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , offset, sectorsize);if (IS_ERR (em)) {
@@ -785,7 +785,7 @@ static noinline int test_btrfs_get_exten ret =0 ; out:if (!IS_ERR (em)) -free_extent_map (em); +btrfs_free_extent_map (em);iput (inode);btrfs_free_dummy_root (root);btrfs_free_dummy_fs_info (fs_info);
@@ -858,7 +858,7 @@ static int test_hole_first(u32 sectorsiz em->flags); goto out; } -free_extent_map (em); +btrfs_free_extent_map (em); em =btrfs_get_extent (BTRFS_I (inode),NULL , sectorsize,2 * sectorsize);if (IS_ERR (em)) {
@@ -883,7 +883,7 @@ static int test_hole_first(u32 sectorsiz ret =0 ; out:if (!IS_ERR (em)) -free_extent_map (em); +btrfs_free_extent_map (em);iput (inode);btrfs_free_dummy_root (root);btrfs_free_dummy_fs_info (fs_info);
fs/btrfs/tree-log.c +4 -4
@@ -4407,7 +4407,7 @@ static int log_csums(struct btrfs_trans_* file which happens to refer to the same extent as well. Such races * can leave checksum items in the log with overlapping ranges. */ - ret =lock_extent (&log_root->log_csum_range, sums->logical, lock_end, + ret =btrfs_lock_extent (&log_root->log_csum_range, sums->logical, lock_end, &cached_state);if (ret)return ret;
@@ -4424,7 +4424,7 @@ static int log_csums(struct btrfs_trans_if (!ret) ret =btrfs_csum_file_blocks (trans, log_root, sums); -unlock_extent (&log_root->log_csum_range, sums->logical, lock_end, +btrfs_unlock_extent (&log_root->log_csum_range, sums->logical, lock_end, &cached_state);return ret;
@@ -5066,7 +5066,7 @@ process:*/ if (ret) {clear_em_logging (inode, em); -free_extent_map (em); +btrfs_free_extent_map (em);continue ; }
@@ -5075,7 +5075,7 @@ process: ret =log_one_extent (trans, inode, em, path, ctx);write_lock (&tree->lock);clear_em_logging (inode, em); -free_extent_map (em); +btrfs_free_extent_map (em); }WARN_ON (!list_empty (&extents));write_unlock (&tree->lock);
fs/btrfs/zoned.c +1 -1
@@ -2035,7 +2035,7 @@ static void btrfs_rewrite_logical_zoned(/* The em should be a new COW extent, thus it should not have an offset. */ ASSERT (em->offset ==0 ); em->disk_bytenr = logical; -free_extent_map (em); +btrfs_free_extent_map (em);write_unlock (&em_tree->lock); }
Greg Kroah-Hartman
maintainerPATCH 1050/1102 btrfs: rename extent map functions to get block start, end and check if in tree
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 2e871330cea44f7459726b0d83252949ae76166f ]
These functions are exported and don't have a 'btrfs_' prefix in their names, which goes against coding style conventions. Rename them to have such prefix, making it clear they are from btrfs and avoiding potential collisions in the future with functions defined elsewhere outside btrfs.
Stable-dep-of: 3f950867c307 ("btrfs: fix extent map leak in NOCOW direct I/O write")
--- fs/btrfs/compression.c | 4 +-- fs/btrfs/defrag.c | 4 +-- fs/btrfs/direct-io.c | 4 +-- fs/btrfs/extent_io.c | 23 +++++++++-------- fs/btrfs/extent_map.c | 50 +++++++++++++++++++------------------- fs/btrfs/extent_map.h | 6 ++-- fs/btrfs/file.c | 6 ++-- fs/btrfs/inode.c | 14 +++++----- fs/btrfs/tests/extent-map-tests.c | 14 +++++----- fs/btrfs/tests/inode-tests.c | 27 ++++++++++---------- fs/btrfs/tree-log.c | 4 +-- 11 files changed, 79 insertions(+), 77 deletions(-)
fs/btrfs/compression.c +2 -2
@@ -510,8 +510,8 @@ static noinline int add_ra_bio_pages(str* to this compressed extent on disk. */ if (!em || cur < em->start || - (cur + fs_info->sectorsize >extent_map_end (em)) || - (extent_map_block_start (em) >>SECTOR_SHIFT ) != + (cur + fs_info->sectorsize >btrfs_extent_map_end (em)) || + (btrfs_extent_map_block_start (em) >>SECTOR_SHIFT ) != orig_bio->bi_iter.bi_sector) {btrfs_free_extent_map (em);btrfs_unlock_extent (tree, cur, page_end,NULL );
fs/btrfs/defrag.c +2 -2
@@ -1077,7 +1077,7 @@ static int defrag_collect_targets(struct add: last_is_target = true; - range_len =min (extent_map_end (em), start + len) - cur; + range_len =min (btrfs_extent_map_end (em), start + len) - cur;/* * This one is a good target, check if it can be merged into * last range of the target list.
@@ -1108,7 +1108,7 @@ add:list_add_tail (&new->list, target_list); next: - cur =extent_map_end (em); + cur =btrfs_extent_map_end (em);btrfs_free_extent_map (em); }if (ret <0 ) {
fs/btrfs/direct-io.c +2 -2
@@ -249,7 +249,7 @@ static int btrfs_get_blocks_direct_writeelse type =BTRFS_ORDERED_NOCOW ; len =min (len, em->len - (start - em->start)); - block_start =extent_map_block_start (em) + (start - em->start); + block_start =btrfs_extent_map_block_start (em) + (start - em->start);if (can_nocow_extent (inode, start, &len, &file_extent, false, false) ==1 ) {
@@ -567,7 +567,7 @@ static int btrfs_dio_iomap_begin(struct iomap->addr =IOMAP_NULL_ADDR ; iomap->type =IOMAP_HOLE ; }else { - iomap->addr =extent_map_block_start (em) + (start - em->start); + iomap->addr =btrfs_extent_map_block_start (em) + (start - em->start); iomap->type =IOMAP_MAPPED ; } iomap->offset = start;
fs/btrfs/extent_io.c +12 -11
@@ -941,8 +941,8 @@ static struct extent_map *get_extent_mapif (*em_cached) { em = *em_cached; -if (extent_map_in_tree (em) && start >= em->start && - start <extent_map_end (em)) { +if (btrfs_extent_map_in_tree (em) && start >= em->start && + start <btrfs_extent_map_end (em)) {refcount_inc (&em->refs);return em; }
@@ -1035,12 +1035,12 @@ static int btrfs_do_readpage(struct folireturn PTR_ERR (em); } extent_offset = cur - em->start; -BUG_ON (extent_map_end (em) <= cur); +BUG_ON (btrfs_extent_map_end (em) <= cur);BUG_ON (end < cur); compress_type =extent_map_compression (em); - iosize =min (extent_map_end (em) - cur, end - cur +1 ); + iosize =min (btrfs_extent_map_end (em) - cur, end - cur +1 ); iosize =ALIGN (iosize, blocksize);/*
@@ -1056,8 +1056,8 @@ static int btrfs_do_readpage(struct foliif (compress_type !=BTRFS_COMPRESS_NONE ) disk_bytenr = em->disk_bytenr;else - disk_bytenr =extent_map_block_start (em) + extent_offset; - block_start =extent_map_block_start (em); + disk_bytenr =btrfs_extent_map_block_start (em) + extent_offset; + block_start =btrfs_extent_map_block_start (em);if (em->flags &EXTENT_FLAG_PREALLOC ) block_start =EXTENT_MAP_HOLE ;
@@ -1479,13 +1479,13 @@ static int submit_one_sector(struct btrf } extent_offset = filepos - em->start; - em_end =extent_map_end (em); + em_end =btrfs_extent_map_end (em);ASSERT (filepos <= em_end);ASSERT (IS_ALIGNED (em->start, sectorsize));ASSERT (IS_ALIGNED (em->len, sectorsize)); - block_start =extent_map_block_start (em); - disk_bytenr =extent_map_block_start (em) + extent_offset; + block_start =btrfs_extent_map_block_start (em); + disk_bytenr =btrfs_extent_map_block_start (em) + extent_offset;ASSERT (!extent_map_is_compressed (em));ASSERT (block_start !=EXTENT_MAP_HOLE );
@@ -2606,7 +2606,8 @@ bool try_release_extent_mapping(struct fbreak ; }if (test_range_bit_exists (io_tree, em->start, -extent_map_end (em) -1 ,EXTENT_LOCKED )) +btrfs_extent_map_end (em) -1 , +EXTENT_LOCKED )) goto next;/* * If it's not in the list of modified extents, used by a fast
@@ -2637,7 +2638,7 @@ remove_em:/* Once for the inode's extent map tree. */ btrfs_free_extent_map (em); next: - start =extent_map_end (em); + start =btrfs_extent_map_end (em);write_unlock (&extent_tree->lock);/* Once for us, for the lookup_extent_mapping() reference. */
fs/btrfs/extent_map.c +25 -25
181 lines not on this page · raw message
fs/btrfs/extent_map.h +3 -3
@@ -144,12 +144,12 @@ static inline bool extent_map_is_compres EXTENT_FLAG_COMPRESS_ZSTD)) != 0; } -static inline int extent_map_in_tree (const struct extent_map *em) +static inline int btrfs_extent_map_in_tree (const struct extent_map *em) {return !RB_EMPTY_NODE (&em->rb_node); } -static inline u64 extent_map_block_start (const struct extent_map *em) +static inline u64 btrfs_extent_map_block_start (const struct extent_map *em) {if (em->disk_bytenr <EXTENT_MAP_LAST_BYTE ) {if (extent_map_is_compressed (em))
@@ -159,7 +159,7 @@ static inline u64 extent_map_block_startreturn em->disk_bytenr; } -static inline u64 extent_map_end (const struct extent_map *em) +static inline u64 btrfs_extent_map_end (const struct extent_map *em) {if (em->start + em->len < em->start)return (u64)-1 ;
fs/btrfs/file.c +3 -3
@@ -2975,7 +2975,7 @@ static int btrfs_zero_range(struct inodeASSERT (IS_ALIGNED (alloc_start, sectorsize)); len = offset + len - alloc_start; offset = alloc_start; - alloc_hint =extent_map_block_start (em) + em->len; + alloc_hint =btrfs_extent_map_block_start (em) + em->len; }btrfs_free_extent_map (em);
@@ -3203,8 +3203,8 @@ static long btrfs_fallocate(struct file ret =PTR_ERR (em);break ; } - last_byte =min (extent_map_end (em), alloc_end); - actual_end =min_t (u64,extent_map_end (em), offset + len); + last_byte =min (btrfs_extent_map_end (em), alloc_end); + actual_end =min_t (u64,btrfs_extent_map_end (em), offset + len); last_byte =ALIGN (last_byte, blocksize); if (em->disk_bytenr == EXTENT_MAP_HOLE || (cur_offset >= inode->i_size &&
fs/btrfs/inode.c +7 -7
@@ -1326,11 +1326,11 @@ u64 btrfs_get_extent_allocation_hint(strbtrfs_free_extent_map (em); em =search_extent_mapping (em_tree,0 ,0 );if (em && em->disk_bytenr <EXTENT_MAP_LAST_BYTE ) - alloc_hint =extent_map_block_start (em); + alloc_hint =btrfs_extent_map_block_start (em);if (em)btrfs_free_extent_map (em); }else { - alloc_hint =extent_map_block_start (em); + alloc_hint =btrfs_extent_map_block_start (em);btrfs_free_extent_map (em); } }
@@ -2787,7 +2787,7 @@ static int btrfs_find_new_delalloc_bytes search_start + em_len -1 , EXTENT_DELALLOC_NEW, cached_state); next: - search_start =extent_map_end (em); + search_start =btrfs_extent_map_end (em);btrfs_free_extent_map (em);if (ret)return ret;
@@ -4935,7 +4935,7 @@ int btrfs_cont_expand(struct btrfs_inode em =NULL ;break ; } - last_byte =min (extent_map_end (em), block_end); + last_byte =min (btrfs_extent_map_end (em), block_end); last_byte =ALIGN (last_byte, fs_info->sectorsize); hole_size = last_byte - cur_offset;
@@ -6992,7 +6992,7 @@ not_found: insert: ret =0 ;btrfs_release_path (path); -if (em->start > start ||extent_map_end (em) <= start) { +if (em->start > start ||btrfs_extent_map_end (em) <= start) {btrfs_err (fs_info,"bad extent! em: [%llu %llu] passed [%llu %llu]" , em->start, em->len, start, len);
@@ -9373,7 +9373,7 @@ ssize_t btrfs_encoded_read(struct kiocb* We only want to return up to EOF even if the extent extends beyond * that. */ - encoded->len =min_t (u64,extent_map_end (em), + encoded->len =min_t (u64,btrfs_extent_map_end (em), inode->vfs_inode.i_size) - iocb->ki_pos;if (em->disk_bytenr ==EXTENT_MAP_HOLE || (em->flags &EXTENT_FLAG_PREALLOC )) {
@@ -9401,7 +9401,7 @@ ssize_t btrfs_encoded_read(struct kiocb goto out_em; encoded->compression = ret; }else { - *disk_bytenr =extent_map_block_start (em) + (start - em->start); + *disk_bytenr =btrfs_extent_map_block_start (em) + (start - em->start);if (encoded->len > count) encoded->len = count;/*
fs/btrfs/tests/extent-map-tests.c +7 -7
@@ -137,7 +137,7 @@ static int test_case_1(struct btrfs_fs_i ret = -ENOENT ; goto out; } -if (em->start !=0 ||extent_map_end (em) !=SZ_16K || +if (em->start !=0 ||btrfs_extent_map_end (em) !=SZ_16K || em->disk_bytenr !=0 || em->disk_num_bytes !=SZ_16K ) { test_err( "case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu disk_bytenr %llu disk_num_bytes %llu",
@@ -235,7 +235,7 @@ static int test_case_2(struct btrfs_fs_i ret = -ENOENT ; goto out; } -if (em->start !=0 ||extent_map_end (em) !=SZ_1K || +if (em->start !=0 ||btrfs_extent_map_end (em) !=SZ_1K || em->disk_bytenr !=EXTENT_MAP_INLINE ) { test_err( "case2 [0 1K]: ret %d return a wrong em (start %llu len %llu disk_bytenr %llu",
@@ -312,8 +312,8 @@ static int __test_case_3(struct btrfs_fs* Since bytes within em are contiguous, em->block_start is identical to * em->start. */ -if (start < em->start || start + len >extent_map_end (em) || - em->start !=extent_map_block_start (em)) { +if (start < em->start || start + len >btrfs_extent_map_end (em) || + em->start !=btrfs_extent_map_block_start (em)) { test_err( "case3 [%llu %llu): ret %d em (start %llu len %llu disk_bytenr %llu block_len %llu)", start, start + len, ret, em->start, em->len,
@@ -438,7 +438,7 @@ static int __test_case_4(struct btrfs_fs ret = -ENOENT ; goto out; } -if (start < em->start || start + len >extent_map_end (em)) { +if (start < em->start || start + len >btrfs_extent_map_end (em)) { test_err( "case4 [%llu %llu): ret %d, added wrong em (start %llu len %llu disk_bytenr %llu disk_num_bytes %llu)", start, start + len, ret, em->start, em->len,
@@ -870,9 +870,9 @@ static int test_case_7(struct btrfs_fs_i goto out; } -if (extent_map_block_start (em) !=SZ_32K +SZ_4K ) { +if (btrfs_extent_map_block_start (em) !=SZ_32K +SZ_4K ) {test_err ("em->block_start is %llu, expected 36K" , -extent_map_block_start (em)); +btrfs_extent_map_block_start (em)); goto out; }
fs/btrfs/tests/inode-tests.c +14 -13
@@ -389,7 +389,7 @@ static noinline int test_btrfs_get_extentest_err ("wrong offset, want 0, have %llu" , em->offset); goto out; } - disk_bytenr =extent_map_block_start (em); + disk_bytenr =btrfs_extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len;btrfs_free_extent_map (em);
@@ -441,9 +441,9 @@ static noinline int test_btrfs_get_exten goto out; } disk_bytenr += (em->start - orig_start); -if (extent_map_block_start (em) != disk_bytenr) { +if (btrfs_extent_map_block_start (em) != disk_bytenr) {test_err ("wrong block start, want %llu, have %llu" , - disk_bytenr,extent_map_block_start (em)); + disk_bytenr,btrfs_extent_map_block_start (em)); goto out; } offset = em->start + em->len;
@@ -502,7 +502,7 @@ static noinline int test_btrfs_get_extentest_err ("wrong offset, want 0, have %llu" , em->offset); goto out; } - disk_bytenr =extent_map_block_start (em); + disk_bytenr =btrfs_extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len;btrfs_free_extent_map (em);
@@ -531,9 +531,9 @@ static noinline int test_btrfs_get_exten em->start - orig_start, em->offset); goto out; } -if (extent_map_block_start (em) != disk_bytenr + em->offset) { +if (btrfs_extent_map_block_start (em) != disk_bytenr + em->offset) {test_err ("unexpected block start, wanted %llu, have %llu" , - disk_bytenr + em->offset,extent_map_block_start (em)); + disk_bytenr + em->offset,btrfs_extent_map_block_start (em)); goto out; } offset = em->start + em->len;
@@ -564,9 +564,9 @@ static noinline int test_btrfs_get_exten em->start, em->offset, orig_start); goto out; } -if (extent_map_block_start (em) != disk_bytenr + em->offset) { +if (btrfs_extent_map_block_start (em) != disk_bytenr + em->offset) {test_err ("unexpected block start, wanted %llu, have %llu" , - disk_bytenr + em->offset,extent_map_block_start (em)); + disk_bytenr + em->offset,btrfs_extent_map_block_start (em)); goto out; } offset = em->start + em->len;
@@ -635,7 +635,7 @@ static noinline int test_btrfs_get_exten BTRFS_COMPRESS_ZLIB, extent_map_compression(em)); goto out; } - disk_bytenr =extent_map_block_start (em); + disk_bytenr =btrfs_extent_map_block_start (em); orig_start = em->start; offset = em->start + em->len;btrfs_free_extent_map (em);
@@ -671,9 +671,9 @@ static noinline int test_btrfs_get_extentest_err ("got an error when we shouldn't have" ); goto out; } -if (extent_map_block_start (em) != disk_bytenr) { +if (btrfs_extent_map_block_start (em) != disk_bytenr) {test_err ("block start does not match, want %llu got %llu" , - disk_bytenr,extent_map_block_start (em)); + disk_bytenr,btrfs_extent_map_block_start (em)); goto out; }if (em->start != offset || em->len !=2 * sectorsize) {
@@ -865,8 +865,9 @@ static int test_hole_first(u32 sectorsiztest_err ("got an error when we shouldn't have" ); goto out; } -if (extent_map_block_start (em) != sectorsize) { -test_err ("expected a real extent, got %llu" ,extent_map_block_start (em)); +if (btrfs_extent_map_block_start (em) != sectorsize) { +test_err ("expected a real extent, got %llu" , +btrfs_extent_map_block_start (em)); goto out; }if (em->start != sectorsize || em->len != sectorsize) {
fs/btrfs/tree-log.c +2 -2
@@ -4765,7 +4765,7 @@ static int log_extent_csums(struct btrfs }/* block start is already adjusted for the file extent offset. */ - block_start =extent_map_block_start (em); + block_start =btrfs_extent_map_block_start (em); csum_root =btrfs_csum_root (trans->fs_info, block_start); ret = btrfs_lookup_csums_list(csum_root, block_start + csum_offset, block_start + csum_offset + csum_len - 1,
@@ -4800,7 +4800,7 @@ static int log_one_extent(struct btrfs_tstruct btrfs_key key;enum btrfs_compression_type compress_type;u64 extent_offset = em->offset; -u64 block_start =extent_map_block_start (em); +u64 block_start =btrfs_extent_map_block_start (em);u64 block_len;int ret;
Greg Kroah-Hartman
maintainerPATCH 1051/1102 btrfs: fix extent map leak in NOCOW direct I/O write
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
[ Upstream commit 3f950867c307c5413d628a153ac44915bd117ffd ]
btrfs_dio_iomap_begin() calls btrfs_get_extent(), which returns an extent map reference that must be dropped on all exit paths.
For direct writes into a NOCOW range, btrfs_get_blocks_direct_write() keeps using that extent map and asks btrfs_create_dio_extent() to allocate the ordered extent. If that fails, for example because btrfs_alloc_ordered_extent() fails, the function returns the error without dropping the input extent map. The PREALLOC path avoided this by dropping the input extent map before replacing it with the newly created one.
Check the error from btrfs_create_dio_extent() before replacing the map and drop the input extent map on failure.
--- fs/btrfs/direct-io.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
fs/btrfs/direct-io.c +13 -6
@@ -280,17 +280,24 @@ static int btrfs_get_blocks_direct_write em2 =btrfs_create_dio_extent (BTRFS_I (inode), dio_data, start, &file_extent, type);btrfs_dec_nocow_writers (bg); -if (type ==BTRFS_ORDERED_PREALLOC ) { -btrfs_free_extent_map (em); - *map = em2; - em = em2; - } -if (IS_ERR (em2)) { ret =PTR_ERR (em2); +btrfs_free_extent_map (em); + *map =NULL ; goto out; } +/* +* True NOCOW writes don't need to create a new extent map, +* while PREALLOC writes must replace the existing one. +*/ +if (em2) { +ASSERT (type ==BTRFS_ORDERED_PREALLOC ); +btrfs_free_extent_map (em); + *map = em2; + em = em2; + } + dio_data->nocow_done = true; }else {/* Our caller expects us to free the input extent map. */
Greg Kroah-Hartman
maintainerPATCH 1052/1102 btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit 15f7c86215e8d5f14b24127fa88af6c79363d50e ]
[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to "-o nodatasum":
FSTYP -- btrfs PLATFORM -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026 MKFS_OPTIONS -- -O bgt -K /dev/mapper/test-scratch1
MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
# generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
# --- tests/generic/628.out 2022-05-11 11:25:30.816666664 +0930 # +++ /home/adam/xfstests/results//generic/628.out.bad 2026-06-08 18:56:49.878542927 +0930 # @@ -8,8 +8,9 @@ # 310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a # 310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/d # test reflink flag not set iflag # +XFS_IOC_CLONE: Invalid argument # 310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a # -310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/b # +d41d8cd98f00b204e9800998ecf8427e SCRATCH_MNT/b
# ...
[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its NODATASUM flag inherited from the mount option. E.g.:
# mkfs.btrfs -f $dev
# mount $dev $mnt -o nodatasum
# touch $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x1(NODATASUM)
^^^^^^^^^ Proper NODATASUM flag # chattr +S $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 2 flags 0x20(SYNC)
^^^^ Only the new SYNC flagThis makes the inode drop the old NODATASUM flag, while the new reflink destination will still inherit the NODATASUM flag. The mismatching NODATASUM flags will cause the reflink to fail.
The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is set, we remove both NODATASUM and NODATACOW flag.
However we should not touch NODATASUM flag, as data COW doesn't require checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply DATASUM.
The deeper problems are:
- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option. So that why "chattr +S" implies "chattr -C", and is forcing us to change NODATACOW along with NODATASUM flag.
- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.
The deeper problems are much harder to attack.
[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove NODATASUM if "nodatasum" mount option is not set.
This allows the existing "chattr +C" then "chattr -C" to remove both NODATACOW and NODATASUM flags on a default mount.
But for a mount with "nodatasum" option, the NODATASUM inode flag will persist through either "chattr +C" and "chattr -C".
--- fs/btrfs/ioctl.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
fs/btrfs/ioctl.c +15 -8
@@ -329,14 +329,21 @@ int btrfs_fileattr_set(struct mnt_idmap inode_flags |=BTRFS_INODE_NODATACOW ; } }else { -/* -* Revert back under same assumptions as above -*/ -if (S_ISREG (inode->vfs_inode.i_mode)) { -if (inode->vfs_inode.i_size ==0 ) - inode_flags &= ~(BTRFS_INODE_NODATACOW | -BTRFS_INODE_NODATASUM ); - }else { +/* We can only change NODATACOW for zero-sized regular file. */ +if (S_ISREG (inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size ==0 )) { + inode_flags &= ~BTRFS_INODE_NODATACOW ; +/* +* There is currently no way to change NODATASUM flag +* through fileattr API. If we unconditionally keep the +* current NODATASUM flag, chattr +C then chattr -C will +* keep the NODATASUM flag, and no way to remove that +* flag. +* +* So respect the current mount option for NODATASUM flag. +*/ +if (!btrfs_test_opt (fs_info,NODATASUM )) + inode_flags &= ~BTRFS_INODE_NODATASUM ; + }else if (!S_ISREG (inode->vfs_inode.i_mode)) { inode_flags &= ~BTRFS_INODE_NODATACOW ; } }
Greg Kroah-Hartman
maintainerPATCH 1053/1102 HID: sony: fix UAF of ghl_poke_timer / ghl_urb at driver unbind
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit a26705bd2e2728833e7a538ce91e58a5eeff496a ]
For GHL (Guitar Hero Live) dongles, sony_probe() arms a periodic timer:ghl_magic_poke() (the timer callback) submits sc->ghl_urb, and the URB completion ghl_magic_poke_cb() re-arms the timer with mod_timer().
sony_remove() drained the timer with timer_delete_sync() and then freed the URB with usb_free_urb():
timer_delete_sync(&sc->ghl_poke_timer); usb_free_urb(sc->ghl_urb);
timer_delete_sync() does not block re-arming, and while the URB is in flight the timer is not pending, so the sync delete is a no-op. A URB completion that runs after the delete re-arms the timer, and usb_free_urb() only drops a reference -- it does not kill an in-flight URB. sc is allocated with devm_kzalloc() and freed once sony_remove() returns, so the re-armed ghl_poke_timer (embedded in sc) then fires on freed memory, a use-after-free from timer softirq. This is a disconnect/rmmod race.
Poison the URB first, then shut the timer down, before freeing the URB. usb_poison_urb() kills any in-flight URB and permanently rejects further submissions, so a poke timer that is still pending cannot re-submit the URB from ghl_magic_poke() in the window before timer_shutdown_sync() runs. usb_kill_urb() would not suffice: it only cancels the in-flight URB and leaves it submittable once it returns, so the pending timer could re-submit it and put a fresh URB in flight over the freed sc. timer_shutdown_sync() then drains any last callback and blocks re-arming. The probe error path is unaffected: it is only reached before the timer is armed.
Reproduced under KASAN on next-20260710 via dummy_hcd + raw-gadget emulation of the GHL PS4 dongle (VID 0x1430 / PID 0x07bb): hid-sony binds and arms the poke timer, the poke URB is held in flight, the driver is unbound (freeing sc), then the URB is released. The completion re-arms the timer on the freed sc, and the re-armed timer fires ~8 s later:
BUG: KASAN: slab-use-after-free in ghl_magic_poke+0x98/0xb0 Read of size 8 at addr ffff88810b02fd50 by task swapper/0/0 ghl_magic_poke+0x98/0xb0 call_timer_fn+0x35/0x2b0 __run_timers+0x69c/0x9a0 run_timer_softirq+0x173/0x2a0 Allocated by task 169: sony_probe Freed by task 338: devres_release_group <- hid_device_remove (sony_remove)
Found by 0sec (https://0sec.ai) using automated source analysis.
--- drivers/hid/hid-sony.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/hid-sony.c +3 -1
@@ -2163,7 +2163,9 @@ static void sony_remove(struct hid_devicstruct sony_sc *sc =hid_get_drvdata (hdev);if (sc->quirks & (GHL_GUITAR_PS3WIIU |GHL_GUITAR_PS4 )) { -del_timer_sync (&sc->ghl_poke_timer); +/* poison, not kill: a pending timer must not re-submit during teardown */ +usb_poison_urb (sc->ghl_urb); +timer_shutdown_sync (&sc->ghl_poke_timer);usb_free_urb (sc->ghl_urb); }
Greg Kroah-Hartman
maintainerPATCH 1054/1102 HID: universal-pidff: stop the device when force-feedback init fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
[ Upstream commit ce08c5555cabcd444d8b77fa69a7cb68bb05f611 ]
universal_pidff_probe() starts the device with hid_hw_start() and then, if force-feedback initialisation fails, returns the error through a label that only does "return error". The device is left started.
The HID core does not unwind on the driver's behalf. __hid_device_probe() releases the devres group, closes the report and clears hdev->driver:
if (ret) {
devres_release_group(&hdev->dev, hdev->devres_group_id);
hid_close_report(hdev);
hdev->driver = NULL;
}The hidraw character device that hid_hw_start() registered through hid_connect() is allocated with kzalloc() and added with cdev_device_add(), so it is not devres-managed and survives that. With hdev->driver NULL, hid_device_remove() skips hid_hw_stop() as well, because it only unwinds while a driver is still attached. The registration therefore outlives the device on both paths.
Opening the surviving /dev/hidrawX writes into freed memory. KASAN reports a use-after-free write from hidraw_open() -> hid_hw_open() -> the transport's open callback, which takes a spinlock inside the freed object. A descriptor that carries a PID usage page and no input reports is enough:
hidraw claims the device so hid_hw_start() succeeds, while hid->inputs stays empty so force-feedback init fails. The other failure returns in hid_pidff_init_with_quirks() - no output reports, an allocation failure, pidff_init_fields(), pidff_check_autocenter(), an unusable effect count, input_ff_create() - all reach the same label.
Stop the device on that path. hid-dr.c and hid-emsff.c, which start the device with the same HID_CONNECT_DEFAULT & ~HID_CONNECT_FF mask, already do this. The two earlier gotos must keep returning without hid_hw_stop(), since neither has a started device, so give the path that fails after the start its own label.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- drivers/hid/hid-universal-pidff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/hid-universal-pidff.c +3 -1
@@ -101,12 +101,14 @@ static int universal_pidff_probe(struct error =init_function (hdev, id->driver_data);if (error) {hid_warn (hdev,"Error initialising force feedback\n" ); - goto err; + goto err_stop; }hid_info (hdev,"Universal pidff driver loaded sucessfully!" );return 0 ; +err_stop: +hid_hw_stop (hdev); err:return error; }
Greg Kroah-Hartman
maintainerPATCH 1055/1102 HID: sony: use guard() and scoped_guard()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosalie Wanders <[email protected]>
[ Upstream commit da4f817ad273bca9aefd8636d347a8c101069111 ]
This replaces the spin_lock_irqsave() and spin_unlock_irqrestore() calls with the RAII guard() and scoped_guard().
Stable-dep-of: 7c65699a3a31 ("HID: sony: clean up device list on probe failure")
--- drivers/hid/hid-sony.c | 53 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 31 deletions(-)
drivers/hid/hid-sony.c +22 -31
@@ -27,6 +27,7 @@* There will be no PIN request from the device. */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h>
@@ -520,14 +521,12 @@ static void sony_set_leds(struct sony_scstatic inline void sony_schedule_work (struct sony_sc *sc,enum sony_worker which) { -unsigned long flags; -switch (which) {case SONY_WORKER_STATE : -spin_lock_irqsave (&sc->lock, flags); -if (!sc->defer_initialization && sc->state_worker_initialized) -schedule_work (&sc->state_worker); -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { +if (!sc->defer_initialization && sc->state_worker_initialized) +schedule_work (&sc->state_worker); + }break ; } }
@@ -796,7 +795,6 @@ static const u8 *sony_report_fixup(strucstatic void sixaxis_parse_report (struct sony_sc *sc,u8 *rd,int size) {static const u8 sixaxis_battery_capacity[] = {0 ,1 ,25 ,50 ,75 ,100 }; -unsigned long flags;int offset;u8 battery_capacity;int battery_status;
@@ -818,10 +816,10 @@ static void sixaxis_parse_report(struct battery_status =POWER_SUPPLY_STATUS_DISCHARGING ; } -spin_lock_irqsave (&sc->lock, flags); - sc->battery_capacity = battery_capacity; - sc->battery_status = battery_status; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->battery_capacity = battery_capacity; + sc->battery_status = battery_status; + }if (sc->quirks &SIXAXIS_CONTROLLER ) {int val;
@@ -1624,15 +1622,14 @@ static int sony_battery_get_property(str union power_supply_propval *val) {struct sony_sc *sc =power_supply_get_drvdata (psy); -unsigned long flags;int ret =0 ;u8 battery_capacity;int battery_status; -spin_lock_irqsave (&sc->lock, flags); - battery_capacity = sc->battery_capacity; - battery_status = sc->battery_status; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + battery_capacity = sc->battery_capacity; + battery_status = sc->battery_status; + }switch (psp) {case POWER_SUPPLY_PROP_PRESENT :
@@ -1714,10 +1711,9 @@ static inline int sony_compare_connectiostatic int sony_check_add_dev_list (struct sony_sc *sc) {struct sony_sc *entry; -unsigned long flags;int ret; -spin_lock_irqsave (&sony_dev_list_lock, flags); +guard (spinlock_irqsave)(&sony_dev_list_lock);list_for_each_entry (entry, &sony_device_list, list_node) { ret = memcmp(sc->mac_address, entry->mac_address,
@@ -1731,26 +1727,23 @@ static int sony_check_add_dev_list(struc"controller with MAC address %pMR already connected\n" , sc->mac_address); } - goto unlock; + goto out; } } ret =0 ;list_add (&(sc->list_node), &sony_device_list); -unlock: -spin_unlock_irqrestore (&sony_dev_list_lock, flags); +out:return ret; }static void sony_remove_dev_list (struct sony_sc *sc) { -unsigned long flags; -if (sc->list_node.next) { -spin_lock_irqsave (&sony_dev_list_lock, flags); -list_del (&(sc->list_node)); -spin_unlock_irqrestore (&sony_dev_list_lock, flags); + scoped_guard(spinlock_irqsave, &sony_dev_list_lock) { +list_del (&(sc->list_node)); + } } }
@@ -1878,12 +1871,10 @@ static inline void sony_init_output_repostatic inline void sony_cancel_work_sync (struct sony_sc *sc) { -unsigned long flags; -if (sc->state_worker_initialized) { -spin_lock_irqsave (&sc->lock, flags); - sc->state_worker_initialized =0 ; -spin_unlock_irqrestore (&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->state_worker_initialized =0 ; + }cancel_work_sync (&sc->state_worker); } }
Greg Kroah-Hartman
maintainerPATCH 1056/1102 HID: sony: clean up device list on probe failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit 7c65699a3a311198a07659a614fe64d45924839e ]
sony_input_configured() adds some controllers to sony_device_list before HID core registers their input devices. input_register_device() can fail after the callback returns successfully. sony_probe() then observes that HID_CLAIMED_INPUT is clear and unwinds, but only stops the HID hardware. The devres-managed sony_sc is freed while its list node remains linked, so the next matching controller traverses freed memory.
Initialize the list node and device ID to inactive states. Make list removal idempotent and run the driver-private cleanup on every probe failure path. This also makes a second cleanup safe when sony_input_configured() already unwound a partial initialization before sony_probe() handles the missing input claim.
Found by 0sec (https://0sec.ai) using automated source analysis; verified against the HID input registration and probe unwind paths.
--- drivers/hid/hid-sony.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-)
drivers/hid/hid-sony.c +17 -16
@@ -1740,11 +1740,10 @@ out:static void sony_remove_dev_list (struct sony_sc *sc) { -if (sc->list_node.next) { - scoped_guard(spinlock_irqsave, &sony_dev_list_lock) { -list_del (&(sc->list_node)); - } - } +guard (spinlock_irqsave)(&sony_dev_list_lock); + +if (!list_empty (&sc->list_node)) +list_del_init (&sc->list_node); }static int sony_get_bt_devaddr (struct sony_sc *sc)
@@ -1879,6 +1878,13 @@ static inline void sony_cancel_work_sync } } +static void sony_cleanup (struct sony_sc *sc) +{ +sony_cancel_work_sync (sc); +sony_remove_dev_list (sc); +sony_release_device_id (sc); +} +static int sony_input_configured (struct hid_device *hdev,struct hid_input *hidinput) {
@@ -2037,9 +2043,7 @@ static int sony_input_configured(struct err_close:hid_hw_close (hdev); err_stop: -sony_cancel_work_sync (sc); -sony_remove_dev_list (sc); -sony_release_device_id (sc); +sony_cleanup (sc);return ret; }
@@ -2065,6 +2069,8 @@ static int sony_probe(struct hid_device }spin_lock_init (&sc->lock); +INIT_LIST_HEAD (&sc->list_node); + sc->device_id =-1 ; sc->quirks = quirks;hid_set_drvdata (hdev, sc);
@@ -2093,6 +2099,7 @@ static int sony_probe(struct hid_device ret =hid_hw_start (hdev, connect_mask);if (ret) {hid_err (hdev,"hw start failed\n" ); +sony_cleanup (sc);return ret; }
@@ -2144,7 +2151,7 @@ static int sony_probe(struct hid_device err:usb_free_urb (sc->ghl_urb); - +sony_cleanup (sc);hid_hw_stop (hdev);return ret; }
@@ -2161,13 +2168,7 @@ static void sony_remove(struct hid_devic }hid_hw_close (hdev); - -sony_cancel_work_sync (sc); - -sony_remove_dev_list (sc); - -sony_release_device_id (sc); - +sony_cleanup (sc);hid_hw_stop (hdev); }
Greg Kroah-Hartman
maintainerPATCH 1057/1102 HID: mcp2221: fix OOB write in mcp2221_raw_event()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Pradines <[email protected]>
[ Upstream commit f097d246677b03db814c5862f368cea341b76a00 ]
mcp2221_raw_event() copies device-supplied data into mcp->rxbuf at offset rxbuf_idx without checking that the copy fits within the destination buffer. A device responding with up to 60 bytes to a small I2C/SMBus read can overflow the buffer.
Add a rxbuf_size field to struct mcp2221, set it alongside rxbuf in mcp_i2c_smbus_read(), and check rxbuf_idx + data[3] <= rxbuf_size before the memcpy.
Stable-dep-of: db2333f88729 ("HID: mcp2221: clear rxbuf after I2C/SMBus transfer completes")
--- drivers/hid/hid-mcp2221.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/hid/hid-mcp2221.c +7 -0
@@ -99,6 +99,7 @@ struct mcp2221 {u8 *rxbuf;u8 txbuf[64 ];int rxbuf_idx; +int rxbuf_size;int status;u8 cur_i2c_clk_div;struct gpio_chip *gc;
@@ -307,12 +308,14 @@ static int mcp_i2c_smbus_read(struct mcp mcp->txbuf[3 ] = (u8)(msg->addr <<1 ); total_len = msg->len; mcp->rxbuf = msg->buf; + mcp->rxbuf_size = msg->len; }else { mcp->txbuf[1 ] = smbus_len; mcp->txbuf[2 ] =0 ; mcp->txbuf[3 ] = (u8)(smbus_addr <<1 ); total_len = smbus_len; mcp->rxbuf = smbus_buf; + mcp->rxbuf_size = smbus_len; } ret =mcp_send_data_req_status (mcp, mcp->txbuf,4 );
@@ -823,6 +826,10 @@ static int mcp2221_raw_event(struct hid_ mcp->status = -EINVAL ;break ; } +if (mcp->rxbuf_idx + data[3 ] > mcp->rxbuf_size) { + mcp->status = -EINVAL ; +break ; + }if (4 + data[3 ] > size) { mcp->status = -EINVAL ;break ;
Greg Kroah-Hartman
maintainerPATCH 1058/1102 HID: mcp2221: clear rxbuf after I2C/SMBus transfer completes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
[ Upstream commit db2333f88729c8aae062cb171ed058725ff5c901 ]
mcp_i2c_smbus_read() stores the caller-supplied buffer pointer in mcp->rxbuf for the duration of a transfer but never clears it when the transfer finishes or times out. Once the caller frees or reuses the buffer, mcp->rxbuf becomes a dangling pointer. A delayed or spurious MCP2221_I2C_GET_DATA report can then drive mcp2221_raw_event() to memcpy device data into the freed memory, causing a write use-after-free.
Route all return paths through a single exit point that clears mcp->rxbuf and mcp->rxbuf_size, so that the existing !mcp->rxbuf guard in the raw_event handler can reject any report arriving after the transfer has ended.
--- drivers/hid/hid-mcp2221.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/hid/hid-mcp2221.c +6 -2
@@ -320,7 +320,7 @@ static int mcp_i2c_smbus_read(struct mcp ret =mcp_send_data_req_status (mcp, mcp->txbuf,4 );if (ret) -return ret; + goto out; mcp->rxbuf_idx =0 ;
@@ -342,7 +342,7 @@ static int mcp_i2c_smbus_read(struct mcp }else {usleep_range (980 ,1000 );mcp_cancel_last_cmd (mcp); -return ret; + goto out; } }else { retries =0 ;
@@ -352,6 +352,10 @@ static int mcp_i2c_smbus_read(struct mcpusleep_range (980 ,1000 ); ret =mcp_chk_last_cmd_status_free_bus (mcp); +out: + mcp->rxbuf =NULL ; + mcp->rxbuf_size =0 ; +return ret; }
Greg Kroah-Hartman
maintainerPATCH 1059/1102 NFSD: Consolidate the revocation-path client unpin
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 3308cf3f11ed23c79f9f3f90b34bbbad3e3a6ea9 ]
The client use-after-free fixes in the state-revocation paths left four open-coded copies of one idiom: drop a cl_rpc_users pin without renewing the client's lease, waking force_expire_client() when the last pin drops on a client it is tearing down. The accompanying "do not renew" rationale was documented at only one of the four sites.
put_client_renew_locked() and put_client_renew() already carry the same pin-drop logic, but they renew a non-expired client's lease and so would resurrect the client whose state is being revoked. Factor the common pin-drop into __put_client_locked(), parameterized by whether to renew. The renew helpers pass true; the new put_client_no_renew_locked() and put_client_no_renew() pass false and carry the revocation paths, which must not revive the client they are tearing down. No change in behavior.
Stable-dep-of: 9026932ac8be ("NFSD: Prevent client use-after-free during blocked-lock reaping")
--- fs/nfsd/nfs4state.c | 65 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 25 deletions(-)
fs/nfsd/nfs4state.c +40 -25
@@ -198,18 +198,28 @@ renew_client_locked(struct nfs4_client * clp->cl_state =NFSD4_ACTIVE ; } +/* +* Finish a cl_rpc_users unpin with the client_lock held. A +* revocation walk clears @renew so the client whose state it is +* revoking is not revived; every other caller renews the lease of +* a still-active client. +*/ +static void __put_client_locked (struct nfs4_client *clp,bool renew) +{ +if (is_client_expired (clp)) +wake_up_all (&expiry_wq); +else if (renew) +renew_client_locked (clp); +} +static void put_client_renew_locked (struct nfs4_client *clp) {struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id);lockdep_assert_held (&nn->client_lock); -if (!atomic_dec_and_test (&clp->cl_rpc_users)) -return ; -if (!is_client_expired (clp)) -renew_client_locked (clp); -else -wake_up_all (&expiry_wq); +if (atomic_dec_and_test (&clp->cl_rpc_users)) +__put_client_locked (clp, true); }static void put_client_renew (struct nfs4_client *clp)
@@ -218,10 +228,27 @@ static void put_client_renew(struct nfs4if (!atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock))return ; -if (!is_client_expired (clp)) -renew_client_locked (clp); -else -wake_up_all (&expiry_wq); +__put_client_locked (clp, true); +spin_unlock (&nn->client_lock); +} + +static void put_client_no_renew_locked (struct nfs4_client *clp) +{ +struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); + +lockdep_assert_held (&nn->client_lock); + +if (atomic_dec_and_test (&clp->cl_rpc_users)) +__put_client_locked (clp, false); +} + +static void put_client_no_renew (struct nfs4_client *clp) +{ +struct nfsd_net *nn =net_generic (clp->net, nfsd_net_id); + +if (!atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) +return ; +__put_client_locked (clp, false);spin_unlock (&nn->client_lock); }
@@ -1937,9 +1964,7 @@ void nfsd4_revoke_states(struct nfsd_net*/ nn->nfs40_last_revoke =ktime_get_boottime_seconds (); -if (atomic_dec_and_test (&clp->cl_rpc_users) && -is_client_expired (clp)) -wake_up_all (&expiry_wq); +put_client_no_renew_locked (clp); goto retry; } }
@@ -6760,9 +6785,7 @@ retry:nfsd4_drop_revoked_stid (stid);nfs4_put_stid (stid);spin_lock (&nn->client_lock); -if (atomic_dec_and_test (&clp->cl_rpc_users) && -is_client_expired (clp)) -wake_up_all (&expiry_wq); +put_client_no_renew_locked (clp); goto retry; }spin_unlock (&clp->cl_lock);
@@ -6834,15 +6857,7 @@ nfs4_laundromat(struct nfsd_net *nn) clp = dp->dl_stid.sc_client;list_del_init (&dp->dl_recall_lru);revoke_delegation (dp); -/* -* Unpin without renewing: put_client_renew() would -* renew the reaped client's lease. -*/ -if (atomic_dec_and_lock (&clp->cl_rpc_users, &nn->client_lock)) { -if (is_client_expired (clp)) -wake_up_all (&expiry_wq); -spin_unlock (&nn->client_lock); - } +put_client_no_renew (clp); }spin_lock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 1060/1102 NFSD: Prevent client use-after-free during blocked-lock reaping
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 9026932ac8be4d0ae01db47f23619a98cc57b671 ]
A bare lock owner -- its only remaining reference a blocked lock on nn->blocked_locks_lru -- holds a raw pointer to its nfs4_client but no reference keeping the client alive. When the per-net laundromat reaps such a lock, freeing the nbl drops the owner reference held through flc_owner, and the final nfs4_put_stateowner() takes the client's cl_lock. Because the laundromat detaches the nbl first, __destroy_client() no longer finds it, so a concurrent force_expire_client() can free the client before nfs4_put_stateowner() runs, dereferencing cl_lock in freed memory.
Pin the client with cl_rpc_users before dropping nn->blocked_locks_lock, and skip clients already expiring, whose blocked locks __destroy_client() frees while holding an owner reference. Take nn->client_lock outside nn->blocked_locks_lock. Every other site holds nn->blocked_locks_lock as a leaf, acquiring no further lock, so placing nn->client_lock outside it cannot form a lock-order cycle.
--- fs/nfsd/nfs4state.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-)
fs/nfsd/nfs4state.c +20 -3
@@ -349,6 +349,16 @@ free_blocked_lock(struct nfsd4_blocked_lkref_put (&nbl->nbl_kref, free_nbl); } +/* A blocked lock's flc_owner is its nfs4_lockowner. */ +static struct nfs4_client * +nbl_client (struct nfsd4_blocked_lock *nbl) +{ +struct nfs4_lockowner *lo; + + lo = (struct nfs4_lockowner *)nbl->nbl_lock.c.flc_owner; +return lo->lo_owner.so_client; +} +static void remove_blocked_locks (struct nfs4_lockowner *lo) {
@@ -6887,22 +6897,29 @@ nfs4_laundromat(struct nfsd_net *nn)* indefinitely once the lock does become free. */ BUG_ON (!list_empty (&reaplist)); +spin_lock (&nn->client_lock);spin_lock (&nn->blocked_locks_lock); -while (!list_empty (&nn->blocked_locks_lru)) { - nbl =list_first_entry (&nn->blocked_locks_lru, - struct nfsd4_blocked_lock, nbl_lru); +list_for_each_safe (pos, next, &nn->blocked_locks_lru) { + nbl = list_entry(pos, struct nfsd4_blocked_lock, nbl_lru);if (!state_expired (<, nbl->nbl_time))break ; + clp =nbl_client (nbl); +if (is_client_expired (clp)) +continue ; +atomic_inc (&clp->cl_rpc_users);list_move (&nbl->nbl_lru, &reaplist);list_del_init (&nbl->nbl_list); }spin_unlock (&nn->blocked_locks_lock); +spin_unlock (&nn->client_lock);while (!list_empty (&reaplist)) { nbl =list_first_entry (&reaplist, struct nfsd4_blocked_lock, nbl_lru); + clp =nbl_client (nbl);list_del_init (&nbl->nbl_lru);free_blocked_lock (nbl); +put_client_no_renew (clp); }#ifdef CONFIG_NFSD_V4_2_INTER_SSC /* service the server-to-server copy delayed unmount list */
Greg Kroah-Hartman
maintainerPATCH 1061/1102 NFSD: Prevent client use-after-free during close_lru reaping
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 2330b788d732f43668b965b3105b37ceb276dfea ]
An nfs4_openowner left on nn->close_lru after its final CLOSE keeps its last closed stateid in oo_last_closed_stid, holding only a raw pointer to its nfs4_client. The laundromat reaps timed-out entries, drops nn->client_lock, and calls nfs4_put_stid(), which dereferences the client through cl_lock. Nothing pins the client across that window, so a concurrent force_expire_client() can free it and nfs4_put_stid() reads freed memory. __destroy_client() hits the same race, walking clp->cl_openowners without cl_lock.
Pin the client with cl_rpc_users before dropping client_lock, and skip clients already expiring. __destroy_client() then cleans up its own close_lru entries through release_last_closed_stateid(), so teardown no longer races the laundromat.
--- fs/nfsd/nfs4state.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/nfsd/nfs4state.c +5 -0
@@ -6877,11 +6877,16 @@ nfs4_laundromat(struct nfsd_net *nn)if (!state_expired (<, oo->oo_time))break ;list_del_init (&oo->oo_close_lru); + clp = oo->oo_owner.so_client; +if (is_client_expired (clp)) +continue ; stp = oo->oo_last_closed_stid; oo->oo_last_closed_stid =NULL ; +atomic_inc (&clp->cl_rpc_users);spin_unlock (&nn->client_lock);nfs4_put_stid (&stp->st_stid);spin_lock (&nn->client_lock); +put_client_no_renew_locked (clp); }spin_unlock (&nn->client_lock);
Greg Kroah-Hartman
maintainerPATCH 1062/1102 erofs: skip sufficiently large global buffers when resizing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil Gurudasani <[email protected]>
[ Upstream commit a7d097cf01301c5da37927c8f26123d006f0fd8a ]
z_erofs_gbuf_nrpages is advanced only after every global buffer has been grown. If a resize fails after some buffers were enlarged, a retry revisits those enlarged buffers.
Retrying the same size then returns -ENOMEM because alloc_pages_bulk() has no pages to add and the unchanged return value is treated as a failure. Retrying an intermediate size allocates a temporary pointer array smaller than gbuf->nrpages and copies more existing pointers than the array can hold.
Skip buffers that already satisfy the request. Once all remaining buffers have caught up, advancing z_erofs_gbuf_nrpages again describes the guaranteed minimum size across the pool.
--- fs/erofs/zutil.c | 2 ++ 1 file changed, 2 insertions(+)
fs/erofs/zutil.c +2 -0
@@ -79,6 +79,8 @@ int z_erofs_gbuf_growsize(unsigned int nfor (i =0 ; i < z_erofs_gbuf_count; ++i) { gbuf = &z_erofs_gbufpool[i]; +if (gbuf->nrpages >= nrpages) +continue ; tmp_pages =kcalloc (nrpages,sizeof (*tmp_pages),GFP_KERNEL );if (!tmp_pages) goto out;
Greg Kroah-Hartman
maintainerPATCH 1063/1102 efi/cper, cxl: Prefix protocol error struct and function names with cxl_
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Smita Koralahalli <[email protected]>
[ Upstream commit 84973331442a57bac31b40eaef6f264496c6f3fd ]
Rename the protocol error struct from struct cper_sec_prot_err to struct cxl_cper_sec_prot_err and cper_print_prot_err() to cxl_cper_print_prot_err() to maintain naming consistency. No functional changes.
Stable-dep-of: 6625ca499c31 ("acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks")
--- drivers/firmware/efi/cper.c | 4 ++-- drivers/firmware/efi/cper_cxl.c | 3 ++- drivers/firmware/efi/cper_cxl.h | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-)
drivers/firmware/efi/cper.c +2 -2
@@ -690,11 +690,11 @@ cper_estatus_print_section(const char *pelse goto err_section_too_small; }else if (guid_equal (sec_type, &CPER_SEC_CXL_PROT_ERR )) { -struct cper_sec_prot_err *prot_err =acpi_hest_get_payload (gdata); +struct cxl_cper_sec_prot_err *prot_err =acpi_hest_get_payload (gdata);printk ("%ssection_type: CXL Protocol Error\n" , newpfx);if (gdata->error_data_length >=sizeof (*prot_err)) -cper_print_prot_err (newpfx, prot_err); +cxl_cper_print_prot_err (newpfx, prot_err);else goto err_section_too_small; }else {
drivers/firmware/efi/cper_cxl.c +2 -1
@@ -55,7 +55,8 @@ enum { USP, /* CXL Upstream Switch Port */ }; -void cper_print_prot_err (const char *pfx,const struct cper_sec_prot_err *prot_err) +void cxl_cper_print_prot_err (const char *pfx, +const struct cxl_cper_sec_prot_err *prot_err) {if (prot_err->valid_bits &PROT_ERR_VALID_AGENT_TYPE ) pr_info("%s agent_type: %d, %s\n", pfx, prot_err->agent_type,
drivers/firmware/efi/cper_cxl.h +3 -2
@@ -18,7 +18,7 @@#pragma pack(1)/* Compute Express Link Protocol Error Section, UEFI v2.10 sec N.2.13 */ -struct cper_sec_prot_err { +struct cxl_cper_sec_prot_err {u64 valid_bits;u8 agent_type;u8 reserved[7 ];
@@ -61,6 +61,7 @@ struct cper_sec_prot_err {#pragma pack() -void cper_print_prot_err (const char *pfx,const struct cper_sec_prot_err *prot_err); +void cxl_cper_print_prot_err (const char *pfx, +const struct cxl_cper_sec_prot_err *prot_err);#endif //__CPER_CXL_
Greg Kroah-Hartman
maintainerPATCH 1064/1102 efi/cper, cxl: Make definitions and structures global
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Smita Koralahalli <[email protected]>
[ Upstream commit 958c3a6706863f9f77b21c90d2428473441cd8a1 ]
In preparation to add tracepoint support, move protocol error UUID definition to a common location, Also, make struct CXL RAS capability, cxl_cper_sec_prot_err and CPER validation flags global for use across different modules.
Stable-dep-of: 6625ca499c31 ("acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks")
--- drivers/firmware/efi/cper.c | 1 drivers/firmware/efi/cper_cxl.c | 35 ----------------- drivers/firmware/efi/cper_cxl.h | 51 ------------------------- include/cxl/event.h | 80 ++++++++++++++++++++++++++++++++++++++++ include/linux/cper.h | 4 ++ 5 files changed, 86 insertions(+), 85 deletions(-)
drivers/firmware/efi/cper.c +1 -0
@@ -25,6 +25,7 @@#include <linux/bcd.h> #include <acpi/ghes.h> #include <ras/ras_event.h> +#include <cxl/event.h> #include "cper_cxl.h" /*
drivers/firmware/efi/cper_cxl.c +1 -34
@@ -8,27 +8,9 @@*/ #include <linux/cper.h> +#include <cxl/event.h> #include "cper_cxl.h" -#define PROT_ERR_VALID_AGENT_TYPE BIT_ULL(0) -#define PROT_ERR_VALID_AGENT_ADDRESS BIT_ULL(1) -#define PROT_ERR_VALID_DEVICE_ID BIT_ULL(2) -#define PROT_ERR_VALID_SERIAL_NUMBER BIT_ULL(3) -#define PROT_ERR_VALID_CAPABILITY BIT_ULL(4) -#define PROT_ERR_VALID_DVSEC BIT_ULL(5) -#define PROT_ERR_VALID_ERROR_LOG BIT_ULL(6) - -/* CXL RAS Capability Structure, CXL v3.0 sec 8.2.4.16 */ -struct cxl_ras_capability_regs { -u32 uncor_status; -u32 uncor_mask; -u32 uncor_severity; -u32 cor_status; -u32 cor_mask; -u32 cap_control; -u32 header_log[16 ]; -}; -static const char *const prot_err_agent_type_strs[] = {"Restricted CXL Device" ,"Restricted CXL Host Downstream Port" ,
@@ -40,21 +22,6 @@ static const char * const prot_err_agent "CXL Upstream Switch Port", }; -/* -* The layout of the enumeration and the values matches CXL Agent Type -* field in the UEFI 2.10 Section N.2.13, -*/ -enum { -RCD ,/* Restricted CXL Device */ -RCH_DP ,/* Restricted CXL Host Downstream Port */ -DEVICE ,/* CXL Device */ -LD ,/* CXL Logical Device */ -FMLD ,/* CXL Fabric Manager managed Logical Device */ -RP ,/* CXL Root Port */ -DSP ,/* CXL Downstream Switch Port */ -USP ,/* CXL Upstream Switch Port */ -}; -void cxl_cper_print_prot_err (const char *pfx,const struct cxl_cper_sec_prot_err *prot_err) {
drivers/firmware/efi/cper_cxl.h +0 -51
57 lines not on this page · raw message
include/cxl/event.h +80 -0
86 lines not on this page · raw message
include/linux/cper.h +4 -0
@@ -89,6 +89,10 @@ enum {#define CPER_NOTIFY_DMAR \ GUID_INIT(0x667DD791, 0xC6B3, 0x4c27, 0x8A, 0x6B, 0x0F, 0x8E, \ 0x72, 0x2D, 0xEB, 0x41) +/* CXL Protocol Error Section */ +#define CPER_SEC_CXL_PROT_ERR \ + GUID_INIT(0x80B9EFB4, 0x52B5, 0x4DE3, 0xA7, 0x77, 0x68, 0x78, \ + 0x4B, 0x77, 0x10, 0x48)/* CXL Event record UUIDs are formatted as GUIDs and reported in section type */ /*
Greg Kroah-Hartman
maintainerPATCH 1065/1102 acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Terry Bowman <[email protected]>
[ Upstream commit 6625ca499c3131ef63be3215f8f942d7a097ea3a ]
The CXL CPER work registration and unregistration helpers acquire cxl_cper_work_lock and cxl_cper_prot_err_work_lock with a spinlock guard(), which leaves local interrupts enabled. The corresponding post paths (cxl_cper_post_event(), cxl_cper_post_prot_err()) execute in hard IRQ context (they are called from the GHES error notification path) and acquire the same locks with an irqsave guard().
If a CPU is holding one of these locks via a spinlock guard() when a GHES interrupt arrives on the same CPU, the IRQ handler spins on the held lock waiting for it to release, while the lock holder is preempted by the IRQ. The result is a deadlock.
Convert both locks from spinlock_t to raw_spinlock_t and use guard() at all call sites. On PREEMPT_RT kernels spinlock_t is backed by rt_mutex and sleeping from hard IRQ context is not permitted; raw_spinlock_t is safe in both contexts.
Add WARN_ONCE to both register functions to surface double-registration bugs at runtime.
Restructure both unregister functions to clear the global work pointer under the lock before calling cancel_work_sync(), closing the window where a CPER interrupt could schedule work on a pointer about to be freed. Add kfifo_reset() after cancel_work_sync() so stale entries are not replayed on next module load.
Both kfifos are single-consumer: only one work_struct is registered at a time, enforced by the WARN_ONCE guard in the register functions. kfifo_reset() is safe outside the lock because cancel_work_sync() has already quiesced the consumer, and no new consumer can register until the current module exit completes and a fresh module init runs.
Remove the redundant cancel_work_sync() call from cxl_ras_exit() and cxl_pci_driver_exit(). The CPER unregister functions now quiesce the work internally.
--- drivers/acpi/apei/ghes.c | 24 ++++++++++++++++-------- drivers/cxl/pci.c | 1 - 2 files changed, 16 insertions(+), 9 deletions(-)
drivers/acpi/apei/ghes.c +16 -8
@@ -710,7 +710,7 @@ static void ghes_defer_non_standard_even DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);/* Synchronize schedule_work() with cxl_cper_work changes */ -static DEFINE_SPINLOCK (cxl_cper_work_lock); +static DEFINE_RAW_SPINLOCK (cxl_cper_work_lock);struct work_struct *cxl_cper_work; static void cxl_cper_post_event(enum cxl_event_type event_type,
@@ -730,7 +730,7 @@ static void cxl_cper_post_event(enum cxlreturn ; } -guard (spinlock_irqsave)(&cxl_cper_work_lock); +guard (raw_spinlock_irqsave)(&cxl_cper_work_lock);if (!cxl_cper_work)return ;
@@ -748,10 +748,11 @@ static void cxl_cper_post_event(enum cxlint cxl_cper_register_work (struct work_struct *work) { -if (cxl_cper_work) +guard (raw_spinlock_irqsave)(&cxl_cper_work_lock); +if (WARN_ONCE (cxl_cper_work, +"CXL CPER kfifo consumer already registered\n" ))return -EINVAL ; -guard (spinlock)(&cxl_cper_work_lock); cxl_cper_work = work;return 0 ; }
@@ -759,11 +760,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_wint cxl_cper_unregister_work (struct work_struct *work) { -if (cxl_cper_work != work) -return -EINVAL ; + scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) { +if (WARN_ONCE (cxl_cper_work != work, +"CXL CPER kfifo consumer mismatch on unregister\n" )) +return -EINVAL ; + cxl_cper_work =NULL ; + } + +cancel_work_sync (work); + +/* Discard stale entries so they are not replayed on next module load */ +kfifo_reset (&cxl_cper_fifo); -guard (spinlock)(&cxl_cper_work_lock); - cxl_cper_work =NULL ;return 0 ; }EXPORT_SYMBOL_NS_GPL (cxl_cper_unregister_work,CXL );
drivers/cxl/pci.c +0 -1
@@ -1084,7 +1084,6 @@ static int __init cxl_pci_driver_init(vo static void __exit cxl_pci_driver_exit(void) {cxl_cper_unregister_work (&cxl_cper_work); -cancel_work_sync (&cxl_cper_work);pci_unregister_driver (&cxl_pci_driver); }
Greg Kroah-Hartman
maintainerPATCH 1066/1102 cpufreq: apple-soc: Fix OPP table cleanup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit d87cb889dc7ab1f2deecadf2a5e9023184bd7900 ]
apple_soc_cpufreq_init() adds OPP tables from firmware, but some failure paths do not remove them. The driver also uses dev_pm_opp_remove_all_dynamic(), which is not the right cleanup helper for OPP tables loaded from firmware.
Use the cpumask OPP helper after the policy CPU mask has been populated. Pair it with the matching cpumask remove helper on failure paths and in apple_soc_cpufreq_exit(). This also removes the separate dev_pm_opp_set_sharing_cpus() call, as the cpumask helper loads the DT OPP tables for all CPUs in the policy.
--- drivers/cpufreq/apple-soc-cpufreq.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-)
drivers/cpufreq/apple-soc-cpufreq.c +14 -22
@@ -223,21 +223,19 @@ static int apple_soc_cpufreq_init(structreturn -ENODEV ; } - ret =dev_pm_opp_of_add_table (cpu_dev); -if (ret <0 ) { -dev_err (cpu_dev,"%s: failed to add OPP table: %d\n" , __func__, ret); -return ret; - } + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; ret =apple_soc_cpufreq_find_cluster (policy, ®_base, &info);if (ret) {dev_err (cpu_dev,"%s: failed to get cluster info: %d\n" , __func__, ret); -return ret; + goto out_free_priv; } - ret =dev_pm_opp_set_sharing_cpus (cpu_dev, policy->cpus); -if (ret) { -dev_err (cpu_dev,"%s: failed to mark OPPs as shared: %d\n" , __func__, ret); + ret =dev_pm_opp_of_cpumask_add_table (policy->cpus); +if (ret <0 ) { +dev_err (cpu_dev,"%s: failed to add OPP table: %d\n" , __func__, ret); goto out_iounmap; }
@@ -245,19 +243,13 @@ static int apple_soc_cpufreq_init(structif (ret <=0 ) {dev_dbg (cpu_dev,"OPP table is not ready, deferring probe\n" ); ret = -EPROBE_DEFER ; - goto out_free_opp; - } - - priv =kzalloc (sizeof (*priv),GFP_KERNEL ); -if (!priv) { - ret = -ENOMEM ; - goto out_free_opp; + goto out_free_table; } ret =dev_pm_opp_init_cpufreq_table (cpu_dev, &freq_table);if (ret) {dev_err (cpu_dev,"failed to init cpufreq table: %d\n" , ret); - goto out_free_priv; + goto out_free_table; }/* Get OPP levels (p-state indexes) and stash them in driver_data */
@@ -302,12 +294,12 @@ static int apple_soc_cpufreq_init(struct out_free_cpufreq_table:dev_pm_opp_free_cpufreq_table (cpu_dev, &freq_table); -out_free_priv: -kfree (priv); -out_free_opp: -dev_pm_opp_remove_all_dynamic (cpu_dev); +out_free_table: +dev_pm_opp_of_cpumask_remove_table (policy->cpus); out_iounmap:iounmap (reg_base); +out_free_priv: +kfree (priv);return ret; }
@@ -316,7 +308,7 @@ static void apple_soc_cpufreq_exit(strucstruct apple_cpu_priv *priv = policy->driver_data;dev_pm_opp_free_cpufreq_table (priv->cpu_dev, &policy->freq_table); -dev_pm_opp_remove_all_dynamic (priv->cpu_dev); +dev_pm_opp_of_cpumask_remove_table (policy->cpus);iounmap (priv->reg_base);kfree (priv); }
Greg Kroah-Hartman
maintainerPATCH 1067/1102 bpf: Factor stackid_init function from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 15b837759a97237d647962f9943afe0d55af615a ]
The new stackid_init function stores all the necessary bits for stackid trace and it will be used by other functions in following changes.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 95 +++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 36 deletions(-)
kernel/bpf/stackmap.c +59 -36
@@ -246,33 +246,54 @@ get_callchain_entry_for_task(struct task#endif } -static long __bpf_get_stackid (struct bpf_map *map, -struct perf_callchain_entry *trace,u64 flags) +struct stackid { +struct stack_map_bucket *bucket; +u64 *ips; +u32 nr; +u32 len; +u32 hash; +u32 id; +}; + +static int stackid_init (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); -struct stack_map_bucket *bucket, *new_bucket, *old_bucket; -u32 hash, id, trace_nr, trace_len, i, max_depth;u32 skip = flags &BPF_F_SKIP_FIELD_MASK ; -bool user = flags &BPF_F_USER_STACK ; -u64 *ips; -bool hash_matches; +u32 max_depth;if (trace->nr <= skip)/* skipping more than usable stack trace */ return -EFAULT ; max_depth =stack_map_calculate_max_depth (map->value_size,stack_map_data_size (map), flags); - trace_nr =min_t (u32, trace->nr - skip, max_depth - skip); - trace_len = trace_nr *sizeof (u64); - ips = trace->ip + skip; - hash =jhash2 ((u32 *)ips, trace_len /sizeof (u32),0 ); - id = hash & (smap->n_buckets -1 ); - bucket =READ_ONCE (smap->buckets[id]); + stackid->nr =min_t (u32, trace->nr - skip, max_depth - skip); + stackid->len = stackid->nr *sizeof (u64); + stackid->ips = trace->ip + skip; + stackid->hash =jhash2 ((u32 *)stackid->ips, stackid->len /sizeof (u32),0 ); + stackid->id = stackid->hash & (smap->n_buckets -1 ); + stackid->bucket =READ_ONCE (smap->buckets[stackid->id]); +return 0 ; +} + +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); +struct stack_map_bucket *new_bucket, *old_bucket; +bool user = flags &BPF_F_USER_STACK ; +bool hash_matches; +u32 trace_len, i; +int err; + + err =stackid_init (stackid, map, trace, flags); +if (err) +return err; - hash_matches = bucket && bucket->hash == hash; + hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash;/* fast cmp */ if (hash_matches && flags &BPF_F_FAST_STACK_CMP ) -return id; +return stackid->id;if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -282,42 +303,42 @@ static long __bpf_get_stackid(struct bpfpcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))return -ENOMEM ; - new_bucket->nr = trace_nr; + new_bucket->nr = stackid->nr; id_offs = (struct bpf_stack_build_id *)new_bucket->data; -for (i =0 ; i < trace_nr; i++) - id_offs[i].ip = ips[i]; -stack_map_get_build_id_offset (id_offs, trace_nr, user, false/* !may_fault */ ); - trace_len = trace_nr *sizeof (struct bpf_stack_build_id ); -if (hash_matches && bucket->nr == trace_nr && -memcmp (bucket->data, new_bucket->data, trace_len) ==0 ) { +for (i =0 ; i < stackid->nr; i++) + id_offs[i].ip = stackid->ips[i]; +stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); + trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); +if (hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, new_bucket->data, trace_len) ==0 ) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode); -return id; +return stackid->id; } -if (bucket && !(flags &BPF_F_REUSE_STACKID )) { +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return -EEXIST ; } }else { -if (hash_matches && bucket->nr == trace_nr && -memcmp (bucket->data, ips, trace_len) ==0 ) -return id; -if (bucket && !(flags &BPF_F_REUSE_STACKID )) +if (hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) +return stackid->id; +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID ))return -EEXIST ; new_bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))return -ENOMEM ; -memcpy (new_bucket->data, ips, trace_len); +memcpy (new_bucket->data, stackid->ips, stackid->len); } - new_bucket->hash = hash; - new_bucket->nr = trace_nr; + new_bucket->hash = stackid->hash; + new_bucket->nr = stackid->nr; - old_bucket =xchg (&smap->buckets[id], new_bucket); + old_bucket =xchg (&smap->buckets[stackid->id], new_bucket);if (old_bucket)pcpu_freelist_push (&smap->freelist, &old_bucket->fnode); -return id; +return stackid->id; } BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
@@ -326,6 +347,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_reu32 elem_size =stack_map_data_size (map);bool user = flags &BPF_F_USER_STACK ;struct perf_callchain_entry *trace; +struct stackid stackid;bool kernel = !user;u32 max_depth;
@@ -341,7 +363,7 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re/* couldn't fetch the stack trace */ return -EFAULT ; -return __bpf_get_stackid (map, trace, flags); +return __bpf_get_stackid (&stackid, map, trace, flags); }const struct bpf_func_proto bpf_get_stackid_proto = {
@@ -370,6 +392,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bp {struct perf_event *event = ctx->event;struct perf_callchain_entry *trace; +struct stackid stackid;bool kernel, user;__u64 nr_kernel;int ret;
@@ -395,7 +418,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpif (kernel) { trace->nr = nr_kernel; - ret =__bpf_get_stackid (map, trace, flags); + ret =__bpf_get_stackid (&stackid, map, trace, flags); }else {/* user */ u64 skip = flags &BPF_F_SKIP_FIELD_MASK ;
@@ -404,7 +427,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpreturn -EFAULT ; flags = (flags & ~BPF_F_SKIP_FIELD_MASK ) | skip; - ret =__bpf_get_stackid (map, trace, flags); + ret =__bpf_get_stackid (&stackid, map, trace, flags); }/* restore nr */
Greg Kroah-Hartman
maintainerPATCH 1068/1102 bpf: Factor stackid_fastpath function from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 0ca56befcffec3a6c9d1842eae06c74e1cf41f11 ]
The new stackid_fastpath does the fast stack hash and trace check, that does not need new bucket allocation. It covers both just-ip and buildid code paths.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-)
kernel/bpf/stackmap.c +28 -15
@@ -253,6 +253,7 @@ struct stackid {u32 len;u32 hash;u32 id; +bool hash_matches; }; static int stackid_init(struct stackid *stackid, struct bpf_map *map,
@@ -273,28 +274,46 @@ static int stackid_init(struct stackid * stackid->hash =jhash2 ((u32 *)stackid->ips, stackid->len /sizeof (u32),0 ); stackid->id = stackid->hash & (smap->n_buckets -1 ); stackid->bucket =READ_ONCE (smap->buckets[stackid->id]); + stackid->hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash;return 0 ; } +static int stackid_fastpath (struct stackid *stackid,struct bpf_map *map, +struct perf_callchain_entry *trace,u64 flags) +{ +int err; + + err =stackid_init (stackid, map, trace, flags); +if (err) +return err; + +/* fast cmp */ +if (stackid->hash_matches && flags &BPF_F_FAST_STACK_CMP ) +return stackid->id; + +if (stack_map_use_build_id (map)) +return -ENOENT ; +if (stackid->hash_matches && stackid->bucket->nr == stackid->nr && +memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) +return stackid->id; +if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) +return -EEXIST ; +return -ENOENT ; +} +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map,struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; -bool hash_matches;u32 trace_len, i;int err; - err =stackid_init (stackid, map, trace, flags); -if (err) + err =stackid_fastpath (stackid, map, trace, flags); +if (err != -ENOENT )return err; - hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash; -/* fast cmp */ -if (hash_matches && flags &BPF_F_FAST_STACK_CMP ) -return stackid->id; -if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -309,7 +328,7 @@ static long __bpf_get_stackid(struct sta id_offs[i].ip = stackid->ips[i];stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); -if (hash_matches && stackid->bucket->nr == stackid->nr && +if (stackid->hash_matches && stackid->bucket->nr == stackid->nr &&memcmp (stackid->bucket->data, new_bucket->data, trace_len) ==0 ) {pcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return stackid->id;
@@ -319,12 +338,6 @@ static long __bpf_get_stackid(struct stareturn -EEXIST ; } }else { -if (hash_matches && stackid->bucket->nr == stackid->nr && -memcmp (stackid->bucket->data, stackid->ips, stackid->len) ==0 ) -return stackid->id; -if (stackid->bucket && !(flags &BPF_F_REUSE_STACKID )) -return -EEXIST ; - new_bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))
Greg Kroah-Hartman
maintainerPATCH 1069/1102 bpf: Factor stackid_new_bucket from __bpf_get_stackid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit bb4e6f4e1b68fe60c04ca04c564c6624e837dbf4 ]
The new stackid_new_bucket allocates the new bucket and initializes it with the trace data.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-)
kernel/bpf/stackmap.c +30 -18
@@ -301,31 +301,52 @@ static int stackid_fastpath(struct stackreturn -ENOENT ; } +static struct stack_map_bucket * +stackid_new_bucket (struct stackid *stackid,struct bpf_map *map) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); +struct bpf_stack_build_id *id_offs; +struct stack_map_bucket *bucket; +u32 i; + + bucket = (struct stack_map_bucket *)pcpu_freelist_pop (&smap->freelist); +if (unlikely (!bucket)) +return NULL ; + +if (stack_map_use_build_id (map)) { + id_offs = (struct bpf_stack_build_id *)bucket->data; +for (i =0 ; i < stackid->nr; i++) + id_offs[i].ip = stackid->ips[i]; + }else { +memcpy (bucket->data, stackid->ips, stackid->len); + } + + bucket->hash = stackid->hash; + bucket->nr = stackid->nr; +return bucket; +} +static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map,struct perf_callchain_entry *trace,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map);struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; -u32 trace_len, i; +u32 trace_len;int err; err =stackid_fastpath (stackid, map, trace, flags);if (err != -ENOENT )return err; + new_bucket =stackid_new_bucket (stackid, map); +if (!new_bucket) +return -ENOMEM ; +if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs; -/* for build_id+offset, pop a bucket before slow cmp */ - new_bucket = (struct stack_map_bucket *) -pcpu_freelist_pop (&smap->freelist); -if (unlikely (!new_bucket)) -return -ENOMEM ; - new_bucket->nr = stackid->nr; id_offs = (struct bpf_stack_build_id *)new_bucket->data; -for (i =0 ; i < stackid->nr; i++) - id_offs[i].ip = stackid->ips[i];stack_map_get_build_id_offset (id_offs, stackid->nr, user, false/* !may_fault */ ); trace_len = stackid->nr *sizeof (struct bpf_stack_build_id ); if (stackid->hash_matches && stackid->bucket->nr == stackid->nr &&
@@ -337,17 +358,8 @@ static long __bpf_get_stackid(struct stapcpu_freelist_push (&smap->freelist, &new_bucket->fnode);return -EEXIST ; } - }else { - new_bucket = (struct stack_map_bucket *) -pcpu_freelist_pop (&smap->freelist); -if (unlikely (!new_bucket)) -return -ENOMEM ; -memcpy (new_bucket->data, stackid->ips, stackid->len); } - new_bucket->hash = stackid->hash; - new_bucket->nr = stackid->nr; - old_bucket =xchg (&smap->buckets[stackid->id], new_bucket);if (old_bucket)pcpu_freelist_push (&smap->freelist, &old_bucket->fnode);
Greg Kroah-Hartman
maintainerPATCH 1070/1102 bpf: Use stack id functions instead of __bpf_get_stackid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 09b3fd6caa0b57f8a39254ee5db3af30bdd53c18 ]
Replacing __bpf_get_stackid calls with sequence of following functions:
stackid_fastpath
stackid_new_bucket
stackid_install
This makes code more structured and allows us to easily disable preemption only in bpf_get_stackid in following changes.
Stable-dep-of: 15f1bd857466 ("bpf: Disable preemption in bpf_get_stackid")
--- kernel/bpf/stackmap.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-)
kernel/bpf/stackmap.c +27 -16
@@ -326,22 +326,13 @@ stackid_new_bucket(struct stackid *stackreturn bucket; } -static long __bpf_get_stackid (struct stackid *stackid,struct bpf_map *map, -struct perf_callchain_entry *trace,u64 flags) +static long stackid_install (struct stackid *stackid,struct bpf_map *map, +struct stack_map_bucket *new_bucket,u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); -struct stack_map_bucket *new_bucket, *old_bucket;bool user = flags &BPF_F_USER_STACK ; +struct stack_map_bucket *old_bucket;u32 trace_len; -int err; - - err =stackid_fastpath (stackid, map, trace, flags); -if (err != -ENOENT ) -return err; - - new_bucket =stackid_new_bucket (stackid, map); -if (!new_bucket) -return -ENOMEM ;if (stack_map_use_build_id (map)) {struct bpf_stack_build_id *id_offs;
@@ -371,10 +362,12 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re {u32 elem_size =stack_map_data_size (map);bool user = flags &BPF_F_USER_STACK ; +struct stack_map_bucket *new_bucket;struct perf_callchain_entry *trace;struct stackid stackid;bool kernel = !user;u32 max_depth; +int err;if (unlikely (flags & ~(BPF_F_SKIP_FIELD_MASK |BPF_F_USER_STACK |BPF_F_FAST_STACK_CMP |BPF_F_REUSE_STACKID )))
@@ -388,7 +381,15 @@ BPF_CALL_3(bpf_get_stackid, struct pt_re/* couldn't fetch the stack trace */ return -EFAULT ; -return __bpf_get_stackid (&stackid, map, trace, flags); + err =stackid_fastpath (&stackid, map, trace, flags); +if (err != -ENOENT ) +return err; + + new_bucket =stackid_new_bucket (&stackid, map); +if (!new_bucket) +return -ENOMEM ; + +return stackid_install (&stackid, map, new_bucket, flags); }const struct bpf_func_proto bpf_get_stackid_proto = {
@@ -416,6 +417,7 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bp struct bpf_map *, map, u64, flags) {struct perf_event *event = ctx->event; +struct stack_map_bucket *new_bucket;struct perf_callchain_entry *trace;struct stackid stackid;bool kernel, user;
@@ -443,7 +445,6 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpif (kernel) { trace->nr = nr_kernel; - ret =__bpf_get_stackid (&stackid, map, trace, flags); }else {/* user */ u64 skip = flags &BPF_F_SKIP_FIELD_MASK ;
@@ -452,12 +453,22 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpreturn -EFAULT ; flags = (flags & ~BPF_F_SKIP_FIELD_MASK ) | skip; - ret =__bpf_get_stackid (&stackid, map, trace, flags); } + ret =stackid_fastpath (&stackid, map, trace, flags); +if (ret != -ENOENT ) + goto out; + + new_bucket =stackid_new_bucket (&stackid, map); +if (new_bucket) { + trace->nr = nr; +return stackid_install (&stackid, map, new_bucket, flags); + } + ret = -ENOMEM ; + +out:/* restore nr */ trace->nr = nr; -return ret; }
Greg Kroah-Hartman
maintainerPATCH 1071/1102 bpf: Disable preemption in bpf_get_stackid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit 15f1bd8574662f1b7b26aaa2e23ebf4066f0117d ]
The get_perf_callchain call needs disabled preemption plus we need it disabled as long as we access its returned trace entries buffer.
Note the bpf_get_stackid_pe function is executed already with preemption disabled.
--- kernel/bpf/stackmap.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
kernel/bpf/stackmap.c +13 -11
@@ -374,20 +374,22 @@ BPF_CALL_3(bpf_get_stackid, struct pt_rereturn -EINVAL ; max_depth =stack_map_calculate_max_depth (map->value_size, elem_size, flags); - trace =get_perf_callchain (regs, kernel, user, max_depth, - false, false); -if (unlikely (!trace)) -/* couldn't fetch the stack trace */ -return -EFAULT ; +scoped_guard (preempt) { + trace =get_perf_callchain (regs, kernel, user, max_depth, + false, false); +if (unlikely (!trace)) +/* couldn't fetch the stack trace */ +return -EFAULT ; - err =stackid_fastpath (&stackid, map, trace, flags); -if (err != -ENOENT ) -return err; + err =stackid_fastpath (&stackid, map, trace, flags); +if (err != -ENOENT ) +return err; - new_bucket =stackid_new_bucket (&stackid, map); -if (!new_bucket) -return -ENOMEM ; + new_bucket =stackid_new_bucket (&stackid, map); +if (!new_bucket) +return -ENOMEM ; + }return stackid_install (&stackid, map, new_bucket, flags); }
Greg Kroah-Hartman
maintainerPATCH 1072/1102 ACPI: TAD: Rearrange RT data validation checking
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit 3329a1416c3350449081ca5daaa94802a65b2992 ]
Move RT data validation checks from acpi_tad_set_real_time() to a separate function called acpi_tad_rt_is_invalid() and use it also in acpi_tad_get_real_time() to validate data coming from the platform firmware.
Also make acpi_tad_set_real_time() return -EINVAL when the RT data passed to it is invalid (instead of -ERANGE which is somewhat confusing) and introduce ACPI_TAD_TZ_UNSPEC to represent the "unspecified timezone" value.
Stable-dep-of: a3df8bbe0a70 ("ACPI: TAD: Add locking around AML evaluations")
--- drivers/acpi/acpi_tad.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
drivers/acpi/acpi_tad.c +20 -6
@@ -49,6 +49,9 @@ MODULE_AUTHOR("Rafael J. Wysocki");/* Special value for disabled timer or expired timer wake policy. */ #define ACPI_TAD_WAKE_DISABLED (~(u32)0) +/* ACPI TAD RTC */ +#define ACPI_TAD_TZ_UNSPEC 2047 +struct acpi_tad_driver_data {u32 capabilities; };
@@ -67,6 +70,16 @@ struct acpi_tad_rt {u8 padding[3 ];/* must be 0 */ } __packed; +static bool acpi_tad_rt_is_invalid (struct acpi_tad_rt *rt) +{ +return rt->year <1900 || rt->year >9999 || + rt->month <1 || rt->month >12 || + rt->hour >23 || rt->minute >59 || rt->second >59 || + rt->tz <-1440 || + (rt->tz >1440 && rt->tz !=ACPI_TAD_TZ_UNSPEC ) || + rt->daylight >3 ; +} +static int acpi_tad_set_real_time (struct device *dev,struct acpi_tad_rt *rt) {acpi_handle handle =ACPI_HANDLE (dev);
@@ -80,12 +93,8 @@ static int acpi_tad_set_real_time(structunsigned long long retval;acpi_status status; -if (rt->year <1900 || rt->year >9999 || - rt->month <1 || rt->month >12 || - rt->hour >23 || rt->minute >59 || rt->second >59 || - rt->tz <-1440 || (rt->tz >1440 && rt->tz !=2047 ) || - rt->daylight >3 ) -return -ERANGE ; +if (acpi_tad_rt_is_invalid (rt)) +return -EINVAL ; args[0 ].buffer.pointer = (u8 *)rt; args[0 ].buffer.length =sizeof (*rt);
@@ -132,6 +141,11 @@ static int acpi_tad_get_real_time(struct goto out_free;memcpy (rt, data,sizeof (*rt)); +if (acpi_tad_rt_is_invalid (rt)) { + ret = -ENODATA ; + goto out_free; + } + ret =0 ; out_free: