diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /mm/kmempagerecorder.c | |
Diffstat (limited to 'mm/kmempagerecorder.c')
| -rw-r--r-- | mm/kmempagerecorder.c | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/mm/kmempagerecorder.c b/mm/kmempagerecorder.c new file mode 100644 index 00000000..20f42900 --- /dev/null +++ b/mm/kmempagerecorder.c @@ -0,0 +1,1101 @@ +#include <linux/init.h> +#include <linux/kmempagerecorder.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/mm.h> +#include <asm/fixmap.h> +#include <linux/highmem.h> +#include <linux/vmalloc.h> +#include <linux/irqflags.h> +#include <linux/spinlock.h> + +#include <linux/device.h> +#include <linux/file.h> +#include <linux/freezer.h> +#include <linux/fs.h> +#include <linux/anon_inodes.h> +#include <linux/kthread.h> +#include <linux/list.h> +#include <linux/memblock.h> +#include <linux/miscdevice.h> +#include <linux/export.h> +#include <linux/rbtree.h> +#include <linux/rtmutex.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/dma-buf.h> +#include <linux/kallsyms.h> +#include <linux/module.h> +#include <linux/stacktrace.h> +#define BACKTRACE_LEVEL 10 +#define DEBUG_DEFAULT_FLAGS 1 +/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ +#define GOLDEN_RATIO_PRIME_32 0x9e370001UL + +extern void *high_memory; +PageHashTable gPageHashTable; +PageObjectTable gKernelPageSymbolTable; +PageObjectTable gKernelPageBtTable; +static struct kmem_cache *page_cachep = NULL; +static unsigned int page_cache_created = false; + +static unsigned int Object_rank_max = 10; +static unsigned int queried_address = 0; +static unsigned int debug_log = 0; +static struct dentry *debug_root; +static unsigned int page_record_total = 0; +static unsigned int page_record_max = 0; +static unsigned int page_record_count = 0; +static unsigned int bt_record_total = 0; +static unsigned int bt_record_max = 0; + +/* init hash table mutex */ +unsigned int page_record_lock_init = 0; +unsigned int bt_record_lock_init = 0; +unsigned int symbol_record_lock_init = 0; +spinlock_t page_record_lock; +spinlock_t bt_record_lock; +spinlock_t symbol_record_lock; +int page_recorder_debug = DEBUG_DEFAULT_FLAGS; +unsigned int page_recorder_memory_usage = 0; +unsigned int page_recorder_limit = 524288; +static char page_recorder_debug_function; + +static int page_recorder_debug_show(struct seq_file *s, void *unused); +static inline unsigned int hash_32(unsigned int val, unsigned int bits); +static inline PageHashEntry *find_page_entry(void *page, int slot); +static char page_recorder_debug_function; + +void disable_page_alloc_tracer(void) +{ + page_recorder_debug = 0; +} + +static int page_recorder_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, page_recorder_debug_show, inode->i_private); +} + +static const struct file_operations debug_page_recorder_fops = { + .open = page_recorder_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int query_page_backtrace(struct seq_file *s, unsigned int *page) +{ + char symbol[KSYM_SYMBOL_LEN]; + unsigned long flags; + unsigned int *backtrace; + unsigned int i; + unsigned int hash = hash_32((unsigned int)page, 16); + unsigned int slot = hash % OBJECT_TABLE_SIZE; + + PageObjectEntry *bt_entry = NULL; + PageHashEntry *entry = NULL; + seq_printf(s, "%s %x\n", "query Page address:", (unsigned int)page); + + /* search page record in hash table */ + spin_lock_irqsave(&page_record_lock, flags); + entry = find_page_entry(page, slot); + if (entry != NULL) { + bt_entry = entry->bt_entry; + backtrace = (unsigned int *)bt_entry->object; + seq_printf(s, "%x allocate %d %s\n", (unsigned int)entry->page, + entry->size * 4096, "bytes and backtrace is "); + for (i = 0; i < bt_entry->numEntries; i++) { + sprint_symbol(symbol, *(backtrace + i)); + seq_printf(s, " KERNEL[%d] 0x%x :: symbol %s\n", i, + backtrace[i], symbol); + } + } else { + seq_printf(s, "can't get page(0x%x) backtrace information\n", + (unsigned int)page); + } + spin_unlock_irqrestore(&page_record_lock, flags); + return 0; +} + +static struct page *fixmap_virt_to_page(const void *fixmap_addr) +{ + unsigned long addr = (unsigned long)fixmap_addr; + struct page *page = NULL; + pgd_t *pgd = pgd_offset_k(addr); + + if (!pgd_none(*pgd)) { + pud_t *pud = pud_offset(pgd, addr); + if (!pud_none(*pud)) { + pmd_t *pmd = pmd_offset(pud, addr); + if (!pmd_none(*pmd)) { + pte_t *ptep, pte; + ptep = pte_offset_map(pmd, addr); + pte = *ptep; + if (pte_present(pte)) + page = pte_page(pte); + pte_unmap(ptep); + } + } + } + return page; +} + +static int query_page_bt_open(struct seq_file *s, void *data) +{ +/* + unsigned int *page_address = NULL; + seq_printf(s, "queried_page is : %x\n", queried_address); + + if(is_vmalloc_or_module_addr((const void *)queried_address)) //vmalloc + { + seq_printf(s, "[vmalloc or module]queried_page is : %x\n", queried_address); + page_address = (unsigned int *)vmalloc_to_page((unsigned int *)(queried_address&0xfffff000)); + } + else if((queried_address >= 0xC0000000) && (queried_address <= (unsigned int)high_memory ))//lowmem + { + seq_printf(s, "[lowmem]queried_page is : %x\n", queried_address); + page_address = (unsigned int *)virt_to_page((void*)(queried_address&0xfffff000 )); + } + else if((queried_address >= FIXADDR_START) && (queried_address <= FIXADDR_TOP)) //fixmap + { + seq_printf(s, "[fixmap]queried_page is : %x\n", queried_address); + page_address = (unsigned int *)fixmap_virt_to_page((const void *)(queried_address&0xfffff000)); + } + else if((queried_address >= PKMAP_BASE) && (queried_address)<= PKMAP_ADDR(LAST_PKMAP))//pkmap + { + seq_printf(s, "[pkmap]queried_page is : %x\n", queried_address); + page_address = (unsigned int *)fixmap_virt_to_page((const void *)(queried_address&0xfffff000)); + } + else + { + seq_printf(s, "[ERROR!!]queried_page is : %x can't find address in memory map\n", queried_address); + } + query_page_backtrace(s,(unsigned int*)page_address); +*/ + return 0; +} + +static int query_page_single_open(struct inode *inode, struct file *file) +{ + return single_open(file, query_page_bt_open, inode->i_private); +} + +static const struct file_operations query_page_ios_fops = { + .open = query_page_single_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static unsigned int get_kernel_backtrace(unsigned long *backtrace, + unsigned int debug) +{ + unsigned long stack_entries[BACKTRACE_LEVEL]; + unsigned int i = 0; + char tmp[KSYM_SYMBOL_LEN]; + struct stack_trace trace = { + .nr_entries = 0, + .entries = &stack_entries[0], + .max_entries = BACKTRACE_LEVEL, + .skip = 1 + }; + save_stack_trace(&trace); + if (trace.nr_entries > 0) { + if (debug) { + for (i = 0; i < trace.nr_entries; i++) { + sprint_symbol(tmp, trace.entries[i]); + pr_debug("[%d] 0x%x %s\n", i, + (unsigned int)trace.entries[i], tmp); + } + } else { + memcpy(backtrace, (unsigned long *)trace.entries, + sizeof(unsigned int) * trace.nr_entries); + } + } else { + pr_err + ("[ERROR]can't get backtrace [get_kernel_backtrace] backtrace num: [%d]\n", + trace.nr_entries); + } + return trace.nr_entries; +} + +static inline unsigned int hash_32(unsigned int val, unsigned int bits) +{ + /* On some cpus multiply is faster, on others gcc will do shifts */ + unsigned int hash = val * GOLDEN_RATIO_PRIME_32; + + /* High bits are more random, so use them. */ + return hash >> (32 - bits); +} + +static uint32_t get_hash(void *object, size_t numEntries) +{ + unsigned int *backtrace = NULL; + unsigned int hash = 0; + size_t i; + + backtrace = (unsigned int *)object; + if (backtrace == NULL) { + return 0; + } + for (i = 0; i < numEntries; i++) { + hash = (hash * 33) + (*(backtrace + i) >> 2); + } + return hash; +} + +PageObjectEntry *find_entry(PageObjectTable * table, unsigned int slot, + void *object, unsigned int numEntries, + unsigned int size) +{ + PageObjectEntry *entry = table->slots[slot]; + while (entry != NULL) { + if (entry->numEntries == numEntries && + !memcmp(object, entry->object, + numEntries * sizeof(unsigned int))) { + return entry; + } + entry = entry->next; + } + return NULL; +} + +static void *allocate_record(unsigned int type) +{ + switch (type) { + case NODE_PAGE_RECORD: + { + if (!page_cache_created) { + /* page_cachep = kmem_cache_create("page_record", + sizeof(PageHashEntry),0,SLAB_NO_DEBUG,NULL); */ + page_cachep = + kmem_cache_create("page_record", + sizeof(PageHashEntry), 0, + 0, NULL); + page_cache_created = true; + } + /* if system ram < 2G, page_record_total should less than 524288 */ + if ((page_cachep != NULL) + && (page_record_total < page_recorder_limit)) { + void *tmp = NULL; + tmp = + (void *)kmem_cache_alloc(page_cachep, + GFP_KERNEL); + if (tmp == 0) { + return NULL; + } + return tmp; + } + return NULL; + break; + } + } + return NULL; +} + +/* get record from hash table or create new node from slab allocator */ +static void *get_record(unsigned int type, page_record_t * param) +{ + page_record_t *tmp = param; + PageObjectEntry *entry = NULL; + unsigned int hash; + unsigned int slot; + unsigned long flags; + + if (tmp != NULL) { + switch (type) { + case HASH_PAGE_NODE_KERNEL_PAGE_ALLOC_BACKTRACE: + { + hash = get_hash(param->backtrace, + param->backtrace_num); + slot = hash % OBJECT_TABLE_SIZE; + spin_lock_irqsave(&bt_record_lock, flags); + entry = + find_entry(&gKernelPageBtTable, slot, + (void *)param->backtrace, + param->backtrace_num, + param->size); + if (entry != NULL) { + entry->reference++; + entry->size = + entry->size + (1 << param->size); + spin_unlock_irqrestore(&bt_record_lock, + flags); + } else { + spin_unlock_irqrestore(&bt_record_lock, + flags); + + /* total bt reocrd size should less than 5MB */ + if (bt_record_total < 50412) { + entry = + kmalloc(sizeof + (PageObjectEntry) + + (20 * + sizeof(unsigned + int)), + GFP_KERNEL); + if (entry == NULL) { + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + } else { + return NULL; + } + + /* kmalloc can't get right memory space when booting */ + if ((unsigned int)entry < 0xC0000000) { + pr_debug + ("[BAKCTRACEINFO][allocate bt mem] entry (0x%x) drop address \n", + (unsigned int)entry); + return NULL; + } + entry->reference = 1; + entry->prev = NULL; + entry->slot = slot; + entry->numEntries = + param->backtrace_num; + entry->size = 1 << param->size; + memcpy(entry->object, param->backtrace, + entry->numEntries * + sizeof(unsigned int)); + spin_lock_irqsave(&bt_record_lock, + flags); + entry->next = + gKernelPageBtTable.slots[slot]; + gKernelPageBtTable.slots[slot] = entry; + if (entry->next != NULL) { + entry->next->prev = entry; + } + gKernelPageBtTable.count++; + bt_record_total++; + if (bt_record_total > bt_record_max) { + bt_record_max = bt_record_total; + } + spin_unlock_irqrestore(&bt_record_lock, + flags); + } + return entry; + } + case HASH_PAGE_NODE_KERNEL_SYMBOL: + { + hash = + get_hash(param->kernel_symbol, + param->backtrace_num); + slot = hash % OBJECT_TABLE_SIZE; + spin_lock_irqsave(&symbol_record_lock, flags); + entry = + find_entry(&gKernelPageSymbolTable, slot, + (void *)param->kernel_symbol, + param->backtrace_num, + param->size); + if (entry != NULL) { + entry->reference++; + spin_unlock_irqrestore + (&symbol_record_lock, flags); + return NULL; + } else { + spin_unlock_irqrestore + (&symbol_record_lock, flags); + entry = + kmalloc(sizeof(PageObjectEntry) + + (param->backtrace_num * + sizeof(unsigned int)), + GFP_KERNEL); + if (entry == NULL) { + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + entry->reference = 1; + entry->prev = NULL; + entry->slot = slot; + entry->numEntries = + param->backtrace_num; + memcpy(entry->object, + param->kernel_symbol, + entry->numEntries * + sizeof(unsigned int)); + spin_lock_irqsave(&symbol_record_lock, + flags); + entry->next = + gKernelPageSymbolTable.slots[slot]; + gKernelPageSymbolTable.slots[slot] = + entry; + if (entry->next != NULL) { + entry->next->prev = entry; + } + gKernelPageSymbolTable.count++; + spin_unlock_irqrestore + (&symbol_record_lock, flags); + } + } + } + } + return NULL; +} + +static inline PageHashEntry *find_page_entry(void *page, int slot) +{ + PageHashEntry *entry = gPageHashTable.page_hash_table[slot]; + while (entry != NULL) { + if (entry->page == page) { + return entry; + } + entry = entry->next; + } + return NULL; +} + +PageHashEntry *record_page_info(PageObjectEntry * bt_entry, + PageObjectEntry * map_entry, void *page, + unsigned int order, unsigned int flag) +{ + /* calculate the hash value */ + unsigned int hash = hash_32((unsigned int)page, 16); + unsigned int slot = hash % OBJECT_TABLE_SIZE; + unsigned long flags; + + PPageHashEntry entry = + (PPageHashEntry) allocate_record(NODE_PAGE_RECORD); + if (!entry) { + pr_debug + ("[get_record][KERNEL_PAGE_ALLOC_BACKTRACE]can't get enough memory to create page entry\n"); + return NULL; + } + /* initialize page entry */ + entry->page = page; + entry->size = 1 << order; + entry->allocate_map_entry = map_entry; + entry->bt_entry = bt_entry; + entry->flag = (2 | flag); + entry->prev = NULL; + spin_lock_irqsave(&page_record_lock, flags); + + /* insert the entry to the head of slot list */ + if (gPageHashTable.page_hash_table[slot] == NULL) { + entry->next = NULL; + } else { + (gPageHashTable.page_hash_table[slot])->prev = entry; + entry->next = gPageHashTable.page_hash_table[slot]; + } + gPageHashTable.page_hash_table[slot] = entry; + gPageHashTable.count++; + page_record_total++; + if (page_record_total > page_record_max) { + page_record_max = page_record_total; + } + page_record_count++; + if (page_record_count >= 1000) { + page_recorder_memory_usage = + page_record_total * sizeof(PageHashEntry) + + bt_record_total * (sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))); + pr_debug + ("[TOTAL PAGE RECORD !!!] page record size is %d max page record size is %d\n", + page_record_total * sizeof(PageHashEntry), + page_record_max * sizeof(PageHashEntry)); + pr_debug + ("[TOTAL BACKTRACE RECORD !!!] bt record size is %d max bt record size is %d\n", + bt_record_total * (sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))), + bt_record_max * (sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int)))); + page_record_count = 0; + } + + spin_unlock_irqrestore(&page_record_lock, flags); + return entry; +} + +int remove_page_info(void *page, unsigned int order) +{ + unsigned int hash = hash_32((unsigned int)page, 16); + unsigned int slot = hash % OBJECT_TABLE_SIZE; + PageObjectEntry *bt_entry = NULL; + PageHashEntry *entry = NULL; + unsigned long flags; + + /* search page record in hash table */ + if (page_record_lock_init == 0) { + page_record_lock_init = 1; + spin_lock_init(&page_record_lock); + } + if (bt_record_lock_init == 0) { + bt_record_lock_init = 1; + spin_lock_init(&bt_record_lock); + } + + spin_lock_irqsave(&page_record_lock, flags); + entry = find_page_entry(page, slot); + if (entry == NULL) { + spin_unlock_irqrestore(&page_record_lock, flags); + /* pr_debug("[remove_page_info]can't find page info 0x%x\n",page); */ + if (debug_log) { + get_kernel_backtrace(NULL, 1); + } + return 1; + } else { + /* remove page record from hash table */ + /* head */ + if (entry->prev == NULL) { + gPageHashTable.page_hash_table[slot] = entry->next; + /* not only one entry in the slot */ + if (gPageHashTable.page_hash_table[slot] != NULL) + gPageHashTable.page_hash_table[slot]->prev = + NULL; + } else if (entry->next == NULL) { + entry->prev->next = NULL; + } else { + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + } + + gPageHashTable.count--; + page_record_total--; + spin_unlock_irqrestore(&page_record_lock, flags); + + /* clean page entry */ + entry->next = NULL; + entry->prev = NULL; + bt_entry = entry->bt_entry; + kmem_cache_free(page_cachep, entry); + + /* create alloc bt entry for historical allocation */ + if (bt_entry == NULL) { + return -1; + } else { + spin_lock_irqsave(&bt_record_lock, flags); + if (bt_entry->reference > 1) { + (bt_entry->reference)--; + bt_entry->size = bt_entry->size - (1 << order); + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_debug + ("[remove_page_info] bt_entry->size %d\n", + bt_entry->size); + } else if (bt_entry->reference == 1) { + unsigned int hash_bt; + unsigned int slot_bt; + hash_bt = + get_hash(bt_entry->object, + bt_entry->numEntries); + slot_bt = hash_bt % OBJECT_TABLE_SIZE; + + if (bt_entry->prev == NULL) { /* head */ + gKernelPageBtTable.slots[slot_bt] = + bt_entry->next; + /* not only one entry in the slot */ + if (gKernelPageBtTable.slots[slot_bt] != + NULL) + gKernelPageBtTable.slots + [slot_bt]->prev = NULL; + } else if (bt_entry->next == NULL) { + bt_entry->prev->next = NULL; + } else { + bt_entry->next->prev = bt_entry->prev; + bt_entry->prev->next = bt_entry->next; + } + spin_unlock_irqrestore(&bt_record_lock, flags); + bt_record_total--; + kfree(bt_entry); + } else { + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_err("ERROR !!!!free page info\n"); + } + } + } + return 0; +} + +int record_page_record(void *page, unsigned int order) +{ + void *entry, *map_entry = NULL; + page_record_t record_param; + if (!page_recorder_debug) { + return 0; + } + if (page_record_lock_init == 0) { + page_record_lock_init = 1; + spin_lock_init(&page_record_lock); + } + if (bt_record_lock_init == 0) { + bt_record_lock_init = 1; + spin_lock_init(&bt_record_lock); + } + if (debug_log & 1) { + /* get_kernel_backtrace(NULL,1); */ + } + record_param.page = page; + record_param.size = order; + record_param.backtrace_num = + (unsigned int)get_kernel_backtrace((unsigned long *) + record_param.backtrace, + (unsigned int)0); + + entry = + get_record(HASH_PAGE_NODE_KERNEL_PAGE_ALLOC_BACKTRACE, + &record_param); + if (entry == NULL) { + pr_debug("[get_record][KERNEL_PAGE_ALLOC_BACKTRACE]"); + pr_debug + ("can't get enough memory to create backtrace object\n"); + return 0; + } + record_page_info((PageObjectEntry *) entry, + (PageObjectEntry *) map_entry, record_param.page, + record_param.size, 0); + return 1; +} + +EXPORT_SYMBOL(record_page_record); + +int remove_page_record(void *page, unsigned int order) +{ + page_record_t record_param; + record_param.page = page; + record_param.size = order; + + if (!page_recorder_debug) { + return 0; + } + /* record_param.backtrace_num = get_kernel_backtrace( + (unsigned long *)record_param.backtrace,(unsigned int)0); */ + /* get_kernel_symbol((unsigned long *)record_param.backtrace, + record_param.backtrace_num,&(record_param.kernel_symbol[0])); */ + if (debug_log & 2) { + /* get_kernel_backtrace(NULL,1); */ + } + + remove_page_info(record_param.page, record_param.size); + return 1; +} + +EXPORT_SYMBOL(remove_page_record); + +static int page_recorder_debug_show(struct seq_file *s, void *unused) +{ + unsigned int index = 0; + unsigned int *backtrace; + unsigned int rank_index = 0; + char symbol[KSYM_SYMBOL_LEN]; + unsigned int i = 0; + struct page_object_rank_entry *rank_head = NULL; + struct page_object_rank_entry *rank_tail = NULL; + unsigned int Object_rank_count = 0; + PageObjectEntry *tmp = NULL; + unsigned long flags; + + seq_printf(s, "page_recorder_debug: [%d]\n", page_recorder_debug); + seq_printf(s, "page_recorder_limit: [%d]\n", page_recorder_limit); + seq_printf(s, "TOP %d page allocation \n", Object_rank_max); + for (index = 0; index < OBJECT_TABLE_SIZE; index++) { + tmp = NULL; + spin_lock_irqsave(&bt_record_lock, flags); + tmp = gKernelPageBtTable.slots[index]; + while (tmp != NULL) { + struct page_object_rank_entry *rank_tmp = rank_head; + struct page_object_rank_entry *rank_tmp_prev = + rank_head; + for (rank_index = 0; rank_index < Object_rank_max; + rank_index++) { + struct page_object_rank_entry *new_rank_entry = + NULL; + PageObjectEntry *entry = NULL; + if ((rank_tmp != NULL) + && (rank_tmp->entry->size <= tmp->size)) { + /* insert current record into list */ + PageObjectEntry *entry = NULL; + new_rank_entry = + (struct page_object_rank_entry *) + kmalloc(sizeof + (struct + page_object_rank_entry), + GFP_ATOMIC); + if (new_rank_entry == NULL) { + spin_unlock_irqrestore + (&bt_record_lock, flags); + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + entry = + kmalloc(sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int)), + GFP_ATOMIC); + if (entry == NULL) { + spin_unlock_irqrestore + (&bt_record_lock, flags); + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + memcpy(entry, tmp, + sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))); + new_rank_entry->entry = entry; + new_rank_entry->prev = rank_tmp->prev; + if (rank_tmp->prev != NULL) { + rank_tmp->prev->next = + new_rank_entry; + } + rank_tmp->prev = new_rank_entry; + new_rank_entry->next = rank_tmp; + if (new_rank_entry->prev == NULL) { + rank_head = new_rank_entry; + } + if (Object_rank_count < + (Object_rank_max)) { + Object_rank_count++; + } else { + /* free last rank_entry */ + if (rank_tail != NULL) { + struct + page_object_rank_entry + *new_tail = NULL; + new_tail = + rank_tail->prev; + rank_tail->prev->next = + NULL; + kfree(rank_tail->entry); + kfree(rank_tail); + rank_tail = new_tail; + } else { + pr_err + ("ERROR!!! rank_tail is NULL\n"); + } + } + break; + } else if ((rank_tmp == NULL) + && (Object_rank_count < + Object_rank_max)) { + /* if rank entry is less than object_entry_max, + create new rank entry and insert it in rank list */ + new_rank_entry = + (struct page_object_rank_entry *) + kmalloc(sizeof + (struct + page_object_rank_entry), + GFP_ATOMIC); + if (new_rank_entry == NULL) { + spin_unlock_irqrestore + (&bt_record_lock, flags); + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + entry = + kmalloc(sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int)), + GFP_ATOMIC); + if (entry == NULL) { + spin_unlock_irqrestore + (&bt_record_lock, flags); + pr_err + ("[PAGE_RECORDER]Error!!! can't get memory from kmalloc\n"); + return NULL; + } + memcpy(entry, tmp, + sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))); + new_rank_entry->entry = entry; + new_rank_entry->next = NULL; + new_rank_entry->prev = rank_tmp_prev; + if (rank_tmp_prev != NULL) { + rank_tmp_prev->next = + new_rank_entry; + } + if (new_rank_entry->prev == NULL) { + rank_head = new_rank_entry; + } + rank_tail = new_rank_entry; + Object_rank_count++; + break; + } + rank_tmp_prev = rank_tmp; + rank_tmp = rank_tmp->next; + } + tmp = tmp->next; + } + spin_unlock_irqrestore(&bt_record_lock, flags); + } + + /* print top object_rank_max record */ + { + struct page_object_rank_entry *rank_tmp = rank_head; + struct page_object_rank_entry *tmp_record = NULL; + rank_index = 0; + while (rank_tmp != NULL) { + backtrace = (unsigned int *)rank_tmp->entry->object; + seq_printf(s, "[%d]%s %d %s\n", rank_index, + "Backtrace pages ", + rank_tmp->entry->size * 4096, "bytes"); + for (i = 0; i < rank_tmp->entry->numEntries; i++) { + sprint_symbol(symbol, *(backtrace + i)); + seq_printf(s, + " KERNEL[%d] 0x%x :: symbol %s\n", + i, backtrace[i], symbol); + } + rank_index++; + tmp_record = rank_tmp; + rank_tmp = rank_tmp->next; + kfree(tmp_record->entry); + kfree(tmp_record); + } + } + return 0; +} + +static int __init setup_page_recorder_debug(char *str) +{ + page_recorder_debug = DEBUG_DEFAULT_FLAGS; + if (*str++ != '=' || !*str) + /* + * No options specified. Switch on full debugging. + */ + goto out; + + if (*str == ',') + /* + * No options but restriction on page recorder. This means full + * debugging for page recorder matching a pattern. + */ + goto check_page_recorder; + + page_recorder_debug = 0; + if (*str == '-') + /* + * Switch off all debugging measures. + */ + goto out; + +check_page_recorder: + if (*str == ',') + page_recorder_debug_function = *(str + 1); +out: + return 1; +} + +__setup("page_recorder_debug", setup_page_recorder_debug); + +#ifdef CONFIG_E_SHOW_MEM + +static int rank_sort( + struct page_object_rank_entry **prank_tail, + struct page_object_rank_entry *rank_tmp, + struct page_object_rank_entry *rank_tmp_prev, + struct page_object_rank_entry **prank_head, + PageObjectEntry *tmp, + unsigned long flags, + unsigned int temp_object_rank_max, + unsigned int *pobject_rank_count) +{ + struct page_object_rank_entry *new_rank_entry = NULL; + PageObjectEntry *entry = NULL; + + if ((rank_tmp != NULL) && (rank_tmp->entry->size <= tmp->size)) { + /* insert current record into list */ + new_rank_entry = (struct page_object_rank_entry *) + kmalloc(sizeof(struct page_object_rank_entry), GFP_ATOMIC); + if (new_rank_entry == NULL) { + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_err("[PAGE_RECORDER]Error!!!can't get memory from kmalloc\n"); + return -1; + } + entry = kmalloc(sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int)), GFP_ATOMIC); + if (entry == NULL) { + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_err("[PAGE_RECORDER]Error!!!can't get memory from kmalloc\n"); + return -1; + } + memcpy(entry, tmp, sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))); + new_rank_entry->entry = entry; + new_rank_entry->prev = rank_tmp->prev; + if (rank_tmp->prev != NULL) + rank_tmp->prev->next = new_rank_entry; + rank_tmp->prev = new_rank_entry; + new_rank_entry->next = rank_tmp; + if (new_rank_entry->prev == NULL) + *prank_head = new_rank_entry; + if (*pobject_rank_count < temp_object_rank_max) + (*pobject_rank_count)++; + else { + /* free last rank_entry */ + if (*prank_tail != NULL) { + struct page_object_rank_entry *new_tail = NULL; + new_tail = (*prank_tail)->prev; + (*prank_tail)->prev->next = NULL; + kfree((*prank_tail)->entry); + kfree(*prank_tail); + *prank_tail = new_tail; + } else { + pr_err("ERROR!!! rank_tail is NULL\n"); + } + } + return -2; + } else if ((rank_tmp == NULL) + && (*pobject_rank_count < temp_object_rank_max)) { + /* if rank entry is less than + object_entry_max,create new rank + entry and insert it in rank list */ + new_rank_entry = (struct page_object_rank_entry *) + kmalloc(sizeof(struct page_object_rank_entry), + GFP_ATOMIC); + if (new_rank_entry == NULL) { + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_err("[PAGE_RECORDER]Error!!can't get memory from kmalloc\n"); + return -1; + } + entry = kmalloc(sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int)), GFP_ATOMIC); + if (entry == NULL) { + spin_unlock_irqrestore(&bt_record_lock, flags); + pr_err("[PAGE_RECORDER]Error!!!can't get memory from kmalloc\n"); + return -1; + } + memcpy(entry, tmp, sizeof(PageObjectEntry) + + (20 * sizeof(unsigned int))); + new_rank_entry->entry = entry; + new_rank_entry->next = NULL; + new_rank_entry->prev = rank_tmp_prev; + if (rank_tmp_prev != NULL) + rank_tmp_prev->next = new_rank_entry; + if (new_rank_entry->prev == NULL) + *prank_head = new_rank_entry; + *prank_tail = new_rank_entry; + (*pobject_rank_count)++; + return -2; + } + return 0; +} + + +static int page_recorder_debug_show_printk(enum e_show_mem_type type) +{ + unsigned int index = 0; + unsigned int *backtrace; + unsigned int rank_index = 0; + char symbol[KSYM_SYMBOL_LEN]; + unsigned int i = 0; + struct page_object_rank_entry *rank_head = NULL; + struct page_object_rank_entry *rank_tail = NULL; + unsigned int object_rank_count = 0; + PageObjectEntry *tmp = NULL; + unsigned long flags; + unsigned int temp_object_rank_max; + unsigned long long total_used = 0; + int ret = 0; + + if (E_SHOW_MEM_BASIC == type) + temp_object_rank_max = 3; + else if (E_SHOW_MEM_CLASSIC == type) + temp_object_rank_max = 6; + else + temp_object_rank_max = 10; + + printk("Detail:\n"); + printk(" page_recorder_debug: [%d]\n", page_recorder_debug); + printk(" page_recorder_limit: [%d]\n", page_recorder_limit); + printk("TOP %d page allocation:\n", temp_object_rank_max); + for (index = 0; index < OBJECT_TABLE_SIZE; index++) { + tmp = NULL; + spin_lock_irqsave(&bt_record_lock, flags); + tmp = gKernelPageBtTable.slots[index]; + while (tmp != NULL) { + struct page_object_rank_entry *rank_tmp = rank_head; + struct page_object_rank_entry *rank_tmp_prev + = rank_head; + for (rank_index = 0; rank_index < temp_object_rank_max; + rank_index++) { + ret = rank_sort( + &rank_tail, + rank_tmp, + rank_tmp_prev, + &rank_head, + tmp, + flags, + temp_object_rank_max, + &object_rank_count); + if (-1 == ret) + return 0; + else if (-2 == ret) + break; + rank_tmp_prev = rank_tmp; + rank_tmp = rank_tmp->next; + } + tmp = tmp->next; + } + spin_unlock_irqrestore(&bt_record_lock, flags); + } + + /* print top object_rank_max record */ + { + struct page_object_rank_entry *rank_tmp = rank_head; + struct page_object_rank_entry *tmp_record = NULL; + rank_index = 0; + while (rank_tmp != NULL) { + backtrace = (unsigned int *)rank_tmp->entry->object; + printk("[%d]%s %d %s\n", rank_index, + "Backtrace pages ", + rank_tmp->entry->size * 4096, "bytes"); + total_used += rank_tmp->entry->size * 4096; + for (i = 0; i < rank_tmp->entry->numEntries; i++) { + sprint_symbol(symbol, *(backtrace + i)); + printk(" KERNEL[%d] 0x%x :: symbol %s\n", + i, backtrace[i], symbol); + } + rank_index++; + tmp_record = rank_tmp; + rank_tmp = rank_tmp->next; + kfree(tmp_record->entry); + kfree(tmp_record); + } + } + + printk("Total used:%llu kB\n", total_used / 1024); + return 0; +} + + + +static int page_recorder_e_show_mem_handler(struct notifier_block *nb, + unsigned long val, void *data) +{ + enum e_show_mem_type type = val; + printk("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + printk("Enhanced Mem-info :PAGE RECORDER\n"); + return page_recorder_debug_show_printk(type); +} + +static struct notifier_block page_recorder_e_show_mem_notifier = { + .notifier_call = page_recorder_e_show_mem_handler, +}; +#endif + +static int __init page_recorder_init(void) +{ + /* Create page allocate */ + debug_root = debugfs_create_dir("page_recorder", NULL); + debugfs_create_file("Usage_rank", 0444, debug_root, NULL, + &debug_page_recorder_fops); + debugfs_create_u32("Rank_number", 0644, debug_root, &Object_rank_max); + debugfs_create_file("query_page", 0644, debug_root, NULL, + &query_page_ios_fops); + debugfs_create_u32("page_virtual_address", 0644, debug_root, + &queried_address); + debugfs_create_u32("debug_log", 0644, debug_root, &debug_log); + debugfs_create_u32("page_recorder_debug", 0644, debug_root, + &page_recorder_debug); + debugfs_create_u32("page_recorder_memory_usage", 0644, debug_root, + &page_recorder_memory_usage); + debugfs_create_u32("page_recorder_limit", 0644, debug_root, + &page_recorder_limit); +#ifdef CONFIG_E_SHOW_MEM + register_e_show_mem_notifier(&page_recorder_e_show_mem_notifier); +#endif + return 0; +} + +late_initcall(page_recorder_init); |
