aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorKonstantin Khlebnikov <khlebnikov@yandex-team.ru>2015-04-16 15:47:56 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-17 09:04:07 -0400
commit90f31d0ea88880f780574f3d0bb1a227c4c66ca3 (patch)
tree5367f83919939a2d83888b3a1c48e5ba06e0f574 /kernel
parent0ec62afeb143a34ce78143cf442f879ef68382f7 (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.c56
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) 534static 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}
698EXPORT_SYMBOL_GPL(mmput); 707EXPORT_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 */
700void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) 718void 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 */
709struct file *get_mm_exe_file(struct mm_struct *mm) 737struct 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
722static 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;