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) #endifmempolicyには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; }