summaryrefslogtreecommitdiff
path: root/mm/kmemleak.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/kmemleak.c')
-rw-r--r--mm/kmemleak.c77
1 files changed, 54 insertions, 23 deletions
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index e196f53f9b46..e3c9dbde014b 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1525,22 +1525,25 @@ static int scan_should_stop(void)
/*
* Scan a memory block (exclusive range) for valid pointers and add those
- * found to the gray list.
+ * found to the gray list. Return non-zero if the scan was interrupted.
*/
-static void scan_block(void *_start, void *_end,
- struct kmemleak_object *scanned)
+static int scan_block(void *_start, void *_end,
+ struct kmemleak_object *scanned)
{
unsigned long *ptr;
unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER);
unsigned long *end = _end - (BYTES_PER_POINTER - 1);
unsigned long flags;
+ int stop = 0;
raw_spin_lock_irqsave(&kmemleak_lock, flags);
for (ptr = start; ptr < end; ptr++) {
unsigned long pointer;
- if (scan_should_stop())
+ if (scan_should_stop()) {
+ stop = 1;
break;
+ }
kasan_disable_current();
pointer = *(unsigned long *)kasan_reset_tag((void *)ptr);
@@ -1550,6 +1553,8 @@ static void scan_block(void *_start, void *_end,
pointer_update_refs(scanned, pointer, OBJECT_PERCPU);
}
raw_spin_unlock_irqrestore(&kmemleak_lock, flags);
+
+ return stop;
}
/*
@@ -1564,7 +1569,7 @@ static void scan_large_block(void *start, void *end)
next = min(start + MAX_SCAN_SIZE, end);
scan_block(start, next, NULL);
start = next;
- cond_resched();
+ cond_resched_tasks_rcu_qs();
}
}
#endif
@@ -1599,7 +1604,7 @@ static void scan_object(struct kmemleak_object *object)
scan_block(start, end, object);
raw_spin_unlock_irqrestore(&object->lock, flags);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
if (!(object->flags & OBJECT_ALLOCATED))
break;
@@ -1621,7 +1626,7 @@ static void scan_object(struct kmemleak_object *object)
break;
raw_spin_unlock_irqrestore(&object->lock, flags);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
raw_spin_lock_irqsave(&object->lock, flags);
} while (object->flags & OBJECT_ALLOCATED);
} else {
@@ -1649,7 +1654,7 @@ static void scan_gray_list(void)
*/
object = list_entry(gray_list.next, typeof(*object), gray_list);
while (&object->gray_list != &gray_list) {
- cond_resched();
+ cond_resched_tasks_rcu_qs();
/* may add new objects to the list */
if (!scan_should_stop())
@@ -1684,7 +1689,7 @@ static void kmemleak_cond_resched(struct kmemleak_object *object)
raw_spin_unlock_irq(&kmemleak_lock);
rcu_read_unlock();
- cond_resched();
+ cond_resched_tasks_rcu_qs();
rcu_read_lock();
raw_spin_lock_irq(&kmemleak_lock);
@@ -1697,6 +1702,43 @@ unlock_put:
}
/*
+ * Scan all task kernel stacks, rescheduling between tasks. Each task is looked
+ * up and pinned within its own RCU read-side section, so no lock is held across
+ * the scan and the walk cannot trip the soft lockup watchdog.
+ */
+static void kmemleak_scan_task_stacks(void)
+{
+ struct pid *pid;
+ int nr = 1;
+ int stop = 0;
+
+ do {
+ struct task_struct *p = NULL;
+
+ rcu_read_lock();
+ pid = find_ge_pid(nr, &init_pid_ns);
+ if (pid) {
+ nr = pid_nr(pid) + 1;
+ p = pid_task(pid, PIDTYPE_PID);
+ if (p)
+ get_task_struct(p);
+ }
+ rcu_read_unlock();
+
+ if (p) {
+ void *stack = try_get_task_stack(p);
+
+ if (stack) {
+ stop = scan_block(stack, stack + THREAD_SIZE, NULL);
+ put_task_stack(p);
+ }
+ put_task_struct(p);
+ }
+ cond_resched_tasks_rcu_qs();
+ } while (pid && !stop);
+}
+
+/*
* Print one leak inline. The hex dump is gated on OBJECT_ALLOCATED so it
* does not touch user memory that was freed concurrently; the rest of the
* report (backtrace, comm, pid) is always emitted since the kmemleak_object
@@ -1866,7 +1908,7 @@ static void kmemleak_scan(void)
struct page *page = pfn_to_online_page(pfn);
if (!(pfn & 63))
- cond_resched();
+ cond_resched_tasks_rcu_qs();
if (!page)
continue;
@@ -1885,19 +1927,8 @@ static void kmemleak_scan(void)
/*
* Scanning the task stacks (may introduce false negatives).
*/
- if (kmemleak_stack_scan) {
- struct task_struct *p, *g;
-
- rcu_read_lock();
- for_each_process_thread(g, p) {
- void *stack = try_get_task_stack(p);
- if (stack) {
- scan_block(stack, stack + THREAD_SIZE, NULL);
- put_task_stack(p);
- }
- }
- rcu_read_unlock();
- }
+ if (kmemleak_stack_scan)
+ kmemleak_scan_task_stacks();
/*
* Scan the objects already referenced from the sections scanned