kmemleak: allow freeing internal objects after kmemleak was disabled

Currently if kmemleak is disabled, the kmemleak objects can never be
freed, no matter if it's disabled by a user or due to fatal errors.

Those objects can be a big waste of memory.

    OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
  1200264 1197433  99%    0.30K  46164       26    369312K kmemleak_object

With this patch, after kmemleak was disabled you can reclaim memory
with:

	# echo clear > /sys/kernel/debug/kmemleak

Also inform users about this with a printk.

Signed-off-by: Li Zefan <lizefan@huawei.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Li Zefan 2014-04-03 14:46:27 -07:00 committed by Linus Torvalds
parent dc9b3f4249
commit c89da70c73
2 changed files with 46 additions and 15 deletions

View file

@ -53,7 +53,8 @@ Memory scanning parameters can be modified at run-time by writing to the
(default 600, 0 to stop the automatic scanning) (default 600, 0 to stop the automatic scanning)
scan - trigger a memory scan scan - trigger a memory scan
clear - clear list of current memory leak suspects, done by clear - clear list of current memory leak suspects, done by
marking all current reported unreferenced objects grey marking all current reported unreferenced objects grey,
or free all kmemleak objects if kmemleak has been disabled.
dump=<addr> - dump information about the object found at <addr> dump=<addr> - dump information about the object found at <addr>
Kmemleak can also be disabled at boot-time by passing "kmemleak=off" on Kmemleak can also be disabled at boot-time by passing "kmemleak=off" on
@ -120,6 +121,18 @@ Then as usual to get your report with:
# cat /sys/kernel/debug/kmemleak # cat /sys/kernel/debug/kmemleak
Freeing kmemleak internal objects
---------------------------------
To allow access to previosuly found memory leaks after kmemleak has been
disabled by the user or due to an fatal error, internal kmemleak objects
won't be freed when kmemleak is disabled, and those objects may occupy
a large part of physical memory.
In this situation, you may reclaim memory with:
# echo clear > /sys/kernel/debug/kmemleak
Kmemleak API Kmemleak API
------------ ------------

View file

@ -1600,6 +1600,8 @@ static void kmemleak_clear(void)
kmemleak_found_leaks = false; kmemleak_found_leaks = false;
} }
static void __kmemleak_do_cleanup(void);
/* /*
* File write operation to configure kmemleak at run-time. The following * File write operation to configure kmemleak at run-time. The following
* commands can be written to the /sys/kernel/debug/kmemleak file: * commands can be written to the /sys/kernel/debug/kmemleak file:
@ -1612,7 +1614,8 @@ static void kmemleak_clear(void)
* disable it) * disable it)
* scan - trigger a memory scan * scan - trigger a memory scan
* clear - mark all current reported unreferenced kmemleak objects as * clear - mark all current reported unreferenced kmemleak objects as
* grey to ignore printing them * grey to ignore printing them, or free all kmemleak objects
* if kmemleak has been disabled.
* dump=... - dump information about the object found at the given address * dump=... - dump information about the object found at the given address
*/ */
static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
@ -1622,9 +1625,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
int buf_size; int buf_size;
int ret; int ret;
if (!atomic_read(&kmemleak_enabled))
return -EBUSY;
buf_size = min(size, (sizeof(buf) - 1)); buf_size = min(size, (sizeof(buf) - 1));
if (strncpy_from_user(buf, user_buf, buf_size) < 0) if (strncpy_from_user(buf, user_buf, buf_size) < 0)
return -EFAULT; return -EFAULT;
@ -1634,6 +1634,19 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
if (ret < 0) if (ret < 0)
return ret; return ret;
if (strncmp(buf, "clear", 5) == 0) {
if (atomic_read(&kmemleak_enabled))
kmemleak_clear();
else
__kmemleak_do_cleanup();
goto out;
}
if (!atomic_read(&kmemleak_enabled)) {
ret = -EBUSY;
goto out;
}
if (strncmp(buf, "off", 3) == 0) if (strncmp(buf, "off", 3) == 0)
kmemleak_disable(); kmemleak_disable();
else if (strncmp(buf, "stack=on", 8) == 0) else if (strncmp(buf, "stack=on", 8) == 0)
@ -1657,8 +1670,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
} }
} else if (strncmp(buf, "scan", 4) == 0) } else if (strncmp(buf, "scan", 4) == 0)
kmemleak_scan(); kmemleak_scan();
else if (strncmp(buf, "clear", 5) == 0)
kmemleak_clear();
else if (strncmp(buf, "dump=", 5) == 0) else if (strncmp(buf, "dump=", 5) == 0)
ret = dump_str_object_info(buf + 5); ret = dump_str_object_info(buf + 5);
else else
@ -1683,6 +1694,16 @@ static const struct file_operations kmemleak_fops = {
.release = kmemleak_release, .release = kmemleak_release,
}; };
static void __kmemleak_do_cleanup(void)
{
struct kmemleak_object *object;
rcu_read_lock();
list_for_each_entry_rcu(object, &object_list, object_list)
delete_object_full(object->pointer);
rcu_read_unlock();
}
/* /*
* Stop the memory scanning thread and free the kmemleak internal objects if * Stop the memory scanning thread and free the kmemleak internal objects if
* no previous scan thread (otherwise, kmemleak may still have some useful * no previous scan thread (otherwise, kmemleak may still have some useful
@ -1690,17 +1711,14 @@ static const struct file_operations kmemleak_fops = {
*/ */
static void kmemleak_do_cleanup(struct work_struct *work) static void kmemleak_do_cleanup(struct work_struct *work)
{ {
struct kmemleak_object *object;
mutex_lock(&scan_mutex); mutex_lock(&scan_mutex);
stop_scan_thread(); stop_scan_thread();
if (!kmemleak_found_leaks) { if (!kmemleak_found_leaks)
rcu_read_lock(); __kmemleak_do_cleanup();
list_for_each_entry_rcu(object, &object_list, object_list) else
delete_object_full(object->pointer); pr_info("Kmemleak disabled without freeing internal data. "
rcu_read_unlock(); "Reclaim the memory with \"echo clear > /sys/kernel/debug/kmemleak\"\n");
}
mutex_unlock(&scan_mutex); mutex_unlock(&scan_mutex);
} }