Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/dtrace.h>
     28 #include <sys/cmn_err.h>
     29 #include <sys/tnf.h>
     30 #include <sys/atomic.h>
     31 #include <sys/prsystm.h>
     32 #include <sys/modctl.h>
     33 #include <sys/aio_impl.h>
     34 
     35 #ifdef __sparc
     36 #include <sys/privregs.h>
     37 #endif
     38 
     39 void (*dtrace_cpu_init)(processorid_t);
     40 void (*dtrace_modload)(struct modctl *);
     41 void (*dtrace_modunload)(struct modctl *);
     42 void (*dtrace_helpers_cleanup)(void);
     43 void (*dtrace_helpers_fork)(proc_t *, proc_t *);
     44 void (*dtrace_cpustart_init)(void);
     45 void (*dtrace_cpustart_fini)(void);
     46 void (*dtrace_cpc_fire)(uint64_t);
     47 
     48 void (*dtrace_debugger_init)(void);
     49 void (*dtrace_debugger_fini)(void);
     50 
     51 dtrace_vtime_state_t dtrace_vtime_active = 0;
     52 dtrace_cacheid_t dtrace_predcache_id = DTRACE_CACHEIDNONE + 1;
     53 
     54 /*
     55  * dtrace_cpc_in_use usage statement: this global variable is used by the cpc
     56  * hardware overflow interrupt handler and the kernel cpc framework to check
     57  * whether or not the DTrace cpc provider is currently in use. The variable is
     58  * set before counters are enabled with the first enabling and cleared when
     59  * the last enabling is disabled. Its value at any given time indicates the
     60  * number of active dcpc based enablings. The global 'kcpc_cpuctx_lock' rwlock
     61  * is held during initial setting to protect races between kcpc_open() and the
     62  * first enabling. The locking provided by the DTrace subsystem, the kernel
     63  * cpc framework and the cpu management framework protect consumers from race
     64  * conditions on enabling and disabling probes.
     65  */
     66 uint32_t dtrace_cpc_in_use = 0;
     67 
     68 typedef struct dtrace_hrestime {
     69 	lock_t		dthr_lock;		/* lock for this element */
     70 	timestruc_t	dthr_hrestime;		/* hrestime value */
     71 	int64_t		dthr_adj;		/* hrestime_adj value */
     72 	hrtime_t	dthr_hrtime;		/* hrtime value */
     73 } dtrace_hrestime_t;
     74 
     75 static dtrace_hrestime_t dtrace_hrestime[2];
     76 
     77 /*
     78  * Making available adjustable high-resolution time in DTrace is regrettably
     79  * more complicated than one might think it should be.  The problem is that
     80  * the variables related to adjusted high-resolution time (hrestime,
     81  * hrestime_adj and friends) are adjusted under hres_lock -- and this lock may
     82  * be held when we enter probe context.  One might think that we could address
     83  * this by having a single snapshot copy that is stored under a different lock
     84  * from hres_tick(), using the snapshot iff hres_lock is locked in probe
     85  * context.  Unfortunately, this too won't work:  because hres_lock is grabbed
     86  * in more than just hres_tick() context, we could enter probe context
     87  * concurrently on two different CPUs with both locks (hres_lock and the
     88  * snapshot lock) held.  As this implies, the fundamental problem is that we
     89  * need to have access to a snapshot of these variables that we _know_ will
     90  * not be locked in probe context.  To effect this, we have two snapshots
     91  * protected by two different locks, and we mandate that these snapshots are
     92  * recorded in succession by a single thread calling dtrace_hres_tick().  (We
     93  * assure this by calling it out of the same CY_HIGH_LEVEL cyclic that calls
     94  * hres_tick().)  A single thread can't be in two places at once:  one of the
     95  * snapshot locks is guaranteed to be unheld at all times.  The
     96  * dtrace_gethrestime() algorithm is thus to check first one snapshot and then
     97  * the other to find the unlocked snapshot.
     98  */
     99 void
    100 dtrace_hres_tick(void)
    101 {
    102 	int i;
    103 	ushort_t spl;
    104 
    105 	for (i = 0; i < 2; i++) {
    106 		dtrace_hrestime_t tmp;
    107 
    108 		spl = hr_clock_lock();
    109 		tmp.dthr_hrestime = hrestime;
    110 		tmp.dthr_adj = hrestime_adj;
    111 		tmp.dthr_hrtime = dtrace_gethrtime();
    112 		hr_clock_unlock(spl);
    113 
    114 		lock_set(&dtrace_hrestime[i].dthr_lock);
    115 		dtrace_hrestime[i].dthr_hrestime = tmp.dthr_hrestime;
    116 		dtrace_hrestime[i].dthr_adj = tmp.dthr_adj;
    117 		dtrace_hrestime[i].dthr_hrtime = tmp.dthr_hrtime;
    118 		dtrace_membar_producer();
    119 
    120 		/*
    121 		 * To allow for lock-free examination of this lock, we use
    122 		 * the same trick that is used hres_lock; for more details,
    123 		 * see the description of this technique in sun4u/sys/clock.h.
    124 		 */
    125 		dtrace_hrestime[i].dthr_lock++;
    126 	}
    127 }
    128 
    129 hrtime_t
    130 dtrace_gethrestime(void)
    131 {
    132 	dtrace_hrestime_t snap;
    133 	hrtime_t now;
    134 	int i = 0, adj, nslt;
    135 
    136 	for (;;) {
    137 		snap.dthr_lock = dtrace_hrestime[i].dthr_lock;
    138 		dtrace_membar_consumer();
    139 		snap.dthr_hrestime = dtrace_hrestime[i].dthr_hrestime;
    140 		snap.dthr_hrtime = dtrace_hrestime[i].dthr_hrtime;
    141 		snap.dthr_adj = dtrace_hrestime[i].dthr_adj;
    142 		dtrace_membar_consumer();
    143 
    144 		if ((snap.dthr_lock & ~1) == dtrace_hrestime[i].dthr_lock)
    145 			break;
    146 
    147 		/*
    148 		 * If we're here, the lock was either locked, or it
    149 		 * transitioned while we were taking the snapshot.  Either
    150 		 * way, we're going to try the other dtrace_hrestime element;
    151 		 * we know that it isn't possible for both to be locked
    152 		 * simultaneously, so we will ultimately get a good snapshot.
    153 		 */
    154 		i ^= 1;
    155 	}
    156 
    157 	/*
    158 	 * We have a good snapshot.  Now perform any necessary adjustments.
    159 	 */
    160 	nslt = dtrace_gethrtime() - snap.dthr_hrtime;
    161 	ASSERT(nslt >= 0);
    162 
    163 	now = ((hrtime_t)snap.dthr_hrestime.tv_sec * (hrtime_t)NANOSEC) +
    164 	    snap.dthr_hrestime.tv_nsec;
    165 
    166 	if (snap.dthr_adj != 0) {
    167 		if (snap.dthr_adj > 0) {
    168 			adj = (nslt >> adj_shift);
    169 			if (adj > snap.dthr_adj)
    170 				adj = (int)snap.dthr_adj;
    171 		} else {
    172 			adj = -(nslt >> adj_shift);
    173 			if (adj < snap.dthr_adj)
    174 				adj = (int)snap.dthr_adj;
    175 		}
    176 		now += adj;
    177 	}
    178 
    179 	return (now);
    180 }
    181 
    182 void
    183 dtrace_vtime_enable(void)
    184 {
    185 	dtrace_vtime_state_t state, nstate;
    186 
    187 	do {
    188 		state = dtrace_vtime_active;
    189 
    190 		switch (state) {
    191 		case DTRACE_VTIME_INACTIVE:
    192 			nstate = DTRACE_VTIME_ACTIVE;
    193 			break;
    194 
    195 		case DTRACE_VTIME_INACTIVE_TNF:
    196 			nstate = DTRACE_VTIME_ACTIVE_TNF;
    197 			break;
    198 
    199 		case DTRACE_VTIME_ACTIVE:
    200 		case DTRACE_VTIME_ACTIVE_TNF:
    201 			panic("DTrace virtual time already enabled");
    202 			/*NOTREACHED*/
    203 		}
    204 
    205 	} while	(cas32((uint32_t *)&dtrace_vtime_active,
    206 	    state, nstate) != state);
    207 }
    208 
    209 void
    210 dtrace_vtime_disable(void)
    211 {
    212 	dtrace_vtime_state_t state, nstate;
    213 
    214 	do {
    215 		state = dtrace_vtime_active;
    216 
    217 		switch (state) {
    218 		case DTRACE_VTIME_ACTIVE:
    219 			nstate = DTRACE_VTIME_INACTIVE;
    220 			break;
    221 
    222 		case DTRACE_VTIME_ACTIVE_TNF:
    223 			nstate = DTRACE_VTIME_INACTIVE_TNF;
    224 			break;
    225 
    226 		case DTRACE_VTIME_INACTIVE:
    227 		case DTRACE_VTIME_INACTIVE_TNF:
    228 			panic("DTrace virtual time already disabled");
    229 			/*NOTREACHED*/
    230 		}
    231 
    232 	} while	(cas32((uint32_t *)&dtrace_vtime_active,
    233 	    state, nstate) != state);
    234 }
    235 
    236 void
    237 dtrace_vtime_enable_tnf(void)
    238 {
    239 	dtrace_vtime_state_t state, nstate;
    240 
    241 	do {
    242 		state = dtrace_vtime_active;
    243 
    244 		switch (state) {
    245 		case DTRACE_VTIME_ACTIVE:
    246 			nstate = DTRACE_VTIME_ACTIVE_TNF;
    247 			break;
    248 
    249 		case DTRACE_VTIME_INACTIVE:
    250 			nstate = DTRACE_VTIME_INACTIVE_TNF;
    251 			break;
    252 
    253 		case DTRACE_VTIME_ACTIVE_TNF:
    254 		case DTRACE_VTIME_INACTIVE_TNF:
    255 			panic("TNF already active");
    256 			/*NOTREACHED*/
    257 		}
    258 
    259 	} while	(cas32((uint32_t *)&dtrace_vtime_active,
    260 	    state, nstate) != state);
    261 }
    262 
    263 void
    264 dtrace_vtime_disable_tnf(void)
    265 {
    266 	dtrace_vtime_state_t state, nstate;
    267 
    268 	do {
    269 		state = dtrace_vtime_active;
    270 
    271 		switch (state) {
    272 		case DTRACE_VTIME_ACTIVE_TNF:
    273 			nstate = DTRACE_VTIME_ACTIVE;
    274 			break;
    275 
    276 		case DTRACE_VTIME_INACTIVE_TNF:
    277 			nstate = DTRACE_VTIME_INACTIVE;
    278 			break;
    279 
    280 		case DTRACE_VTIME_ACTIVE:
    281 		case DTRACE_VTIME_INACTIVE:
    282 			panic("TNF already inactive");
    283 			/*NOTREACHED*/
    284 		}
    285 
    286 	} while	(cas32((uint32_t *)&dtrace_vtime_active,
    287 	    state, nstate) != state);
    288 }
    289 
    290 void
    291 dtrace_vtime_switch(kthread_t *next)
    292 {
    293 	dtrace_icookie_t cookie;
    294 	hrtime_t ts;
    295 
    296 	if (tnf_tracing_active) {
    297 		tnf_thread_switch(next);
    298 
    299 		if (dtrace_vtime_active == DTRACE_VTIME_INACTIVE_TNF)
    300 			return;
    301 	}
    302 
    303 	cookie = dtrace_interrupt_disable();
    304 	ts = dtrace_gethrtime();
    305 
    306 	if (curthread->t_dtrace_start != 0) {
    307 		curthread->t_dtrace_vtime += ts - curthread->t_dtrace_start;
    308 		curthread->t_dtrace_start = 0;
    309 	}
    310 
    311 	next->t_dtrace_start = ts;
    312 
    313 	dtrace_interrupt_enable(cookie);
    314 }
    315 
    316 void (*dtrace_fasttrap_fork_ptr)(proc_t *, proc_t *);
    317 void (*dtrace_fasttrap_exec_ptr)(proc_t *);
    318 void (*dtrace_fasttrap_exit_ptr)(proc_t *);
    319 
    320 /*
    321  * This function is called by cfork() in the event that it appears that
    322  * there may be dtrace tracepoints active in the parent process's address
    323  * space. This first confirms the existence of dtrace tracepoints in the
    324  * parent process and calls into the fasttrap module to remove the
    325  * corresponding tracepoints from the child. By knowing that there are
    326  * existing tracepoints, and ensuring they can't be removed, we can rely
    327  * on the fasttrap module remaining loaded.
    328  */
    329 void
    330 dtrace_fasttrap_fork(proc_t *p, proc_t *cp)
    331 {
    332 	ASSERT(p->p_proc_flag & P_PR_LOCK);
    333 	ASSERT(p->p_dtrace_count > 0);
    334 	ASSERT(dtrace_fasttrap_fork_ptr != NULL);
    335 
    336 	dtrace_fasttrap_fork_ptr(p, cp);
    337 }
    338