Re: [PATCH v5 27/36] mm: add NODE_PRIVATE_CAP_USER_NUMA for userland numa controls

[email protected] Tue, 21 Jul 2026 20:13:41 +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 3 potential issue(s) to consider:

New issues:
- [Medium] Relative node indices are incorrectly treated as absolute node IDs for private node checking in mpol_set_nodemask.
- [Medium] Hugetlb folio migration to private nodes unconditionally fails because the hugetlb allocator ignores ALLOC_ZONELIST_PRIVATE.

Pre-existing issues:
- [High] Returning ACTION_AGAIN on pte_offset_map_lock() failure causes an infinite loop in the page table walker.
--

--- Patch [27]: [PATCH v5 27/36] mm: add NODE_PRIVATE_CAP_USER_NUMA for userland numa controls ---
commit 90c010ac54e1befaa9a30ca8c81fdac4b3524857
Author: Gregory Price <[email protected]>

mm: add NODE_PRIVATE_CAP_USER_NUMA for userland numa controls

This commit provides a mechanism to opt private nodes into userland numa
management. It adds node_allows_user_numa() to encapsulate whether a node
supports userland NUMA controls and modifies the memory policy and migration
interfaces to support these nodes.

> diff --git a/mm/mempolicy.c b/mm/mempolicy.c
> index a3ffb09897489..fe42a510590a2 100644
> --- a/mm/mempolicy.c
> +++ b/mm/mempolicy.c
> @@ -434,13 +434,14 @@ static int mpol_set_nodemask(struct mempolicy *pol,
>  
>  	/*
>  	 * Private nodes are not in cpuset.mems, so they're always stripped.
> -	 * Driver-allocated policies will already have MPOL_F_PRIVATE set,
> -	 * if that's the case, add back in the requested set of private nodes.
> +	 * Driver-allocated policies (MPOL_F_PRIVATE) and CAP_USER_NUMA private
> +	 * nodes should be added back into the nodemask.
>  	 */
>  	for_each_node_mask(nid, *nodes) {
>  		if (!node_is_private(nid))
>  			continue;
> -		if (pol->flags & MPOL_F_PRIVATE)
> +		if ((pol->flags & MPOL_F_PRIVATE) ||
> +		    node_allows_user_numa(nid))
>  			node_set(nid, nsc->mask2);
>  	}

[Severity: Medium]
When MPOL_F_RELATIVE_NODES is set, the *nodes mask contains relative indices 
instead of absolute node IDs. Does this loop incorrectly evaluate those 
relative indices as absolute node IDs when calling node_is_private(nid)?

If the relative index happens to match an absolute private node ID, it appears
that node could be erroneously added to the final policy mask.

[ ... ]

> @@ -752,7 +753,8 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,
>  			continue;
>  		}
>  		folio = vm_normal_folio(vma, addr, ptent);
> -		if (!folio || folio_is_private_managed(folio))
> +		if (!folio || folio_is_zone_device(folio) ||
> +		    !node_allows_user_numa(folio_nid(folio)))
>  			continue;
>  		if (folio_test_large(folio) && max_nr != 1)
>  			nr = folio_pte_batch(folio, pte, ptent, max_nr);

[Severity: High]
This is a pre-existing issue, but earlier in queue_folios_pte_range() we
return ACTION_AGAIN if pte_offset_map_lock() fails:

queue_folios_pte_range()
    ...
    mapped_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
    if (!pte) {
        walk->action = ACTION_AGAIN;
        return 0;
    }

Does setting ACTION_AGAIN cause an infinite retry loop in the pagewalker 
when pte_offset_map_lock() fails non-transiently, such as when encountering
migration entries?

> diff --git a/mm/migrate.c b/mm/migrate.c
> index d20674c07b947..b548d79352a38 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -2231,7 +2231,8 @@ struct folio *alloc_migration_target(struct folio *src, unsigned long private)
>  	if (is_highmem_idx(zidx) || zidx == ZONE_MOVABLE)
>  		gfp_mask |= __GFP_HIGHMEM;
>  
> -	return __folio_alloc(gfp_mask, order, nid, mtc->nmask, ALLOC_DEFAULT);
> +	return __folio_alloc(gfp_mask, order, nid, mtc->nmask,
> +			     mtc->alloc_flags);
>  }

[Severity: Medium]
This correctly updates the standard folio allocation to use mtc->alloc_flags,
but earlier in alloc_migration_target(), the hugetlb allocation path does
not pass this flag:

    if (folio_test_hugetlb(src)) {
        ...
        return alloc_hugetlb_folio_nodemask(h, nid, mtc->nmask, gfp_mask,
                                            htlb_allow_alloc_fallback(mtc->reason));
    }

Will hugetlb migration to private nodes fail unconditionally because it misses
the ALLOC_ZONELIST_PRIVATE flag needed by the hugetlb allocator?

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=27