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