numaのノードID取得
alloc_pages()でpageを取得する時、NUMAはalloc_pages_current()、SMPはnuma_node_id()をノードとしてalloc_pages_node()をコールします。numa_node_id()はCPU変数から取得したカレント動作しているCPUのノードです。
MPOL_DEFAULTは、MPOL_PREFERRED/MPOL_F_LOCALとなります。
MPOL_INTERLEAVEなら、interleave_nodes()でカレントプロセスcurrent->il_nextをノードIDとしalloc_page_interleave()を、そうでないならpolicy_zonelist()でノードIDを取得し、__alloc_pages_nodemask()をコールします。
・MPOL_PREFERREDの時
MPOL_F_LOCALが設定されていれば、カレントノードがノードIDとなります。そうでな、mempolicyのpreferred_nodeが。このノードは一般的に優先度の高いノードあるいは特別なノードを設定します。
・MPOL_BINDの時
__GFP_THISNODEでないか、カレントノードがmempolicyに設定されていなければ、mempolicy->v.nodesの最初(下位ビットから検索)のノードを取得する。そうでなければカレントノードがノードIDとなる。
NODE_DATA(nid)のMAX_ZONELISTS=2つゾーンリストメンバstruct zonelist node_zonelists[MAX_ZONELISTS]のnode_zonelists[0]のリストを選択することになります。これは全ゾーンのメモリがリストされています。(たぶん)
#ifdef CONFIG_NUMA
static inline struct page *
alloc_pages(gfp_t gfp_mask, unsigned int order)
{
return alloc_pages_current(gfp_mask, order);
}
#else
#define alloc_pages(gfp_mask, order) \
alloc_pages_node(numa_node_id(), gfp_mask, order)
#endif
mempolicyにはv/wの2つのノードマスクで、mode/flagsの組み合わせによって、どのノードを使うかを決定します。通常vのノードを使うことになります。(wのノードの使い方は調査中です。)
struct mempolicy {
atomic_t refcnt;
unsigned short mode; /* See MPOL_* above */
unsigned short flags; /* See set_mempolicy() MPOL_F_* above */
union {
short preferred_node; /* preferred */
nodemask_t nodes; /* interleave/bind */
/* undefined for default */
} v;
union {
nodemask_t cpuset_mems_allowed; /* relative to these nodes */
nodemask_t user_nodemask; /* nodemask passed by user */
} w;
};MPOL_DEFAULTは、MPOL_PREFERRED/MPOL_F_LOCALとなります。
static struct mempolicy default_policy = {
.refcnt = ATOMIC_INIT(1), /* never free it */
.mode = MPOL_PREFERRED,
.flags = MPOL_F_LOCAL,
};
alloc_pages_current()でNUMA時のpage割り当てを行います。mempolicyのmodeに応じてどのノードからpageを割り当てるかと云う事です。MPOL_INTERLEAVEなら、interleave_nodes()でカレントプロセスcurrent->il_nextをノードIDとしalloc_page_interleave()を、そうでないならpolicy_zonelist()でノードIDを取得し、__alloc_pages_nodemask()をコールします。
struct page *alloc_pages_current(gfp_t gfp, unsigned order)
{
struct mempolicy *pol = current->mempolicy;
struct page *page;
if (!pol || in_interrupt() || (gfp & __GFP_THISNODE))
pol = &default_policy;
get_mems_allowed();
if (pol->mode == MPOL_INTERLEAVE)
page = alloc_page_interleave(gfp, order, interleave_nodes(pol));
else
page = __alloc_pages_nodemask(gfp, order,
policy_zonelist(gfp, pol, numa_node_id()),
policy_nodemask(gfp, pol));
put_mems_allowed();
return page;
}
EXPORT_SYMBOL(alloc_pages_current);
MPOL_INTERLEAVEのノードID取得を行います。カレントプロセスのil_nextをノードIDとし、next_node()でその次のノードIDを次回のノードIDとして、カレントプロセスのil_nextに設定することで、ノードを順に使用することになります。カレントプロセス下から取得することで、異なるエレメント間でのpage取得においても、ノード順に相互に反映されます。
static unsigned interleave_nodes(struct mempolicy *policy)
{
unsigned nid, next;
struct task_struct *me = current;
nid = me->il_next;
next = next_node(nid, policy->v.nodes);
if (next >= MAX_NUMNODES)
next = first_node(policy->v.nodes);
if (next < MAX_NUMNODES)
me->il_next = next;
return nid;
}
MPOL_INTERLEAVE以外の時のノードIDそしてノードリストの取得を行います。引数のndはnuma_node_id()で、カレントノードIDです。・MPOL_PREFERREDの時
MPOL_F_LOCALが設定されていれば、カレントノードがノードIDとなります。そうでな、mempolicyのpreferred_nodeが。このノードは一般的に優先度の高いノードあるいは特別なノードを設定します。
・MPOL_BINDの時
__GFP_THISNODEでないか、カレントノードがmempolicyに設定されていなければ、mempolicy->v.nodesの最初(下位ビットから検索)のノードを取得する。そうでなければカレントノードがノードIDとなる。
static struct zonelist *policy_zonelist(gfp_t gfp, struct mempolicy *policy,
int nd)
{
switch (policy->mode) {
case MPOL_PREFERRED:
if (!(policy->flags & MPOL_F_LOCAL))
nd = policy->v.preferred_node;
break;
case MPOL_BIND:
if (unlikely(gfp & __GFP_THISNODE) &&
unlikely(!node_isset(nd, policy->v.nodes)))
nd = first_node(policy->v.nodes);
break;
default:
BUG();
}
return node_zonelist(nd, gfp);
}
以上概略要約すれば、MPOL_DEFAULT/MPOL_INTERLEAVEは、使用可能なノードを順番に、MPOL_PREFERREDはmempolicyのv.preferred_nodeを、MPOL_BINDはカレントノードが使用可能ノードならカレントノードを、そうでないなら使用可能な最初のノードを。という感じでしょうか。ただしmempolicyのflag及び、取得pageフラグgfp_t gfpにも依存しています。補足
SMPでのpage取得は、ノードIDがたぶん0でのalloc_page_interleave()からnode_zonelist()がコールされ、NODE_DATA(nid)のMAX_ZONELISTS=2つゾーンリストメンバstruct zonelist node_zonelists[MAX_ZONELISTS]のnode_zonelists[0]のリストを選択することになります。これは全ゾーンのメモリがリストされています。(たぶん)
static struct page *alloc_page_interleave(gfp_t gfp, unsigned order,
unsigned nid)
{
struct zonelist *zl;
struct page *page;
zl = node_zonelist(nid, gfp);
page = __alloc_pages(gfp, order, zl);
if (page && page_zone(page) == zonelist_zone(&zl->_zonerefs[0]))
inc_zone_page_state(page, NUMA_INTERLEAVE_HIT);
return page;
}
static inline struct zonelist *node_zonelist(int nid, gfp_t flags)
{
return NODE_DATA(nid)->node_zonelists + gfp_zonelist(flags);
}
static inline int gfp_zonelist(gfp_t flags)
{
if (NUMA_BUILD && unlikely(flags & __GFP_THISNODE))
return 1;
return 0;
}





