kmemleak: Do not trigger a scan when reading the debug/kmemleak file

Since there is a kernel thread for automatically scanning the memory, it
makes sense for the debug/kmemleak file to only show its findings. This
patch also adds support for "echo scan > debug/kmemleak" to trigger an
intermediate memory scan and eliminates the kmemleak_mutex (scan_mutex
covers all the cases now).

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Catalin Marinas 2009-06-26 17:38:27 +01:00
parent bab4a34afc
commit 4698c1f2bb
2 changed files with 48 additions and 53 deletions

View file

@ -17,12 +17,16 @@ Usage
CONFIG_DEBUG_KMEMLEAK in "Kernel hacking" has to be enabled. A kernel CONFIG_DEBUG_KMEMLEAK in "Kernel hacking" has to be enabled. A kernel
thread scans the memory every 10 minutes (by default) and prints the thread scans the memory every 10 minutes (by default) and prints the
number of new unreferenced objects found. To trigger an intermediate number of new unreferenced objects found. To display the details of all
scan and display the details of all the possible memory leaks: the possible memory leaks:
# mount -t debugfs nodev /sys/kernel/debug/ # mount -t debugfs nodev /sys/kernel/debug/
# cat /sys/kernel/debug/kmemleak # cat /sys/kernel/debug/kmemleak
To trigger an intermediate memory scan:
# echo scan > /sys/kernel/debug/kmemleak
Note that the orphan objects are listed in the order they were allocated Note that the orphan objects are listed in the order they were allocated
and one object at the beginning of the list may cause other subsequent and one object at the beginning of the list may cause other subsequent
objects to be reported as orphan. objects to be reported as orphan.
@ -37,6 +41,7 @@ Memory scanning parameters can be modified at run-time by writing to the
scan=off - stop the automatic memory scanning thread scan=off - stop the automatic memory scanning thread
scan=<secs> - set the automatic memory scanning period in seconds scan=<secs> - set the automatic memory scanning period in seconds
(default 600, 0 to stop the automatic scanning) (default 600, 0 to stop the automatic scanning)
scan - trigger a memory scan
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
the kernel command line. the kernel command line.

View file

@ -48,10 +48,10 @@
* scanned. This list is only modified during a scanning episode when the * scanned. This list is only modified during a scanning episode when the
* scan_mutex is held. At the end of a scan, the gray_list is always empty. * scan_mutex is held. At the end of a scan, the gray_list is always empty.
* Note that the kmemleak_object.use_count is incremented when an object is * Note that the kmemleak_object.use_count is incremented when an object is
* added to the gray_list and therefore cannot be freed * added to the gray_list and therefore cannot be freed. This mutex also
* - kmemleak_mutex (mutex): prevents multiple users of the "kmemleak" debugfs * prevents multiple users of the "kmemleak" debugfs file together with
* file together with modifications to the memory scanning parameters * modifications to the memory scanning parameters including the scan_thread
* including the scan_thread pointer * pointer
* *
* The kmemleak_object structures have a use_count incremented or decremented * The kmemleak_object structures have a use_count incremented or decremented
* using the get_object()/put_object() functions. When the use_count becomes * using the get_object()/put_object() functions. When the use_count becomes
@ -195,10 +195,8 @@ static unsigned long jiffies_min_age;
static signed long jiffies_scan_wait; static signed long jiffies_scan_wait;
/* enables or disables the task stacks scanning */ /* enables or disables the task stacks scanning */
static int kmemleak_stack_scan = 1; static int kmemleak_stack_scan = 1;
/* mutex protecting the memory scanning */ /* protects the memory scanning, parameters and debug/kmemleak file access */
static DEFINE_MUTEX(scan_mutex); static DEFINE_MUTEX(scan_mutex);
/* mutex protecting the access to the /sys/kernel/debug/kmemleak file */
static DEFINE_MUTEX(kmemleak_mutex);
/* number of leaks reported (for limitation purposes) */ /* number of leaks reported (for limitation purposes) */
static int reported_leaks; static int reported_leaks;
@ -927,6 +925,7 @@ static void kmemleak_scan(void)
struct kmemleak_object *object, *tmp; struct kmemleak_object *object, *tmp;
struct task_struct *task; struct task_struct *task;
int i; int i;
int new_leaks = 0;
/* prepare the kmemleak_object's */ /* prepare the kmemleak_object's */
rcu_read_lock(); rcu_read_lock();
@ -1024,6 +1023,26 @@ static void kmemleak_scan(void)
object = tmp; object = tmp;
} }
WARN_ON(!list_empty(&gray_list)); WARN_ON(!list_empty(&gray_list));
/*
* Scanning result reporting.
*/
rcu_read_lock();
list_for_each_entry_rcu(object, &object_list, object_list) {
spin_lock_irqsave(&object->lock, flags);
if (unreferenced_object(object) &&
!(object->flags & OBJECT_REPORTED)) {
object->flags |= OBJECT_REPORTED;
new_leaks++;
}
spin_unlock_irqrestore(&object->lock, flags);
}
rcu_read_unlock();
if (new_leaks)
pr_info("%d new suspected memory leaks (see "
"/sys/kernel/debug/kmemleak)\n", new_leaks);
} }
/* /*
@ -1045,33 +1064,12 @@ static int kmemleak_scan_thread(void *arg)
} }
while (!kthread_should_stop()) { while (!kthread_should_stop()) {
struct kmemleak_object *object;
signed long timeout = jiffies_scan_wait; signed long timeout = jiffies_scan_wait;
int new_leaks = 0;
mutex_lock(&scan_mutex); mutex_lock(&scan_mutex);
kmemleak_scan(); kmemleak_scan();
rcu_read_lock();
list_for_each_entry_rcu(object, &object_list, object_list) {
unsigned long flags;
spin_lock_irqsave(&object->lock, flags);
if (unreferenced_object(object) &&
!(object->flags & OBJECT_REPORTED)) {
object->flags |= OBJECT_REPORTED;
new_leaks++;
}
spin_unlock_irqrestore(&object->lock, flags);
}
rcu_read_unlock();
if (new_leaks)
pr_info("%d new suspected memory leaks (see "
"/sys/kernel/debug/kmemleak)\n", new_leaks);
mutex_unlock(&scan_mutex); mutex_unlock(&scan_mutex);
/* wait before the next scan */ /* wait before the next scan */
while (timeout && !kthread_should_stop()) while (timeout && !kthread_should_stop())
timeout = schedule_timeout_interruptible(timeout); timeout = schedule_timeout_interruptible(timeout);
@ -1084,7 +1082,7 @@ static int kmemleak_scan_thread(void *arg)
/* /*
* Start the automatic memory scanning thread. This function must be called * Start the automatic memory scanning thread. This function must be called
* with the kmemleak_mutex held. * with the scan_mutex held.
*/ */
void start_scan_thread(void) void start_scan_thread(void)
{ {
@ -1099,7 +1097,7 @@ void start_scan_thread(void)
/* /*
* Stop the automatic memory scanning thread. This function must be called * Stop the automatic memory scanning thread. This function must be called
* with the kmemleak_mutex held. * with the scan_mutex held.
*/ */
void stop_scan_thread(void) void stop_scan_thread(void)
{ {
@ -1119,10 +1117,8 @@ static void *kmemleak_seq_start(struct seq_file *seq, loff_t *pos)
struct kmemleak_object *object; struct kmemleak_object *object;
loff_t n = *pos; loff_t n = *pos;
if (!n) { if (!n)
kmemleak_scan();
reported_leaks = 0; reported_leaks = 0;
}
if (reported_leaks >= REPORTS_NR) if (reported_leaks >= REPORTS_NR)
return NULL; return NULL;
@ -1206,13 +1202,10 @@ static int kmemleak_open(struct inode *inode, struct file *file)
if (!atomic_read(&kmemleak_enabled)) if (!atomic_read(&kmemleak_enabled))
return -EBUSY; return -EBUSY;
ret = mutex_lock_interruptible(&kmemleak_mutex); ret = mutex_lock_interruptible(&scan_mutex);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (file->f_mode & FMODE_READ) { if (file->f_mode & FMODE_READ) {
ret = mutex_lock_interruptible(&scan_mutex);
if (ret < 0)
goto kmemleak_unlock;
ret = seq_open(file, &kmemleak_seq_ops); ret = seq_open(file, &kmemleak_seq_ops);
if (ret < 0) if (ret < 0)
goto scan_unlock; goto scan_unlock;
@ -1221,8 +1214,6 @@ static int kmemleak_open(struct inode *inode, struct file *file)
scan_unlock: scan_unlock:
mutex_unlock(&scan_mutex); mutex_unlock(&scan_mutex);
kmemleak_unlock:
mutex_unlock(&kmemleak_mutex);
out: out:
return ret; return ret;
} }
@ -1231,11 +1222,9 @@ static int kmemleak_release(struct inode *inode, struct file *file)
{ {
int ret = 0; int ret = 0;
if (file->f_mode & FMODE_READ) { if (file->f_mode & FMODE_READ)
seq_release(inode, file); seq_release(inode, file);
mutex_unlock(&scan_mutex); mutex_unlock(&scan_mutex);
}
mutex_unlock(&kmemleak_mutex);
return ret; return ret;
} }
@ -1250,6 +1239,7 @@ static int kmemleak_release(struct inode *inode, struct file *file)
* scan=off - stop the automatic memory scanning thread * scan=off - stop the automatic memory scanning thread
* scan=... - set the automatic memory scanning period in seconds (0 to * scan=... - set the automatic memory scanning period in seconds (0 to
* disable it) * disable it)
* scan - trigger a memory scan
*/ */
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,
size_t size, loff_t *ppos) size_t size, loff_t *ppos)
@ -1287,7 +1277,9 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf,
jiffies_scan_wait = msecs_to_jiffies(secs * 1000); jiffies_scan_wait = msecs_to_jiffies(secs * 1000);
start_scan_thread(); start_scan_thread();
} }
} else } else if (strncmp(buf, "scan", 4) == 0)
kmemleak_scan();
else
return -EINVAL; return -EINVAL;
/* ignore the rest of the buffer, only one command at a time */ /* ignore the rest of the buffer, only one command at a time */
@ -1312,11 +1304,9 @@ static int kmemleak_cleanup_thread(void *arg)
{ {
struct kmemleak_object *object; struct kmemleak_object *object;
mutex_lock(&kmemleak_mutex);
stop_scan_thread();
mutex_unlock(&kmemleak_mutex);
mutex_lock(&scan_mutex); mutex_lock(&scan_mutex);
stop_scan_thread();
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(object, &object_list, object_list) list_for_each_entry_rcu(object, &object_list, object_list)
delete_object(object->pointer); delete_object(object->pointer);
@ -1458,9 +1448,9 @@ static int __init kmemleak_late_init(void)
&kmemleak_fops); &kmemleak_fops);
if (!dentry) if (!dentry)
pr_warning("Failed to create the debugfs kmemleak file\n"); pr_warning("Failed to create the debugfs kmemleak file\n");
mutex_lock(&kmemleak_mutex); mutex_lock(&scan_mutex);
start_scan_thread(); start_scan_thread();
mutex_unlock(&kmemleak_mutex); mutex_unlock(&scan_mutex);
pr_info("Kernel memory leak detector initialized\n"); pr_info("Kernel memory leak detector initialized\n");