読者です 読者をやめる 読者になる 読者になる

kmalloc()めも

linux kernel

c⌒っ゚д゚)っφ メモメモ...
kzalloc()を見てて気づいたことをメモ程度に残しとこう。

kzalloc()はこんな感じでkmalloc()を呼び出すだけです。

 344/**
 345 * kzalloc - allocate memory. The memory is set to zero.
 346 * @size: how many bytes of memory are required.
 347 * @flags: the type of memory to allocate (see kmalloc).
 348 */
 349static inline void *kzalloc(size_t size, gfp_t flags)
 350{
 351        return kmalloc(size, flags | __GFP_ZERO);
 352}

kzalloc()はフラグに__GFP_ZEROを追加してkmalloc()を呼び出すというのはそうですねってだけですが、
では、kmalloc()はというとこうなってます。

 128static __always_inline void *kmalloc(size_t size, gfp_t flags)
 129{
 130        struct kmem_cache *cachep;
 131        void *ret;
 132
 133        if (__builtin_constant_p(size)) {
 134                int i = 0;
 135
 136                if (!size)
 137                        return ZERO_SIZE_PTR;
 138
 139#define CACHE(x) \
 140                if (size <= x) \
 141                        goto found; \
 142                else \
 143                        i++;
 144#include <linux/kmalloc_sizes.h>
 145#undef CACHE
 146                return NULL;
 147found:
 148#ifdef CONFIG_ZONE_DMA
 149                if (flags & GFP_DMA)
 150                        cachep = malloc_sizes[i].cs_dmacachep;
 151                else
 152#endif
 153                        cachep = malloc_sizes[i].cs_cachep;
 154
 155                ret = kmem_cache_alloc_trace(size, cachep, flags);
 156
 157                return ret;
 158        }
 159        return __kmalloc(size, flags);
 160}

__always_inlineがついているから常にinline化されるんですね。さて、kmalloc()で一番最初のif文ですが、__builtin_constant_p()はgccのビルトイン関数でこれは__builtin_constant_p()に渡した引数が定数かどうかをチェックしてます。コンパイル時に取得したいメモリのサイズが分かっているかどうかで条件分けているということですね。
その後はサイズが0だった場合のチェックが136行目にあって、次からは配列malloc_sizesにアクセスするためのインデックスを計算してます。これは144行目でインクルードしているkmalloc_sizes.hで実現してます。これでsizeに応じて最適なキャッシュサイズのインデックスを求めて、kmem_cache_alloc_trace()でメモリの確保って流れですね。
そして、kmem_cache_alloc_trace(これはCONFIG_TRACINGが定義されている場合のほうです)はこのような感じになっています()

3776void *
3777kmem_cache_alloc_trace(size_t size, struct kmem_cache *cachep, gfp_t flags)
3778{
3779        void *ret;
3780
3781        ret = __cache_alloc(cachep, flags, __builtin_return_address(0));
3782
3783        trace_kmalloc(_RET_IP_, ret,
3784                      size, slab_buffer_size(cachep), flags);
3785        return ret;
3786}
3787EXPORT_SYMBOL(kmem_cache_alloc_trace);

__cache_alloc()でメモリ確保して、trace_kmalloc()でトレースをしておくんですね。trace_kmalloc()はkmemleakで使うためだと思います。

さて、コンパイル時点でsizeが決定できない場合は__kmalloc()が呼ばれます。
これはmm/slab.cにいます。

3886void *__kmalloc(size_t size, gfp_t flags)
3887{
3888        return __do_kmalloc(size, flags, __builtin_return_address(0));
3889}

そして、__do_kmalloc()が呼ばれます。

3862static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
3863                                          void *caller)
3864{
3865        struct kmem_cache *cachep;
3866        void *ret;
3867
3868        /* If you want to save a few bytes .text space: replace
3869         * __ with kmem_.
3870         * Then kmalloc uses the uninlined functions instead of the inline
3871         * functions.
3872         */
3873        cachep = __find_general_cachep(size, flags);
3874        if (unlikely(ZERO_OR_NULL_PTR(cachep)))
3875                return cachep;
3876        ret = __cache_alloc(cachep, flags, caller);
3877
3878        trace_kmalloc((unsigned long) caller, ret,
3879                      size, cachep->buffer_size, flags);
3880
3881        return ret;
3882}
3883

これもsizeから適切なキャッシュを探してメモリを確保しています。その後の流れは__cache_alloc()->trace_kmalloc()とkmem_cache_alloc_trace()と同じですね。
ということで、kmalloc()はコンパイル時にメモリ確保する量が分かっている場合は必要なキャッシュのサイズもコンパイル時点でわかるので直接em_cache_alloc_trace()を呼び、コンパイル時点でsizeが未定の場合は、__kmalloc()を呼んでキャッシュの大きさを調べてからメモリ確保するようになっているということですね( ..)φメモメモ