aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-07-25 22:45:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-26 15:00:08 -0400
commitdae33574dcf5211e1f43c7e45fa29f73ba3e00cb (patch)
tree090eb4af17451836fb39cebf57fbee64a6bd23b5
parentdaded34be96b1975ff8539ff62ad8b158ce7d842 (diff)
tracehook: release_task
This moves the ptrace-related logic from release_task into tracehook.h and ptrace.h inlines. It provides clean hooks both before and after locking tasklist_lock, for future tracing logic to do more cleanup without the lock. This also changes release_task() itself in the rare "zap_leader" case to set the leader to EXIT_DEAD before iterating. This maintains the invariant that release_task() only ever handles a task in EXIT_DEAD. This is a common-sense invariant that is already always true except in this one arcane case of zombie leader whose parent ignores SIGCHLD. This change is harmless and only costs one store in this one rare case. It keeps the expected state more consisently sane, which is nicer when debugging weirdness in release_task(). It also lets some future code in the tracehook entry points rely on this invariant for bookkeeping. Signed-off-by: Roland McGrath <roland@redhat.com> Cc: Oleg Nesterov <oleg@tv-sign.ru> Reviewed-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/ptrace.h13
-rw-r--r--include/linux/tracehook.h28
-rw-r--r--kernel/exit.c21
3 files changed, 50 insertions, 12 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index dae6d85520fb..ed69c03692d9 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -176,6 +176,19 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
176 } 176 }
177} 177}
178 178
179/**
180 * ptrace_release_task - final ptrace-related cleanup of a zombie being reaped
181 * @task: task in %EXIT_DEAD state
182 *
183 * Called with write_lock(&tasklist_lock) held.
184 */
185static inline void ptrace_release_task(struct task_struct *task)
186{
187 BUG_ON(!list_empty(&task->ptraced));
188 ptrace_unlink(task);
189 BUG_ON(!list_empty(&task->ptrace_entry));
190}
191
179#ifndef force_successful_syscall_return 192#ifndef force_successful_syscall_return
180/* 193/*
181 * System call handlers that, upon successful completion, need to return a 194 * System call handlers that, upon successful completion, need to return a
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 830e6e16097d..9a5b3be2503a 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -228,4 +228,32 @@ static inline void tracehook_report_vfork_done(struct task_struct *child,
228 ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid); 228 ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid);
229} 229}
230 230
231/**
232 * tracehook_prepare_release_task - task is being reaped, clean up tracing
233 * @task: task in %EXIT_DEAD state
234 *
235 * This is called in release_task() just before @task gets finally reaped
236 * and freed. This would be the ideal place to remove and clean up any
237 * tracing-related state for @task.
238 *
239 * Called with no locks held.
240 */
241static inline void tracehook_prepare_release_task(struct task_struct *task)
242{
243}
244
245/**
246 * tracehook_finish_release_task - task is being reaped, clean up tracing
247 * @task: task in %EXIT_DEAD state
248 *
249 * This is called in release_task() when @task is being in the middle of
250 * being reaped. After this, there must be no tracing entanglements.
251 *
252 * Called with write_lock_irq(&tasklist_lock) held.
253 */
254static inline void tracehook_finish_release_task(struct task_struct *task)
255{
256 ptrace_release_task(task);
257}
258
231#endif /* <linux/tracehook.h> */ 259#endif /* <linux/tracehook.h> */
diff --git a/kernel/exit.c b/kernel/exit.c
index c3691cbc220a..da28745f7c38 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -163,27 +163,17 @@ static void delayed_put_task_struct(struct rcu_head *rhp)
163 put_task_struct(container_of(rhp, struct task_struct, rcu)); 163 put_task_struct(container_of(rhp, struct task_struct, rcu));
164} 164}
165 165
166/*
167 * Do final ptrace-related cleanup of a zombie being reaped.
168 *
169 * Called with write_lock(&tasklist_lock) held.
170 */
171static void ptrace_release_task(struct task_struct *p)
172{
173 BUG_ON(!list_empty(&p->ptraced));
174 ptrace_unlink(p);
175 BUG_ON(!list_empty(&p->ptrace_entry));
176}
177 166
178void release_task(struct task_struct * p) 167void release_task(struct task_struct * p)
179{ 168{
180 struct task_struct *leader; 169 struct task_struct *leader;
181 int zap_leader; 170 int zap_leader;
182repeat: 171repeat:
172 tracehook_prepare_release_task(p);
183 atomic_dec(&p->user->processes); 173 atomic_dec(&p->user->processes);
184 proc_flush_task(p); 174 proc_flush_task(p);
185 write_lock_irq(&tasklist_lock); 175 write_lock_irq(&tasklist_lock);
186 ptrace_release_task(p); 176 tracehook_finish_release_task(p);
187 __exit_signal(p); 177 __exit_signal(p);
188 178
189 /* 179 /*
@@ -205,6 +195,13 @@ repeat:
205 * that case. 195 * that case.
206 */ 196 */
207 zap_leader = task_detached(leader); 197 zap_leader = task_detached(leader);
198
199 /*
200 * This maintains the invariant that release_task()
201 * only runs on a task in EXIT_DEAD, just for sanity.
202 */
203 if (zap_leader)
204 leader->exit_state = EXIT_DEAD;
208 } 205 }
209 206
210 write_unlock_irq(&tasklist_lock); 207 write_unlock_irq(&tasklist_lock);