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