> Just in case fyi, I cooked the almost identical patch yesterday, but
> it didn't help: make xcheck crashes. Not that I really expected it can
> help on x86.

Right.

> Not sure I understand exactly... Yes, sometimes we know we don't need
> read_barrier_depends(). For example,
> 
>       @@ -302,7 +309,7 @@ struct utrace_engine *utrace_attach_task(
>               if (!utrace) {
>                       if (unlikely(!utrace_task_alloc(target)))
>                               return ERR_PTR(-ENOMEM);
>       -               utrace = target->utrace;
>       +               utrace = task_utrace_struct(target);
>               }
> 
> I think this change is not necessary (but imho good anyway), this path
> must take task_lock() and thus it must always see the correct *utrace.

Ok.  If we were to change it we should add a comment about that.

> But read_barrier_depends() does nothig except on alpha, why should we
> care?

I thought this was the barrier you were talking about.  Anyway, we should
be pedantically correct about these.  (People do even still build Linux/Alpha.)

> This reminds me, utrace_interrupt_pending() and similar code needs rmb()
> or task->utrace != NULL check.

I take it you mean that:

        if (task->utrace_flags)
                ... use task->utrace ...

paths need a barrier to ensure task->utrace_flags read nonzero implies
task->utrace will be read non-null when racing with the first attach.
Right?

This only needs smp_rmb(), right?  Is there an existing explicit barrier
this pairs with?  The corresponding smp_wmb() needs to be somewhere between
utrace_task_alloc()'s "task->utrace = utrace;" and utrace_add_engine():

        if (!target->utrace_flags) {
                target->utrace_flags = UTRACE_EVENT(REAP);

Right?  A lot goes on in between there that probably has some implied
barriers (task_unlock, kmem_cache_alloc, spin_lock).  But do we really 
have the exact barrier we need?  Or maybe we need:

                /*
                 * This barrier makes sure the initialization of the struct
                 * precedes the installation of the pointer.  This pairs
                 * with read_barrier_depends() in task_utrace_struct().
                 */
                wmb();
                task->utrace = utrace;
                /*
                 * This barrier pairs with task_utrace_struct()'s smp_rmb().
                 * It ensures this task->utrace store is always seen before
                 * task->utrace_flags is first set in utrace_add_engine().
                 */
                smp_wmb();

This is basically all the tracehook.h paths calling into utrace.
I think the easiest thing to do is to assume that use pattern and
also roll this barrier into task_utrace_struct().

diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index 8924783..0000000 100644  
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -157,7 +157,18 @@ static inline unsigned long task_utrace_
 
 static inline struct utrace *task_utrace_struct(struct task_struct *task)
 {
-       struct utrace *utrace = task->utrace;
+       struct utrace *utrace;
+
+       /*
+        * This barrier ensures that any prior load of task->utrace_flags
+        * is ordered before this load of task->utrace.  We use those
+        * utrace_flags checks in the hot path to decide to call into
+        * the utrace code.  The first attach installs task->utrace before
+        * setting task->utrace_flags nonzero, with a barrier between.
+        */
+       smp_rmb();
+       utrace = task->utrace;
+
        read_barrier_depends(); /* See utrace_task_alloc().  */
        return utrace;
 }


Is that what you have in mind?


Thanks,
Roland

Reply via email to