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:
parent
dc9b3f4249
commit
c89da70c73
2 changed files with 46 additions and 15 deletions
|
@ -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
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue