xref: /titanic_52/usr/src/lib/libtnfctl/kernel_int.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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  * Interfaces to control kernel tracing and kernel probes
30  */
31 
32 #ifndef DEBUG
33 #define	NDEBUG	1
34 #endif
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h>	/* for strerror() */
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/tnf.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <assert.h>
47 
48 #include "tnfctl_int.h"
49 #include "kernel_int.h"
50 
51 /* The TNF pseudo-device */
52 #define	TNFDRIVER	"/dev/tnfctl"
53 
54 /* Dummy "test" function  -- just used to flag enabled probes */
55 #define	PRBK_DUMMY_TEST	((tnf_probe_test_func_t) 4)
56 
57 /* Dummy "commit" function -- just used to flag trace enabled */
58 #define	PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8)
59 
60 /* Dummy "rollback" function -- just used to flag trace disabled */
61 #define	PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12)
62 
63 /* Dummy "end" function */
64 #define	PRBK_DUMMY_END ((uintptr_t) 16)
65 
66 /* Dummy "alloc" function */
67 #define	PRBK_DUMMY_ALLOC ((uintptr_t) 20)
68 
69 /* Minimum and maximum allowed buffer sizes. */
70 /* XXX -- maximum should be some function of physmem. */
71 #define	KERNEL_MINBUF_SIZE	(128 * 1024)
72 #define	KERNEL_MAXBUF_SIZE	(128 * 1024 * 1024)
73 
74 static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl);
75 static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe);
76 
77 /*
78  * Initialize the kernel interface:  Open the TNF control device,
79  * and determine the current kernel probes state, including the
80  * current pidfilter list.
81  */
82 tnfctl_errcode_t
83 _tnfctl_prbk_init(tnfctl_handle_t *hdl)
84 {
85 	tnfctl_errcode_t prexstat;
86 	tifiocstate_t kstate;
87 	int kfd;
88 
89 	kfd = open(TNFDRIVER, O_RDWR);
90 	if (kfd < 0) {
91 		return (tnfctl_status_map(errno));
92 	}
93 	if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0)
94 		return (tnfctl_status_map(errno));
95 
96 	hdl->kfd = kfd;
97 	hdl->kpidfilter_state = kstate.pidfilter_mode;
98 	hdl->trace_state = !kstate.trace_stopped;
99 	hdl->trace_min_size = KERNEL_MINBUF_SIZE;
100 	prexstat = prbk_get_buf_attrs(hdl);
101 	if (prexstat)
102 		return (prexstat);
103 
104 	return (TNFCTL_ERR_NONE);
105 }
106 
107 /*
108  * Close the TNF control device.
109  */
110 tnfctl_errcode_t
111 _tnfctl_prbk_close(tnfctl_handle_t *hdl)
112 {
113 	if (hdl == NULL)
114 		return (TNFCTL_ERR_NONE);
115 
116 	if (close(hdl->kfd) == -1) {
117 		return (tnfctl_status_map(errno));
118 	}
119 	return (TNFCTL_ERR_NONE);
120 }
121 
122 /*
123  * Returns function addresses that can be plugged into function pointers
124  * in kernel probes.  These are actually dummy values that get
125  * interpreted by a routine in this file when a probe is flushed.
126  */
127 void
128 _tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp,
129 	uintptr_t *rollbackp, uintptr_t *endp)
130 {
131 	*allocp = PRBK_DUMMY_ALLOC;
132 	*commitp = (uintptr_t) PRBK_DUMMY_COMMIT;
133 	*rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK;
134 	*endp = PRBK_DUMMY_END;
135 }
136 
137 
138 /*
139  * Returns test function address
140  */
141 void
142 _tnfctl_prbk_test_func(uintptr_t *outp)
143 {
144 	*outp = (uintptr_t) PRBK_DUMMY_TEST;
145 }
146 
147 /*
148  * Allocate a trace buffer.  Check for reasonable size; reject if there's
149  * already a buffer.
150  */
151 tnfctl_errcode_t
152 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size)
153 {
154 	tifiocstate_t bufstat;
155 	tnfctl_errcode_t prexstat;
156 	int saved_val;
157 
158 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
159 		return (tnfctl_status_map(errno));
160 	}
161 	if (bufstat.buffer_state != TIFIOCBUF_NONE) {
162 		return (TNFCTL_ERR_BUFEXISTS);
163 	}
164 	if (size < KERNEL_MINBUF_SIZE) {
165 		return (TNFCTL_ERR_SIZETOOSMALL);
166 	} else if (size > KERNEL_MAXBUF_SIZE) {
167 		/* REMIND: make this an error ? */
168 		size = KERNEL_MAXBUF_SIZE;
169 	}
170 	if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) {
171 		saved_val = errno;
172 		(void) prbk_get_buf_attrs(hdl);
173 		return (tnfctl_status_map(saved_val));
174 	}
175 
176 	prexstat = prbk_get_buf_attrs(hdl);
177 	if (prexstat)
178 		return (prexstat);
179 
180 	return (TNFCTL_ERR_NONE);
181 }
182 
183 /*
184  * Deallocate the kernel's trace buffer.
185  */
186 tnfctl_errcode_t
187 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl)
188 {
189 	tifiocstate_t bufstat;
190 	tnfctl_errcode_t prexstat;
191 	int saved_val;
192 
193 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
194 		return (tnfctl_status_map(errno));
195 	}
196 	if (bufstat.buffer_state == TIFIOCBUF_NONE) {
197 		return (TNFCTL_ERR_NOBUF);
198 	}
199 
200 	if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) {
201 		return (TNFCTL_ERR_BADDEALLOC);
202 	}
203 	if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) {
204 		saved_val = errno;
205 		(void) prbk_get_buf_attrs(hdl);
206 		return (tnfctl_status_map(saved_val));
207 	}
208 
209 	prexstat = prbk_get_buf_attrs(hdl);
210 	if (prexstat)
211 		return (prexstat);
212 
213 	return (TNFCTL_ERR_NONE);
214 }
215 
216 /*
217  * Turns kernel global tracing on or off.
218  */
219 tnfctl_errcode_t
220 _tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff)
221 {
222 	if (hdl->trace_state != onoff &&
223 	    ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) {
224 		if (errno == ENOMEM && onoff)
225 			return (TNFCTL_ERR_NOBUF);
226 		else
227 			return (tnfctl_status_map(errno));
228 	}
229 	hdl->trace_state = onoff;
230 	return (TNFCTL_ERR_NONE);
231 }
232 
233 /*
234  * Turn process filter mode on or off.  The process filter is maintained
235  * even when process filtering is off, but has no effect:  all processes
236  * are traced.
237  */
238 tnfctl_errcode_t
239 _tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff)
240 {
241 	if (hdl->kpidfilter_state != onoff &&
242 	    ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) {
243 		return (tnfctl_status_map(errno));
244 	}
245 	hdl->kpidfilter_state = onoff;
246 	return (TNFCTL_ERR_NONE);
247 }
248 
249 /*
250  * Return the process filter list.
251  */
252 tnfctl_errcode_t
253 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p,
254 				int *ret_count)
255 {
256 	tifiocstate_t kstate;
257 	int *filterset;
258 	int i;
259 	pid_t *ret_list;
260 
261 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0)
262 		return (tnfctl_status_map(errno));
263 
264 	if (kstate.pidfilter_size == 0) {
265 		*ret_count = 0;
266 		*ret_list_p = NULL;
267 		return (TNFCTL_ERR_NONE);
268 	}
269 
270 	filterset = (int *) malloc((kstate.pidfilter_size + 1) *
271 					sizeof (pid_t));
272 	if (filterset == NULL)
273 		return (TNFCTL_ERR_ALLOCFAIL);
274 	if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0)
275 		return (tnfctl_status_map(errno));
276 
277 	/* filterset[0] contains size of array */
278 	ret_list = malloc(filterset[0] * sizeof (pid_t));
279 	if (ret_list == NULL)
280 		return (TNFCTL_ERR_ALLOCFAIL);
281 
282 	for (i = 1; i <= filterset[0]; ++i)
283 		ret_list[i - 1] = filterset[i];
284 
285 	*ret_count = filterset[0];
286 	(void) free(filterset);
287 	*ret_list_p = ret_list;
288 	return (TNFCTL_ERR_NONE);
289 }
290 
291 /*
292  * Add the pid to the process filter list.
293  * check whether it's already in the filter list,
294  * and whether the process exists.
295  */
296 tnfctl_errcode_t
297 _tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add)
298 {
299 	if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) {
300 		return (tnfctl_status_map(errno));
301 	}
302 	return (TNFCTL_ERR_NONE);
303 }
304 
305 /*
306  * Drop the pid from the process filter list.
307  */
308 tnfctl_errcode_t
309 _tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del)
310 {
311 	if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) {
312 		if (errno == ESRCH) {
313 			return (TNFCTL_ERR_NOPROCESS);
314 		} else {
315 			return (tnfctl_status_map(errno));
316 		}
317 	}
318 	return (TNFCTL_ERR_NONE);
319 }
320 
321 /*
322  * get the buffer attributes - side effect tnfctl handle
323  */
324 static tnfctl_errcode_t
325 prbk_get_buf_attrs(tnfctl_handle_t *hdl)
326 {
327 	tifiocstate_t bufstat;
328 
329 	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
330 		return (tnfctl_status_map(errno));
331 	}
332 
333 	hdl->trace_file_name = NULL;
334 	hdl->trace_buf_size = bufstat.buffer_size;
335 	if (bufstat.buffer_state == TIFIOCBUF_NONE)
336 		hdl->trace_buf_state = TNFCTL_BUF_NONE;
337 	else if (bufstat.buffer_state == TIFIOCBUF_BROKEN)
338 		hdl->trace_buf_state = TNFCTL_BUF_BROKEN;
339 	else
340 		hdl->trace_buf_state = TNFCTL_BUF_OK;
341 	return (TNFCTL_ERR_NONE);
342 }
343 
344 /*
345  * "Flush" a probe:  i.e., sync up the kernel state with the
346  * (desired) state stored in our data structure.
347  */
348 tnfctl_errcode_t
349 _tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p)
350 {
351 	tnf_probevals_t probebuf;
352 
353 	probebuf.probenum = p->probe_id;
354 	probebuf.enabled = (p->wrkprbctl.test_func != NULL);
355 	probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT);
356 	if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0)
357 		return (tnfctl_status_map(errno));
358 	return (TNFCTL_ERR_NONE);
359 }
360 
361 /*
362  * Refresh our understanding of the existing probes in the kernel.
363  */
364 tnfctl_errcode_t
365 _tnfctl_refresh_kernel(tnfctl_handle_t *hndl)
366 {
367 	int maxprobe, i;
368 	int pos;
369 	tnfctl_errcode_t prexstat;
370 	tnf_probevals_t probebuf;
371 	objlist_t *obj_p;
372 	prbctlref_t *p = NULL;
373 
374 	prexstat = prbk_get_buf_attrs(hndl);
375 	if (prexstat)
376 		return (prexstat);
377 	/*
378 	 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to
379 	 * B_TRUE for all existing objects.  We currently don't need
380 	 * it until we get modload/unload working correctly with probes
381 	 */
382 	if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0)
383 		return (tnfctl_status_map(errno));
384 	if (maxprobe == hndl->num_probes) {
385 		/* XXX Inadequate in the presence of module unloading */
386 		return (TNFCTL_ERR_NONE);
387 	}
388 
389 	prexstat = alloc_probe_space(hndl, maxprobe);
390 	if (prexstat)
391 		return (prexstat);
392 
393 	NOTE(NO_COMPETING_THREADS_NOW)
394 	obj_p = hndl->objlist;
395 	NOTE(COMPETING_THREADS_NOW)
396 	assert((obj_p != NULL) && (obj_p->probes != NULL));
397 
398 	for (i = 1; i <= maxprobe; ++i) {
399 
400 		if (i >= (obj_p->min_probe_num + obj_p->probecnt)) {
401 			obj_p = obj_p->next;
402 		}
403 
404 		/* make sure we are in the correct object */
405 		assert(obj_p != NULL);
406 		assert((i >= obj_p->min_probe_num) &&
407 			(i < (obj_p->min_probe_num + obj_p->probecnt)));
408 
409 		/* get a pointer to correct probe */
410 		pos = i - obj_p->min_probe_num;
411 		p = &(obj_p->probes[pos]);
412 		assert((p != NULL) && (p->probe_id == i) && (p->probe_handle));
413 
414 		probebuf.probenum = i;
415 		if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) {
416 			if (errno == ENOENT) {
417 				/*
418 				 * This probe has vanished due to a module
419 				 * unload.
420 				 */
421 				p->probe_handle->valid = B_FALSE;
422 			} else {
423 				return (tnfctl_status_map(errno));
424 			}
425 		} else {
426 			if (p->probe_handle->valid == B_FALSE) {
427 				/*
428 				 * seeing this probe for the first time
429 				 * (alloc_probe_space() initialized this
430 				 * "valid" field to B_FALSE)
431 				 */
432 				/* Update our info about this probe */
433 				p->wrkprbctl.test_func = (probebuf.enabled) ?
434 					PRBK_DUMMY_TEST : NULL;
435 				p->wrkprbctl.commit_func = (probebuf.traced) ?
436 					PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK;
437 				p->probe_handle->valid = B_TRUE;
438 				if (probebuf.attrsize < sizeof (probebuf))
439 					probebuf.attrsize = sizeof (probebuf);
440 				p->attr_string = malloc(probebuf.attrsize);
441 				if (p->attr_string == NULL)
442 					return (TNFCTL_ERR_ALLOCFAIL);
443 				/*
444 				 * NOTE: the next statement is a structure
445 				 * copy and *not* a pointer assignment
446 				 */
447 /* LINTED pointer cast may result in improper alignment */
448 				*(tnf_probevals_t *) p->attr_string = probebuf;
449 				if (ioctl(hndl->kfd, TIFIOCGPROBESTRING,
450 						p->attr_string) < 0)
451 					return (tnfctl_status_map(errno));
452 				if (hndl->create_func) {
453 				    p->probe_handle->client_registered_data =
454 					hndl->create_func(hndl,
455 						p->probe_handle);
456 				}
457 			}
458 		}
459 	}
460 	hndl->num_probes = maxprobe;
461 	return (TNFCTL_ERR_NONE);
462 }
463 
464 /*
465  * check if there are any new probes in the kernel that we aren't aware of.
466  * If so, allocate space for those probes in our data structure.
467  */
468 static tnfctl_errcode_t
469 alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe)
470 {
471 	objlist_t **o_pp;
472 	objlist_t *obj_p, *nobj_p;
473 	int min_probe_num, i;
474 	prbctlref_t *probe_p;
475 
476 	/* we know that: hndl->maxprobe != maxprobe */
477 	NOTE(NO_COMPETING_THREADS_NOW)
478 	obj_p = hndl->objlist;
479 	NOTE(COMPETING_THREADS_NOW)
480 	if (obj_p == NULL) {
481 		/* no objects allocated */
482 		o_pp = &(hndl->objlist);
483 		min_probe_num = 1;
484 	} else {
485 		/* find last object */
486 		while (obj_p->next != NULL) {
487 			/* reset new_probe field on modload/unload */
488 			obj_p->new_probe = B_FALSE;
489 			obj_p = obj_p->next;
490 		}
491 		o_pp = &(obj_p->next);
492 		min_probe_num = obj_p->min_probe_num + obj_p->probecnt;
493 	}
494 
495 	nobj_p = calloc(1, sizeof (objlist_t));
496 	if (nobj_p == NULL)
497 		return (TNFCTL_ERR_ALLOCFAIL);
498 	/* add to the linked list */
499 	*o_pp = nobj_p;
500 	/* NULL, B_FALSE, or 0's not explicitly initialized */
501 	nobj_p->new_probe = B_TRUE;
502 	nobj_p->new = B_TRUE;
503 	nobj_p->objfd = -1;
504 	nobj_p->min_probe_num = min_probe_num;
505 	nobj_p->probecnt = maxprobe - min_probe_num + 1;
506 	nobj_p->probes = calloc(nobj_p->probecnt,  sizeof (prbctlref_t));
507 	if (nobj_p->probes == NULL) {
508 		free(nobj_p);
509 		return (TNFCTL_ERR_ALLOCFAIL);
510 	}
511 
512 	probe_p = &(nobj_p->probes[0]);
513 	for (i = min_probe_num; i <= maxprobe; i++) {
514 		NOTE(NO_COMPETING_THREADS_NOW)
515 		probe_p->obj = nobj_p;
516 		NOTE(COMPETING_THREADS_NOW)
517 		probe_p->probe_id = i;
518 		probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t));
519 		if (probe_p->probe_handle == NULL) {
520 			if (nobj_p->probes)
521 				free(nobj_p->probes);
522 			free(nobj_p);
523 			return (TNFCTL_ERR_ALLOCFAIL);
524 		}
525 		probe_p->probe_handle->valid = B_FALSE;
526 		probe_p->probe_handle->probe_p = probe_p;
527 		/* link in probe handle into chain off tnfctl_handle_t */
528 		probe_p->probe_handle->next = hndl->probe_handle_list_head;
529 		hndl->probe_handle_list_head = probe_p->probe_handle;
530 
531 		probe_p++;
532 	}
533 
534 	hndl->num_probes = maxprobe;
535 	return (TNFCTL_ERR_NONE);
536 }
537