2 This software is available to you under a choice of one of two
3 licenses. You may choose to be licensed under the terms of the GNU
4 General Public License (GPL) Version 2, available at
5 <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
6 license, available in the LICENSE.TXT file accompanying this
7 software. These details are also available at
8 <http://openib.org/license.html>.
10 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
14 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
15 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
36 #ifdef kmem_cache_alloc
37 #undef kmem_cache_alloc
39 #ifdef kmem_cache_free
40 #undef kmem_cache_free
43 #include <linux/module.h>
44 #include <linux/kernel.h>
45 #include <linux/slab.h>
46 #include <linux/interrupt.h>
47 #include <linux/vmalloc.h>
48 #include <linux/version.h>
49 #include <asm/uaccess.h>
50 #include <linux/proc_fs.h>
53 #include <linux/moduleparam.h>
56 MODULE_AUTHOR("Mellanox Technologies LTD.");
57 MODULE_DESCRIPTION("Memory allocations tracking");
58 MODULE_LICENSE("GPL");
60 #define MEMTRACK_HASH_SZ ((1<<15)-19) /* prime: http://www.utm.edu/research/primes/lists/2small/0bit.html */
61 #define MAX_FILENAME_LEN 31
63 #define memtrack_spin_lock(spl, flags) spin_lock_irqsave(spl, flags)
64 #define memtrack_spin_unlock(spl, flags) spin_unlock_irqrestore(spl, flags)
66 /* if a bit is set then the corresponding allocation is tracked.
67 bit0 corresponds to MEMTRACK_KMALLOC, bit1 corresponds to MEMTRACK_VMALLOC etc. */
68 static unsigned long track_mask = -1; /* effectively everything */
69 module_param(track_mask, ulong, 0444);
70 MODULE_PARM_DESC(track_mask, "bitmask definenig what is tracked");
72 /* if a bit is set then the corresponding allocation is strictly tracked.
73 That is, before inserting the whole range is checked to not overlap any
74 of the allocations already in the database */
75 static unsigned long strict_track_mask = 0; /* no strict tracking */
76 module_param(strict_track_mask, ulong, 0444);
77 MODULE_PARM_DESC(strict_track_mask, "bitmask which allocation requires strict tracking");
79 typedef struct memtrack_meminfo_st {
82 unsigned long line_num;
83 struct memtrack_meminfo_st *next;
84 struct list_head list; /* used to link all items from a certain type together */
85 char filename[MAX_FILENAME_LEN + 1]; /* putting the char array last is better for struct. packing */
88 static struct kmem_cache *meminfo_cache;
91 memtrack_meminfo_t *mem_hash[MEMTRACK_HASH_SZ];
93 unsigned long count; /* size of memory tracked (*malloc) or number of objects tracked */
94 struct list_head tracked_objs_head; /* head of list of all objects */
95 int strict_track; /* if 1 then for each object inserted check if it overlaps any of the objects already in the list */
98 static tracked_obj_desc_t *tracked_objs_arr[MEMTRACK_NUM_OF_MEMTYPES];
100 static const char *rsc_names[MEMTRACK_NUM_OF_MEMTYPES] = {
107 static const char *rsc_free_names[MEMTRACK_NUM_OF_MEMTYPES] = {
114 static inline const char *memtype_alloc_str(memtrack_memtype_t memtype)
117 case MEMTRACK_KMALLOC:
118 case MEMTRACK_VMALLOC:
119 case MEMTRACK_KMEM_OBJ:
120 return rsc_names[memtype];
122 return "(Unknown allocation type)";
126 static inline const char *memtype_free_str(memtrack_memtype_t memtype)
129 case MEMTRACK_KMALLOC:
130 case MEMTRACK_VMALLOC:
131 case MEMTRACK_KMEM_OBJ:
132 return rsc_free_names[memtype];
134 return "(Unknown allocation type)";
141 static int overlap_a_b(unsigned long a_start, unsigned long a_end,
142 unsigned long b_start, unsigned long b_end)
144 if ((b_start > a_end) || (a_start > b_end)) {
153 static void check_overlap(memtrack_memtype_t memtype,
154 memtrack_meminfo_t * mem_info_p,
155 tracked_obj_desc_t * obj_desc_p)
157 struct list_head *pos, *next;
158 memtrack_meminfo_t *cur;
159 unsigned long start_a, end_a, start_b, end_b;
161 list_for_each_safe(pos, next, &obj_desc_p->tracked_objs_head) {
162 cur = list_entry(pos, memtrack_meminfo_t, list);
164 start_a = mem_info_p->addr;
165 end_a = mem_info_p->addr + mem_info_p->size - 1;
167 end_b = cur->addr + cur->size - 1;
169 if (overlap_a_b(start_a, end_a, start_b, end_b)) {
171 ("%s overlaps! new_start=0x%lx, new_end=0x%lx, item_start=0x%lx, item_end=0x%lx\n",
172 memtype_alloc_str(memtype), mem_info_p->addr,
173 mem_info_p->addr + mem_info_p->size - 1, cur->addr,
174 cur->addr + cur->size - 1);
179 /* Invoke on memory allocation */
180 void memtrack_alloc(memtrack_memtype_t memtype, unsigned long addr,
181 unsigned long size, const char *filename,
182 const unsigned long line_num, int alloc_flags)
184 unsigned long hash_val;
185 memtrack_meminfo_t *cur_mem_info_p, *new_mem_info_p;
186 tracked_obj_desc_t *obj_desc_p;
189 if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
190 printk("%s: Invalid memory type (%d)\n", __func__, memtype);
194 if (!tracked_objs_arr[memtype]) {
195 /* object is not tracked */
198 obj_desc_p = tracked_objs_arr[memtype];
200 hash_val = addr % MEMTRACK_HASH_SZ;
202 new_mem_info_p = (memtrack_meminfo_t *)
203 kmem_cache_alloc(meminfo_cache, alloc_flags);
204 if (new_mem_info_p == NULL) {
206 ("%s: Failed allocating kmem_cache item for new mem_info. "
207 "Lost tracking on allocation at %s:%lu...\n", __func__,
211 /* save allocation properties */
212 new_mem_info_p->addr = addr;
213 new_mem_info_p->size = size;
214 new_mem_info_p->line_num = line_num;
215 /* Make sure that we will print out the path tail if the given filename is longer
216 * than MAX_FILENAME_LEN. (otherwise, we will not see the name of the actual file
217 * in the printout -- only the path head!
219 if (strlen(filename) > MAX_FILENAME_LEN) {
220 strncpy(new_mem_info_p->filename, filename + strlen(filename) - MAX_FILENAME_LEN, MAX_FILENAME_LEN);
222 strncpy(new_mem_info_p->filename, filename, MAX_FILENAME_LEN);
224 new_mem_info_p->filename[MAX_FILENAME_LEN] = 0; /* NULL terminate anyway */
226 memtrack_spin_lock(&obj_desc_p->hash_lock, flags);
227 /* make sure given memory location is not already allocated */
228 cur_mem_info_p = obj_desc_p->mem_hash[hash_val];
229 while (cur_mem_info_p != NULL) {
230 if (cur_mem_info_p->addr == addr) {
231 /* Found given address in the database */
233 ("mtl rsc inconsistency: %s: %s::%lu: %s @ addr=0x%lX which is already known from %s:%lu\n",
234 __func__, filename, line_num,
235 memtype_alloc_str(memtype), addr,
236 cur_mem_info_p->filename,
237 cur_mem_info_p->line_num);
238 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
239 kmem_cache_free(meminfo_cache, new_mem_info_p);
242 cur_mem_info_p = cur_mem_info_p->next;
244 /* not found - we can put in the hash bucket */
246 new_mem_info_p->next = obj_desc_p->mem_hash[hash_val];
247 obj_desc_p->mem_hash[hash_val] = new_mem_info_p;
248 if (obj_desc_p->strict_track) {
249 check_overlap(memtype, new_mem_info_p, obj_desc_p);
251 obj_desc_p->count += size;
252 list_add(&new_mem_info_p->list, &obj_desc_p->tracked_objs_head);
254 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
258 /* Invoke on memory free */
259 void memtrack_free(memtrack_memtype_t memtype, unsigned long addr,
260 const char *filename, const unsigned long line_num)
262 unsigned long hash_val;
263 memtrack_meminfo_t *cur_mem_info_p, *prev_mem_info_p;
264 tracked_obj_desc_t *obj_desc_p;
267 if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
268 printk("%s: Invalid memory type (%d)\n", __func__, memtype);
272 if (!tracked_objs_arr[memtype]) {
273 /* object is not tracked */
276 obj_desc_p = tracked_objs_arr[memtype];
278 hash_val = addr % MEMTRACK_HASH_SZ;
280 memtrack_spin_lock(&obj_desc_p->hash_lock, flags);
281 /* find mem_info of given memory location */
282 prev_mem_info_p = NULL;
283 cur_mem_info_p = obj_desc_p->mem_hash[hash_val];
284 while (cur_mem_info_p != NULL) {
285 if (cur_mem_info_p->addr == addr) {
286 /* Found given address in the database - remove from the bucket/list */
287 if (prev_mem_info_p == NULL) {
288 obj_desc_p->mem_hash[hash_val] = cur_mem_info_p->next; /* removing first */
290 prev_mem_info_p->next = cur_mem_info_p->next; /* "crossover" */
292 list_del(&cur_mem_info_p->list);
294 obj_desc_p->count -= cur_mem_info_p->size;
295 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
296 kmem_cache_free(meminfo_cache, cur_mem_info_p);
299 prev_mem_info_p = cur_mem_info_p;
300 cur_mem_info_p = cur_mem_info_p->next;
305 ("mtl rsc inconsistency: %s: %s::%lu: %s for unknown address=0x%lX\n",
306 __func__, filename, line_num, memtype_free_str(memtype), addr);
307 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
311 /* Report current allocations status (for all memory types) */
312 static void memtrack_report(void)
314 memtrack_memtype_t memtype;
315 unsigned long cur_bucket;
316 memtrack_meminfo_t *cur_mem_info_p;
318 tracked_obj_desc_t *obj_desc_p;
321 printk("%s: Currently known allocations:\n", __func__);
322 for (memtype = 0; memtype < MEMTRACK_NUM_OF_MEMTYPES; memtype++) {
323 if (tracked_objs_arr[memtype]) {
324 printk("%d) %s:\n", serial, memtype_alloc_str(memtype));
325 obj_desc_p = tracked_objs_arr[memtype];
326 /* Scan all buckets to find existing allocations */
327 /* TBD: this may be optimized by holding a linked list of all hash items */
328 for (cur_bucket = 0; cur_bucket < MEMTRACK_HASH_SZ;
330 memtrack_spin_lock(&obj_desc_p->hash_lock, flags); /* protect per bucket/list */
332 obj_desc_p->mem_hash[cur_bucket];
333 while (cur_mem_info_p != NULL) { /* scan bucket */
334 printk("%s::%lu: %s(%lu)==%lX\n",
335 cur_mem_info_p->filename,
336 cur_mem_info_p->line_num,
337 memtype_alloc_str(memtype),
338 cur_mem_info_p->size,
339 cur_mem_info_p->addr);
340 cur_mem_info_p = cur_mem_info_p->next;
341 } /* while cur_mem_info_p */
342 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
343 } /* for cur_bucket */
351 static struct proc_dir_entry *memtrack_tree;
353 static memtrack_memtype_t get_rsc_by_name(const char *name)
355 memtrack_memtype_t i;
357 for (i=0; i<MEMTRACK_NUM_OF_MEMTYPES; ++i) {
358 if (strcmp(name, rsc_names[i]) == 0) {
367 static ssize_t memtrack_read(struct file *filp,
372 unsigned long cur, flags;
373 loff_t pos = *offset;
374 static char kbuf[20];
376 int _read, to_ret, left;
378 memtrack_memtype_t memtype;
383 fname= filp->f_dentry->d_name.name;
385 memtype= get_rsc_by_name(fname);
386 if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
387 printk("invalid file name\n");
392 memtrack_spin_lock(&tracked_objs_arr[memtype]->hash_lock, flags);
393 cur= tracked_objs_arr[memtype]->count;
394 memtrack_spin_unlock(&tracked_objs_arr[memtype]->hash_lock, flags);
395 _read = sprintf(kbuf, "%lu\n", cur);
404 left = file_len - pos;
405 to_ret = (left < size) ? left : size;
406 if ( copy_to_user(buf, kbuf+pos, to_ret) ) {
410 *offset = pos + to_ret;
415 static struct file_operations memtrack_proc_fops = {
416 .read = memtrack_read,
419 static const char *memtrack_proc_entry_name = "mt_memtrack";
421 static int create_procfs_tree(void)
423 struct proc_dir_entry *dir_ent;
424 struct proc_dir_entry *proc_ent;
426 unsigned long bit_mask;
428 dir_ent = proc_mkdir(memtrack_proc_entry_name, NULL);
433 memtrack_tree = dir_ent;
435 for (i=0, bit_mask=1; i<MEMTRACK_NUM_OF_MEMTYPES; ++i, bit_mask<<=1) {
436 if (bit_mask & track_mask) {
437 proc_ent = create_proc_entry(rsc_names[i], S_IRUGO, memtrack_tree);
439 goto undo_create_root;
441 proc_ent->proc_fops = &memtrack_proc_fops;
448 for (j=0, bit_mask=1; j<i; ++j, bit_mask<<=1) {
449 if (bit_mask & track_mask) {
450 remove_proc_entry(rsc_names[j], memtrack_tree);
453 remove_proc_entry(memtrack_proc_entry_name, NULL);
461 static void destroy_procfs_tree(void)
464 unsigned long bit_mask;
466 for (i=0, bit_mask=1; i<MEMTRACK_NUM_OF_MEMTYPES; ++i, bit_mask<<=1) {
467 if (bit_mask & track_mask) {
468 remove_proc_entry(rsc_names[i], memtrack_tree);
471 remove_proc_entry(memtrack_proc_entry_name, NULL);
475 /* module entry points */
477 int init_module(void)
479 memtrack_memtype_t i;
481 unsigned long bit_mask;
484 /* create a cache for the memtrack_meminfo_t strcutures */
485 meminfo_cache = kmem_cache_create("memtrack_meminfo_t",
486 sizeof(memtrack_meminfo_t), 0,
487 SLAB_HWCACHE_ALIGN, NULL);
488 if (!meminfo_cache) {
489 printk("memtrack::%s: failed to allocate meminfo cache\n", __func__);
493 /* initialize array of descriptors */
494 memset(tracked_objs_arr, 0, sizeof(tracked_objs_arr));
496 /* create a tracking object descriptor for all required objects */
497 for (i = 0, bit_mask = 1; i < MEMTRACK_NUM_OF_MEMTYPES;
498 ++i, bit_mask <<= 1) {
499 if (bit_mask & track_mask) {
500 tracked_objs_arr[i] =
501 vmalloc(sizeof(tracked_obj_desc_t));
502 if (!tracked_objs_arr[i]) {
503 printk("memtrack: failed to allocate tracking object\n");
504 goto undo_cache_create;
507 memset(tracked_objs_arr[i], 0, sizeof(tracked_obj_desc_t));
508 spin_lock_init(&tracked_objs_arr[i]->hash_lock);
509 INIT_LIST_HEAD(&tracked_objs_arr[i]->tracked_objs_head);
510 if (bit_mask & strict_track_mask) {
511 tracked_objs_arr[i]->strict_track = 1;
513 tracked_objs_arr[i]->strict_track = 0;
519 if ( create_procfs_tree() ) {
520 printk("%s: create_procfs_tree() failed\n", __FILE__);
521 goto undo_cache_create;
525 printk("memtrack::%s done.\n", __func__);
530 for (j=0; j<i; ++j) {
531 if (tracked_objs_arr[j]) {
532 vfree(tracked_objs_arr[j]);
536 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
537 if (kmem_cache_destroy(meminfo_cache) != 0) {
538 printk("Failed on kmem_cache_destroy !\n");
541 kmem_cache_destroy(meminfo_cache);
547 void cleanup_module(void)
549 memtrack_memtype_t memtype;
550 unsigned long cur_bucket;
551 memtrack_meminfo_t *cur_mem_info_p, *next_mem_info_p;
552 tracked_obj_desc_t *obj_desc_p;
559 destroy_procfs_tree();
561 /* clean up any hash table left-overs */
562 for (memtype = 0; memtype < MEMTRACK_NUM_OF_MEMTYPES; memtype++) {
563 /* Scan all buckets to find existing allocations */
564 /* TBD: this may be optimized by holding a linked list of all hash items */
565 if (tracked_objs_arr[memtype]) {
566 obj_desc_p = tracked_objs_arr[memtype];
567 for (cur_bucket = 0; cur_bucket < MEMTRACK_HASH_SZ;
569 memtrack_spin_lock(&obj_desc_p->hash_lock, flags); /* protect per bucket/list */
571 obj_desc_p->mem_hash[cur_bucket];
572 while (cur_mem_info_p != NULL) { /* scan bucket */
573 next_mem_info_p = cur_mem_info_p->next; /* save "next" pointer before the "free" */
574 kmem_cache_free(meminfo_cache,
576 cur_mem_info_p = next_mem_info_p;
577 } /* while cur_mem_info_p */
578 memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
579 } /* for cur_bucket */
584 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
585 if (kmem_cache_destroy(meminfo_cache) != 0) {
587 ("memtrack::cleanup_module: Failed on kmem_cache_destroy !\n");
590 kmem_cache_destroy(meminfo_cache);
592 printk("memtrack::cleanup_module done.\n");
595 EXPORT_SYMBOL(memtrack_alloc);
596 EXPORT_SYMBOL(memtrack_free);
598 //module_init(memtrack_init)
599 //module_exit(memtrack_exit)