スラブオブジェクトからサイズを取得するには(メモ)

kobjsize()

スラブオブジェクトのアドレス objp から,オブジェクトサイズを取得する.

kobjsize(スラブオブジェクトのアドレスobjp) 
↓
ksize(objp)
↓
return obj_size(virt_to_cache(objp))

virt_to_cache()

スラブオブジェクトのアドレス objp から キャッシュディスクリプタ kmem_cache を取得する.
kmem_cache は slab.h で定義され,slab_size,buffer_size メンバを持つ.obj_size() は buffer_size を返す.
この二つの違いは後で押さえる.

virt_to_cache(objp)
↓
struct page *page = virt_to_head_page(objp)
↓
return page_get_cache(page)

virt_to_head_page()

virt_to_head_page(objp)
↓
struct page *page = virt_to_page(objp)
↓
return compound_head(page);

virt_to_page() 重要

アドレスから,オブジェクトが属するページのページディスクリプタを取得する.
ページディスクリプタの取得には,ページの物理アドレスが必要.ここでオブジェクトのアドレスは仮想アドレスである.
x86でRAMのZONE_NORMAL(16MBから896MB)に対応する物理アドレスマッピングされた仮想アドレスの場合,リニアマッピングされているので,仮想アドレス-0xc0000000 で物理アドレスが取得できる.

virt_to_page(objp)
↓
pfn_to_page(__pa(objp) >> PAGE_SHIFT) 
→ARMの場合 #define __pa(x) __virt_to_phys((unsigned long)(x))
→x86の場合 #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)

__virt_to_phys() 重要

仮想アドレスから物理アドレスを得る.
ARMの場合,RAMが物理アドレスの0番地から始まるのではないため,RAMが開始するPHYS_OFFSETを物理アドレスに加算している.また,仮想アドレスはPAGE_OFFSET(大体の場合,3GB)以降にリニアマッピングされるため,それを減算している.こうやって仮想アドレスから物理アドレスを得る.

#define __virt_to_phys(x)       ((x) - PAGE_OFFSET + PHYS_OFFSET)

pfn_to_page() 重要

pfn=page frame number.ページの物理アドレスが分かると,アドレスをPAGE_SFHITすることでページフレーム番号も分かる.(CONFIG_FLATMEMの場合)ページディスクリプタはページディスクリプタ配列の先頭である mem_map と ページフレーム番号から取得できる.

#define __pfn_to_page(pfn)      (mem_map + ((pfn) - ARCH_PFN_OFFSET))

page_get_cache()

ページディスクリプタから,そのページの使用目的を表すキャッシュディスクリプタを得る.キャッシュ生成時に,page->lru.nextにセットされる(page_set_cache()).

return (struct kmem_cache *)page->lru.next;