-
-#ifdef CONFIG_DEBUG_SLAB_LEAK
-
-static void *leaks_start(struct seq_file *m, loff_t *pos)
-{
- loff_t n = *pos;
- struct list_head *p;
-
- mutex_lock(&cache_chain_mutex);
- p = cache_chain.next;
- while (n--) {
- p = p->next;
- if (p == &cache_chain)
- return NULL;
- }
- return list_entry(p, struct kmem_cache, next);
-}
-
-static inline int add_caller(unsigned long *n, unsigned long v)
-{
- unsigned long *p;
- int l;
- if (!v)
- return 1;
- l = n[1];
- p = n + 2;
- while (l) {
- int i = l/2;
- unsigned long *q = p + 2 * i;
- if (*q == v) {
- q[1]++;
- return 1;
- }
- if (*q > v) {
- l = i;
- } else {
- p = q + 2;
- l -= i + 1;
- }
- }
- if (++n[1] == n[0])
- return 0;
- memmove(p + 2, p, n[1] * 2 * sizeof(unsigned long) - ((void *)p - (void *)n));
- p[0] = v;
- p[1] = 1;
- return 1;
-}
-
-static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s)
-{
- void *p;
- int i;
- if (n[0] == n[1])
- return;
- for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) {
- if (slab_bufctl(s)[i] != BUFCTL_ACTIVE)
- continue;
- if (!add_caller(n, (unsigned long)*dbg_userword(c, p)))
- return;
- }
-}
-
-static void show_symbol(struct seq_file *m, unsigned long address)
-{
-#ifdef CONFIG_KALLSYMS
- char *modname;
- const char *name;
- unsigned long offset, size;
- char namebuf[KSYM_NAME_LEN+1];
-
- name = kallsyms_lookup(address, &size, &offset, &modname, namebuf);
-
- if (name) {
- seq_printf(m, "%s+%#lx/%#lx", name, offset, size);
- if (modname)
- seq_printf(m, " [%s]", modname);
- return;
- }
-#endif
- seq_printf(m, "%p", (void *)address);
-}
-
-static int leaks_show(struct seq_file *m, void *p)
-{
- struct kmem_cache *cachep = p;
- struct slab *slabp;
- struct kmem_list3 *l3;
- const char *name;
- unsigned long *n = m->private;
- int node;
- int i;
-
- if (!(cachep->flags & SLAB_STORE_USER))
- return 0;
- if (!(cachep->flags & SLAB_RED_ZONE))
- return 0;
-
- /* OK, we can do it */
-
- n[1] = 0;
-
- for_each_online_node(node) {
- l3 = cachep->nodelists[node];
- if (!l3)
- continue;
-
- check_irq_on();
- spin_lock_irq(&l3->list_lock);
-
- list_for_each_entry(slabp, &l3->slabs_full, list)
- handle_slab(n, cachep, slabp);
- list_for_each_entry(slabp, &l3->slabs_partial, list)
- handle_slab(n, cachep, slabp);
- spin_unlock_irq(&l3->list_lock);
- }
- name = cachep->name;
- if (n[0] == n[1]) {
- /* Increase the buffer size */
- mutex_unlock(&cache_chain_mutex);
- m->private = kzalloc(n[0] * 4 * sizeof(unsigned long), GFP_KERNEL);
- if (!m->private) {
- /* Too bad, we are really out */
- m->private = n;
- mutex_lock(&cache_chain_mutex);
- return -ENOMEM;
- }
- *(unsigned long *)m->private = n[0] * 2;
- kfree(n);
- mutex_lock(&cache_chain_mutex);
- /* Now make sure this entry will be retried */
- m->count = m->size;
- return 0;
- }
- for (i = 0; i < n[1]; i++) {
- seq_printf(m, "%s: %lu ", name, n[2*i+3]);
- show_symbol(m, n[2*i+2]);
- seq_putc(m, '\n');
- }
- return 0;
-}
-
-struct seq_operations slabstats_op = {
- .start = leaks_start,
- .next = s_next,
- .stop = s_stop,
- .show = leaks_show,
-};
-#endif