]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_kpc.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / kern / kern_kpc.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <kern/debug.h>
30 #include <sys/param.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 #include <libkern/libkern.h>
35 #include <kern/assert.h>
36
37 #include <kern/kpc.h>
38
39 #include <pexpert/pexpert.h>
40
41 /* Various sysctl requests */
42 #define REQ_CLASSES (1)
43 #define REQ_COUNTING (2)
44 #define REQ_THREAD_COUNTING (3)
45 #define REQ_CONFIG_COUNT (4)
46 #define REQ_COUNTER_COUNT (5)
47 #define REQ_THREAD_COUNTERS (6)
48 #define REQ_COUNTERS (7)
49 #define REQ_SHADOW_COUNTERS (8)
50 #define REQ_CONFIG (9)
51 #define REQ_PERIOD (10)
52 #define REQ_ACTIONID (11)
53 #define REQ_FORCE_ALL_CTRS (12)
54 #define REQ_DISABLE_WHITELIST (13)
55
56 /* Type-munging casts */
57 typedef int (*getint_t)(void);
58 typedef int (*setint_t)(int);
59
60 /* safety */
61 static int kpc_initted = 0;
62
63 /* locking and buffer for large data requests */
64 static lck_grp_attr_t *sysctl_buffer_lckgrp_attr = NULL;
65 static lck_grp_t *sysctl_buffer_lckgrp = NULL;
66 static lck_mtx_t sysctl_buffer_lock;
67 static void *sysctl_buffer = NULL;
68
69 typedef int (*setget_func_t)(int);
70
71 /* init our stuff */
72 extern void kpc_thread_init(void); /* osfmk/kern/kpc_thread.c */
73 extern void kpc_arch_init(void);
74
75 void
76 kpc_init(void)
77 {
78 sysctl_buffer_lckgrp_attr = lck_grp_attr_alloc_init();
79 sysctl_buffer_lckgrp = lck_grp_alloc_init("kpc",
80 sysctl_buffer_lckgrp_attr);
81 lck_mtx_init(&sysctl_buffer_lock, sysctl_buffer_lckgrp, LCK_ATTR_NULL);
82
83 kpc_arch_init();
84 kpc_thread_init();
85
86 kpc_initted = 1;
87 }
88
89 /* abstract sysctl handlers */
90 static int
91 sysctl_get_int( struct sysctl_oid *oidp, struct sysctl_req *req,
92 uint32_t value )
93 {
94 int error = 0;
95
96 /* copy out the old value */
97 error = sysctl_handle_int(oidp, &value, 0, req);
98
99 return error;
100 }
101
102 static int
103 sysctl_getset_int( struct sysctl_oid *oidp, struct sysctl_req *req,
104 int (*get_func)(void), int (*set_func)(int) )
105 {
106 int error = 0;
107 uint32_t value = 0;
108
109 /* get the old value and process it */
110 value = get_func();
111
112 /* copy out the old value, get the new value */
113 error = sysctl_handle_int(oidp, &value, 0, req);
114 if (error || !req->newptr)
115 return (error);
116
117 /* if that worked, and we're writing... */
118 error = set_func( value );
119
120 return error;
121 }
122
123 static int
124 sysctl_setget_int( struct sysctl_req *req,
125 int (*setget_func)(int) )
126 {
127 int error = 0;
128 int value = 0;
129
130 error = SYSCTL_IN( req, &value, sizeof(value) );
131 if( error )
132 return error;
133
134 value = setget_func(value);
135
136 error = SYSCTL_OUT( req, &value, sizeof(value) );
137
138 return error;
139 }
140
141 static int
142 kpc_sysctl_acquire_buffer(void)
143 {
144 if( sysctl_buffer == NULL )
145 sysctl_buffer = kpc_counterbuf_alloc();
146
147 if( !sysctl_buffer )
148 {
149 return ENOMEM;
150 }
151
152 return 0;
153 }
154
155 static int
156 sysctl_kpc_get_counters(uint32_t counters,
157 uint32_t *size, void *buf)
158 {
159 uint64_t *ctr_buf = (uint64_t*)buf;
160 int curcpu;
161 uint32_t count;
162
163 count = kpc_get_cpu_counters(counters & KPC_ALL_CPUS,
164 counters,
165 &curcpu, &ctr_buf[1]);
166 if (!count)
167 return EINVAL;
168
169 ctr_buf[0] = curcpu;
170
171 *size = (count+1) * sizeof(uint64_t);
172
173 return 0;
174 }
175
176 static int
177 sysctl_kpc_get_shadow_counters(uint32_t counters,
178 uint32_t *size, void *buf)
179 {
180 uint64_t *ctr_buf = (uint64_t*)buf;
181 int curcpu;
182 uint32_t count;
183
184 count = kpc_get_shadow_counters(counters & KPC_ALL_CPUS,
185 counters,
186 &curcpu, &ctr_buf[1]);
187
188 if (!count)
189 return EINVAL;
190
191 ctr_buf[0] = curcpu;
192
193 *size = (count+1) * sizeof(uint64_t);
194
195 return 0;
196 }
197
198 static int
199 sysctl_kpc_get_thread_counters(uint32_t tid,
200 uint32_t *size, void *buf)
201 {
202 uint32_t count = *size / sizeof(uint64_t);
203 int r;
204
205 if( tid != 0 )
206 return EINVAL;
207
208 r = kpc_get_curthread_counters(&count, buf);
209 if( !r )
210 *size = count * sizeof(uint64_t);
211
212 return r;
213 }
214
215 static int
216 sysctl_kpc_get_config(uint32_t classes, void* buf)
217 {
218 return kpc_get_config( classes, buf );
219 }
220
221 static int
222 sysctl_kpc_set_config(uint32_t classes, void* buf)
223 {
224 return kpc_set_config( classes, buf);
225 }
226
227 static int
228 sysctl_kpc_get_period(uint32_t classes, void* buf)
229 {
230 return kpc_get_period( classes, buf );
231 }
232
233 static int
234 sysctl_kpc_set_period(uint32_t classes, void* buf)
235 {
236 return kpc_set_period( classes, buf);
237 }
238
239 static int
240 sysctl_kpc_get_actionid(uint32_t classes, void* buf)
241 {
242 return kpc_get_actionid( classes, buf );
243 }
244
245 static int
246 sysctl_kpc_set_actionid(uint32_t classes, void* buf)
247 {
248 return kpc_set_actionid( classes, buf);
249 }
250
251
252 static int
253 sysctl_get_bigarray( struct sysctl_req *req,
254 int (*get_fn)(uint32_t, uint32_t*, void*) )
255 {
256 int error = 0;
257 uint32_t bufsize = KPC_MAX_COUNTERS * sizeof(uint64_t); /* XXX? */
258 uint32_t arg = 0;
259
260 /* get the argument */
261 error = SYSCTL_IN( req, &arg, sizeof(arg) );
262 if(error)
263 {
264 printf( "kpc: no arg?\n" );
265 return error;
266 }
267
268 /* get the wired buffer */
269 error = kpc_sysctl_acquire_buffer();
270 if (error)
271 return error;
272
273 /* atomically get the array into the wired buffer. We have a double
274 * copy, but this is better than page faulting / interrupting during
275 * a copy.
276 */
277 error = get_fn( arg, &bufsize, sysctl_buffer );
278
279 /* do the copy out */
280 if( !error )
281 error = SYSCTL_OUT( req, sysctl_buffer, bufsize );
282
283 return error;
284 }
285
286 /* given a config word, how many bytes does it take? */
287 static int
288 sysctl_config_size( uint32_t config )
289 {
290 return kpc_get_config_count(config) * sizeof(kpc_config_t);
291 }
292
293 static int
294 sysctl_counter_size( uint32_t classes )
295 {
296 return kpc_get_counter_count(classes) * sizeof(uint64_t);
297 }
298
299 static int
300 sysctl_actionid_size( uint32_t classes )
301 {
302 return kpc_get_counter_count(classes) * sizeof(int32_t);
303 }
304
305 static int
306 sysctl_getset_bigarray( struct sysctl_req *req,
307 int (*size_fn)(uint32_t arg),
308 int (*get_fn)(uint32_t, void*),
309 int (*set_fn)(uint32_t, void*) )
310 {
311 int error = 0;
312 uint32_t bufsize = KPC_MAX_COUNTERS * sizeof(uint64_t); /* XXX? */
313 uint32_t regsize = 0;
314 uint64_t arg;
315
316 /* get the config word */
317 error = SYSCTL_IN( req, &arg, sizeof(arg) );
318 if(error)
319 {
320 printf( "kpc: no arg?\n" );
321 return error;
322 }
323
324 /* Work out size of registers */
325 regsize = size_fn((uint32_t)arg);
326
327 /* Ignore NULL requests */
328 if(regsize == 0)
329 return EINVAL;
330
331 /* ensure not too big */
332 if( regsize > bufsize )
333 return EINVAL;
334
335 /* get the wired buffer */
336 error = kpc_sysctl_acquire_buffer();
337 if (error)
338 return error;
339
340 // if writing...
341 if(req->newptr)
342 {
343 // copy in the rest in -- sysctl remembers we did one already
344 error = SYSCTL_IN( req, sysctl_buffer,
345 regsize );
346
347 // if SYSCTL_IN fails it means we are only doing a read
348 if(!error) {
349 // set it
350 error = set_fn( (uint32_t)arg, sysctl_buffer );
351 if( error )
352 goto fail;
353 }
354 }
355
356 // if reading
357 if(req->oldptr)
358 {
359 // read it
360 error = get_fn( (uint32_t)arg, sysctl_buffer );
361 if( error )
362 goto fail;
363
364 // copy out the full set
365 error = SYSCTL_OUT( req, sysctl_buffer, regsize );
366 }
367
368 fail:
369 return error;
370 }
371
372
373
374 /*
375 * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp, \
376 * void *arg1, int arg2, \
377 * struct sysctl_req *req )
378 */
379 static int
380 kpc_sysctl SYSCTL_HANDLER_ARGS
381 {
382 int ret;
383
384 // __unused struct sysctl_oid *unused_oidp = oidp;
385 (void)arg2;
386
387 if( !kpc_initted )
388 panic("kpc_init not called");
389
390 lck_mtx_lock(&sysctl_buffer_lock);
391
392 /* which request */
393 switch( (uintptr_t) arg1 )
394 {
395 case REQ_CLASSES:
396 ret = sysctl_get_int( oidp, req,
397 kpc_get_classes() );
398 break;
399 case REQ_COUNTING:
400 ret = sysctl_getset_int( oidp, req,
401 (getint_t)kpc_get_running,
402 (setint_t)kpc_set_running );
403 break;
404 case REQ_THREAD_COUNTING:
405 ret = sysctl_getset_int( oidp, req,
406 (getint_t)kpc_get_thread_counting,
407 (setint_t)kpc_set_thread_counting );
408 break;
409
410 case REQ_CONFIG_COUNT:
411 ret = sysctl_setget_int( req,
412 (setget_func_t)kpc_get_config_count );
413 break;
414
415 case REQ_COUNTER_COUNT:
416 ret = sysctl_setget_int( req,
417 (setget_func_t)kpc_get_counter_count );
418 break;
419
420
421 case REQ_THREAD_COUNTERS:
422 ret = sysctl_get_bigarray( req, sysctl_kpc_get_thread_counters );
423 break;
424
425 case REQ_COUNTERS:
426 ret = sysctl_get_bigarray( req, sysctl_kpc_get_counters );
427 break;
428
429 case REQ_SHADOW_COUNTERS:
430 ret = sysctl_get_bigarray( req, sysctl_kpc_get_shadow_counters );
431 break;
432
433 case REQ_CONFIG:
434 ret = sysctl_getset_bigarray( req,
435 sysctl_config_size,
436 sysctl_kpc_get_config,
437 sysctl_kpc_set_config );
438 break;
439
440 case REQ_PERIOD:
441 ret = sysctl_getset_bigarray( req,
442 sysctl_counter_size,
443 sysctl_kpc_get_period,
444 sysctl_kpc_set_period );
445 break;
446
447 case REQ_ACTIONID:
448 ret = sysctl_getset_bigarray( req,
449 sysctl_actionid_size,
450 sysctl_kpc_get_actionid,
451 sysctl_kpc_set_actionid );
452 break;
453
454 default:
455 ret = ENOENT;
456 break;
457 }
458
459 lck_mtx_unlock(&sysctl_buffer_lock);
460
461 return ret;
462 }
463
464
465 /*** sysctl definitions ***/
466
467 /* root kperf node */
468 SYSCTL_NODE(, OID_AUTO, kpc, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
469 "kpc");
470
471 /* values */
472 SYSCTL_PROC(_kpc, OID_AUTO, classes,
473 CTLTYPE_INT|CTLFLAG_RD|CTLFLAG_ANYBODY,
474 (void*)REQ_CLASSES,
475 sizeof(int), kpc_sysctl, "I", "Available classes");
476
477 SYSCTL_PROC(_kpc, OID_AUTO, counting,
478 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
479 (void*)REQ_COUNTING,
480 sizeof(int), kpc_sysctl, "I", "PMCs counting");
481
482 SYSCTL_PROC(_kpc, OID_AUTO, thread_counting,
483 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
484 (void*)REQ_THREAD_COUNTING,
485 sizeof(int), kpc_sysctl, "I", "Thread accumulation");
486
487 /* faux values */
488 SYSCTL_PROC(_kpc, OID_AUTO, config_count,
489 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
490 (void*)REQ_CONFIG_COUNT,
491 sizeof(int), kpc_sysctl, "S", "Config count");
492
493 SYSCTL_PROC(_kpc, OID_AUTO, counter_count,
494 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
495 (void*)REQ_COUNTER_COUNT,
496 sizeof(int), kpc_sysctl, "S", "Counter count");
497
498 /* arrays */
499 SYSCTL_PROC(_kpc, OID_AUTO, thread_counters,
500 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
501 (void*)REQ_THREAD_COUNTERS,
502 sizeof(uint64_t), kpc_sysctl,
503 "QU", "Current thread counters");
504
505 SYSCTL_PROC(_kpc, OID_AUTO, counters,
506 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
507 (void*)REQ_COUNTERS,
508 sizeof(uint64_t), kpc_sysctl,
509 "QU", "Current counters");
510
511 SYSCTL_PROC(_kpc, OID_AUTO, shadow_counters,
512 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
513 (void*)REQ_SHADOW_COUNTERS,
514 sizeof(uint64_t), kpc_sysctl,
515 "QU", "Current shadow counters");
516
517 SYSCTL_PROC(_kpc, OID_AUTO, config,
518 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
519 (void*)REQ_CONFIG,
520 sizeof(uint64_t), kpc_sysctl,
521 "QU", "Set counter configs");
522
523 SYSCTL_PROC(_kpc, OID_AUTO, period,
524 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
525 (void*)REQ_PERIOD,
526 sizeof(uint64_t), kpc_sysctl,
527 "QU", "Set counter periods");
528
529 SYSCTL_PROC(_kpc, OID_AUTO, actionid,
530 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
531 (void*)REQ_ACTIONID,
532 sizeof(uint32_t), kpc_sysctl,
533 "QU", "Set counter actionids");