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