2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <kern/debug.h>
30 #include <kern/kalloc.h>
31 #include <sys/param.h>
34 #include <sys/sysctl.h>
35 #include <libkern/libkern.h>
36 #include <kern/assert.h>
39 #include <sys/ktrace.h>
41 #include <pexpert/pexpert.h>
42 #include <kperf/kperf.h>
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)
59 /* Type-munging casts */
60 typedef int (*getint_t
)(void);
61 typedef int (*setint_t
)(int);
63 static int kpc_initted
= 0;
65 static lck_grp_attr_t
*sysctl_lckgrp_attr
= NULL
;
66 static lck_grp_t
*sysctl_lckgrp
= NULL
;
67 static lck_mtx_t sysctl_lock
;
70 * Another element is needed to hold the CPU number when getting counter values.
72 #define KPC_MAX_BUF_LEN (KPC_MAX_COUNTERS_COPIED + 1)
74 typedef int (*setget_func_t
)(int);
79 sysctl_lckgrp_attr
= lck_grp_attr_alloc_init();
80 sysctl_lckgrp
= lck_grp_alloc_init("kpc", sysctl_lckgrp_attr
);
81 lck_mtx_init(&sysctl_lock
, sysctl_lckgrp
, LCK_ATTR_NULL
);
89 kpc_get_bigarray(uint32_t *size_out
)
91 static uint64_t *bigarray
= NULL
;
93 LCK_MTX_ASSERT(&sysctl_lock
, LCK_MTX_ASSERT_OWNED
);
95 uint32_t size
= kpc_get_counterbuf_size() + sizeof(uint64_t);
103 * Another element is needed to hold the CPU number when getting counter
106 bigarray
= kheap_alloc_tag(KHEAP_DATA_BUFFERS
, size
,
107 Z_WAITOK
, VM_KERN_MEMORY_DIAG
);
108 assert(bigarray
!= NULL
);
112 /* abstract sysctl handlers */
114 sysctl_get_int( struct sysctl_oid
*oidp
, struct sysctl_req
*req
,
119 /* copy out the old value */
120 error
= sysctl_handle_int(oidp
, &value
, 0, req
);
126 sysctl_set_int( struct sysctl_req
*req
, int (*set_func
)(int))
131 error
= SYSCTL_IN( req
, &value
, sizeof(value
));
136 error
= set_func( value
);
142 sysctl_getset_int( struct sysctl_oid
*oidp
, struct sysctl_req
*req
,
143 int (*get_func
)(void), int (*set_func
)(int))
148 /* get the old value and process it */
151 /* copy out the old value, get the new value */
152 error
= sysctl_handle_int(oidp
, &value
, 0, req
);
153 if (error
|| !req
->newptr
) {
157 /* if that worked, and we're writing... */
158 error
= set_func( value
);
165 sysctl_setget_int( struct sysctl_req
*req
,
166 int (*setget_func
)(int))
171 error
= SYSCTL_IN( req
, &value
, sizeof(value
));
176 value
= setget_func(value
);
178 error
= SYSCTL_OUT( req
, &value
, sizeof(value
));
184 sysctl_kpc_get_counters(uint32_t counters
,
185 uint32_t *size
, void *buf
)
187 uint64_t *ctr_buf
= (uint64_t*)buf
;
191 count
= kpc_get_cpu_counters(counters
& KPC_ALL_CPUS
,
193 &curcpu
, &ctr_buf
[1]);
200 *size
= (count
+ 1) * sizeof(uint64_t);
206 sysctl_kpc_get_shadow_counters(uint32_t counters
,
207 uint32_t *size
, void *buf
)
209 uint64_t *ctr_buf
= (uint64_t*)buf
;
213 count
= kpc_get_shadow_counters(counters
& KPC_ALL_CPUS
,
215 &curcpu
, &ctr_buf
[1]);
223 *size
= (count
+ 1) * sizeof(uint64_t);
229 sysctl_kpc_get_thread_counters(uint32_t tid
,
230 uint32_t *size
, void *buf
)
232 uint32_t count
= *size
/ sizeof(uint64_t);
239 r
= kpc_get_curthread_counters(&count
, buf
);
241 *size
= count
* sizeof(uint64_t);
248 sysctl_kpc_get_config(uint32_t classes
, void* buf
)
250 return kpc_get_config( classes
, buf
);
254 sysctl_kpc_set_config(uint32_t classes
, void* buf
)
256 /* userspace cannot reconfigure the power class */
257 if (classes
& KPC_CLASS_POWER_MASK
) {
260 return kpc_set_config( classes
, buf
);
264 sysctl_kpc_get_period(uint32_t classes
, void* buf
)
266 return kpc_get_period( classes
, buf
);
270 sysctl_kpc_set_period(uint32_t classes
, void* buf
)
272 /* userspace cannot reconfigure the power class */
273 if (classes
& KPC_CLASS_POWER_MASK
) {
276 return kpc_set_period( classes
, buf
);
280 sysctl_kpc_get_actionid(uint32_t classes
, void* buf
)
282 return kpc_get_actionid( classes
, buf
);
286 sysctl_kpc_set_actionid(uint32_t classes
, void* buf
)
288 return kpc_set_actionid( classes
, buf
);
293 sysctl_get_bigarray(struct sysctl_req
*req
,
294 int (*get_fn
)(uint32_t, uint32_t*, void*))
296 uint32_t bufsize
= 0;
297 uint64_t *buf
= kpc_get_bigarray(&bufsize
);
300 /* get the argument */
301 int error
= SYSCTL_IN(req
, &arg
, sizeof(arg
));
306 error
= get_fn(arg
, &bufsize
, buf
);
308 error
= SYSCTL_OUT(req
, buf
, bufsize
);
314 /* given a config word, how many bytes does it take? */
316 sysctl_config_size( uint32_t config
)
318 return kpc_get_config_count(config
) * sizeof(kpc_config_t
);
322 sysctl_counter_size( uint32_t classes
)
324 return kpc_get_counter_count(classes
) * sizeof(uint64_t);
328 sysctl_actionid_size( uint32_t classes
)
330 return kpc_get_counter_count(classes
) * sizeof(int32_t);
334 sysctl_getset_bigarray(struct sysctl_req
*req
, int (*size_fn
)(uint32_t arg
),
335 int (*get_fn
)(uint32_t, void*), int (*set_fn
)(uint32_t, void*))
340 uint32_t bufsize
= 0;
341 uint64_t *buf
= kpc_get_bigarray(&bufsize
);
343 /* get the config word */
344 error
= SYSCTL_IN(req
, &arg
, sizeof(arg
));
349 /* Determine the size of registers to modify. */
350 uint32_t regsize
= size_fn((uint32_t)arg
);
351 if (regsize
== 0 || regsize
> bufsize
) {
357 /* copy the rest -- SYSCTL_IN knows the copyin should be shifted */
358 error
= SYSCTL_IN(req
, buf
, regsize
);
360 /* SYSCTL_IN failure means only need to read */
362 error
= set_fn((uint32_t)arg
, buf
);
371 error
= get_fn((uint32_t)arg
, buf
);
376 error
= SYSCTL_OUT(req
, buf
, regsize
);
383 kpc_sysctl SYSCTL_HANDLER_ARGS
387 // __unused struct sysctl_oid *unused_oidp = oidp;
391 panic("kpc_init not called");
394 if (!kpc_supported
) {
400 // Most sysctls require an access check, but a few are public.
401 switch ((uintptr_t) arg1
) {
403 case REQ_CONFIG_COUNT
:
404 case REQ_COUNTER_COUNT
:
405 // These read-only sysctls are public.
409 // Require kperf access to read or write anything else.
410 // This is either root or the blessed pid.
411 if ((ret
= ktrace_read_check())) {
420 lck_mtx_lock(&sysctl_lock
);
423 switch ((uintptr_t) arg1
) {
425 ret
= sysctl_get_int( oidp
, req
,
429 ret
= sysctl_getset_int( oidp
, req
,
430 (getint_t
)kpc_get_running
,
431 (setint_t
)kpc_set_running
);
433 case REQ_THREAD_COUNTING
:
434 ret
= sysctl_getset_int( oidp
, req
,
435 (getint_t
)kpc_get_thread_counting
,
436 (setint_t
)kpc_set_thread_counting
);
439 case REQ_CONFIG_COUNT
:
440 ret
= sysctl_setget_int( req
,
441 (setget_func_t
)kpc_get_config_count
);
444 case REQ_COUNTER_COUNT
:
445 ret
= sysctl_setget_int( req
,
446 (setget_func_t
)kpc_get_counter_count
);
450 case REQ_THREAD_COUNTERS
:
451 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_thread_counters
);
455 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_counters
);
458 case REQ_SHADOW_COUNTERS
:
459 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_shadow_counters
);
463 ret
= sysctl_getset_bigarray( req
,
465 sysctl_kpc_get_config
,
466 sysctl_kpc_set_config
);
470 ret
= sysctl_getset_bigarray( req
,
472 sysctl_kpc_get_period
,
473 sysctl_kpc_set_period
);
477 ret
= sysctl_getset_bigarray( req
,
478 sysctl_actionid_size
,
479 sysctl_kpc_get_actionid
,
480 sysctl_kpc_set_actionid
);
485 ret
= sysctl_set_int( req
, (setget_func_t
)kpc_set_sw_inc
);
488 case REQ_PMU_VERSION
:
489 ret
= sysctl_get_int(oidp
, req
, kpc_get_pmu_version());
497 lck_mtx_unlock(&sysctl_lock
);
503 /*** sysctl definitions ***/
505 /* root kperf node */
506 SYSCTL_NODE(, OID_AUTO
, kpc
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0,
510 SYSCTL_PROC(_kpc
, OID_AUTO
, classes
,
511 CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
513 sizeof(int), kpc_sysctl
, "I", "Available classes");
515 SYSCTL_PROC(_kpc
, OID_AUTO
, counting
,
516 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
518 sizeof(int), kpc_sysctl
, "I", "PMCs counting");
520 SYSCTL_PROC(_kpc
, OID_AUTO
, thread_counting
,
521 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
522 (void*)REQ_THREAD_COUNTING
,
523 sizeof(int), kpc_sysctl
, "I", "Thread accumulation");
525 SYSCTL_PROC(_kpc
, OID_AUTO
, pmu_version
,
526 CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
527 (void *)REQ_PMU_VERSION
,
528 sizeof(int), kpc_sysctl
, "I", "PMU version for hardware");
531 SYSCTL_PROC(_kpc
, OID_AUTO
, config_count
,
532 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
533 (void*)REQ_CONFIG_COUNT
,
534 sizeof(int), kpc_sysctl
, "S", "Config count");
536 SYSCTL_PROC(_kpc
, OID_AUTO
, counter_count
,
537 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
538 (void*)REQ_COUNTER_COUNT
,
539 sizeof(int), kpc_sysctl
, "S", "Counter count");
541 SYSCTL_PROC(_kpc
, OID_AUTO
, sw_inc
,
542 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
544 sizeof(int), kpc_sysctl
, "S", "Software increment");
547 SYSCTL_PROC(_kpc
, OID_AUTO
, thread_counters
,
548 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
549 (void*)REQ_THREAD_COUNTERS
,
550 sizeof(uint64_t), kpc_sysctl
,
551 "QU", "Current thread counters");
553 SYSCTL_PROC(_kpc
, OID_AUTO
, counters
,
554 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
556 sizeof(uint64_t), kpc_sysctl
,
557 "QU", "Current counters");
559 SYSCTL_PROC(_kpc
, OID_AUTO
, shadow_counters
,
560 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
561 (void*)REQ_SHADOW_COUNTERS
,
562 sizeof(uint64_t), kpc_sysctl
,
563 "QU", "Current shadow counters");
565 SYSCTL_PROC(_kpc
, OID_AUTO
, config
,
566 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
568 sizeof(uint64_t), kpc_sysctl
,
569 "QU", "Set counter configs");
571 SYSCTL_PROC(_kpc
, OID_AUTO
, period
,
572 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
574 sizeof(uint64_t), kpc_sysctl
,
575 "QU", "Set counter periods");
577 SYSCTL_PROC(_kpc
, OID_AUTO
, actionid
,
578 CTLFLAG_RD
| CTLFLAG_WR
| CTLFLAG_ANYBODY
| CTLFLAG_MASKED
| CTLFLAG_LOCKED
,
580 sizeof(uint32_t), kpc_sysctl
,
581 "QU", "Set counter actionids");