]>
Commit | Line | Data |
---|---|---|
316670eb A |
1 | /* |
2 | * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
39037602 | 5 | * |
316670eb A |
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. | |
39037602 | 14 | * |
316670eb A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
39037602 | 17 | * |
316670eb A |
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. | |
39037602 | 25 | * |
316670eb A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | ||
f427ee49 | 29 | /* sysctl interface for parameters from user-land */ |
316670eb | 30 | |
39037602 A |
31 | #include <kern/debug.h> |
32 | #include <libkern/libkern.h> | |
33 | #include <pexpert/pexpert.h> | |
316670eb A |
34 | #include <sys/param.h> |
35 | #include <sys/mman.h> | |
36 | #include <sys/stat.h> | |
37 | #include <sys/sysctl.h> | |
39236c6e | 38 | #include <sys/kauth.h> |
316670eb | 39 | |
316670eb | 40 | #include <kperf/action.h> |
39037602 A |
41 | #include <kperf/context.h> |
42 | #include <kperf/kdebug_trigger.h> | |
316670eb | 43 | #include <kperf/kperf.h> |
39037602 | 44 | #include <kperf/kperfbsd.h> |
f427ee49 | 45 | #include <kperf/kptimer.h> |
39037602 | 46 | #include <kperf/pet.h> |
d9a64523 | 47 | #include <kperf/lazy.h> |
316670eb | 48 | |
39037602 | 49 | #include <sys/ktrace.h> |
39236c6e | 50 | |
d9a64523 A |
51 | /* Requests from kperf sysctls. */ |
52 | enum kperf_request { | |
53 | REQ_SAMPLING, | |
54 | REQ_RESET, | |
55 | ||
56 | REQ_ACTION_COUNT, | |
57 | REQ_ACTION_SAMPLERS, | |
58 | REQ_ACTION_USERDATA, | |
59 | REQ_ACTION_FILTER_BY_TASK, | |
60 | REQ_ACTION_FILTER_BY_PID, | |
61 | REQ_ACTION_UCALLSTACK_DEPTH, | |
62 | REQ_ACTION_KCALLSTACK_DEPTH, | |
63 | ||
64 | REQ_TIMER_COUNT, | |
65 | REQ_TIMER_PERIOD, | |
66 | REQ_TIMER_PET, | |
67 | REQ_TIMER_ACTION, | |
68 | ||
69 | REQ_KDBG_CSWITCH, | |
70 | ||
71 | REQ_BLESS, | |
72 | REQ_BLESS_PREEMPT, | |
73 | ||
74 | REQ_PET_IDLE_RATE, | |
75 | REQ_LIGHTWEIGHT_PET, | |
76 | ||
77 | REQ_KDEBUG_FILTER, | |
78 | REQ_KDEBUG_ACTION, | |
79 | ||
80 | REQ_LAZY_WAIT_TIME_THRESHOLD, | |
81 | REQ_LAZY_WAIT_ACTION, | |
82 | REQ_LAZY_CPU_TIME_THRESHOLD, | |
83 | REQ_LAZY_CPU_ACTION, | |
84 | }; | |
39236c6e | 85 | |
39037602 | 86 | int kperf_debug_level = 0; |
39236c6e | 87 | |
39037602 A |
88 | #if DEVELOPMENT || DEBUG |
89 | _Atomic long long kperf_pending_ipis = 0; | |
90 | #endif /* DEVELOPMENT || DEBUG */ | |
fe8ab488 | 91 | |
39037602 | 92 | /* |
d9a64523 | 93 | * kperf has unique requirements from sysctl. |
39037602 A |
94 | * |
95 | * For simple queries like the number of actions, the normal sysctl style | |
96 | * of get/set works well. | |
39236c6e | 97 | * |
39037602 A |
98 | * However, when requesting information about something specific, like an |
99 | * action, user space needs to provide some contextual information. This | |
100 | * information is stored in a uint64_t array that includes the context, like | |
101 | * the action ID it is interested in. If user space is getting the value from | |
102 | * the kernel, then the get side of the sysctl is valid. If it is setting the | |
103 | * value, then the get pointers are left NULL. | |
39236c6e | 104 | * |
39037602 A |
105 | * These functions handle marshalling and unmarshalling data from sysctls. |
106 | */ | |
39236c6e | 107 | |
39037602 A |
108 | static int |
109 | kperf_sysctl_get_set_uint32(struct sysctl_req *req, | |
0a7de745 | 110 | uint32_t (*get)(void), int (*set)(uint32_t)) |
39236c6e | 111 | { |
39037602 A |
112 | assert(req != NULL); |
113 | assert(get != NULL); | |
114 | assert(set != NULL); | |
39236c6e | 115 | |
39037602 A |
116 | uint32_t value = 0; |
117 | if (req->oldptr) { | |
118 | value = get(); | |
119 | } | |
316670eb | 120 | |
39037602 A |
121 | int error = sysctl_io_number(req, value, sizeof(value), &value, NULL); |
122 | ||
123 | if (error || !req->newptr) { | |
124 | return error; | |
125 | } | |
126 | ||
127 | return set(value); | |
128 | } | |
316670eb A |
129 | |
130 | static int | |
39037602 | 131 | kperf_sysctl_get_set_int(struct sysctl_req *req, |
0a7de745 | 132 | int (*get)(void), int (*set)(int)) |
316670eb | 133 | { |
39037602 A |
134 | assert(req != NULL); |
135 | assert(get != NULL); | |
136 | assert(set != NULL); | |
316670eb | 137 | |
39037602 A |
138 | int value = 0; |
139 | if (req->oldptr) { | |
140 | value = get(); | |
141 | } | |
316670eb | 142 | |
39037602 | 143 | int error = sysctl_io_number(req, value, sizeof(value), &value, NULL); |
39236c6e | 144 | |
39037602 A |
145 | if (error || !req->newptr) { |
146 | return error; | |
147 | } | |
148 | ||
149 | return set(value); | |
39236c6e A |
150 | } |
151 | ||
d9a64523 A |
152 | static int |
153 | kperf_sysctl_get_set_uint64(struct sysctl_req *req, | |
0a7de745 | 154 | uint64_t (*get)(void), int (*set)(uint64_t)) |
d9a64523 A |
155 | { |
156 | assert(req != NULL); | |
157 | assert(get != NULL); | |
158 | assert(set != NULL); | |
159 | ||
160 | uint64_t value = 0; | |
161 | if (req->oldptr) { | |
162 | value = get(); | |
163 | } | |
164 | ||
f427ee49 | 165 | int error = sysctl_io_number(req, (long long)value, sizeof(value), &value, NULL); |
d9a64523 A |
166 | |
167 | if (error || !req->newptr) { | |
168 | return error; | |
169 | } | |
170 | ||
171 | return set(value); | |
172 | } | |
173 | ||
39236c6e | 174 | static int |
39037602 | 175 | kperf_sysctl_get_set_unsigned_uint32(struct sysctl_req *req, |
0a7de745 | 176 | int (*get)(unsigned int, uint32_t *), int (*set)(unsigned int, uint32_t)) |
39236c6e | 177 | { |
39037602 A |
178 | assert(req != NULL); |
179 | assert(get != NULL); | |
180 | assert(set != NULL); | |
181 | ||
5ba3f43e A |
182 | int error = 0; |
183 | uint64_t inputs[2] = {}; | |
184 | ||
185 | if (req->newptr == USER_ADDR_NULL) { | |
186 | return EFAULT; | |
187 | } | |
188 | ||
189 | if ((error = copyin(req->newptr, inputs, sizeof(inputs)))) { | |
39037602 A |
190 | return error; |
191 | } | |
192 | ||
193 | unsigned int action_id = (unsigned int)inputs[0]; | |
194 | uint32_t new_value = (uint32_t)inputs[1]; | |
316670eb | 195 | |
39037602 A |
196 | if (req->oldptr != USER_ADDR_NULL) { |
197 | uint32_t value_out = 0; | |
198 | if ((error = get(action_id, &value_out))) { | |
199 | return error; | |
200 | } | |
39236c6e | 201 | |
39037602 | 202 | inputs[1] = value_out; |
39236c6e | 203 | |
5ba3f43e | 204 | return copyout(inputs, req->oldptr, sizeof(inputs)); |
39037602 | 205 | } else { |
5ba3f43e | 206 | return set(action_id, new_value); |
39037602 | 207 | } |
316670eb A |
208 | } |
209 | ||
39037602 A |
210 | /* |
211 | * These functions are essentially the same as the generic | |
212 | * kperf_sysctl_get_set_unsigned_uint32, except they have unique input sizes. | |
213 | */ | |
214 | ||
316670eb | 215 | static int |
39037602 | 216 | sysctl_timer_period(struct sysctl_req *req) |
316670eb | 217 | { |
5ba3f43e A |
218 | uint64_t inputs[2] = {}; |
219 | ||
5ba3f43e A |
220 | if (req->newptr == USER_ADDR_NULL) { |
221 | return EFAULT; | |
222 | } | |
223 | ||
f427ee49 | 224 | int error = 0; |
5ba3f43e | 225 | if ((error = copyin(req->newptr, inputs, sizeof(inputs)))) { |
39037602 A |
226 | return error; |
227 | } | |
39037602 A |
228 | unsigned int timer = (unsigned int)inputs[0]; |
229 | uint64_t new_period = inputs[1]; | |
316670eb | 230 | |
39037602 A |
231 | if (req->oldptr != USER_ADDR_NULL) { |
232 | uint64_t period_out = 0; | |
f427ee49 | 233 | if ((error = kptimer_get_period(timer, &period_out))) { |
39037602 A |
234 | return error; |
235 | } | |
236 | ||
237 | inputs[1] = period_out; | |
5ba3f43e | 238 | return copyout(inputs, req->oldptr, sizeof(inputs)); |
39037602 | 239 | } else { |
f427ee49 | 240 | return kptimer_set_period(timer, new_period); |
39037602 | 241 | } |
39236c6e A |
242 | } |
243 | ||
244 | static int | |
5ba3f43e | 245 | sysctl_action_filter(struct sysctl_req *req, bool is_task_t) |
39236c6e | 246 | { |
5ba3f43e A |
247 | int error = 0; |
248 | uint64_t inputs[2] = {}; | |
249 | ||
39037602 A |
250 | assert(req != NULL); |
251 | ||
5ba3f43e A |
252 | if (req->newptr == USER_ADDR_NULL) { |
253 | return EFAULT; | |
254 | } | |
255 | ||
256 | if ((error = copyin(req->newptr, inputs, sizeof(inputs)))) { | |
39037602 A |
257 | return error; |
258 | } | |
259 | ||
260 | unsigned int actionid = (unsigned int)inputs[0]; | |
261 | int new_filter = (int)inputs[1]; | |
39236c6e | 262 | |
39037602 A |
263 | if (req->oldptr != USER_ADDR_NULL) { |
264 | int filter_out; | |
265 | if ((error = kperf_action_get_filter(actionid, &filter_out))) { | |
266 | return error; | |
267 | } | |
268 | ||
f427ee49 | 269 | inputs[1] = (uint64_t)filter_out; |
5ba3f43e | 270 | return copyout(inputs, req->oldptr, sizeof(inputs)); |
39037602 A |
271 | } else { |
272 | int pid = is_task_t ? kperf_port_to_pid((mach_port_name_t)new_filter) | |
0a7de745 | 273 | : new_filter; |
39236c6e | 274 | |
5ba3f43e | 275 | return kperf_action_set_filter(actionid, pid); |
39037602 | 276 | } |
39236c6e A |
277 | } |
278 | ||
279 | static int | |
39037602 | 280 | sysctl_bless(struct sysctl_req *req) |
39236c6e | 281 | { |
39037602 A |
282 | int value = ktrace_get_owning_pid(); |
283 | int error = sysctl_io_number(req, value, sizeof(value), &value, NULL); | |
39236c6e | 284 | |
39037602 A |
285 | if (error || !req->newptr) { |
286 | return error; | |
287 | } | |
39236c6e | 288 | |
39037602 | 289 | return ktrace_set_owning_pid(value); |
316670eb A |
290 | } |
291 | ||
39037602 A |
292 | /* sysctl handlers that use the generic functions */ |
293 | ||
316670eb | 294 | static int |
39037602 | 295 | sysctl_action_samplers(struct sysctl_req *req) |
316670eb | 296 | { |
39037602 | 297 | return kperf_sysctl_get_set_unsigned_uint32(req, |
0a7de745 | 298 | kperf_action_get_samplers, kperf_action_set_samplers); |
316670eb A |
299 | } |
300 | ||
301 | static int | |
39037602 | 302 | sysctl_action_userdata(struct sysctl_req *req) |
316670eb | 303 | { |
39037602 | 304 | return kperf_sysctl_get_set_unsigned_uint32(req, |
0a7de745 | 305 | kperf_action_get_userdata, kperf_action_set_userdata); |
316670eb A |
306 | } |
307 | ||
308 | static int | |
39037602 | 309 | sysctl_action_ucallstack_depth(struct sysctl_req *req) |
316670eb | 310 | { |
39037602 | 311 | return kperf_sysctl_get_set_unsigned_uint32(req, |
0a7de745 | 312 | kperf_action_get_ucallstack_depth, kperf_action_set_ucallstack_depth); |
316670eb A |
313 | } |
314 | ||
315 | static int | |
39037602 | 316 | sysctl_action_kcallstack_depth(struct sysctl_req *req) |
316670eb | 317 | { |
39037602 | 318 | return kperf_sysctl_get_set_unsigned_uint32(req, |
0a7de745 | 319 | kperf_action_get_kcallstack_depth, kperf_action_set_kcallstack_depth); |
316670eb A |
320 | } |
321 | ||
39236c6e | 322 | static int |
39037602 | 323 | sysctl_kdebug_action(struct sysctl_req *req) |
39236c6e | 324 | { |
39037602 | 325 | return kperf_sysctl_get_set_int(req, kperf_kdebug_get_action, |
0a7de745 | 326 | kperf_kdebug_set_action); |
39236c6e A |
327 | } |
328 | ||
329 | static int | |
39037602 | 330 | sysctl_kdebug_filter(struct sysctl_req *req) |
39236c6e | 331 | { |
39037602 | 332 | assert(req != NULL); |
39236c6e | 333 | |
39037602 A |
334 | if (req->oldptr != USER_ADDR_NULL) { |
335 | struct kperf_kdebug_filter *filter = NULL; | |
336 | uint32_t n_debugids = kperf_kdebug_get_filter(&filter); | |
337 | size_t filter_size = KPERF_KDEBUG_FILTER_SIZE(n_debugids); | |
39236c6e | 338 | |
39037602 A |
339 | if (n_debugids == 0) { |
340 | return EINVAL; | |
341 | } | |
39236c6e | 342 | |
39037602 | 343 | return SYSCTL_OUT(req, filter, filter_size); |
d9a64523 A |
344 | } else if (req->newptr != USER_ADDR_NULL) { |
345 | return kperf_kdebug_set_filter(req->newptr, (uint32_t)req->newlen); | |
346 | } else { | |
347 | return EINVAL; | |
39037602 | 348 | } |
39236c6e A |
349 | } |
350 | ||
f427ee49 A |
351 | static uint32_t |
352 | kperf_sampling_get(void) | |
353 | { | |
354 | return kperf_is_sampling(); | |
355 | } | |
356 | ||
39236c6e | 357 | static int |
39037602 | 358 | kperf_sampling_set(uint32_t sample_start) |
39236c6e | 359 | { |
39037602 | 360 | if (sample_start) { |
f427ee49 | 361 | return kperf_enable_sampling(); |
39037602 | 362 | } else { |
f427ee49 | 363 | return kperf_disable_sampling(); |
39037602 | 364 | } |
39236c6e A |
365 | } |
366 | ||
367 | static int | |
39037602 | 368 | sysctl_sampling(struct sysctl_req *req) |
39236c6e | 369 | { |
f427ee49 | 370 | return kperf_sysctl_get_set_uint32(req, kperf_sampling_get, |
0a7de745 | 371 | kperf_sampling_set); |
39037602 | 372 | } |
39236c6e | 373 | |
39037602 A |
374 | static int |
375 | sysctl_action_count(struct sysctl_req *req) | |
376 | { | |
377 | return kperf_sysctl_get_set_uint32(req, kperf_action_get_count, | |
0a7de745 | 378 | kperf_action_set_count); |
39037602 | 379 | } |
39236c6e | 380 | |
39037602 A |
381 | static int |
382 | sysctl_timer_count(struct sysctl_req *req) | |
383 | { | |
f427ee49 A |
384 | return kperf_sysctl_get_set_uint32(req, kptimer_get_count, |
385 | kptimer_set_count); | |
39236c6e A |
386 | } |
387 | ||
3e170ce0 | 388 | static int |
39037602 | 389 | sysctl_timer_action(struct sysctl_req *req) |
3e170ce0 | 390 | { |
f427ee49 A |
391 | return kperf_sysctl_get_set_unsigned_uint32(req, kptimer_get_action, |
392 | kptimer_set_action); | |
39037602 | 393 | } |
3e170ce0 | 394 | |
39037602 A |
395 | static int |
396 | sysctl_timer_pet(struct sysctl_req *req) | |
397 | { | |
f427ee49 A |
398 | return kperf_sysctl_get_set_uint32(req, kptimer_get_pet_timerid, |
399 | kptimer_set_pet_timerid); | |
39037602 | 400 | } |
3e170ce0 | 401 | |
39037602 A |
402 | static int |
403 | sysctl_bless_preempt(struct sysctl_req *req) | |
404 | { | |
405 | return sysctl_io_number(req, ktrace_root_set_owner_allowed, | |
0a7de745 A |
406 | sizeof(ktrace_root_set_owner_allowed), |
407 | &ktrace_root_set_owner_allowed, NULL); | |
3e170ce0 A |
408 | } |
409 | ||
410 | static int | |
39037602 | 411 | sysctl_kperf_reset(struct sysctl_req *req) |
3e170ce0 | 412 | { |
39037602 | 413 | int should_reset = 0; |
3e170ce0 | 414 | |
39037602 | 415 | int error = sysctl_io_number(req, should_reset, sizeof(should_reset), |
0a7de745 | 416 | &should_reset, NULL); |
39037602 A |
417 | if (error) { |
418 | return error; | |
419 | } | |
3e170ce0 | 420 | |
39037602 A |
421 | if (should_reset) { |
422 | ktrace_reset(KTRACE_KPERF); | |
423 | } | |
424 | return 0; | |
3e170ce0 A |
425 | } |
426 | ||
427 | static int | |
39037602 | 428 | sysctl_pet_idle_rate(struct sysctl_req *req) |
3e170ce0 | 429 | { |
f427ee49 A |
430 | return kperf_sysctl_get_set_int(req, kppet_get_idle_rate, |
431 | kppet_set_idle_rate); | |
39037602 | 432 | } |
3e170ce0 | 433 | |
39037602 A |
434 | static int |
435 | sysctl_lightweight_pet(struct sysctl_req *req) | |
436 | { | |
f427ee49 A |
437 | return kperf_sysctl_get_set_int(req, kppet_get_lightweight_pet, |
438 | kppet_set_lightweight_pet); | |
39037602 | 439 | } |
3e170ce0 | 440 | |
39037602 A |
441 | static int |
442 | sysctl_kdbg_cswitch(struct sysctl_req *req) | |
443 | { | |
444 | return kperf_sysctl_get_set_int(req, kperf_kdbg_cswitch_get, | |
0a7de745 | 445 | kperf_kdbg_cswitch_set); |
3e170ce0 A |
446 | } |
447 | ||
d9a64523 A |
448 | static int |
449 | sysctl_lazy_wait_time_threshold(struct sysctl_req *req) | |
450 | { | |
451 | return kperf_sysctl_get_set_uint64(req, kperf_lazy_get_wait_time_threshold, | |
0a7de745 | 452 | kperf_lazy_set_wait_time_threshold); |
d9a64523 A |
453 | } |
454 | ||
455 | static int | |
456 | sysctl_lazy_wait_action(struct sysctl_req *req) | |
457 | { | |
458 | return kperf_sysctl_get_set_int(req, kperf_lazy_get_wait_action, | |
0a7de745 | 459 | kperf_lazy_set_wait_action); |
d9a64523 A |
460 | } |
461 | ||
462 | static int | |
463 | sysctl_lazy_cpu_time_threshold(struct sysctl_req *req) | |
464 | { | |
465 | return kperf_sysctl_get_set_uint64(req, kperf_lazy_get_cpu_time_threshold, | |
0a7de745 | 466 | kperf_lazy_set_cpu_time_threshold); |
d9a64523 A |
467 | } |
468 | ||
469 | static int | |
470 | sysctl_lazy_cpu_action(struct sysctl_req *req) | |
471 | { | |
472 | return kperf_sysctl_get_set_int(req, kperf_lazy_get_cpu_action, | |
0a7de745 | 473 | kperf_lazy_set_cpu_action); |
d9a64523 A |
474 | } |
475 | ||
316670eb A |
476 | static int |
477 | kperf_sysctl SYSCTL_HANDLER_ARGS | |
478 | { | |
39037602 | 479 | #pragma unused(oidp, arg2) |
39236c6e | 480 | int ret; |
d9a64523 | 481 | enum kperf_request type = (enum kperf_request)arg1; |
39236c6e | 482 | |
5ba3f43e | 483 | ktrace_lock(); |
39236c6e | 484 | |
39037602 A |
485 | if (req->oldptr == USER_ADDR_NULL && req->newptr != USER_ADDR_NULL) { |
486 | if ((ret = ktrace_configure(KTRACE_KPERF))) { | |
5ba3f43e | 487 | ktrace_unlock(); |
39037602 A |
488 | return ret; |
489 | } | |
490 | } else { | |
491 | if ((ret = ktrace_read_check())) { | |
5ba3f43e | 492 | ktrace_unlock(); |
39037602 A |
493 | return ret; |
494 | } | |
39236c6e A |
495 | } |
496 | ||
316670eb | 497 | /* which request */ |
39037602 | 498 | switch (type) { |
316670eb | 499 | case REQ_ACTION_COUNT: |
39037602 | 500 | ret = sysctl_action_count(req); |
39236c6e | 501 | break; |
316670eb | 502 | case REQ_ACTION_SAMPLERS: |
39037602 | 503 | ret = sysctl_action_samplers(req); |
39236c6e A |
504 | break; |
505 | case REQ_ACTION_USERDATA: | |
39037602 | 506 | ret = sysctl_action_userdata(req); |
39236c6e | 507 | break; |
316670eb | 508 | case REQ_TIMER_COUNT: |
39037602 | 509 | ret = sysctl_timer_count(req); |
39236c6e | 510 | break; |
316670eb | 511 | case REQ_TIMER_PERIOD: |
39037602 | 512 | ret = sysctl_timer_period(req); |
39236c6e | 513 | break; |
316670eb | 514 | case REQ_TIMER_PET: |
39037602 | 515 | ret = sysctl_timer_pet(req); |
39236c6e A |
516 | break; |
517 | case REQ_TIMER_ACTION: | |
39037602 | 518 | ret = sysctl_timer_action(req); |
39236c6e | 519 | break; |
316670eb | 520 | case REQ_SAMPLING: |
39037602 | 521 | ret = sysctl_sampling(req); |
39236c6e | 522 | break; |
3e170ce0 | 523 | case REQ_KDBG_CSWITCH: |
39037602 | 524 | ret = sysctl_kdbg_cswitch(req); |
3e170ce0 | 525 | break; |
39236c6e | 526 | case REQ_ACTION_FILTER_BY_TASK: |
5ba3f43e | 527 | ret = sysctl_action_filter(req, true); |
39236c6e A |
528 | break; |
529 | case REQ_ACTION_FILTER_BY_PID: | |
5ba3f43e | 530 | ret = sysctl_action_filter(req, false); |
39037602 A |
531 | break; |
532 | case REQ_KDEBUG_ACTION: | |
533 | ret = sysctl_kdebug_action(req); | |
534 | break; | |
535 | case REQ_KDEBUG_FILTER: | |
536 | ret = sysctl_kdebug_filter(req); | |
39236c6e A |
537 | break; |
538 | case REQ_PET_IDLE_RATE: | |
39037602 | 539 | ret = sysctl_pet_idle_rate(req); |
39236c6e A |
540 | break; |
541 | case REQ_BLESS_PREEMPT: | |
39037602 A |
542 | ret = sysctl_bless_preempt(req); |
543 | break; | |
544 | case REQ_RESET: | |
545 | ret = sysctl_kperf_reset(req); | |
39236c6e | 546 | break; |
39037602 A |
547 | case REQ_ACTION_UCALLSTACK_DEPTH: |
548 | ret = sysctl_action_ucallstack_depth(req); | |
3e170ce0 | 549 | break; |
39037602 A |
550 | case REQ_ACTION_KCALLSTACK_DEPTH: |
551 | ret = sysctl_action_kcallstack_depth(req); | |
3e170ce0 | 552 | break; |
39037602 A |
553 | case REQ_LIGHTWEIGHT_PET: |
554 | ret = sysctl_lightweight_pet(req); | |
0a7de745 | 555 | break; |
d9a64523 A |
556 | case REQ_LAZY_WAIT_TIME_THRESHOLD: |
557 | ret = sysctl_lazy_wait_time_threshold(req); | |
0a7de745 | 558 | break; |
d9a64523 A |
559 | case REQ_LAZY_WAIT_ACTION: |
560 | ret = sysctl_lazy_wait_action(req); | |
0a7de745 | 561 | break; |
d9a64523 A |
562 | case REQ_LAZY_CPU_TIME_THRESHOLD: |
563 | ret = sysctl_lazy_cpu_time_threshold(req); | |
0a7de745 | 564 | break; |
d9a64523 A |
565 | case REQ_LAZY_CPU_ACTION: |
566 | ret = sysctl_lazy_cpu_action(req); | |
0a7de745 | 567 | break; |
316670eb | 568 | default: |
39236c6e A |
569 | ret = ENOENT; |
570 | break; | |
571 | } | |
572 | ||
5ba3f43e | 573 | ktrace_unlock(); |
39236c6e A |
574 | |
575 | return ret; | |
576 | } | |
577 | ||
578 | static int | |
579 | kperf_sysctl_bless_handler SYSCTL_HANDLER_ARGS | |
580 | { | |
39037602 | 581 | #pragma unused(oidp, arg2) |
39236c6e | 582 | int ret; |
39236c6e | 583 | |
5ba3f43e | 584 | ktrace_lock(); |
39037602 A |
585 | |
586 | /* if setting a new "blessed pid" (ktrace owning pid) */ | |
587 | if (req->newptr != USER_ADDR_NULL) { | |
588 | /* | |
589 | * root can bypass the ktrace check when a flag is set (for | |
590 | * backwards compatibility) or when ownership is maintained over | |
591 | * subsystems resets (to allow the user space process that set | |
592 | * ownership to unset it). | |
593 | */ | |
594 | if (!((ktrace_root_set_owner_allowed || | |
0a7de745 A |
595 | ktrace_keep_ownership_on_reset) && |
596 | kauth_cred_issuser(kauth_cred_get()))) { | |
39037602 | 597 | if ((ret = ktrace_configure(KTRACE_KPERF))) { |
5ba3f43e | 598 | ktrace_unlock(); |
39037602 A |
599 | return ret; |
600 | } | |
601 | } | |
602 | } else { | |
603 | if ((ret = ktrace_read_check())) { | |
5ba3f43e | 604 | ktrace_unlock(); |
39236c6e | 605 | return ret; |
39236c6e A |
606 | } |
607 | } | |
608 | ||
39037602 A |
609 | /* which request */ |
610 | if ((uintptr_t)arg1 == REQ_BLESS) { | |
611 | ret = sysctl_bless(req); | |
612 | } else { | |
613 | ret = ENOENT; | |
316670eb | 614 | } |
39236c6e | 615 | |
5ba3f43e | 616 | ktrace_unlock(); |
fe8ab488 | 617 | |
39037602 | 618 | return ret; |
316670eb A |
619 | } |
620 | ||
621 | /* root kperf node */ | |
39037602 A |
622 | |
623 | SYSCTL_NODE(, OID_AUTO, kperf, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 624 | "kperf"); |
316670eb | 625 | |
39037602 A |
626 | /* actions */ |
627 | ||
628 | SYSCTL_NODE(_kperf, OID_AUTO, action, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 629 | "action"); |
316670eb A |
630 | |
631 | SYSCTL_PROC(_kperf_action, OID_AUTO, count, | |
0a7de745 A |
632 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED | |
633 | CTLFLAG_MASKED, | |
634 | (void *)REQ_ACTION_COUNT, | |
635 | sizeof(int), kperf_sysctl, "I", "Number of actions"); | |
316670eb A |
636 | |
637 | SYSCTL_PROC(_kperf_action, OID_AUTO, samplers, | |
0a7de745 A |
638 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
639 | (void *)REQ_ACTION_SAMPLERS, | |
640 | 3 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
641 | "What to sample when a trigger fires an action"); | |
316670eb | 642 | |
39236c6e | 643 | SYSCTL_PROC(_kperf_action, OID_AUTO, userdata, |
0a7de745 A |
644 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
645 | (void *)REQ_ACTION_USERDATA, | |
646 | 3 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
647 | "User data to attribute to action"); | |
39236c6e A |
648 | |
649 | SYSCTL_PROC(_kperf_action, OID_AUTO, filter_by_task, | |
0a7de745 A |
650 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
651 | (void *)REQ_ACTION_FILTER_BY_TASK, | |
652 | 3 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
653 | "Apply a task filter to the action"); | |
39236c6e A |
654 | |
655 | SYSCTL_PROC(_kperf_action, OID_AUTO, filter_by_pid, | |
0a7de745 A |
656 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
657 | (void *)REQ_ACTION_FILTER_BY_PID, | |
658 | 3 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
659 | "Apply a pid filter to the action"); | |
39236c6e | 660 | |
39037602 | 661 | SYSCTL_PROC(_kperf_action, OID_AUTO, ucallstack_depth, |
0a7de745 A |
662 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
663 | (void *)REQ_ACTION_UCALLSTACK_DEPTH, | |
664 | sizeof(int), kperf_sysctl, "I", | |
665 | "Maximum number of frames to include in user callstacks"); | |
39037602 A |
666 | |
667 | SYSCTL_PROC(_kperf_action, OID_AUTO, kcallstack_depth, | |
0a7de745 A |
668 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
669 | (void *)REQ_ACTION_KCALLSTACK_DEPTH, | |
670 | sizeof(int), kperf_sysctl, "I", | |
671 | "Maximum number of frames to include in kernel callstacks"); | |
39037602 A |
672 | |
673 | /* timers */ | |
674 | ||
675 | SYSCTL_NODE(_kperf, OID_AUTO, timer, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 676 | "timer"); |
316670eb A |
677 | |
678 | SYSCTL_PROC(_kperf_timer, OID_AUTO, count, | |
0a7de745 A |
679 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
680 | | CTLFLAG_MASKED, | |
681 | (void *)REQ_TIMER_COUNT, | |
682 | sizeof(int), kperf_sysctl, "I", "Number of time triggers"); | |
316670eb A |
683 | |
684 | SYSCTL_PROC(_kperf_timer, OID_AUTO, period, | |
0a7de745 A |
685 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
686 | (void *)REQ_TIMER_PERIOD, | |
687 | 2 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
688 | "Timer number and period"); | |
316670eb | 689 | |
39236c6e | 690 | SYSCTL_PROC(_kperf_timer, OID_AUTO, action, |
0a7de745 A |
691 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
692 | (void *)REQ_TIMER_ACTION, | |
693 | 2 * sizeof(uint64_t), kperf_sysctl, "UQ", | |
694 | "Timer number and actionid"); | |
39236c6e | 695 | |
316670eb | 696 | SYSCTL_PROC(_kperf_timer, OID_AUTO, pet_timer, |
0a7de745 A |
697 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
698 | | CTLFLAG_MASKED, | |
699 | (void *)REQ_TIMER_PET, | |
700 | sizeof(int), kperf_sysctl, "I", "Which timer ID does PET"); | |
316670eb | 701 | |
39037602 A |
702 | /* kdebug trigger */ |
703 | ||
704 | SYSCTL_NODE(_kperf, OID_AUTO, kdebug, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 705 | "kdebug"); |
39037602 A |
706 | |
707 | SYSCTL_PROC(_kperf_kdebug, OID_AUTO, action, | |
0a7de745 A |
708 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
709 | | CTLFLAG_MASKED, | |
710 | (void*)REQ_KDEBUG_ACTION, | |
711 | sizeof(int), kperf_sysctl, "I", "ID of action to trigger on kdebug events"); | |
39037602 A |
712 | |
713 | SYSCTL_PROC(_kperf_kdebug, OID_AUTO, filter, | |
0a7de745 A |
714 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
715 | (void*)REQ_KDEBUG_FILTER, | |
716 | sizeof(int), kperf_sysctl, "P", "The filter that determines which kdebug events trigger a sample"); | |
39037602 | 717 | |
d9a64523 A |
718 | /* lazy sampling */ |
719 | ||
720 | SYSCTL_NODE(_kperf, OID_AUTO, lazy, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 721 | "lazy"); |
d9a64523 A |
722 | |
723 | SYSCTL_PROC(_kperf_lazy, OID_AUTO, wait_time_threshold, | |
0a7de745 A |
724 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
725 | (void *)REQ_LAZY_WAIT_TIME_THRESHOLD, | |
726 | sizeof(uint64_t), kperf_sysctl, "UQ", | |
727 | "How many ticks a thread must wait to take a sample"); | |
d9a64523 A |
728 | |
729 | SYSCTL_PROC(_kperf_lazy, OID_AUTO, wait_action, | |
0a7de745 A |
730 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
731 | (void *)REQ_LAZY_WAIT_ACTION, | |
732 | sizeof(uint64_t), kperf_sysctl, "UQ", | |
733 | "Which action to fire when a thread waits longer than threshold"); | |
d9a64523 A |
734 | |
735 | SYSCTL_PROC(_kperf_lazy, OID_AUTO, cpu_time_threshold, | |
0a7de745 A |
736 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
737 | (void *)REQ_LAZY_CPU_TIME_THRESHOLD, | |
738 | sizeof(uint64_t), kperf_sysctl, "UQ", | |
739 | "Minimum number of ticks a CPU must run between samples"); | |
d9a64523 A |
740 | |
741 | SYSCTL_PROC(_kperf_lazy, OID_AUTO, cpu_action, | |
0a7de745 A |
742 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
743 | (void *)REQ_LAZY_CPU_ACTION, | |
744 | sizeof(uint64_t), kperf_sysctl, "UQ", | |
745 | "Which action to fire for lazy CPU samples"); | |
d9a64523 | 746 | |
316670eb | 747 | /* misc */ |
39037602 | 748 | |
316670eb | 749 | SYSCTL_PROC(_kperf, OID_AUTO, sampling, |
0a7de745 A |
750 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
751 | | CTLFLAG_MASKED, | |
752 | (void *)REQ_SAMPLING, | |
753 | sizeof(int), kperf_sysctl, "I", "Sampling running"); | |
316670eb | 754 | |
39037602 | 755 | SYSCTL_PROC(_kperf, OID_AUTO, reset, |
0a7de745 A |
756 | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED, |
757 | (void *)REQ_RESET, | |
758 | 0, kperf_sysctl, "-", "Reset kperf"); | |
39037602 | 759 | |
39236c6e | 760 | SYSCTL_PROC(_kperf, OID_AUTO, blessed_pid, |
0a7de745 A |
761 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED /* must be root */ |
762 | | CTLFLAG_MASKED, | |
763 | (void *)REQ_BLESS, | |
764 | sizeof(int), kperf_sysctl_bless_handler, "I", "Blessed pid"); | |
39236c6e A |
765 | |
766 | SYSCTL_PROC(_kperf, OID_AUTO, blessed_preempt, | |
0a7de745 A |
767 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED | |
768 | CTLFLAG_MASKED, | |
769 | (void *)REQ_BLESS_PREEMPT, | |
770 | sizeof(int), kperf_sysctl, "I", "Blessed preemption"); | |
39236c6e | 771 | |
3e170ce0 | 772 | SYSCTL_PROC(_kperf, OID_AUTO, kdbg_cswitch, |
0a7de745 A |
773 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
774 | | CTLFLAG_MASKED, | |
775 | (void *)REQ_KDBG_CSWITCH, | |
776 | sizeof(int), kperf_sysctl, "I", "Generate context switch info"); | |
39236c6e A |
777 | |
778 | SYSCTL_PROC(_kperf, OID_AUTO, pet_idle_rate, | |
0a7de745 A |
779 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
780 | | CTLFLAG_MASKED, | |
781 | (void *)REQ_PET_IDLE_RATE, | |
782 | sizeof(int), kperf_sysctl, "I", | |
783 | "Rate at which unscheduled threads are forced to be sampled in " | |
784 | "PET mode"); | |
39037602 A |
785 | |
786 | SYSCTL_PROC(_kperf, OID_AUTO, lightweight_pet, | |
0a7de745 A |
787 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED |
788 | | CTLFLAG_MASKED, | |
789 | (void *)REQ_LIGHTWEIGHT_PET, | |
790 | sizeof(int), kperf_sysctl, "I", | |
791 | "Status of lightweight PET mode"); | |
3e170ce0 | 792 | |
5ba3f43e A |
793 | /* limits */ |
794 | ||
795 | SYSCTL_NODE(_kperf, OID_AUTO, limits, CTLFLAG_RW | CTLFLAG_LOCKED, 0, | |
0a7de745 | 796 | "limits"); |
5ba3f43e | 797 | |
d9a64523 A |
798 | enum kperf_limit_request { |
799 | REQ_LIM_PERIOD_NS, | |
800 | REQ_LIM_BG_PERIOD_NS, | |
801 | REQ_LIM_PET_PERIOD_NS, | |
802 | REQ_LIM_BG_PET_PERIOD_NS, | |
803 | }; | |
5ba3f43e A |
804 | |
805 | static int | |
806 | kperf_sysctl_limits SYSCTL_HANDLER_ARGS | |
807 | { | |
808 | #pragma unused(oidp, arg2) | |
f427ee49 A |
809 | enum kptimer_period_limit limit = (enum kptimer_period_limit)arg1; |
810 | if (limit >= KTPL_MAX) { | |
5ba3f43e A |
811 | return ENOENT; |
812 | } | |
f427ee49 A |
813 | uint64_t period = kptimer_minperiods_ns[limit]; |
814 | return sysctl_io_number(req, (long long)period, sizeof(period), &period, | |
815 | NULL); | |
5ba3f43e A |
816 | } |
817 | ||
818 | SYSCTL_PROC(_kperf_limits, OID_AUTO, timer_min_period_ns, | |
0a7de745 A |
819 | CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_LOCKED, |
820 | (void *)REQ_LIM_PERIOD_NS, sizeof(uint64_t), kperf_sysctl_limits, | |
821 | "Q", "Minimum timer period in nanoseconds"); | |
5ba3f43e | 822 | SYSCTL_PROC(_kperf_limits, OID_AUTO, timer_min_bg_period_ns, |
0a7de745 A |
823 | CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_LOCKED, |
824 | (void *)REQ_LIM_BG_PERIOD_NS, sizeof(uint64_t), kperf_sysctl_limits, | |
825 | "Q", "Minimum background timer period in nanoseconds"); | |
5ba3f43e | 826 | SYSCTL_PROC(_kperf_limits, OID_AUTO, timer_min_pet_period_ns, |
0a7de745 A |
827 | CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_LOCKED, |
828 | (void *)REQ_LIM_PET_PERIOD_NS, sizeof(uint64_t), kperf_sysctl_limits, | |
829 | "Q", "Minimum PET timer period in nanoseconds"); | |
5ba3f43e | 830 | SYSCTL_PROC(_kperf_limits, OID_AUTO, timer_min_bg_pet_period_ns, |
0a7de745 A |
831 | CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_ANYBODY | CTLFLAG_LOCKED, |
832 | (void *)REQ_LIM_BG_PET_PERIOD_NS, sizeof(uint64_t), kperf_sysctl_limits, | |
833 | "Q", "Minimum background PET timer period in nanoseconds"); | |
5ba3f43e | 834 | |
39236c6e | 835 | /* debug */ |
39037602 | 836 | SYSCTL_INT(_kperf, OID_AUTO, debug_level, CTLFLAG_RW | CTLFLAG_LOCKED, |
0a7de745 | 837 | &kperf_debug_level, 0, "debug level"); |
39236c6e | 838 | |
39037602 A |
839 | #if DEVELOPMENT || DEBUG |
840 | SYSCTL_QUAD(_kperf, OID_AUTO, already_pending_ipis, | |
0a7de745 A |
841 | CTLFLAG_RD | CTLFLAG_LOCKED, |
842 | &kperf_pending_ipis, ""); | |
39037602 | 843 | #endif /* DEVELOPMENT || DEBUG */ |