]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/ofed/drivers/infiniband/debug/memtrack.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / ofed / drivers / infiniband / debug / memtrack.c
1 /*
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>.
9
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
17   SOFTWARE.
18
19   Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
20 */
21
22 #define C_MEMTRACK_C
23
24 #ifdef kmalloc
25         #undef kmalloc
26 #endif
27 #ifdef kfree
28         #undef kfree
29 #endif
30 #ifdef vmalloc
31         #undef vmalloc
32 #endif
33 #ifdef vfree
34         #undef vfree
35 #endif
36 #ifdef kmem_cache_alloc
37         #undef kmem_cache_alloc
38 #endif
39 #ifdef kmem_cache_free
40         #undef kmem_cache_free
41 #endif
42
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>
51 #include <memtrack.h>
52
53 #include <linux/moduleparam.h>
54
55
56 MODULE_AUTHOR("Mellanox Technologies LTD.");
57 MODULE_DESCRIPTION("Memory allocations tracking");
58 MODULE_LICENSE("GPL");
59
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
62
63 #define memtrack_spin_lock(spl, flags)     spin_lock_irqsave(spl, flags)
64 #define memtrack_spin_unlock(spl, flags)   spin_unlock_irqrestore(spl, flags)
65
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");
71
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");
78
79 typedef struct memtrack_meminfo_st {
80         unsigned long addr;
81         unsigned long size;
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 */
86 } memtrack_meminfo_t;
87
88 static struct kmem_cache *meminfo_cache;
89
90 typedef struct {
91         memtrack_meminfo_t *mem_hash[MEMTRACK_HASH_SZ];
92         spinlock_t hash_lock;
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 */
96 } tracked_obj_desc_t;
97
98 static tracked_obj_desc_t *tracked_objs_arr[MEMTRACK_NUM_OF_MEMTYPES];
99
100 static const char *rsc_names[MEMTRACK_NUM_OF_MEMTYPES] = {
101         "kmalloc",
102         "vmalloc",
103         "kmem_cache_alloc"
104 };
105
106
107 static const char *rsc_free_names[MEMTRACK_NUM_OF_MEMTYPES] = {
108         "kfree",
109         "vfree",
110         "kmem_cache_free"
111 };
112
113
114 static inline const char *memtype_alloc_str(memtrack_memtype_t memtype)
115 {
116         switch (memtype) {
117                 case MEMTRACK_KMALLOC:
118                 case MEMTRACK_VMALLOC:
119                 case MEMTRACK_KMEM_OBJ:
120                         return rsc_names[memtype];
121                 default:
122                         return "(Unknown allocation type)";
123         }
124 }
125
126 static inline const char *memtype_free_str(memtrack_memtype_t memtype)
127 {
128         switch (memtype) {
129                 case MEMTRACK_KMALLOC:
130                 case MEMTRACK_VMALLOC:
131                 case MEMTRACK_KMEM_OBJ:
132                         return rsc_free_names[memtype];
133                 default:
134                         return "(Unknown allocation type)";
135         }
136 }
137
138 /*
139  *  overlap_a_b
140  */
141 static int overlap_a_b(unsigned long a_start, unsigned long a_end,
142                        unsigned long b_start, unsigned long b_end)
143 {
144         if ((b_start > a_end) || (a_start > b_end)) {
145                 return 0;
146         }
147         return 1;
148 }
149
150 /*
151  *  check_overlap
152  */
153 static void check_overlap(memtrack_memtype_t memtype,
154                           memtrack_meminfo_t * mem_info_p,
155                           tracked_obj_desc_t * obj_desc_p)
156 {
157         struct list_head *pos, *next;
158         memtrack_meminfo_t *cur;
159         unsigned long start_a, end_a, start_b, end_b;
160
161         list_for_each_safe(pos, next, &obj_desc_p->tracked_objs_head) {
162                 cur = list_entry(pos, memtrack_meminfo_t, list);
163
164                 start_a = mem_info_p->addr;
165                 end_a = mem_info_p->addr + mem_info_p->size - 1;
166                 start_b = cur->addr;
167                 end_b = cur->addr + cur->size - 1;
168
169                 if (overlap_a_b(start_a, end_a, start_b, end_b)) {
170                         printk
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);
175                 }
176         }
177 }
178
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)
183 {
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;
187         unsigned long flags;
188
189         if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
190                 printk("%s: Invalid memory type (%d)\n", __func__, memtype);
191                 return;
192         }
193
194         if (!tracked_objs_arr[memtype]) {
195                 /* object is not tracked */
196                 return;
197         }
198         obj_desc_p = tracked_objs_arr[memtype];
199
200         hash_val = addr % MEMTRACK_HASH_SZ;
201
202         new_mem_info_p = (memtrack_meminfo_t *)
203             kmem_cache_alloc(meminfo_cache, alloc_flags);
204         if (new_mem_info_p == NULL) {
205                 printk
206                     ("%s: Failed allocating kmem_cache item for new mem_info. "
207                      "Lost tracking on allocation at %s:%lu...\n", __func__,
208                      filename, line_num);
209                 return;
210         }
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!
218          */
219         if (strlen(filename) > MAX_FILENAME_LEN) {
220           strncpy(new_mem_info_p->filename, filename + strlen(filename) - MAX_FILENAME_LEN, MAX_FILENAME_LEN);
221         } else {
222           strncpy(new_mem_info_p->filename, filename, MAX_FILENAME_LEN);
223         }
224         new_mem_info_p->filename[MAX_FILENAME_LEN] = 0; /* NULL terminate anyway */
225
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 */
232                         printk
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);
240                         return;
241                 }
242                 cur_mem_info_p = cur_mem_info_p->next;
243         }
244         /* not found - we can put in the hash bucket */
245         /* link as first */
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);
250         }
251         obj_desc_p->count += size;
252         list_add(&new_mem_info_p->list, &obj_desc_p->tracked_objs_head);
253
254         memtrack_spin_unlock(&obj_desc_p->hash_lock, flags);
255         return;
256 }
257
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)
261 {
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;
265         unsigned long flags;
266
267         if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
268                 printk("%s: Invalid memory type (%d)\n", __func__, memtype);
269                 return;
270         }
271
272         if (!tracked_objs_arr[memtype]) {
273                 /* object is not tracked */
274                 return;
275         }
276         obj_desc_p = tracked_objs_arr[memtype];
277
278         hash_val = addr % MEMTRACK_HASH_SZ;
279
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 */
289                         } else {
290                                 prev_mem_info_p->next = cur_mem_info_p->next;   /* "crossover" */
291                         }
292                         list_del(&cur_mem_info_p->list);
293
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);
297                         return;
298                 }
299                 prev_mem_info_p = cur_mem_info_p;
300                 cur_mem_info_p = cur_mem_info_p->next;
301         }
302
303         /* not found */
304         printk
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);
308         return;
309 }
310
311 /* Report current allocations status (for all memory types) */
312 static void memtrack_report(void)
313 {
314         memtrack_memtype_t memtype;
315         unsigned long cur_bucket;
316         memtrack_meminfo_t *cur_mem_info_p;
317         int serial = 1;
318         tracked_obj_desc_t *obj_desc_p;
319         unsigned long flags;
320
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;
329                              cur_bucket++) {
330                                 memtrack_spin_lock(&obj_desc_p->hash_lock, flags);      /* protect per bucket/list */
331                                 cur_mem_info_p =
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 */
344                         serial++;
345                 }
346         }                       /* for memtype */
347 }
348
349
350
351 static struct proc_dir_entry *memtrack_tree;
352
353 static memtrack_memtype_t get_rsc_by_name(const char *name)
354 {
355         memtrack_memtype_t i;
356
357         for (i=0; i<MEMTRACK_NUM_OF_MEMTYPES; ++i) {
358                 if (strcmp(name, rsc_names[i]) == 0) {
359                         return i;
360                 }
361         }
362
363         return i;
364 }
365
366
367 static ssize_t memtrack_read(struct file *filp,
368                                                  char __user *buf,
369                                                          size_t size,
370                                                          loff_t *offset)
371 {
372         unsigned long cur, flags;
373         loff_t pos = *offset;
374         static char kbuf[20];
375         static int file_len;
376         int _read, to_ret, left;
377         const char *fname;
378         memtrack_memtype_t memtype;
379
380         if (pos < 0)
381                 return -EINVAL;
382
383         fname= filp->f_dentry->d_name.name;
384
385         memtype= get_rsc_by_name(fname);
386         if (memtype >= MEMTRACK_NUM_OF_MEMTYPES) {
387                 printk("invalid file name\n");
388                 return -EINVAL;
389         }
390
391         if ( pos == 0 ) {
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);
396                 if ( _read < 0 ) {
397                         return _read;
398                 }
399                 else {
400                         file_len = _read;
401                 }
402         }
403
404         left = file_len - pos;
405         to_ret = (left < size) ? left : size;
406         if ( copy_to_user(buf, kbuf+pos, to_ret) ) {
407                 return -EFAULT;
408         }
409         else {
410                 *offset = pos + to_ret;
411                 return to_ret;
412         }
413 }
414
415 static struct file_operations memtrack_proc_fops = {
416         .read = memtrack_read,
417 };
418
419 static const char *memtrack_proc_entry_name = "mt_memtrack";
420
421 static int create_procfs_tree(void)
422 {
423         struct proc_dir_entry *dir_ent;
424         struct proc_dir_entry *proc_ent;
425         int i, j;
426         unsigned long bit_mask;
427
428         dir_ent = proc_mkdir(memtrack_proc_entry_name, NULL);
429         if ( !dir_ent ) {
430                 return -1;
431         }
432
433         memtrack_tree = dir_ent;
434
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);
438                         if ( !proc_ent )
439                                 goto undo_create_root;
440
441                         proc_ent->proc_fops = &memtrack_proc_fops;
442                 }
443         }
444
445         goto exit_ok;
446
447 undo_create_root:
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);
451                 }
452         }
453         remove_proc_entry(memtrack_proc_entry_name, NULL);
454         return -1;
455
456 exit_ok:
457         return 0;
458 }
459
460
461 static void destroy_procfs_tree(void)
462 {
463         int i;
464         unsigned long bit_mask;
465
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);
469                 }
470         }
471         remove_proc_entry(memtrack_proc_entry_name, NULL);
472 }
473
474
475 /* module entry points */
476
477 int init_module(void)
478 {
479         memtrack_memtype_t i;
480         int j;
481         unsigned long bit_mask;
482
483
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__);
490                 return -1;
491         }
492
493         /* initialize array of descriptors */
494         memset(tracked_objs_arr, 0, sizeof(tracked_objs_arr));
495
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;
505                         }
506
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;
512                         } else {
513                                 tracked_objs_arr[i]->strict_track = 0;
514                         }
515                 }
516         }
517
518
519         if ( create_procfs_tree() ) {
520                   printk("%s: create_procfs_tree() failed\n", __FILE__);
521                   goto undo_cache_create;
522         }
523
524
525         printk("memtrack::%s done.\n", __func__);
526
527         return 0;
528
529 undo_cache_create:
530         for (j=0; j<i; ++j) {
531                 if (tracked_objs_arr[j]) {
532                         vfree(tracked_objs_arr[j]);
533                 }
534         }
535
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");
539         }
540 #else
541         kmem_cache_destroy(meminfo_cache);
542 #endif
543         return -1;
544 }
545
546
547 void cleanup_module(void)
548 {
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;
553         unsigned long flags;
554
555
556         memtrack_report();
557
558
559         destroy_procfs_tree();
560
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;
568                              cur_bucket++) {
569                                 memtrack_spin_lock(&obj_desc_p->hash_lock, flags);      /* protect per bucket/list */
570                                 cur_mem_info_p =
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,
575                                                         cur_mem_info_p);
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 */
580                         vfree(obj_desc_p);
581                 }
582         }                       /* for memtype */
583
584 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
585         if (kmem_cache_destroy(meminfo_cache) != 0) {
586                 printk
587                     ("memtrack::cleanup_module: Failed on kmem_cache_destroy !\n");
588         }
589 #else
590         kmem_cache_destroy(meminfo_cache);
591 #endif
592         printk("memtrack::cleanup_module done.\n");
593 }
594
595 EXPORT_SYMBOL(memtrack_alloc);
596 EXPORT_SYMBOL(memtrack_free);
597
598 //module_init(memtrack_init)
599 //module_exit(memtrack_exit)
600