| /* | 
 |  *  linux/mm/mem_tracker.c | 
 |  * | 
 |  *  Copyright (C) 1993  Linus Torvalds | 
 |  *  Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 | 
 |  *   vmalloc/vfree, Tigran Aivazian <tigran@veritas.com>, May 2000 | 
 |  *  Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002 | 
 |  *  Numa awareness, Christoph Lameter, SGI, June 2005 | 
 |  */ | 
 | #include <linux/mm.h> | 
 | #include <linux/sched.h> | 
 | #include <linux/spinlock.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/kallsyms.h> | 
 | #include <linux/list.h> | 
 | #include <linux/pfn.h> | 
 | #include <linux/atomic.h> | 
 | #include <asm/stacktrace.h> | 
 | #include <mach/iomap.h> | 
 | #include <linux/kprobes.h> | 
 | #include <asm/traps.h> | 
 | #include <linux/mem_tracker_def.h> | 
 |  | 
 | /******************************************************************************* | 
 | *                                   ºê¶¨Òå                                     * | 
 | *******************************************************************************/ | 
 | #undef CONFIG_KALLSYMS | 
 |  | 
 | #ifdef CONFIG_KALLSYMS | 
 | #define MEM_TRACKER_MAX_STACK_LEN  	(100)  /*Õ»º¯Êý¹ì¼£µÄ×Ö·û¸öÊýÉÏÏÞ£»*/ | 
 | #endif  | 
 | #define MEM_TRACKER_MAX_STACK_LEVEL (6)    /*Õ»µÄ²ãÊý*/ | 
 | #define MEM_TRACKER_NOT_TRACE_LEVEL (2) | 
 | #define MEM_TRACKER_ALLOCED         0x00 | 
 | #define MEM_TRACKER_FREED          	0x02 | 
 |  | 
 | /******************************************************************************* | 
 | *                                Êý¾ÝÀàÐͶ¨Òå                                  * | 
 | *******************************************************************************/ | 
 | struct heap_record { | 
 |     struct list_head    node; | 
 | 	void	 			*addr;    /*ÉêÇë³öµÄÄÚ´æµØÖ·*/ | 
 | 	unsigned int        employ:8; | 
 |     unsigned int 		size:24;	  /*¼Ç¼ÉêÇëµÄÄڴ泤¶È*/ | 
 | 	char                task_name[TASK_COMM_LEN]; | 
 | #ifdef CONFIG_KALLSYMS | 
 |     char				stack_frame[MEM_TRACKER_MAX_STACK_LEVEL][MEM_TRACKER_MAX_STACK_LEN]; | 
 | 								/*º¯Êý»ØËݹ켣*/ | 
 | #else | 
 | 	unsigned long 	    stack_frame[MEM_TRACKER_MAX_STACK_LEVEL]; | 
 | #endif | 
 | }; | 
 |  | 
 | /******************************************************************************* | 
 | *                                º¯ÊýÉùÃ÷                                  * | 
 | *******************************************************************************/ | 
 | /******************************************************************************* | 
 | *								 È«¾Ö±äÁ¿¶¨Òå								   * | 
 | *******************************************************************************/ | 
 | int 				kmem_node_max; | 
 | int 				node_list_max;      | 
 | static spinlock_t  	buddy_track_lock; | 
 | static spinlock_t  	kmem_track_lock; | 
 |  | 
 |  | 
 | /*ÕýÔÚʹÓõÄÊý¾ÝÁ´±í*/ | 
 | static LIST_HEAD(buddy_employ_head); | 
 | static LIST_HEAD(kmem_employ_head); | 
 |  | 
 | /*ÒѾÊͷŵÄÊý¾ÝÁ´±í*/ | 
 | static LIST_HEAD(buddy_free_head); | 
 | static LIST_HEAD(kmem_free_head); | 
 |  | 
 | /******************************************************************************* | 
 | *                                ¾Ö²¿º¯ÊýʵÏÖ                                  * | 
 | *******************************************************************************/ | 
 | static void memset_tracker(struct heap_record * node) | 
 | { | 
 | 	node->size = 0; | 
 | 	node->addr = NULL; | 
 | 	 | 
 | 	memset(node->task_name,0, TASK_COMM_LEN); | 
 | 	memset(node->stack_frame, 0, sizeof(node->stack_frame)); | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö: 	¶Ôµ±Ç°Õ»½øÐлØËÝ£¬½«º¯Êý¹ì¼£±£´æ | 
 | * ²ÎÊý˵Ã÷: 	 | 
 | *	(´«Èë²ÎÊý)	my_frame:¹ì¼£ÐÅÏ¢±£´æµÄµØÖ· | 
 | *                | 
 | *	(´«³ö²ÎÊý)	void | 
 | * ·µ »Ø Öµ: 	void | 
 | * ÆäËü˵Ã÷: 	ÎÞ | 
 | *******************************************************************************/ | 
 | #ifdef CONFIG_KALLSYMS | 
 | void get_stack_process(unsigned char *my_frame) | 
 | #else | 
 | void get_stack_process(unsigned long *my_frame) | 
 | #endif  | 
 | { | 
 | 	int urc; | 
 | 	int i 	= 0; | 
 | #ifdef CONFIG_KALLSYMS | 
 | 	char mytmp[MEM_TRACKER_MAX_STACK_LEN] = {0}; | 
 | #endif | 
 | 	struct stackframe frame; | 
 | 	register unsigned long current_sp asm ("sp"); | 
 |  | 
 | #ifndef CONFIG_KALLSYMS | 
 |     memset(my_frame, 0, MEM_TRACKER_MAX_STACK_LEVEL * sizeof(unsigned long)); | 
 | #endif | 
 |  | 
 | 	frame.fp = (unsigned long)__builtin_frame_address(0); | 
 | 	frame.lr = (unsigned long)__builtin_return_address(0); | 
 | 	frame.sp = current_sp; | 
 | 	frame.pc = (unsigned long)get_stack_process; | 
 |  | 
 | 	while (1) {		 | 
 | 		urc = unwind_frame(&frame); | 
 | 		/*Èô»ØËݽáÊø£¬ÔòÁ¢¼´Í˳ö*/ | 
 | 		if (urc < 0) | 
 | 		    return; | 
 |  | 
 | 		/*ΪÁ˽ÚÊ¡Äڴ棬×îÉϲãµÄ2¸öº¯Êý²»¸ú×Ù*/ | 
 | 		if (++i >= MEM_TRACKER_NOT_TRACE_LEVEL) { | 
 | #ifdef CONFIG_KALLSYMS | 
 | 			snprintf(mytmp, MEM_TRACKER_MAX_STACK_LEN - 1, "<%ps>", (void *)frame.pc); | 
 | 			strcpy(my_frame + ((i - MEM_TRACKER_NOT_TRACE_LEVEL) * MEM_TRACKER_MAX_STACK_LEN), mytmp); | 
 | #else | 
 | 			my_frame[i - MEM_TRACKER_NOT_TRACE_LEVEL] = (unsigned long)frame.pc; | 
 | #endif	 | 
 | 			if (i - MEM_TRACKER_NOT_TRACE_LEVEL >= MEM_TRACKER_MAX_STACK_LEVEL - 1) | 
 | 				return; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö: 	»ñÈ¡×îºóÒ»¸ö½Úµã | 
 | * ²ÎÊý˵Ã÷: 	 | 
 | *	(´«Èë²ÎÊý)	head:Á´±íÍ· | 
 | *	(´«³ö²ÎÊý)	track¸ú×ÙÐÅÏ¢ | 
 | * ·µ »Ø Öµ: 	void | 
 | * ÆäËü˵Ã÷: 	ÎÞ | 
 | *******************************************************************************/ | 
 | static struct list_head *list_get_last_node(struct list_head *head) | 
 | { | 
 |  	struct heap_record 	*h 		   = NULL; | 
 | 	struct list_head 	*tail_node = NULL; | 
 | 	 | 
 |  	if (list_empty(head)) | 
 | 		return NULL; | 
 |  | 
 |     tail_node = head->prev; | 
 | 	 | 
 | 	h = container_of(tail_node, struct heap_record, node); | 
 | 	 | 
 |     if(h->employ == MEM_TRACKER_FREED) | 
 | 		list_del(head->prev);		 | 
 | 	else | 
 | 		panic("[MEM_TRACKER]list error"); | 
 | 	 | 
 | 	return tail_node; | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö:     ÄÚ´æ¸ú×Ù³õʼ»¯ | 
 | * ²ÎÊý˵Ã÷:      | 
 | *   (´«Èë²ÎÊý)  void              | 
 | *   (´«³ö²ÎÊý)  void | 
 | * ·µ »Ø Öµ:     void | 
 | * ÆäËü˵Ã÷:    ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ  | 
 | *******************************************************************************/ | 
 | void mem_trace_init(void) | 
 | { | 
 |     int 				index; | 
 | 	unsigned long 		flags; | 
 |     struct heap_record *record = NULL; | 
 | 	void __iomem 		*base  = NULL; | 
 | 	 | 
 |     spin_lock_init(&buddy_track_lock); | 
 | 	 | 
 |     spin_lock_irqsave(&buddy_track_lock, flags); | 
 | 	 | 
 | 	base 			= ZX_MEM_TRACKER_BASE;  | 
 | 	node_list_max 	= ZX_MEM_TRACKER_SIZE / sizeof(struct heap_record); | 
 |  | 
 | 	/* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ | 
 |     for (index = 0; index < node_list_max; index++) | 
 |     { | 
 |         record = (struct heap_record *)(base + index * sizeof(struct heap_record)); | 
 | 		record->employ = MEM_TRACKER_FREED; | 
 | 		list_add(&record->node, &buddy_free_head); | 
 |     } | 
 | 	 | 
 |     spin_unlock_irqrestore(&buddy_track_lock, flags); | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö:     kmallocÄÚ´æ¸ú×Ù³õʼ»¯ | 
 | * ²ÎÊý˵Ã÷:      | 
 | *   (´«Èë²ÎÊý)  void              | 
 | *   (´«³ö²ÎÊý)  void | 
 | * ·µ »Ø Öµ:     void | 
 | * ÆäËü˵Ã÷:    ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ  | 
 | *******************************************************************************/ | 
 | void kmalloc_trace_init(void) | 
 | { | 
 |     int 				index; | 
 | 	unsigned long 		flags; | 
 |     struct heap_record *record = NULL; | 
 | 	void __iomem 		*base  = NULL; | 
 | 	 | 
 | 	spin_lock_init(&kmem_track_lock); | 
 | 	spin_lock_irqsave(&kmem_track_lock, flags); | 
 |  | 
 | 	base 			= ZX_KMALLOC_TRACKER_BASE;  | 
 | 	kmem_node_max 	= ZX_KMALLOC_TRACKER_SIZE / sizeof(struct heap_record); | 
 |  | 
 | 	/* ³õʼ»¯¿ÕÏÐÄÚ´æ½ÚµãÁ´±í */ | 
 |     for (index = 0; index < kmem_node_max; index++) | 
 |     { | 
 |         record = (struct heap_record *)(base + index * sizeof(struct heap_record)); | 
 | 		record->employ = MEM_TRACKER_FREED; | 
 | 		list_add(&record->node, &kmem_free_head); | 
 |     } | 
 |  | 
 |     spin_unlock_irqrestore(&kmem_track_lock, flags); | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö:     ÑéÖ¤entry½ÚµãºÏ·¨ÐÔ | 
 | * ²ÎÊý˵Ã÷:      | 
 | *   (´«Èë²ÎÊý)  void              | 
 | *   (´«³ö²ÎÊý)  void | 
 | * ·µ »Ø Öµ:     void | 
 | * ÆäËü˵Ã÷:    ΪʵÏÖÄÚ´æ¹ì¼£¸ú×Ùʱ²»Õ¼ÓÃÏÖÓÐÄڴ棬ʹÓÃ64MÒÔºóµÄÄÚ´æ  | 
 | *******************************************************************************/ | 
 | int check_node_entry(const int entry) | 
 | { | 
 | 	void __iomem *base	= NULL; | 
 | 	long		 limit  = 0;	  | 
 | 	 | 
 | 	base  = ZX_KMALLOC_TRACKER_BASE;  | 
 | 	limit = ZX_KMALLOC_TRACKER_SIZE + (int)base; | 
 | 	 | 
 | 	if ((entry >= base) && (entry < limit)) | 
 | 		return MEM_TRUE; | 
 | 	 | 
 | 	return MEM_FALSE; | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö: 	ÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ | 
 | * ²ÎÊý˵Ã÷: 	 | 
 | *	(´«Èë²ÎÊý)	addr:ÄÚ´æµØÖ· | 
 | *               order:Äڴ泤¶È,orderΪµ¥Î»£¬2µÄorder´Î·½¸öÒ³ | 
 | *	(´«³ö²ÎÊý)	void | 
 | * ·µ »Ø Öµ: 	void | 
 | * ÆäËü˵Ã÷: 	ÎÞ | 
 | *******************************************************************************/ | 
 | void mem_alloc_tracker(void *addr, int order) | 
 | { | 
 |     unsigned long 		spin_flag; | 
 |     struct heap_record  *section    = NULL; | 
 | 	struct list_head 	*head_ptr  = NULL;  | 
 | 	struct page 		*page_addr = NULL; | 
 |  | 
 | 	if(!addr) | 
 | 		panic("[MEM_TRACKER] input item error\n"); | 
 |  | 
 | 	page_addr = (struct page *)(addr); | 
 |    	 | 
 |     spin_lock_irqsave(&buddy_track_lock, spin_flag); | 
 |     if (!(head_ptr = list_get_last_node(&buddy_free_head))) | 
 |     { | 
 | 		printk("[MEM_TRACKER]buddy free_link_node have no node!!!\n"); | 
 | 		spin_unlock_irqrestore(&buddy_track_lock, spin_flag); | 
 | 		return; | 
 |     } | 
 |     spin_unlock_irqrestore(&buddy_track_lock, spin_flag); | 
 | 	section = container_of(head_ptr, struct heap_record, node); | 
 |  | 
 | 	/*³õʼ»¯½ÚµãÐÅÏ¢*/ | 
 |     memset_tracker(section); | 
 | 	section->addr 	= addr; | 
 | 	section->size 	= (1 << order) * PAGE_SIZE; | 
 | 	section->employ = MEM_TRACKER_ALLOCED; | 
 | 	memcpy(section->task_name, current->comm, TASK_COMM_LEN);	 | 
 |  | 
 | 	/*»ñȡջ¹ì¼£*/ | 
 | 	get_stack_process(section->stack_frame); | 
 | 	page_addr->mem_track_entry = section; | 
 | 	 | 
 | 	/*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ | 
 |     spin_lock_irqsave(&buddy_track_lock, spin_flag); | 
 |     list_add(§ion->node, &buddy_employ_head); | 
 |     spin_unlock_irqrestore(&buddy_track_lock,spin_flag); | 
 | } | 
 | EXPORT_SYMBOL(mem_alloc_tracker); | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö: 	kmallocÉêÇëÄÚ´æµãµ÷Óøýӿڣ¬¼Ç¼ÄÚ´æÉêÇë¹ì¼£ | 
 | * ²ÎÊý˵Ã÷: 	 | 
 | *	(´«Èë²ÎÊý)	addr:ÄÚ´æµØÖ· | 
 | *               len:Äڴ泤¶È | 
 | *	(´«³ö²ÎÊý)	void | 
 | * ·µ »Ø Öµ: 	void | 
 | * ÆäËü˵Ã÷: 	ÎÞ | 
 | *******************************************************************************/ | 
 | void kmalloc_alloc_tracker(void *addr, int len) | 
 | { | 
 |     unsigned long 		spin_flag; | 
 |     struct heap_record *section   		  = NULL; | 
 | 	struct list_head 	*head_ptr 	      = NULL; | 
 | 	struct list_head 	*free_node_head   = NULL;                                          | 
 | 	struct list_head 	*employ_node_head = NULL;   | 
 | 	struct page 		*page_addr 	      = NULL; | 
 |  | 
 | 	if(!addr) | 
 | 		panic("[MEM_TRACKER] input item error\n"); | 
 |  | 
 | 	free_node_head   = &kmem_free_head; | 
 | 	employ_node_head = &kmem_employ_head; | 
 | 	 | 
 |     spin_lock_irqsave(&kmem_track_lock, spin_flag); | 
 |      | 
 |     if (!(head_ptr = list_get_last_node(free_node_head))) | 
 |     { | 
 | 		printk("[MEM_TRACKER]kmalloc free_link_node have no node!!!\n"); | 
 | 		spin_unlock_irqrestore(&kmem_track_lock, spin_flag); | 
 | 		return; | 
 |     } | 
 |     spin_unlock_irqrestore(&kmem_track_lock, spin_flag); | 
 |  | 
 | 	section = container_of(head_ptr, struct heap_record, node); | 
 |  | 
 | 	/*³õʼ»¯½ÚµãÐÅÏ¢*/ | 
 |     memset_tracker(section); | 
 | 	section->addr 	= KMALLOC_SETUP(addr); | 
 | 	section->size 	= len; | 
 | 	section->employ = MEM_TRACKER_ALLOCED; | 
 | 	memcpy(section->task_name, current->comm, TASK_COMM_LEN);	 | 
 |  | 
 | 	/*»ñȡջ¹ì¼£*/ | 
 | 	get_stack_process(section->stack_frame); | 
 | 	KMALLOC_SET_ENTRY(addr, section); | 
 |  | 
 | 	if(MEM_FALSE == check_node_entry(section)) | 
 | 		panic("error"); | 
 |   | 
 | 	/*¼ÓÈëÒÑÉêÇëÄÚ´æ½Úµã¼Ç¼Á´±í*/ | 
 |     spin_lock_irqsave(&kmem_track_lock, spin_flag); | 
 |     list_add(§ion->node, employ_node_head); | 
 | 	 | 
 |     spin_unlock_irqrestore(&kmem_track_lock,spin_flag); | 
 | } | 
 | EXPORT_SYMBOL(kmalloc_alloc_tracker); | 
 |  | 
 | /******************************************************************************* | 
 | * ¹¦ÄÜÃèÊö: 	ÊÍ·ÅÄÚ´æµãµ÷ÓÃ¸Ã½Ó¿Ú | 
 | * ²ÎÊý˵Ã÷: 	 | 
 | *	(´«Èë²ÎÊý)	entry:ÄÚ´æµØÖ·         | 
 | *	(´«³ö²ÎÊý)	void | 
 | * ·µ »Ø Öµ: 	void | 
 | * ÆäËü˵Ã÷: 	ÎÞ | 
 | *******************************************************************************/ | 
 | void mem_free_tracker(void *entry, int type) | 
 | { | 
 |     unsigned long 	   spin_flag; | 
 | 	spinlock_t 		   *mem_lock       = NULL; | 
 | 	struct list_head   *free_node_head = NULL;      | 
 |     struct heap_record *free_node = (struct heap_record *)entry; | 
 |  | 
 |     if (entry == NULL) | 
 | 		return; | 
 |  | 
 | 	if (type == MEM_TRACKER_TYPE_BUDDY) | 
 | 	{ | 
 | 		mem_lock = &buddy_track_lock; | 
 | 		free_node_head = &buddy_free_head; | 
 | 	} | 
 | 	else if (type == MEM_TRACKER_TYPE_KMALLOC) | 
 | 	{ | 
 | 		mem_lock = &kmem_track_lock; | 
 | 		free_node_head = &kmem_free_head; | 
 | 	} | 
 | 	else | 
 | 		panic("mem_tracker type error\n"); | 
 |  | 
 |     spin_lock_irqsave(mem_lock, spin_flag); | 
 |  | 
 | 	if (free_node->employ == MEM_TRACKER_ALLOCED) | 
 | 	{ | 
 | 		free_node->employ = MEM_TRACKER_FREED;	 | 
 | 		list_move(&free_node->node, free_node_head); | 
 | 	} | 
 | 	else  | 
 | 	{		     | 
 | 		printk("[MEM_TRACKER]error: employ is 0x%x  employ_node MEM_TRACKER_FREED !!\n", free_node->employ); | 
 | 	} | 
 | 	 | 
 | 	spin_unlock_irqrestore(mem_lock, spin_flag);		   | 
 | } | 
 | EXPORT_SYMBOL(mem_free_tracker); | 
 |  | 
 |  | 
 |  |