|
@@ -1600,6 +1600,8 @@ static void kmemleak_clear(void)
|
|
|
kmemleak_found_leaks = false;
|
|
|
}
|
|
|
|
|
|
+static void __kmemleak_do_cleanup(void);
|
|
|
+
|
|
|
/*
|
|
|
* File write operation to configure kmemleak at run-time. The following
|
|
|
* commands can be written to the /sys/kernel/debug/kmemleak file:
|
|
@@ -1612,7 +1614,8 @@ static void kmemleak_clear(void)
|
|
|
* disable it)
|
|
|
* scan - trigger a memory scan
|
|
|
* 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
|
|
|
*/
|
|
|
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 ret;
|
|
|
|
|
|
- if (!atomic_read(&kmemleak_enabled))
|
|
|
- return -EBUSY;
|
|
|
-
|
|
|
buf_size = min(size, (sizeof(buf) - 1));
|
|
|
if (strncpy_from_user(buf, user_buf, buf_size) < 0)
|
|
|
return -EFAULT;
|
|
@@ -1634,6 +1634,19 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
|
|
|
if (ret < 0)
|
|
|
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)
|
|
|
kmemleak_disable();
|
|
|
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)
|
|
|
kmemleak_scan();
|
|
|
- else if (strncmp(buf, "clear", 5) == 0)
|
|
|
- kmemleak_clear();
|
|
|
else if (strncmp(buf, "dump=", 5) == 0)
|
|
|
ret = dump_str_object_info(buf + 5);
|
|
|
else
|
|
@@ -1683,6 +1694,16 @@ static const struct file_operations kmemleak_fops = {
|
|
|
.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
|
|
|
* 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)
|
|
|
{
|
|
|
- struct kmemleak_object *object;
|
|
|
-
|
|
|
mutex_lock(&scan_mutex);
|
|
|
stop_scan_thread();
|
|
|
|
|
|
- if (!kmemleak_found_leaks) {
|
|
|
- rcu_read_lock();
|
|
|
- list_for_each_entry_rcu(object, &object_list, object_list)
|
|
|
- delete_object_full(object->pointer);
|
|
|
- rcu_read_unlock();
|
|
|
- }
|
|
|
+ if (!kmemleak_found_leaks)
|
|
|
+ __kmemleak_do_cleanup();
|
|
|
+ else
|
|
|
+ pr_info("Kmemleak disabled without freeing internal data. "
|
|
|
+ "Reclaim the memory with \"echo clear > /sys/kernel/debug/kmemleak\"\n");
|
|
|
mutex_unlock(&scan_mutex);
|
|
|
}
|
|
|
|