numaのノードID取得


alloc_pages()でpageを取得する時、NUMAはalloc_pages_current()、SMPはnuma_node_id()をノードとしてalloc_pages_node()をコールします。numa_node_id()はCPU変数から取得したカレント動作しているCPUのノードです。
#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;
}

最終更新 2014/03/04 03:26:11 - north
(2014/03/03 01:19:08 作成)


検索

アクセス数
3586184
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。