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