History

memcg: clear mm->owner when last possible owner leaves

The following crash was reported: > Call Trace: > [<ffffffff81139792>] mem_cgroup_from_task+0x15/0x17 > [<ffffffff8113a75a>] __mem_cgroup_try_charge+0x148/0x4b4 > [<ffffffff810493f3>] ? need_resched+0x23/0x2d > [<ffffffff814cbf43>] ? preempt_schedule+0x46/0x4f > [<ffffffff8113afe8>] mem_cgroup_charge_common+0x9a/0xce > [<ffffffff8113b6d1>] mem_cgroup_newpage_charge+0x5d/0x5f > [<ffffffff81134024>] khugepaged+0x5da/0xfaf > [<ffffffff81078ea0>] ? __init_waitqueue_head+0x4b/0x4b > [<ffffffff81133a4a>] ? add_mm_counter.constprop.5+0x13/0x13 > [<ffffffff81078625>] kthread+0xa8/0xb0 > [<ffffffff814d13e8>] ? sub_preempt_count+0xa1/0xb4 > [<ffffffff814d5664>] kernel_thread_helper+0x4/0x10 > [<ffffffff814ce858>] ? retint_restore_args+0x13/0x13 > [<ffffffff8107857d>] ? __init_kthread_worker+0x5a/0x5a What happens is that khugepaged tries to charge a huge page against an mm whose last possible owner has already exited, and the memory controller crashes when the stale mm->owner is used to look up the cgroup to charge. mm->owner has never been set to NULL with the last owner going away, but nobody cared until khugepaged came along. Even then it wasn't a problem because the final mmput() on an mm was forced to acquire and release mmap_sem in write-mode, preventing an exiting owner to go away while the mmap_sem was held, and until "692e0b3 mm: thp: optimize memcg charge in khugepaged", the memory cgroup charge was protected by mmap_sem in read-mode. Instead of going back to relying on the mmap_sem to enforce lifetime of a task, this patch ensures that mm->owner is properly set to NULL when the last possible owner is exiting, which the memory controller can handle just fine. [akpm@linux-foundation.org: tweak comments] Signed-off-by: Hugh Dickins <hughd@google.com> Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reported-by: Hugh Dickins <hughd@google.com> Reported-by: Dave Jones <davej@redhat.com> Reviewed-by: Andrea Arcangeli <aarcange@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

KAMEZAWA Hiroyuki committed 15 years ago

Commit 733eda71 parent 37573e81 file · +15 −16Browse files

Unified · Split

kernel/exit.c+15−16
@@ -561,29 +561,28 @@ void exit_files(struct task_struct *tsk) · 560 unmodified lines
561561
562562 #ifdef CONFIG_MM_OWNER
563563 /*
564− * Task p is exiting and it owned mm, lets find a new owner for it
564+ * A task is exiting. If it owned this mm, find a new owner for the mm.
565565 */
566−static inline int
567−mm_need_new_owner(struct mm_struct *mm, struct task_struct *p)
568−{
569− /*
570− * If there are other users of the mm and the owner (us) is exiting
571− * we need to find a new owner to take on the responsibility.
572− */
573− if (atomic_read(&mm->mm_users) <= 1)
574− return 0;
575− if (mm->owner != p)
576− return 0;
577− return 1;
578−}
579−
580566 void mm_update_next_owner(struct mm_struct *mm)
581567 {
582568 struct task_struct *c, *g, *p = current;
583569
584570 retry:
585− if (!mm_need_new_owner(mm, p))
571+ /*
572+ * If the exiting or execing task is not the owner, it's
573+ * someone else's problem.
574+ */
575+ if (mm->owner != p)
586576 return;
577+ /*
578+ * The current owner is exiting/execing and there are no other
579+ * candidates. Do not leave the mm pointing to a possibly
580+ * freed task structure.
581+ */
582+ if (atomic_read(&mm->mm_users) <= 1) {
583+ mm->owner = NULL;
584+ return;
585+ }
587586
588587 read_lock(&tasklist_lock);
589588 /*