]> sourceware.org Git - systemtap.git/commitdiff
Use new task_work kernel feature to truly stop tasks.
authorDavid Smith <dsmith@redhat.com>
Fri, 27 Apr 2012 20:43:16 +0000 (15:43 -0500)
committerDavid Smith <dsmith@redhat.com>
Fri, 27 Apr 2012 20:43:16 +0000 (15:43 -0500)
* buildrun.cxx (compile_pass): Add STAPCONF_TASK_WORK_ADD_EXPORTED autoconf.
* runtime/autoconf-utrace-via-ftrace.c (__autoconf_func): Makes sure
  <linux/task_work.h> is present and works.
* runtime/autoconf-utrace-via-tracepoints.c (__autoconf_func): Ditto.
* runtime/runtime.h: Added export kludge variables for task_work_add() and
  task_work_cancel().
* runtime/stp_utrace.c (utrace_init): If the task_work_* functions aren't
  exported, use kallsyms_lookup_name() to find them.
  (utrace_cleanup): Cancel work function if necessary.
  (utrace_task_alloc): Initialize task work structure.
  (utrace_free): Cancel work function if necessary.
  (utrace_do_stop): Use task_work_add() functionality to replace
  set_notify_resume().
  (utrace_stop): Ditto.
  (utrace_control): Ditto.
  (finish_report): Ditto.
  (utrace_resume): Updated to work as task worker function.
* runtime/task_finder2.c (__stp_task_worker): New function.
  (__stp_utrace_task_finder_target_quiesce): If we can't sleep, use
  task_work_add() to truly stop the task.
* runtime/uprobes-inode.c (stapiu_change_plus): Check build-ids.

buildrun.cxx
runtime/autoconf-utrace-via-ftrace.c
runtime/autoconf-utrace-via-tracepoints.c
runtime/runtime.h
runtime/stp_utrace.c
runtime/task_finder2.c
runtime/uprobes-inode.c

index a2f5ea4b058431461ba55f0feea833b4b0a4df82..2163a4408ee101e20e9c4730f5dcbfb4bd7e080e 100644 (file)
@@ -325,6 +325,9 @@ compile_pass (systemtap_session& s)
   // used by runtime/loc2c-runtime.h
   output_exportconf(s, o, "task_user_regset_view", "STAPCONF_TASK_USER_REGSET_VIEW_EXPORTED");
 
+  // used by runtime/stp_utrace.c
+  output_exportconf(s, o, "task_work_add", "STAPCONF_TASK_WORK_ADD_EXPORTED");
+
   o << module_cflags << " += -include $(STAPCONF_HEADER)" << endl;
 
   for (unsigned i=0; i<s.macros.size(); i++)
index 5089af81c499b2740d29aeb28a3e46736fe14636..8e1b4f9a34e916ebed4b08fb13db539c05c2d784 100644 (file)
@@ -1,10 +1,13 @@
 #include <trace/events/sched.h>
 #include <trace/events/syscalls.h>
 #include <linux/ftrace.h>
+#include <linux/task_work.h>
 
 // The utrace-less task_finder needs either:
 // - 5 specific tracepoints
 // - 4 specific tracepoints and ftrace_set_filter()
+// Both scenarios need <linux/task_work.h>.
+//
 // Check scenario #2.
 
 void __sched_process_fork(void *cb_data __attribute__((unused)),
@@ -39,6 +42,7 @@ struct ftrace_ops __ftrace_ops;
 void __autoconf_func(void)
 {
        char *report_exec_name;
+       struct task_work work;
 
        (void) register_trace_sched_process_fork(__sched_process_fork, NULL);
        (void) register_trace_sched_process_exit(__sched_process_exit, NULL);
@@ -49,4 +53,5 @@ void __autoconf_func(void)
        ftrace_set_filter(&__ftrace_ops, report_exec_name,
                          strlen(report_exec_name), 1);
        (void) register_ftrace_function(&__ftrace_ops);
+       init_task_work(&work, NULL, NULL);
 }
index 3e0dcf97b9b491c9dd689540095cd9f74415efe3..dc679134306708543a94f089d954040d903b3edb 100644 (file)
@@ -1,9 +1,12 @@
 #include <trace/events/sched.h>
 #include <trace/events/syscalls.h>
+#include <linux/task_work.h>
 
 // The utrace-less task_finder needs either:
 // - 5 specific tracepoints
 // - 4 specific tracepoints and ftrace_set_filter()
+// Both scenarios need <linux/task_work.h>.
+// 
 // Check scenario #1.
 
 void __sched_process_fork(void *cb_data __attribute__((unused)),
@@ -43,9 +46,12 @@ void __sys_exit(void *cb_data __attribute__ ((unused)),
 
 void __autoconf_func(void)
 {
+       struct task_work work;
+
        (void) register_trace_sched_process_fork(__sched_process_fork, NULL);
        (void) register_trace_sched_process_exit(__sched_process_exit, NULL);
        (void) register_trace_sched_process_exec(__sched_process_exec, NULL);
        (void) register_trace_sys_enter(__sys_enter, NULL);
        (void) register_trace_sys_exit(__sys_exit, NULL);
+       init_task_work(&work, NULL, NULL);
 }
index 2df27f3961968b8ec31be07afb17fa3762f3c832..e79829f2e44048f1861c25736f65b87b0f41dc3f 100644 (file)
@@ -144,6 +144,12 @@ void *kallsyms_uprobe_register;
 void *kallsyms_uprobe_unregister;
 #endif
 
+/* task_work functions lack the necessary SYMBOL_EXPORT's */
+#if !defined(STAPCONF_TASK_WORK_ADD_EXPORTED)
+void *kallsyms_task_work_add;
+void *kallsyms_task_work_cancel;
+#endif
+
 #include "access_process_vm.h"
 #include "loc2c-runtime.h"
 
index f495415d37f63ff2ddd66a25fe2a8e8868a078eb..0d4c9ee6a9065dd84c926fb1a620e0c1b7a2633f 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/spinlock.h>
 #include <trace/events/sched.h>
 #include <trace/events/syscalls.h>
+#include <linux/task_work.h>
 
 /*
  * Per-thread structure private to utrace implementation.
@@ -79,11 +80,14 @@ struct utrace {
        unsigned int death:1;   /* in utrace_report_death() now */
        unsigned int reap:1;    /* release_task() has run */
        unsigned int pending_attach:1; /* need splice_attaching() */
+       unsigned int task_work_added:1; /* called task_work_add() */
 
        unsigned long utrace_flags;
 
        struct hlist_node hlist;       /* task_utrace_table linkage */
        struct task_struct *task;
+
+       struct task_work work;
 };
 
 #define TASK_UTRACE_HASH_BITS 5
@@ -126,6 +130,15 @@ static struct ftrace_ops utrace_report_exec_ops __read_mostly =
 #define __UTRACE_REGISTERED    1
 static atomic_t utrace_state = ATOMIC_INIT(__UTRACE_UNREGISTERED);
 
+#if !defined(STAPCONF_TASK_WORK_ADD_EXPORTED)
+typedef int (*task_work_add_fn)(struct task_struct *task,
+                                 struct task_work *twork, bool notify);
+#define task_work_add (* (task_work_add_fn)kallsyms_task_work_add)
+typedef struct task_work *(*task_work_cancel_fn)(struct task_struct *,
+                                                task_work_func_t);
+#define task_work_cancel (* (task_work_cancel_fn)kallsyms_task_work_cancel)
+#endif
+
 int utrace_init(void)
 {
        int i;
@@ -134,6 +147,25 @@ int utrace_init(void)
        char *report_exec_name;
 #endif
 
+#if !defined(STAPCONF_TASK_WORK_ADD_EXPORTED)
+       /* The task_work_add()/task_work_cancel() functions aren't
+        * exported. Look up those function addresses. */
+        kallsyms_task_work_add = (void *)kallsyms_lookup_name ("task_work_add");
+        if (kallsyms_task_work_add == NULL) {
+               printk(KERN_ERR "%s can't resolve task_work_add!",
+                      THIS_MODULE->name);
+               rc = -ENOENT;
+               goto error;
+        }
+        kallsyms_task_work_cancel = (void *)kallsyms_lookup_name ("task_work_cancel");
+        if (kallsyms_task_work_cancel == NULL) {
+               printk(KERN_ERR "%s can't resolve task_work_cancel!",
+                      THIS_MODULE->name);
+               rc = -ENOENT;
+               goto error;
+        }
+#endif
+
        /* initialize the list heads */
        for (i = 0; i < TASK_UTRACE_TABLE_SIZE; i++) {
                INIT_HLIST_HEAD(&task_utrace_table[i]);
@@ -216,6 +248,8 @@ int utrace_exit(void)
        return 0;
 }
 
+void utrace_resume(struct task_work *work);
+
 /*
  * Clean up everything associated with @task.utrace.
  *
@@ -243,6 +277,16 @@ static void utrace_cleanup(struct utrace *utrace)
            list_del(&engine->entry);
            kmem_cache_free(utrace_engine_cachep, engine);
        }
+
+       if (utrace->task_work_added) {
+               if (task_work_cancel(utrace->task, &utrace_resume) == NULL)
+                       printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
+                              __FUNCTION__, __LINE__, utrace->task,
+                              utrace->task->tgid,
+                              (utrace->task->comm ? utrace->task->comm
+                               : "UNKNOWN"));
+               utrace->task_work_added = 0;
+       }
        spin_unlock(&utrace->lock);
 
        /* Free the struct utrace itself. */
@@ -345,6 +389,7 @@ static bool utrace_task_alloc(struct task_struct *task)
                kmem_cache_free(utrace_cachep, utrace);
        }
 
+       init_task_work(&utrace->work, &utrace_resume, NULL);
        return true;
 }
 
@@ -376,6 +421,17 @@ static void utrace_free(struct utrace *utrace)
                       list_empty(&utrace->attached),
                       list_empty(&utrace->attaching));
 #endif
+
+       if (utrace->task_work_added) {
+               if (task_work_cancel(utrace->task, &utrace_resume) == NULL)
+                       printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
+                              __FUNCTION__, __LINE__, utrace->task,
+                              utrace->task->tgid,
+                              (utrace->task->comm ? utrace->task->comm
+                               : "UNKNOWN"));
+               utrace->task_work_added = 0;
+       }
+
        kmem_cache_free(utrace_cachep, utrace);
 }
 
@@ -883,12 +939,18 @@ static bool utrace_do_stop(struct task_struct *target, struct utrace *utrace)
                if (likely(task_is_stopped(target)))
                        __set_task_state(target, TASK_TRACED);
                spin_unlock_irq(&target->sighand->siglock);
-#if 0
-       /* FIXME: needed?  If so, what to do here? */
        } else if (utrace->resume > UTRACE_REPORT) {
                utrace->resume = UTRACE_REPORT;
-               set_notify_resume(target);
-#endif
+               if (! utrace->task_work_added) {
+                       int rc = task_work_add(target, &utrace->work, true);
+                       if (rc != 0)
+                               printk(KERN_ERR
+                                      "%s:%d - task_work_add() returned %d\n",
+                                      __FUNCTION__, __LINE__, rc);
+                       else {
+                               utrace->task_work_added = 1;
+                       }
+               }
        }
 
        return task_is_traced(target);
@@ -1018,8 +1080,16 @@ relock:
                 * Ensure a reporting pass when we're resumed.
                 */
                utrace->resume = action;
-               /* FIXME: needed? */
-               set_thread_flag(TIF_NOTIFY_RESUME);
+               if (! utrace->task_work_added) {
+                       int rc = task_work_add(task, &utrace->work, true);
+                       if (rc != 0)
+                               printk(KERN_ERR
+                                      "%s:%d - task_work_add() returned %d\n",
+                                      __FUNCTION__, __LINE__, rc);
+                       else {
+                               utrace->task_work_added = 1;
+                       }
+               }
        }
 
        /*
@@ -1360,13 +1430,19 @@ int utrace_control(struct task_struct *target,
                 * In that case, utrace_get_signal() will be reporting soon.
                 */
                clear_engine_wants_stop(engine);
-#if 0
-               /* FIXME: needed?  If so, what to do here? */
                if (action < utrace->resume) {
                        utrace->resume = action;
-                       set_notify_resume(target);
+                       if (! utrace->task_work_added) {
+                               ret = task_work_add(target, &utrace->work, true);
+                               if (ret != 0)
+                                       printk(KERN_ERR
+                                              "%s:%d - task_work_add() returned %d\n",
+                                              __FUNCTION__, __LINE__, ret);
+                               else {
+                                       utrace->task_work_added = 1;
+                               }
+                       }
                }
-#endif
                break;
 
        default:
@@ -1513,8 +1589,16 @@ static void finish_report(struct task_struct *task, struct utrace *utrace,
        if (resume < utrace->resume) {
                spin_lock(&utrace->lock);
                utrace->resume = resume;
-               /* FIXME: Hmm, unsure about calling set_tsk_thread_flag()... */
-               set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
+               if (! utrace->task_work_added) {
+                       int rc = task_work_add(task, &utrace->work, true);
+                       if (rc != 0)
+                               printk(KERN_ERR
+                                      "%s:%d - task_work_add() returned %d\n",
+                                      __FUNCTION__, __LINE__, rc);
+                       else {
+                               utrace->task_work_added = 1;
+                       }
+               }
                spin_unlock(&utrace->lock);
        }
 
@@ -1883,7 +1967,6 @@ static void utrace_report_clone(void *cb_data __attribute__ ((unused)),
                unsigned long clone_flags = 0;
                INIT_REPORT(report);
 
-
                /* FIXME: Figure out what the clone_flags were. For
                 * task_finder's purposes, all we need is CLONE_THREAD. */
                if (task->mm == child->mm)
@@ -2047,12 +2130,22 @@ static void finish_resume_report(struct task_struct *task,
  * We are close to user mode, and this is the place to report or stop.
  * When we return, we're going to user mode or into the signals code.
  */
-void utrace_resume(struct task_struct *task, struct pt_regs *regs)
+void utrace_resume(struct task_work *work)
 {
-       struct utrace *utrace = task_utrace_struct(task);
+       /*
+        * We could also do 'task_utrace_struct()' here to find the
+        * task's 'struct utrace', but 'container_of()' should be
+        * instantaneous (where 'task_utrace_struct()' has to do a
+        * hash lookup).
+        */
+       struct utrace *utrace = container_of(work, struct utrace, work);
+       struct task_struct *task = current;
        INIT_REPORT(report);
        struct utrace_engine *engine;
 
+       might_sleep();
+       utrace->task_work_added = 0;
+
        /*
         * Some machines get here with interrupts disabled.  The same arch
         * code path leads to calling into get_signal_to_deliver(), which
index ff2ac76c7ffdd70c665cc770a55a6d473f06581a..8dea139d2f71606fa610dba8953c7057228c4ba8 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef STAPCONF_TASK_UID
 #include <linux/cred.h>
 #endif
+#include <linux/task_work.h>
 #include "syscall.h"
 #include "task_finder_map.c"
 
@@ -120,6 +121,38 @@ static void
 __stp_call_mmap_callbacks_for_task(struct stap_task_finder_target *tgt,
                                   struct task_struct *tsk);
 
+
+static inline void
+__stp_call_callbacks(struct stap_task_finder_target *tgt,
+                    struct task_struct *tsk, int register_p, int process_p);
+
+static void
+__stp_task_worker(struct task_work *work)
+{
+       struct stap_task_finder_target *tgt = work->data;
+
+       might_sleep();
+       _stp_kfree(work);
+       if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING)
+               return;
+
+       __stp_tf_handler_start();
+
+       /* Call the callbacks.  Assume that if the thread is a
+        * thread group leader, it is a process. */
+       __stp_call_callbacks(tgt, current, 1, (current->pid == current->tgid));
+       /* If this is just a thread other than the thread group
+        * leader, don't bother inform map callback clients about its
+        * memory map, since they will simply duplicate each other. */
+       if (tgt->mmap_events == 1 && current->tgid == current->pid) {
+           __stp_call_mmap_callbacks_for_task(tgt, current);
+       }
+
+       __stp_tf_handler_end();
+       return;
+}
+
 static int
 stap_register_task_finder_target(struct stap_task_finder_target *new_tgt)
 {
@@ -1166,16 +1199,38 @@ __stp_utrace_task_finder_target_quiesce(u32 action,
                _stp_error("utrace_set_events returned error %d on pid %d",
                           rc, (int)tsk->pid);
 
+       if (in_atomic() || irqs_disabled()) {
+               struct task_work *work;
 
-       /* Call the callbacks.  Assume that if the thread is a
-        * thread group leader, it is a process. */
-       __stp_call_callbacks(tgt, tsk, 1, (tsk->pid == tsk->tgid));
+               /* If we can't sleep, arrange for the task to truly
+                * stop so we can sleep. */
+               work = _stp_kmalloc(sizeof(*work));
+               if (work == NULL) {
+                       _stp_error("Unable to allocate space for task_work");
+                       return UTRACE_RESUME;
+               }
+               init_task_work(work, &__stp_task_worker, tgt);
+               /* FIXME: Hmm, let's say we exit between adding the
+                * task work and it firing.  How do we cancel? */
+               rc = task_work_add(tsk, work, true);
+               if (rc != 0) {
+                       printk(KERN_ERR
+                              "%s:%d - task_work_add() returned %d\n",
+                              __FUNCTION__, __LINE__, rc);
+               }
+       }
+       else {
+               /* Call the callbacks.  Assume that if the thread is a
+                * thread group leader, it is a process. */
+               __stp_call_callbacks(tgt, tsk, 1, (tsk->pid == tsk->tgid));
  
-       /* If this is just a thread other than the thread group leader,
-           don't bother inform map callback clients about its memory map,
-           since they will simply duplicate each other. */
-       if (tgt->mmap_events == 1 && tsk->tgid == tsk->pid) {
-               __stp_call_mmap_callbacks_for_task(tgt, tsk);
+               /* If this is just a thread other than the thread
+                  group leader, don't bother inform map callback
+                  clients about its memory map, since they will
+                  simply duplicate each other. */
+               if (tgt->mmap_events == 1 && tsk->tgid == tsk->pid) {
+                       __stp_call_mmap_callbacks_for_task(tgt, tsk);
+               }
        }
 
        __stp_tf_handler_end();
index ee396107ce6055f95847512d6ccbf0769fdba6fe..fc2dd058ed61d3ea69b1853527a8774180b63186 100644 (file)
@@ -174,6 +174,7 @@ stapiu_decrement_semaphores(struct stapiu_target *targets, size_t ntargets)
        /* NB: no stapiu_process_lock needed, as the task_finder engine is
         * already stopped by now, so no one else will mess with us.  We need
         * to be sleepable for access_process_vm.  */
+       might_sleep();
        for (i = 0; i < ntargets; ++i) {
                struct stapiu_target *ut = &targets[i];
                struct stapiu_consumer *c;
@@ -307,6 +308,8 @@ stapiu_change_plus(struct stapiu_target* target, struct task_struct *task,
 {
        size_t i;
        struct stapiu_process *p;
+       int rc;
+
        write_lock(&stapiu_process_lock);
        for (i = 0; i < MAXUPROBES; ++i) {
                p = &stapiu_process_slots[i];
@@ -321,6 +324,11 @@ stapiu_change_plus(struct stapiu_target* target, struct task_struct *task,
                }
        }
        write_unlock(&stapiu_process_lock);
+
+       if ((rc = _stp_usermodule_check(task, target->filename,
+                                       relocation)))
+               return rc;
+
        return 0; /* XXX: or an error? maxskipped? */
 }
 
@@ -357,7 +365,8 @@ stapiu_change_semaphore_plus(struct stapiu_target* target, struct task_struct *t
                if (c->sdt_sem_offset) {
                        unsigned long addr = process->base + c->sdt_sem_offset;
                        if (addr >= relocation && addr < relocation + length) {
-                               int rc2 = stapiu_write_semaphore(addr, +1);
+                               int rc2 = stapiu_write_task_semaphore(task,
+                                                                     addr, +1);
                                if (!rc)
                                        rc = rc2;
                        }
This page took 0.046691 seconds and 5 git commands to generate.