While this is not documented explicitely (afaik), the kernel depends
on store-mb-load behavior. Just for example,

        CPU_0:

                current->state = TASK_INTERRUPTIBLE;
                mb();
                if (!COND)
                        schedule();
vs
        CPU_1:
                COND = 1;
                wake_up_process(task);
                
                        mb();   // <---- implicit
                        if (task->state == TASK_RUNNING)
                                return;
                        ...
                        task->state = TASK_RUNNING;

should always work. Either CPU_0 must see the condition, or wakeup
must see task->state.

IOW, if we have A == B == 0, then

        CPU_0                           CPU_1

        A = 1;                          B = 1
        mb();                           mb();
        if (B)                          if (A)
                printk("OK");                   printk("OK");

"OK" should be printed at least once when both CPUs pass this code.

This means we can avoid utrace_release_task() when there are no attached
engines. With this patch we have:

        attach:

                task->utrace_flags = REAP;
                mb();
                if (task->exit_state == EXIT_DEAD)
                        abort;

                add the new engine

        release:

                task->exit_state = exit_DEAD;
                mb();
                if (task->utrace_flags)
                        utrace_release_task();

Signed-off-by: Oleg Nesterov <o...@redhat.com>
---

 include/linux/tracehook.h |    5 ++++-
 kernel/utrace.c           |   26 ++++++++++++++++++++------
 2 files changed, 24 insertions(+), 7 deletions(-)

--- __UTRACE/include/linux/tracehook.h~2_DONT_REAP_UNCONDITIONALLY      
2009-08-04 19:48:28.000000000 +0200
+++ __UTRACE/include/linux/tracehook.h  2009-09-08 20:58:53.000000000 +0200
@@ -362,7 +362,10 @@ static inline void tracehook_report_vfor
  */
 static inline void tracehook_prepare_release_task(struct task_struct *task)
 {
-       utrace_release_task(task);
+       /* see utrace_add_engine() about this barrier */
+       smp_mb();
+       if (task_utrace_flags(task))
+               utrace_release_task(task);
 }
 
 /**
--- __UTRACE/kernel/utrace.c~2_DONT_REAP_UNCONDITIONALLY        2009-09-08 
20:48:56.000000000 +0200
+++ __UTRACE/kernel/utrace.c    2009-09-08 21:10:23.000000000 +0200
@@ -139,7 +139,7 @@ static int utrace_add_engine(struct task
                             const struct utrace_engine_ops *ops,
                             void *data)
 {
-       int ret;
+       int ret = 0;
 
        spin_lock(&utrace->lock);
 
@@ -149,7 +149,7 @@ static int utrace_add_engine(struct task
                 */
                ret = -ESRCH;
        } else if ((flags & UTRACE_ATTACH_EXCLUSIVE) &&
-           unlikely(matching_engine(utrace, flags, ops, data))) {
+                  unlikely(matching_engine(utrace, flags, ops, data))) {
                ret = -EEXIST;
        } else {
                /*
@@ -168,10 +168,24 @@ static int utrace_add_engine(struct task
                 * In case we had no engines before, make sure that
                 * utrace_flags is not zero.
                 */
-               target->utrace_flags |= UTRACE_EVENT(REAP);
-               list_add_tail(&engine->entry, &utrace->attaching);
-               utrace->pending_attach = 1;
-               ret = 0;
+               if (!target->utrace_flags) {
+                       target->utrace_flags = UTRACE_EVENT(REAP);
+                       /*
+                        * If we race with tracehook_prepare_release_task()
+                        * make sure that either it sees utrace_flags != 0
+                        * or we see exit_state == EXIT_DEAD.
+                        */
+                       smp_mb();
+                       if (unlikely(target->exit_state == EXIT_DEAD)) {
+                               target->utrace_flags = 0;
+                               ret = -ESRCH;
+                       }
+               }
+
+               if (!ret) {
+                       list_add_tail(&engine->entry, &utrace->attaching);
+                       utrace->pending_attach = 1;
+               }
        }
 
        spin_unlock(&utrace->lock);

Reply via email to