1 /*
2 * Copyright (c) 2010-2020 Apple Computer, 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 * @OSF_COPYRIGHT@
30 */
31
32 #include <kern/kern_types.h>
33 #include <kern/ledger.h>
34 #include <kern/kalloc.h>
35 #include <kern/task.h>
36 #include <kern/thread.h>
37 #include <kern/coalition.h>
38
39 #include <kern/processor.h>
40 #include <kern/machine.h>
41 #include <kern/queue.h>
42 #include <kern/policy_internal.h>
43
44 #include <sys/errno.h>
45
46 #include <libkern/OSAtomic.h>
47 #include <mach/mach_types.h>
48 #include <os/overflow.h>
49
50 #include <vm/pmap.h>
51
52 /*
53 * Ledger entry flags. Bits in second nibble (masked by 0xF0) are used for
54 * ledger actions (LEDGER_ACTION_BLOCK, etc).
55 */
56 #define LF_ENTRY_ACTIVE 0x0001 /* entry is active if set */
57 #define LF_WAKE_NEEDED 0x0100 /* one or more threads are asleep */
58 #define LF_WAKE_INPROGRESS 0x0200 /* the wait queue is being processed */
59 #define LF_REFILL_SCHEDULED 0x0400 /* a refill timer has been set */
60 #define LF_REFILL_INPROGRESS 0x0800 /* the ledger is being refilled */
61 #define LF_CALLED_BACK 0x1000 /* callback was called for balance in deficit */
62 #define LF_WARNED 0x2000 /* callback was called for balance warning */
63 #define LF_TRACKING_MAX 0x4000 /* track max balance. Exclusive w.r.t refill */
64 #define LF_PANIC_ON_NEGATIVE 0x8000 /* panic if it goes negative */
65 #define LF_TRACK_CREDIT_ONLY 0x10000 /* only update "credit" */
66
67
68 /*
69 * Ledger entry IDs are actually a tuple of (size, offset).
70 * For backwards compatibility, they're stored in an int.
71 * Size is stored in the upper 16 bits, and offset is stored in the lower 16 bits.
72 *
73 * Use the ENTRY_ID_SIZE and ENTRY_ID_OFFSET macros to extract size and offset.
74 */
75 #define ENTRY_ID_SIZE_SHIFT 16
76 #define ENTRY_ID_OFFSET_MASK ((1 << ENTRY_ID_SIZE_SHIFT) - 1)
77 #define ENTRY_ID_OFFSET(x) ((x) & (ENTRY_ID_OFFSET_MASK))
78 #define ENTRY_ID_SIZE_MASK (ENTRY_ID_OFFSET_MASK << ENTRY_ID_SIZE_SHIFT)
79 #define ENTRY_ID_SIZE(x) ((((uint32_t) (x)) & (ENTRY_ID_SIZE_MASK)) >> ENTRY_ID_SIZE_SHIFT)
80 _Static_assert(((sizeof(struct ledger_entry_small) << ENTRY_ID_SIZE_SHIFT) | (UINT16_MAX / sizeof(struct ledger_entry_small))) > 0, "Valid ledger index < 0");
81 _Static_assert(((sizeof(struct ledger_entry) << ENTRY_ID_SIZE_SHIFT) | (UINT16_MAX / sizeof(struct ledger_entry_small))) > 0, "Valid ledger index < 0");
82 _Static_assert(sizeof(int) * 8 >= ENTRY_ID_SIZE_SHIFT * 2, "Ledger indices don't fit in an int.");
83 #define MAX_LEDGER_ENTRIES (UINT16_MAX / sizeof(struct ledger_entry_small))
84
85 /* These features can fit in a small ledger entry. All others require a full size ledger entry */
86 #define LEDGER_ENTRY_SMALL_FLAGS (LEDGER_ENTRY_ALLOW_PANIC_ON_NEGATIVE | LEDGER_ENTRY_ALLOW_INACTIVE)
87
88 /* Turn on to debug invalid ledger accesses */
89 #if MACH_ASSERT
90 #define PANIC_ON_INVALID_LEDGER_ACCESS 1
91 #endif /* MACH_ASSERT */
92
93 static inline volatile uint32_t *
get_entry_flags(ledger_t l,int index)94 get_entry_flags(ledger_t l, int index)
95 {
96 assert(l != NULL);
97
98 uint16_t size, offset;
99 size = ENTRY_ID_SIZE(index);
100 offset = ENTRY_ID_OFFSET(index);
101 struct ledger_entry_small *les = &l->l_entries[offset];
102 if (size == sizeof(struct ledger_entry)) {
103 return &((struct ledger_entry *)les)->le_flags;
104 } else if (size == sizeof(struct ledger_entry_small)) {
105 return &les->les_flags;
106 } else {
107 panic("Unknown ledger entry size! ledger=%p, index=0x%x, entry_size=%d\n", l, index, size);
108 }
109 }
110
111 #if PANIC_ON_INVALID_LEDGER_ACCESS
112 #define INVALID_LEDGER_ACCESS(l, e) if ((e) != -1) panic("Invalid ledger access: ledger=%p, entry=0x%x, entry_size=0x%x, entry_offset=0x%x\n", \
113 (l), (e), (ENTRY_ID_SIZE((e))), ENTRY_ID_OFFSET((e)));
114 #else
115 #define INVALID_LEDGER_ACCESS(l, e)
116 #endif /* PANIC_ON_INVALID_LEDGER_ACCESS */
117
118 /* Determine whether a ledger entry exists */
119 static inline bool
is_entry_valid(ledger_t l,int entry)120 is_entry_valid(ledger_t l, int entry)
121 {
122 uint32_t size, offset, end_offset;
123 size = ENTRY_ID_SIZE(entry);
124 offset = ENTRY_ID_OFFSET(entry);
125 if (l == NULL) {
126 return false;
127 }
128 if (os_mul_overflow(offset, sizeof(struct ledger_entry_small), &offset) || offset >= l->l_size) {
129 INVALID_LEDGER_ACCESS(l, entry);
130 return false;
131 }
132 if (os_add_overflow(size, offset, &end_offset) || end_offset > l->l_size) {
133 INVALID_LEDGER_ACCESS(l, entry);
134 return false;
135 }
136 return true;
137 }
138
139 static inline bool
is_entry_active(ledger_t l,int entry)140 is_entry_active(ledger_t l, int entry)
141 {
142 uint32_t flags = *get_entry_flags(l, entry);
143 if ((flags & LF_ENTRY_ACTIVE) != LF_ENTRY_ACTIVE) {
144 return false;
145 }
146
147 return true;
148 }
149
150 static inline bool
is_entry_valid_and_active(ledger_t l,int entry)151 is_entry_valid_and_active(ledger_t l, int entry)
152 {
153 return is_entry_valid(l, entry) && is_entry_active(l, entry);
154 }
155
156 #define ASSERT(a) assert(a)
157
158 #ifdef LEDGER_DEBUG
159 int ledger_debug = 0;
160
161 #define lprintf(a) if (ledger_debug) { \
162 printf("%lld ", abstime_to_nsecs(mach_absolute_time() / 1000000)); \
163 printf a ; \
164 }
165 #else
166 #define lprintf(a)
167 #endif
168
169 struct ledger_callback {
170 ledger_callback_t lc_func;
171 const void *lc_param0;
172 const void *lc_param1;
173 };
174
175 struct entry_template {
176 char et_key[LEDGER_NAME_MAX];
177 char et_group[LEDGER_NAME_MAX];
178 char et_units[LEDGER_NAME_MAX];
179 uint32_t et_flags;
180 uint16_t et_size;
181 uint16_t et_offset;
182 struct ledger_callback *et_callback;
183 };
184
185 LCK_GRP_DECLARE(ledger_lck_grp, "ledger");
186 os_refgrp_decl(static, ledger_refgrp, "ledger", NULL);
187
188 /*
189 * Modifying the reference count, table size, table contents, lt_next_offset, or lt_entries_lut,
190 * requires holding the lt_lock. Modfying the table address requires both
191 * lt_lock and setting the inuse bit. This means that the lt_entries field can
192 * be safely dereferenced if you hold either the lock or the inuse bit. The
193 * inuse bit exists solely to allow us to swap in a new, larger entries
194 * table without requiring a full lock to be acquired on each lookup.
195 * Accordingly, the inuse bit should never be held for longer than it takes
196 * to extract a value from the table - i.e., 2 or 3 memory references.
197 */
198 struct ledger_template {
199 const char *lt_name;
200 int lt_refs;
201 volatile uint32_t lt_inuse;
202 lck_mtx_t lt_lock;
203 zone_t lt_zone;
204 bool lt_initialized;
205 uint16_t lt_next_offset;
206 uint16_t lt_cnt;
207 uint16_t lt_table_size;
208 struct entry_template *lt_entries;
209 /* Lookup table to go from entry_offset to index in the lt_entries table. */
210 uint16_t *lt_entries_lut;
211 };
212
213 static inline uint16_t
ledger_template_entries_lut_size(uint16_t lt_table_size)214 ledger_template_entries_lut_size(uint16_t lt_table_size)
215 {
216 /*
217 * The lookup table needs to be big enough to store lt_table_size entries of the largest
218 * entry size (struct ledger_entry) given a stride of the smallest entry size (struct ledger_entry_small)
219 */
220 if (os_mul_overflow(lt_table_size, (sizeof(struct ledger_entry) / sizeof(struct ledger_entry_small)), <_table_size)) {
221 /*
222 * This means MAX_LEDGER_ENTRIES is misconfigured or
223 * someone has accidently passed in an lt_table_size that is > MAX_LEDGER_ENTRIES
224 */
225 panic("Attempt to create a lookup table for a ledger template with too many entries. lt_table_size=%u, MAX_LEDGER_ENTRIES=%lu\n", lt_table_size, MAX_LEDGER_ENTRIES);
226 }
227 return lt_table_size;
228 }
229
230 #define template_lock(template) lck_mtx_lock(&(template)->lt_lock)
231 #define template_unlock(template) lck_mtx_unlock(&(template)->lt_lock)
232
233 #define TEMPLATE_INUSE(s, t) { \
234 s = splsched(); \
235 while (OSCompareAndSwap(0, 1, &((t)->lt_inuse))) \
236 ; \
237 }
238
239 #define TEMPLATE_IDLE(s, t) { \
240 (t)->lt_inuse = 0; \
241 splx(s); \
242 }
243
244 static int ledger_cnt = 0;
245 /* ledger ast helper functions */
246 static uint32_t ledger_check_needblock(ledger_t l, uint64_t now);
247 static kern_return_t ledger_perform_blocking(ledger_t l);
248 static uint32_t flag_set(volatile uint32_t *flags, uint32_t bit);
249 static uint32_t flag_clear(volatile uint32_t *flags, uint32_t bit);
250
251 static void ledger_entry_check_new_balance(thread_t thread, ledger_t ledger,
252 int entry);
253
254 #if 0
255 static void
256 debug_callback(const void *p0, __unused const void *p1)
257 {
258 printf("ledger: resource exhausted [%s] for task %p\n",
259 (const char *)p0, p1);
260 }
261 #endif
262
263 /************************************/
264
265 static uint64_t
abstime_to_nsecs(uint64_t abstime)266 abstime_to_nsecs(uint64_t abstime)
267 {
268 uint64_t nsecs;
269
270 absolutetime_to_nanoseconds(abstime, &nsecs);
271 return nsecs;
272 }
273
274 static uint64_t
nsecs_to_abstime(uint64_t nsecs)275 nsecs_to_abstime(uint64_t nsecs)
276 {
277 uint64_t abstime;
278
279 nanoseconds_to_absolutetime(nsecs, &abstime);
280 return abstime;
281 }
282
283 static const uint16_t *
ledger_entry_to_template_idx(ledger_template_t template,int index)284 ledger_entry_to_template_idx(ledger_template_t template, int index)
285 {
286 uint16_t offset = ENTRY_ID_OFFSET(index);
287 if (offset / sizeof(struct ledger_entry_small) >= template->lt_cnt) {
288 return NULL;
289 }
290
291 return &template->lt_entries_lut[offset];
292 }
293
294 /*
295 * Convert the id to a ledger entry.
296 * It's the callers responsibility to ensure the id is valid and a full size
297 * ledger entry.
298 */
299 static struct ledger_entry *
ledger_entry_identifier_to_entry(ledger_t ledger,int id)300 ledger_entry_identifier_to_entry(ledger_t ledger, int id)
301 {
302 assert(is_entry_valid(ledger, id));
303 assert(ENTRY_ID_SIZE(id) == sizeof(struct ledger_entry));
304 return (struct ledger_entry *) &ledger->l_entries[ENTRY_ID_OFFSET(id)];
305 }
306
307
308 ledger_template_t
ledger_template_create(const char * name)309 ledger_template_create(const char *name)
310 {
311 ledger_template_t template;
312
313 template = kalloc_type(struct ledger_template, Z_WAITOK | Z_ZERO | Z_NOFAIL);
314 template->lt_name = name;
315 template->lt_refs = 1;
316 template->lt_table_size = 1;
317 lck_mtx_init(&template->lt_lock, &ledger_lck_grp, LCK_ATTR_NULL);
318
319 template->lt_entries = kalloc_type(struct entry_template,
320 template->lt_table_size, Z_WAITOK | Z_ZERO);
321 if (template->lt_entries == NULL) {
322 kfree_type(struct ledger_template, template);
323 template = NULL;
324 }
325 template->lt_entries_lut = kalloc_type(uint16_t, ledger_template_entries_lut_size(template->lt_table_size),
326 Z_WAITOK | Z_ZERO);
327 if (template->lt_entries_lut == NULL) {
328 kfree_type(struct entry_template, template->lt_entries);
329 kfree_type(struct ledger_template, template);
330 template = NULL;
331 }
332
333 return template;
334 }
335
336 ledger_template_t
ledger_template_copy(ledger_template_t template,const char * name)337 ledger_template_copy(ledger_template_t template, const char *name)
338 {
339 struct entry_template * new_entries = NULL;
340 uint16_t *new_entries_lut = NULL;
341 size_t new_entries_lut_size = 0;
342 ledger_template_t new_template = ledger_template_create(name);
343
344 if (new_template == NULL) {
345 return new_template;
346 }
347
348 template_lock(template);
349 assert(template->lt_initialized);
350
351 new_entries = kalloc_type(struct entry_template, template->lt_table_size,
352 Z_WAITOK | Z_ZERO);
353
354 if (new_entries == NULL) {
355 /* Tear down the new template; we've failed. :( */
356 ledger_template_dereference(new_template);
357 new_template = NULL;
358 goto out;
359 }
360 new_entries_lut_size = ledger_template_entries_lut_size(template->lt_table_size);
361
362 new_entries_lut = kalloc_type(uint16_t, new_entries_lut_size,
363 Z_WAITOK | Z_ZERO);
364 if (new_entries_lut == NULL) {
365 /* Tear down the new template; we've failed. :( */
366 ledger_template_dereference(new_template);
367 new_template = NULL;
368 goto out;
369 }
370
371 /* Copy the template entries. */
372 bcopy(template->lt_entries, new_entries, sizeof(struct entry_template) * template->lt_table_size);
373 kfree_type(struct entry_template, new_template->lt_table_size, new_template->lt_entries);
374 /* Copy the look up table. */
375 bcopy(template->lt_entries_lut, new_entries_lut, sizeof(uint16_t) * new_entries_lut_size);
376 kfree_type(uint16_t, ledger_template_entries_lut_size(new_template->lt_table_size), new_template->lt_entries_lut);
377
378 new_template->lt_entries = new_entries;
379 new_template->lt_table_size = template->lt_table_size;
380 new_template->lt_cnt = template->lt_cnt;
381 new_template->lt_next_offset = template->lt_next_offset;
382 new_template->lt_entries_lut = new_entries_lut;
383
384 out:
385 template_unlock(template);
386
387 return new_template;
388 }
389
390 void
ledger_template_dereference(ledger_template_t template)391 ledger_template_dereference(ledger_template_t template)
392 {
393 template_lock(template);
394 template->lt_refs--;
395 template_unlock(template);
396
397 if (template->lt_refs == 0) {
398 kfree_type(struct entry_template, template->lt_table_size, template->lt_entries);
399 kfree_type(uint16_t, ledger_template_entries_lut_size(template->lt_table_size), template->lt_entries_lut);
400 lck_mtx_destroy(&template->lt_lock, &ledger_lck_grp);
401 if (template->lt_zone) {
402 zdestroy(template->lt_zone);
403 }
404 kfree_type(struct ledger_template, template);
405 }
406 }
407
408 static inline int
ledger_entry_id(uint16_t size,uint16_t offset)409 ledger_entry_id(uint16_t size, uint16_t offset)
410 {
411 int idx = offset;
412 idx |= (size << ENTRY_ID_SIZE_SHIFT);
413 assert(idx >= 0);
414 return idx;
415 }
416
417 static inline int
ledger_entry_id_from_template_entry(const struct entry_template * et)418 ledger_entry_id_from_template_entry(const struct entry_template *et)
419 {
420 return ledger_entry_id(et->et_size, et->et_offset);
421 }
422
423 int
ledger_entry_add_with_flags(ledger_template_t template,const char * key,const char * group,const char * units,uint64_t flags)424 ledger_entry_add_with_flags(ledger_template_t template, const char *key,
425 const char *group, const char *units, uint64_t flags)
426 {
427 uint16_t template_idx;
428 struct entry_template *et;
429 uint16_t size = 0, next_offset = 0, entry_idx = 0;
430
431 if ((key == NULL) || (strlen(key) >= LEDGER_NAME_MAX) || (template->lt_zone != NULL)) {
432 return -1;
433 }
434
435 template_lock(template);
436
437 /* Make sure we have space for this entry */
438 if (template->lt_cnt == MAX_LEDGER_ENTRIES) {
439 template_unlock(template);
440 return -1;
441 }
442
443 /* If the table is full, attempt to double its size */
444 if (template->lt_cnt == template->lt_table_size) {
445 struct entry_template *new_entries, *old_entries;
446 uint16_t *new_entries_lut = NULL, *old_entries_lut = NULL;
447 uint16_t old_cnt, new_cnt;
448 spl_t s;
449
450 old_cnt = template->lt_table_size;
451 /* double old_sz allocation, but check for overflow */
452 if (os_mul_overflow(old_cnt, 2, &new_cnt)) {
453 template_unlock(template);
454 return -1;
455 }
456
457 if (new_cnt > MAX_LEDGER_ENTRIES) {
458 template_unlock(template);
459 panic("Attempt to create a ledger template with more than MAX_LEDGER_ENTRIES. MAX_LEDGER_ENTRIES=%lu, old_cnt=%u, new_cnt=%u\n", MAX_LEDGER_ENTRIES, old_cnt, new_cnt);
460 }
461
462 new_entries = kalloc_type(struct entry_template, new_cnt,
463 Z_WAITOK | Z_ZERO);
464 if (new_entries == NULL) {
465 template_unlock(template);
466 return -1;
467 }
468 new_entries_lut = kalloc_type(uint16_t, ledger_template_entries_lut_size(new_cnt),
469 Z_WAITOK | Z_ZERO);
470 if (new_entries_lut == NULL) {
471 template_unlock(template);
472 kfree_type(struct entry_template, new_cnt, new_entries);
473 return -1;
474 }
475
476 memcpy(new_entries, template->lt_entries,
477 old_cnt * sizeof(struct entry_template));
478 template->lt_table_size = new_cnt;
479
480 memcpy(new_entries_lut, template->lt_entries_lut,
481 ledger_template_entries_lut_size(old_cnt) * sizeof(uint16_t));
482
483 old_entries = template->lt_entries;
484 old_entries_lut = template->lt_entries_lut;
485
486 TEMPLATE_INUSE(s, template);
487 template->lt_entries = new_entries;
488 template->lt_entries_lut = new_entries_lut;
489 TEMPLATE_IDLE(s, template);
490
491 kfree_type(struct entry_template, old_cnt, old_entries);
492 kfree_type(uint16_t, ledger_template_entries_lut_size(old_cnt), old_entries_lut);
493 }
494
495 et = &template->lt_entries[template->lt_cnt];
496 strlcpy(et->et_key, key, LEDGER_NAME_MAX);
497 strlcpy(et->et_group, group, LEDGER_NAME_MAX);
498 strlcpy(et->et_units, units, LEDGER_NAME_MAX);
499 et->et_flags = LF_ENTRY_ACTIVE;
500 /*
501 * Currently we only have two types of variable sized entries
502 * CREDIT_ONLY and full-fledged leger_entry.
503 * In the future, we can add more gradations based on the flags.
504 */
505 if ((flags & ~(LEDGER_ENTRY_SMALL_FLAGS)) == 0) {
506 size = sizeof(struct ledger_entry_small);
507 et->et_flags |= LF_TRACK_CREDIT_ONLY;
508 } else {
509 size = sizeof(struct ledger_entry);
510 }
511 et->et_size = size;
512 et->et_offset = (template->lt_next_offset / sizeof(struct ledger_entry_small));
513 et->et_callback = NULL;
514
515 template_idx = template->lt_cnt++;
516 next_offset = template->lt_next_offset;
517 entry_idx = next_offset / sizeof(struct ledger_entry_small);
518 template->lt_next_offset += size;
519 assert(template->lt_next_offset > next_offset);
520 template->lt_entries_lut[entry_idx] = template_idx;
521 template_unlock(template);
522
523 return ledger_entry_id(size, entry_idx);
524 }
525
526 /*
527 * Add a new entry to the list of entries in a ledger template. There is
528 * currently no mechanism to remove an entry. Implementing such a mechanism
529 * would require us to maintain per-entry reference counts, which we would
530 * prefer to avoid if possible.
531 */
532 int
ledger_entry_add(ledger_template_t template,const char * key,const char * group,const char * units)533 ledger_entry_add(ledger_template_t template, const char *key,
534 const char *group, const char *units)
535 {
536 /*
537 * When using the legacy interface we have to be pessimistic
538 * and allocate memory for all of the features.
539 */
540 return ledger_entry_add_with_flags(template, key, group, units,
541 LEDGER_ENTRY_ALLOW_CALLBACK | LEDGER_ENTRY_ALLOW_MAXIMUM |
542 LEDGER_ENTRY_ALLOW_DEBIT | LEDGER_ENTRY_ALLOW_LIMIT |
543 LEDGER_ENTRY_ALLOW_ACTION | LEDGER_ENTRY_ALLOW_INACTIVE);
544 }
545
546
547 kern_return_t
ledger_entry_setactive(ledger_t ledger,int entry)548 ledger_entry_setactive(ledger_t ledger, int entry)
549 {
550 volatile uint32_t *flags = NULL;
551
552 if (!is_entry_valid(ledger, entry)) {
553 return KERN_INVALID_ARGUMENT;
554 }
555
556 flags = get_entry_flags(ledger, entry);
557
558 if ((*flags & LF_ENTRY_ACTIVE) == 0) {
559 flag_set(flags, LF_ENTRY_ACTIVE);
560 }
561 return KERN_SUCCESS;
562 }
563
564
565 int
ledger_key_lookup(ledger_template_t template,const char * key)566 ledger_key_lookup(ledger_template_t template, const char *key)
567 {
568 int id = -1;
569 struct entry_template *et = NULL;
570
571 template_lock(template);
572 if (template->lt_entries != NULL) {
573 for (uint16_t idx = 0; idx < template->lt_cnt; idx++) {
574 et = &template->lt_entries[idx];
575 if (strcmp(key, et->et_key) == 0) {
576 id = ledger_entry_id(et->et_size, et->et_offset);
577 break;
578 }
579 }
580 }
581
582 template_unlock(template);
583
584 return id;
585 }
586
587 /*
588 * Complete the initialization of ledger template
589 * by initializing ledger zone. After initializing
590 * the ledger zone, adding an entry in the ledger
591 * template will fail.
592 */
593 void
ledger_template_complete(ledger_template_t template)594 ledger_template_complete(ledger_template_t template)
595 {
596 size_t ledger_size;
597 ledger_size = sizeof(struct ledger) + template->lt_next_offset;
598 assert(ledger_size > sizeof(struct ledger));
599 template->lt_zone = zone_create(template->lt_name, ledger_size,
600 ZC_PGZ_USE_GUARDS | ZC_DESTRUCTIBLE);
601 template->lt_initialized = true;
602 }
603
604 /*
605 * Like ledger_template_complete, except we'll ask
606 * the pmap layer to manage allocations for us.
607 * Meant for ledgers that should be owned by the
608 * pmap layer.
609 */
610 void
ledger_template_complete_secure_alloc(ledger_template_t template)611 ledger_template_complete_secure_alloc(ledger_template_t template)
612 {
613 size_t ledger_size;
614 ledger_size = sizeof(struct ledger) + template->lt_next_offset;
615
616 /**
617 * Ensure that the amount of space being allocated by the PPL for each
618 * ledger is large enough.
619 */
620 pmap_ledger_verify_size(ledger_size);
621 template->lt_initialized = true;
622 }
623
624 /*
625 * Create a new ledger based on the specified template. As part of the
626 * ledger creation we need to allocate space for a table of ledger entries.
627 * The size of the table is based on the size of the template at the time
628 * the ledger is created. If additional entries are added to the template
629 * after the ledger is created, they will not be tracked in this ledger.
630 */
631 ledger_t
ledger_instantiate(ledger_template_t template,int entry_type)632 ledger_instantiate(ledger_template_t template, int entry_type)
633 {
634 ledger_t ledger;
635 uint16_t entries_size;
636 uint16_t num_entries;
637 uint16_t i;
638
639 template_lock(template);
640 template->lt_refs++;
641 entries_size = template->lt_next_offset;
642 num_entries = template->lt_cnt;
643 template_unlock(template);
644
645 if (template->lt_zone) {
646 ledger = (ledger_t)zalloc(template->lt_zone);
647 } else {
648 /**
649 * If the template doesn't contain a zone to allocate ledger objects
650 * from, then assume that these ledger objects should be allocated by
651 * the pmap. This is done on PPL-enabled systems to give the PPL a
652 * method of validating ledger objects when updating them from within
653 * the PPL.
654 */
655 ledger = pmap_ledger_alloc();
656 }
657
658 if (ledger == NULL) {
659 ledger_template_dereference(template);
660 return LEDGER_NULL;
661 }
662
663 ledger->l_template = template;
664 ledger->l_id = ledger_cnt++;
665 os_ref_init(&ledger->l_refs, &ledger_refgrp);
666 assert(entries_size > 0);
667 ledger->l_size = (uint16_t) entries_size;
668
669 template_lock(template);
670 assert(ledger->l_size <= template->lt_next_offset);
671 for (i = 0; i < num_entries; i++) {
672 uint16_t size, offset;
673 struct entry_template *et = &template->lt_entries[i];
674 size = et->et_size;
675 offset = et->et_offset;
676 assert(offset < ledger->l_size);
677
678 struct ledger_entry_small *les = &ledger->l_entries[offset];
679 if (size == sizeof(struct ledger_entry)) {
680 struct ledger_entry *le = (struct ledger_entry *) les;
681
682 le->le_flags = et->et_flags;
683 /* make entry inactive by removing active bit */
684 if (entry_type == LEDGER_CREATE_INACTIVE_ENTRIES) {
685 flag_clear(&le->le_flags, LF_ENTRY_ACTIVE);
686 }
687 /*
688 * If template has a callback, this entry is opted-in,
689 * by default.
690 */
691 if (et->et_callback != NULL) {
692 flag_set(&le->le_flags, LEDGER_ACTION_CALLBACK);
693 }
694 le->le_credit = 0;
695 le->le_debit = 0;
696 le->le_limit = LEDGER_LIMIT_INFINITY;
697 le->le_warn_percent = LEDGER_PERCENT_NONE;
698 le->_le.le_refill.le_refill_period = 0;
699 le->_le.le_refill.le_last_refill = 0;
700 } else {
701 les->les_flags = et->et_flags;
702 les->les_credit = 0;
703 }
704 }
705 template_unlock(template);
706
707 return ledger;
708 }
709
710 static uint32_t
flag_set(volatile uint32_t * flags,uint32_t bit)711 flag_set(volatile uint32_t *flags, uint32_t bit)
712 {
713 return OSBitOrAtomic(bit, flags);
714 }
715
716 static uint32_t
flag_clear(volatile uint32_t * flags,uint32_t bit)717 flag_clear(volatile uint32_t *flags, uint32_t bit)
718 {
719 return OSBitAndAtomic(~bit, flags);
720 }
721
722 /*
723 * Take a reference on a ledger
724 */
725 void
ledger_reference(ledger_t ledger)726 ledger_reference(ledger_t ledger)
727 {
728 if (!LEDGER_VALID(ledger)) {
729 return;
730 }
731
732 os_ref_retain(&ledger->l_refs);
733 }
734
735 /*
736 * Remove a reference on a ledger. If this is the last reference,
737 * deallocate the unused ledger.
738 */
739 void
ledger_dereference(ledger_t ledger)740 ledger_dereference(ledger_t ledger)
741 {
742 if (!LEDGER_VALID(ledger)) {
743 return;
744 }
745
746 if (os_ref_release(&ledger->l_refs) == 0) {
747 ledger_template_t template = ledger->l_template;
748 if (template->lt_zone) {
749 zfree(template->lt_zone, ledger);
750 } else {
751 /**
752 * If the template doesn't contain a zone to allocate ledger objects
753 * from, then assume that these ledger objects were allocated by the
754 * pmap. This is done on PPL-enabled systems to give the PPL a
755 * method of validating ledger objects when updating them from
756 * within the PPL.
757 */
758 pmap_ledger_free(ledger);
759 }
760 ledger_template_dereference(template);
761 }
762 }
763
764 /*
765 * Determine whether an entry has exceeded its warning level.
766 */
767 static inline int
warn_level_exceeded(struct ledger_entry * le)768 warn_level_exceeded(struct ledger_entry *le)
769 {
770 ledger_amount_t balance;
771
772 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
773 assert(le->le_debit == 0);
774 } else {
775 assert((le->le_credit >= 0) && (le->le_debit >= 0));
776 }
777
778 /*
779 * XXX - Currently, we only support warnings for ledgers which
780 * use positive limits.
781 */
782 balance = le->le_credit - le->le_debit;
783 if (le->le_warn_percent != LEDGER_PERCENT_NONE &&
784 ((balance > (le->le_limit * le->le_warn_percent) >> 16))) {
785 return 1;
786 }
787 return 0;
788 }
789
790 /*
791 * Determine whether an entry has exceeded its limit.
792 */
793 static inline int
limit_exceeded(struct ledger_entry * le)794 limit_exceeded(struct ledger_entry *le)
795 {
796 ledger_amount_t balance;
797
798 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
799 assert(le->le_debit == 0);
800 } else {
801 assert((le->le_credit >= 0) && (le->le_debit >= 0));
802 }
803
804 balance = le->le_credit - le->le_debit;
805 if ((le->le_limit <= 0) && (balance < le->le_limit)) {
806 return 1;
807 }
808
809 if ((le->le_limit > 0) && (balance > le->le_limit)) {
810 return 1;
811 }
812 return 0;
813 }
814
815 static inline struct ledger_callback *
entry_get_callback(ledger_t ledger,int entry)816 entry_get_callback(ledger_t ledger, int entry)
817 {
818 struct ledger_callback *callback = NULL;
819 spl_t s;
820 const uint16_t *ledger_template_idx_p = NULL;
821
822 TEMPLATE_INUSE(s, ledger->l_template);
823 ledger_template_idx_p = ledger_entry_to_template_idx(ledger->l_template, entry);
824 if (ledger_template_idx_p != NULL) {
825 callback = ledger->l_template->lt_entries[*ledger_template_idx_p].et_callback;
826 }
827 TEMPLATE_IDLE(s, ledger->l_template);
828
829 return callback;
830 }
831
832 /*
833 * If the ledger value is positive, wake up anybody waiting on it.
834 */
835 static inline void
ledger_limit_entry_wakeup(struct ledger_entry * le)836 ledger_limit_entry_wakeup(struct ledger_entry *le)
837 {
838 uint32_t flags;
839
840 if (!limit_exceeded(le)) {
841 flags = flag_clear(&le->le_flags, LF_CALLED_BACK);
842
843 while (le->le_flags & LF_WAKE_NEEDED) {
844 flag_clear(&le->le_flags, LF_WAKE_NEEDED);
845 thread_wakeup((event_t)le);
846 }
847 }
848 }
849
850 /*
851 * Refill the coffers.
852 */
853 static void
ledger_refill(uint64_t now,ledger_t ledger,int entry)854 ledger_refill(uint64_t now, ledger_t ledger, int entry)
855 {
856 uint64_t elapsed, period, periods;
857 struct ledger_entry *le;
858 ledger_amount_t balance, due;
859
860 if (!is_entry_valid(ledger, entry)) {
861 return;
862 }
863
864 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
865 /* Small entries can't do refills */
866 return;
867 }
868
869 le = ledger_entry_identifier_to_entry(ledger, entry);
870
871 assert(le->le_limit != LEDGER_LIMIT_INFINITY);
872
873 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
874 assert(le->le_debit == 0);
875 return;
876 }
877
878 /*
879 * If another thread is handling the refill already, we're not
880 * needed.
881 */
882 if (flag_set(&le->le_flags, LF_REFILL_INPROGRESS) & LF_REFILL_INPROGRESS) {
883 return;
884 }
885
886 /*
887 * If the timestamp we're about to use to refill is older than the
888 * last refill, then someone else has already refilled this ledger
889 * and there's nothing for us to do here.
890 */
891 if (now <= le->_le.le_refill.le_last_refill) {
892 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
893 return;
894 }
895
896 /*
897 * See how many refill periods have passed since we last
898 * did a refill.
899 */
900 period = le->_le.le_refill.le_refill_period;
901 elapsed = now - le->_le.le_refill.le_last_refill;
902 if ((period == 0) || (elapsed < period)) {
903 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
904 return;
905 }
906
907 /*
908 * Optimize for the most common case of only one or two
909 * periods elapsing.
910 */
911 periods = 0;
912 while ((periods < 2) && (elapsed > 0)) {
913 periods++;
914 elapsed -= period;
915 }
916
917 /*
918 * OK, it's been a long time. Do a divide to figure out
919 * how long.
920 */
921 if (elapsed > 0) {
922 periods = (now - le->_le.le_refill.le_last_refill) / period;
923 }
924
925 balance = le->le_credit - le->le_debit;
926 due = periods * le->le_limit;
927
928 if (balance - due < 0) {
929 due = balance;
930 }
931
932 if (due < 0 && (le->le_flags & LF_PANIC_ON_NEGATIVE)) {
933 assertf(due >= 0, "now=%llu, ledger=%p, entry=%d, balance=%lld, due=%lld", now, ledger, entry, balance, due);
934 } else {
935 OSAddAtomic64(due, &le->le_debit);
936 assert(le->le_debit >= 0);
937 }
938 /*
939 * If we've completely refilled the pool, set the refill time to now.
940 * Otherwise set it to the time at which it last should have been
941 * fully refilled.
942 */
943 if (balance == due) {
944 le->_le.le_refill.le_last_refill = now;
945 } else {
946 le->_le.le_refill.le_last_refill += (le->_le.le_refill.le_refill_period * periods);
947 }
948
949 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
950
951 lprintf(("Refill %lld %lld->%lld\n", periods, balance, balance - due));
952 if (!limit_exceeded(le)) {
953 ledger_limit_entry_wakeup(le);
954 }
955 }
956
957 void
ledger_entry_check_new_balance(thread_t thread,ledger_t ledger,int entry)958 ledger_entry_check_new_balance(thread_t thread, ledger_t ledger,
959 int entry)
960 {
961 uint16_t size, offset;
962 struct ledger_entry *le = NULL;
963 struct ledger_entry_small *les = NULL;
964 if (!is_entry_valid(ledger, entry)) {
965 return;
966 }
967 size = ENTRY_ID_SIZE(entry);
968 offset = ENTRY_ID_OFFSET(entry);
969 les = &ledger->l_entries[offset];
970 if (size == sizeof(struct ledger_entry_small)) {
971 if ((les->les_flags & LF_PANIC_ON_NEGATIVE) && les->les_credit < 0) {
972 panic("ledger_entry_check_new_balance(%p,%d): negative ledger %p credit:%lld debit:0 balance:%lld",
973 ledger, entry, les,
974 les->les_credit,
975 les->les_credit);
976 }
977 } else if (size == sizeof(struct ledger_entry)) {
978 le = (struct ledger_entry *)les;
979 if (le->le_flags & LF_TRACKING_MAX) {
980 ledger_amount_t balance = le->le_credit - le->le_debit;
981
982 if (balance > le->_le._le_max.le_lifetime_max) {
983 le->_le._le_max.le_lifetime_max = balance;
984 }
985
986 #if CONFIG_LEDGER_INTERVAL_MAX
987 if (balance > le->_le._le_max.le_interval_max) {
988 le->_le._le_max.le_interval_max = balance;
989 }
990 #endif /* LEDGER_CONFIG_INTERVAL_MAX */
991 }
992
993 /* Check to see whether we're due a refill */
994 if (le->le_flags & LF_REFILL_SCHEDULED) {
995 assert(!(le->le_flags & LF_TRACKING_MAX));
996
997 uint64_t now = mach_absolute_time();
998 if ((now - le->_le.le_refill.le_last_refill) > le->_le.le_refill.le_refill_period) {
999 ledger_refill(now, ledger, entry);
1000 }
1001 }
1002
1003 if (limit_exceeded(le)) {
1004 /*
1005 * We've exceeded the limit for this entry. There
1006 * are several possible ways to handle it. We can block,
1007 * we can execute a callback, or we can ignore it. In
1008 * either of the first two cases, we want to set the AST
1009 * flag so we can take the appropriate action just before
1010 * leaving the kernel. The one caveat is that if we have
1011 * already called the callback, we don't want to do it
1012 * again until it gets rearmed.
1013 */
1014 if ((le->le_flags & LEDGER_ACTION_BLOCK) ||
1015 (!(le->le_flags & LF_CALLED_BACK) &&
1016 entry_get_callback(ledger, entry))) {
1017 act_set_astledger_async(thread);
1018 }
1019 } else {
1020 /*
1021 * The balance on the account is below the limit.
1022 *
1023 * If there are any threads blocked on this entry, now would
1024 * be a good time to wake them up.
1025 */
1026 if (le->le_flags & LF_WAKE_NEEDED) {
1027 ledger_limit_entry_wakeup(le);
1028 }
1029
1030 if (le->le_flags & LEDGER_ACTION_CALLBACK) {
1031 /*
1032 * Client has requested that a callback be invoked whenever
1033 * the ledger's balance crosses into or out of the warning
1034 * level.
1035 */
1036 if (warn_level_exceeded(le)) {
1037 /*
1038 * This ledger's balance is above the warning level.
1039 */
1040 if ((le->le_flags & LF_WARNED) == 0) {
1041 /*
1042 * If we are above the warning level and
1043 * have not yet invoked the callback,
1044 * set the AST so it can be done before returning
1045 * to userland.
1046 */
1047 act_set_astledger_async(thread);
1048 }
1049 } else {
1050 /*
1051 * This ledger's balance is below the warning level.
1052 */
1053 if (le->le_flags & LF_WARNED) {
1054 /*
1055 * If we are below the warning level and
1056 * the LF_WARNED flag is still set, we need
1057 * to invoke the callback to let the client
1058 * know the ledger balance is now back below
1059 * the warning level.
1060 */
1061 act_set_astledger_async(thread);
1062 }
1063 }
1064 }
1065 }
1066
1067 if ((le->le_flags & LF_PANIC_ON_NEGATIVE) &&
1068 (le->le_credit < le->le_debit)) {
1069 panic("ledger_entry_check_new_balance(%p,%d): negative ledger %p credit:%lld debit:%lld balance:%lld",
1070 ledger, entry, le,
1071 le->le_credit,
1072 le->le_debit,
1073 le->le_credit - le->le_debit);
1074 }
1075 } else {
1076 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, size);
1077 }
1078 }
1079
1080 void
ledger_check_new_balance(thread_t thread,ledger_t ledger,int entry)1081 ledger_check_new_balance(thread_t thread, ledger_t ledger, int entry)
1082 {
1083 ledger_entry_check_new_balance(thread, ledger, entry);
1084 }
1085
1086 /*
1087 * Add value to an entry in a ledger for a specific thread.
1088 */
1089 kern_return_t
ledger_credit_thread(thread_t thread,ledger_t ledger,int entry,ledger_amount_t amount)1090 ledger_credit_thread(thread_t thread, ledger_t ledger, int entry, ledger_amount_t amount)
1091 {
1092 ledger_amount_t old, new;
1093 struct ledger_entry *le;
1094 uint16_t entry_size = ENTRY_ID_SIZE(entry);
1095
1096 if (!is_entry_valid_and_active(ledger, entry) || (amount < 0)) {
1097 return KERN_INVALID_VALUE;
1098 }
1099
1100 if (amount == 0) {
1101 return KERN_SUCCESS;
1102 }
1103
1104 if (entry_size == sizeof(struct ledger_entry_small)) {
1105 struct ledger_entry_small *les = &ledger->l_entries[ENTRY_ID_OFFSET(entry)];
1106 old = OSAddAtomic64(amount, &les->les_credit);
1107 new = old + amount;
1108 } else if (entry_size == sizeof(struct ledger_entry)) {
1109 le = ledger_entry_identifier_to_entry(ledger, entry);
1110
1111 old = OSAddAtomic64(amount, &le->le_credit);
1112 new = old + amount;
1113 } else {
1114 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, entry_size);
1115 }
1116
1117 lprintf(("%p Credit %lld->%lld\n", thread, old, new));
1118 if (thread) {
1119 ledger_entry_check_new_balance(thread, ledger, entry);
1120 }
1121
1122 return KERN_SUCCESS;
1123 }
1124
1125 /*
1126 * Add value to an entry in a ledger.
1127 */
1128 kern_return_t
ledger_credit(ledger_t ledger,int entry,ledger_amount_t amount)1129 ledger_credit(ledger_t ledger, int entry, ledger_amount_t amount)
1130 {
1131 return ledger_credit_thread(current_thread(), ledger, entry, amount);
1132 }
1133
1134 /*
1135 * Add value to an entry in a ledger; do not check balance after update.
1136 */
1137 kern_return_t
ledger_credit_nocheck(ledger_t ledger,int entry,ledger_amount_t amount)1138 ledger_credit_nocheck(ledger_t ledger, int entry, ledger_amount_t amount)
1139 {
1140 return ledger_credit_thread(NULL, ledger, entry, amount);
1141 }
1142
1143 /* Add all of one ledger's values into another.
1144 * They must have been created from the same template.
1145 * This is not done atomically. Another thread (if not otherwise synchronized)
1146 * may see bogus values when comparing one entry to another.
1147 * As each entry's credit & debit are modified one at a time, the warning/limit
1148 * may spuriously trip, or spuriously fail to trip, or another thread (if not
1149 * otherwise synchronized) may see a bogus balance.
1150 */
1151 kern_return_t
ledger_rollup(ledger_t to_ledger,ledger_t from_ledger)1152 ledger_rollup(ledger_t to_ledger, ledger_t from_ledger)
1153 {
1154 int id;
1155 ledger_template_t template = NULL;
1156 struct entry_template *et = NULL;
1157
1158 assert(to_ledger->l_template->lt_cnt == from_ledger->l_template->lt_cnt);
1159 template = from_ledger->l_template;
1160 assert(template->lt_initialized);
1161
1162 for (uint16_t i = 0; i < template->lt_cnt; i++) {
1163 et = &template->lt_entries[i];
1164 uint16_t size = et->et_size;
1165 id = ledger_entry_id(size, et->et_offset);
1166 ledger_rollup_entry(to_ledger, from_ledger, id);
1167 }
1168
1169 return KERN_SUCCESS;
1170 }
1171
1172 /* Add one ledger entry value to another.
1173 * They must have been created from the same template.
1174 * Since the credit and debit values are added one
1175 * at a time, other thread might read the a bogus value.
1176 */
1177 kern_return_t
ledger_rollup_entry(ledger_t to_ledger,ledger_t from_ledger,int entry)1178 ledger_rollup_entry(ledger_t to_ledger, ledger_t from_ledger, int entry)
1179 {
1180 struct ledger_entry_small *from_les, *to_les;
1181 uint16_t entry_size, entry_offset;
1182 entry_size = ENTRY_ID_SIZE(entry);
1183 entry_offset = ENTRY_ID_OFFSET(entry);
1184
1185 assert(to_ledger->l_template->lt_cnt == from_ledger->l_template->lt_cnt);
1186 if (is_entry_valid(from_ledger, entry) && is_entry_valid(to_ledger, entry)) {
1187 from_les = &from_ledger->l_entries[entry_offset];
1188 to_les = &to_ledger->l_entries[entry_offset];
1189 if (entry_size == sizeof(struct ledger_entry)) {
1190 struct ledger_entry *from = (struct ledger_entry *)from_les;
1191 struct ledger_entry *to = (struct ledger_entry *)to_les;
1192 OSAddAtomic64(from->le_credit, &to->le_credit);
1193 OSAddAtomic64(from->le_debit, &to->le_debit);
1194 } else if (entry_size == sizeof(struct ledger_entry_small)) {
1195 OSAddAtomic64(from_les->les_credit, &to_les->les_credit);
1196 } else {
1197 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", from_ledger, entry, entry_size);
1198 }
1199 }
1200
1201 return KERN_SUCCESS;
1202 }
1203
1204 /*
1205 * Zero the balance of a ledger by adding to its credit or debit, whichever is smaller.
1206 * Note that some clients of ledgers (notably, task wakeup statistics) require that
1207 * le_credit only ever increase as a function of ledger_credit().
1208 */
1209 kern_return_t
ledger_zero_balance(ledger_t ledger,int entry)1210 ledger_zero_balance(ledger_t ledger, int entry)
1211 {
1212 struct ledger_entry *le;
1213 struct ledger_entry_small *les;
1214 ledger_amount_t debit, credit;
1215 uint16_t entry_size, entry_offset;
1216 entry_size = ENTRY_ID_SIZE(entry);
1217 entry_offset = ENTRY_ID_OFFSET(entry);
1218
1219 if (!is_entry_valid_and_active(ledger, entry)) {
1220 return KERN_INVALID_VALUE;
1221 }
1222
1223 les = &ledger->l_entries[entry_offset];
1224 if (entry_size == sizeof(struct ledger_entry_small)) {
1225 while (true) {
1226 credit = les->les_credit;
1227 if (OSCompareAndSwap64(credit, 0, &les->les_credit)) {
1228 break;
1229 }
1230 }
1231 } else if (entry_size == sizeof(struct ledger_entry)) {
1232 le = (struct ledger_entry *)les;
1233 top:
1234 debit = le->le_debit;
1235 credit = le->le_credit;
1236
1237 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
1238 assert(le->le_debit == 0);
1239 if (!OSCompareAndSwap64(credit, 0, &le->le_credit)) {
1240 goto top;
1241 }
1242 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, 0));
1243 } else if (credit > debit) {
1244 if (!OSCompareAndSwap64(debit, credit, &le->le_debit)) {
1245 goto top;
1246 }
1247 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_debit, le->le_credit));
1248 } else if (credit < debit) {
1249 if (!OSCompareAndSwap64(credit, debit, &le->le_credit)) {
1250 goto top;
1251 }
1252 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, le->le_debit));
1253 }
1254 } else {
1255 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, entry_size);
1256 }
1257
1258 return KERN_SUCCESS;
1259 }
1260
1261 kern_return_t
ledger_get_limit(ledger_t ledger,int entry,ledger_amount_t * limit)1262 ledger_get_limit(ledger_t ledger, int entry, ledger_amount_t *limit)
1263 {
1264 struct ledger_entry *le;
1265
1266 if (!is_entry_valid_and_active(ledger, entry)) {
1267 return KERN_INVALID_VALUE;
1268 }
1269
1270 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1271 /* Small entries can't have limits */
1272 *limit = LEDGER_LIMIT_INFINITY;
1273 } else {
1274 le = ledger_entry_identifier_to_entry(ledger, entry);
1275 *limit = le->le_limit;
1276 }
1277
1278 lprintf(("ledger_get_limit: %lld\n", *limit));
1279
1280 return KERN_SUCCESS;
1281 }
1282
1283 /*
1284 * Adjust the limit of a limited resource. This does not affect the
1285 * current balance, so the change doesn't affect the thread until the
1286 * next refill.
1287 *
1288 * warn_level: If non-zero, causes the callback to be invoked when
1289 * the balance exceeds this level. Specified as a percentage [of the limit].
1290 */
1291 kern_return_t
ledger_set_limit(ledger_t ledger,int entry,ledger_amount_t limit,uint8_t warn_level_percentage)1292 ledger_set_limit(ledger_t ledger, int entry, ledger_amount_t limit,
1293 uint8_t warn_level_percentage)
1294 {
1295 struct ledger_entry *le;
1296
1297 if (!is_entry_valid_and_active(ledger, entry)) {
1298 return KERN_INVALID_VALUE;
1299 }
1300
1301 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1302 /* Small entries can't have limits */
1303 return KERN_INVALID_ARGUMENT;
1304 }
1305
1306 lprintf(("ledger_set_limit: %lld\n", limit));
1307 le = ledger_entry_identifier_to_entry(ledger, entry);
1308
1309 if (limit == LEDGER_LIMIT_INFINITY) {
1310 /*
1311 * Caller wishes to disable the limit. This will implicitly
1312 * disable automatic refill, as refills implicitly depend
1313 * on the limit.
1314 */
1315 ledger_disable_refill(ledger, entry);
1316 }
1317
1318 le->le_limit = limit;
1319 if (le->le_flags & LF_REFILL_SCHEDULED) {
1320 assert(!(le->le_flags & LF_TRACKING_MAX));
1321 le->_le.le_refill.le_last_refill = 0;
1322 }
1323 flag_clear(&le->le_flags, LF_CALLED_BACK);
1324 flag_clear(&le->le_flags, LF_WARNED);
1325 ledger_limit_entry_wakeup(le);
1326
1327 if (warn_level_percentage != 0) {
1328 assert(warn_level_percentage <= 100);
1329 assert(limit > 0); /* no negative limit support for warnings */
1330 assert(limit != LEDGER_LIMIT_INFINITY); /* warn % without limit makes no sense */
1331 le->le_warn_percent = warn_level_percentage * (1u << 16) / 100;
1332 } else {
1333 le->le_warn_percent = LEDGER_PERCENT_NONE;
1334 }
1335
1336 return KERN_SUCCESS;
1337 }
1338
1339 #if CONFIG_LEDGER_INTERVAL_MAX
1340 kern_return_t
ledger_get_interval_max(ledger_t ledger,int entry,ledger_amount_t * max_interval_balance,int reset)1341 ledger_get_interval_max(ledger_t ledger, int entry,
1342 ledger_amount_t *max_interval_balance, int reset)
1343 {
1344 struct ledger_entry *le;
1345
1346 if (!is_entry_valid_and_active(ledger, entry)) {
1347 return KERN_INVALID_VALUE;
1348 }
1349
1350 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1351 /* Small entries can't track max */
1352 return KERN_INVALID_ARGUMENT;
1353 }
1354
1355 le = ledger_entry_identifier_to_entry(ledger, entry);
1356
1357 if (!(le->le_flags & LF_TRACKING_MAX)) {
1358 return KERN_INVALID_VALUE;
1359 }
1360
1361 *max_interval_balance = le->_le._le_max.le_interval_max;
1362 lprintf(("ledger_get_interval_max: %lld%s\n", *max_interval_balance,
1363 (reset) ? " --> 0" : ""));
1364
1365 if (reset) {
1366 le->_le._le_max.le_interval_max = 0;
1367 }
1368
1369 return KERN_SUCCESS;
1370 }
1371 #endif /* CONFIG_LEDGER_INTERVAL_MAX */
1372
1373 kern_return_t
ledger_get_lifetime_max(ledger_t ledger,int entry,ledger_amount_t * max_lifetime_balance)1374 ledger_get_lifetime_max(ledger_t ledger, int entry,
1375 ledger_amount_t *max_lifetime_balance)
1376 {
1377 struct ledger_entry *le;
1378
1379 if (!is_entry_valid_and_active(ledger, entry)) {
1380 return KERN_INVALID_VALUE;
1381 }
1382
1383 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1384 /* Small entries can't track max */
1385 return KERN_INVALID_ARGUMENT;
1386 }
1387
1388 le = ledger_entry_identifier_to_entry(ledger, entry);
1389
1390 if (!(le->le_flags & LF_TRACKING_MAX)) {
1391 return KERN_INVALID_VALUE;
1392 }
1393
1394 *max_lifetime_balance = le->_le._le_max.le_lifetime_max;
1395 lprintf(("ledger_get_lifetime_max: %lld\n", *max_lifetime_balance));
1396
1397 return KERN_SUCCESS;
1398 }
1399
1400 /*
1401 * Enable tracking of periodic maximums for this ledger entry.
1402 */
1403 kern_return_t
ledger_track_maximum(ledger_template_t template,int entry,__unused int period_in_secs)1404 ledger_track_maximum(ledger_template_t template, int entry,
1405 __unused int period_in_secs)
1406 {
1407 uint16_t idx;
1408 const uint16_t *idx_p;
1409 struct entry_template *et = NULL;
1410 kern_return_t kr = KERN_INVALID_VALUE;
1411
1412 template_lock(template);
1413
1414 idx_p = ledger_entry_to_template_idx(template, entry);
1415 if (idx_p == NULL) {
1416 kr = KERN_INVALID_VALUE;
1417 goto out;
1418 }
1419 idx = *idx_p;
1420 if (idx >= template->lt_cnt) {
1421 kr = KERN_INVALID_VALUE;
1422 goto out;
1423 }
1424 et = &template->lt_entries[idx];
1425 /* Ensure the caller asked for enough space up front */
1426 if (et->et_size != sizeof(struct ledger_entry)) {
1427 kr = KERN_INVALID_VALUE;
1428 goto out;
1429 }
1430
1431 /* Refill is incompatible with max tracking. */
1432 if (et->et_flags & LF_REFILL_SCHEDULED) {
1433 kr = KERN_INVALID_VALUE;
1434 goto out;
1435 }
1436
1437 et->et_flags |= LF_TRACKING_MAX;
1438 kr = KERN_SUCCESS;
1439 out:
1440 template_unlock(template);
1441
1442 return kr;
1443 }
1444
1445 kern_return_t
ledger_panic_on_negative(ledger_template_t template,int entry)1446 ledger_panic_on_negative(ledger_template_t template, int entry)
1447 {
1448 const uint16_t *idx_p;
1449 uint16_t idx;
1450 template_lock(template);
1451
1452 idx_p = ledger_entry_to_template_idx(template, entry);
1453 if (idx_p == NULL) {
1454 template_unlock(template);
1455 return KERN_INVALID_VALUE;
1456 }
1457 idx = *idx_p;
1458 if (idx >= template->lt_cnt) {
1459 template_unlock(template);
1460 return KERN_INVALID_VALUE;
1461 }
1462
1463 template->lt_entries[idx].et_flags |= LF_PANIC_ON_NEGATIVE;
1464
1465 template_unlock(template);
1466
1467 return KERN_SUCCESS;
1468 }
1469
1470 kern_return_t
ledger_track_credit_only(ledger_template_t template,int entry)1471 ledger_track_credit_only(ledger_template_t template, int entry)
1472 {
1473 const uint16_t *idx_p;
1474 uint16_t idx;
1475 struct entry_template *et = NULL;
1476 kern_return_t kr = KERN_INVALID_VALUE;
1477 template_lock(template);
1478
1479 idx_p = ledger_entry_to_template_idx(template, entry);
1480 if (idx_p == NULL) {
1481 kr = KERN_INVALID_VALUE;
1482 goto out;
1483 }
1484 idx = *idx_p;
1485 if (idx >= template->lt_cnt) {
1486 kr = KERN_INVALID_VALUE;
1487 goto out;
1488 }
1489 et = &template->lt_entries[idx];
1490 /* Ensure the caller asked for enough space up front */
1491 if (et->et_size != sizeof(struct ledger_entry)) {
1492 kr = KERN_INVALID_VALUE;
1493 goto out;
1494 }
1495
1496 et->et_flags |= LF_TRACK_CREDIT_ONLY;
1497 kr = KERN_SUCCESS;
1498
1499 out:
1500 template_unlock(template);
1501
1502 return kr;
1503 }
1504
1505 /*
1506 * Add a callback to be executed when the resource goes into deficit.
1507 */
1508 kern_return_t
ledger_set_callback(ledger_template_t template,int entry,ledger_callback_t func,const void * param0,const void * param1)1509 ledger_set_callback(ledger_template_t template, int entry,
1510 ledger_callback_t func, const void *param0, const void *param1)
1511 {
1512 struct entry_template *et;
1513 struct ledger_callback *old_cb, *new_cb;
1514 const uint16_t *idx_p;
1515 uint16_t idx;
1516
1517 idx_p = ledger_entry_to_template_idx(template, entry);
1518 if (idx_p == NULL) {
1519 return KERN_INVALID_VALUE;
1520 }
1521 idx = *idx_p;
1522
1523 if (idx >= template->lt_cnt) {
1524 return KERN_INVALID_VALUE;
1525 }
1526
1527 if (func) {
1528 new_cb = kalloc_type(struct ledger_callback, Z_WAITOK);
1529 new_cb->lc_func = func;
1530 new_cb->lc_param0 = param0;
1531 new_cb->lc_param1 = param1;
1532 } else {
1533 new_cb = NULL;
1534 }
1535
1536 template_lock(template);
1537 et = &template->lt_entries[idx];
1538 /* Ensure the caller asked for enough space up front */
1539 if (et->et_size != sizeof(struct ledger_entry)) {
1540 kfree_type(struct ledger_callback, new_cb);
1541 template_unlock(template);
1542 return KERN_INVALID_VALUE;
1543 }
1544 old_cb = et->et_callback;
1545 et->et_callback = new_cb;
1546 template_unlock(template);
1547 if (old_cb) {
1548 kfree_type(struct ledger_callback, old_cb);
1549 }
1550
1551 return KERN_SUCCESS;
1552 }
1553
1554 /*
1555 * Disable callback notification for a specific ledger entry.
1556 *
1557 * Otherwise, if using a ledger template which specified a
1558 * callback function (ledger_set_callback()), it will be invoked when
1559 * the resource goes into deficit.
1560 */
1561 kern_return_t
ledger_disable_callback(ledger_t ledger,int entry)1562 ledger_disable_callback(ledger_t ledger, int entry)
1563 {
1564 struct ledger_entry *le = NULL;
1565
1566 if (!is_entry_valid_and_active(ledger, entry)) {
1567 return KERN_INVALID_VALUE;
1568 }
1569
1570 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1571 /* Small entries can't have callbacks */
1572 return KERN_INVALID_ARGUMENT;
1573 }
1574
1575 le = ledger_entry_identifier_to_entry(ledger, entry);
1576
1577 /*
1578 * le_warn_percent is used to indicate *if* this ledger has a warning configured,
1579 * in addition to what that warning level is set to.
1580 * This means a side-effect of ledger_disable_callback() is that the
1581 * warning level is forgotten.
1582 */
1583 le->le_warn_percent = LEDGER_PERCENT_NONE;
1584 flag_clear(&le->le_flags, LEDGER_ACTION_CALLBACK);
1585 return KERN_SUCCESS;
1586 }
1587
1588 /*
1589 * Enable callback notification for a specific ledger entry.
1590 *
1591 * This is only needed if ledger_disable_callback() has previously
1592 * been invoked against an entry; there must already be a callback
1593 * configured.
1594 */
1595 kern_return_t
ledger_enable_callback(ledger_t ledger,int entry)1596 ledger_enable_callback(ledger_t ledger, int entry)
1597 {
1598 struct ledger_entry *le = NULL;
1599
1600 if (!is_entry_valid_and_active(ledger, entry)) {
1601 return KERN_INVALID_VALUE;
1602 }
1603
1604 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1605 /* Small entries can't have callbacks */
1606 return KERN_INVALID_ARGUMENT;
1607 }
1608
1609 le = ledger_entry_identifier_to_entry(ledger, entry);
1610
1611 assert(entry_get_callback(ledger, entry) != NULL);
1612
1613 flag_set(&le->le_flags, LEDGER_ACTION_CALLBACK);
1614 return KERN_SUCCESS;
1615 }
1616
1617 /*
1618 * Query the automatic refill period for this ledger entry.
1619 *
1620 * A period of 0 means this entry has none configured.
1621 */
1622 kern_return_t
ledger_get_period(ledger_t ledger,int entry,uint64_t * period)1623 ledger_get_period(ledger_t ledger, int entry, uint64_t *period)
1624 {
1625 struct ledger_entry *le;
1626
1627 if (!is_entry_valid_and_active(ledger, entry)) {
1628 return KERN_INVALID_VALUE;
1629 }
1630
1631 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1632 /* Small entries can't do refills */
1633 return KERN_INVALID_ARGUMENT;
1634 }
1635
1636 le = ledger_entry_identifier_to_entry(ledger, entry);
1637
1638 *period = abstime_to_nsecs(le->_le.le_refill.le_refill_period);
1639 lprintf(("ledger_get_period: %llx\n", *period));
1640 return KERN_SUCCESS;
1641 }
1642
1643 /*
1644 * Adjust the automatic refill period.
1645 */
1646 kern_return_t
ledger_set_period(ledger_t ledger,int entry,uint64_t period)1647 ledger_set_period(ledger_t ledger, int entry, uint64_t period)
1648 {
1649 struct ledger_entry *le = NULL;
1650
1651 if (!is_entry_valid_and_active(ledger, entry)) {
1652 return KERN_INVALID_VALUE;
1653 }
1654
1655 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1656 /* Small entries can't do refills */
1657 return KERN_INVALID_ARGUMENT;
1658 }
1659
1660 lprintf(("ledger_set_period: %llx\n", period));
1661
1662 le = ledger_entry_identifier_to_entry(ledger, entry);
1663
1664 /*
1665 * A refill period refills the ledger in multiples of the limit,
1666 * so if you haven't set one yet, you need a lesson on ledgers.
1667 */
1668 assert(le->le_limit != LEDGER_LIMIT_INFINITY);
1669
1670 if (le->le_flags & LF_TRACKING_MAX) {
1671 /*
1672 * Refill is incompatible with rolling max tracking.
1673 */
1674 return KERN_INVALID_VALUE;
1675 }
1676
1677 le->_le.le_refill.le_refill_period = nsecs_to_abstime(period);
1678
1679 /*
1680 * Set the 'starting time' for the next refill to now. Since
1681 * we're resetting the balance to zero here, we consider this
1682 * moment the starting time for accumulating a balance that
1683 * counts towards the limit.
1684 */
1685 le->_le.le_refill.le_last_refill = mach_absolute_time();
1686 ledger_zero_balance(ledger, entry);
1687
1688 flag_set(&le->le_flags, LF_REFILL_SCHEDULED);
1689
1690 return KERN_SUCCESS;
1691 }
1692
1693 /*
1694 * Disable automatic refill.
1695 */
1696 kern_return_t
ledger_disable_refill(ledger_t ledger,int entry)1697 ledger_disable_refill(ledger_t ledger, int entry)
1698 {
1699 struct ledger_entry *le = NULL;
1700
1701 if (!is_entry_valid_and_active(ledger, entry)) {
1702 return KERN_INVALID_VALUE;
1703 }
1704
1705 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1706 /* Small entries can't do refills */
1707 return KERN_INVALID_ARGUMENT;
1708 }
1709
1710 le = ledger_entry_identifier_to_entry(ledger, entry);
1711
1712 flag_clear(&le->le_flags, LF_REFILL_SCHEDULED);
1713
1714 return KERN_SUCCESS;
1715 }
1716
1717 kern_return_t
ledger_get_actions(ledger_t ledger,int entry,int * actions)1718 ledger_get_actions(ledger_t ledger, int entry, int *actions)
1719 {
1720 struct ledger_entry *le = NULL;
1721 *actions = 0;
1722
1723 if (!is_entry_valid_and_active(ledger, entry)) {
1724 return KERN_INVALID_VALUE;
1725 }
1726
1727 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1728 /* Small entries can't have actions */
1729 return KERN_INVALID_ARGUMENT;
1730 }
1731
1732 le = ledger_entry_identifier_to_entry(ledger, entry);
1733
1734 *actions = le->le_flags & LEDGER_ACTION_MASK;
1735 lprintf(("ledger_get_actions: %#x\n", *actions));
1736 return KERN_SUCCESS;
1737 }
1738
1739 kern_return_t
ledger_set_action(ledger_t ledger,int entry,int action)1740 ledger_set_action(ledger_t ledger, int entry, int action)
1741 {
1742 lprintf(("ledger_set_action: %#x\n", action));
1743 struct ledger_entry *le = NULL;
1744
1745 if (!is_entry_valid_and_active(ledger, entry)) {
1746 return KERN_INVALID_VALUE;
1747 }
1748
1749 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
1750 /* Small entries can't have actions */
1751 return KERN_INVALID_ARGUMENT;
1752 }
1753
1754 le = ledger_entry_identifier_to_entry(ledger, entry);
1755
1756 flag_set(&le->le_flags, action);
1757 return KERN_SUCCESS;
1758 }
1759
1760 kern_return_t
ledger_debit_thread(thread_t thread,ledger_t ledger,int entry,ledger_amount_t amount)1761 ledger_debit_thread(thread_t thread, ledger_t ledger, int entry, ledger_amount_t amount)
1762 {
1763 struct ledger_entry *le;
1764 ledger_amount_t old, new;
1765 uint16_t entry_size = ENTRY_ID_SIZE(entry);
1766
1767 if (!is_entry_valid_and_active(ledger, entry) || (amount < 0)) {
1768 return KERN_INVALID_ARGUMENT;
1769 }
1770
1771 if (amount == 0) {
1772 return KERN_SUCCESS;
1773 }
1774
1775 if (entry_size == sizeof(struct ledger_entry_small)) {
1776 struct ledger_entry_small *les = &ledger->l_entries[ENTRY_ID_OFFSET(entry)];
1777 old = OSAddAtomic64(-amount, &les->les_credit);
1778 new = old - amount;
1779 } else if (entry_size == sizeof(struct ledger_entry)) {
1780 le = ledger_entry_identifier_to_entry(ledger, entry);
1781
1782 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
1783 assert(le->le_debit == 0);
1784 old = OSAddAtomic64(-amount, &le->le_credit);
1785 new = old - amount;
1786 } else {
1787 old = OSAddAtomic64(amount, &le->le_debit);
1788 new = old + amount;
1789 }
1790 } else {
1791 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, entry_size);
1792 }
1793 lprintf(("%p Debit %lld->%lld\n", thread, old, new));
1794
1795 if (thread) {
1796 ledger_entry_check_new_balance(thread, ledger, entry);
1797 }
1798
1799 return KERN_SUCCESS;
1800 }
1801
1802 kern_return_t
ledger_debit(ledger_t ledger,int entry,ledger_amount_t amount)1803 ledger_debit(ledger_t ledger, int entry, ledger_amount_t amount)
1804 {
1805 return ledger_debit_thread(current_thread(), ledger, entry, amount);
1806 }
1807
1808 kern_return_t
ledger_debit_nocheck(ledger_t ledger,int entry,ledger_amount_t amount)1809 ledger_debit_nocheck(ledger_t ledger, int entry, ledger_amount_t amount)
1810 {
1811 return ledger_debit_thread(NULL, ledger, entry, amount);
1812 }
1813
1814 void
ledger_ast(thread_t thread)1815 ledger_ast(thread_t thread)
1816 {
1817 struct ledger *l = thread->t_ledger;
1818 struct ledger *thl;
1819 struct ledger *coalition_ledger;
1820 uint32_t block;
1821 uint64_t now;
1822 uint8_t task_flags;
1823 uint8_t task_percentage;
1824 uint64_t task_interval;
1825
1826 kern_return_t ret;
1827 task_t task = get_threadtask(thread);
1828
1829 lprintf(("Ledger AST for %p\n", thread));
1830
1831 ASSERT(task != NULL);
1832 ASSERT(thread == current_thread());
1833
1834 top:
1835 /*
1836 * Take a self-consistent snapshot of the CPU usage monitor parameters. The task
1837 * can change them at any point (with the task locked).
1838 */
1839 task_lock(task);
1840 task_flags = task->rusage_cpu_flags;
1841 task_percentage = task->rusage_cpu_perthr_percentage;
1842 task_interval = task->rusage_cpu_perthr_interval;
1843 task_unlock(task);
1844
1845 /*
1846 * Make sure this thread is up to date with regards to any task-wide per-thread
1847 * CPU limit, but only if it doesn't have a thread-private blocking CPU limit.
1848 */
1849 if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) != 0) &&
1850 ((thread->options & TH_OPT_PRVT_CPULIMIT) == 0)) {
1851 uint8_t percentage;
1852 uint64_t interval;
1853 int action;
1854
1855 thread_get_cpulimit(&action, &percentage, &interval);
1856
1857 /*
1858 * If the thread's CPU limits no longer match the task's, or the
1859 * task has a limit but the thread doesn't, update the limit.
1860 */
1861 if (((thread->options & TH_OPT_PROC_CPULIMIT) == 0) ||
1862 (interval != task_interval) || (percentage != task_percentage)) {
1863 thread_set_cpulimit(THREAD_CPULIMIT_EXCEPTION, task_percentage, task_interval);
1864 assert((thread->options & TH_OPT_PROC_CPULIMIT) != 0);
1865 }
1866 } else if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) == 0) &&
1867 (thread->options & TH_OPT_PROC_CPULIMIT)) {
1868 assert((thread->options & TH_OPT_PRVT_CPULIMIT) == 0);
1869
1870 /*
1871 * Task no longer has a per-thread CPU limit; remove this thread's
1872 * corresponding CPU limit.
1873 */
1874 thread_set_cpulimit(THREAD_CPULIMIT_DISABLE, 0, 0);
1875 assert((thread->options & TH_OPT_PROC_CPULIMIT) == 0);
1876 }
1877
1878 /*
1879 * If the task or thread is being terminated, let's just get on with it
1880 */
1881 if ((l == NULL) || !task->active || task->halting || !thread->active) {
1882 return;
1883 }
1884
1885 /*
1886 * Examine all entries in deficit to see which might be eligble for
1887 * an automatic refill, which require callbacks to be issued, and
1888 * which require blocking.
1889 */
1890 block = 0;
1891 now = mach_absolute_time();
1892
1893 /*
1894 * Note that thread->t_threadledger may have been changed by the
1895 * thread_set_cpulimit() call above - so don't examine it until afterwards.
1896 */
1897 thl = thread->t_threadledger;
1898 if (LEDGER_VALID(thl)) {
1899 block |= ledger_check_needblock(thl, now);
1900 }
1901 block |= ledger_check_needblock(l, now);
1902
1903 coalition_ledger = coalition_ledger_get_from_task(task);
1904 if (LEDGER_VALID(coalition_ledger)) {
1905 block |= ledger_check_needblock(coalition_ledger, now);
1906 }
1907 ledger_dereference(coalition_ledger);
1908 /*
1909 * If we are supposed to block on the availability of one or more
1910 * resources, find the first entry in deficit for which we should wait.
1911 * Schedule a refill if necessary and then sleep until the resource
1912 * becomes available.
1913 */
1914 if (block) {
1915 if (LEDGER_VALID(thl)) {
1916 ret = ledger_perform_blocking(thl);
1917 if (ret != KERN_SUCCESS) {
1918 goto top;
1919 }
1920 }
1921 ret = ledger_perform_blocking(l);
1922 if (ret != KERN_SUCCESS) {
1923 goto top;
1924 }
1925 } /* block */
1926 }
1927
1928 static uint32_t
ledger_check_needblock(ledger_t l,uint64_t now)1929 ledger_check_needblock(ledger_t l, uint64_t now)
1930 {
1931 int i;
1932 uint32_t flags, block = 0;
1933 struct ledger_entry *le;
1934 struct ledger_callback *lc;
1935 struct entry_template *et = NULL;
1936 ledger_template_t template = NULL;
1937
1938 template = l->l_template;
1939 assert(template != NULL);
1940 assert(template->lt_initialized);
1941 /*
1942 * The template has been initialized so the entries table can't change.
1943 * Thus we don't need to acquire the template lock or the inuse bit.
1944 */
1945
1946
1947 for (i = 0; i < template->lt_cnt; i++) {
1948 spl_t s;
1949 et = &template->lt_entries[i];
1950 if (et->et_size == sizeof(struct ledger_entry_small)) {
1951 /* Small entries don't track limits or have callbacks */
1952 continue;
1953 }
1954 assert(et->et_size == sizeof(struct ledger_entry));
1955 le = (struct ledger_entry *) &l->l_entries[et->et_offset];
1956
1957 TEMPLATE_INUSE(s, template);
1958 lc = template->lt_entries[i].et_callback;
1959 TEMPLATE_IDLE(s, template);
1960
1961 if (limit_exceeded(le) == FALSE) {
1962 if (le->le_flags & LEDGER_ACTION_CALLBACK) {
1963 /*
1964 * If needed, invoke the callback as a warning.
1965 * This needs to happen both when the balance rises above
1966 * the warning level, and also when it dips back below it.
1967 */
1968 assert(lc != NULL);
1969 /*
1970 * See comments for matching logic in ledger_check_new_balance().
1971 */
1972 if (warn_level_exceeded(le)) {
1973 flags = flag_set(&le->le_flags, LF_WARNED);
1974 if ((flags & LF_WARNED) == 0) {
1975 lc->lc_func(LEDGER_WARNING_ROSE_ABOVE, lc->lc_param0, lc->lc_param1);
1976 }
1977 } else {
1978 flags = flag_clear(&le->le_flags, LF_WARNED);
1979 if (flags & LF_WARNED) {
1980 lc->lc_func(LEDGER_WARNING_DIPPED_BELOW, lc->lc_param0, lc->lc_param1);
1981 }
1982 }
1983 }
1984
1985 continue;
1986 }
1987
1988 /* We're over the limit, so refill if we are eligible and past due. */
1989 if (le->le_flags & LF_REFILL_SCHEDULED) {
1990 assert(!(le->le_flags & LF_TRACKING_MAX));
1991
1992 if ((le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period) <= now) {
1993 ledger_refill(now, l, i);
1994 if (limit_exceeded(le) == FALSE) {
1995 continue;
1996 }
1997 }
1998 }
1999
2000 if (le->le_flags & LEDGER_ACTION_BLOCK) {
2001 block = 1;
2002 }
2003 if ((le->le_flags & LEDGER_ACTION_CALLBACK) == 0) {
2004 continue;
2005 }
2006
2007 /*
2008 * If the LEDGER_ACTION_CALLBACK flag is on, we expect there to
2009 * be a registered callback.
2010 */
2011 assert(lc != NULL);
2012 flags = flag_set(&le->le_flags, LF_CALLED_BACK);
2013 /* Callback has already been called */
2014 if (flags & LF_CALLED_BACK) {
2015 continue;
2016 }
2017 lc->lc_func(FALSE, lc->lc_param0, lc->lc_param1);
2018 }
2019 return block;
2020 }
2021
2022
2023 /* return KERN_SUCCESS to continue, KERN_FAILURE to restart */
2024 static kern_return_t
ledger_perform_blocking(ledger_t l)2025 ledger_perform_blocking(ledger_t l)
2026 {
2027 int i;
2028 kern_return_t ret;
2029 struct ledger_entry *le;
2030 ledger_template_t template = NULL;
2031 struct entry_template *et = NULL;
2032
2033 template = l->l_template;
2034 assert(template->lt_initialized);
2035
2036 for (i = 0; i < template->lt_cnt; i++) {
2037 et = &template->lt_entries[i];
2038 if (et->et_size != sizeof(struct ledger_entry)) {
2039 /* Small entries do not block for anything. */
2040 continue;
2041 }
2042 le = (struct ledger_entry *) &l->l_entries[et->et_offset];
2043 if ((!limit_exceeded(le)) ||
2044 ((le->le_flags & LEDGER_ACTION_BLOCK) == 0)) {
2045 continue;
2046 }
2047
2048 assert(!(le->le_flags & LF_TRACKING_MAX));
2049
2050 /* Prepare to sleep until the resource is refilled */
2051 ret = assert_wait_deadline(le, THREAD_INTERRUPTIBLE,
2052 le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period);
2053 if (ret != THREAD_WAITING) {
2054 return KERN_SUCCESS;
2055 }
2056
2057 /* Mark that somebody is waiting on this entry */
2058 flag_set(&le->le_flags, LF_WAKE_NEEDED);
2059
2060 ret = thread_block_reason(THREAD_CONTINUE_NULL, NULL,
2061 AST_LEDGER);
2062 if (ret != THREAD_AWAKENED) {
2063 return KERN_SUCCESS;
2064 }
2065
2066 /*
2067 * The world may have changed while we were asleep.
2068 * Some other resource we need may have gone into
2069 * deficit. Or maybe we're supposed to die now.
2070 * Go back to the top and reevaluate.
2071 */
2072 return KERN_FAILURE;
2073 }
2074 return KERN_SUCCESS;
2075 }
2076
2077
2078 kern_return_t
ledger_get_entries(ledger_t ledger,int entry,ledger_amount_t * credit,ledger_amount_t * debit)2079 ledger_get_entries(ledger_t ledger, int entry, ledger_amount_t *credit,
2080 ledger_amount_t *debit)
2081 {
2082 struct ledger_entry *le = NULL;
2083 struct ledger_entry_small *les = NULL;
2084 uint16_t entry_size, entry_offset;
2085
2086 if (!is_entry_valid_and_active(ledger, entry)) {
2087 return KERN_INVALID_ARGUMENT;
2088 }
2089
2090 entry_size = ENTRY_ID_SIZE(entry);
2091 entry_offset = ENTRY_ID_OFFSET(entry);
2092 les = &ledger->l_entries[entry_offset];
2093 if (entry_size == sizeof(struct ledger_entry)) {
2094 le = (struct ledger_entry *)les;
2095 *credit = le->le_credit;
2096 *debit = le->le_debit;
2097 } else if (entry_size == sizeof(struct ledger_entry_small)) {
2098 *credit = les->les_credit;
2099 *debit = 0;
2100 } else {
2101 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, entry_size);
2102 }
2103
2104 return KERN_SUCCESS;
2105 }
2106
2107 kern_return_t
ledger_reset_callback_state(ledger_t ledger,int entry)2108 ledger_reset_callback_state(ledger_t ledger, int entry)
2109 {
2110 struct ledger_entry *le = NULL;
2111
2112 if (!is_entry_valid_and_active(ledger, entry)) {
2113 return KERN_INVALID_ARGUMENT;
2114 }
2115
2116 if (ENTRY_ID_SIZE(entry) != sizeof(struct ledger_entry)) {
2117 /* small entries can't have callbacks */
2118 return KERN_INVALID_ARGUMENT;
2119 }
2120
2121 le = ledger_entry_identifier_to_entry(ledger, entry);
2122
2123 flag_clear(&le->le_flags, LF_CALLED_BACK);
2124
2125 return KERN_SUCCESS;
2126 }
2127
2128 kern_return_t
ledger_disable_panic_on_negative(ledger_t ledger,int entry)2129 ledger_disable_panic_on_negative(ledger_t ledger, int entry)
2130 {
2131 volatile uint32_t *flags;
2132
2133 if (!is_entry_valid_and_active(ledger, entry)) {
2134 return KERN_INVALID_ARGUMENT;
2135 }
2136 flags = get_entry_flags(ledger, entry);
2137
2138 flag_clear(flags, LF_PANIC_ON_NEGATIVE);
2139
2140 return KERN_SUCCESS;
2141 }
2142
2143 kern_return_t
ledger_get_panic_on_negative(ledger_t ledger,int entry,int * panic_on_negative)2144 ledger_get_panic_on_negative(ledger_t ledger, int entry, int *panic_on_negative)
2145 {
2146 volatile uint32_t flags;
2147
2148 if (!is_entry_valid_and_active(ledger, entry)) {
2149 return KERN_INVALID_ARGUMENT;
2150 }
2151 flags = *get_entry_flags(ledger, entry);
2152
2153 if (flags & LF_PANIC_ON_NEGATIVE) {
2154 *panic_on_negative = TRUE;
2155 } else {
2156 *panic_on_negative = FALSE;
2157 }
2158
2159 return KERN_SUCCESS;
2160 }
2161
2162 kern_return_t
ledger_get_balance(ledger_t ledger,int entry,ledger_amount_t * balance)2163 ledger_get_balance(ledger_t ledger, int entry, ledger_amount_t *balance)
2164 {
2165 kern_return_t kr;
2166 ledger_amount_t credit, debit;
2167
2168 kr = ledger_get_entries(ledger, entry, &credit, &debit);
2169 if (kr != KERN_SUCCESS) {
2170 return kr;
2171 }
2172 *balance = credit - debit;
2173
2174 return KERN_SUCCESS;
2175 }
2176
2177 int
ledger_template_info(void ** buf,int * len)2178 ledger_template_info(void **buf, int *len)
2179 {
2180 struct ledger_template_info *lti;
2181 struct entry_template *et;
2182 ledger_template_t template;
2183 int i;
2184 ledger_t l;
2185
2186 /*
2187 * Since all tasks share a ledger template, we'll just use the
2188 * caller's as the source.
2189 */
2190 l = current_task()->ledger;
2191 if ((*len < 0) || (l == NULL)) {
2192 return EINVAL;
2193 }
2194 template = l->l_template;
2195 assert(template);
2196 assert(template->lt_initialized);
2197
2198 if (*len > template->lt_cnt) {
2199 *len = template->lt_cnt;
2200 }
2201 lti = kalloc_data((*len) * sizeof(struct ledger_template_info),
2202 Z_WAITOK);
2203 if (lti == NULL) {
2204 return ENOMEM;
2205 }
2206 *buf = lti;
2207
2208 template_lock(template);
2209 et = template->lt_entries;
2210
2211 for (i = 0; i < *len; i++) {
2212 memset(lti, 0, sizeof(*lti));
2213 strlcpy(lti->lti_name, et->et_key, LEDGER_NAME_MAX);
2214 strlcpy(lti->lti_group, et->et_group, LEDGER_NAME_MAX);
2215 strlcpy(lti->lti_units, et->et_units, LEDGER_NAME_MAX);
2216 et++;
2217 lti++;
2218 }
2219 template_unlock(template);
2220
2221 return 0;
2222 }
2223
2224 static kern_return_t
ledger_fill_entry_info(ledger_t ledger,int entry,struct ledger_entry_info * lei,uint64_t now)2225 ledger_fill_entry_info(ledger_t ledger,
2226 int entry,
2227 struct ledger_entry_info *lei,
2228 uint64_t now)
2229 {
2230 assert(ledger != NULL);
2231 assert(lei != NULL);
2232 if (!is_entry_valid(ledger, entry)) {
2233 return KERN_INVALID_ARGUMENT;
2234 }
2235 uint16_t entry_size, entry_offset;
2236 struct ledger_entry_small *les = NULL;
2237 struct ledger_entry *le = NULL;
2238 entry_size = ENTRY_ID_SIZE(entry);
2239 entry_offset = ENTRY_ID_OFFSET(entry);
2240
2241 les = &ledger->l_entries[entry_offset];
2242 memset(lei, 0, sizeof(*lei));
2243 if (entry_size == sizeof(struct ledger_entry_small)) {
2244 lei->lei_limit = LEDGER_LIMIT_INFINITY;
2245 lei->lei_credit = les->les_credit;
2246 lei->lei_debit = 0;
2247 lei->lei_refill_period = 0;
2248 lei->lei_last_refill = abstime_to_nsecs(now);
2249 } else if (entry_size == sizeof(struct ledger_entry)) {
2250 le = (struct ledger_entry *) les;
2251 lei->lei_limit = le->le_limit;
2252 lei->lei_credit = le->le_credit;
2253 lei->lei_debit = le->le_debit;
2254 lei->lei_refill_period = (le->le_flags & LF_REFILL_SCHEDULED) ?
2255 abstime_to_nsecs(le->_le.le_refill.le_refill_period) : 0;
2256 lei->lei_last_refill = abstime_to_nsecs(now - le->_le.le_refill.le_last_refill);
2257 } else {
2258 panic("Unknown ledger entry size! ledger=%p, entry=0x%x, entry_size=%d\n", ledger, entry, entry_size);
2259 }
2260
2261 lei->lei_balance = lei->lei_credit - lei->lei_debit;
2262
2263 return KERN_SUCCESS;
2264 }
2265
2266 int
ledger_get_task_entry_info_multiple(task_t task,void ** buf,int * len)2267 ledger_get_task_entry_info_multiple(task_t task, void **buf, int *len)
2268 {
2269 struct ledger_entry_info *lei_buf = NULL, *lei_curr = NULL;
2270 uint64_t now = mach_absolute_time();
2271 vm_size_t size = 0;
2272 int i;
2273 ledger_t l;
2274 ledger_template_t template;
2275 struct entry_template *et = NULL;
2276
2277 if ((*len < 0) || ((l = task->ledger) == NULL)) {
2278 return EINVAL;
2279 }
2280 template = l->l_template;
2281 assert(template && template->lt_initialized);
2282
2283 if (*len > template->lt_cnt) {
2284 *len = template->lt_cnt;
2285 }
2286 size = (*len) * sizeof(struct ledger_entry_info);
2287 lei_buf = kalloc_data(size, Z_WAITOK);
2288 if (lei_buf == NULL) {
2289 return ENOMEM;
2290 }
2291 lei_curr = lei_buf;
2292
2293 for (i = 0; i < *len; i++) {
2294 et = &template->lt_entries[i];
2295 int index = ledger_entry_id_from_template_entry(et);
2296 if (ledger_fill_entry_info(l, index, lei_curr, now) != KERN_SUCCESS) {
2297 kfree_data(lei_buf, size);
2298 lei_buf = NULL;
2299 return EINVAL;
2300 }
2301 lei_curr++;
2302 }
2303
2304 *buf = lei_buf;
2305 return 0;
2306 }
2307
2308 void
ledger_get_entry_info(ledger_t ledger,int entry,struct ledger_entry_info * lei)2309 ledger_get_entry_info(ledger_t ledger,
2310 int entry,
2311 struct ledger_entry_info *lei)
2312 {
2313 uint64_t now = mach_absolute_time();
2314
2315 assert(ledger != NULL);
2316 assert(lei != NULL);
2317
2318 ledger_fill_entry_info(ledger, entry, lei, now);
2319 }
2320
2321 int
ledger_info(task_t task,struct ledger_info * info)2322 ledger_info(task_t task, struct ledger_info *info)
2323 {
2324 ledger_t l;
2325
2326 if ((l = task->ledger) == NULL) {
2327 return ENOENT;
2328 }
2329
2330 memset(info, 0, sizeof(*info));
2331
2332 strlcpy(info->li_name, l->l_template->lt_name, LEDGER_NAME_MAX);
2333 info->li_id = l->l_id;
2334 info->li_entries = l->l_template->lt_cnt;
2335 return 0;
2336 }
2337
2338 #ifdef LEDGER_DEBUG
2339 int
ledger_limit(task_t task,struct ledger_limit_args * args)2340 ledger_limit(task_t task, struct ledger_limit_args *args)
2341 {
2342 ledger_t l;
2343 int64_t limit;
2344 int idx;
2345
2346 if ((l = task->ledger) == NULL) {
2347 return EINVAL;
2348 }
2349
2350 idx = ledger_key_lookup(l->l_template, args->lla_name);
2351 if (idx < 0) {
2352 return EINVAL;
2353 }
2354 if (ENTRY_ID_SIZE(idx) == sizeof(ledger_entry_small)) {
2355 /* Small entries can't have limits */
2356 return EINVAL;
2357 }
2358
2359 /*
2360 * XXX - this doesn't really seem like the right place to have
2361 * a context-sensitive conversion of userspace units into kernel
2362 * units. For now I'll handwave and say that the ledger() system
2363 * call isn't meant for civilians to use - they should be using
2364 * the process policy interfaces.
2365 */
2366 if (idx == task_ledgers.cpu_time) {
2367 int64_t nsecs;
2368
2369 if (args->lla_refill_period) {
2370 /*
2371 * If a refill is scheduled, then the limit is
2372 * specified as a percentage of one CPU. The
2373 * syscall specifies the refill period in terms of
2374 * milliseconds, so we need to convert to nsecs.
2375 */
2376 args->lla_refill_period *= 1000000;
2377 nsecs = args->lla_limit *
2378 (args->lla_refill_period / 100);
2379 lprintf(("CPU limited to %lld nsecs per second\n",
2380 nsecs));
2381 } else {
2382 /*
2383 * If no refill is scheduled, then this is a
2384 * fixed amount of CPU time (in nsecs) that can
2385 * be consumed.
2386 */
2387 nsecs = args->lla_limit;
2388 lprintf(("CPU limited to %lld nsecs\n", nsecs));
2389 }
2390 limit = nsecs_to_abstime(nsecs);
2391 } else {
2392 limit = args->lla_limit;
2393 lprintf(("%s limited to %lld\n", args->lla_name, limit));
2394 }
2395
2396 if (args->lla_refill_period > 0) {
2397 ledger_set_period(l, idx, args->lla_refill_period);
2398 }
2399
2400 ledger_set_limit(l, idx, limit);
2401
2402 flag_set(ledger_entry_identifier_to_entry(l, idx)->le_flags, LEDGER_ACTION_BLOCK);
2403 return 0;
2404 }
2405 #endif
2406