xref: /xnu-8019.80.24/osfmk/kern/ledger.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
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)), &lt_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