exit: Clarify choice of new parent in forget_original_parent()
Subsystems (13)
A message that is not in the archive (sent off-list or not yet received): 20150525162722.5171.15901.stgit@pro
RFC PATCH 1/13 exit: Clarify choice of new parent in forget_original_parent()
Second parameter of find_new_reaper() and the similarity of its name and find_child_reaper()'s confuse a reader.
Rename find_child_reaper() for better conformity of its name and its function. Also delete the second parameter of find_new_reaper().
These both improve modularity.
--- kernel/exit.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
kernel/exit.c +9 -10
@@ -450,7 +450,7 @@ static struct task_struct *find_alive_thread(struct task_struct *p)return NULL ; } -static struct task_struct *find_child_reaper (struct task_struct *father) +static void check_pid_ns_reaper_exit (struct task_struct *father) __releases(&tasklist_lock) __acquires(&tasklist_lock) {
@@ -458,12 +458,12 @@ static struct task_struct *find_child_reaper(struct task_struct *father)struct task_struct *reaper = pid_ns->child_reaper;if (likely (reaper != father)) -return reaper; +return ; reaper =find_alive_thread (father);if (reaper) { pid_ns->child_reaper = reaper; -return reaper; +return ; }write_unlock_irq (&tasklist_lock);
@@ -473,8 +473,6 @@ static struct task_struct *find_child_reaper(struct task_struct *father) }zap_pid_ns_processes (pid_ns);write_lock_irq (&tasklist_lock); - -return father; }/*
@@ -484,15 +482,16 @@ static struct task_struct *find_child_reaper(struct task_struct *father)* child_subreaper for its children (like a service manager) * 3. give it to the init process (PID 1) in our pid namespace */ -static struct task_struct *find_new_reaper (struct task_struct *father, -struct task_struct *child_reaper) +static struct task_struct *find_new_reaper (struct task_struct *father) { -struct task_struct *thread, *reaper; +struct task_struct *thread, *reaper, *child_reaper; thread =find_alive_thread (father);if (thread)return thread; + child_reaper =task_active_pid_ns (father)->child_reaper; +if (father->signal->has_child_subreaper) {/* * Find the first ->is_child_subreaper ancestor in our pid_ns.
@@ -557,11 +556,11 @@ static void forget_original_parent(struct task_struct *father,exit_ptrace (father, dead);/* Can drop and reacquire tasklist_lock */ - reaper =find_child_reaper (father); +check_pid_ns_reaper_exit (father);if (list_empty (&father->children))return ; - reaper =find_new_reaper (father, reaper); + reaper =find_new_reaper (father);list_for_each_entry (p, &father->children, sibling) {for_each_thread (p, t) { t->real_parent = reaper;
RFC PATCH 2/13 rwlock_t: Implement double_write_{,un}lock()
In reply to a message not in the archiveNew helpers for locking two locks at a time.
--- include/linux/spinlock.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
include/linux/spinlock.h +19 -0
@@ -426,4 +426,23 @@ extern int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock);#define atomic_dec_and_lock (atomic, lock) \ __cond_lock(lock, _atomic_dec_and_lock(atomic, lock)) +static inline void double_write_lock (rwlock_t *lock1,rwlock_t *lock2) +{ +if (lock1 < lock2) { +write_lock (lock1); +write_lock (lock2); + }else if (lock1 > lock2) { +write_lock (lock2); +write_lock (lock1); + }else { +write_lock (lock1); + } +} +static inline void double_write_unlock (rwlock_t *lock1,rwlock_t *lock2) +{ +if (lock2 != lock1) +write_unlock (lock2); +write_unlock (lock1); +} +#endif /* __LINUX_SPINLOCK_H */
RFC PATCH 3/13 pid_ns: Implement rwlock_t pid_ns::cr_lock for locking child_reaper
In reply to a message not in the archiveProtects child_reaper modifitations.
--- include/linux/pid_namespace.h | 1 + kernel/exit.c | 15 ++++++++++++--- kernel/fork.c | 1 + kernel/pid.c | 10 +++++++++- kernel/pid_namespace.c | 5 +++-- 5 files changed, 26 insertions(+), 6 deletions(-)
include/linux/pid_namespace.h +1 -0
@@ -28,6 +28,7 @@ struct pid_namespace {int last_pid;unsigned int nr_hashed;struct task_struct *child_reaper; +rwlock_t cr_lock;struct kmem_cache *pid_cachep;unsigned int level;struct pid_namespace *parent;
kernel/exit.c +12 -3
@@ -455,16 +455,23 @@ static void check_pid_ns_reaper_exit(struct task_struct *father) __acquires(&tasklist_lock) {struct pid_namespace *pid_ns =task_active_pid_ns (father); -struct task_struct *reaper = pid_ns->child_reaper; +struct task_struct *reaper; + +read_lock (&pid_ns->cr_lock); + reaper = pid_ns->child_reaper; +read_unlock (&pid_ns->cr_lock);if (likely (reaper != father))return ; +write_lock (&pid_ns->cr_lock); reaper =find_alive_thread (father); -if (reaper) { +if (reaper) pid_ns->child_reaper = reaper; +write_unlock (&pid_ns->cr_lock); + +if (reaper)return ; - }write_unlock_irq (&tasklist_lock);if (unlikely (pid_ns == &init_pid_ns)) {
@@ -560,6 +567,7 @@ static void forget_original_parent(struct task_struct *father,if (list_empty (&father->children))return ; +read_lock (&task_active_pid_ns (father)->cr_lock); reaper =find_new_reaper (father);list_for_each_entry (p, &father->children, sibling) {for_each_thread (p, t) {
@@ -579,6 +587,7 @@ static void forget_original_parent(struct task_struct *father,reparent_leader (father, p, dead); }list_splice_tail_init (&father->children, &reaper->children); +read_unlock (&task_active_pid_ns (father)->cr_lock); }/*
kernel/fork.c +1 -0
@@ -1563,6 +1563,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,init_task_pid (p,PIDTYPE_SID ,task_session (current));if (is_child_reaper (pid)) { +/* Lockless, as we're the only process in ns */ ns_of_pid (pid)->child_reaper = p; p->signal->flags |=SIGNAL_UNKILLABLE ; }
kernel/pid.c +9 -1
@@ -78,6 +78,7 @@ struct pid_namespace init_pid_ns = { .nr_hashed = PIDNS_HASH_ADDING, .level = 0, .child_reaper = &init_task, + .cr_lock = __RW_LOCK_UNLOCKED(&init_pid_ns.cr_lock), .user_ns = &init_user_ns, .ns.inum = PROC_PID_INIT_INO,#ifdef CONFIG_PID_NS
@@ -259,6 +260,7 @@ static void delayed_put_pid(struct rcu_head *rhp)void free_pid (struct pid *pid) {/* We can be called with write_lock_irq(&tasklist_lock) held */ +struct task_struct *child_reaper =NULL ;int i;unsigned long flags;
@@ -274,7 +276,8 @@ void free_pid(struct pid *pid)* is the reaper wake up the reaper. The reaper * may be sleeping in zap_pid_ns_processes(). */ -wake_up_process (ns->child_reaper); + child_reaper = ns->child_reaper; +get_task_struct (child_reaper);break ;case PIDNS_HASH_ADDING :/* Handle a fork failure of the first process */
@@ -288,6 +291,11 @@ void free_pid(struct pid *pid) }spin_unlock_irqrestore (&pidmap_lock, flags); +if (child_reaper) { +wake_up_process (child_reaper); +put_task_struct (child_reaper); + } +for (i =0 ; i <= pid->level; i++)free_pidmap (pid->numbers + i);
kernel/pid_namespace.c +3 -2
@@ -115,6 +115,7 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns ns->parent =get_pid_ns (parent_pid_ns); ns->user_ns =get_user_ns (user_ns); ns->nr_hashed =PIDNS_HASH_ADDING ; +rwlock_init (&ns->cr_lock);INIT_WORK (&ns->proc_work, proc_cleanup_work);set_bit (0 , ns->pidmap[0 ].page);
@@ -324,9 +325,9 @@ int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)return -EINVAL ; } -read_lock (&tasklist_lock); +read_lock (&pid_ns->cr_lock);force_sig (SIGKILL , pid_ns->child_reaper); -read_unlock (&tasklist_lock); +read_unlock (&pid_ns->cr_lock);do_exit (0 );
RFC PATCH 4/13 exit: Small refactoring mm_update_next_owner()
In reply to a message not in the archiveThis will be used in next patches. No functionality change.
--- kernel/exit.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
kernel/exit.c +9 -4
@@ -322,16 +322,20 @@ void mm_update_next_owner(struct mm_struct *mm)* Search in the children */ list_for_each_entry (c, &p->children, sibling) { -if (c->mm == mm) +if (c->mm == mm) { +get_task_struct (c); goto assign_new_owner; + } }/* * Search in the siblings */ list_for_each_entry (c, &p->real_parent->children, sibling) { -if (c->mm == mm) +if (c->mm == mm) { +get_task_struct (c); goto assign_new_owner; + } }/*
@@ -341,8 +345,10 @@ void mm_update_next_owner(struct mm_struct *mm)if (g->flags &PF_KTHREAD )continue ;for_each_thread (g, c) { -if (c->mm == mm) +if (c->mm == mm) { +get_task_struct (c); goto assign_new_owner; + }if (c->mm)break ; }
@@ -358,7 +364,6 @@ void mm_update_next_owner(struct mm_struct *mm) assign_new_owner:BUG_ON (c == p); -get_task_struct (c);/* * The task_lock protects c->mm from changing. * We always want mm->owner->mm == mm
RFC PATCH 5/13 fs: Refactoring in get_children_pid()
In reply to a message not in the archiveThis will be used in next patches. No functionality change.
--- fs/proc/array.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-)
fs/proc/array.c +16 -12
@@ -576,9 +576,7 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)struct task_struct *start, *task;struct pid *pid =NULL ; -read_lock (&tasklist_lock); - - start =pid_task (proc_pid (inode),PIDTYPE_PID ); + start =get_pid_task (proc_pid (inode),PIDTYPE_PID );if (!start) goto out;
@@ -586,18 +584,21 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)* Lets try to continue searching first, this gives * us significant speedup on children-rich processes. */ -if (pid_prev) { - task =pid_task (pid_prev,PIDTYPE_PID ); -if (task && task->real_parent == start && - !(list_empty (&task->sibling))) { -if (list_is_last (&task->sibling, &start->children)) - goto out; + task =get_pid_task (pid_prev,PIDTYPE_PID ); +if (!task) + goto put_start; + +read_lock (&tasklist_lock); +if (task->real_parent == start && !(list_empty (&task->sibling))) { +put_task_struct (task); +if (!list_is_last (&task->sibling, &start->children)) { task =list_first_entry (&task->sibling, - struct task_struct, sibling); + struct task_struct, sibling); pid =get_pid (task_pid (task)); - goto out; } + goto unlock; } +put_task_struct (task);/* * Slow search case.
@@ -621,8 +622,11 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos) } } -out: +unlock:read_unlock (&tasklist_lock); +put_start: +put_task_struct (start); +out:return pid; }
RFC PATCH 6/13 core: Add rwlock_t task_list::kin_lock
In reply to a message not in the archiveAdd a new primitive for synchronization between relatives. It will be used in next/further patches.
--- include/linux/init_task.h | 1 + include/linux/sched.h | 1 + kernel/fork.c | 1 + 3 files changed, 3 insertions(+)
include/linux/init_task.h +1 -0
@@ -218,6 +218,7 @@ extern struct task_group root_task_group; .parent = &tsk, \ .children = LIST_HEAD_INIT(tsk.children), \ .sibling = LIST_HEAD_INIT(tsk.sibling), \ + .kin_lock = __RW_LOCK_UNLOCKED(tsk.kin_lock), \ .group_leader = &tsk, \RCU_POINTER_INITIALIZER (real_cred, &init_cred), \ RCU_POINTER_INITIALIZER(cred, &init_cred), \
include/linux/sched.h +1 -0
@@ -1477,6 +1477,7 @@ struct task_struct {struct list_head children;/* list of my children */ struct list_head sibling;/* linkage in my parent's children list */ struct task_struct *group_leader;/* threadgroup leader */ +rwlock_t kin_lock;/* * ptraced is the list of tasks this task is using ptrace on.
kernel/fork.c +1 -0
@@ -1330,6 +1330,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->flags |=PF_FORKNOEXEC ;INIT_LIST_HEAD (&p->children);INIT_LIST_HEAD (&p->sibling); +rwlock_init (&p->kin_lock);rcu_copy_process (p); p->vfork_done =NULL ;spin_lock_init (&p->alloc_lock);
RFC PATCH 7/13 kin_lock: Implement helpers for kin_lock locking.
In reply to a message not in the archive--- include/linux/sched.h | 302 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+)
include/linux/sched.h +302 -0
@@ -1835,6 +1835,308 @@ static inline bool should_numa_migrate_memory(struct task_struct *p, }#endif +static inline void write_real_parent_lock (struct task_struct *p) +{ +struct task_struct *real_parent; +rcu_read_lock (); +again: + real_parent = p->real_parent; +write_lock (&real_parent->kin_lock); +if (real_parent != p->real_parent) { +write_unlock (&real_parent->kin_lock); + goto again; + } +rcu_read_unlock (); +} + +static inline void write_real_parent_lock_irq (struct task_struct *p) +{ +local_irq_disable (); +write_real_parent_lock (p); +} + +static inline void write_real_parent_unlock (struct task_struct *p) +{ +write_unlock (&p->real_parent->kin_lock); +} + +static inline void write_real_parent_unlock_irq (struct task_struct *p) +{ +write_unlock_irq (&p->real_parent->kin_lock); +} + +static inline void read_parent_lock (struct task_struct *p) +{ +struct task_struct *parent; +rcu_read_lock (); +again: + parent = p->parent; +read_lock (&parent->kin_lock); +if (parent != p->parent) { +read_unlock (&parent->kin_lock); + goto again; + } +rcu_read_unlock (); + +} + +static inline void read_parent_unlock (struct task_struct *p) +{ +read_unlock (&p->parent->kin_lock); +} + +static inline void read_real_parent_lock (struct task_struct *p) +{ +struct task_struct *real_parent; +rcu_read_lock (); +again: + real_parent = p->real_parent; +read_lock (&real_parent->kin_lock); +if (real_parent != p->real_parent) { +read_unlock (&real_parent->kin_lock); + goto again; + } +rcu_read_unlock (); + +} + +static inline void read_real_parent_unlock (struct task_struct *p) +{ +read_unlock (&p->real_parent->kin_lock); +} + +static inline void write_parent_lock (struct task_struct *p) +{ +struct task_struct *parent; +rcu_read_lock (); +again: + parent = p->parent; +write_lock (&parent->kin_lock); +if (parent != p->parent) { +write_unlock (&parent->kin_lock); + goto again; + } +rcu_read_unlock (); +} + +static inline void write_parent_lock_irq (struct task_struct *p) +{ +local_irq_disable (); +write_parent_lock (p); +} + +static inline void write_parent_unlock (struct task_struct *p) +{ +write_unlock (&p->parent->kin_lock); +} + +static inline void write_parent_unlock_irq (struct task_struct *p) +{ +write_unlock_irq (&p->parent->kin_lock); +} + +static inline void write_parent_and_given_lock (struct task_struct *child, +struct task_struct *given) +{ +struct task_struct *parent; +rcu_read_lock (); +again: + parent = child->parent; +if (parent <= given) { +write_lock (&parent->kin_lock); +if (parent != child->parent) { +write_unlock (&parent->kin_lock); + goto again; + } +if (parent != given) +write_lock (&given->kin_lock); + }else { +write_lock (&given->kin_lock); +write_lock (&parent->kin_lock); +if (parent != child->parent) { +write_unlock (&given->kin_lock); +write_unlock (&parent->kin_lock); + goto again; + } + } +rcu_read_unlock (); +} + +static inline void write_parent_and_given_lock_irq (struct task_struct *child, +struct task_struct *given) +{ +local_irq_disable (); +write_parent_and_given_lock (child, given); +} + +static inline void read_parent_and_given_lock (struct task_struct *child, +struct task_struct *given) +{ +struct task_struct *parent; +rcu_read_lock (); +again: + parent = child->parent; +if (parent <= given) { +read_lock (&parent->kin_lock); +if (parent != child->parent) { +read_unlock (&parent->kin_lock); + goto again; + } +if (parent != given) +read_lock (&given->kin_lock); + }else { +read_lock (&given->kin_lock); +read_lock (&parent->kin_lock); +if (parent != child->parent) { +read_unlock (&given->kin_lock); +read_unlock (&parent->kin_lock); + goto again; + } + } +rcu_read_unlock (); +} + +static inline void read_real_parent_and_given_lock (struct task_struct *child, +struct task_struct *given) +{ +struct task_struct *real_parent; +rcu_read_lock (); +again: + real_parent = child->real_parent; +if (real_parent <= given) { +read_lock (&real_parent->kin_lock); +if (real_parent != child->real_parent) { +read_unlock (&real_parent->kin_lock); + goto again; + } +if (real_parent != given) +read_lock (&given->kin_lock); + }else { +read_lock (&given->kin_lock); +read_lock (&real_parent->kin_lock); +if (real_parent != child->real_parent) { +read_unlock (&given->kin_lock); +read_unlock (&real_parent->kin_lock); + goto again; + } + } +rcu_read_unlock (); +} + +static inline void read_real_parent_and_given_unlock (struct task_struct *child, +struct task_struct *given) +{ +struct task_struct *real_parent = child->real_parent; + +if (real_parent != given) +read_unlock (&real_parent->kin_lock); +read_unlock (&given->kin_lock); +} +static inline void write_parent_and_real_parent_lock (struct task_struct *child) +{ +struct task_struct *parent, *real_parent; +rcu_read_lock (); +again: + parent = child->parent; + real_parent = child->real_parent; +if (parent <= real_parent) { +write_lock (&parent->kin_lock); +if (parent != child->parent) { +write_unlock (&parent->kin_lock); + goto again; + } +if (parent != real_parent) { +write_lock (&real_parent->kin_lock); +if (real_parent != child->real_parent) { +write_unlock (&parent->kin_lock); +write_unlock (&real_parent->kin_lock); + goto again; + } + } + }else { +write_lock (&real_parent->kin_lock); +write_lock (&parent->kin_lock); +if (real_parent != child->real_parent || + parent != child->parent) { +write_unlock (&real_parent->kin_lock); +write_unlock (&parent->kin_lock); + goto again; + } + } +rcu_read_unlock (); +} + +static inline void write_parent_and_real_parent_lock_irq (struct task_struct *child) +{ +local_irq_disable (); +write_parent_and_real_parent_lock (child); +} + +static inline void write_parent_and_real_parent_unlock (struct task_struct *child) +{ +struct task_struct *parent, *real_parent; + + parent = child->parent; + real_parent = child->real_parent; + +write_unlock (&real_parent->kin_lock); +if (parent != real_parent) +write_unlock (&parent->kin_lock); +} + +static inline void write_parent_and_real_parent_unlock_irq (struct task_struct *child) +{ +write_parent_and_real_parent_unlock (child); +local_irq_enable (); +} + +static inline void read_parent_and_real_parent_lock (struct task_struct *child) +{ +struct task_struct *parent, *real_parent; +rcu_read_lock (); +again: + parent = child->parent; + real_parent = child->real_parent; +if (parent <= real_parent) { +read_lock (&parent->kin_lock); +if (parent != child->parent) { +read_unlock (&parent->kin_lock); + goto again; + } +if (parent != real_parent) { +read_lock (&real_parent->kin_lock); +if (real_parent != child->real_parent) { +read_unlock (&parent->kin_lock); +read_unlock (&real_parent->kin_lock); + goto again; + } + } + }else { +read_lock (&real_parent->kin_lock); +read_lock (&parent->kin_lock); +if (real_parent != child->real_parent || + parent != child->parent) { +read_unlock (&real_parent->kin_lock); +read_unlock (&parent->kin_lock); + goto again; + } + } +rcu_read_unlock (); +} + +static inline void read_parent_and_real_parent_unlock (struct task_struct *child) +{ +struct task_struct *parent, *real_parent; + + parent = child->parent; + real_parent = child->real_parent; + +read_unlock (&real_parent->kin_lock); +if (parent != real_parent) +read_unlock (&parent->kin_lock); +} + +static inline struct pid *task_pid (struct task_struct *task) {return task->pids[PIDTYPE_PID ].pid;
RFC PATCH 8/13 core: Use kin_lock synchronizations between parent and child and for thread group
In reply to a message not in the archivereal_parent->kin_lock protects:
child's threads, place in sibling list etc.
parent->kin_lock protects:
ptrace operations
Note: sighand is still stable under tasklist_lock.
--- fs/exec.c | 16 ++++++++--- fs/proc/array.c | 4 +-- kernel/exit.c | 68 +++++++++++++++++++++++++++++++++++++--------- kernel/fork.c | 4 +++ kernel/ptrace.c | 45 ++++++++++++++++++++++++++---- kernel/signal.c | 9 ++++++ kernel/sys.c | 8 ++++- mm/oom_kill.c | 9 ++++-- security/keys/keyctl.c | 4 +-- security/selinux/hooks.c | 4 +-- 10 files changed, 136 insertions(+), 35 deletions(-)
fs/exec.c +12 -4
@@ -932,14 +932,16 @@ static int de_thread(struct task_struct *tsk)for (;;) {threadgroup_change_begin (tsk);write_lock_irq (&tasklist_lock); +write_real_parent_lock (tsk);/* -* Do this under tasklist_lock to ensure that -* exit_notify() can't miss ->group_exit_task +* Do this under real_parent's kin_lock to ensure +* that exit_notify() can't miss ->group_exit_task. */ sig->notify_count =-1 ;if (likely (leader->exit_state))break ;__set_current_state (TASK_KILLABLE ); +write_real_parent_unlock (tsk);write_unlock_irq (&tasklist_lock);threadgroup_change_end (tsk);schedule ();
@@ -995,9 +997,13 @@ static int de_thread(struct task_struct *tsk)* We are going to release_task()->ptrace_unlink() silently, * the tracer can sleep in do_wait(). EXIT_DEAD guarantees * the tracer wont't block again waiting for this thread. +* +* Also, we not need hold leader->parent->kin_lock, because +* it can't change till we release real_parent->kin_lock. */ if (unlikely (leader->ptrace))__wake_up_parent (leader, leader->parent); +write_real_parent_unlock (tsk);write_unlock_irq (&tasklist_lock);threadgroup_change_end (tsk);
@@ -1029,9 +1035,11 @@ static int de_thread(struct task_struct *tsk) sizeof(newsighand->action));write_lock_irq (&tasklist_lock); +write_parent_and_real_parent_lock (tsk);spin_lock (&oldsighand->siglock);rcu_assign_pointer (tsk->sighand, newsighand);spin_unlock (&oldsighand->siglock); +write_parent_and_real_parent_unlock (tsk);write_unlock_irq (&tasklist_lock);__cleanup_sighand (oldsighand);
@@ -1042,10 +1050,10 @@ static int de_thread(struct task_struct *tsk) killed:/* protects against exit_notify() and __exit_signal() */ -read_lock (&tasklist_lock); +read_real_parent_lock (tsk); sig->group_exit_task =NULL ; sig->notify_count =0 ; -read_unlock (&tasklist_lock); +read_real_parent_unlock (tsk);return -EAGAIN ; }
fs/proc/array.c +2 -2
@@ -588,7 +588,7 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)if (!task) goto put_start; -read_lock (&tasklist_lock); +read_lock (&start->kin_lock);if (task->real_parent == start && !(list_empty (&task->sibling))) {put_task_struct (task);if (!list_is_last (&task->sibling, &start->children)) {
@@ -623,7 +623,7 @@ get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos) } unlock: -read_unlock (&tasklist_lock); +read_unlock (&start->kin_lock); put_start:put_task_struct (start); out:
kernel/exit.c +55 -13
184 lines not on this page · raw message
kernel/fork.c +4 -0
@@ -1524,9 +1524,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,/* CLONE_PARENT re-uses the old parent */ if (clone_flags & (CLONE_PARENT |CLONE_THREAD )) { +write_real_parent_lock (current); p->real_parent = current->real_parent; p->parent_exec_id = current->parent_exec_id; }else { +write_lock (¤t->kin_lock); p->real_parent = current; p->parent_exec_id = current->self_exec_id; }
@@ -1550,6 +1552,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,recalc_sigpending ();if (signal_pending (current)) {spin_unlock (¤t->sighand->siglock); +write_real_parent_unlock (p);write_unlock_irq (&tasklist_lock); retval = -ERESTARTNOINTR ; goto bad_fork_free_pid;
@@ -1591,6 +1594,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, total_forks++;spin_unlock (¤t->sighand->siglock); +write_real_parent_unlock (p);syscall_tracepoint_update (p);write_unlock_irq (&tasklist_lock);
kernel/ptrace.c +38 -7
129 lines not on this page · raw message
kernel/signal.c +9 -0
@@ -1882,6 +1882,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)spin_unlock_irq (¤t->sighand->siglock);read_lock (&tasklist_lock); +read_parent_and_real_parent_lock (current);if (may_ptrace_stop ()) {/* * Notify parents of the stop.
@@ -1904,6 +1905,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)* XXX: implement read_unlock_no_resched(). */ preempt_disable (); +read_parent_and_real_parent_unlock (current);read_unlock (&tasklist_lock);preempt_enable_no_resched ();freezable_schedule ();
@@ -1925,6 +1927,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)__set_current_state (TASK_RUNNING );if (clear_code) current->exit_code =0 ; +read_parent_and_real_parent_unlock (current);read_unlock (&tasklist_lock); }
@@ -2079,7 +2082,9 @@ static bool do_signal_stop(int signr)*/ if (notify) {read_lock (&tasklist_lock); +read_parent_and_real_parent_lock (current);do_notify_parent_cldstop (current, false, notify); +read_parent_and_real_parent_unlock (current);read_unlock (&tasklist_lock); }
@@ -2225,11 +2230,13 @@ int get_signal(struct ksignal *ksig)* a duplicate. */ read_lock (&tasklist_lock); +read_parent_and_real_parent_lock (current->group_leader);do_notify_parent_cldstop (current, false, why);if (ptrace_reparented (current->group_leader))do_notify_parent_cldstop (current->group_leader, true, why); +read_parent_and_real_parent_unlock (current->group_leader);read_unlock (&tasklist_lock); goto relock;
@@ -2476,7 +2483,9 @@ void exit_signals(struct task_struct *tsk)*/ if (unlikely (group_stop)) {read_lock (&tasklist_lock); +read_parent_and_real_parent_lock (tsk);do_notify_parent_cldstop (tsk, false, group_stop); +read_parent_and_real_parent_unlock (tsk);read_unlock (&tasklist_lock); } }
kernel/sys.c +6 -2
@@ -939,7 +939,9 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) err = -ESRCH ; p =find_task_by_vpid (pid);if (!p) - goto out; + goto out2; + +write_real_parent_lock (p); err = -EINVAL ;if (!thread_group_leader (p))
@@ -981,7 +983,9 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) err =0 ; out: -/* All paths lead to here, thus we are safe. -DaveM */ +/* All (almost) paths lead to here, thus we are safe. -DaveM */ +write_real_parent_unlock (p); +out2:write_unlock_irq (&tasklist_lock);rcu_read_unlock ();return err;
mm/oom_kill.c +6 -3
@@ -538,13 +538,14 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,* parent. This attempts to lose the minimal amount of work done while * still freeing memory. */ -read_lock (&tasklist_lock); +rcu_read_lock ();for_each_thread (p, t) { +read_lock (&t->kin_lock);list_for_each_entry (child, &t->children, sibling) {unsigned int child_points;if (child->mm == p->mm) -continue ; + goto unlock;/* * oom_badness() returns 0 if the thread is unkillable */
@@ -557,8 +558,10 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,get_task_struct (victim); } } +unlock: +read_unlock (&t->kin_lock); } -read_unlock (&tasklist_lock); +rcu_read_unlock (); p =find_lock_task_mm (victim);if (!p) {
security/keys/keyctl.c +2 -2
@@ -1491,7 +1491,7 @@ long keyctl_session_to_parent(void) me = current;rcu_read_lock (); -write_lock_irq (&tasklist_lock); +write_real_parent_lock_irq (me); ret = -EPERM ; oldwork =NULL ;
@@ -1540,7 +1540,7 @@ long keyctl_session_to_parent(void)if (!ret) newwork =NULL ; unlock: -write_unlock_irq (&tasklist_lock); +write_real_parent_unlock_irq (me);rcu_read_unlock ();if (oldwork) put_cred(container_of(oldwork, struct cred, rcu));
security/selinux/hooks.c +2 -2
@@ -2461,9 +2461,9 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)/* Wake up the parent if it is waiting so that it can recheck * wait permission to the new task SID. */ -read_lock (&tasklist_lock); +read_real_parent_lock (current);__wake_up_parent (current, current->real_parent); -read_unlock (&tasklist_lock); +read_real_parent_unlock (current); }/* superblock security operations */
RFC PATCH 9/13 exit: Use for_each_thread() in do_wait()
In reply to a message not in the archiveRefactoring, no functionality change.
--- kernel/exit.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
kernel/exit.c +2 -3
@@ -1538,8 +1538,7 @@ static long do_wait(struct wait_opts *wo)set_current_state (TASK_INTERRUPTIBLE );read_lock (&tasklist_lock); - tsk = current; - do { +for_each_thread (current, tsk) { retval =do_wait_thread (wo, tsk);if (retval) goto end;
@@ -1550,7 +1549,7 @@ static long do_wait(struct wait_opts *wo)if (wo->wo_flags & __WNOTHREAD)break ; - }while_each_thread (current, tsk); + }read_unlock (&tasklist_lock); notask:
Oleg Nesterov
maintainerRe: [PATCH RFC 09/13] exit: Use for_each_thread() in do_wait()
Quoted 2 lines · Kirill Tkhai
Refactoring, no functionality change.
Hmm. unless I missed something this change is wrong.
Quoted 13 lines
kernel/exit.c +1
-2@@ -1538,8 +1538,7 @@ static long do_wait(struct wait_opts *wo)set_current_state (TASK_INTERRUPTIBLE );read_lock (&tasklist_lock); - tsk = current; - do { +for_each_thread (current, tsk) { retval =do_wait_thread (wo, tsk);if (retval) goto end;
kernel/exit.c static long do_wait(struct wait_opts *wo) · line 1552
Quoted 5 lines
kernel/exit.c +1
-1@@ -1550,7 +1549,7 @@ static long do_wait(struct wait_opts *wo)if (wo->wo_flags & __WNOTHREAD)break ; - }while_each_thread (current, tsk); + }
Please note the __WNOTHREAD check. This is the rare case when we actually want while_each_thread() (although it should die anyway).
for_each_thread() always starts from ->group_leader, but we need to start from "current" first.
Oleg.
Re: [PATCH RFC 09/13] exit: Use for_each_thread() in do_wait()
26.05.2015, 22:47, "Oleg Nesterov" <[email protected]>:
Quoted 24 lines · Kirill Tkhai
Quoted 1 line
Refactoring, no functionality change.
Hmm. unless I missed something this change is wrong.
Quoted 14 lines
---
a/kernel/exit.c
+++b/kernel/exit.c
@@ -1538,8 +1538,7 @@ static longdo_wait(struct wait_opts *wo)set_current_state(TASK_INTERRUPTIBLE);
read_lock(&tasklist_lock);
- tsk = current;
- do {
+for_each_thread(current, tsk){
retval =do_wait_thread(wo, tsk);
if (retval)
goto end;
@@ -1550,7 +1549,7 @@ static longdo_wait(struct wait_opts *wo)Quoted 4 lines
if (
wo->wo_flags&__WNOTHREAD)
break;
- }while_each_thread(current, tsk);
+ }
Quoted 5 lines
Please note the
__WNOTHREADcheck. This is the rare case when we actually wantwhile_each_thread()(although it should die anyway).
for_each_thread()always starts from ->group_leader, but we need to start from "current" first.
Sure, this must be like below. Thanks!
I won't resend the whole series with only this one patch changed to do not bomb mail boxes. Waiting for the review.
kernel/exit.c +5 -3
@@ -1538,8 +1538,10 @@ static long do_wait(struct wait_opts *wo)set_current_state (TASK_INTERRUPTIBLE );read_lock (&tasklist_lock); - tsk = current; - do { +for_each_thread (current, tsk) { +if (wo->wo_flags & __WNOTHREAD) + tsk = current; + retval =do_wait_thread (wo, tsk);if (retval) goto end;
@@ -1550,7 +1552,7 @@ static long do_wait(struct wait_opts *wo)if (wo->wo_flags & __WNOTHREAD)break ; - }while_each_thread (current, tsk); + }read_unlock (&tasklist_lock); notask:
Re: [PATCH RFC 09/13] exit: Use for_each_thread() in do_wait()
27.05.2015, 12:34, "Kirill Tkhai" <[email protected]>:
Quoted 57 lines
26.05.2015, 22:47, "Oleg Nesterov" <[email protected]>:
Quoted 21 lines · Kirill Tkhai
Quoted 1 line
Refactoring, no functionality change.
Hmm. unless I missed something this change is wrong.
Quoted 14 lines
---
a/kernel/exit.c
+++b/kernel/exit.c
@@ -1538,8 +1538,7 @@ static longdo_wait(struct wait_opts *wo)set_current_state(TASK_INTERRUPTIBLE);
read_lock(&tasklist_lock);
- tsk = current;
- do {
+for_each_thread(current, tsk){
retval =do_wait_thread(wo, tsk);
if (retval)
goto end;
@@ -1550,7 +1549,7 @@ static longdo_wait(struct wait_opts *wo)Quoted 4 lines
if (
wo->wo_flags&__WNOTHREAD) break;
- }while_each_thread(current, tsk);
+ }Quoted 5 lines
Please note the
__WNOTHREADcheck. This is the rare case when we actually wantwhile_each_thread()(although it should die anyway).for_each_thread() always starts from ->group_leader, but we need to start from "current" first.
Sure, this must be like below. Thanks!
I won't resend the whole series with only this one patch changed to do not bomb mail boxes. Waiting for the review.kernel/exit.c +5
-3@@ -1538,8 +1538,10 @@ static long do_wait(struct wait_opts *wo) set_current_state(TASK_INTERRUPTIBLE); read_lock(&tasklist_lock); - tsk = current; - do { +for_each_thread (current, tsk) { +if (wo->wo_flags & __WNOTHREAD) + tsk = current; + retval = do_wait_thread(wo, tsk); if (retval) goto end;@@ -1550,7 +1552,7 @@ static long do_wait(struct wait_opts *wo) if (wo->wo_flags & __WNOTHREAD) break; - }while_each_thread (current, tsk); + } read_unlock(&tasklist_lock);
Quoted 1 line
notask:
Hm. Once again. Is the problem in __WNOTHREAD only? Should we firstly reap our own children in common case?
RFC PATCH 10/13 exit: Add struct wait_opts's member held_lock and use it for tasklist_lock
In reply to a message not in the archiveThese will be used in next patches.
--- kernel/exit.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/exit.c +6 -4
@@ -953,6 +953,7 @@ struct wait_opts {wait_queue_t child_wait;int notask_error; +rwlock_t *held_lock; }; static inline
@@ -1034,7 +1035,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)int why;get_task_struct (p); -read_unlock (&tasklist_lock); +read_unlock (wo->held_lock);sched_annotate_sleep ();if ((exit_code &0x7f ) ==0 ) {
@@ -1056,7 +1057,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)/* * We own this thread, nobody else can reap it. */ -read_unlock (&tasklist_lock); +read_unlock (wo->held_lock);sched_annotate_sleep ();/*
@@ -1246,7 +1247,7 @@ static int wait_task_stopped(struct wait_opts *wo,get_task_struct (p); pid =task_pid_vnr (p); why = ptrace ?CLD_TRAPPED :CLD_STOPPED ; -read_unlock (&tasklist_lock); +read_unlock (wo->held_lock);sched_annotate_sleep ();if (unlikely (wo->wo_flags &WNOWAIT ))
@@ -1309,7 +1310,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) pid =task_pid_vnr (p);get_task_struct (p); -read_unlock (&tasklist_lock); +read_unlock (wo->held_lock);sched_annotate_sleep ();if (!wo->wo_info) {
@@ -1538,6 +1539,7 @@ static long do_wait(struct wait_opts *wo)set_current_state (TASK_INTERRUPTIBLE );read_lock (&tasklist_lock); + wo->held_lock = &tasklist_lock;for_each_thread (current, tsk) { retval =do_wait_thread (wo, tsk);if (retval)
RFC PATCH 11/13 exit: Syncronize on kin_lock while do_notify_parent()
In reply to a message not in the archiveNote: do_wait() (working on tsk) may catch its child, which is being traced by a thread from tsk's thread group. For proper synchronization, we must hold both parent and real_parent locks for calling do_notify_parent().
Also delete tasklist_lock dependence in ptrace_attach() etc, because everything is already synchronized on kin_lock (Due to previous patches).
--- kernel/exit.c | 42 ++++++++++++++++++++++++++++-------------- kernel/ptrace.c | 20 ++++++-------------- kernel/signal.c | 11 ++--------- 3 files changed, 36 insertions(+), 37 deletions(-)
kernel/exit.c +28 -14
@@ -183,6 +183,7 @@ void release_task(struct task_struct *p)write_parent_and_real_parent_lock (p);ptrace_release_task (p);__exit_signal (p); +write_unlock (&tasklist_lock);/* * If we are the last non-leader member of the thread
@@ -203,8 +204,7 @@ void release_task(struct task_struct *p) leader->exit_state =EXIT_DEAD ; } -write_parent_and_real_parent_unlock (p); -write_unlock_irq (&tasklist_lock); +write_parent_and_real_parent_unlock_irq (p);release_thread (p);call_rcu (&p->rcu, delayed_put_task_struct);
@@ -1016,7 +1016,7 @@ static int wait_noreap_copyout(struct wait_opts *wo, struct task_struct *p,/* * Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold -* read_lock(&tasklist_lock) on entry. If we return zero, we still hold +* read_lock(wo->held_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */
@@ -1036,6 +1036,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)get_task_struct (p);read_unlock (wo->held_lock); +rcu_read_unlock ();sched_annotate_sleep ();if ((exit_code &0x7f ) ==0 ) {
@@ -1058,6 +1059,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)* We own this thread, nobody else can reap it. */ read_unlock (wo->held_lock); +rcu_read_unlock ();sched_annotate_sleep ();/*
@@ -1152,16 +1154,21 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p) retval = pid;if (state ==EXIT_TRACE ) { -write_lock_irq (&tasklist_lock); +struct task_struct *old_parent; +write_parent_and_real_parent_lock_irq (p); + old_parent = p->parent;/* We dropped tasklist, ptracer could die and untrace */ ptrace_unlink (p); +if (p->parent != old_parent) +write_unlock (&old_parent->kin_lock); +/* If parent wants a zombie, don't release it now */ state =EXIT_ZOMBIE ;if (do_notify_parent (p, p->exit_signal)) state =EXIT_DEAD ; p->exit_state = state; -write_unlock_irq (&tasklist_lock); +write_parent_unlock_irq (p); }if (state ==EXIT_DEAD )release_task (p);
@@ -1191,13 +1198,13 @@ static int *task_stopped_code(struct task_struct *p, bool ptrace)* Handle sys_wait4() work for %p in state %TASK_STOPPED or %TASK_TRACED. * * CONTEXT: -* read_lock(&tasklist_lock), which is released if return value is +* read_lock(wo->held_lock), which is released if return value is * non-zero. Also, grabs and releases @p->sighand->siglock. * * RETURNS: * 0 if wait condition didn't exist and search for other wait conditions * should continue. Non-zero return, -errno on failure and @p's pid on -* success, implies that tasklist_lock is released and wait condition +* success, implies that wo->held_lock is released and wait condition * search should terminate. */ static int wait_task_stopped(struct wait_opts *wo,
@@ -1241,13 +1248,14 @@ static int wait_task_stopped(struct wait_opts *wo,* Now we are pretty sure this task is interesting. * Make sure it doesn't get reaped out from under us while we * give up the lock and then examine it below. We don't want to -* keep holding onto the tasklist_lock while we call getrusage and +* keep holding onto wo->held_lock while we call getrusage and * possibly take page faults for user memory. */ get_task_struct (p); pid =task_pid_vnr (p); why = ptrace ?CLD_TRAPPED :CLD_STOPPED ;read_unlock (wo->held_lock); +rcu_read_unlock ();sched_annotate_sleep ();if (unlikely (wo->wo_flags &WNOWAIT ))
@@ -1281,7 +1289,7 @@ static int wait_task_stopped(struct wait_opts *wo,/* * Handle do_wait work for one task in a live, non-stopped state. -* read_lock(&tasklist_lock) on entry. If we return zero, we still hold +* read_lock(wo->held_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */
@@ -1311,6 +1319,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) pid =task_pid_vnr (p);get_task_struct (p);read_unlock (wo->held_lock); +rcu_read_unlock ();sched_annotate_sleep ();if (!wo->wo_info) {
@@ -1334,7 +1343,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)* Consider @p for a wait by @parent. * * -ECHILD should be in ->notask_error before the first call. -* Returns nonzero for a final return, when we have unlocked tasklist_lock. +* Returns nonzero for a final return, when we have unlocked wo->held_lock. * Returns zero if the search for a child should continue; * then ->notask_error is 0 if @p is an eligible child, * or another error from security_task_wait(), or still -ECHILD.
@@ -1392,6 +1401,9 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,* If a ptracer wants to distinguish these two events for its * own children it should create a separate process which takes * the role of real parent. +* +* Since we use call do_notify_parent() under both parent's and +* real_parent's kin_locks, we are protected from it. */ if (!ptrace_reparented (p)) ptrace =1 ;
@@ -1460,7 +1472,7 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,* Do the work of do_wait() for one thread in the group, @tsk. * * -ECHILD should be in ->notask_error before the first call. -* Returns nonzero for a final return, when we have unlocked tasklist_lock. +* Returns nonzero for a final return, when we have unlocked wo->held_lock. * Returns zero if the search for a child should continue; then * ->notask_error is 0 if there were any eligible children, * or another error from security_task_wait(), or still -ECHILD.
@@ -1538,9 +1550,10 @@ static long do_wait(struct wait_opts *wo) goto notask;set_current_state (TASK_INTERRUPTIBLE ); -read_lock (&tasklist_lock); - wo->held_lock = &tasklist_lock; +rcu_read_lock ();for_each_thread (current, tsk) { +read_lock (&tsk->kin_lock); + wo->held_lock = &tsk->kin_lock; retval =do_wait_thread (wo, tsk);if (retval) goto end;
@@ -1548,11 +1561,12 @@ static long do_wait(struct wait_opts *wo) retval =ptrace_do_wait (wo, tsk);if (retval) goto end; +read_unlock (&tsk->kin_lock);if (wo->wo_flags & __WNOTHREAD)break ; } -read_unlock (&tasklist_lock); +rcu_read_unlock (); notask: retval = wo->notask_error;
kernel/ptrace.c +6 -14
@@ -180,7 +180,6 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)* we are sure that this is our traced child and that can only * be changed by us so it's not changing right after this. */ -read_lock (&tasklist_lock);read_parent_lock (child);if (child->ptrace && child->parent == current) {WARN_ON (child->state == __TASK_TRACED);
@@ -192,7 +191,6 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) ret =0 ; }read_parent_unlock (child); -read_unlock (&tasklist_lock);if (!ret && !ignore_state) {if (!wait_task_inactive (child, __TASK_TRACED)) {
@@ -314,8 +312,7 @@ static int ptrace_attach(struct task_struct *task, long request,if (retval) goto unlock_creds; -write_lock_irq (&tasklist_lock); -write_parent_and_given_lock (task, current); +write_parent_and_given_lock_irq (task, current); old_parent = task->parent; retval = -EPERM ;if (unlikely (task->exit_state) || task->ptrace) {
@@ -365,8 +362,7 @@ static int ptrace_attach(struct task_struct *task, long request, retval =0 ; unlock_current: -write_unlock (¤t->kin_lock); -write_unlock_irq (&tasklist_lock); +write_unlock_irq (¤t->kin_lock); unlock_creds:mutex_unlock (&task->signal->cred_guard_mutex); out:
@@ -389,8 +385,7 @@ static int ptrace_traceme(void) {int ret = -EPERM ; -write_lock_irq (&tasklist_lock); -write_parent_lock (current); +write_parent_lock_irq (current);/* Are we already being traced? */ if (!current->ptrace) { ret =security_ptrace_traceme (current->parent);
@@ -405,8 +400,7 @@ static int ptrace_traceme(void)__ptrace_link (current, current->real_parent); } } -write_parent_unlock (current); -write_unlock_irq (&tasklist_lock); +write_parent_unlock_irq (current);return ret; }
@@ -474,8 +468,7 @@ static int ptrace_detach(struct task_struct *child, unsigned int data)ptrace_disable (child);clear_tsk_thread_flag (child,TIF_SYSCALL_TRACE ); -write_lock_irq (&tasklist_lock); -write_parent_and_real_parent_lock (child); +write_parent_and_real_parent_lock_irq (child); old_parent = child->parent;/* * We rely on ptrace_freeze_traced(). It can't be killed and
@@ -490,8 +483,7 @@ static int ptrace_detach(struct task_struct *child, unsigned int data)__ptrace_detach (current, child);if (old_parent != child->real_parent)write_unlock (&old_parent->kin_lock); -write_real_parent_unlock (child); -write_unlock_irq (&tasklist_lock); +write_real_parent_unlock_irq (child);proc_ptrace_connector (child,PTRACE_DETACH );
kernel/signal.c +2 -9
@@ -1732,6 +1732,8 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,struct sighand_struct *sighand;cputime_t utime, stime; +BUG_ON (!same_thread_group (tsk, current)); +if (for_ptracer) { parent = tsk->parent; }else {
@@ -1881,7 +1883,6 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)task_clear_jobctl_trapping (current);spin_unlock_irq (¤t->sighand->siglock); -read_lock (&tasklist_lock);read_parent_and_real_parent_lock (current);if (may_ptrace_stop ()) {/*
@@ -1906,7 +1907,6 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)*/ preempt_disable ();read_parent_and_real_parent_unlock (current); -read_unlock (&tasklist_lock);preempt_enable_no_resched ();freezable_schedule (); }else {
@@ -1928,7 +1928,6 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)if (clear_code) current->exit_code =0 ;read_parent_and_real_parent_unlock (current); -read_unlock (&tasklist_lock); }/*
@@ -2081,11 +2080,9 @@ static bool do_signal_stop(int signr)* TASK_TRACED. */ if (notify) { -read_lock (&tasklist_lock);read_parent_and_real_parent_lock (current);do_notify_parent_cldstop (current, false, notify);read_parent_and_real_parent_unlock (current); -read_unlock (&tasklist_lock); }/* Now we don't run again until woken by SIGCONT or SIGKILL */
@@ -2229,7 +2226,6 @@ int get_signal(struct ksignal *ksig)* the ptracer of the group leader too unless it's gonna be * a duplicate. */ -read_lock (&tasklist_lock);read_parent_and_real_parent_lock (current->group_leader);do_notify_parent_cldstop (current, false, why);
@@ -2237,7 +2233,6 @@ int get_signal(struct ksignal *ksig)do_notify_parent_cldstop (current->group_leader, true, why);read_parent_and_real_parent_unlock (current->group_leader); -read_unlock (&tasklist_lock); goto relock; }
@@ -2482,11 +2477,9 @@ void exit_signals(struct task_struct *tsk)* should always go to the real parent of the group leader. */ if (unlikely (group_stop)) { -read_lock (&tasklist_lock);read_parent_and_real_parent_lock (tsk);do_notify_parent_cldstop (tsk, false, group_stop);read_parent_and_real_parent_unlock (tsk); -read_unlock (&tasklist_lock); } }
RFC PATCH 12/13 exit: Delete write dependence on tasklist_lock in exit_notify()
In reply to a message not in the archiveRelatives and parent notifications have already protected by appropriate kin_lock, so we may do not to take tasklist_lock for writing.
Hovewer, we still need it read locked to protect PGRP and PSID.
--- kernel/exit.c | 19 +++++++------------ kernel/ptrace.c | 6 +++--- 2 files changed, 10 insertions(+), 15 deletions(-)
kernel/exit.c +7 -12
@@ -460,8 +460,6 @@ static struct task_struct *find_alive_thread(struct task_struct *p) }static void check_pid_ns_reaper_exit (struct task_struct *father) - __releases(&tasklist_lock) - __acquires(&tasklist_lock) {struct pid_namespace *pid_ns =task_active_pid_ns (father);struct task_struct *reaper;
@@ -473,7 +471,7 @@ static void check_pid_ns_reaper_exit(struct task_struct *father)if (likely (reaper != father))return ; -write_lock (&pid_ns->cr_lock); +write_lock_irq (&pid_ns->cr_lock);read_real_parent_lock (father); reaper =find_alive_thread (father);
@@ -481,18 +479,16 @@ static void check_pid_ns_reaper_exit(struct task_struct *father) pid_ns->child_reaper = reaper;read_real_parent_unlock (father); -write_unlock (&pid_ns->cr_lock); +write_unlock_irq (&pid_ns->cr_lock);if (reaper)return ; -write_unlock_irq (&tasklist_lock);if (unlikely (pid_ns == &init_pid_ns)) {panic ("Attempted to kill init! exitcode=0x%08x\n" , father->signal->group_exit_code ?: father->exit_code); }zap_pid_ns_processes (pid_ns); -write_lock_irq (&tasklist_lock); }/*
@@ -598,7 +594,6 @@ static void forget_original_parent(struct task_struct *father,if (unlikely (!list_empty (&father->ptraced)))exit_ptrace (father, dead); -/* Can drop and reacquire tasklist_lock */ check_pid_ns_reaper_exit (father);/* read_lock() guarantees concurrent thread sees our PF_EXITING */
@@ -609,7 +604,7 @@ static void forget_original_parent(struct task_struct *father, }read_unlock (&father->kin_lock); -read_lock (&task_active_pid_ns (father)->cr_lock); +read_lock_irq (&task_active_pid_ns (father)->cr_lock); reaper =find_double_lock_new_reaper (father);list_for_each_entry (p, &father->children, sibling) {
@@ -632,7 +627,7 @@ static void forget_original_parent(struct task_struct *father,list_splice_tail_init (&father->children, &reaper->children);double_write_unlock (&reaper->kin_lock, &father->kin_lock); -read_unlock (&task_active_pid_ns (father)->cr_lock); +read_unlock_irq (&task_active_pid_ns (father)->cr_lock); }/*
@@ -645,12 +640,13 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead); -write_lock_irq (&tasklist_lock);forget_original_parent (tsk, &dead); +read_lock_irq (&tasklist_lock);write_parent_and_real_parent_lock (tsk);if (group_dead)kill_orphaned_pgrp (tsk->group_leader,NULL ); +read_unlock (&tasklist_lock);if (unlikely (tsk->ptrace)) { int sig = thread_group_leader(tsk) &&
@@ -672,8 +668,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead)/* mt-exec, de_thread() is waiting for group leader */ if (unlikely (tsk->signal->notify_count <0 ))wake_up_process (tsk->signal->group_exit_task); -write_parent_and_real_parent_unlock (tsk); -write_unlock_irq (&tasklist_lock); +write_parent_and_real_parent_unlock_irq (tsk);list_for_each_entry_safe (p, n, &dead, ptrace_entry) {list_del_init (&p->ptrace_entry);
kernel/ptrace.c +3 -3
@@ -505,9 +505,9 @@ void exit_ptrace(struct task_struct *tracer, struct list_head *dead)if (!p)break ; -write_parent_and_real_parent_lock (p); +write_parent_and_real_parent_lock_irq (p);if (p->parent != tracer) { -write_parent_and_real_parent_unlock (p); +write_parent_and_real_parent_unlock_irq (p);continue ; }
@@ -517,7 +517,7 @@ void exit_ptrace(struct task_struct *tracer, struct list_head *dead)if (__ptrace_detach (tracer, p))list_add (&p->ptrace_entry, dead); -write_parent_and_real_parent_unlock (p); +write_parent_and_real_parent_unlock_irq (p); }rcu_read_unlock (); }
RFC PATCH 13/13 core: Nest tasklist_lock into task_struct::kin_lock
In reply to a message not in the archivetasklist_lock is global lock, and it should be locked as small time as possible. So that, lets nest it under kin_lock to minimize this time.
--- fs/exec.c | 20 ++++++++++---------- kernel/exit.c | 13 +++++++------ kernel/fork.c | 15 +++++++-------- kernel/sys.c | 17 ++++++++--------- 4 files changed, 32 insertions(+), 33 deletions(-)
fs/exec.c +10 -10
@@ -931,8 +931,7 @@ static int de_thread(struct task_struct *tsk)for (;;) {threadgroup_change_begin (tsk); -write_lock_irq (&tasklist_lock); -write_real_parent_lock (tsk); +write_real_parent_lock_irq (tsk);/* * Do this under real_parent's kin_lock to ensure * that exit_notify() can't miss ->group_exit_task.
@@ -941,8 +940,7 @@ static int de_thread(struct task_struct *tsk)if (likely (leader->exit_state))break ;__set_current_state (TASK_KILLABLE ); -write_real_parent_unlock (tsk); -write_unlock_irq (&tasklist_lock); +write_real_parent_unlock_irq (tsk);threadgroup_change_end (tsk);schedule ();if (unlikely (__fatal_signal_pending (tsk)))
@@ -964,6 +962,8 @@ static int de_thread(struct task_struct *tsk)BUG_ON (!same_thread_group (leader, tsk));BUG_ON (has_group_leader_pid (tsk)); + +write_lock (&tasklist_lock);/* * An exec() starts a new thread group with the * TGID of the previous thread group. Rehash the
@@ -982,6 +982,7 @@ static int de_thread(struct task_struct *tsk)transfer_pid (leader, tsk,PIDTYPE_SID );list_replace_rcu (&leader->tasks, &tsk->tasks); +write_unlock (&tasklist_lock);list_replace_init (&leader->sibling, &tsk->sibling); tsk->group_leader = tsk;
@@ -1003,8 +1004,7 @@ static int de_thread(struct task_struct *tsk)*/ if (unlikely (leader->ptrace))__wake_up_parent (leader, leader->parent); -write_real_parent_unlock (tsk); -write_unlock_irq (&tasklist_lock); +write_real_parent_unlock_irq (tsk);threadgroup_change_end (tsk);release_task (leader);
@@ -1034,13 +1034,13 @@ static int de_thread(struct task_struct *tsk)memcpy (newsighand->action, oldsighand->action,sizeof (newsighand->action)); -write_lock_irq (&tasklist_lock); -write_parent_and_real_parent_lock (tsk); +write_parent_and_real_parent_lock_irq (tsk); +write_lock (&tasklist_lock);spin_lock (&oldsighand->siglock);rcu_assign_pointer (tsk->sighand, newsighand);spin_unlock (&oldsighand->siglock); -write_parent_and_real_parent_unlock (tsk); -write_unlock_irq (&tasklist_lock); +write_unlock (&tasklist_lock); +write_parent_and_real_parent_unlock_irq (tsk);__cleanup_sighand (oldsighand); }
kernel/exit.c +7 -6
@@ -179,9 +179,9 @@ void release_task(struct task_struct *p)proc_flush_task (p); -write_lock_irq (&tasklist_lock); -write_parent_and_real_parent_lock (p); +write_parent_and_real_parent_lock_irq (p);ptrace_release_task (p); +write_lock (&tasklist_lock);__exit_signal (p);write_unlock (&tasklist_lock);
@@ -642,11 +642,12 @@ static void exit_notify(struct task_struct *tsk, int group_dead)forget_original_parent (tsk, &dead); -read_lock_irq (&tasklist_lock); -write_parent_and_real_parent_lock (tsk); -if (group_dead) +write_parent_and_real_parent_lock_irq (tsk); +if (group_dead) { +read_lock (&tasklist_lock);kill_orphaned_pgrp (tsk->group_leader,NULL ); -read_unlock (&tasklist_lock); +read_unlock (&tasklist_lock); + }if (unlikely (tsk->ptrace)) { int sig = thread_group_leader(tsk) &&
kernel/fork.c +7 -8
@@ -1520,19 +1520,18 @@ static struct task_struct *copy_process(unsigned long clone_flags,* Make it visible to the rest of the system, but dont wake it up yet. * Need tasklist lock for parent etc handling! */ -write_lock_irq (&tasklist_lock); -/* CLONE_PARENT re-uses the old parent */ if (clone_flags & (CLONE_PARENT |CLONE_THREAD )) { -write_real_parent_lock (current); +write_real_parent_lock_irq (current); p->real_parent = current->real_parent; p->parent_exec_id = current->parent_exec_id; }else { -write_lock (¤t->kin_lock); +write_lock_irq (¤t->kin_lock); p->real_parent = current; p->parent_exec_id = current->self_exec_id; } +write_lock (&tasklist_lock);spin_lock (¤t->sighand->siglock);/*
@@ -1552,8 +1551,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,recalc_sigpending ();if (signal_pending (current)) {spin_unlock (¤t->sighand->siglock); -write_real_parent_unlock (p); -write_unlock_irq (&tasklist_lock); +write_unlock (&tasklist_lock); +write_real_parent_unlock_irq (p); retval = -ERESTARTNOINTR ; goto bad_fork_free_pid; }
@@ -1594,9 +1593,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, total_forks++;spin_unlock (¤t->sighand->siglock); -write_real_parent_unlock (p); +write_unlock (&tasklist_lock); +write_real_parent_unlock_irq (p);syscall_tracepoint_update (p); -write_unlock_irq (&tasklist_lock);proc_fork_connector (p);cgroup_post_fork (p);
kernel/sys.c +8 -9
@@ -931,17 +931,16 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)return -EINVAL ;rcu_read_lock (); -/* From this point forward we keep holding onto the tasklist lock -* so that our parent does not change from under us. -DaveM -*/ -write_lock_irq (&tasklist_lock); - err = -ESRCH ; p =find_task_by_vpid (pid);if (!p) goto out2; -write_real_parent_lock (p); +write_real_parent_lock_irq (p); +if (p->flags &PF_EXITPIDONE ) + goto out3; + +write_lock (&tasklist_lock); err = -EINVAL ;if (!thread_group_leader (p))
@@ -983,10 +982,10 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) err =0 ; out: -/* All (almost) paths lead to here, thus we are safe. -DaveM */ -write_real_parent_unlock (p); +write_unlock (&tasklist_lock); +out3: +write_real_parent_unlock_irq (p); out2: -write_unlock_irq (&tasklist_lock);rcu_read_unlock ();return err; }