summaryrefslogtreecommitdiffstats
path: root/runtime/uprobes/uprobes.c
diff options
context:
space:
mode:
authorkenistoj <kenistoj>2007-10-09 22:06:40 +0000
committerkenistoj <kenistoj>2007-10-09 22:06:40 +0000
commit35f3ab9e17f5e743e2deb3e5c14a3f1776b18e84 (patch)
treec57e39c0c4b1d80254da7ed9c772a193ec3ad9a5 /runtime/uprobes/uprobes.c
parent899869e3e7d8adcc1c2d95b79256ac849df5afb2 (diff)
downloadsystemtap-steved-35f3ab9e17f5e743e2deb3e5c14a3f1776b18e84.tar.gz
systemtap-steved-35f3ab9e17f5e743e2deb3e5c14a3f1776b18e84.tar.xz
systemtap-steved-35f3ab9e17f5e743e2deb3e5c14a3f1776b18e84.zip
PR 5083
* runtime/uprobes/uprobes.c: Adjust module ref-count when creating or removing uprobe_process, so "rmmod --wait uprobes" waits as needed until uretprobed functions return.
Diffstat (limited to 'runtime/uprobes/uprobes.c')
-rw-r--r--runtime/uprobes/uprobes.c51
1 files changed, 38 insertions, 13 deletions
diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c
index 16c5e046..01b40670 100644
--- a/runtime/uprobes/uprobes.c
+++ b/runtime/uprobes/uprobes.c
@@ -475,10 +475,16 @@ static void uprobe_free_process(struct uprobe_process *uproc)
* Decrement uproc's ref count. If it's zero, free uproc and return 1.
* Else return 0. If uproc is locked, don't call this; use
* uprobe_decref_process().
+ *
+ * If we free uproc, we also decrement the ref-count on the uprobes
+ * module, if any. If somebody is doing "rmmod --wait uprobes", this
+ * function could schedule removal of the module. Therefore, don't call
+ * this function and then sleep in uprobes code, unless you know you'll
+ * return with the module ref-count > 0.
*/
static int uprobe_put_process(struct uprobe_process *uproc)
{
- int ret = 0;
+ int freed = 0;
if (atomic_dec_and_test(&uproc->refcount)) {
lock_uproc_table();
down_write(&uproc->rwsem);
@@ -491,11 +497,13 @@ static int uprobe_put_process(struct uprobe_process *uproc)
up_write(&uproc->rwsem);
} else {
uprobe_free_process(uproc);
- ret = 1;
+ freed = 1;
}
unlock_uproc_table();
}
- return ret;
+ if (freed)
+ module_put(THIS_MODULE);
+ return freed;
}
static struct uprobe_kimg *uprobe_mk_kimg(struct uprobe *u)
@@ -842,10 +850,18 @@ int register_uprobe(struct uprobe *u)
if (uproc)
unlock_uproc_table();
else {
+ /* Creating a new uprobe_process. Ref-count the module. */
+ if (!try_module_get(THIS_MODULE)) {
+ /* uprobes.ko is being removed. */
+ ret = -ENOSYS;
+ unlock_uproc_table();
+ goto fail_tsk;
+ }
uproc = uprobe_mk_process(p);
if (IS_ERR(uproc)) {
ret = (int) PTR_ERR(uproc);
unlock_uproc_table();
+ module_put(THIS_MODULE);
goto fail_tsk;
}
/* Hold uproc_mutex until we've added uproc to uproc_table. */
@@ -955,6 +971,7 @@ fail_uproc:
if (uproc_is_new) {
uprobe_free_process(uproc);
unlock_uproc_table();
+ module_put(THIS_MODULE);
} else {
up_write(&uproc->rwsem);
uprobe_put_process(uproc);
@@ -1063,8 +1080,9 @@ static struct task_struct *find_surviving_thread(struct uprobe_process *uproc)
/*
* Run all the deferred_registrations previously queued by the current utask.
- * Runs with no locks or mutexes held. The current utask could disappear
- * as the result of unregister_u*probe() called here.
+ * Runs with no locks or mutexes held. The current utask's uprobe_process
+ * is ref-counted, so they won't disappear as the result of
+ * unregister_u*probe() called here.
*/
static void uprobe_run_def_regs(struct list_head *drlist)
{
@@ -1577,7 +1595,7 @@ static u32 uprobe_report_signal(struct utrace_attached_engine *engine,
u32 ret;
unsigned long probept;
int hit_uretprobe_trampoline = 0;
- LIST_HEAD(def_reg_list);
+ int registrations_deferred = 0;
utask = rcu_dereference((struct uprobe_task *)engine->data);
BUG_ON(!utask);
@@ -1673,12 +1691,16 @@ bkpt_done:
utask->active_probe = NULL;
- /*
- * The deferred_registrations list accumulates in utask,
- * but utask could go away when we uprobe_run_def_regs.
- * So switch the list head to a local variable.
- */
- list_splice_init(&utask->deferred_registrations, &def_reg_list);
+ if (!list_empty(&utask->deferred_registrations)) {
+ /*
+ * Make sure utask doesn't go away before we run
+ * the deferred registrations. This also keeps
+ * the module from getting unloaded before we're
+ * ready.
+ */
+ registrations_deferred = 1;
+ uprobe_get_process(uproc);
+ }
ret = UTRACE_ACTION_HIDE | UTRACE_SIGNAL_IGN
| UTRACE_ACTION_NEWSTATE;
@@ -1698,7 +1720,10 @@ bkpt_done:
*/
uprobe_put_process(uproc);
- uprobe_run_def_regs(&def_reg_list);
+ if (registrations_deferred) {
+ uprobe_run_def_regs(&utask->deferred_registrations);
+ uprobe_put_process(uproc);
+ }
break;
default:
goto no_interest;