Skip to content

Commit

Permalink
ksm: fix conflict between mmput and scan_get_next_rmap_item
Browse files Browse the repository at this point in the history
A concurrency issue about KSM in the function scan_get_next_rmap_item.

task A (ksmd):				|task B (the mm's task):
					|
mm = slot->mm;				|
down_read(&mm->mmap_sem);		|
					|
...					|
					|
spin_lock(&ksm_mmlist_lock);		|
					|
ksm_scan.mm_slot go to the next slot;	|
					|
spin_unlock(&ksm_mmlist_lock);		|
					|mmput() ->
					|	ksm_exit():
					|
					|spin_lock(&ksm_mmlist_lock);
					|if (mm_slot && ksm_scan.mm_slot != mm_slot) {
					|	if (!mm_slot->rmap_list) {
					|		easy_to_free = 1;
					|		...
					|
					|if (easy_to_free) {
					|	mmdrop(mm);
					|	...
					|
					|So this mm_struct may be freed in the mmput().
					|
up_read(&mm->mmap_sem);			|

As we can see above, the ksmd thread may access a mm_struct that already
been freed to the kmem_cache.  Suppose a fork will get this mm_struct from
the kmem_cache, the ksmd thread then call up_read(&mm->mmap_sem), will
cause mmap_sem.count to become -1.

As suggested by Andrea Arcangeli, unmerge_and_remove_all_rmap_items has
the same SMP race condition, so fix it too.  My prev fix in function
scan_get_next_rmap_item will introduce a different SMP race condition, so
just invert the up_read/spin_unlock order as Andrea Arcangeli said.

Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Zhou Chengming <[email protected]>
Suggested-by: Andrea Arcangeli <[email protected]>
Reviewed-by: Andrea Arcangeli <[email protected]>
Cc: Hugh Dickins <[email protected]>
Cc: Kirill A. Shutemov <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Cc: Geliang Tang <[email protected]>
Cc: Minchan Kim <[email protected]>
Cc: Hanjun Guo <[email protected]>
Cc: Ding Tianhong <[email protected]>
Cc: Li Bin <[email protected]>
Cc: Zhen Lei <[email protected]>
Cc: Xishi Qiu <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
zhouchengming1 authored and torvalds committed May 12, 2016
1 parent c25a1e0 commit 7496fea
Showing 1 changed file with 10 additions and 5 deletions.
15 changes: 10 additions & 5 deletions mm/ksm.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ static int unmerge_and_remove_all_rmap_items(void)
}

remove_trailing_rmap_items(mm_slot, &mm_slot->rmap_list);
up_read(&mm->mmap_sem);

spin_lock(&ksm_mmlist_lock);
ksm_scan.mm_slot = list_entry(mm_slot->mm_list.next,
Expand All @@ -794,12 +795,9 @@ static int unmerge_and_remove_all_rmap_items(void)

free_mm_slot(mm_slot);
clear_bit(MMF_VM_MERGEABLE, &mm->flags);
up_read(&mm->mmap_sem);
mmdrop(mm);
} else {
} else
spin_unlock(&ksm_mmlist_lock);
up_read(&mm->mmap_sem);
}
}

/* Clean up stable nodes, but don't worry if some are still busy */
Expand Down Expand Up @@ -1663,8 +1661,15 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
up_read(&mm->mmap_sem);
mmdrop(mm);
} else {
spin_unlock(&ksm_mmlist_lock);
up_read(&mm->mmap_sem);
/*
* up_read(&mm->mmap_sem) first because after
* spin_unlock(&ksm_mmlist_lock) run, the "mm" may
* already have been freed under us by __ksm_exit()
* because the "mm_slot" is still hashed and
* ksm_scan.mm_slot doesn't point to it anymore.
*/
spin_unlock(&ksm_mmlist_lock);
}

/* Repeat until we've completed scanning the whole list */
Expand Down

0 comments on commit 7496fea

Please sign in to comment.