周末在家闲来无事,研究一下mempolicy对内存页分配的影响。分析基于linux内核4.19.195.
先看看内核里面支持什么内存分配策略:(这里的内存分配策略指的NUMA mem policy策略)
enum {
MPOL_DEFAULT,
MPOL_PREFERRED,
MPOL_BIND,
MPOL_INTERLEAVE,
MPOL_LOCAL,
MPOL_MAX,
};
在处理缺页中断的流程中,若是hugetlbfs的缺页,最终会走到alloc_huge_page函数完成大页的分配。
struct page *alloc_huge_page(struct vm_area_struct *vma,
unsigned long addr, int avoid_reserve)
{
***
page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve, gbl_chg);
***
}
内存分配主要走的dequeue_huge_page_vma函数,我们详细看这个函数。
static struct page *dequeue_huge_page_vma(struct hstate *h,
struct vm_area_struct *vma,
unsigned long address, int avoid_reserve,
long chg)
{
struct page *page;
struct mempolicy *mpol;
gfp_t gfp_mask;
nodemask_t *nodemask;
int nid;
if (!vma_has_reserves(vma, chg) &&
h->free_huge_pages - h->resv_huge_pages == 0)
goto err;
if (avoid_reserve && h->free_huge_pages - h->resv_huge_pages == 0)
goto err;
gfp_mask = htlb_alloc_mask(h);
nid = huge_node(vma, address, gfp_mask, &mpol, &nodemask);
page = dequeue_huge_page_nodemask(h, gfp_mask, nid, nodemask);
if (page && !avoid_reserve && vma_has_reserves(vma, chg)) {
SetPagePrivate(page);
h->resv_huge_pages--;
}
mpol_cond_put(mpol);
return page;
err:
return NULL;
}
其中,真正的内存分配执行函数是dequeue_huge_page_nodemask,在调用这个函数之前,会通过huge_node函数,初始化nid以及nodemask这两个变量用于传递给dequeue_huge_page_nodemask。其中,nid是内存分配首选的node节点,nodemask用于指明允许在哪些节点上分配内存。从函数dequeue_huge_page_nodemask中我们可以看到,hugetlbfs的内存分配方法和buddy类似,也是依赖zonelist的顺序来做内存分配优先级的排列,然后依赖nodemask来辨别是否允许在zonglist上的zone节点分配内存。 那么。nid和nodemask是怎么初始化的呢?
int huge_node(struct vm_area_struct *vma, unsigned long addr, gfp_t gfp_flags,
struct mempolicy **mpol, nodemask_t **nodemask)
{
int nid;
*mpol = get_vma_policy(vma, addr);
*nodemask = NULL;
if (unlikely((*mpol)->mode == MPOL_INTERLEAVE)) {
nid = interleave_nid(*mpol, vma, addr,
huge_page_shift(hstate_vma(vma)));
} else {
nid = policy_node(gfp_flags, *mpol, numa_node_id());
if ((*mpol)->mode == MPOL_BIND)
*nodemask = &(*mpol)->v.nodes;
}
return nid;
}
从函数huge_node中可以看到,除非使用了MPOL_BIND内存策略,否则一律允许在所有node节点上尝试分配内存(即*nodemask=NULL)。 我们来看常用走的policy_node函数。
static int policy_node(gfp_t gfp, struct mempolicy *policy,
int nd)
{
if (policy->mode == MPOL_PREFERRED && !(policy->flags & MPOL_F_LOCAL))
nd = policy->v.preferred_node;
else {
WARN_ON_ONCE(policy->mode == MPOL_BIND && (gfp & __GFP_THISNODE));
}
return nd;
}
从函数的实现中可以看到,只有在内存分配策略是MPOL_PREFERRED且MPOL_F_LOCAL标志位没有被设置的时候,才会从内存分配策略中获取node节点,否则,一律使用当前cpu所在的node节点作为内存分配的首选节点。
|