This patch moves "struct utrace" into "struct task_struct" directly instead of
being referenced by a pointer from task_struct. The main reason is utrace code
leaving stale ->utrace pointer and freeing "struct utrace" itself. This
manifests as crashes in __rcu_process_callbacks() and other nasties.
As side effect this makes attaching and detaching logic much simpler and
streamlined. As you see ~200 lines are gone.
I can't oops 2-way Opteron box with this patch applied.
NOTE: patch is against (utrace patch against 2.6.21)
NOTE: "utrace_engine_cache" still leaks
NOTE: "XXX ptrace_report_death" leaks still happen
NOTE: most certainly some locking is still missed and I need to test
on a couple more boxes.
Signed-off-by: Alexey Dobriyan <[email protected]>
---
include/linux/sched.h | 7
include/linux/tracehook.h | 15 -
include/linux/utrace.h | 39 ++-
kernel/fork.c | 9
kernel/utrace.c | 457 ++++++++++++----------------------------------
5 files changed, 157 insertions(+), 370 deletions(-)
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -83,6 +83,7 @@ #include <linux/resource.h>
#include <linux/timer.h>
#include <linux/hrtimer.h>
#include <linux/task_io_accounting.h>
+#include <linux/utrace.h>
#include <asm/processor.h>
@@ -940,11 +941,7 @@ #endif
void *security;
struct audit_context *audit_context;
seccomp_t seccomp;
-
-#ifdef CONFIG_UTRACE
- struct utrace *utrace;
- unsigned long utrace_flags;
-#endif
+ struct utrace utrace;
/* Thread group tracking */
u32 parent_exec_id;
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -310,16 +310,6 @@ utrace_regset_copyin_ignore(unsigned int
***
***/
-
-/*
- * Called in copy_process when setting up the copied task_struct,
- * with tasklist_lock held for writing.
- */
-static inline void tracehook_init_task(struct task_struct *child)
-{
- utrace_init_task(child);
-}
-
/*
* Called from release_task, no locks held.
* After this, there should be no tracing entanglements.
@@ -327,8 +317,7 @@ static inline void tracehook_init_task(s
static inline void tracehook_release_task(struct task_struct *p)
{
smp_mb();
- if (tsk_utrace_struct(p) != NULL)
- utrace_release_task(p);
+ utrace_release_task(p);
}
/*
@@ -339,7 +328,7 @@ static inline void tracehook_release_tas
*/
static inline int tracehook_check_released(struct task_struct *p)
{
- return unlikely(tsk_utrace_struct(p) != NULL);
+ return 0;
}
/*
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -50,11 +50,30 @@ #include <linux/sched.h>
struct linux_binprm;
struct pt_regs;
-struct utrace;
+struct task_struct;
struct utrace_signal;
struct utrace_regset;
struct utrace_regset_view;
+#ifdef CONFIG_UTRACE
+struct utrace {
+ unsigned long flags;
+ union {
+ struct {
+ struct task_struct *cloning;
+ struct utrace_signal *signal;
+ } live;
+ struct {
+ unsigned long flags;
+ } exit;
+ } u;
+ struct list_head engines;
+ spinlock_t lock;
+};
+#else
+struct utrace {
+};
+#endif
/*
* Flags in task_struct.utrace_flags and utrace_attached_engine.flags.
@@ -493,19 +512,8 @@ void utrace_signal_handler_singlestep(st
/*
* <linux/tracehook.h> uses these accessors to avoid #ifdef CONFIG_UTRACE.
*/
-static inline unsigned long tsk_utrace_flags(struct task_struct *tsk)
-{
- return tsk->utrace_flags;
-}
-static inline struct utrace *tsk_utrace_struct(struct task_struct *tsk)
-{
- return tsk->utrace;
-}
-static inline void utrace_init_task(struct task_struct *child)
-{
- child->utrace_flags = 0;
- child->utrace = NULL;
-}
+#define tsk_utrace_flags(tsk) ((tsk)->utrace.flags)
+#define tsk_utrace_struct(tsk) (&(tsk)->utrace)
#else /* !CONFIG_UTRACE */
@@ -517,9 +525,6 @@ static struct utrace *tsk_utrace_struct(
{
return NULL;
}
-static inline void utrace_init_task(struct task_struct *child)
-{
-}
/*
* The calls to these should all be in if (0) and optimized out entirely.
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1241,8 +1241,13 @@ #endif
if (likely(p->pid)) {
add_parent(p);
- tracehook_init_task(p);
-
+#ifdef CONFIG_UTRACE
+ p->utrace.flags = 0;
+ p->utrace.u.live.cloning = NULL;
+ p->utrace.u.live.signal = NULL;
+ INIT_LIST_HEAD(&p->utrace.engines);
+ spin_lock_init(&p->utrace.lock);
+#endif
if (thread_group_leader(p)) {
p->signal->tty = current->signal->tty;
p->signal->pgrp = process_group(current);
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -10,13 +10,13 @@
* Red Hat Author: Roland McGrath.
*/
-#include <linux/utrace.h>
#include <linux/tracehook.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/utrace.h>
#include <asm/tracehook.h>
@@ -29,48 +29,11 @@ #define CHECK_INIT(p) do { } while (0)
#define CHECK_DEAD(p) do { } while (0)
#endif
-/*
- * Per-thread structure task_struct.utrace points to.
- *
- * The task itself never has to worry about this going away after
- * some event is found set in task_struct.utrace_flags.
- * Once created, this pointer is changed only when the task is quiescent
- * (TASK_TRACED or TASK_STOPPED with the siglock held, or dead).
- *
- * For other parties, the pointer to this is protected by RCU and
- * task_lock. Since call_rcu is never used while the thread is alive and
- * using this struct utrace, we can overlay the RCU data structure used
- * only for a dead struct with some local state used only for a live utrace
- * on an active thread.
- */
-struct utrace
-{
- union {
- struct rcu_head dead;
- struct {
- struct task_struct *cloning;
- struct utrace_signal *signal;
- } live;
- struct {
- unsigned long flags;
- } exit;
- } u;
-
- struct list_head engines;
- spinlock_t lock;
- atomic_t check_dead;
-};
-
-static struct kmem_cache *utrace_cachep;
static struct kmem_cache *utrace_engine_cachep;
static int __init
utrace_init(void)
{
- utrace_cachep =
- kmem_cache_create("utrace_cache",
- sizeof(struct utrace), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
utrace_engine_cachep =
kmem_cache_create("utrace_engine_cache",
sizeof(struct utrace_attached_engine), 0,
@@ -79,106 +42,6 @@ utrace_init(void)
}
subsys_initcall(utrace_init);
-
-/*
- * Make sure target->utrace is allocated, and return with it locked on
- * success. This function mediates startup races. The creating parent
- * task has priority, and other callers will delay here to let its call
- * succeed and take the new utrace lock first.
- */
-static struct utrace *
-utrace_first_engine(struct task_struct *target,
- struct utrace_attached_engine *engine)
- __acquires(utrace->lock)
-{
- struct utrace *utrace, *ret;
-
- /*
- * If this is a newborn thread and we are not the creator,
- * we have to wait for it. The creator gets the first chance
- * to attach. The PF_STARTING flag is cleared after its
- * report_clone hook has had a chance to run.
- */
- if ((target->flags & PF_STARTING)
- && (current->utrace == NULL
- || current->utrace->u.live.cloning != target)) {
- yield();
- return (signal_pending(current)
- ? ERR_PTR(-ERESTARTNOINTR) : NULL);
- }
-
- utrace = kmem_cache_alloc(utrace_cachep, GFP_KERNEL);
- if (unlikely(utrace == NULL))
- return ERR_PTR(-ENOMEM);
-
- utrace->u.live.cloning = NULL;
- utrace->u.live.signal = NULL;
- INIT_LIST_HEAD(&utrace->engines);
- list_add(&engine->entry, &utrace->engines);
- spin_lock_init(&utrace->lock);
- CHECK_INIT(utrace);
-
- ret = utrace;
- spin_lock(&utrace->lock);
- task_lock(target);
- if (likely(target->utrace == NULL)) {
- rcu_assign_pointer(target->utrace, utrace);
- /*
- * The task_lock protects us against another thread doing
- * the same thing. We might still be racing against
- * tracehook_release_task. It's called with ->exit_state
- * set to EXIT_DEAD and then checks ->utrace with an
- * smp_mb() in between. If EXIT_DEAD is set, then
- * release_task might have checked ->utrace already and saw
- * it NULL; we can't attach. If we see EXIT_DEAD not yet
- * set after our barrier, then we know release_task will
- * see our target->utrace pointer.
- */
- smp_mb();
- if (target->exit_state == EXIT_DEAD) {
- /*
- * The target has already been through release_task.
- */
- target->utrace = NULL;
- goto cannot_attach;
- }
- task_unlock(target);
- }
- else {
- /*
- * Another engine attached first, so there is a struct already.
- * A null return says to restart looking for the existing one.
- */
- cannot_attach:
- ret = NULL;
- task_unlock(target);
- spin_unlock(&utrace->lock);
- kmem_cache_free(utrace_cachep, utrace);
- }
-
- return ret;
-}
-
-static void
-utrace_free(struct rcu_head *rhead)
-{
- struct utrace *utrace = container_of(rhead, struct utrace, u.dead);
- kmem_cache_free(utrace_cachep, utrace);
-}
-
-/*
- * Called with utrace locked. Clean it up and free it via RCU.
- */
-static void
-rcu_utrace_free(struct utrace *utrace)
- __releases(utrace->lock)
-{
- CHECK_DEAD(utrace);
- spin_unlock(&utrace->lock);
- INIT_RCU_HEAD(&utrace->u.dead);
- call_rcu(&utrace->u.dead, utrace_free);
-}
-
static void
utrace_engine_free(struct rcu_head *rhead)
{
@@ -200,16 +63,10 @@ rcu_engine_free(struct utrace_attached_e
* forced signal (or it's quiescent in utrace_get_signal).
*/
static inline void
-utrace_clear_tsk(struct task_struct *tsk, struct utrace *utrace)
+utrace_clear_tsk(struct utrace *utrace)
{
- if (utrace->u.live.signal == NULL) {
- task_lock(tsk);
- if (likely(tsk->utrace != NULL)) {
- rcu_assign_pointer(tsk->utrace, NULL);
- tsk->utrace_flags &= UTRACE_ACTION_NOREAP;
- }
- task_unlock(tsk);
- }
+ if (utrace->u.live.signal == NULL)
+ utrace->flags &= UTRACE_ACTION_NOREAP;
}
/*
@@ -218,12 +75,11 @@ utrace_clear_tsk(struct task_struct *tsk
* pending, utrace is left locked and not freed, but is removed from the task.
*/
static void
-remove_engine(struct utrace_attached_engine *engine,
- struct task_struct *tsk, struct utrace *utrace)
+remove_engine(struct utrace_attached_engine *engine, struct utrace *utrace)
{
list_del_rcu(&engine->entry);
if (list_empty(&utrace->engines))
- utrace_clear_tsk(tsk, utrace);
+ utrace_clear_tsk(utrace);
rcu_engine_free(engine);
}
@@ -254,7 +110,7 @@ check_dead_utrace(struct task_struct *ts
* If tracing was preventing a SIGCHLD or self-reaping
* and is no longer, we'll do that report or reaping now.
*/
- if (((tsk->utrace_flags &~ flags) & UTRACE_ACTION_NOREAP)
+ if (((tsk->utrace.flags &~ flags) & UTRACE_ACTION_NOREAP)
&& tsk->exit_state) {
/*
* While holding the utrace lock, mark that it's been done.
@@ -304,11 +160,8 @@ check_dead_utrace(struct task_struct *ts
}
- tsk->utrace_flags = flags;
- if (flags)
- spin_unlock(&utrace->lock);
- else
- rcu_utrace_free(utrace);
+ tsk->utrace.flags = flags;
+ spin_unlock(&utrace->lock);
/*
* Now we're finished updating the utrace state.
@@ -348,7 +201,7 @@ quiesce(struct task_struct *target, int
{
int quiescent;
- target->utrace_flags |= UTRACE_ACTION_QUIESCE;
+ target->utrace.flags |= UTRACE_ACTION_QUIESCE;
read_barrier_depends();
quiescent = (target->exit_state
@@ -399,93 +252,63 @@ struct utrace_attached_engine *
utrace_attach(struct task_struct *target, int flags,
const struct utrace_engine_ops *ops, void *data)
{
- struct utrace *utrace;
+ struct utrace *utrace = &target->utrace;
struct utrace_attached_engine *engine;
+ int wait_for_creator = 0;
-restart:
- rcu_read_lock();
- utrace = rcu_dereference(target->utrace);
- smp_rmb();
- if (unlikely(target->exit_state == EXIT_DEAD)) {
+ if (unlikely(target->exit_state == EXIT_DEAD))
/*
* The target has already been reaped.
* Check this first; a race with reaping may lead to restart.
*/
- rcu_read_unlock();
return ERR_PTR(-ESRCH);
- }
- if (utrace == NULL) {
- rcu_read_unlock();
-
- if (!(flags & UTRACE_ATTACH_CREATE))
- return ERR_PTR(-ENOENT);
+ /*
+ * If this is a newborn thread and we are not the creator,
+ * we have to wait for it. The creator gets the first chance
+ * to attach. The PF_STARTING flag is cleared after its
+ * report_clone hook has had a chance to run.
+ */
+ if (target->flags & PF_STARTING) {
+ struct utrace *u = ¤t->utrace;
- engine = kmem_cache_alloc(utrace_engine_cachep, GFP_KERNEL);
- if (unlikely(engine == NULL))
- return ERR_PTR(-ENOMEM);
- engine->flags = 0;
- CHECK_INIT(engine);
+ spin_lock(&u->lock);
+ wait_for_creator = (u->u.live.cloning != target);
+ spin_unlock(&u->lock);
+ }
+ if (wait_for_creator) {
+ yield();
+ return (signal_pending(current)
+ ? ERR_PTR(-ERESTARTNOINTR) : NULL);
+ }
- first:
- utrace = utrace_first_engine(target, engine);
- if (IS_ERR(utrace) || unlikely(utrace == NULL)) {
- kmem_cache_free(utrace_engine_cachep, engine);
- if (unlikely(utrace == NULL)) /* Race condition. */
- goto restart;
- return ERR_PTR(PTR_ERR(utrace));
- }
+ spin_lock(&utrace->lock);
+ if (!(flags & UTRACE_ATTACH_CREATE)) {
+ engine = matching_engine(utrace, flags, ops, data);
+ spin_unlock(&utrace->lock);
+ return engine;
}
- else {
- if (!(flags & UTRACE_ATTACH_CREATE)) {
- engine = matching_engine(utrace, flags, ops, data);
- rcu_read_unlock();
- return engine;
- }
- rcu_read_unlock();
-
- engine = kmem_cache_alloc(utrace_engine_cachep, GFP_KERNEL);
- if (unlikely(engine == NULL))
- return ERR_PTR(-ENOMEM);
- engine->flags = 0;
- CHECK_INIT(engine);
-
- rcu_read_lock();
- utrace = rcu_dereference(target->utrace);
- if (unlikely(utrace == NULL)) { /* Race with detach. */
- rcu_read_unlock();
- goto first;
- }
- spin_lock(&utrace->lock);
+ spin_unlock(&utrace->lock);
- if (flags & UTRACE_ATTACH_EXCLUSIVE) {
- struct utrace_attached_engine *old;
- old = matching_engine(utrace, flags, ops, data);
- if (!IS_ERR(old)) {
- spin_unlock(&utrace->lock);
- rcu_read_unlock();
- kmem_cache_free(utrace_engine_cachep, engine);
- return ERR_PTR(-EEXIST);
- }
- }
+ engine = kmem_cache_alloc(utrace_engine_cachep, GFP_KERNEL);
+ if (unlikely(engine == NULL))
+ return ERR_PTR(-ENOMEM);
+ engine->flags = 0;
+ engine->ops = ops;
+ engine->data = data;
+ CHECK_INIT(engine);
- if (unlikely(rcu_dereference(target->utrace) != utrace)) {
- /*
- * We lost a race with other CPUs doing a sequence
- * of detach and attach before we got in.
- */
+ spin_lock(&utrace->lock);
+ if (flags & UTRACE_ATTACH_EXCLUSIVE) {
+ struct utrace_attached_engine *old;
+
+ old = matching_engine(utrace, flags, ops, data);
+ if (!IS_ERR(old)) {
spin_unlock(&utrace->lock);
- rcu_read_unlock();
kmem_cache_free(utrace_engine_cachep, engine);
- goto restart;
+ return ERR_PTR(-EEXIST);
}
- rcu_read_unlock();
-
- list_add_tail_rcu(&engine->entry, &utrace->engines);
}
-
- engine->ops = ops;
- engine->data = data;
-
+ list_add_tail_rcu(&engine->entry, &utrace->engines);
spin_unlock(&utrace->lock);
return engine;
@@ -627,13 +450,10 @@ get_utrace_lock_attached(struct task_str
struct utrace_attached_engine *engine)
__acquires(utrace->lock)
{
- struct utrace *utrace;
+ struct utrace *utrace = &target->utrace;
+ int dead_engine;
- rcu_read_lock();
- utrace = rcu_dereference(target->utrace);
- smp_rmb();
- if (unlikely(utrace == NULL)
- || unlikely(target->exit_state == EXIT_DEAD))
+ if (unlikely(target->exit_state == EXIT_DEAD))
/*
* If all engines detached already, utrace is clear.
* Otherwise, we're called after utrace_release_task might
@@ -641,22 +461,18 @@ get_utrace_lock_attached(struct task_str
* callback might already be in progress or engine might
* even have been freed already.
*/
- utrace = ERR_PTR(-ESRCH);
- else {
- spin_lock(&utrace->lock);
- if (unlikely(rcu_dereference(target->utrace) != utrace)
- || unlikely(rcu_dereference(engine->ops)
- == &dead_engine_ops)) {
- /*
- * By the time we got the utrace lock,
- * it had been reaped or detached already.
- */
- spin_unlock(&utrace->lock);
- utrace = ERR_PTR(-ESRCH);
- }
- }
+ return ERR_PTR(-ESRCH);
+ rcu_read_lock();
+ dead_engine = rcu_dereference(engine->ops) == &dead_engine_ops;
rcu_read_unlock();
+ if (unlikely(dead_engine))
+ /*
+ * By the time we got the utrace lock,
+ * it had been reaped or detached already.
+ */
+ return ERR_PTR(-ESRCH);
+ spin_lock(&utrace->lock);
return utrace;
}
@@ -691,7 +507,7 @@ utrace_detach(struct task_struct *target
rcu_assign_pointer(engine->ops, &dead_engine_ops);
if (quiesce(target, 1)) {
- remove_engine(engine, target, utrace);
+ remove_engine(engine, utrace);
wake_quiescent(flags, utrace, target);
}
else
@@ -733,8 +549,7 @@ restart:
}
rcu_engine_free(engine);
}
-
- rcu_utrace_free(utrace);
+ spin_unlock(&utrace->lock);
}
/*
@@ -743,20 +558,12 @@ restart:
void
utrace_release_task(struct task_struct *target)
{
- struct utrace *utrace;
-
- task_lock(target);
- utrace = target->utrace;
- rcu_assign_pointer(target->utrace, NULL);
- task_unlock(target);
-
- if (unlikely(utrace == NULL))
- return;
+ struct utrace *utrace = &target->utrace;
spin_lock(&utrace->lock);
utrace->u.exit.flags |= EXIT_FLAG_REAP;
- if (target->utrace_flags & (UTRACE_EVENT(DEATH)
+ if (target->utrace.flags & (UTRACE_EVENT(DEATH)
| UTRACE_EVENT(QUIESCE)))
/*
* The target will do some final callbacks but hasn't
@@ -797,7 +604,7 @@ #endif
restart: /* See below. */
- old_utrace_flags = target->utrace_flags;
+ old_utrace_flags = target->utrace.flags;
old_flags = engine->flags;
if (target->exit_state
@@ -833,12 +640,12 @@ restart: /* See below. */
spin_unlock(&utrace->lock);
return ret;
}
- target->utrace_flags |= flags;
+ target->utrace.flags |= flags;
read_unlock(&tasklist_lock);
}
engine->flags = flags;
- target->utrace_flags |= flags;
+ target->utrace.flags |= flags;
ret = 0;
report = 0;
@@ -861,7 +668,7 @@ restart: /* See below. */
* in user mode to get those effects, even if the target is
* not going to be quiescent right now.
*/
- if (!(target->utrace_flags & UTRACE_ACTION_QUIESCE)
+ if (!(target->utrace.flags & UTRACE_ACTION_QUIESCE)
&& !target->exit_state
&& ((flags &~ old_utrace_flags)
& (UTRACE_ACTION_SINGLESTEP | UTRACE_ACTION_BLOCKSTEP
@@ -934,10 +741,10 @@ #endif
*/
if (((ret ^ engine->flags) & (UTRACE_ACTION_STATE_MASK
& ~UTRACE_ACTION_QUIESCE)))
- tsk->utrace_flags |= UTRACE_ACTION_QUIESCE;
+ tsk->utrace.flags |= UTRACE_ACTION_QUIESCE;
engine->flags &= ~UTRACE_ACTION_STATE_MASK;
engine->flags |= ret & UTRACE_ACTION_STATE_MASK;
- tsk->utrace_flags |= engine->flags;
+ tsk->utrace.flags |= engine->flags;
spin_unlock(&utrace->lock);
}
else
@@ -965,7 +772,7 @@ remove_detached(struct task_struct *tsk,
list_for_each_entry_safe(engine, next, &utrace->engines, entry) {
if (engine->ops == &dead_engine_ops)
- remove_engine(engine, tsk, utrace);
+ remove_engine(engine, utrace);
else
flags |= engine->flags | UTRACE_EVENT(REAP);
}
@@ -987,8 +794,8 @@ check_detach(struct task_struct *tsk, u3
* getting into utrace_report_death.
*/
BUG_ON(tsk != current);
- spin_lock(&tsk->utrace->lock);
- action = remove_detached(tsk, tsk->utrace, action, ~0UL);
+ spin_lock(&tsk->utrace.lock);
+ action = remove_detached(tsk, &tsk->utrace, action, ~0UL);
}
return action;
}
@@ -1011,7 +818,7 @@ void
utrace_report_clone(unsigned long clone_flags, struct task_struct *child)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action;
@@ -1056,7 +863,7 @@ int
utrace_report_jctl(int what)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action;
@@ -1122,7 +929,7 @@ sigkill_pending(struct task_struct *tsk)
int
utrace_quiescent(struct task_struct *tsk, struct utrace_signal *signal)
{
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
unsigned long action;
restart:
@@ -1151,7 +958,7 @@ restart:
* Never stop when there is a SIGKILL bringing us down.
*/
killed = sigkill_pending(tsk);
- if (!killed && (tsk->utrace_flags & UTRACE_ACTION_QUIESCE)) {
+ if (!killed && (tsk->utrace.flags & UTRACE_ACTION_QUIESCE)) {
set_current_state(TASK_TRACED);
/*
* If there is a group stop in progress,
@@ -1171,7 +978,7 @@ restart:
* u.live.signal is set, see check_dead_utrace.
* This makes it safe to clear its pointer here.
*/
- BUG_ON(tsk->utrace != utrace);
+ BUG_ON(&tsk->utrace != utrace);
BUG_ON(utrace->u.live.signal != signal);
utrace->u.live.signal = NULL;
}
@@ -1186,12 +993,12 @@ restart:
* longer quiescent, so don't need to do any RCU locking.
* But we do need to check our utrace pointer anew.
*/
- utrace = tsk->utrace;
- if (tsk->utrace_flags
+ utrace = &tsk->utrace;
+ if (tsk->utrace.flags
& (UTRACE_EVENT(QUIESCE) | UTRACE_ACTION_STATE_MASK))
goto restart;
}
- else if (tsk->utrace_flags & UTRACE_ACTION_QUIESCE) {
+ else if (tsk->utrace.flags & UTRACE_ACTION_QUIESCE) {
/*
* Our flags are out of date.
* Update the set of events of interest from the union
@@ -1202,11 +1009,12 @@ restart:
* still needs to process a pending forced signal.
*/
unsigned long flags;
- utrace = rcu_dereference(tsk->utrace);
+
+ utrace = &tsk->utrace;
spin_lock(&utrace->lock);
flags = rescan_flags(utrace);
if (flags == 0)
- utrace_clear_tsk(tsk, utrace);
+ utrace_clear_tsk(utrace);
check_dead_utrace(tsk, utrace, flags);
}
@@ -1226,7 +1034,7 @@ #ifdef ARCH_HAS_BLOCK_STEP
else
tracehook_disable_block_step(tsk);
#endif
- if (tsk->utrace_flags & UTRACE_EVENT_SYSCALL)
+ if (tsk->utrace.flags & UTRACE_EVENT_SYSCALL)
tracehook_enable_syscall_trace(tsk);
else
tracehook_disable_syscall_trace(tsk);
@@ -1242,7 +1050,7 @@ void
utrace_report_exit(long *exit_code)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action;
@@ -1386,17 +1194,9 @@ utrace_report_death(struct task_struct *
void
utrace_report_delayed_group_leader(struct task_struct *tsk)
{
- struct utrace *utrace;
+ struct utrace *utrace = &tsk->utrace;
- rcu_read_lock();
- utrace = rcu_dereference(tsk->utrace);
- if (unlikely(utrace == NULL)) {
- rcu_read_unlock();
- return;
- }
spin_lock(&utrace->lock);
- rcu_read_unlock();
-
utrace->u.exit.flags |= EXIT_FLAG_DELAYED_GROUP_LEADER;
/*
@@ -1404,7 +1204,7 @@ utrace_report_delayed_group_leader(struc
* started already, there is nothing more to do now.
*/
if ((utrace->u.exit.flags & (EXIT_FLAG_DEATH | EXIT_FLAG_REAP))
- || !likely(tsk->utrace_flags & UTRACE_ACTION_NOREAP))
+ || !likely(tsk->utrace.flags & UTRACE_ACTION_NOREAP))
spin_unlock(&utrace->lock);
else
report_delayed_group_leader(tsk, utrace);
@@ -1417,7 +1217,7 @@ void
utrace_report_vfork_done(pid_t child_pid)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action;
@@ -1442,7 +1242,7 @@ void
utrace_report_exec(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action;
@@ -1467,7 +1267,7 @@ void
utrace_report_syscall(struct pt_regs *regs, int is_exit)
{
struct task_struct *tsk = current;
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
unsigned long action, ev;
@@ -1586,7 +1386,7 @@ void
utrace_signal_handler_singlestep(struct task_struct *tsk, struct pt_regs *regs)
{
u32 action;
- action = report_signal(tsk, regs, tsk->utrace, UTRACE_SIGNAL_HANDLER,
+ action = report_signal(tsk, regs, &tsk->utrace, UTRACE_SIGNAL_HANDLER,
UTRACE_EVENT_SIGNAL_ALL,
UTRACE_ACTION_SINGLESTEP|UTRACE_ACTION_BLOCKSTEP,
NULL, NULL, NULL);
@@ -1605,7 +1405,7 @@ utrace_get_signal(struct task_struct *ts
__releases(tsk->sighand->siglock)
__acquires(tsk->sighand->siglock)
{
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct utrace_signal signal = { info, return_ka, 0 };
struct k_sigaction *ka;
unsigned long action, event;
@@ -1634,7 +1434,7 @@ utrace_get_signal(struct task_struct *ts
* First stash a pointer to the state on our stack,
* so that utrace_inject_signal can tell us what to do.
*/
- if (tsk->utrace_flags & UTRACE_ACTION_QUIESCE) {
+ if (tsk->utrace.flags & UTRACE_ACTION_QUIESCE) {
int killed = sigkill_pending(tsk);
if (!killed) {
spin_unlock_irq(&tsk->sighand->siglock);
@@ -1698,7 +1498,7 @@ utrace_get_signal(struct task_struct *ts
* If noone is interested in intercepting signals, let the caller
* just dequeue them normally.
*/
- if ((tsk->utrace_flags & UTRACE_EVENT_SIGNAL_ALL) == 0)
+ if ((tsk->utrace.flags & UTRACE_EVENT_SIGNAL_ALL) == 0)
return 0;
/*
@@ -1749,7 +1549,7 @@ utrace_get_signal(struct task_struct *ts
action = UTRACE_SIGNAL_TERM;
}
- if (tsk->utrace_flags & event) {
+ if (tsk->utrace.flags & event) {
/*
* We have some interested engines, so tell them about the
* signal and let them change its disposition.
@@ -1964,23 +1764,19 @@ EXPORT_SYMBOL_GPL(utrace_regset);
struct task_struct *
utrace_tracer_task(struct task_struct *target)
{
- struct utrace *utrace;
+ struct utrace *utrace = &target->utrace;
struct task_struct *tracer = NULL;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ const struct utrace_engine_ops *ops;
- utrace = rcu_dereference(target->utrace);
- if (utrace != NULL) {
- struct list_head *pos, *next;
- struct utrace_attached_engine *engine;
- const struct utrace_engine_ops *ops;
- list_for_each_safe_rcu(pos, next, &utrace->engines) {
- engine = list_entry(pos, struct utrace_attached_engine,
- entry);
- ops = rcu_dereference(engine->ops);
- if (ops->tracer_task) {
- tracer = (*ops->tracer_task)(engine, target);
- if (tracer != NULL)
- break;
- }
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ ops = rcu_dereference(engine->ops);
+ if (ops->tracer_task) {
+ tracer = (*ops->tracer_task)(engine, target);
+ if (tracer != NULL)
+ break;
}
}
@@ -1990,26 +1786,21 @@ utrace_tracer_task(struct task_struct *t
int
utrace_allow_access_process_vm(struct task_struct *target)
{
- struct utrace *utrace;
+ struct utrace *utrace = &target->utrace;
+ struct list_head *pos, *next;
+ struct utrace_attached_engine *engine;
+ const struct utrace_engine_ops *ops;
int ret = 0;
rcu_read_lock();
- utrace = rcu_dereference(target->utrace);
- if (utrace != NULL) {
- struct list_head *pos, *next;
- struct utrace_attached_engine *engine;
- const struct utrace_engine_ops *ops;
- list_for_each_safe_rcu(pos, next, &utrace->engines) {
- engine = list_entry(pos, struct utrace_attached_engine,
- entry);
- ops = rcu_dereference(engine->ops);
- if (ops->allow_access_process_vm) {
- ret = (*ops->allow_access_process_vm)(engine,
- target,
- current);
- if (ret)
- break;
- }
+ list_for_each_safe_rcu(pos, next, &utrace->engines) {
+ engine = list_entry(pos, struct utrace_attached_engine, entry);
+ ops = rcu_dereference(engine->ops);
+ if (ops->allow_access_process_vm) {
+ ret = (*ops->allow_access_process_vm)(engine, target,
+ current);
+ if (ret)
+ break;
}
}
rcu_read_unlock();
@@ -2024,7 +1815,7 @@ utrace_allow_access_process_vm(struct ta
int
utrace_unsafe_exec(struct task_struct *tsk)
{
- struct utrace *utrace = tsk->utrace;
+ struct utrace *utrace = &tsk->utrace;
struct list_head *pos, *next;
struct utrace_attached_engine *engine;
const struct utrace_engine_ops *ops;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]