svn commit: r250953 - head/sys/cddl/contrib/opensolaris/uts/common/dtrace

Alan Somers asomers at freebsd.org
Wed Jul 3 17:28:25 UTC 2013


This creates another panic on module unload when WITNESS is enabled,
because the module exits while holding the fasttrap_cleanup_mtx.  This
patch fixes the problem.  I'm not sure if the mtx_destroy() is
necessary, but I would feel dirty to leave it out.  Does this patch
look good to you?

Index: sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c	(revision 252490)
+++ sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c	(working copy)
@@ -2434,6 +2434,7 @@
 	wakeup(&fasttrap_cleanup_cv);
 	mtx_sleep(&fasttrap_cleanup_drain, &fasttrap_cleanup_mtx, 0, "ftcld",
 	    0);
+	mtx_unlock(&fasttrap_cleanup_mtx);
 	fasttrap_cleanup_proc = NULL;

 #ifdef DEBUG
@@ -2473,6 +2474,7 @@
 #if !defined(sun)
 	destroy_dev(fasttrap_cdev);
 	mutex_destroy(&fasttrap_count_mtx);
+	mtx_destroy(&fasttrap_cleanup_mtx);
 	CPU_FOREACH(i) {
 		mutex_destroy(&fasttrap_cpuc_pid_lock[i]);
 	}


On Thu, May 23, 2013 at 9:29 PM, Mark Johnston <markj at freebsd.org> wrote:
> Author: markj
> Date: Fri May 24 03:29:32 2013
> New Revision: 250953
> URL: http://svnweb.freebsd.org/changeset/base/250953
>
> Log:
>   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
>
> Modified:
>   head/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
>
> Modified: head/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
> ==============================================================================
> --- head/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c      Fri May 24 02:18:37 2013        (r250952)
> +++ head/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c      Fri May 24 03:29:32 2013        (r250953)
> @@ -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).
> -        */
> -       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
> -       }
> +        * Wake up the thread in fasttrap_unload() now that we're done.
> +        */
> +       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
>         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).
>          */
> @@ -2389,15 +2389,6 @@ fasttrap_unload(void)
>                 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);


More information about the svn-src-all mailing list