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 }