linux arm的高階記憶體對映(1) vmalloc
高階記憶體對映
與高階對映對立的是低端對映或所謂直接對映,核心中有關變數定義它們的它們的分界點,全域性變數high_memory,該變數定義在mm/memory.c檔案中(存在MMU的前提下),可見不區分體系結構,對於當前我手頭的marvell的arm裝置即對於arm體系結構,high_memory在初始化階段的建立記憶體頁表時初始化值,它的值就是:實體記憶體最後一個node的末尾,比如實體記憶體只有一個node,大小是256MB,再根據如下的演算法就可以得出high_memory是多少:
high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1;
max_low代表的是當前node的在實體記憶體中的物理頁地址,比如實體記憶體從0x0開始(由PHYS_OFFSET決定),大小是256MB,即65536(0x10000)個物理頁,那麼max_low的值為0x10000,則high_memory的值為該物理頁地址轉為實體地址再轉為虛擬地址的結果:0xd0000000。
high_memory之上就是高階記憶體的範圍,這樣的說法也不一定對,比如對於有的體系結構如arm,它的永久對映實際上就在high_memory之下的地方,但它依然是高階記憶體,所有實體記憶體都在初始化時對映在低端空間也是不一定正確的(這個可以在初始化時記憶體對映中發現,哪樣的實體記憶體是會屬於HIGHMEM區),所以我想通常意義的高階記憶體可以基本上定義為“不能直接通過偏移實現虛擬地址和實體地址對映”的虛擬空間、而“可以直接通過偏移實現虛擬地址和實體地址對映”的虛擬空間是低端記憶體(為什麼低端對映也叫直接對映,這裡體現出了直接的感覺)這樣的方式界定比較好一些
大體上核心有3種高階對映的機制,分別是永久對映、臨時對映、非連續記憶體分配對映,
一、非連續記憶體分配(vmalloc):
1.1、vmalloc原理:
前面的文章細緻描述過核心是如何通過slab/buddy獲取大大小小的連續的實體記憶體的,它們是linux在低端虛擬地址空間的分配機制,使用連續實體記憶體是有很大好處的,對於充分利用CPU的cache有極大的利好;此外buddy和slab分別盡力的避免了實體記憶體空間的外碎片和內碎片;
只是有些時候比如我們的實體記憶體本身就不大,隨著執行時間增長,實體記憶體的碎片還是可能會越來越多,分配連續的實體記憶體尤其是大尺寸連續的實體記憶體將越來越費勁;
為了儘可能避免這種情況或者在出現這種情況下能夠緩解進一步費勁,對於某些不頻繁的分配釋放的記憶體申請,可以採用一種方式,即所謂的不連續記憶體分配如下圖:
PAGE_OFFSET |
high_memory |
VMALLOC_START |
Vmalloc 1 |
4KB隔離帶 |
Vmalloc 2 |
4KB隔離帶 |
Vmalloc 3 |
4KB隔離帶 |
…….. |
VMALLOC_END |
PKMAP_BASE |
8MB隔離帶 |
Vmalloc N |
8KB隔離帶 |
低端(物理)記憶體對映 |
FIXADDR_START |
永久對映區 |
臨時對映區 |
通過上圖,不連續記憶體對映空間即vmalloc區在4GB地址空間的什麼位置應該很清楚了,現在描述下所謂不連續記憶體對映空間是什麼意思:
不連續記憶體對映,不連續是指實體記憶體不連續,後面描述它的具體實現就能很清楚的發現它為什麼是不連續,這裡直接知道結果就是它對映實體地址的方式是一頁一頁對映的,比如需要對映10頁實體地址空間,那麼它是10次呼叫alloc_page從buddy獲取,這樣就會存在實體地址不連續的可能,之所以這樣做就是如上面所說,某些東西申請記憶體後不會頻繁的訪問,並且它的長度如果不是很大的話,那麼它適合vmalloc,因為分配一個連續的很大的實體地址是越來越困難的;
但是它的虛擬地址是連續的,如上面的圖所示,每一個vmalloc X就是一個vmalloc對映,顯然虛擬地址是連續的,只是它對映的實體地址是不一定連續的;
看到這裡,就會明白為什麼vmalloc需要增加記憶體頁表的表項了,這都是初始化時沒有的對映關係,而且確切的說是需要二級對映的,因為虛擬地址和實體地址之間都是一頁一頁對映的,可見vmalloc申請起來比較麻煩;另外,虛擬地址和實體地址之間都是一頁一頁對映,說明最好在申請的時候也按頁的整數倍即對齊值申請。
那麼,巨集VMALLOC_START和VMALLOC_END是怎麼定義的呢?如下:
#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_END (PAGE_OFFSET + 0x30000000)
即vmalloc區始於high_memory加8MB的位置,結束於一個固定位置為0xF0000000;
最後看看到底哪些場合使用vmalloc:
1、swap;2、為module分配空間,見函式module_alloc就明白了;3、為IO驅動程式分配緩衝區(ioremap);
1.2、vmalloc實現:
1、介面函式:
介面函式就是vmalloc和__vmalloc,它倆的區別就是vmalloc只需指定長度即可,而__vmalloc是由呼叫者自行指定gfpmask和prot引數,prot引數一般都是PAGE_KERNEL,gfpmask引數一般就是GFP_KERNEL | __GFP_HIGHMEM,呼叫__vmalloc可能會加上標誌GFP_ZERO用於清零,它們都呼叫函式__vmalloc_node;此外還有個用於只申請一段vmalloc區間但不對映實體地址的介面函式get_vm_area;
2、具體實現:
static void *__vmalloc_node(unsigned long size, unsigned long align,
gfp_t gfp_mask, pgprot_t prot,
int node, void *caller)
{
struct vm_struct *area;
void *addr;
unsigned long real_size = size;
/*size 頁對齊,因為vmalloc對映的實體記憶體不連續,所以是一頁一頁的對映,
即對映的實體記憶體大小必然是頁的倍數,所以必須頁對齊*/
size = PAGE_ALIGN(size);
/*檢查size正確性,不能為0且不能大於totalram_pages,
totalram_pages是bootmem分配器移交給夥伴系統的實體記憶體頁數總和*/
if (!size || (size >> PAGE_SHIFT) > totalram_pages)
return NULL;
/*申請一個vm_struct插入vmlist連結串列,申請一個vmap_area並插入紅黑樹
完成非連續記憶體區的高階虛擬地址分配,注意size總會額外在最後加一頁,用於安全區(上圖的4KB隔離帶)
注意: vm_struct本身是使用kmalloc_node()在slab,所以在低端記憶體中;
而函式alloc_vmap_area真正分配了連續的高階虛擬地址
簡單的總結: 分配一個vm_struct結構,獲取對應長度(注意額外加一頁)高階連續地址,最終插入vmlist連結串列*/
area = __get_vm_area_node(size, align, VM_ALLOC, VMALLOC_START,
VMALLOC_END, node, gfp_mask, caller);
if (!area)
return NULL;
/*本函式實際的給虛擬地址映射了不連續的實體記憶體(呼叫函式alloc_page一頁一頁的分配實體地址,函式map_vm_area實現對映)
返回值是分配的高階虛擬地址的起始*/
addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);
/*
* A ref_count = 3 is needed because the vm_struct and vmap_area
* structures allocated in the __get_vm_area_node() function contain
* references to the virtual address of the vmalloc'ed block.
*/
kmemleak_alloc(addr, real_size, 3, gfp_mask);
/*返回值是分配的高階虛擬地址的起始*/
return addr;
}
主要就是兩大部分:分配高階虛擬地址(即分配一段vmalloc區間) + 給虛擬地址對映實體地址;
1.2.1、高階地址分配:
進入函式__get_vm_area_node:
static struct vm_struct *__get_vm_area_node(unsigned long size,
unsigned long align, unsigned long flags, unsigned long start,
unsigned long end, int node, gfp_t gfp_mask, void *caller)
{
static struct vmap_area *va;
struct vm_struct *area;
BUG_ON(in_interrupt());
if (flags & VM_IOREMAP) {
int bit = fls(size);
if (bit > IOREMAP_MAX_ORDER)
bit = IOREMAP_MAX_ORDER;
else if (bit < PAGE_SHIFT)
bit = PAGE_SHIFT;
align = 1ul << bit;
}
size = PAGE_ALIGN(size);
if (unlikely(!size))
return NULL;
/*申請一個vm_struct,本質還是通過kmalloc申請,申請的是高階的虛擬記憶體
kmalloc可保證虛擬記憶體的連續性,這驗證了vmalloc申請的虛擬地址是連續的
本質就是: 使用kmalloc_node()在slab中,分配一個vm_struct結構*/
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
if (unlikely(!area))
return NULL;
/*
* We always allocate a guard page.
*/
/*vmalloc總是要將size加上一個頁框的大小作為安全區*/
size += PAGE_SIZE;
/*在start到end中,分配足夠size大小的核心虛擬空間*/
/*注意: vmap_area結構體(返回值va)本身也是通過kmalloc分配,所以也在低端記憶體中,
它的成員va_start和va_end指示了真正申請的高階虛擬記憶體的地址範圍,可見是線性的(連續的)
[va_start---va_end]落在高階記憶體的非連續對映區(vmalloc區)中,va_end - va_start = size = 實際需要對映長度 + 4KB(安全區)
尋找新節點在紅黑樹的插入點並計算出應該的高階地址值(addr),關於紅黑樹,細節暫不討論留在後續
將最終的高階地址值賦給va,並插入紅黑樹中*/
va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
if (IS_ERR(va)) {
kfree(area);
return NULL;
}
/*將va的值(高階地址起始和長度)賦給area,最終把area插入vmlist連結串列*/
insert_vmalloc_vm(area, va, flags, caller);
/*這裡area已經被賦值的成員有,addr和size(高階地址)、flag、caller*/
return area;
}
首先注意結構體vm_struct,它是vmalloc的管理方法非常重要:
struct vm_struct {
struct vm_struct *next; /*指向下一個vm區域*/
void *addr; /*指向第一個記憶體單元(線性地址)*/
unsigned long size; /*該塊記憶體區的大小*/
unsigned long flags; /*記憶體型別的標識欄位*/
struct page **pages; /*指向頁描述符指標陣列*/
unsigned int nr_pages; /*記憶體區大小對應的頁框數*/
unsigned long phys_addr; /*用來對映硬體裝置的IO共享記憶體,其他情況下為0*/
void *caller; /*呼叫vmalloc類的函式的返回地址*/
};
全域性變數vmlist是管理所有vmalloc物件的連結串列表頭,每個vmalloc對映都要把它的對映結果即一個struct vm_struct型的描述符加入連結串列中,成員next用於這個連結串列;addr指示這段vmalloc區的虛擬地址起始;size標識這段vmalloc區的長度;flags標識對映方式,在include/linux/vmalloc.h檔案中有明確的使用方式,像在__vmalloc_node呼叫就是VM_ALLOC:
#define VM_IOREMAP 0x00000001 /* ioremap() and friends */
#define VM_ALLOC 0x00000002 /* vmalloc() */
#define VM_MAP 0x00000004 /* vmap()ed pages */
#define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */
#define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */
成員pages是一個數組,每個成員都是所對映的物理頁的page描述符地址;nr_pages標識所對映的物理頁,注意它不包括一頁的隔離帶;phys_addr用來對映硬體裝置的IO共享記憶體,其他情況下為0;caller是呼叫vmalloc類的函式的返回地址,它是用於除錯和找問題的比如可以通過proc下的vmallocinfo看是哪個函式在申請高階虛擬記憶體;
對struct vm_struct有了個大概認識後可以具體看原始碼,首先通過kmalloc申請一個struct vm_struct結構變數area,注意這個結構變數本身是在低端連續記憶體區,然後呼叫函式alloc_vmap_area,這個比較重要如下:
static struct vmap_area *alloc_vmap_area(unsigned long size,
unsigned long align,
unsigned long vstart, unsigned long vend,
int node, gfp_t gfp_mask)
{
struct vmap_area *va;
struct rb_node *n;
unsigned long addr;
int purged = 0;
BUG_ON(!size);
BUG_ON(size & ~PAGE_MASK);
/*vmap_area結構體本身也是通過kmalloc分配,所以也在低端記憶體中*/
va = kmalloc_node(sizeof(struct vmap_area),
gfp_mask & GFP_RECLAIM_MASK, node);
if (unlikely(!va))
return ERR_PTR(-ENOMEM);
/*下面是尋找新節點在紅黑樹的插入點並計算出應該的高階地址值(addr),關於紅黑樹,細節暫不討論留在後續*/
retry:
addr = ALIGN(vstart, align);
spin_lock(&vmap_area_lock);
if (addr + size - 1 < addr)
goto overflow;
/* XXX: could have a last_hole cache */
n = vmap_area_root.rb_node;
if (n) {
struct vmap_area *first = NULL;
do {
struct vmap_area *tmp;
tmp = rb_entry(n, struct vmap_area, rb_node);
if (tmp->va_end >= addr) {
if (!first && tmp->va_start < addr + size)
first = tmp;
n = n->rb_left;
} else {
first = tmp;
n = n->rb_right;
}
} while (n);
if (!first)
goto found;
if (first->va_end < addr) {
n = rb_next(&first->rb_node);
if (n)
first = rb_entry(n, struct vmap_area, rb_node);
else
goto found;
}
while (addr + size > first->va_start && addr + size <= vend) {
addr = ALIGN(first->va_end + PAGE_SIZE, align);
if (addr + size - 1 < addr)
goto overflow;
n = rb_next(&first->rb_node);
if (n)
first = rb_entry(n, struct vmap_area, rb_node);
else
goto found;
}
}
found:
if (addr + size > vend) {
overflow:
spin_unlock(&vmap_area_lock);
if (!purged) {
purge_vmap_area_lazy();
purged = 1;
goto retry;
}
if (printk_ratelimit())
printk(KERN_WARNING
"vmap allocation for size %lu failed: "
"use vmalloc=<size> to increase size.\n", size);
kfree(va);
return ERR_PTR(-EBUSY);
}
BUG_ON(addr & (align-1));
/*將最終的高階地址值賦給va,並插入紅黑樹中*/
va->va_start = addr;
va->va_end = addr + size;
va->flags = 0;
__insert_vmap_area(va);
spin_unlock(&vmap_area_lock);
return va;
}
這個函式alloc_vmap_area作用就是根據所要申請的高階地址的長度size(注意這裡的size已經是加上一頁隔離帶的size),在vmalloc區找到一個合適的區間並把起始虛擬地址和結尾地址通知給核心,具體說來還包括struct vmap_area的問題,它是實際維護vmalloc資訊的資料結構,比較複雜,linux核心維護vmalloc資訊是通過紅黑樹演算法(一種特殊的平衡二叉樹,增刪查改效率高)實現,這個東西比較麻煩一些,後續專門討論它,但不瞭解它不影響對vmalloc管理的分析,這裡知道alloc_vmap_area函式的最終作用是得到被分配的高階虛擬地址起始和結尾地址即可;
然後最終呼叫函式insert_vmalloc_vm把這個vm_struct結構變數加入vmlist連結串列,原始碼不貼了就。
1.2.2、分配和對映實體地址:
這時還沒有給這個vmalloc區分配和對映實體地址,現在呼叫函式__vmalloc_area_node:
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
pgprot_t prot, int node, void *caller)
{
struct page **pages;
unsigned int nr_pages, array_size, i;
/*得到實際需要對映的頁數(減去一頁的安全區)*/
nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
/*並得到所需的空間(頁數*page結構長度)*/
array_size = (nr_pages * sizeof(struct page *));
area->nr_pages = nr_pages;
/* Please note that the recursion is strictly bounded. */
/*不僅要對映的高階地址通過__get_vm_area_node分配高階地址,
提供對映的頁指標也在高階地址分配,不足一頁的話在低端地址中分配*/
if (array_size > PAGE_SIZE) {
pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,
PAGE_KERNEL, node, caller);
area->flags |= VM_VPAGES;
} else {
pages = kmalloc_node(array_size,
(gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,
node);
}
/*將對映用的頁表pages在分配到高階(不足一頁在低端)地址後,賦給area*/
area->pages = pages;
area->caller = caller;
if (!area->pages) {
remove_vm_area(area->addr);
kfree(area);
return NULL;
}
/*從夥伴系統中進行實體記憶體頁面的分配,注意是為每一個頁面分配空間*/
for (i = 0; i < area->nr_pages; i++) {
struct page *page;
/*UMA系統*/
if (node < 0)
page = alloc_page(gfp_mask);
/*NUMA系統*/
else
page = alloc_pages_node(node, gfp_mask, 0);
if (unlikely(!page)) {
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
goto fail;
}
/*將頁表pages裡的內容填充,填充的是一個一個的物理頁地址*/
area->pages[i] = page;
}
/*area的addr和size代表了要對映的高階地址,pages裡填充了實際被對映的物理頁地址
接下來完成虛擬地址到實體地址的對映,注意最終是要建立二級對映(二級頁表空間需從buddy申請,大小為1頁)*/
if (map_vm_area(area, prot, &pages))
goto fail;
return area->addr;
fail:
vfree(area->addr);
return NULL;
}
首先實際需要對映的頁數(注意不包含一頁的隔離帶),計算這個的目的是到頁表所需的空間(頁數*page結構長度),確切的說是二級頁表所需的空間(從之前的文章可知道,二級對映的頁表是動態建立的,一級頁表即段頁表是常駐記憶體),注意如果這個二級頁表它所佔的空間超出一頁長度,那麼也在vmalloc區裡分配它,否則就在低端連續區分配即可;另外從程式設計角度看,這裡遞迴了一下函式__vmalloc_node;
把二級頁表地址和呼叫者成員賦值給area,接下來就開始分配物理空間了,這裡可以清楚的看到是使用for迴圈一頁一頁分配的,這就是為什麼實體地址不一定連續的原因;另外要注意,把這些物理頁地址依次寫進了二級頁表表項,即“area->pages[i] = page”;
最後一個操作就是呼叫函式map_vm_area把分配的實體地址和高階虛擬地址做對映,
int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)
{
unsigned long addr = (unsigned long)area->addr;
unsigned long end = addr + area->size - PAGE_SIZE;
int err;
/*start和end代表了要對映的高階地址,pages裡填充了實際被對映的物理頁地址
注意最終是要在核心頁表中建立二級對映*/
err = vmap_page_range(addr, end, prot, *pages);
if (err > 0) {
*pages += err;
err = 0;
}
return err;
}
注意都是二級對映,這裡涉及了記憶體頁表知識可以看之前的描述記憶體頁表的那篇文章,arm的MMU只有二級對映,本函式前期基本相當於空跑即跳過linux的pud、pmd,直到函式vmap_pte_range開始建立二級對映;
1.3、vmalloc在proc:
在proc下有個vmallocinfo,可以檢視當前vmalloc資訊,就是通過上面說的vmlist連結串列檢視每一個正在對映的vmalloc,打印出虛擬起始地址、虛擬結尾地址、對映長度、呼叫者、對映頁數、對映方式,非常簡單原始碼如下:
static int s_show(struct seq_file *m, void *p)
{
struct vm_struct *v = p;
seq_printf(m, "0x%p-0x%p %7ld",
v->addr, v->addr + v->size, v->size);
if (v->caller) {
char buff[KSYM_SYMBOL_LEN];
seq_putc(m, ' ');
sprint_symbol(buff, (unsigned long)v->caller);
seq_puts(m, buff);
}
if (v->nr_pages)
seq_printf(m, " pages=%d", v->nr_pages);
if (v->phys_addr)
seq_printf(m, " phys=%lx", v->phys_addr);
if (v->flags & VM_IOREMAP)
seq_printf(m, " ioremap");
if (v->flags & VM_ALLOC)
seq_printf(m, " vmalloc");
if (v->flags & VM_MAP)
seq_printf(m, " vmap");
if (v->flags & VM_USERMAP)
seq_printf(m, " user");
if (v->flags & VM_VPAGES)
seq_printf(m, " vpages");
show_numa_info(m, v);
seq_putc(m, '\n');
return 0;
}
比如我的當前列印如下:
/ # cat proc/vmallocinfo
0xbf000000-0xbf0b3000 733184 module_alloc+0x54/0x60 pages=178 vmalloc
0xd085e000-0xd0860000 8192 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd0861000-0xd0882000 135168 ubi_attach_mtd_dev+0x390/0x9c8 pages=32 vmalloc
0xd0883000-0xd08a4000 135168 ubi_attach_mtd_dev+0x3b0/0x9c8 pages=32 vmalloc
0xd08a5000-0xd08ac000 28672 ubi_read_volume_table+0x178/0x8cc pages=6 vmalloc
0xd08b6000-0xd08b8000 8192 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd08ba000-0xd08bc000 8192 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd08bd000-0xd08ce000 69632 lzo_init+0x18/0x30 pages=16 vmalloc
0xd08cf000-0xd0912000 274432 deflate_init+0x1c/0xe8 pages=66 vmalloc
0xd0913000-0xd0934000 135168 ubifs_get_sb+0x79c/0x1104 pages=32 vmalloc
0xd0935000-0xd0937000 8192 ubifs_lpt_init+0x30/0x428 pages=1 vmalloc
0xd095d000-0xd095f000 8192 ubifs_lpt_init+0x30/0x428 pages=1 vmalloc
0xd0960000-0xd0965000 20480 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd0966000-0xd0987000 135168 ubi_attach_mtd_dev+0x390/0x9c8 pages=32 vmalloc
0xd0988000-0xd09a9000 135168 ubi_attach_mtd_dev+0x3b0/0x9c8 pages=32 vmalloc
0xd09aa000-0xd09b1000 28672 ubi_read_volume_table+0x178/0x8cc pages=6 vmalloc
0xd09ba000-0xd09db000 135168 ubifs_get_sb+0x79c/0x1104 pages=32 vmalloc
0xd09dc000-0xd09fd000 135168 ubifs_get_sb+0x7b8/0x1104 pages=32 vmalloc
0xd0a00000-0xd0b01000 1052672 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd0bd0000-0xd0bd2000 8192 ubifs_lpt_init+0x220/0x428 pages=1 vmalloc
0xd0bd3000-0xd0bf4000 135168 ubifs_lpt_init+0x234/0x428 pages=32 vmalloc
0xd0bf5000-0xd0bf8000 12288 tpm_db_mod2_setup_jump_area+0x84/0x3cc pages=2 vmalloc
0xd0bf9000-0xd0bfb000 8192 tpm_db_mod2_setup_jump_area+0x100/0x3cc pages=1 vmalloc
0xd0bfc000-0xd0bfe000 8192 tpm_db_mod2_setup_jump_area+0x174/0x3cc pages=1 vmalloc
0xd0c00000-0xd0d01000 1052672 __arm_ioremap_pfn+0x64/0x144 ioremap
0xd0d24000-0xd0d45000 135168 ubifs_mount_orphans+0x44/0x41c pages=32 vmalloc
0xd0d46000-0xd0d48000 8192 tpm_db_mod2_setup_jump_area+0x1f4/0x3cc pages=1 vmalloc
0xd0d49000-0xd0d4b000 8192 tpm_db_mod2_setup_jump_area+0x270/0x3cc pages=1 vmalloc
0xd0d4c000-0xd0d4e000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d4f000-0xd0d51000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d52000-0xd0d54000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d55000-0xd0d57000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d58000-0xd0d5a000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d5b000-0xd0d5d000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d5e000-0xd0d60000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d61000-0xd0d63000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d64000-0xd0d66000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d67000-0xd0d69000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d6a000-0xd0d6c000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d6d000-0xd0d6f000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d70000-0xd0d72000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d73000-0xd0d75000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d76000-0xd0d78000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d79000-0xd0d7b000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d7c000-0xd0d7e000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d7f000-0xd0d81000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d82000-0xd0d84000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d85000-0xd0d87000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d88000-0xd0d8a000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d8b000-0xd0d8d000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d8e000-0xd0d90000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d91000-0xd0d93000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d94000-0xd0d96000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0d97000-0xd0d99000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0d9a000-0xd0d9c000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0d9d000-0xd0db0000 77824 tpm_db_mod2_setup_chain_area+0x264/0x308 pages=18 vmalloc
0xd0db1000-0xd0db4000 12288 tpm_db_mod2_setup_jump_area+0x84/0x3cc pages=2 vmalloc
0xd0db5000-0xd0db7000 8192 tpm_db_mod2_setup_jump_area+0x100/0x3cc pages=1 vmalloc
0xd0db8000-0xd0dba000 8192 tpm_db_mod2_setup_jump_area+0x174/0x3cc pages=1 vmalloc
0xd0dbb000-0xd0dbd000 8192 tpm_db_mod2_setup_jump_area+0x1f4/0x3cc pages=1 vmalloc
0xd0dbe000-0xd0dc0000 8192 tpm_db_mod2_setup_jump_area+0x270/0x3cc pages=1 vmalloc
0xd0dc1000-0xd0dc3000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0dc4000-0xd0dc6000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0dc7000-0xd0dc9000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0dca000-0xd0dcc000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0dcd000-0xd0dcf000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0dd0000-0xd0dd2000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0dd3000-0xd0dd5000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0dd6000-0xd0dd8000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0dd9000-0xd0ddb000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0ddc000-0xd0dde000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0ddf000-0xd0de1000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0de2000-0xd0de4000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0de5000-0xd0de7000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc
0xd0de8000-0xd0dea000 8192 tpm_db_mod2_setup_chain_area+0x150/0x308 pages=1 vmalloc
0xd0deb000-0xd0ded000 8192 tpm_db_mod2_setup_chain_area+0x1b0/0x308 pages=1 vmalloc
0xd0dee000-0xd0df0000 8192 tpm_db_mod2_setup_chain_area+0xd4/0x308 pages=1 vmalloc