]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/kern/kern_kpc.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_kpc.c
... / ...
CommitLineData
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 <kern/kalloc.h>
31#include <sys/param.h>
32#include <sys/mman.h>
33#include <sys/stat.h>
34#include <sys/sysctl.h>
35#include <libkern/libkern.h>
36#include <kern/assert.h>
37
38#include <kern/kpc.h>
39#include <sys/ktrace.h>
40
41#include <pexpert/pexpert.h>
42#include <kperf/kperf.h>
43
44/* Various sysctl requests */
45#define REQ_CLASSES (1)
46#define REQ_COUNTING (2)
47#define REQ_THREAD_COUNTING (3)
48#define REQ_CONFIG_COUNT (4)
49#define REQ_COUNTER_COUNT (5)
50#define REQ_THREAD_COUNTERS (6)
51#define REQ_COUNTERS (7)
52#define REQ_SHADOW_COUNTERS (8)
53#define REQ_CONFIG (9)
54#define REQ_PERIOD (10)
55#define REQ_ACTIONID (11)
56#define REQ_SW_INC (14)
57#define REQ_PMU_VERSION (15)
58
59/* Type-munging casts */
60typedef int (*getint_t)(void);
61typedef int (*setint_t)(int);
62
63static int kpc_initted = 0;
64
65static LCK_GRP_DECLARE(sysctl_lckgrp, "kpc");
66static LCK_MTX_DECLARE(sysctl_lock, &sysctl_lckgrp);
67
68/*
69 * Another element is needed to hold the CPU number when getting counter values.
70 */
71#define KPC_MAX_BUF_LEN (KPC_MAX_COUNTERS_COPIED + 1)
72
73typedef int (*setget_func_t)(int);
74
75void
76kpc_init(void)
77{
78 kpc_arch_init();
79
80 kpc_initted = 1;
81}
82
83static uint64_t *
84kpc_get_bigarray(uint32_t *size_out)
85{
86 static uint64_t *bigarray = NULL;
87
88 LCK_MTX_ASSERT(&sysctl_lock, LCK_MTX_ASSERT_OWNED);
89
90 uint32_t size = kpc_get_counterbuf_size() + sizeof(uint64_t);
91 *size_out = size;
92
93 if (bigarray) {
94 return bigarray;
95 }
96
97 /*
98 * Another element is needed to hold the CPU number when getting counter
99 * values.
100 */
101 bigarray = kheap_alloc_tag(KHEAP_DATA_BUFFERS, size,
102 Z_WAITOK, VM_KERN_MEMORY_DIAG);
103 assert(bigarray != NULL);
104 return bigarray;
105}
106
107/* abstract sysctl handlers */
108static int
109sysctl_get_int( struct sysctl_oid *oidp, struct sysctl_req *req,
110 uint32_t value )
111{
112 int error = 0;
113
114 /* copy out the old value */
115 error = sysctl_handle_int(oidp, &value, 0, req);
116
117 return error;
118}
119
120static int
121sysctl_set_int( struct sysctl_req *req, int (*set_func)(int))
122{
123 int error = 0;
124 int value = 0;
125
126 error = SYSCTL_IN( req, &value, sizeof(value));
127 if (error) {
128 return error;
129 }
130
131 error = set_func( value );
132
133 return error;
134}
135
136static int
137sysctl_getset_int( struct sysctl_oid *oidp, struct sysctl_req *req,
138 int (*get_func)(void), int (*set_func)(int))
139{
140 int error = 0;
141 uint32_t value = 0;
142
143 /* get the old value and process it */
144 value = get_func();
145
146 /* copy out the old value, get the new value */
147 error = sysctl_handle_int(oidp, &value, 0, req);
148 if (error || !req->newptr) {
149 return error;
150 }
151
152 /* if that worked, and we're writing... */
153 error = set_func( value );
154
155 return error;
156}
157
158
159static int
160sysctl_setget_int( struct sysctl_req *req,
161 int (*setget_func)(int))
162{
163 int error = 0;
164 int value = 0;
165
166 error = SYSCTL_IN( req, &value, sizeof(value));
167 if (error) {
168 return error;
169 }
170
171 value = setget_func(value);
172
173 error = SYSCTL_OUT( req, &value, sizeof(value));
174
175 return error;
176}
177
178static int
179sysctl_kpc_get_counters(uint32_t counters,
180 uint32_t *size, void *buf)
181{
182 uint64_t *ctr_buf = (uint64_t*)buf;
183 int curcpu;
184 uint32_t count;
185
186 count = kpc_get_cpu_counters(counters & KPC_ALL_CPUS,
187 counters,
188 &curcpu, &ctr_buf[1]);
189 if (!count) {
190 return EINVAL;
191 }
192
193 ctr_buf[0] = curcpu;
194
195 *size = (count + 1) * sizeof(uint64_t);
196
197 return 0;
198}
199
200static int
201sysctl_kpc_get_shadow_counters(uint32_t counters,
202 uint32_t *size, void *buf)
203{
204 uint64_t *ctr_buf = (uint64_t*)buf;
205 int curcpu;
206 uint32_t count;
207
208 count = kpc_get_shadow_counters(counters & KPC_ALL_CPUS,
209 counters,
210 &curcpu, &ctr_buf[1]);
211
212 if (!count) {
213 return EINVAL;
214 }
215
216 ctr_buf[0] = curcpu;
217
218 *size = (count + 1) * sizeof(uint64_t);
219
220 return 0;
221}
222
223static int
224sysctl_kpc_get_thread_counters(uint32_t tid,
225 uint32_t *size, void *buf)
226{
227 uint32_t count = *size / sizeof(uint64_t);
228 int r;
229
230 if (tid != 0) {
231 return EINVAL;
232 }
233
234 r = kpc_get_curthread_counters(&count, buf);
235 if (!r) {
236 *size = count * sizeof(uint64_t);
237 }
238
239 return r;
240}
241
242static int
243sysctl_kpc_get_config(uint32_t classes, void* buf)
244{
245 return kpc_get_config( classes, buf );
246}
247
248static int
249sysctl_kpc_set_config(uint32_t classes, void* buf)
250{
251 /* userspace cannot reconfigure the power class */
252 if (classes & KPC_CLASS_POWER_MASK) {
253 return EPERM;
254 }
255 return kpc_set_config( classes, buf);
256}
257
258static int
259sysctl_kpc_get_period(uint32_t classes, void* buf)
260{
261 return kpc_get_period( classes, buf );
262}
263
264static int
265sysctl_kpc_set_period(uint32_t classes, void* buf)
266{
267 /* userspace cannot reconfigure the power class */
268 if (classes & KPC_CLASS_POWER_MASK) {
269 return EPERM;
270 }
271 return kpc_set_period( classes, buf);
272}
273
274static int
275sysctl_kpc_get_actionid(uint32_t classes, void* buf)
276{
277 return kpc_get_actionid( classes, buf );
278}
279
280static int
281sysctl_kpc_set_actionid(uint32_t classes, void* buf)
282{
283 return kpc_set_actionid( classes, buf);
284}
285
286
287static int
288sysctl_get_bigarray(struct sysctl_req *req,
289 int (*get_fn)(uint32_t, uint32_t*, void*))
290{
291 uint32_t bufsize = 0;
292 uint64_t *buf = kpc_get_bigarray(&bufsize);
293 uint32_t arg = 0;
294
295 /* get the argument */
296 int error = SYSCTL_IN(req, &arg, sizeof(arg));
297 if (error) {
298 return error;
299 }
300
301 error = get_fn(arg, &bufsize, buf);
302 if (!error) {
303 error = SYSCTL_OUT(req, buf, bufsize);
304 }
305
306 return error;
307}
308
309/* given a config word, how many bytes does it take? */
310static int
311sysctl_config_size( uint32_t config )
312{
313 return kpc_get_config_count(config) * sizeof(kpc_config_t);
314}
315
316static int
317sysctl_counter_size( uint32_t classes )
318{
319 return kpc_get_counter_count(classes) * sizeof(uint64_t);
320}
321
322static int
323sysctl_actionid_size( uint32_t classes )
324{
325 return kpc_get_counter_count(classes) * sizeof(int32_t);
326}
327
328static int
329sysctl_getset_bigarray(struct sysctl_req *req, int (*size_fn)(uint32_t arg),
330 int (*get_fn)(uint32_t, void*), int (*set_fn)(uint32_t, void*))
331{
332 int error = 0;
333 uint64_t arg;
334
335 uint32_t bufsize = 0;
336 uint64_t *buf = kpc_get_bigarray(&bufsize);
337
338 /* get the config word */
339 error = SYSCTL_IN(req, &arg, sizeof(arg));
340 if (error) {
341 return error;
342 }
343
344 /* Determine the size of registers to modify. */
345 uint32_t regsize = size_fn((uint32_t)arg);
346 if (regsize == 0 || regsize > bufsize) {
347 return EINVAL;
348 }
349
350 /* if writing */
351 if (req->newptr) {
352 /* copy the rest -- SYSCTL_IN knows the copyin should be shifted */
353 error = SYSCTL_IN(req, buf, regsize);
354
355 /* SYSCTL_IN failure means only need to read */
356 if (!error) {
357 error = set_fn((uint32_t)arg, buf);
358 if (error) {
359 return error;
360 }
361 }
362 }
363
364 /* if reading */
365 if (req->oldptr) {
366 error = get_fn((uint32_t)arg, buf);
367 if (error) {
368 return error;
369 }
370
371 error = SYSCTL_OUT(req, buf, regsize);
372 }
373
374 return error;
375}
376
377static int
378kpc_sysctl SYSCTL_HANDLER_ARGS
379{
380 int ret;
381
382 // __unused struct sysctl_oid *unused_oidp = oidp;
383 (void)arg2;
384
385 if (!kpc_initted) {
386 panic("kpc_init not called");
387 }
388
389 if (!kpc_supported) {
390 return ENOTSUP;
391 }
392
393 ktrace_lock();
394
395 // Most sysctls require an access check, but a few are public.
396 switch ((uintptr_t) arg1) {
397 case REQ_CLASSES:
398 case REQ_CONFIG_COUNT:
399 case REQ_COUNTER_COUNT:
400 // These read-only sysctls are public.
401 break;
402
403 default:
404 // Require kperf access to read or write anything else.
405 // This is either root or the blessed pid.
406 if ((ret = ktrace_read_check())) {
407 ktrace_unlock();
408 return ret;
409 }
410 break;
411 }
412
413 ktrace_unlock();
414
415 lck_mtx_lock(&sysctl_lock);
416
417 /* which request */
418 switch ((uintptr_t) arg1) {
419 case REQ_CLASSES:
420 ret = sysctl_get_int( oidp, req,
421 kpc_get_classes());
422 break;
423 case REQ_COUNTING:
424 ret = sysctl_getset_int( oidp, req,
425 (getint_t)kpc_get_running,
426 (setint_t)kpc_set_running );
427 break;
428 case REQ_THREAD_COUNTING:
429 ret = sysctl_getset_int( oidp, req,
430 (getint_t)kpc_get_thread_counting,
431 (setint_t)kpc_set_thread_counting );
432 break;
433
434 case REQ_CONFIG_COUNT:
435 ret = sysctl_setget_int( req,
436 (setget_func_t)kpc_get_config_count );
437 break;
438
439 case REQ_COUNTER_COUNT:
440 ret = sysctl_setget_int( req,
441 (setget_func_t)kpc_get_counter_count );
442 break;
443
444
445 case REQ_THREAD_COUNTERS:
446 ret = sysctl_get_bigarray( req, sysctl_kpc_get_thread_counters );
447 break;
448
449 case REQ_COUNTERS:
450 ret = sysctl_get_bigarray( req, sysctl_kpc_get_counters );
451 break;
452
453 case REQ_SHADOW_COUNTERS:
454 ret = sysctl_get_bigarray( req, sysctl_kpc_get_shadow_counters );
455 break;
456
457 case REQ_CONFIG:
458 ret = sysctl_getset_bigarray( req,
459 sysctl_config_size,
460 sysctl_kpc_get_config,
461 sysctl_kpc_set_config );
462 break;
463
464 case REQ_PERIOD:
465 ret = sysctl_getset_bigarray( req,
466 sysctl_counter_size,
467 sysctl_kpc_get_period,
468 sysctl_kpc_set_period );
469 break;
470
471 case REQ_ACTIONID:
472 ret = sysctl_getset_bigarray( req,
473 sysctl_actionid_size,
474 sysctl_kpc_get_actionid,
475 sysctl_kpc_set_actionid );
476 break;
477
478
479 case REQ_SW_INC:
480 ret = sysctl_set_int( req, (setget_func_t)kpc_set_sw_inc );
481 break;
482
483 case REQ_PMU_VERSION:
484 ret = sysctl_get_int(oidp, req, kpc_get_pmu_version());
485 break;
486
487 default:
488 ret = ENOENT;
489 break;
490 }
491
492 lck_mtx_unlock(&sysctl_lock);
493
494 return ret;
495}
496
497
498/*** sysctl definitions ***/
499
500/* root kperf node */
501SYSCTL_NODE(, OID_AUTO, kpc, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
502 "kpc");
503
504/* values */
505SYSCTL_PROC(_kpc, OID_AUTO, classes,
506 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
507 (void*)REQ_CLASSES,
508 sizeof(int), kpc_sysctl, "I", "Available classes");
509
510SYSCTL_PROC(_kpc, OID_AUTO, counting,
511 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
512 (void*)REQ_COUNTING,
513 sizeof(int), kpc_sysctl, "I", "PMCs counting");
514
515SYSCTL_PROC(_kpc, OID_AUTO, thread_counting,
516 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
517 (void*)REQ_THREAD_COUNTING,
518 sizeof(int), kpc_sysctl, "I", "Thread accumulation");
519
520SYSCTL_PROC(_kpc, OID_AUTO, pmu_version,
521 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
522 (void *)REQ_PMU_VERSION,
523 sizeof(int), kpc_sysctl, "I", "PMU version for hardware");
524
525/* faux values */
526SYSCTL_PROC(_kpc, OID_AUTO, config_count,
527 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
528 (void*)REQ_CONFIG_COUNT,
529 sizeof(int), kpc_sysctl, "S", "Config count");
530
531SYSCTL_PROC(_kpc, OID_AUTO, counter_count,
532 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
533 (void*)REQ_COUNTER_COUNT,
534 sizeof(int), kpc_sysctl, "S", "Counter count");
535
536SYSCTL_PROC(_kpc, OID_AUTO, sw_inc,
537 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
538 (void*)REQ_SW_INC,
539 sizeof(int), kpc_sysctl, "S", "Software increment");
540
541/* arrays */
542SYSCTL_PROC(_kpc, OID_AUTO, thread_counters,
543 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
544 (void*)REQ_THREAD_COUNTERS,
545 sizeof(uint64_t), kpc_sysctl,
546 "QU", "Current thread counters");
547
548SYSCTL_PROC(_kpc, OID_AUTO, counters,
549 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
550 (void*)REQ_COUNTERS,
551 sizeof(uint64_t), kpc_sysctl,
552 "QU", "Current counters");
553
554SYSCTL_PROC(_kpc, OID_AUTO, shadow_counters,
555 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
556 (void*)REQ_SHADOW_COUNTERS,
557 sizeof(uint64_t), kpc_sysctl,
558 "QU", "Current shadow counters");
559
560SYSCTL_PROC(_kpc, OID_AUTO, config,
561 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
562 (void*)REQ_CONFIG,
563 sizeof(uint64_t), kpc_sysctl,
564 "QU", "Set counter configs");
565
566SYSCTL_PROC(_kpc, OID_AUTO, period,
567 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
568 (void*)REQ_PERIOD,
569 sizeof(uint64_t), kpc_sysctl,
570 "QU", "Set counter periods");
571
572SYSCTL_PROC(_kpc, OID_AUTO, actionid,
573 CTLFLAG_RD | CTLFLAG_WR | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
574 (void*)REQ_ACTIONID,
575 sizeof(uint32_t), kpc_sysctl,
576 "QU", "Set counter actionids");
577
578