Make register_uprobe/ unregister_uprobe to defer if the task having an
active probe is stopped or traced
    
Signed-off-by: Srikar Dronamraju <sri...@linux.vnet.ibm.com>
Reviewed-by: Jim Keniston<jkeni...@us.ibm.com>
---
 kernel/uprobes_core.c |   78 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/kernel/uprobes_core.c b/kernel/uprobes_core.c
index 74d568b..7cea30b 100644
--- a/kernel/uprobes_core.c
+++ b/kernel/uprobes_core.c
@@ -824,12 +824,35 @@ static void purge_uprobe(struct uprobe_kimg *uk)
                uprobe_free_probept(ppt);
 }
 
-/* Runs with utask->uproc read-locked.  Returns -EINPROGRESS on success. */
+/*
+ * Runs with utask->uproc locked.
+ * read lock if called from uprobe handler.
+ * else write lock.
+ * Returns -EINPROGRESS on success.
+ * Returns -EBUSY if a request for defer registration already exists.
+ * Returns 0 if we have deferred request for both register/unregister.
+ *
+ */
 static int defer_registration(struct uprobe *u, int regflag,
                struct uprobe_task *utask)
 {
-       struct deferred_registration *dr;
+       struct deferred_registration *dr, *d;
 
+       /* Check if we already have such a defer request */
+       list_for_each_entry_safe(dr, d, &utask->deferred_registrations, list) {
+               if (dr->uprobe == u) {
+                       if (dr->regflag != regflag) {
+                               /* same as successful register + unregister */
+                               list_del(&dr->list);
+                               kfree(dr);
+                               return 0;
+                       } else
+                               /* we already have identical request */
+                               return -EBUSY;
+               }
+       }
+
+       /* We have a new unique request */
        dr = kmalloc(sizeof(struct deferred_registration), GFP_USER);
        if (!dr)
                return -ENOMEM;
@@ -885,7 +908,8 @@ int register_uprobe(struct uprobe *u)
                return -ESRCH;
 
        cur_utask = uprobe_find_utask(current);
-       if (cur_utask && cur_utask->active_probe) {
+       if (cur_utask && (cur_utask->state == UPTASK_TRAMPOLINE_HIT ||
+                               cur_utask->state == UPTASK_BP_HIT)) {
                /*
                 * Called from handler; cur_utask->uproc is read-locked.
                 * Do this registration later.
@@ -898,9 +922,36 @@ int register_uprobe(struct uprobe *u)
        mutex_lock(&uproc_mutex);
        uproc = uprobe_find_process(p);
 
-       if (uproc)
+       if (uproc) {
+               struct uprobe_task *utask;
+
                mutex_unlock(&uproc_mutex);
-       else {
+               list_for_each_entry(utask, &uproc->thread_list, list) {
+                       if (!utask->active_probe)
+                               continue;
+                       /*
+                        * utask is at a probepoint, but has dropped
+                        * uproc->rwsem to single-step.  If utask is
+                        * stopped, then it's probably because some
+                        * other engine has asserted UTRACE_STOP;
+                        * that engine may not allow UTRACE_RESUME
+                        * until register_uprobe() returns.  But, for
+                        * reasons we won't go into here, utask wants
+                        * to finish with utask->active_probe before
+                        * allowing handle_pending_uprobes() to run
+                        * (via utask_fake_quiesce()).  So we defer this
+                        * registration operation; it will be run after
+                        * utask->active_probe is taken care of.
+                        */
+                       BUG_ON(utask->state != UPTASK_SSTEP);
+                       if (task_is_stopped_or_traced(utask->tsk)) {
+                               put_pid(p);
+                               ret =  defer_registration(u, 1, utask);
+                               up_write(&uproc->rwsem);
+                               return ret;
+                       }
+               }
+       } else {
                uproc = uprobe_mk_process(p);
                if (IS_ERR(uproc)) {
                        ret = (int) PTR_ERR(uproc);
@@ -1041,6 +1092,7 @@ void unregister_uprobe(struct uprobe *u)
        struct uprobe_kimg *uk;
        struct uprobe_probept *ppt;
        struct uprobe_task *cur_utask, *cur_utask_quiescing = NULL;
+       struct uprobe_task *utask;
 
        if (!u)
                return;
@@ -1049,7 +1101,8 @@ void unregister_uprobe(struct uprobe *u)
                return;
 
        cur_utask = uprobe_find_utask(current);
-       if (cur_utask && cur_utask->active_probe) {
+       if (cur_utask && (cur_utask->state == UPTASK_TRAMPOLINE_HIT ||
+                               cur_utask->state == UPTASK_BP_HIT)) {
                /* Called from handler; uproc is read-locked; do this later */
                put_pid(p);
                (void) defer_registration(u, 0, cur_utask);
@@ -1067,6 +1120,19 @@ void unregister_uprobe(struct uprobe *u)
        if (!uproc)
                return;
 
+       list_for_each_entry(utask, &uproc->thread_list, list) {
+               if (!utask->active_probe)
+                       continue;
+
+               /* See comment in register_uprobe(). */
+               BUG_ON(utask->state != UPTASK_SSTEP);
+               if (task_is_stopped_or_traced(utask->tsk)) {
+                       put_pid(p);
+                       (void) defer_registration(u, 0, utask);
+                       up_write(&uproc->rwsem);
+                       return;
+               }
+       }
        uk = (struct uprobe_kimg *)u->kdata;
        if (!uk)
                /*

Reply via email to