1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright (c) 1994, by Sun Microsytems, Inc. 24*0Sstevel@tonic-gate */ 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate /* 29*0Sstevel@tonic-gate * Includes 30*0Sstevel@tonic-gate */ 31*0Sstevel@tonic-gate #include <sys/param.h> 32*0Sstevel@tonic-gate #include <sys/time.h> 33*0Sstevel@tonic-gate #include <sys/debug.h> 34*0Sstevel@tonic-gate #include <sys/cmn_err.h> 35*0Sstevel@tonic-gate #include <sys/tnf.h> 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include "tnf_buf.h" 38*0Sstevel@tonic-gate #include "tnf_types.h" 39*0Sstevel@tonic-gate #include "tnf_trace.h" 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate /* 42*0Sstevel@tonic-gate * Defines 43*0Sstevel@tonic-gate */ 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate #define ENCODED_TAG(tag, tagarg) \ 46*0Sstevel@tonic-gate ((tag) | ((tagarg) & 0xfffc) | TNF_REF32_T_PAIR) 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate /* 49*0Sstevel@tonic-gate * CAUTION: halfword_accessible assumes that the pointer is to a reclaimable 50*0Sstevel@tonic-gate * block - i.e. negative offsets have a 0 in high bit 51*0Sstevel@tonic-gate */ 52*0Sstevel@tonic-gate #define HALFWORD_ACCESSIBLE(x) \ 53*0Sstevel@tonic-gate ((((x) & 0xffff8000) == 0) || (((x) & 0xffff8000) == 0x7fff8000)) 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate /* 56*0Sstevel@tonic-gate * Check that x can be encoded in tagarg slot 57*0Sstevel@tonic-gate * Same as above, but operates on ints (no space bit) 58*0Sstevel@tonic-gate */ 59*0Sstevel@tonic-gate #define TAGARG_CHECK(x) \ 60*0Sstevel@tonic-gate (((x) < 32767) && ((x) > -32768)) 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate /* 63*0Sstevel@tonic-gate * Check that hit 32 bits of hrtime are zero 64*0Sstevel@tonic-gate */ 65*0Sstevel@tonic-gate #define TIME_CHECK(x) \ 66*0Sstevel@tonic-gate (((x) >> 32) == 0) 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate /* 69*0Sstevel@tonic-gate * CAUTION: Use the following macro only when doing a self relative pointer 70*0Sstevel@tonic-gate * to a target in the same block 71*0Sstevel@tonic-gate */ 72*0Sstevel@tonic-gate #define PTR_DIFF(item, ref) \ 73*0Sstevel@tonic-gate ((tnf_ref32_t)((tnf_record_p)(item) - (tnf_record_p)(ref))) 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate /* 76*0Sstevel@tonic-gate * Typedefs 77*0Sstevel@tonic-gate */ 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate typedef struct { 80*0Sstevel@tonic-gate tnf_probe_event_t probe_event; 81*0Sstevel@tonic-gate tnf_time_delta_t time_delta; 82*0Sstevel@tonic-gate } probe_event_prototype_t; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate /* 85*0Sstevel@tonic-gate * Declarations 86*0Sstevel@tonic-gate */ 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate /* 89*0Sstevel@tonic-gate * tnf_trace_alloc 90*0Sstevel@tonic-gate * the probe allocation function 91*0Sstevel@tonic-gate */ 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate void * 94*0Sstevel@tonic-gate tnf_trace_alloc(tnf_ops_t *ops, tnf_probe_control_t *probe_p, 95*0Sstevel@tonic-gate tnf_probe_setup_t *set_p) 96*0Sstevel@tonic-gate { 97*0Sstevel@tonic-gate TNFW_B_WCB *wcb; 98*0Sstevel@tonic-gate uintptr_t probe_index; 99*0Sstevel@tonic-gate tnf_record_p sched_record_p; 100*0Sstevel@tonic-gate tnf_reference_t sched_offset, tag_disp; 101*0Sstevel@tonic-gate tnf_block_header_t *block; 102*0Sstevel@tonic-gate tnf_uint32_t shift; 103*0Sstevel@tonic-gate probe_event_prototype_t *buffer; 104*0Sstevel@tonic-gate hrtime_t curr_time, time_diff; 105*0Sstevel@tonic-gate tnf_schedule_t *sched; 106*0Sstevel@tonic-gate tnf_ref32_t *fwd_p; 107*0Sstevel@tonic-gate size_t size, asize; 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate /* 110*0Sstevel@tonic-gate * Check the "tracing active" flag after setting the busy bit; 111*0Sstevel@tonic-gate * this avoids a race in which we check the "tracing active" 112*0Sstevel@tonic-gate * flag, then it gets turned off, and the buffer gets 113*0Sstevel@tonic-gate * deallocated, before we've set the busy bit. 114*0Sstevel@tonic-gate */ 115*0Sstevel@tonic-gate if (!lock_try(&ops->busy)) /* atomic op flushes WB */ 116*0Sstevel@tonic-gate return (NULL); 117*0Sstevel@tonic-gate if (!tnf_tracing_active) 118*0Sstevel@tonic-gate goto null_ret; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate /* 121*0Sstevel@tonic-gate * Write probe tag if needed 122*0Sstevel@tonic-gate */ 123*0Sstevel@tonic-gate probe_index = probe_p->index; 124*0Sstevel@tonic-gate if (probe_index == 0) { 125*0Sstevel@tonic-gate if ((probe_index = tnf_probe_tag(ops, probe_p)) == 0) 126*0Sstevel@tonic-gate goto null_ret; 127*0Sstevel@tonic-gate } 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate /* 130*0Sstevel@tonic-gate * Determine how much memory is required 131*0Sstevel@tonic-gate */ 132*0Sstevel@tonic-gate size = probe_p->tnf_event_size; 133*0Sstevel@tonic-gate asize = size + sizeof (tnf_ref32_t); /* one fwd ptr */ 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate if (PROBE_IS_FILE_PTR(probe_index)) 136*0Sstevel@tonic-gate /* common case - probe_index is a file ptr */ 137*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 32-bit integer */ 138*0Sstevel@tonic-gate tag_disp = probe_index & PROBE_INDEX_LOW_MASK; 139*0Sstevel@tonic-gate else 140*0Sstevel@tonic-gate /* rare case -- get an extra fwd ptr */ 141*0Sstevel@tonic-gate asize += sizeof (tnf_ref32_t); 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate /* 144*0Sstevel@tonic-gate * Allocate memory 145*0Sstevel@tonic-gate */ 146*0Sstevel@tonic-gate wcb = &ops->wcb; 147*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 16-bit integer */ 148*0Sstevel@tonic-gate TNFW_B_ALLOC(wcb, asize, buffer, probe_event_prototype_t *); 149*0Sstevel@tonic-gate if (buffer == NULL) 150*0Sstevel@tonic-gate goto null_ret; 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate /* LINTED pointer cast may result in improper alignment */ 153*0Sstevel@tonic-gate fwd_p = (tnf_ref32_t *)((char *)buffer + size); 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * Check if the probe tag needs more work 157*0Sstevel@tonic-gate */ 158*0Sstevel@tonic-gate if (!PROBE_IS_FILE_PTR(probe_index)) { 159*0Sstevel@tonic-gate /* use up first fwd ptr */ 160*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 32-bit integer */ 161*0Sstevel@tonic-gate *fwd_p = TNF_REF32_MAKE_PERMANENT( 162*0Sstevel@tonic-gate (tnf_record_p)probe_index - tnf_buf); 163*0Sstevel@tonic-gate /* LINTED cast from 64-bit integer to 32-bit integer */ 164*0Sstevel@tonic-gate tag_disp = PTR_DIFF(fwd_p, buffer); 165*0Sstevel@tonic-gate tag_disp |= TNF_TAG16_T_REL; 166*0Sstevel@tonic-gate tag_disp = tag_disp << TNF_REF32_TAG16_SHIFT; 167*0Sstevel@tonic-gate fwd_p++; 168*0Sstevel@tonic-gate } 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate /* 171*0Sstevel@tonic-gate * Get timestamp 172*0Sstevel@tonic-gate */ 173*0Sstevel@tonic-gate curr_time = gethrtime(); 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate /* 176*0Sstevel@tonic-gate * Write schedule record if needed 177*0Sstevel@tonic-gate */ 178*0Sstevel@tonic-gate sched = &ops->schedule; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate /* LINTED pointer cast */ 181*0Sstevel@tonic-gate shift = ((tnf_buf_file_header_t *)tnf_buf)->com.file_log_size; 182*0Sstevel@tonic-gate block = (tnf_block_header_t *)((uintptr_t)buffer & TNF_BLOCK_MASK); 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate if ((sched_record_p = sched->record_p) == NULL) 185*0Sstevel@tonic-gate /* No record written yet */ 186*0Sstevel@tonic-gate goto new_schedule; 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate /* 189*0Sstevel@tonic-gate * Note: Don't bother about space bit here, because we'll 190*0Sstevel@tonic-gate * only use bits 15:2 anyway 191*0Sstevel@tonic-gate */ 192*0Sstevel@tonic-gate #if defined(_LP64) 193*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 32-bit integer */ 194*0Sstevel@tonic-gate sched_offset = ((sched->record_gen - block->generation) << shift) + 195*0Sstevel@tonic-gate (sched_record_p - (caddr_t)buffer); 196*0Sstevel@tonic-gate #else 197*0Sstevel@tonic-gate sched_offset = ((sched->record_gen - block->generation) << shift) + 198*0Sstevel@tonic-gate (sched_record_p - (caddr_t)buffer); 199*0Sstevel@tonic-gate #endif 200*0Sstevel@tonic-gate if (!TAGARG_CHECK(sched_offset)) 201*0Sstevel@tonic-gate /* Record too far away to reference */ 202*0Sstevel@tonic-gate goto new_schedule; 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate time_diff = curr_time - sched->time_base; 205*0Sstevel@tonic-gate if (!TIME_CHECK(time_diff)) 206*0Sstevel@tonic-gate /* Time delta can't fit in 32 bits */ 207*0Sstevel@tonic-gate goto new_schedule; 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate if (sched->cpuid != CPU->cpu_id) 210*0Sstevel@tonic-gate /* CPU information is invalid */ 211*0Sstevel@tonic-gate goto new_schedule; 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate /* 214*0Sstevel@tonic-gate * Can reuse existing schedule record 215*0Sstevel@tonic-gate * Since we did not allocate any more space, can giveback 216*0Sstevel@tonic-gate */ 217*0Sstevel@tonic-gate #if defined(_LP64) 218*0Sstevel@tonic-gate /* LINTED warning: assignment of 64-bit integer to 16-bit integer */ 219*0Sstevel@tonic-gate TNFW_B_GIVEBACK(wcb, fwd_p); 220*0Sstevel@tonic-gate #else 221*0Sstevel@tonic-gate TNFW_B_GIVEBACK(wcb, fwd_p); 222*0Sstevel@tonic-gate #endif 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate good_ret: 225*0Sstevel@tonic-gate /* 226*0Sstevel@tonic-gate * Store return params and two common event members, return buffer 227*0Sstevel@tonic-gate */ 228*0Sstevel@tonic-gate set_p->tpd_p = ops; 229*0Sstevel@tonic-gate set_p->buffer_p = buffer; 230*0Sstevel@tonic-gate set_p->probe_p = probe_p; 231*0Sstevel@tonic-gate buffer->probe_event = ENCODED_TAG(tag_disp, sched_offset); 232*0Sstevel@tonic-gate #if defined(_LP64) 233*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 32-bit integer */ 234*0Sstevel@tonic-gate buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff, 235*0Sstevel@tonic-gate &buffer->probe_time_delta); 236*0Sstevel@tonic-gate #else 237*0Sstevel@tonic-gate buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff, 238*0Sstevel@tonic-gate &buffer->probe_time_delta); 239*0Sstevel@tonic-gate #endif 240*0Sstevel@tonic-gate return (buffer); 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate new_schedule: 243*0Sstevel@tonic-gate /* 244*0Sstevel@tonic-gate * Write a new schedule record for this thread 245*0Sstevel@tonic-gate */ 246*0Sstevel@tonic-gate sched->cpuid = CPU->cpu_id; 247*0Sstevel@tonic-gate sched->time_base = curr_time; 248*0Sstevel@tonic-gate time_diff = 0; 249*0Sstevel@tonic-gate if ((sched_record_p = tnf_kernel_schedule(ops, sched)) != NULL) { 250*0Sstevel@tonic-gate /* use one of the extra alloced words for the forwarding ptr */ 251*0Sstevel@tonic-gate #if defined(_LP64) 252*0Sstevel@tonic-gate /* LINTED assignment of 64-bit integer to 32-bit integer */ 253*0Sstevel@tonic-gate *fwd_p = TNF_REF32_MAKE_RECLAIMABLE( 254*0Sstevel@tonic-gate ((sched->record_gen - block->generation) << shift) + 255*0Sstevel@tonic-gate /* LINTED */ 256*0Sstevel@tonic-gate (sched_record_p - (tnf_record_p)fwd_p)); 257*0Sstevel@tonic-gate /* LINTED cast from 64-bit integer to 32-bit integer */ 258*0Sstevel@tonic-gate sched_offset = PTR_DIFF(fwd_p, buffer); 259*0Sstevel@tonic-gate #else 260*0Sstevel@tonic-gate *fwd_p = TNF_REF32_MAKE_RECLAIMABLE( 261*0Sstevel@tonic-gate ((sched->record_gen - block->generation) << shift) + 262*0Sstevel@tonic-gate (sched_record_p - (tnf_record_p)fwd_p)); 263*0Sstevel@tonic-gate sched_offset = PTR_DIFF(fwd_p, buffer); 264*0Sstevel@tonic-gate #endif 265*0Sstevel@tonic-gate } else { 266*0Sstevel@tonic-gate /* Allocation failed (tracing may have been stopped) */ 267*0Sstevel@tonic-gate sched_offset = 0; 268*0Sstevel@tonic-gate *fwd_p = TNF_NULL; 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate goto good_ret; 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate null_ret: 273*0Sstevel@tonic-gate /* 274*0Sstevel@tonic-gate * Clear busy flag and return null 275*0Sstevel@tonic-gate */ 276*0Sstevel@tonic-gate LOCK_INIT_CLEAR(&ops->busy); /* XXX save a call */ 277*0Sstevel@tonic-gate return (NULL); 278*0Sstevel@tonic-gate } 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate /* 281*0Sstevel@tonic-gate * tnf_trace_commit 282*0Sstevel@tonic-gate */ 283*0Sstevel@tonic-gate void 284*0Sstevel@tonic-gate tnf_trace_commit(tnf_probe_setup_t *set_p) 285*0Sstevel@tonic-gate { 286*0Sstevel@tonic-gate tnf_ops_t *ops; 287*0Sstevel@tonic-gate TNFW_B_WCB *wcb; 288*0Sstevel@tonic-gate TNFW_B_POS *pos; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate ops = set_p->tpd_p; 291*0Sstevel@tonic-gate wcb = &ops->wcb; 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate /* commit reusable bytes */ 294*0Sstevel@tonic-gate pos = &wcb->tnfw_w_pos; 295*0Sstevel@tonic-gate TNFW_B_COMMIT(pos); 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate /* commit tag bytes */ 298*0Sstevel@tonic-gate pos = &wcb->tnfw_w_tag_pos; 299*0Sstevel@tonic-gate TNFW_B_COMMIT(pos); 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate /* clear busy flag */ 302*0Sstevel@tonic-gate LOCK_INIT_CLEAR(&ops->busy); /* XXX save a call */ 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate /* 306*0Sstevel@tonic-gate * tnf_trace_rollback 307*0Sstevel@tonic-gate */ 308*0Sstevel@tonic-gate void 309*0Sstevel@tonic-gate tnf_trace_rollback(tnf_probe_setup_t *set_p) 310*0Sstevel@tonic-gate { 311*0Sstevel@tonic-gate tnf_ops_t *ops; 312*0Sstevel@tonic-gate TNFW_B_WCB *wcb; 313*0Sstevel@tonic-gate TNFW_B_POS *pos; 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate ops = set_p->tpd_p; 316*0Sstevel@tonic-gate wcb = &ops->wcb; 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate /* rollback data bytes */ 319*0Sstevel@tonic-gate pos = &wcb->tnfw_w_pos; 320*0Sstevel@tonic-gate TNFW_B_ROLLBACK(pos); 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate /* commit tag bytes */ 323*0Sstevel@tonic-gate pos = &wcb->tnfw_w_tag_pos; 324*0Sstevel@tonic-gate TNFW_B_COMMIT(pos); 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate /* zap schedule record, since it is in uncommitted store */ 327*0Sstevel@tonic-gate ops->schedule.record_p = NULL; 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate /* clear busy flag */ 330*0Sstevel@tonic-gate LOCK_INIT_CLEAR(&ops->busy); /* XXX save a call */ 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate /* 334*0Sstevel@tonic-gate * tnf_allocate 335*0Sstevel@tonic-gate * exported interface for allocating trace memory 336*0Sstevel@tonic-gate */ 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate void * 339*0Sstevel@tonic-gate tnf_allocate(tnf_ops_t *ops, size_t size) 340*0Sstevel@tonic-gate { 341*0Sstevel@tonic-gate return (tnfw_b_alloc(&ops->wcb, size, ops->mode)); 342*0Sstevel@tonic-gate } 343