summaryrefslogtreecommitdiffstats
path: root/sys/cddl
diff options
context:
space:
mode:
authorpfg <pfg@FreeBSD.org>2013-04-01 19:13:46 +0000
committerpfg <pfg@FreeBSD.org>2013-04-01 19:13:46 +0000
commit50c97d79d92898d42c89c511f1d7b5971e269fc6 (patch)
tree616a766c28f9857c1c230647efb85c7655238976 /sys/cddl
parent3deb97fc0b145c3f68e924fbc0d4c173749860fd (diff)
parent27bdc9a206a2012fbb5f2dfc4ba6e485f4bd1ec9 (diff)
downloadFreeBSD-src-50c97d79d92898d42c89c511f1d7b5971e269fc6.zip
FreeBSD-src-50c97d79d92898d42c89c511f1d7b5971e269fc6.tar.gz
Dtrace: enablings on defunct providers prevent providers from unregistering
Merge change from illumos: 1368 enablings on defunct providers prevent providers from unregistering We try to address some underlying differences between the Solaris and FreeBSD implementations: dtrace_attach() / dtrace_detach() are currently unimplemented in FreeBSD but the new code from illumos makes use of taskq so some adaptations were made to dtrace_open() and dtrace_close() to handle them appropriately. Illumos Revision: r13430:8e6add739e38 Reference: https://www.illumos.org/issues/1368 Reviewed by: gnn Tested by: Fabian Keil Obtained from: Illumos MFC after: 3 weeks
Diffstat (limited to 'sys/cddl')
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c151
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c22
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h13
3 files changed, 171 insertions, 15 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
index 391c814..c8bb4de 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
@@ -182,6 +182,7 @@ int dtrace_err_verbose;
hrtime_t dtrace_deadman_interval = NANOSEC;
hrtime_t dtrace_deadman_timeout = (hrtime_t)10 * NANOSEC;
hrtime_t dtrace_deadman_user = (hrtime_t)30 * NANOSEC;
+hrtime_t dtrace_unregister_defunct_reap = (hrtime_t)60 * NANOSEC;
/*
* DTrace External Variables
@@ -203,8 +204,8 @@ static dev_info_t *dtrace_devi; /* device info */
#if defined(sun)
static vmem_t *dtrace_arena; /* probe ID arena */
static vmem_t *dtrace_minor; /* minor number arena */
-static taskq_t *dtrace_taskq; /* task queue */
#else
+static taskq_t *dtrace_taskq; /* task queue */
static struct unrhdr *dtrace_arena; /* Probe ID number. */
#endif
static dtrace_probe_t **dtrace_probes; /* array of all probes */
@@ -550,11 +551,13 @@ static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id);
static void dtrace_enabling_provide(dtrace_provider_t *);
static int dtrace_enabling_match(dtrace_enabling_t *, int *);
static void dtrace_enabling_matchall(void);
+static void dtrace_enabling_reap(void);
static dtrace_state_t *dtrace_anon_grab(void);
static uint64_t dtrace_helper(int, dtrace_mstate_t *,
dtrace_state_t *, uint64_t, uint64_t);
static dtrace_helpers_t *dtrace_helpers_create(proc_t *);
static void dtrace_buffer_drop(dtrace_buffer_t *);
+static int dtrace_buffer_consumed(dtrace_buffer_t *, hrtime_t when);
static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *, size_t, size_t,
dtrace_state_t *, dtrace_mstate_t *);
static int dtrace_state_option(dtrace_state_t *, dtrace_optid_t,
@@ -7593,7 +7596,7 @@ dtrace_unregister(dtrace_provider_id_t id)
{
dtrace_provider_t *old = (dtrace_provider_t *)id;
dtrace_provider_t *prev = NULL;
- int i, self = 0;
+ int i, self = 0, noreap = 0;
dtrace_probe_t *probe, *first = NULL;
if (old->dtpv_pops.dtps_enable ==
@@ -7652,14 +7655,31 @@ dtrace_unregister(dtrace_provider_id_t id)
continue;
/*
- * We have at least one ECB; we can't remove this provider.
+ * If we are trying to unregister a defunct provider, and the
+ * provider was made defunct within the interval dictated by
+ * dtrace_unregister_defunct_reap, we'll (asynchronously)
+ * attempt to reap our enablings. To denote that the provider
+ * should reattempt to unregister itself at some point in the
+ * future, we will return a differentiable error code (EAGAIN
+ * instead of EBUSY) in this case.
*/
+ if (dtrace_gethrtime() - old->dtpv_defunct >
+ dtrace_unregister_defunct_reap)
+ noreap = 1;
+
if (!self) {
mutex_exit(&dtrace_lock);
mutex_exit(&mod_lock);
mutex_exit(&dtrace_provider_lock);
}
- return (EBUSY);
+
+ if (noreap)
+ return (EBUSY);
+
+ (void) taskq_dispatch(dtrace_taskq,
+ (task_func_t *)dtrace_enabling_reap, NULL, TQ_SLEEP);
+
+ return (EAGAIN);
}
/*
@@ -7756,7 +7776,7 @@ dtrace_invalidate(dtrace_provider_id_t id)
mutex_enter(&dtrace_provider_lock);
mutex_enter(&dtrace_lock);
- pvp->dtpv_defunct = 1;
+ pvp->dtpv_defunct = dtrace_gethrtime();
mutex_exit(&dtrace_lock);
mutex_exit(&dtrace_provider_lock);
@@ -10719,6 +10739,7 @@ dtrace_buffer_switch(dtrace_buffer_t *buf)
caddr_t tomax = buf->dtb_tomax;
caddr_t xamot = buf->dtb_xamot;
dtrace_icookie_t cookie;
+ hrtime_t now = dtrace_gethrtime();
ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
ASSERT(!(buf->dtb_flags & DTRACEBUF_RING));
@@ -10734,6 +10755,8 @@ dtrace_buffer_switch(dtrace_buffer_t *buf)
buf->dtb_drops = 0;
buf->dtb_errors = 0;
buf->dtb_flags &= ~(DTRACEBUF_ERROR | DTRACEBUF_DROPPED);
+ buf->dtb_interval = now - buf->dtb_switched;
+ buf->dtb_switched = now;
dtrace_interrupt_enable(cookie);
}
@@ -11222,6 +11245,36 @@ dtrace_buffer_polish(dtrace_buffer_t *buf)
}
}
+/*
+ * This routine determines if data generated at the specified time has likely
+ * been entirely consumed at user-level. This routine is called to determine
+ * if an ECB on a defunct probe (but for an active enabling) can be safely
+ * disabled and destroyed.
+ */
+static int
+dtrace_buffer_consumed(dtrace_buffer_t *bufs, hrtime_t when)
+{
+ int i;
+
+ for (i = 0; i < NCPU; i++) {
+ dtrace_buffer_t *buf = &bufs[i];
+
+ if (buf->dtb_size == 0)
+ continue;
+
+ if (buf->dtb_flags & DTRACEBUF_RING)
+ return (0);
+
+ if (!buf->dtb_switched && buf->dtb_offset != 0)
+ return (0);
+
+ if (buf->dtb_switched - buf->dtb_interval < when)
+ return (0);
+ }
+
+ return (1);
+}
+
static void
dtrace_buffer_free(dtrace_buffer_t *bufs)
{
@@ -11693,6 +11746,85 @@ dtrace_enabling_provide(dtrace_provider_t *prv)
}
/*
+ * Called to reap ECBs that are attached to probes from defunct providers.
+ */
+static void
+dtrace_enabling_reap(void)
+{
+ dtrace_provider_t *prov;
+ dtrace_probe_t *probe;
+ dtrace_ecb_t *ecb;
+ hrtime_t when;
+ int i;
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL)
+ continue;
+
+ if (probe->dtpr_ecb == NULL)
+ continue;
+
+ prov = probe->dtpr_provider;
+
+ if ((when = prov->dtpv_defunct) == 0)
+ continue;
+
+ /*
+ * We have ECBs on a defunct provider: we want to reap these
+ * ECBs to allow the provider to unregister. The destruction
+ * of these ECBs must be done carefully: if we destroy the ECB
+ * and the consumer later wishes to consume an EPID that
+ * corresponds to the destroyed ECB (and if the EPID metadata
+ * has not been previously consumed), the consumer will abort
+ * processing on the unknown EPID. To reduce (but not, sadly,
+ * eliminate) the possibility of this, we will only destroy an
+ * ECB for a defunct provider if, for the state that
+ * corresponds to the ECB:
+ *
+ * (a) There is no speculative tracing (which can effectively
+ * cache an EPID for an arbitrary amount of time).
+ *
+ * (b) The principal buffers have been switched twice since the
+ * provider became defunct.
+ *
+ * (c) The aggregation buffers are of zero size or have been
+ * switched twice since the provider became defunct.
+ *
+ * We use dts_speculates to determine (a) and call a function
+ * (dtrace_buffer_consumed()) to determine (b) and (c). Note
+ * that as soon as we've been unable to destroy one of the ECBs
+ * associated with the probe, we quit trying -- reaping is only
+ * fruitful in as much as we can destroy all ECBs associated
+ * with the defunct provider's probes.
+ */
+ while ((ecb = probe->dtpr_ecb) != NULL) {
+ dtrace_state_t *state = ecb->dte_state;
+ dtrace_buffer_t *buf = state->dts_buffer;
+ dtrace_buffer_t *aggbuf = state->dts_aggbuffer;
+
+ if (state->dts_speculates)
+ break;
+
+ if (!dtrace_buffer_consumed(buf, when))
+ break;
+
+ if (!dtrace_buffer_consumed(aggbuf, when))
+ break;
+
+ dtrace_ecb_disable(ecb);
+ ASSERT(probe->dtpr_ecb != ecb);
+ dtrace_ecb_destroy(ecb);
+ }
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+}
+
+/*
* DTrace DOF Functions
*/
/*ARGSUSED*/
@@ -15529,6 +15661,10 @@ dtrace_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
#else
devfs_set_cdevpriv(state, dtrace_dtr);
#endif
+ /* This code actually belongs in dtrace_attach() */
+ if (dtrace_opens == 1)
+ dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri,
+ 1, INT_MAX, 0);
#endif
mutex_exit(&cpu_lock);
@@ -15616,6 +15752,11 @@ dtrace_dtr(void *data)
(void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE);
#else
--dtrace_opens;
+ /* This code actually belongs in dtrace_detach() */
+ if ((dtrace_opens == 0) && (dtrace_taskq != NULL)) {
+ taskq_destroy(dtrace_taskq);
+ dtrace_taskq = NULL;
+ }
#endif
mutex_exit(&dtrace_lock);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
index ad3b2d5..691b6c9 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
@@ -175,6 +175,9 @@ static volatile uint64_t fasttrap_mod_gen;
static uint32_t fasttrap_max;
static uint32_t fasttrap_total;
+/*
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ */
#define FASTTRAP_TPOINTS_DEFAULT_SIZE 0x4000
#define FASTTRAP_PROVIDERS_DEFAULT_SIZE 0x100
@@ -317,7 +320,7 @@ fasttrap_pid_cleanup_cb(void *data)
fasttrap_provider_t **fpp, *fp;
fasttrap_bucket_t *bucket;
dtrace_provider_id_t provid;
- int i, later = 0;
+ int i, later = 0, rval;
static volatile int in = 0;
ASSERT(in == 0);
@@ -378,9 +381,13 @@ fasttrap_pid_cleanup_cb(void *data)
* clean out the unenabled probes.
*/
provid = fp->ftp_provid;
- if (dtrace_unregister(provid) != 0) {
+ if ((rval = dtrace_unregister(provid)) != 0) {
if (fasttrap_total > fasttrap_max / 2)
(void) dtrace_condense(provid);
+
+ if (rval == EAGAIN)
+ fp->ftp_marked = 1;
+
later += fp->ftp_marked;
fpp = &fp->ftp_next;
} else {
@@ -408,12 +415,15 @@ fasttrap_pid_cleanup_cb(void *data)
* get a chance to do that work if and when the timeout is reenabled
* (if detach fails).
*/
- if (later > 0 && callout_active(&fasttrap_timeout))
- callout_reset(&fasttrap_timeout, hz, &fasttrap_pid_cleanup_cb,
- NULL);
+ 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 {
+ } else {
#if !defined(sun)
/* Nothing to be done for FreeBSD */
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
index 6870a0b..4d9a4f8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
@@ -26,11 +26,13 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ */
+
#ifndef _SYS_DTRACE_IMPL_H
#define _SYS_DTRACE_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -429,8 +431,11 @@ typedef struct dtrace_buffer {
uint32_t dtb_errors; /* number of errors */
uint32_t dtb_xamot_errors; /* errors in inactive buffer */
#ifndef _LP64
- uint64_t dtb_pad1;
+ uint64_t dtb_pad1; /* pad out to 64 bytes */
#endif
+ uint64_t dtb_switched; /* time of last switch */
+ uint64_t dtb_interval; /* observed switch interval */
+ uint64_t dtb_pad2[6]; /* pad to avoid false sharing */
} dtrace_buffer_t;
/*
@@ -1162,7 +1167,7 @@ struct dtrace_provider {
dtrace_pops_t dtpv_pops; /* provider operations */
char *dtpv_name; /* provider name */
void *dtpv_arg; /* provider argument */
- uint_t dtpv_defunct; /* boolean: defunct provider */
+ hrtime_t dtpv_defunct; /* when made defunct */
struct dtrace_provider *dtpv_next; /* next provider */
};
OpenPOWER on IntegriCloud