Re: [PATCH v5 22/36] mm/mempolicy: add MPOL_F_PRIVATE and zonelist selection
[email protected] Tue, 21 Jul 2026 19:28:52 +0000
| Newsgroups | dev.linux.lists.nvdimm,dev.linux.lists.damon,org.kernel.vger.kvm,org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider: New issues: - [High] Private nodes are permanently lost from MPOL_F_STATIC_NODES policies during memory offlining/rebinding. - [High] Mempolicies targeting private nodes will silently fail to allocate from them due to cpuset restrictions. - [Medium] Non-atomic clearing of MPOL_F_PRIVATE in pol->flags causes a data race with lockless page allocators. Pre-existing issues: - [High] Lockless and unprotected access to pol->nodes in alloc_pages_bulk_interleave can lead to a divide-by-zero crash. - [High] Unconditional GFP_KERNEL allocation in alloc_pages_bulk_weighted_interleave violates caller's GFP constraints. -- --- Patch [22]: [PATCH v5 22/36] mm/mempolicy: add MPOL_F_PRIVATE and zonelist selection --- commit 11224b898bbcf008cfe6641a6cd14bd637edb1d1 Author: Gregory Price <[email protected]> mm/mempolicy: add MPOL_F_PRIVATE and zonelist selection This patch adds MPOL_F_PRIVATE to mempolicy flags to identify policies containing private nodes, allowing private node memory to be reachable. It also plumbs zonelist selection into the mempolicy allocator interfaces using the alloc_flags mechanism. > diff --git a/mm/mempolicy.c b/mm/mempolicy.c > index 2b76c57a460c9..90110e9761122 100644 > --- a/mm/mempolicy.c > +++ b/mm/mempolicy.c [ ... ] > @@ -505,22 +509,33 @@ static void mpol_rebind_default(struct mempolicy *pol, const nodemask_t *nodes) > > static void mpol_rebind_nodemask(struct mempolicy *pol, const nodemask_t *nodes) > { > - nodemask_t tmp; > + nodemask_t tmp, priv; > + > + /* preserve online private nodes to re-add later */ > + nodes_and(priv, pol->nodes, node_states[N_MEMORY_PRIVATE]); [Severity: High] Does this evaluate private nodes against pol->nodes instead of pol->w.user_nodemask? If a private node targeted by an MPOL_F_STATIC_NODES policy goes offline, it is removed from pol->nodes. When the node comes back online and mpol_rebind_nodemask() runs, attempting to restore private nodes from pol->nodes will find it empty. Could this result in permanent loss of private nodes from static policies? > if (pol->flags & MPOL_F_STATIC_NODES) > nodes_and(tmp, pol->w.user_nodemask, *nodes); [ ... ] > - if (nodes_empty(tmp)) > + /* private nodes are identity-mapped during remap, drop them here */ > + nodes_and(tmp, tmp, node_states[N_MEMORY]); > + if (nodes_empty(tmp) && nodes_empty(priv)) > tmp = *nodes; > > - pol->nodes = tmp; > + /* If any online private nodes remain, add them back */ > + nodes_or(pol->nodes, tmp, priv); > + /* If no online private nodes remain, strip the private flag */ > + if (nodes_empty(priv)) > + pol->flags &= ~MPOL_F_PRIVATE; [Severity: Medium] Does clearing MPOL_F_PRIVATE non-atomically create a data race with lockless page allocators? When mpol_alloc_flags() concurrently reads (pol->flags & MPOL_F_PRIVATE) locklessly in the page allocator fastpath, could this result in torn reads and trigger KCSAN warnings? > } [ ... ] > @@ -2488,7 +2513,7 @@ static struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order, > } > } > > - page = __alloc_frozen_pages_noprof(gfp, order, nid, nodemask, ALLOC_DEFAULT); > + page = __alloc_frozen_pages_noprof(gfp, order, nid, nodemask, aflags); [Severity: High] For MPOL_BIND policies, will cpuset_nodemask_valid_mems_allowed() return false because private nodes are excluded from cpuset.mems, setting nodemask to NULL? When a NULL nodemask is passed to the allocator, the fastpath defaults to cpuset_current_mems_allowed with ALLOC_CPUSET. Since private nodes are absent from the cpuset, won't __cpuset_zone_allowed() skip the private nodes regardless of ALLOC_ZONELIST_PRIVATE? Will mempolicies targeting private nodes silently fail to allocate from them and fall back to standard nodes? > if (unlikely(pol->mode == MPOL_INTERLEAVE || [ ... ] > @@ -2597,6 +2622,7 @@ static unsigned long alloc_pages_bulk_interleave(gfp_t gfp, > struct mempolicy *pol, unsigned long nr_pages, > struct page **page_array) > { > + unsigned int aflags = mpol_alloc_flags(pol); > int nodes; > unsigned long nr_pages_per_node; [Severity: High] This isn't a bug introduced by this patch, but is it safe to read pol->nodes locklessly in alloc_pages_bulk_interleave()? alloc_pages_bulk_interleave() reads the nodemask word-by-word via nodes_weight(pol->nodes) further down in this function. Concurrently, mpol_rebind_nodemask() mutates pol->nodes word-by-word via nodes_or(pol->nodes, tmp, priv). On systems with >64 nodes, could a torn read result in a weight of 0, causing a divide-by-zero panic when calculating nr_pages_per_node = nr_pages / nodes? [ ... ] > @@ -2632,6 +2658,7 @@ static unsigned long alloc_pages_bulk_weighted_interleave(gfp_t gfp, > struct mempolicy *pol, unsigned long nr_pages, > struct page **page_array) > { > + unsigned int aflags = mpol_alloc_flags(pol); > struct weighted_interleave_state *state; > struct task_struct *me = current; [Severity: High] This is a pre-existing issue, but does alloc_pages_bulk_weighted_interleave() violate the caller's GFP constraints? This function can be called via alloc_pages_bulk_mempolicy_noprof() from vmalloc with restrictive flags like GFP_NOFS or GFP_NOIO. However, it allocates its weights array unconditionally using kzalloc(nr_node_ids, GFP_KERNEL) later in the function. Could this strip the __GFP_FS or __GFP_IO protection and allow the allocation to recurse into the filesystem or block layer, potentially causing a deadlock? -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=22