/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2016 Joyent, Inc.
*/
#include <sys/aio_impl.h>
#ifdef __sparc
#include <sys/privregs.h>
#endif
void (*dtrace_cpustart_init)(void);
void (*dtrace_cpustart_fini)(void);
void (*dtrace_closef)(void);
void (*dtrace_debugger_init)(void);
void (*dtrace_debugger_fini)(void);
/*
* dtrace_cpc_in_use usage statement: this global variable is used by the cpc
* hardware overflow interrupt handler and the kernel cpc framework to check
* whether or not the DTrace cpc provider is currently in use. The variable is
* set before counters are enabled with the first enabling and cleared when
* the last enabling is disabled. Its value at any given time indicates the
* number of active dcpc based enablings. The global 'kcpc_cpuctx_lock' rwlock
* is held during initial setting to protect races between kcpc_open() and the
* first enabling. The locking provided by the DTrace subsystem, the kernel
* cpc framework and the cpu management framework protect consumers from race
* conditions on enabling and disabling probes.
*/
typedef struct dtrace_hrestime {
/*
* Making available adjustable high-resolution time in DTrace is regrettably
* more complicated than one might think it should be. The problem is that
* the variables related to adjusted high-resolution time (hrestime,
* hrestime_adj and friends) are adjusted under hres_lock -- and this lock may
* be held when we enter probe context. One might think that we could address
* this by having a single snapshot copy that is stored under a different lock
* from hres_tick(), using the snapshot iff hres_lock is locked in probe
* context. Unfortunately, this too won't work: because hres_lock is grabbed
* in more than just hres_tick() context, we could enter probe context
* concurrently on two different CPUs with both locks (hres_lock and the
* snapshot lock) held. As this implies, the fundamental problem is that we
* need to have access to a snapshot of these variables that we _know_ will
* not be locked in probe context. To effect this, we have two snapshots
* protected by two different locks, and we mandate that these snapshots are
* recorded in succession by a single thread calling dtrace_hres_tick(). (We
* assure this by calling it out of the same CY_HIGH_LEVEL cyclic that calls
* hres_tick().) A single thread can't be in two places at once: one of the
* snapshot locks is guaranteed to be unheld at all times. The
* dtrace_gethrestime() algorithm is thus to check first one snapshot and then
* the other to find the unlocked snapshot.
*/
void
dtrace_hres_tick(void)
{
int i;
for (i = 0; i < 2; i++) {
spl = hr_clock_lock();
/*
* To allow for lock-free examination of this lock, we use
* the same trick that is used hres_lock; for more details,
*/
dtrace_hrestime[i].dthr_lock++;
}
}
dtrace_gethrestime(void)
{
for (;;) {
break;
/*
* If we're here, the lock was either locked, or it
* transitioned while we were taking the snapshot. Either
* way, we're going to try the other dtrace_hrestime element;
* we know that it isn't possible for both to be locked
* simultaneously, so we will ultimately get a good snapshot.
*/
i ^= 1;
}
/*
* We have a good snapshot. Now perform any necessary adjustments.
*/
} else {
}
}
return (now);
}
void
dtrace_vtime_enable(void)
{
do {
switch (state) {
case DTRACE_VTIME_INACTIVE:
break;
break;
case DTRACE_VTIME_ACTIVE:
case DTRACE_VTIME_ACTIVE_TNF:
panic("DTrace virtual time already enabled");
/*NOTREACHED*/
}
}
void
dtrace_vtime_disable(void)
{
do {
switch (state) {
case DTRACE_VTIME_ACTIVE:
break;
case DTRACE_VTIME_ACTIVE_TNF:
break;
case DTRACE_VTIME_INACTIVE:
panic("DTrace virtual time already disabled");
/*NOTREACHED*/
}
}
void
dtrace_vtime_enable_tnf(void)
{
do {
switch (state) {
case DTRACE_VTIME_ACTIVE:
break;
case DTRACE_VTIME_INACTIVE:
break;
case DTRACE_VTIME_ACTIVE_TNF:
panic("TNF already active");
/*NOTREACHED*/
}
}
void
dtrace_vtime_disable_tnf(void)
{
do {
switch (state) {
case DTRACE_VTIME_ACTIVE_TNF:
break;
break;
case DTRACE_VTIME_ACTIVE:
case DTRACE_VTIME_INACTIVE:
panic("TNF already inactive");
/*NOTREACHED*/
}
}
void
{
if (tnf_tracing_active) {
return;
}
ts = dtrace_gethrtime();
if (curthread->t_dtrace_start != 0) {
curthread->t_dtrace_start = 0;
}
}
/*
* This function is called by cfork() in the event that it appears that
* there may be dtrace tracepoints active in the parent process's address
* space. This first confirms the existence of dtrace tracepoints in the
* parent process and calls into the fasttrap module to remove the
* corresponding tracepoints from the child. By knowing that there are
* existing tracepoints, and ensuring they can't be removed, we can rely
* on the fasttrap module remaining loaded.
*/
void
{
ASSERT(p->p_dtrace_count > 0);
}