SLUBの場合のstruct kmem_cacheでstruct kmem_cache_node *node[MAX_NUMNODES];がどのように使われているのかというメモ。 kernelのバージョンは3.18。
struct kmem_cache_nodeはSLAB・SLUBの場合分けがifdefでされていて、debugオプション無しのSLUBの場合はこのようになる。
317 struct kmem_cache_node { 318 spinlock_t list_lock; 333 #ifdef CONFIG_SLUB 334 unsigned long nr_partial; 335 struct list_head partial; 341 #endif 342 343 };
基本的にpartialなデータをリストで繋ぐための構造と言った感じ。
この構造体を操作するのは__add_partial()と__remove_partial()の2関数。add_partial()はこのような処理でremove_partial()はこの逆の操作。見ての通り、リストに繋がるのはstruct pageです。これらの関数は関数名にアンダーバーが付かない版の関数から呼ばれます。
1492 static inline void 1493 __add_partial(struct kmem_cache_node *n, struct page *page, int tail) 1494 { 1495 n->nr_partial++; 1496 if (tail == DEACTIVATE_TO_TAIL) 1497 list_add_tail(&page->lru, &n->partial); 1498 else 1499 list_add(&page->lru, &n->partial); 1500 }
では、これらの関数はどんな時に呼ばれるのか? 主なケースとしてはkmem_cache_free()でslabオブジェクトを解放するときにadd_partial()が呼ばれ、kmem_cache_alloc()でslabオブジェクトを取得する場合にremove_partial()が呼ばれる。
というわけで、まずは__add_partial()が呼ばれる場合から。kmem_cache_free()の実質的な処理はslab_free()が行います。
この関数の中で以下の部分ですが、pageは解放しようとしているslabオブジェクトが存在するページです。これがc->page(cはstruct kmem_cache_cpu の変数で今動いているcpuと関連付いているのでc->pageは今のcpuで設定されたslabオブジェクトのページ)と同じなら新しくslabオブジェクトを作る時と同じようにset_freepointer()で空きオブジェクトとして設定し、そうでな場合に__slab_free()が呼ばれる。add_partial()が呼ばれるのはslab_free()が呼ばれる場合。
2661 if (likely(page == c->page)) { 2662 set_freepointer(s, object, c->freelist); 〜略〜 2671 } 2672 stat(s, FREE_FASTPATH); 2673 } else 2674 __slab_free(s, page, x, addr);
__slab_free()の処理もadd_partial()のところだけ見ると2箇所あって、ひとつはput_cpu_partial()からの場合。
2576 if (likely(!n)) { 2577 2578 /* 2579 * If we just froze the page then put it onto the 2580 * per cpu partial list. 2581 */ 2582 if (new.frozen && !was_frozen) { 2583 put_cpu_partial(s, page, 1); 2584 stat(s, CPU_PARTIAL_FREE); 2585 }
もう一箇所はここ。上記のところはちょっと面倒なのでこちらだけ見ていくと、kmem_cache_has_cpu_partial()はCONFIG_SLUB_CPU_PARTIALがセットされていなければ常にfalseを返し、priorがNULLなことはまず無いということでif文の中に入り、add_partial()が呼ばれる。
2598 /* 2599 * Objects left in the slab. If it was not on the partial list before 2600 * then add it. 2601 */ 2602 if (!kmem_cache_has_cpu_partial(s) && unlikely(!prior)) { 2603 if (kmem_cache_debug(s)) 2604 remove_full(s, n, page); 2605 add_partial(n, page, DEACTIVATE_TO_TAIL); 2606 stat(s, FREE_ADD_PARTIAL); 2607 }
そして、解放しようとしたslabオブジェクトだけを解放するのではなくて、pageそのものがkmem_cache_node構造体のpartialリストに繋げられる。
次に__remove_partial()を使うパターン。 ここではkmem_cache_alloc()時に新しいslabを作る必要があってnew_slab_objects()が呼ばれた場合。
2163 static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, 2164 int node, struct kmem_cache_cpu **pc) 2165 { 2166 void *freelist; 2167 struct kmem_cache_cpu *c = *pc; 2168 struct page *page; 2169 2170 freelist = get_partial(s, flags, node, c); 2171
get_partial()は最初にどのNUMAノードから検索するかを決めて、get_partial_node()でオブジェクトの取得を行う。kmem_cache_alloc()からの流れではnodeにはNUMA_NO_NODEが渡ってきて、どのノードでも良いとなっている。
1688 static void *get_partial(struct kmem_cache *s, gfp_t flags, int node, 1689 struct kmem_cache_cpu *c) 1690 { 1691 void *object; 1692 int searchnode = node; 1693 1694 if (node == NUMA_NO_NODE) 1695 searchnode = numa_mem_id(); 1696 else if (!node_present_pages(node)) 1697 searchnode = node_to_mem_node(node); 1698 1699 object = get_partial_node(s, get_node(s, searchnode), c, flags); 1700 if (object || node != NUMA_NO_NODE) 1701 return object; 1702 1703 return
get_partial_node()はlist_for_each_entry_safeマクロでリストを手繰りながらacquire_slab()でオブジェクトの取得をする。
1593 list_for_each_entry_safe(page, page2, &n->partial, lru) { 1594 void *t; 1595 1596 if (!pfmemalloc_match(page, flags)) 1597 continue; 1598 1599 t = acquire_slab(s, n, page, object == NULL, &objects); 1600 if (!t) 1601 break; 1602 1603 available += objects; 1604 if (!object) { 1605 c->page = page; 1606 stat(s, ALLOC_FROM_PARTIAL); 1607 object = t; 1608 } else { 1609 put_cpu_partial(s, page, 0); 1610 stat(s, CPU_PARTIAL_NODE); 1611 }
acquire_slab()ではどのようなことをするかというと、pageからfreelist等を取得して、そこから新しくstruct pageを作り__cmpxchg_double_slab()でデータの入れ替えをし、最後にpartialのリストからpageを外すということをする。
1529 static inline void *acquire_slab(struct kmem_cache *s, 1530 struct kmem_cache_node *n, struct page *page, 1531 int mode, int *objects) 1532 { 1533 void *freelist; 1534 unsigned long counters; 1535 struct page new; 1536 1537 lockdep_assert_held(&n->list_lock); 1538 1539 /* 1540 * Zap the freelist and set the frozen bit. 1541 * The old freelist is the list of objects for the 1542 * per cpu allocation list. 1543 */ 1544 freelist = page->freelist; 1545 counters = page->counters; 1546 new.counters = counters; 1547 *objects = new.objects - new.inuse; 1548 if (mode) { 1549 new.inuse = page->objects; 1550 new.freelist = NULL; 1551 } else { 1552 new.freelist = freelist; 1553 } 1554 1555 VM_BUG_ON(new.frozen); 1556 new.frozen = 1; 1557 1558 if (!__cmpxchg_double_slab(s, page, 1559 freelist, counters, 1560 new.freelist, new.counters, 1561 "acquire_slab")) 1562 return NULL; 1563 1564 remove_partial(n, page); 1565 WARN_ON(!freelist); 1566 return freelist; 1567 }
そして、戻った先でstruct kmem_cache_cpuのc->pageに戻り値として返したfreelistをセットする。
ここまでで見てきたようにstruct kmem_cache_nodeが使われるのはslabオブジェクト(page)がstruct kmem_cache_cpuのpageでなくなる場合にadd_partial()によってリストに繋がれ、c->pageからも外れる。逆に、partialリストにつながっていたページをc->pageにセットする場合はremove_partial()でpartialリストから外す。
こう見るとリストの使いかたがSLABとは大分変わったんだなーというのがわかりますね。
32ビットコンピュータをやさしく語る はじめて読む486 (アスキー書籍)
- 作者: 蒲地輝尚
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/10/21
- メディア: Kindle版
- この商品を含むブログを見る