スラブの確保
kmallocの流れはstruct kmem_cacheから、プールしているスラブオブジェクトを確保するものでした。もしそこにオブジェクトがなければ、リストしているスラブからこのプールに移動することでメモリを確保していたわけです。そして、もしこのスラブ保持しているリスト上にも、スラブが無かった時、新規にスラブを確保しこのリストに登録することになります。この処理を行うのが、cache_grow関数です。
スラブの確保
概略はバディシステムから必要とされるページを確保し、そのメモリーをスラブとして利用する旨の情報をページディスクリプタに設定し、そのメモリ空間を所定のオブジェクトを持つスラブとするようにスラブディスクリプタ等の設定をしたのち、キャッシュディスクリプタの完全未使用スラブリストに登録するというものです。
引数のstruct kmem_cache下のスラブリストから、offset = l3->colour_nextで色付け情報を取得し、次回の取得のためl3->colour_next++とし、ただしそれがif (l3->colour_next >= cachep->colour)でカラー数を越えていたら、l3->colour_next = 0としています。
そして、offset *= cachep->colour_offで、ずらすサイズをoffsetに設定します。cachep->colour_offはアライメントサイズ(通常4バイトです。たぶん)です。
次にkmem_getpagesでバディシステムからページを取得し、alloc_slabmgmtでそのメモリー空間に、スラブディスクリプタ等の設定を施し、slab_map_pagesは、そのページが属しているキャッシュディスクリプタ/スラブディスクリプタを設定、ページディスクリプタに設定しています。従ってページディスクリプタからこのページがスラブとして使われいるのかどうか、また使われているならどのようなスラブとして使われているかが分かると言うことです。
cache_init_objsはスラブ内のオブジェクトを割り当てる際に、初期化処理としてコントラク登録されていればそれを実行と、オブジェクトディスクリプタの設定を行います。
以上でスラブができました。後はlist_add_tail(&slabp->list, &(l3->slabs_free))で、空きスラブリストに登録し、l3->free_objects += cachep->numで空きオブジェクト数を加算して終了です。
スラブ内に持つ場合、そのアドレスは色付けする必要があります。objpはスラブの先頭アドレスで、colour_offは移動するサイズです。従ってslabp = objp + colour_offで、このスラブの色付けした先頭アドレスとなります。colour_off += cachep->slab_sizeはオブジェクトディスクリプタの領域です。ディスクリプタは unsigend intです。
最後にスラブディスクリプタの各メンバを設定しています。slabp->s_memは最初のオブジェクトのアドレスです。なお、slabp->free = 0となっているのは、実際のオブジェクトはオブジェクトディスクリプタを設定したのち有効となるためです。
そして最後のインデックスはslab_bufctl(slabp)[i - 1] = BUFCTL_ENDとして終了のコードを設定しています。
スラブの確保
概略はバディシステムから必要とされるページを確保し、そのメモリーをスラブとして利用する旨の情報をページディスクリプタに設定し、そのメモリ空間を所定のオブジェクトを持つスラブとするようにスラブディスクリプタ等の設定をしたのち、キャッシュディスクリプタの完全未使用スラブリストに登録するというものです。
引数のstruct kmem_cache下のスラブリストから、offset = l3->colour_nextで色付け情報を取得し、次回の取得のためl3->colour_next++とし、ただしそれがif (l3->colour_next >= cachep->colour)でカラー数を越えていたら、l3->colour_next = 0としています。
そして、offset *= cachep->colour_offで、ずらすサイズをoffsetに設定します。cachep->colour_offはアライメントサイズ(通常4バイトです。たぶん)です。
次にkmem_getpagesでバディシステムからページを取得し、alloc_slabmgmtでそのメモリー空間に、スラブディスクリプタ等の設定を施し、slab_map_pagesは、そのページが属しているキャッシュディスクリプタ/スラブディスクリプタを設定、ページディスクリプタに設定しています。従ってページディスクリプタからこのページがスラブとして使われいるのかどうか、また使われているならどのようなスラブとして使われているかが分かると言うことです。
cache_init_objsはスラブ内のオブジェクトを割り当てる際に、初期化処理としてコントラク登録されていればそれを実行と、オブジェクトディスクリプタの設定を行います。
以上でスラブができました。後はlist_add_tail(&slabp->list, &(l3->slabs_free))で、空きスラブリストに登録し、l3->free_objects += cachep->numで空きオブジェクト数を加算して終了です。
static int cache_grow(struct kmem_cache *cachep, gfp_t flags, int nodeid, void *objp) { struct slab *slabp; size_t offset; gfp_t local_flags; struct kmem_list3 *l3; local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK); check_irq_off(); l3 = cachep->nodelists[nodeid]; spin_lock(&l3->list_lock);ACL linux offset = l3->colour_next; l3->colour_next++; if (l3->colour_next >= cachep->colour) l3->colour_next = 0; spin_unlock(&l3->list_lock); offset *= cachep->colour_off; if (local_flags & __GFP_WAIT) local_irq_enable(); kmem_flagcheck(cachep, flags); if (!objp) objp = kmem_getpages(cachep, ACL linuxlocal_flags, nodeid); if (!objp) goto failed; slabp = alloc_slabmgmt(cachep, objp, offset, local_flags & ~GFP_CONSTRAINT_MASK, nodeid); if (!slabp) goto opps1; slab_map_pages(cachep, slabp, objp); cache_init_objs(cachep, slabp); if (local_flags & __GFP_WAIT) local_irq_disable(); check_irq_off(); spin_lock(&l3->list_lock); list_add_tail(&slabp->list, &(l3->slabs_free)); STATS_INC_GROWN(cachep); l3->free_objects += cachep->num; spin_unlock(&l3->list_lock); return 1; opps1: kmem_freepages(cachep, objp); failed: if (local_flags & __GFP_WAIT) local_irq_disable(); return 0; }alloc_slabmgmtでは割り当てたメモリに、スラブディスクリプタを施します。OFF_SLABマクロでstruct kmem_cache->flagsにより、スラブディスクリプタをスラブ内に持つか、スラブ外に持つか判断します。
スラブ内に持つ場合、そのアドレスは色付けする必要があります。objpはスラブの先頭アドレスで、colour_offは移動するサイズです。従ってslabp = objp + colour_offで、このスラブの色付けした先頭アドレスとなります。colour_off += cachep->slab_sizeはオブジェクトディスクリプタの領域です。ディスクリプタは unsigend intです。
最後にスラブディスクリプタの各メンバを設定しています。slabp->s_memは最初のオブジェクトのアドレスです。なお、slabp->free = 0となっているのは、実際のオブジェクトはオブジェクトディスクリプタを設定したのち有効となるためです。
static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp, int colour_off, gfp_t local_flags, int nodeid) { struct slab *slabp; if (OFF_SLAB(cachep)) { slabp = kmem_cache_alloc_node(cachep->slabp_cache, local_flags & ~GFP_THISNODE, nodeid); if (!slabp) return NULL;ACL linux } else { slabp = objp + colour_off; colour_off += cachep->slab_size; } slabp->inuse = 0; slabp->colouroff = colour_off; slabp->s_mem = objp + colour_off; slabp->nodeid = nodeid; slabp->free = 0; return slabp; } #define OFF_SLAB(x) ((x)->flags & CFLGS_OFF_SLAB)slab_map_pagesでスラブとして使用する全ページに対して、page->lru.next = (struct list_head *)cacheでそのstruct kmem_cacheを、page->lru.prev = (struct list_head *)slabでstruct slabを施します。
static void slab_map_pages(struct kmem_cache *cache, struct slab *slab, void *addr) { int nr_pages; struct page *page; page = virt_to_page(addr); nr_pages = 1; if (likely(!PageCompound(page))) nr_pages <<= cache->gfporder; do { page_set_cache(page, cache); page_set_slab(page, slab); page++; } while (--nr_pages); } static inline void page_set_cache(struct page *page, struct kmem_cache *cache) { page->lru.next = (struct list_head *)cache; } static inline void page_set_slab(struct page *page, struct slab *slab) { page->lru.prev = (struct list_head *)slab; }cache_init_objsでスラブ内に実際のオブジェクトを配置します。for (i = 0; i < cachep->num; i++)で配置するオブジェクトの数で、コントラクタが設定されていればすべてのオブジェクトに対して機動します。そしてslab_bufctl(slabp)[i] = i + 1でオブジェクトディスクリプタを設定します。 +1 としているところに注目する必要があります。このディスクリプタは次の空きオブジェクトのインデックスを保持している空です。
そして最後のインデックスはslab_bufctl(slabp)[i - 1] = BUFCTL_ENDとして終了のコードを設定しています。
static void cache_init_objs(struct kmem_cache *cachep, struct slab *slabp) { int i; for (i = 0; i < cachep->num; i++) { void *objp = index_to_obj(cachep, slabp, i); if (cachep->ctor) cachep->ctor(objp); slab_bufctl(slabp)[i] = i + 1; } slab_bufctl(slabp)[i - 1] = BUFCTL_END; }