[PATCH 08/12] mm/mempolicy: Create a page allocator for policy

From: Ben Widawsky
Date: Tue Jun 30 2020 - 17:25:31 EST


This patch adds a helper function which takes care of handling multiple
preferred nodes. It will be called by future patches that need to handle
this, specifically VMA based page allocation, and task based page
allocation. Huge pages don't quite fit the same pattern because they use
different underlying page allocation functions. This consumes the
previous interleave policy specific allocation function to make a one
stop shop for policy based allocation.

For now, only interleaved policy will be used so there should be no
functional change yet. However, if bisection points to issues in the
next few commits, it was likely the fault of this patch.

Similar functionality is offered via policy_node() and
policy_nodemask(). By themselves however, neither can achieve this
fallback style of sets of nodes.

Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Michal Hocko <mhocko@xxxxxxxxxx>
CC: Vlastimil Babka <vbabka@xxxxxxx>
Signed-off-by: Ben Widawsky <ben.widawsky@xxxxxxxxx>
---
mm/mempolicy.c | 60 +++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 47 insertions(+), 13 deletions(-)

diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 3b38c9c4e580..1009cf90ad37 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2199,22 +2199,56 @@ bool mempolicy_nodemask_intersects(struct task_struct *tsk,
return ret;
}

-/* Allocate a page in interleaved policy.
- Own path because it needs to do special accounting. */
-static struct page *alloc_page_interleave(gfp_t gfp, unsigned order,
- unsigned nid)
+/* Handle page allocation for all but interleaved policies */
+static struct page *alloc_pages_policy(struct mempolicy *pol, gfp_t gfp,
+ unsigned int order, int preferred_nid)
{
struct page *page;
+ gfp_t gfp_mask = gfp;

- page = __alloc_pages(gfp, order, nid);
- /* skip NUMA_INTERLEAVE_HIT counter update if numa stats is disabled */
- if (!static_branch_likely(&vm_numa_stat_key))
+ if (pol->mode == MPOL_INTERLEAVE) {
+ page = __alloc_pages(gfp, order, preferred_nid);
+ /* skip NUMA_INTERLEAVE_HIT counter update if numa stats is disabled */
+ if (!static_branch_likely(&vm_numa_stat_key))
+ return page;
+ if (page && page_to_nid(page) == preferred_nid) {
+ preempt_disable();
+ __inc_numa_state(page_zone(page), NUMA_INTERLEAVE_HIT);
+ preempt_enable();
+ }
return page;
- if (page && page_to_nid(page) == nid) {
- preempt_disable();
- __inc_numa_state(page_zone(page), NUMA_INTERLEAVE_HIT);
- preempt_enable();
}
+
+ VM_BUG_ON(preferred_nid != NUMA_NO_NODE);
+
+ preferred_nid = numa_node_id();
+
+ /*
+ * There is a two pass approach implemented here for
+ * MPOL_PREFERRED_MANY. In the first pass we pretend the preferred nodes
+ * are bound, but allow the allocation to fail. The below table explains
+ * how this is achieved.
+ *
+ * | Policy | preferred nid | nodemask |
+ * |-------------------------------|---------------|------------|
+ * | MPOL_DEFAULT | local | NULL |
+ * | MPOL_PREFERRED | best | NULL |
+ * | MPOL_INTERLEAVE | ERR | ERR |
+ * | MPOL_BIND | local | pol->nodes |
+ * | MPOL_PREFERRED_MANY | best | pol->nodes |
+ * | MPOL_PREFERRED_MANY (round 2) | local | NULL |
+ * +-------------------------------+---------------+------------+
+ */
+ if (pol->mode == MPOL_PREFERRED_MANY)
+ gfp_mask |= __GFP_RETRY_MAYFAIL;
+
+ page = __alloc_pages_nodemask(gfp_mask, order,
+ policy_node(gfp, pol, preferred_nid),
+ policy_nodemask(gfp, pol));
+
+ if (unlikely(!page && pol->mode == MPOL_PREFERRED_MANY))
+ page = __alloc_pages_nodemask(gfp, order, preferred_nid, NULL);
+
return page;
}

@@ -2256,8 +2290,8 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
unsigned nid;

nid = interleave_nid(pol, vma, addr, PAGE_SHIFT + order);
+ page = alloc_pages_policy(pol, gfp, order, nid);
mpol_cond_put(pol);
- page = alloc_page_interleave(gfp, order, nid);
goto out;
}

@@ -2341,7 +2375,7 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order)
* nor system default_policy
*/
if (pol->mode == MPOL_INTERLEAVE)
- page = alloc_page_interleave(gfp, order, interleave_nodes(pol));
+ page = alloc_pages_policy(pol, gfp, order, interleave_nodes(pol));
else
page = __alloc_pages_nodemask(gfp, order,
policy_node(gfp, pol, numa_node_id()),
--
2.27.0