diff options
author | Konstantin Khlebnikov <khlebnikov@yandex-team.ru> | 2015-04-16 15:47:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 09:04:07 -0400 |
commit | 90f31d0ea88880f780574f3d0bb1a227c4c66ca3 (patch) | |
tree | 5367f83919939a2d83888b3a1c48e5ba06e0f574 /kernel | |
parent | 0ec62afeb143a34ce78143cf442f879ef68382f7 (diff) |
mm: rcu-protected get_mm_exe_file()
This patch removes mm->mmap_sem from mm->exe_file read side.
Also it kills dup_mm_exe_file() and moves exe_file duplication into
dup_mmap() where both mmap_sems are locked.
[akpm@linux-foundation.org: fix comment typo]
Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Cc: Davidlohr Bueso <dbueso@suse.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/fork.c | 56 |
1 files changed, 37 insertions, 19 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 8807a129711b..259202637531 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -403,6 +403,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) | |||
403 | */ | 403 | */ |
404 | down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); | 404 | down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); |
405 | 405 | ||
406 | /* No ordering required: file already has been exposed. */ | ||
407 | RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | ||
408 | |||
406 | mm->total_vm = oldmm->total_vm; | 409 | mm->total_vm = oldmm->total_vm; |
407 | mm->shared_vm = oldmm->shared_vm; | 410 | mm->shared_vm = oldmm->shared_vm; |
408 | mm->exec_vm = oldmm->exec_vm; | 411 | mm->exec_vm = oldmm->exec_vm; |
@@ -528,7 +531,13 @@ static inline void mm_free_pgd(struct mm_struct *mm) | |||
528 | pgd_free(mm, mm->pgd); | 531 | pgd_free(mm, mm->pgd); |
529 | } | 532 | } |
530 | #else | 533 | #else |
531 | #define dup_mmap(mm, oldmm) (0) | 534 | static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) |
535 | { | ||
536 | down_write(&oldmm->mmap_sem); | ||
537 | RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | ||
538 | up_write(&oldmm->mmap_sem); | ||
539 | return 0; | ||
540 | } | ||
532 | #define mm_alloc_pgd(mm) (0) | 541 | #define mm_alloc_pgd(mm) (0) |
533 | #define mm_free_pgd(mm) | 542 | #define mm_free_pgd(mm) |
534 | #endif /* CONFIG_MMU */ | 543 | #endif /* CONFIG_MMU */ |
@@ -697,35 +706,46 @@ void mmput(struct mm_struct *mm) | |||
697 | } | 706 | } |
698 | EXPORT_SYMBOL_GPL(mmput); | 707 | EXPORT_SYMBOL_GPL(mmput); |
699 | 708 | ||
709 | /** | ||
710 | * set_mm_exe_file - change a reference to the mm's executable file | ||
711 | * | ||
712 | * This changes mm's executable file (shown as symlink /proc/[pid]/exe). | ||
713 | * | ||
714 | * Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). | ||
715 | * Callers prevent concurrent invocations: in mmput() nobody alive left, | ||
716 | * in execve task is single-threaded, prctl holds mmap_sem exclusively. | ||
717 | */ | ||
700 | void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | 718 | void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) |
701 | { | 719 | { |
720 | struct file *old_exe_file = rcu_dereference_protected(mm->exe_file, | ||
721 | !atomic_read(&mm->mm_users) || current->in_execve || | ||
722 | lockdep_is_held(&mm->mmap_sem)); | ||
723 | |||
702 | if (new_exe_file) | 724 | if (new_exe_file) |
703 | get_file(new_exe_file); | 725 | get_file(new_exe_file); |
704 | if (mm->exe_file) | 726 | rcu_assign_pointer(mm->exe_file, new_exe_file); |
705 | fput(mm->exe_file); | 727 | if (old_exe_file) |
706 | mm->exe_file = new_exe_file; | 728 | fput(old_exe_file); |
707 | } | 729 | } |
708 | 730 | ||
731 | /** | ||
732 | * get_mm_exe_file - acquire a reference to the mm's executable file | ||
733 | * | ||
734 | * Returns %NULL if mm has no associated executable file. | ||
735 | * User must release file via fput(). | ||
736 | */ | ||
709 | struct file *get_mm_exe_file(struct mm_struct *mm) | 737 | struct file *get_mm_exe_file(struct mm_struct *mm) |
710 | { | 738 | { |
711 | struct file *exe_file; | 739 | struct file *exe_file; |
712 | 740 | ||
713 | /* We need mmap_sem to protect against races with removal of exe_file */ | 741 | rcu_read_lock(); |
714 | down_read(&mm->mmap_sem); | 742 | exe_file = rcu_dereference(mm->exe_file); |
715 | exe_file = mm->exe_file; | 743 | if (exe_file && !get_file_rcu(exe_file)) |
716 | if (exe_file) | 744 | exe_file = NULL; |
717 | get_file(exe_file); | 745 | rcu_read_unlock(); |
718 | up_read(&mm->mmap_sem); | ||
719 | return exe_file; | 746 | return exe_file; |
720 | } | 747 | } |
721 | 748 | ||
722 | static void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm) | ||
723 | { | ||
724 | /* It's safe to write the exe_file pointer without exe_file_lock because | ||
725 | * this is called during fork when the task is not yet in /proc */ | ||
726 | newmm->exe_file = get_mm_exe_file(oldmm); | ||
727 | } | ||
728 | |||
729 | /** | 749 | /** |
730 | * get_task_mm - acquire a reference to the task's mm | 750 | * get_task_mm - acquire a reference to the task's mm |
731 | * | 751 | * |
@@ -887,8 +907,6 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) | |||
887 | if (!mm_init(mm, tsk)) | 907 | if (!mm_init(mm, tsk)) |
888 | goto fail_nomem; | 908 | goto fail_nomem; |
889 | 909 | ||
890 | dup_mm_exe_file(oldmm, mm); | ||
891 | |||
892 | err = dup_mmap(mm, oldmm); | 910 | err = dup_mmap(mm, oldmm); |
893 | if (err) | 911 | if (err) |
894 | goto free_pt; | 912 | goto free_pt; |