1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

The fasttrap provider cleans up probes asynchronously when a process with

USDT probes exits. This was previously done with a callout; however, it is
possible to sleep while holding the DTrace mutexes, so a panic will occur
on INVARIANTS kernels if the callout handler can't immediately acquire one
of these mutexes. This panic will be frequently triggered on systems where
a USDT-enabled program (perl, for instance) is often run.

This revision changes the fasttrap cleanup mechanism so that a dedicated
thread is used instead of a callout. The old behaviour is otherwise
preserved.

Reviewed by:	rpaulo
MFC after:	1 month
This commit is contained in:
Mark Johnston 2013-05-24 03:29:32 +00:00
parent 4591f0d339
commit 427bc75e19
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=250953

View File

@ -155,9 +155,9 @@ static struct cdevsw fasttrap_cdevsw = {
static struct cdev *fasttrap_cdev;
static dtrace_meta_provider_id_t fasttrap_meta_id;
static struct callout fasttrap_timeout;
static struct proc *fasttrap_cleanup_proc;
static struct mtx fasttrap_cleanup_mtx;
static uint_t fasttrap_cleanup_work;
static uint_t fasttrap_cleanup_work, fasttrap_cleanup_drain, fasttrap_cleanup_cv;
/*
* Generation count on modifications to the global tracepoint lookup table.
@ -310,8 +310,11 @@ fasttrap_mod_barrier(uint64_t gen)
}
/*
* This is the timeout's callback for cleaning up the providers and their
* probes.
* This function performs asynchronous cleanup of fasttrap providers. The
* Solaris implementation of this mechanism use a timeout that's activated in
* fasttrap_pid_cleanup(), but this doesn't work in FreeBSD: one may sleep while
* holding the DTrace mutexes, but it is unsafe to sleep in a callout handler.
* Thus we use a dedicated process to perform the cleanup when requested.
*/
/*ARGSUSED*/
static void
@ -322,11 +325,8 @@ fasttrap_pid_cleanup_cb(void *data)
dtrace_provider_id_t provid;
int i, later = 0, rval;
static volatile int in = 0;
ASSERT(in == 0);
in = 1;
while (fasttrap_cleanup_work) {
mtx_lock(&fasttrap_cleanup_mtx);
while (!fasttrap_cleanup_drain || later > 0) {
fasttrap_cleanup_work = 0;
mtx_unlock(&fasttrap_cleanup_mtx);
@ -397,39 +397,32 @@ fasttrap_pid_cleanup_cb(void *data)
}
mutex_exit(&bucket->ftb_mtx);
}
mtx_lock(&fasttrap_cleanup_mtx);
}
#if 0
ASSERT(fasttrap_timeout != 0);
#endif
/*
* If we were unable to retire a provider, try again after a
* second. This situation can occur in certain circumstances
* where providers cannot be unregistered even though they have
* no probes enabled because of an execution of dtrace -l or
* something similar.
*/
if (later > 0 || fasttrap_cleanup_work ||
fasttrap_cleanup_drain) {
mtx_unlock(&fasttrap_cleanup_mtx);
pause("ftclean", hz);
mtx_lock(&fasttrap_cleanup_mtx);
} else
mtx_sleep(&fasttrap_cleanup_cv, &fasttrap_cleanup_mtx,
0, "ftcl", 0);
}
/*
* If we were unable to remove a retired provider, try again after
* a second. This situation can occur in certain circumstances where
* providers cannot be unregistered even though they have no probes
* enabled because of an execution of dtrace -l or something similar.
* If the timeout has been disabled (set to 1 because we're trying
* to detach), we set fasttrap_cleanup_work to ensure that we'll
* get a chance to do that work if and when the timeout is reenabled
* (if detach fails).
* Wake up the thread in fasttrap_unload() now that we're done.
*/
if (later > 0) {
if (callout_active(&fasttrap_timeout)) {
callout_reset(&fasttrap_timeout, hz,
&fasttrap_pid_cleanup_cb, NULL);
}
else if (later > 0)
fasttrap_cleanup_work = 1;
} else {
#if !defined(sun)
/* Nothing to be done for FreeBSD */
#endif
}
wakeup(&fasttrap_cleanup_drain);
mtx_unlock(&fasttrap_cleanup_mtx);
in = 0;
kthread_exit();
}
/*
@ -440,8 +433,10 @@ fasttrap_pid_cleanup(void)
{
mtx_lock(&fasttrap_cleanup_mtx);
fasttrap_cleanup_work = 1;
callout_reset(&fasttrap_timeout, 1, &fasttrap_pid_cleanup_cb, NULL);
if (!fasttrap_cleanup_work) {
fasttrap_cleanup_work = 1;
wakeup(&fasttrap_cleanup_cv);
}
mtx_unlock(&fasttrap_cleanup_mtx);
}
@ -991,7 +986,6 @@ fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg)
proc_t *p = NULL;
int i, rc;
ASSERT(probe != NULL);
ASSERT(!probe->ftp_enabled);
ASSERT(id == probe->ftp_id);
@ -2272,17 +2266,23 @@ static int
fasttrap_load(void)
{
ulong_t nent;
int i;
int i, ret;
/* Create the /dev/dtrace/fasttrap entry. */
fasttrap_cdev = make_dev(&fasttrap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/fasttrap");
mtx_init(&fasttrap_cleanup_mtx, "fasttrap clean", "dtrace", MTX_DEF);
callout_init_mtx(&fasttrap_timeout, &fasttrap_cleanup_mtx, 0);
mutex_init(&fasttrap_count_mtx, "fasttrap count mtx", MUTEX_DEFAULT,
NULL);
ret = kproc_create(fasttrap_pid_cleanup_cb, NULL,
&fasttrap_cleanup_proc, 0, 0, "ftcleanup");
if (ret != 0) {
destroy_dev(fasttrap_cdev);
return (ret);
}
/*
* Install our hooks into fork(2), exec(2), and exit(2).
*/
@ -2388,15 +2388,6 @@ fasttrap_unload(void)
dtrace_meta_unregister(fasttrap_meta_id) != 0)
return (-1);
/*
* Prevent any new timeouts from running by setting fasttrap_timeout
* to a non-zero value, and wait for the current timeout to complete.
*/
mtx_lock(&fasttrap_cleanup_mtx);
fasttrap_cleanup_work = 0;
callout_drain(&fasttrap_timeout);
mtx_unlock(&fasttrap_cleanup_mtx);
/*
* Iterate over all of our providers. If there's still a process
* that corresponds to that pid, fail to detach.
@ -2431,26 +2422,20 @@ fasttrap_unload(void)
}
if (fail) {
uint_t work;
/*
* If we're failing to detach, we need to unblock timeouts
* and start a new timeout if any work has accumulated while
* we've been unsuccessfully trying to detach.
*/
mtx_lock(&fasttrap_cleanup_mtx);
work = fasttrap_cleanup_work;
callout_drain(&fasttrap_timeout);
mtx_unlock(&fasttrap_cleanup_mtx);
if (work)
fasttrap_pid_cleanup();
(void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,
&fasttrap_meta_id);
return (-1);
}
mtx_lock(&fasttrap_cleanup_mtx);
fasttrap_cleanup_drain = 1;
/* Wait for the cleanup thread to finish up and signal us. */
wakeup(&fasttrap_cleanup_cv);
mtx_sleep(&fasttrap_cleanup_drain, &fasttrap_cleanup_mtx, 0, "ftcld",
0);
fasttrap_cleanup_proc = NULL;
#ifdef DEBUG
mutex_enter(&fasttrap_count_mtx);
ASSERT(fasttrap_pid_count == 0);