mm: fix lockdep build in rcu-protected get_mm_exe_file()
Subsystems (5)
PATCH 1/1 mm: fix lockdep build in rcu-protected get_mm_exe_file()
--- kernel/fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/fork.c +1 -1
@@ -696,7 +696,7 @@ void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) {struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, !atomic_read (&mm->mm_users) || current->in_execve || -lock_is_held (&mm->mmap_sem)); +lockdep_is_held (&mm->mmap_sem));if (new_exe_file)get_file (new_exe_file);
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Actually, here's a patch that gets rid of that condition, per Oleg.
What do you think?
8<------------------------------------------------
Subject: [PATCH -next] prctl: avoid using mmap_sem for exe_file serialization
Oleg cleverly suggested using xchg() to set the new mm->exe_file instead of calling set_mm_exe_file() which requires some form of serialization -- mmap_sem in this case. For archs that do not have atomic rmw instructions we still fallback to a spinlock alternative, so this should always be safe. As such, we only need the mmap_sem for looking up the backing vm_file, which can be done sharing the lock. Naturally, this means we need to manually deal with both the new and old file reference counting, and we need not worry about the MMF_EXE_FILE_CHANGED bits, which can probably be deleted in the future anyway.
--- kernel/fork.c | 11 ++++++----- kernel/sys.c | 43 ++++++++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 22 deletions(-)
kernel/fork.c +6 -5
@@ -688,15 +688,16 @@ EXPORT_SYMBOL_GPL(mmput);* * This changes mm's executale file (shown as symlink /proc/[pid]/exe). * -* Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). -* Callers prevent concurrent invocations: in mmput() nobody alive left, -* in execve task is single-threaded, prctl holds mmap_sem exclusively. +* Main users are mmput() and sys_execve(). Callers prevent concurrent +* invocations: in mmput() nobody alive left, in execve task is single +* threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the +* mm->exe_file, but does so without using set_mm_exe_file() in order +* to do avoid the need for any locks. */ void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) {struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lock_is_held (&mm->mmap_sem)); + !atomic_read (&mm->mm_users) || current->in_execve);if (new_exe_file)get_file (new_exe_file);
kernel/sys.c +26 -17
87 lines not on this page · raw message
Signature · 9 lines
2.1.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
kernel/fork.c EXPORT_SYMBOL_GPL(mmput); · line 700
Quoted 7 lines · Davidlohr Bueso
void
set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file){kernel/fork.c +1
-2struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lock_is_held (&mm->mmap_sem)); + !atomic_read (&mm->mm_users) || current->in_execve);
Thanks, looks correct at first glance...
But can't we remove the ->in_execve check above? and check
atomic_read(&mm->mm_users) <= 1
instead. OK, this is subjective, I won't insist. Just current->in_execve looks a bit confusing, it means "I swear, the caller is flush_old_exec() and this mm is actualy bprm->mm".
"atomic_read(&mm->mm_users) <= 1" looks a bit more "safe". But again, I won't insist.
Oleg.
Quoted 2 lines · Oleg Nesterov
"
atomic_read(&mm->mm_users)<= 1" looks a bit more "safe". But again, I won't insist.
Agreed, it is nicer to do that check, but I have no strong preference either, perhaps Konstantin or akpm do. Anyway, here's the change that can be folded in if you guys want to.
Thanks.
8<-------------------------------------------------
kernel/fork.c +1 -1
@@ -697,7 +697,7 @@ EXPORT_SYMBOL_GPL(mmput);void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) {struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve); +atomic_read (&mm->mm_users) <=1 );if (new_exe_file)get_file (new_exe_file);
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 13 lines · Oleg Nesterov
kernel/fork.c EXPORT_SYMBOL_GPL(mmput); · line 700
Quoted 7 lines · Davidlohr Bueso
void
set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file){
struct file*old_exe_file= rcu_dereference_protected(mm->exe_file,kernel/fork.c +1
-2- !atomic_read (&mm->mm_users) || current->in_execve || - lock_is_held(&mm->mmap_sem)); + !atomic_read(&mm->mm_users) || current->in_execve);Thanks, looks correct at first glance...
But can't we remove the ->in_execve check above? and check
Quoted 8 lines
atomic_read(&mm->mm_users) <= 1instead. OK, this is subjective, I won't insist. Just
current->in_execvelooks a bit confusing, it means "I swear, the caller isflush_old_exec()and this mm is actualybprm->mm"."
atomic_read(&mm->mm_users)<= 1" looks a bit more "safe". But again, I won't insist.
Not so safe: this will race with get_task_mm(). A lot of proc files grab temporary reference to task mm. But this just a debug -- we can place here "true".
Quoted 3 lines
Oleg.
Signature · 7 lines
Konstantin -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 17 lines · Konstantin Khlebnikov
Quoted 15 lines · Oleg Nesterov
kernel/fork.c EXPORT_SYMBOL_GPL(mmput); · line 700
Quoted 7 lines · Davidlohr Bueso
void
set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file){
struct file*old_exe_file= rcu_dereference_protected(mm->exe_file,kernel/fork.c +1
-2- !atomic_read (&mm->mm_users) || current->in_execve || - lock_is_held(&mm->mmap_sem)); + !atomic_read(&mm->mm_users) || current->in_execve);Thanks, looks correct at first glance...
But can't we remove the ->in_execve check above? and check
atomic_read(&mm->mm_users) <= 1
Quoted 8 lines
Quoted 6 lines
instead. OK, this is subjective, I won't insist. Just
current->in_execvelooks a bit confusing, it means "I swear, the caller isflush_old_exec()and this mm is actualybprm->mm"."
atomic_read(&mm->mm_users)<= 1" looks a bit more "safe". But again, I won't insist.Not so safe: this will race with
get_task_mm().
How?
If set_mm_exe_file() can race with get_task_mm() then we have a bug. And it will be reported ;)
Quoted 2 lines
A lot of proc files grab temporary reference to task mm. But this just a debug -- we can place here "true".
Yeees, probably rcu_dereference_raw() would be even better. set_mm_exe_file() must be called only if nobody but us can access this mm.
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 22 lines · Oleg Nesterov
Quoted 21 lines · Konstantin Khlebnikov
Quoted 19 lines · Oleg Nesterov
kernel/fork.c EXPORT_SYMBOL_GPL(mmput); · line 700
Quoted 7 lines · Davidlohr Bueso
void
set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file){ struct file *old_exe_file = rcu_dereference_protected(mm->exe_file,kernel/fork.c +1
-2- !atomic_read (&mm->mm_users) || current->in_execve || - lock_is_held(&mm->mmap_sem)); + !atomic_read(&mm->mm_users) || current->in_execve);Thanks, looks correct at first glance...
But can't we remove the ->in_execve check above? and check
atomic_read(&mm->mm_users) <= 1instead. OK, this is subjective, I won't insist. Just
current->in_execvelooks a bit confusing, it means "I swear, the caller isflush_old_exec()and this mm is actualybprm->mm".
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
"
atomic_read(&mm->mm_users)<= 1" looks a bit more "safe". But again, I won't insist.Not so safe: this will race with
get_task_mm().How?
I mean rcu/lockdep debug migh race with get_task_mm() and generate false-positive warning about non-protected rcu_dereference.
Quoted 9 lines
If
set_mm_exe_file()can race withget_task_mm()then we have a bug. And it will be reported ;)Quoted 2 lines
A lot of proc files grab temporary reference to task mm. But this just a debug -- we can place here "true".
Yeees, probably
rcu_dereference_raw()would be even better.set_mm_exe_file()must be called only if nobody but us can access this mm.
Yep.
Quoted 8 lines
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 6 lines · Konstantin Khlebnikov
Quoted 4 lines · Oleg Nesterov
Quoted 4 lines
Quoted 3 lines
"
atomic_read(&mm->mm_users)<= 1" looks a bit more "safe". But again, I won't insist.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Not so safe: this will race with
get_task_mm().How?
I mean rcu/lockdep debug migh race with
get_task_mm()and generate false-positive warning about non-protectedrcu_dereference.
Still can't understand, I think it can't... and if it could, then this warning would not be false positive.
Anut this doesn't matter because we seem to agree this check should go away.
Quoted 4 lines
Quoted 2 lines
Yeees, probably
rcu_dereference_raw()would be even better.set_mm_exe_file()must be called only if nobody but us can access this mm.Yep.
Great. Davidlohr will you agree?
Oleg.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 1/1 prctl: avoid using mmap_sem for exe_file serialization
Oleg cleverly suggested using xchg() to set the new mm->exe_file instead of calling set_mm_exe_file() which requires some form of serialization -- mmap_sem in this case. For archs that do not have atomic rmw instructions we still fallback to a spinlock alternative, so this should always be safe. As such, we only need the mmap_sem for looking up the backing vm_file, which can be done sharing the lock. Naturally, this means we need to manually deal with both the new and old file reference counting, and we need not worry about the MMF_EXE_FILE_CHANGED bits, which can probably be deleted in the future anyway.
---
Changes from v1:
- use rcu_dereference_raw()
- comment lockless use in flush_old_exec()
fs/exec.c | 6 ++++++ kernel/fork.c | 19 +++++++++++++------ kernel/sys.c | 43 ++++++++++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 23 deletions(-)
fs/exec.c +6 -0
@@ -1082,7 +1082,13 @@ int flush_old_exec(struct linux_binprm * bprm)if (retval) goto out; +/* +* Must be called _before_ exec_mmap() as bprm->mm is +* not visible until then. This also enables the update +* to be lockless. +*/ set_mm_exe_file (bprm->mm, bprm->file); +/* * Release all of the old mmap stuff */
kernel/fork.c +13 -6
@@ -688,15 +688,22 @@ EXPORT_SYMBOL_GPL(mmput);* * This changes mm's executale file (shown as symlink /proc/[pid]/exe). * -* Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). -* Callers prevent concurrent invocations: in mmput() nobody alive left, -* in execve task is single-threaded, prctl holds mmap_sem exclusively. +* Main users are mmput() and sys_execve(). Callers prevent concurrent +* invocations: in mmput() nobody alive left, in execve task is single +* threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the +* mm->exe_file, but does so without using set_mm_exe_file() in order +* to do avoid the need for any locks. */ void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) { -struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lockdep_is_held (&mm->mmap_sem)); +struct file *old_exe_file; + +/* +* It is safe to dereference the exe_file without RCU as +* this function is only called if nobody else can access +* this mm -- see comment above for justification. +*/ + old_exe_file =rcu_dereference_raw (mm->exe_file);if (new_exe_file)get_file (new_exe_file);
kernel/sys.c +26 -17
87 lines not on this page · raw message
Signature · 9 lines
2.1.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v2] prctl: avoid using mmap_sem for exe_file serialization
Quoted 98 lines · Davidlohr Bueso
Oleg cleverly suggested using
xchg()to set the newmm->exe_fileinstead of callingset_mm_exe_file()which requires some form of serialization --mmap_semin this case. For archs that do not have atomic rmw instructions we still fallback to a spinlock alternative, so this should always be safe. As such, we only need themmap_semfor looking up the backingvm_file, which can be done sharing the lock. Naturally, this means we need to manually deal with both the new and old file reference counting, and we need not worry about theMMF_EXE_FILE_CHANGEDbits, which can probably be deleted in the future anyway.Suggested-by Oleg Nesterov <[email protected]>Cc Konstantin Khlebnikov <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>---
Changes from v1:
- usercu_dereference_raw()
- comment lockless use inflush_old_exec()fs/exec.c | 6 ++++++ kernel/fork.c | 19 +++++++++++++------ kernel/sys.c | 43 ++++++++++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 23 deletions(-)fs/exec.c +6
-0@@ -1082,7 +1082,13 @@ int flush_old_exec(struct linux_binprm * bprm)if (retval) goto out; +/* +* Must be called _before_ exec_mmap() as bprm->mm is +* not visible until then. This also enables the update +* to be lockless. +*/ set_mm_exe_file (bprm->mm, bprm->file); +/* * Release all of the old mmap stuff */ kernel/fork.c +13
-6@@ -688,15 +688,22 @@ EXPORT_SYMBOL_GPL(mmput);* * This changes mm's executale file (shown as symlink /proc/[pid]/exe). * -* Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). -* Callers prevent concurrent invocations: in mmput() nobody alive left, -* in execve task is single-threaded, prctl holds mmap_sem exclusively. +* Main users are mmput() and sys_execve(). Callers prevent concurrent +* invocations: in mmput() nobody alive left, in execve task is single +* threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the +* mm->exe_file, but does so without using set_mm_exe_file() in order +* to do avoid the need for any locks. */ void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) { -struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lockdep_is_held (&mm->mmap_sem)); +struct file *old_exe_file; + +/* +* It is safe to dereference the exe_file without RCU as +* this function is only called if nobody else can access +* this mm -- see comment above for justification. +*/ + old_exe_file =rcu_dereference_raw (mm->exe_file);if (new_exe_file)get_file (new_exe_file);kernel/sys.c +2
-3@@ -1649,14 +1649,13 @@ SYSCALL_DEFINE1(umask, int, mask)return mask; } -static int prctl_set_mm_exe_file_locked (struct mm_struct *mm,unsigned int fd) +static int prctl_set_mm_exe_file (struct mm_struct *mm,unsigned int fd) {struct fd exe; +struct file *old_exe, *exe_file;struct inode *inode;int err; -VM_BUG_ON_MM (!rwsem_is_locked (&mm->mmap_sem), mm); - exe =fdget (fd);if (!exe.file)return -EBADF ;
kernel/sys.c static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd) · line 1684
Quoted 6 lines
kernel/sys.c +1
-0@@ -1680,15 +1679,22 @@ static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd)/* * Forbid mm->exe_file change if old file still mapped. */ + exe_file =get_mm_exe_file (mm); err = -EBUSY ;if (mm->exe_file) {
exe_file not mm->exe_file
kernel/sys.c static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd) · line 1692
Quoted 11 lines
kernel/sys.c +5
-3struct vm_area_struct *vma; -for (vma = mm->mmap; vma; vma = vma->vm_next) -if (vma->vm_file && -path_equal (&vma->vm_file->f_path, +down_read (&mm->mmap_sem); +for (vma = mm->mmap; vma; vma = vma->vm_next) { +if (!vma->vm_file) +continue ; +if (path_equal (&vma->vm_file->f_path, &mm->exe_file->f_path))
same here
Quoted 58 lines
kernel/sys.c +18
-11- goto exit; + goto exit_err; + } + +up_read (&mm->mmap_sem); +fput (exe_file); }/* @@ -1702,10 +1708,18 @@ static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd) goto exit; err =0 ; -set_mm_exe_file (mm, exe.file);/* this grabs a reference to exe.file */ +/* set the new file, lockless */ +get_file (exe.file); + old_exe =xchg (&mm->exe_file, exe.file); +if (old_exe) +fput (old_exe); exit:fdput (exe);return err; +exit_err: +up_read (&mm->mmap_sem); +fput (exe_file); + goto exit; }#ifdef CONFIG_CHECKPOINT_RESTORE @@ -1840,10 +1854,9 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data user_auxv[AT_VECTOR_SIZE -1 ] =AT_NULL ; } -down_write (&mm->mmap_sem);if (prctl_map.exe_fd != (u32)-1 ) - error =prctl_set_mm_exe_file_locked (mm, prctl_map.exe_fd); -downgrade_write (&mm->mmap_sem); + error =prctl_set_mm_exe_file (mm, prctl_map.exe_fd); +down_read (&mm->mmap_sem);if (error) goto out;@@ -1909,12 +1922,8 @@ static int prctl_set_mm(int opt, unsigned long addr,if (!capable (CAP_SYS_RESOURCE ))return -EPERM ; -if (opt ==PR_SET_MM_EXE_FILE ) { -down_write (&mm->mmap_sem); - error =prctl_set_mm_exe_file_locked (mm, (unsigned int )addr); -up_write (&mm->mmap_sem); -return error; - } +if (opt ==PR_SET_MM_EXE_FILE ) +return prctl_set_mm_exe_file (mm, (unsigned int )addr);if (addr >=TASK_SIZE || addr < mmap_min_addr)return -EINVAL ;
Signature · 7 lines
Konstantin -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v3 1/1 prctl: avoid using mmap_sem for exe_file serialization
Oleg cleverly suggested using xchg() to set the new mm->exe_file instead of calling set_mm_exe_file() which requires some form of serialization -- mmap_sem in this case. For archs that do not have atomic rmw instructions we still fallback to a spinlock alternative, so this should always be safe. As such, we only need the mmap_sem for looking up the backing vm_file, which can be done sharing the lock. Naturally, this means we need to manually deal with both the new and old file reference counting, and we need not worry about the MMF_EXE_FILE_CHANGED bits, which can probably be deleted in the future anyway.
---
Changes from v2: use correct exe_file (sigh), per Konstantin.
fs/exec.c | 6 ++++++ kernel/fork.c | 19 +++++++++++++------ kernel/sys.c | 47 ++++++++++++++++++++++++++++------------------- 3 files changed, 47 insertions(+), 25 deletions(-)
fs/exec.c +6 -0
@@ -1082,7 +1082,13 @@ int flush_old_exec(struct linux_binprm * bprm)if (retval) goto out; +/* +* Must be called _before_ exec_mmap() as bprm->mm is +* not visibile until then. This also enables the update +* to be lockless. +*/ set_mm_exe_file (bprm->mm, bprm->file); +/* * Release all of the old mmap stuff */
kernel/fork.c +13 -6
@@ -688,15 +688,22 @@ EXPORT_SYMBOL_GPL(mmput);* * This changes mm's executale file (shown as symlink /proc/[pid]/exe). * -* Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). -* Callers prevent concurrent invocations: in mmput() nobody alive left, -* in execve task is single-threaded, prctl holds mmap_sem exclusively. +* Main users are mmput() and sys_execve(). Callers prevent concurrent +* invocations: in mmput() nobody alive left, in execve task is single +* threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the +* mm->exe_file, but does so without using set_mm_exe_file() in order +* to do avoid the need for any locks. */ void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) { -struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lockdep_is_held (&mm->mmap_sem)); +struct file *old_exe_file; + +/* +* It is safe to dereference the exe_file without RCU as +* this function is only called if nobody else can access +* this mm -- see comment above for justification. +*/ + old_exe_file =rcu_dereference_raw (mm->exe_file);if (new_exe_file)get_file (new_exe_file);
kernel/sys.c +28 -19
89 lines not on this page · raw message
Signature · 9 lines
2.1.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v3] prctl: avoid using mmap_sem for exe_file serialization
Quoted 12 lines · Davidlohr Bueso
Oleg cleverly suggested using
xchg()to set the newmm->exe_fileinstead of callingset_mm_exe_file()which requires some form of serialization --mmap_semin this case. For archs that do not have atomic rmw instructions we still fallback to a spinlock alternative, so this should always be safe. As such, we only need themmap_semfor looking up the backingvm_file, which can be done sharing the lock. Naturally, this means we need to manually deal with both the new and old file reference counting, and we need not worry about theMMF_EXE_FILE_CHANGEDbits, which can probably be deleted in the future anyway.
Quoted 3 lines
Suggested-by Oleg Nesterov <[email protected]>Cc Konstantin Khlebnikov <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>
If this is preparation for future rework of mmap_sem maybe we could postpone committing this patch.
Quoted 159 lines
---
Changes from v2: use correct
exe_file(sigh), per Konstantin.fs/exec.c | 6 ++++++ kernel/fork.c | 19 +++++++++++++------ kernel/sys.c | 47 ++++++++++++++++++++++++++++------------------- 3 files changed, 47 insertions(+), 25 deletions(-)fs/exec.c +6
-0@@ -1082,7 +1082,13 @@ int flush_old_exec(struct linux_binprm * bprm)if (retval) goto out; +/* +* Must be called _before_ exec_mmap() as bprm->mm is +* not visibile until then. This also enables the update +* to be lockless. +*/ set_mm_exe_file (bprm->mm, bprm->file); +/* * Release all of the old mmap stuff */ kernel/fork.c +13
-6@@ -688,15 +688,22 @@ EXPORT_SYMBOL_GPL(mmput);* * This changes mm's executale file (shown as symlink /proc/[pid]/exe). * -* Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). -* Callers prevent concurrent invocations: in mmput() nobody alive left, -* in execve task is single-threaded, prctl holds mmap_sem exclusively. +* Main users are mmput() and sys_execve(). Callers prevent concurrent +* invocations: in mmput() nobody alive left, in execve task is single +* threaded. sys_prctl(PR_SET_MM_MAP/EXE_FILE) also needs to set the +* mm->exe_file, but does so without using set_mm_exe_file() in order +* to do avoid the need for any locks. */ void set_mm_exe_file (struct mm_struct *mm,struct file *new_exe_file) { -struct file *old_exe_file =rcu_dereference_protected (mm->exe_file, - !atomic_read (&mm->mm_users) || current->in_execve || -lockdep_is_held (&mm->mmap_sem)); +struct file *old_exe_file; + +/* +* It is safe to dereference the exe_file without RCU as +* this function is only called if nobody else can access +* this mm -- see comment above for justification. +*/ + old_exe_file =rcu_dereference_raw (mm->exe_file);if (new_exe_file)get_file (new_exe_file);kernel/sys.c +28
-19@@ -1649,14 +1649,13 @@ SYSCALL_DEFINE1(umask, int, mask)return mask; } -static int prctl_set_mm_exe_file_locked (struct mm_struct *mm,unsigned int fd) +static int prctl_set_mm_exe_file (struct mm_struct *mm,unsigned int fd) {struct fd exe; +struct file *old_exe, *exe_file;struct inode *inode;int err; -VM_BUG_ON_MM (!rwsem_is_locked (&mm->mmap_sem), mm); - exe =fdget (fd);if (!exe.file)return -EBADF ;@@ -1680,15 +1679,22 @@ static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd)/* * Forbid mm->exe_file change if old file still mapped. */ + exe_file =get_mm_exe_file (mm); err = -EBUSY ; -if (mm->exe_file) { +if (exe_file) {struct vm_area_struct *vma; -for (vma = mm->mmap; vma; vma = vma->vm_next) -if (vma->vm_file && -path_equal (&vma->vm_file->f_path, - &mm->exe_file->f_path)) - goto exit; +down_read (&mm->mmap_sem); +for (vma = mm->mmap; vma; vma = vma->vm_next) { +if (!vma->vm_file) +continue ; +if (path_equal (&vma->vm_file->f_path, + &exe_file->f_path)) + goto exit_err; + } + +up_read (&mm->mmap_sem); +fput (exe_file); }/* @@ -1702,10 +1708,18 @@ static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd) goto exit; err =0 ; -set_mm_exe_file (mm, exe.file);/* this grabs a reference to exe.file */ +/* set the new file, lockless */ +get_file (exe.file); + old_exe =xchg (&mm->exe_file, exe.file); +if (old_exe) +fput (old_exe); exit:fdput (exe);return err; +exit_err: +up_read (&mm->mmap_sem); +fput (exe_file); + goto exit; }#ifdef CONFIG_CHECKPOINT_RESTORE @@ -1840,10 +1854,9 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data user_auxv[AT_VECTOR_SIZE -1 ] =AT_NULL ; } -down_write (&mm->mmap_sem);if (prctl_map.exe_fd != (u32)-1 ) - error =prctl_set_mm_exe_file_locked (mm, prctl_map.exe_fd); -downgrade_write (&mm->mmap_sem); + error =prctl_set_mm_exe_file (mm, prctl_map.exe_fd); +down_read (&mm->mmap_sem);if (error) goto out;@@ -1909,12 +1922,8 @@ static int prctl_set_mm(int opt, unsigned long addr,if (!capable (CAP_SYS_RESOURCE ))return -EPERM ; -if (opt ==PR_SET_MM_EXE_FILE ) { -down_write (&mm->mmap_sem); - error =prctl_set_mm_exe_file_locked (mm, (unsigned int )addr); -up_write (&mm->mmap_sem); -return error; - } +if (opt ==PR_SET_MM_EXE_FILE ) +return prctl_set_mm_exe_file (mm, (unsigned int )addr);if (addr >=TASK_SIZE || addr < mmap_min_addr)return -EINVAL ;
Signature · 7 lines
Konstantin -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v3] prctl: avoid using mmap_sem for exe_file serialization
Quoted 1 line · Konstantin Khlebnikov
Reviewed-by Konstantin Khlebnikov <[email protected]>
Thanks.
Quoted 2 lines
If this is preparation for future rework of
mmap_semmaybe we could postpone committing this patch.
Personally, I think this patch is fine to commit for v4.1, along with your rcu one, regardless of my mmap_sem work (which is still a wip). Furthermore we've already got all the exe_file users updated now in next, so I'd like to wrap this exe_file thing up if nobody has any objections.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v3] prctl: avoid using mmap_sem for exe_file serialization
In reply to Davidlohr BuesoQuoted 2 lines · Davidlohr Bueso
Changes from v2: use correct
exe_file(sigh), per Konstantin.
Looks good, thanks!
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>