slub data structure
TRANSCRIPT
README• このスライドは以下の点に絞ってます
• SLUBのデータ構造
• slab cacheの作成
• 下記資料にあるSLUB data structuresを理解する
• http://events.linuxfoundation.org/sites/events/files/slides/slaballocators.pdf
• memory control groupも絡むのですが、そこは気にしない方向でコードリーディングしてます
Common variable name• slub.cでよく使われれる変数名
• このスライドでも単にcと書いている場合はstruct kmem_cache_cpuの変数を指します
• s
• struct kmem_cacheの変数
• c
• struct kmem_cache_cpuの変数
• n
• struct kmem_cache_nodeの変数
Important variables• s->cpu_slab
• 現在のcpuに紐づくstruct kmem_cache_cpuへのポインタ
• c->page
• slab objectが使用しているstruct pageへのポインタ
• c->freelist
• リストと名前が付いてるが次の空きslabオブジェクトを指す
Slab allocator implementations in Linux
• LinuxにおけるSlab allocator実装の一つ
• SLAB
• SunOSのSlab allocatorアルゴリズムに基づく
• SLUB
• 2.6.23からデフォルトのSlab allocatorに
• メモリのフラグメンテーションが発生しにくい
• SLOB
• サイズの小さいallocator
SLUB basis• SLABにあったfull、partial、emptyといったリストによる管理をしていない
• slab objectへはstruct kmem_cache_cpuのfreelistより参照
• partial listはあるけどSLABとは違う使い方
• Slab cache作成時に似たサイズのスラブがあればマージ
• Cache Coloring無し
SLUB merge• スラブキャッシュ作成時にすでに同サイズのスラブがあればそこにマージ
• slabinfo -aでマージされたSlabを確認可能
• kernel source treeにあるtools/vm/slabinfo.c
:at-0000104 <- sda1 buffer_head:at-0000320 <- jbd2_transaction_s nfs_direct_cache:t-0000024 <- scsi_data_buffer numa_policy:t-0000032 <- sd_ext_cdb dnotify_struct kmalloc-32:t-0000040 <- khugepaged_mm_slot Acpi-Namespace ext4_system_zone
Cache Coloring• SLABでは使用している
• SLUBでは実装していない
• Christophさん曰く、これについてはcpuのキャッシュ設計側で面倒見るべきだろう
• https://lkml.org/lkml/2008/8/4/521
Main structures• struct kmem_cache
• include/linux/slub_def.h
• slab objectを管理する構造体
• 下記の2つの構造体はkmem_cache構造体のメンバ変数
• struct kmem_cache_cpu
• include/linux/slub_def.h
• slab objectへのアクセスはここから
• struct kmem_cache_node
• mm/slab.h
• NUMAノード毎のslabを指す
• struct page
• freelistへのポインタ、オブジェクト使用数などの変数が追加された
Slab object - offset• struct kmem_cacheの変数offset
• 下記の条件に該当する場合、objectの先頭アドレス+offsetバイト目からをslab objectにする
• slab objectのfree時にRCUを使う
• SLAB_POISONがセットされている
• コンストラクタが指定されている
• offsetは下記のサイズに設定される
• objectのサイズをsizeof(void *)の境界にあうようにalignしたサイズ
• SLAB_REDZONEがセットされている場合はRED ZONEのサイズも足す
struct kmem_cachestruct kmem_cache { struct kmem_cache_cpu __percpu *cpu_slab; /* Used for retriving partial slabs etc */ unsigned long flags; unsigned long min_partial; int size; /* The size of an object including meta data */ int object_size; /* The size of an object without meta data */ int offset; /* Free pointer offset. */ int cpu_partial; /* Number of per cpu partial objects to keep around */ struct kmem_cache_order_objects oo; ~略~ struct kmem_cache_node *node[MAX_NUMNODES]; };
Slab Objectの管理に使う主な変数
struct kmem_cache_cpu
struct kmem_cache_cpu { void **freelist; /* Pointer to next available object */ unsigned long tid; /* Globally unique transaction id */ struct page *page; /* The slab from which we are allocating */ struct page *partial; /* Partially allocated frozen slabs */ #ifdef CONFIG_SLUB_STATS unsigned stat[NR_SLUB_STAT_ITEMS]; #endif };
struct kmem_cache_node
struct kmem_cache_node { spinlock_t list_lock; ~略~ #ifdef CONFIG_SLUB unsigned long nr_partial; struct list_head partial; #ifdef CONFIG_SLUB_DEBUG atomic_long_t nr_slabs; atomic_long_t total_objects; struct list_head full; #endif #endif
debug用途を除くとlistと個数を保持する変数だけ
struct page struct { union { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* sl[aou]b first free object */ ~略~ } union { ~略~ /* Used for cmpxchg_double in slub */ unsigned long counters; ~略~ struct { union { ~略 struct { /* SLUB */ unsigned inuse:16; unsigned objects:15; unsigned frozen:1; };
Slab data relation• 現在のcpuで使用しているslabへのアクセス
• cよりアクセス
• c->freelist、c->pageなど
• s->cpu_slabから外されたslab
• s->node[node number]からアクセス
Create a slab• slabの作成はkmem_cache_create()
• KMEM_CACHE(__struct, __flags) マクロもある
• kmem_cache_create()の時点ではslab objectsの作成はしない
• freelistの作成、slab objectの初期化などはkmem_cache_alloc()実行時
• 処理は2段階
• 最初にstruct kmem_cacheのオブジェクトを取得
• 次に取得したsの変数を設定
struct kmem_cache * kmem_cache_create(const char *name, size_t size,size_t align, unsigned long flags, void (*ctor)(void *))
slab creating flowkmem_cache_create() ̶> __kmem_cache_alias() ̶> do_kmem_cache_create() ̶> kmem_cache_zalloc() ̶> __kmem_cache_create() ̶> kmem_cache_open() ̶> calucalate_sizes() ̶> init_kmem_cache_nodes() ̶> alloc_kmem_cache_cpus()
主なところ
kmem_cache_create()• kmem_cache構造体のオブジェクトを取得して返す
• 作成しようとしたslabがマージ可能かチェック
• マージ可能ならslabオブジェクトの作成せずに終了
• do_kmem_cache_create()でkmem_cache構造体のオブジェクト(s)を取得
do_kmem_cache_create()• kmem_cache_zalloc()でstruct kmem_cache構造体のオブジェクトを取得
• kmem_cache構造体はslubで管理されている
• これを管理しているオブジェクトはkmem_cache
• kernelの初期化時にkmem_cache_init()にて設定
• sの変数を一部設定
• __kmem_cache_create()でsの設定
• slab_cachesリストにsをつなぐ
• 全kmem_cache構造体をつないでいるリスト
kmem_cache_zalloc()• __GFP_ZEROをフラグに設定してkmem_cache_alloc()を呼ぶだけ
• kmem_cache_alloc()はslab_alloc()のラッパー的なもの
• slab_alloc()はslab_alloc_node()へのラッパー
• 大雑把にいうとstruct kmem_cache構造体のインスタンスを取得して返す
• しかし、この処理が肝なので後のスライドで説明します
__kmem_cache_create()• kmem_cache_open()でsの初期化
• sysfsでオブジェクトが見えるように設定[root@miko slab]# ls -al /sys/kernel/slab/ total 0 drwxr-xr-x 108 root root 0 Dec 14 01:54 . drwxr-xr-x 9 root root 0 Dec 14 01:54 .. lrwxrwxrwx 1 root root 0 Dec 13 18:29 Acpi-Namespace -> :t-0000040 lrwxrwxrwx 1 root root 0 Dec 13 18:29 Acpi-Operand -> :t-0000072 lrwxrwxrwx 1 root root 0 Dec 13 18:29 Acpi-Parse -> :t-0000048 lrwxrwxrwx 1 root root 0 Dec 13 18:29 Acpi-ParseExt -> :t-0000072 lrwxrwxrwx 1 root root 0 Dec 13 18:29 Acpi-State -> :t-0000080 drwxr-xr-x 3 root root 0 Dec 14 01:54 anon_vma lrwxrwxrwx 1 root root 0 Dec 13 18:29 anon_vma_chain -> :t-0000064 drwxr-xr-x 3 root root 0 Dec 14 01:54 :at-0000016
symlinkがalias (mergeされたもの)
kmem_cache_open()
• calucalate_sizes()でslab objectのサイズに関する変数を設定
• kmem_cache_node構造体の初期化
• percpuな kmem_cache_cpu構造体の初期化
calucalate_sizes()• slab objectのサイズ周りの変数を設定
• s->size、 s->offset、s->reserved
• debugオプション等によってオブジェクトの大きさが変わる
• 他に、allocationのフラグ、メモリ確保に使用するオーダーの設定もする
init_kmem_cache_nodes()• kmem_cache_alloc_node()でstruct kmem_cache_nodeオブジェクトの取得
• slubで管理されている
• 管理しているのはkmem_cache_node
• init_kmem_cache_node()でstruct kmem_cache_nodeの初期化
• リストの初期化等
alloc_kmem_cache_cpus()• s->cpu_slabオブジェクトの設定
• __alloc_percpu()を使う
• init_kmem_cache_cpu()でslubで使うトランザクションIDの設定
/* * The transaction ids are globally unique per cpu and per operation on * a per cpu queue. Thus they can be guarantee that the cmpxchg_double * occurs on the right processor and that there was no operation on the * linked list in between. */
setup freelist• kmem_cache_alloc()実行時に行う
• freelist用のpage取得
• slab objectsの初期化
• __slab_alloc()にて以下に該当する場合にsetupする
• c->page == NULL or c->freelist == NULL
setup flow__slab_alloc() ̶> new_slab_objects() ̶> new_slab() ̶> allocate_slab() ̶> alloc_slab_page() ̶> alloc_pages() or alloc_pages_exact_node() ̶> setup_object() ̶> set_freepointer()
主なもの
__slab_alloc()• c->page、c->freelistがNULLかチェックし、NULLの場合にnew_slab_objects()を呼ぶ
• pageを確保出来たらc->freelist、c->tidを設定
• freelistが最初の未使用slab objectを指す
• get_freepointer()で設定
new_slab_objects()• get_partial()で他のノードにfreelistがあるかチェック
• 見つかればそれを返す(過去にsetup済みなので)
• new_slab()でpageの確保
• sよりcを取得し、cがNULLでない場合はcpu_slabを入れ替えるため下記を実行
• deactivate_slab()でslabをpartialリストにつなぐ
• cのデータを初期化する
• c->page = c->freelist = NULL等
• page->freelistを戻り値の変数freelistに代入し、page->freelistはNULLに
• c->pageを確保したpageに置き換え
new_slab()• alloca_slab()でfreelist用のpageを確保
• slab objectを初期化
for_each_object_idx(p, idx, s, start, page->objects) { setup_object(s, page, p); if (likely(idx < page->objects)) set_freepointer(s, p, p + s->size); else set_freepointer(s, p, NULL); }
for_each_object_idxマクロの終了条件はidx <= page->objects
allocate_slab()• allocate_slab_page()でpageを確保するのが仕事
• s->ooで指定されているorderでpageを確保を試みる
• もし失敗した場合はs->minに設定されている最小サイズでpage確保をリトライ
• これでも失敗したらNULLを返す
alloc_slab_page()• 実際にpage確保の関数を呼ぶのが個々
• NUMAノードの指定有無で呼ぶ関数が変わる
• node == NUMA_NO_NODE(ノード不問)
• alloc_pages()
• node != NUMA_NO_NODE
• alloc_pages_exact_node()
setup_object()
• setup_object_debug()でデバッグ用のオブジェクト設定
• POISON、RED ZONE、Tracking
• s->ctorが設定されていればコンストラクタ関数の呼び出し
set_freepointer()• object + s->offsetのところに次のobjectのアドレスを設定
static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) { *(void **)(object + s->offset) = fp; }
for_each_object_idx(p, idx, s, start, page->objects) { setup_object(s, page, p); if (likely(idx < page->objects)) set_freepointer(s, p, p + s->size); else set_freepointer(s, p, NULL); }
参考資料• Slab allocators in the Linux Kernel: SLAB, SLOB, SLUB
• http://events.linuxfoundation.org/sites/events/files/slides/slaballocators.pdf
• Linux Kernel Watch番外編 Linuxメモリ管理の最先端を探る
• http://www.atmarkit.co.jp/flinux/rensai/watch2008/watchmema.html