2 * Copyright (c) 2010-2020 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
39 #include <kern/processor.h>
40 #include <kern/machine.h>
41 #include <kern/queue.h>
42 #include <kern/policy_internal.h>
44 #include <sys/errno.h>
46 #include <libkern/OSAtomic.h>
47 #include <mach/mach_types.h>
48 #include <os/overflow.h>
53 * Ledger entry flags. Bits in second nibble (masked by 0xF0) are used for
54 * ledger actions (LEDGER_ACTION_BLOCK, etc).
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" */
67 /* Determine whether a ledger entry exists and has been initialized and active */
68 #define ENTRY_VALID(l, e) \
69 (((l) != NULL) && ((e) >= 0) && ((e) < (l)->l_size) && \
70 (((l)->l_entries[e].le_flags & LF_ENTRY_ACTIVE) == LF_ENTRY_ACTIVE))
72 #define ASSERT(a) assert(a)
77 #define lprintf(a) if (ledger_debug) { \
78 printf("%lld ", abstime_to_nsecs(mach_absolute_time() / 1000000)); \
85 struct ledger_callback
{
86 ledger_callback_t lc_func
;
87 const void *lc_param0
;
88 const void *lc_param1
;
91 struct entry_template
{
92 char et_key
[LEDGER_NAME_MAX
];
93 char et_group
[LEDGER_NAME_MAX
];
94 char et_units
[LEDGER_NAME_MAX
];
96 struct ledger_callback
*et_callback
;
99 LCK_GRP_DECLARE(ledger_lck_grp
, "ledger");
100 os_refgrp_decl(static, ledger_refgrp
, "ledger", NULL
);
103 * Modifying the reference count, table size, or table contents requires
104 * holding the lt_lock. Modfying the table address requires both lt_lock
105 * and setting the inuse bit. This means that the lt_entries field can be
106 * safely dereferenced if you hold either the lock or the inuse bit. The
107 * inuse bit exists solely to allow us to swap in a new, larger entries
108 * table without requiring a full lock to be acquired on each lookup.
109 * Accordingly, the inuse bit should never be held for longer than it takes
110 * to extract a value from the table - i.e., 2 or 3 memory references.
112 struct ledger_template
{
117 volatile uint32_t lt_inuse
;
121 struct entry_template
*lt_entries
;
124 #define template_lock(template) lck_mtx_lock(&(template)->lt_lock)
125 #define template_unlock(template) lck_mtx_unlock(&(template)->lt_lock)
127 #define TEMPLATE_INUSE(s, t) { \
129 while (OSCompareAndSwap(0, 1, &((t)->lt_inuse))) \
133 #define TEMPLATE_IDLE(s, t) { \
138 static int ledger_cnt
= 0;
139 /* ledger ast helper functions */
140 static uint32_t ledger_check_needblock(ledger_t l
, uint64_t now
);
141 static kern_return_t
ledger_perform_blocking(ledger_t l
);
142 static uint32_t flag_set(volatile uint32_t *flags
, uint32_t bit
);
143 static uint32_t flag_clear(volatile uint32_t *flags
, uint32_t bit
);
145 static void ledger_entry_check_new_balance(thread_t thread
, ledger_t ledger
,
146 int entry
, struct ledger_entry
*le
);
150 debug_callback(const void *p0
, __unused
const void *p1
)
152 printf("ledger: resource exhausted [%s] for task %p\n",
153 (const char *)p0
, p1
);
157 /************************************/
160 abstime_to_nsecs(uint64_t abstime
)
164 absolutetime_to_nanoseconds(abstime
, &nsecs
);
169 nsecs_to_abstime(uint64_t nsecs
)
173 nanoseconds_to_absolutetime(nsecs
, &abstime
);
178 ledger_template_create(const char *name
)
180 ledger_template_t
template;
182 template = (ledger_template_t
)kalloc(sizeof(*template));
183 if (template == NULL
) {
187 template->lt_name
= name
;
188 template->lt_refs
= 1;
189 template->lt_cnt
= 0;
190 template->lt_table_size
= 1;
191 template->lt_inuse
= 0;
192 template->lt_zone
= NULL
;
193 lck_mtx_init(&template->lt_lock
, &ledger_lck_grp
, LCK_ATTR_NULL
);
195 template->lt_entries
= (struct entry_template
*)
196 kalloc(sizeof(struct entry_template
) * template->lt_table_size
);
197 if (template->lt_entries
== NULL
) {
198 kfree(template, sizeof(*template));
206 ledger_template_copy(ledger_template_t
template, const char *name
)
208 struct entry_template
* new_entries
= NULL
;
209 ledger_template_t new_template
= ledger_template_create(name
);
211 if (new_template
== NULL
) {
215 template_lock(template);
216 assert(template->lt_initialized
);
218 new_entries
= (struct entry_template
*)
219 kalloc(sizeof(struct entry_template
) * template->lt_table_size
);
222 /* Copy the template entries. */
223 bcopy(template->lt_entries
, new_entries
, sizeof(struct entry_template
) * template->lt_table_size
);
224 kfree(new_template
->lt_entries
, sizeof(struct entry_template
) * new_template
->lt_table_size
);
226 new_template
->lt_entries
= new_entries
;
227 new_template
->lt_table_size
= template->lt_table_size
;
228 new_template
->lt_cnt
= template->lt_cnt
;
230 /* Tear down the new template; we've failed. :( */
231 ledger_template_dereference(new_template
);
235 template_unlock(template);
241 ledger_template_dereference(ledger_template_t
template)
243 template_lock(template);
245 template_unlock(template);
247 if (template->lt_refs
== 0) {
248 kfree(template->lt_entries
, sizeof(struct entry_template
) * template->lt_table_size
);
249 lck_mtx_destroy(&template->lt_lock
, &ledger_lck_grp
);
250 kfree(template, sizeof(*template));
255 * Add a new entry to the list of entries in a ledger template. There is
256 * currently no mechanism to remove an entry. Implementing such a mechanism
257 * would require us to maintain per-entry reference counts, which we would
258 * prefer to avoid if possible.
261 ledger_entry_add(ledger_template_t
template, const char *key
,
262 const char *group
, const char *units
)
265 struct entry_template
*et
;
267 if ((key
== NULL
) || (strlen(key
) >= LEDGER_NAME_MAX
) || (template->lt_zone
!= NULL
)) {
271 template_lock(template);
273 /* If the table is full, attempt to double its size */
274 if (template->lt_cnt
== template->lt_table_size
) {
275 struct entry_template
*new_entries
, *old_entries
;
276 int old_cnt
, old_sz
, new_sz
= 0;
279 old_cnt
= template->lt_table_size
;
280 old_sz
= old_cnt
* (int)(sizeof(struct entry_template
));
281 /* double old_sz allocation, but check for overflow */
282 if (os_mul_overflow(old_sz
, 2, &new_sz
)) {
283 template_unlock(template);
286 new_entries
= kalloc(new_sz
);
287 if (new_entries
== NULL
) {
288 template_unlock(template);
291 memcpy(new_entries
, template->lt_entries
, old_sz
);
292 memset(((char *)new_entries
) + old_sz
, 0, old_sz
);
293 /* assume: if the sz didn't overflow, neither will the count */
294 template->lt_table_size
= old_cnt
* 2;
296 old_entries
= template->lt_entries
;
298 TEMPLATE_INUSE(s
, template);
299 template->lt_entries
= new_entries
;
300 TEMPLATE_IDLE(s
, template);
302 kfree(old_entries
, old_sz
);
305 et
= &template->lt_entries
[template->lt_cnt
];
306 strlcpy(et
->et_key
, key
, LEDGER_NAME_MAX
);
307 strlcpy(et
->et_group
, group
, LEDGER_NAME_MAX
);
308 strlcpy(et
->et_units
, units
, LEDGER_NAME_MAX
);
309 et
->et_flags
= LF_ENTRY_ACTIVE
;
310 et
->et_callback
= NULL
;
312 idx
= template->lt_cnt
++;
313 template_unlock(template);
320 ledger_entry_setactive(ledger_t ledger
, int entry
)
322 struct ledger_entry
*le
;
324 if ((ledger
== NULL
) || (entry
< 0) || (entry
>= ledger
->l_size
)) {
325 return KERN_INVALID_ARGUMENT
;
328 le
= &ledger
->l_entries
[entry
];
329 if ((le
->le_flags
& LF_ENTRY_ACTIVE
) == 0) {
330 flag_set(&le
->le_flags
, LF_ENTRY_ACTIVE
);
337 ledger_key_lookup(ledger_template_t
template, const char *key
)
341 template_lock(template);
342 for (idx
= 0; idx
< template->lt_cnt
; idx
++) {
343 if (template->lt_entries
!= NULL
&&
344 (strcmp(key
, template->lt_entries
[idx
].et_key
) == 0)) {
349 if (idx
>= template->lt_cnt
) {
352 template_unlock(template);
358 * Complete the initialization of ledger template
359 * by initializing ledger zone. After initializing
360 * the ledger zone, adding an entry in the ledger
361 * template would fail.
364 ledger_template_complete(ledger_template_t
template)
367 ledger_size
= sizeof(struct ledger
) + (template->lt_cnt
* sizeof(struct ledger_entry
));
368 template->lt_zone
= zone_create(template->lt_name
, ledger_size
, ZC_NONE
);
369 template->lt_initialized
= true;
373 * Like ledger_template_complete, except we'll ask
374 * the pmap layer to manage allocations for us.
375 * Meant for ledgers that should be owned by the
379 ledger_template_complete_secure_alloc(ledger_template_t
template)
382 ledger_size
= sizeof(struct ledger
) + (template->lt_cnt
* sizeof(struct ledger_entry
));
383 pmap_ledger_alloc_init(ledger_size
);
384 template->lt_initialized
= true;
388 * Create a new ledger based on the specified template. As part of the
389 * ledger creation we need to allocate space for a table of ledger entries.
390 * The size of the table is based on the size of the template at the time
391 * the ledger is created. If additional entries are added to the template
392 * after the ledger is created, they will not be tracked in this ledger.
395 ledger_instantiate(ledger_template_t
template, int entry_type
)
401 template_lock(template);
403 cnt
= template->lt_cnt
;
404 template_unlock(template);
406 if (template->lt_zone
) {
407 ledger
= (ledger_t
)zalloc(template->lt_zone
);
409 ledger
= pmap_ledger_alloc();
412 if (ledger
== NULL
) {
413 ledger_template_dereference(template);
417 ledger
->l_template
= template;
418 ledger
->l_id
= ledger_cnt
++;
419 os_ref_init(&ledger
->l_refs
, &ledger_refgrp
);
420 ledger
->l_size
= (int32_t)cnt
;
422 template_lock(template);
423 assert(ledger
->l_size
<= template->lt_cnt
);
424 for (i
= 0; i
< ledger
->l_size
; i
++) {
425 struct ledger_entry
*le
= &ledger
->l_entries
[i
];
426 struct entry_template
*et
= &template->lt_entries
[i
];
428 le
->le_flags
= et
->et_flags
;
429 /* make entry inactive by removing active bit */
430 if (entry_type
== LEDGER_CREATE_INACTIVE_ENTRIES
) {
431 flag_clear(&le
->le_flags
, LF_ENTRY_ACTIVE
);
434 * If template has a callback, this entry is opted-in,
437 if (et
->et_callback
!= NULL
) {
438 flag_set(&le
->le_flags
, LEDGER_ACTION_CALLBACK
);
442 le
->le_limit
= LEDGER_LIMIT_INFINITY
;
443 le
->le_warn_percent
= LEDGER_PERCENT_NONE
;
444 le
->_le
.le_refill
.le_refill_period
= 0;
445 le
->_le
.le_refill
.le_last_refill
= 0;
447 template_unlock(template);
453 flag_set(volatile uint32_t *flags
, uint32_t bit
)
455 return OSBitOrAtomic(bit
, flags
);
459 flag_clear(volatile uint32_t *flags
, uint32_t bit
)
461 return OSBitAndAtomic(~bit
, flags
);
465 * Take a reference on a ledger
468 ledger_reference(ledger_t ledger
)
470 if (!LEDGER_VALID(ledger
)) {
474 os_ref_retain(&ledger
->l_refs
);
478 * Remove a reference on a ledger. If this is the last reference,
479 * deallocate the unused ledger.
482 ledger_dereference(ledger_t ledger
)
484 if (!LEDGER_VALID(ledger
)) {
488 if (os_ref_release(&ledger
->l_refs
) == 0) {
489 if (ledger
->l_template
->lt_zone
) {
490 zfree(ledger
->l_template
->lt_zone
, ledger
);
492 pmap_ledger_free(ledger
);
498 * Determine whether an entry has exceeded its warning level.
501 warn_level_exceeded(struct ledger_entry
*le
)
503 ledger_amount_t balance
;
505 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
506 assert(le
->le_debit
== 0);
508 assert((le
->le_credit
>= 0) && (le
->le_debit
>= 0));
512 * XXX - Currently, we only support warnings for ledgers which
513 * use positive limits.
515 balance
= le
->le_credit
- le
->le_debit
;
516 if (le
->le_warn_percent
!= LEDGER_PERCENT_NONE
&&
517 ((balance
> (le
->le_limit
* le
->le_warn_percent
) >> 16))) {
524 * Determine whether an entry has exceeded its limit.
527 limit_exceeded(struct ledger_entry
*le
)
529 ledger_amount_t balance
;
531 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
532 assert(le
->le_debit
== 0);
534 assert((le
->le_credit
>= 0) && (le
->le_debit
>= 0));
537 balance
= le
->le_credit
- le
->le_debit
;
538 if ((le
->le_limit
<= 0) && (balance
< le
->le_limit
)) {
542 if ((le
->le_limit
> 0) && (balance
> le
->le_limit
)) {
548 static inline struct ledger_callback
*
549 entry_get_callback(ledger_t ledger
, int entry
)
551 struct ledger_callback
*callback
;
554 TEMPLATE_INUSE(s
, ledger
->l_template
);
555 callback
= ledger
->l_template
->lt_entries
[entry
].et_callback
;
556 TEMPLATE_IDLE(s
, ledger
->l_template
);
562 * If the ledger value is positive, wake up anybody waiting on it.
565 ledger_limit_entry_wakeup(struct ledger_entry
*le
)
569 if (!limit_exceeded(le
)) {
570 flags
= flag_clear(&le
->le_flags
, LF_CALLED_BACK
);
572 while (le
->le_flags
& LF_WAKE_NEEDED
) {
573 flag_clear(&le
->le_flags
, LF_WAKE_NEEDED
);
574 thread_wakeup((event_t
)le
);
580 * Refill the coffers.
583 ledger_refill(uint64_t now
, ledger_t ledger
, int entry
)
585 uint64_t elapsed
, period
, periods
;
586 struct ledger_entry
*le
;
587 ledger_amount_t balance
, due
;
589 assert(entry
>= 0 && entry
< ledger
->l_size
);
591 le
= &ledger
->l_entries
[entry
];
593 assert(le
->le_limit
!= LEDGER_LIMIT_INFINITY
);
595 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
596 assert(le
->le_debit
== 0);
601 * If another thread is handling the refill already, we're not
604 if (flag_set(&le
->le_flags
, LF_REFILL_INPROGRESS
) & LF_REFILL_INPROGRESS
) {
609 * If the timestamp we're about to use to refill is older than the
610 * last refill, then someone else has already refilled this ledger
611 * and there's nothing for us to do here.
613 if (now
<= le
->_le
.le_refill
.le_last_refill
) {
614 flag_clear(&le
->le_flags
, LF_REFILL_INPROGRESS
);
619 * See how many refill periods have passed since we last
622 period
= le
->_le
.le_refill
.le_refill_period
;
623 elapsed
= now
- le
->_le
.le_refill
.le_last_refill
;
624 if ((period
== 0) || (elapsed
< period
)) {
625 flag_clear(&le
->le_flags
, LF_REFILL_INPROGRESS
);
630 * Optimize for the most common case of only one or two
634 while ((periods
< 2) && (elapsed
> 0)) {
640 * OK, it's been a long time. Do a divide to figure out
644 periods
= (now
- le
->_le
.le_refill
.le_last_refill
) / period
;
647 balance
= le
->le_credit
- le
->le_debit
;
648 due
= periods
* le
->le_limit
;
650 if (balance
- due
< 0) {
654 if (due
< 0 && (le
->le_flags
& LF_PANIC_ON_NEGATIVE
)) {
655 assertf(due
>= 0, "now=%llu, ledger=%p, entry=%d, balance=%lld, due=%lld", now
, ledger
, entry
, balance
, due
);
657 OSAddAtomic64(due
, &le
->le_debit
);
658 assert(le
->le_debit
>= 0);
661 * If we've completely refilled the pool, set the refill time to now.
662 * Otherwise set it to the time at which it last should have been
665 if (balance
== due
) {
666 le
->_le
.le_refill
.le_last_refill
= now
;
668 le
->_le
.le_refill
.le_last_refill
+= (le
->_le
.le_refill
.le_refill_period
* periods
);
671 flag_clear(&le
->le_flags
, LF_REFILL_INPROGRESS
);
673 lprintf(("Refill %lld %lld->%lld\n", periods
, balance
, balance
- due
));
674 if (!limit_exceeded(le
)) {
675 ledger_limit_entry_wakeup(le
);
680 ledger_entry_check_new_balance(thread_t thread
, ledger_t ledger
,
681 int entry
, struct ledger_entry
*le
)
683 if (le
->le_flags
& LF_TRACKING_MAX
) {
684 ledger_amount_t balance
= le
->le_credit
- le
->le_debit
;
686 if (balance
> le
->_le
._le_max
.le_lifetime_max
) {
687 le
->_le
._le_max
.le_lifetime_max
= balance
;
690 #if CONFIG_LEDGER_INTERVAL_MAX
691 if (balance
> le
->_le
._le_max
.le_interval_max
) {
692 le
->_le
._le_max
.le_interval_max
= balance
;
694 #endif /* LEDGER_CONFIG_INTERVAL_MAX */
697 /* Check to see whether we're due a refill */
698 if (le
->le_flags
& LF_REFILL_SCHEDULED
) {
699 assert(!(le
->le_flags
& LF_TRACKING_MAX
));
701 uint64_t now
= mach_absolute_time();
702 if ((now
- le
->_le
.le_refill
.le_last_refill
) > le
->_le
.le_refill
.le_refill_period
) {
703 ledger_refill(now
, ledger
, entry
);
707 if (limit_exceeded(le
)) {
709 * We've exceeded the limit for this entry. There
710 * are several possible ways to handle it. We can block,
711 * we can execute a callback, or we can ignore it. In
712 * either of the first two cases, we want to set the AST
713 * flag so we can take the appropriate action just before
714 * leaving the kernel. The one caveat is that if we have
715 * already called the callback, we don't want to do it
716 * again until it gets rearmed.
718 if ((le
->le_flags
& LEDGER_ACTION_BLOCK
) ||
719 (!(le
->le_flags
& LF_CALLED_BACK
) &&
720 entry_get_callback(ledger
, entry
))) {
721 act_set_astledger_async(thread
);
725 * The balance on the account is below the limit.
727 * If there are any threads blocked on this entry, now would
728 * be a good time to wake them up.
730 if (le
->le_flags
& LF_WAKE_NEEDED
) {
731 ledger_limit_entry_wakeup(le
);
734 if (le
->le_flags
& LEDGER_ACTION_CALLBACK
) {
736 * Client has requested that a callback be invoked whenever
737 * the ledger's balance crosses into or out of the warning
740 if (warn_level_exceeded(le
)) {
742 * This ledger's balance is above the warning level.
744 if ((le
->le_flags
& LF_WARNED
) == 0) {
746 * If we are above the warning level and
747 * have not yet invoked the callback,
748 * set the AST so it can be done before returning
751 act_set_astledger_async(thread
);
755 * This ledger's balance is below the warning level.
757 if (le
->le_flags
& LF_WARNED
) {
759 * If we are below the warning level and
760 * the LF_WARNED flag is still set, we need
761 * to invoke the callback to let the client
762 * know the ledger balance is now back below
765 act_set_astledger_async(thread
);
771 if ((le
->le_flags
& LF_PANIC_ON_NEGATIVE
) &&
772 (le
->le_credit
< le
->le_debit
)) {
773 panic("ledger_entry_check_new_balance(%p,%d): negative ledger %p credit:%lld debit:%lld balance:%lld\n",
777 le
->le_credit
- le
->le_debit
);
782 ledger_check_new_balance(thread_t thread
, ledger_t ledger
, int entry
)
784 struct ledger_entry
*le
;
785 assert(entry
> 0 && entry
<= ledger
->l_size
);
786 le
= &ledger
->l_entries
[entry
];
787 ledger_entry_check_new_balance(thread
, ledger
, entry
, le
);
791 * Add value to an entry in a ledger for a specific thread.
794 ledger_credit_thread(thread_t thread
, ledger_t ledger
, int entry
, ledger_amount_t amount
)
796 ledger_amount_t old
, new;
797 struct ledger_entry
*le
;
799 if (!ENTRY_VALID(ledger
, entry
) || (amount
< 0)) {
800 return KERN_INVALID_VALUE
;
807 le
= &ledger
->l_entries
[entry
];
809 old
= OSAddAtomic64(amount
, &le
->le_credit
);
811 lprintf(("%p Credit %lld->%lld\n", thread
, old
, new));
814 ledger_entry_check_new_balance(thread
, ledger
, entry
, le
);
821 * Add value to an entry in a ledger.
824 ledger_credit(ledger_t ledger
, int entry
, ledger_amount_t amount
)
826 return ledger_credit_thread(current_thread(), ledger
, entry
, amount
);
830 * Add value to an entry in a ledger; do not check balance after update.
833 ledger_credit_nocheck(ledger_t ledger
, int entry
, ledger_amount_t amount
)
835 return ledger_credit_thread(NULL
, ledger
, entry
, amount
);
838 /* Add all of one ledger's values into another.
839 * They must have been created from the same template.
840 * This is not done atomically. Another thread (if not otherwise synchronized)
841 * may see bogus values when comparing one entry to another.
842 * As each entry's credit & debit are modified one at a time, the warning/limit
843 * may spuriously trip, or spuriously fail to trip, or another thread (if not
844 * otherwise synchronized) may see a bogus balance.
847 ledger_rollup(ledger_t to_ledger
, ledger_t from_ledger
)
851 assert(to_ledger
->l_template
->lt_cnt
== from_ledger
->l_template
->lt_cnt
);
853 for (i
= 0; i
< to_ledger
->l_size
; i
++) {
854 ledger_rollup_entry(to_ledger
, from_ledger
, i
);
860 /* Add one ledger entry value to another.
861 * They must have been created from the same template.
862 * Since the credit and debit values are added one
863 * at a time, other thread might read the a bogus value.
866 ledger_rollup_entry(ledger_t to_ledger
, ledger_t from_ledger
, int entry
)
868 struct ledger_entry
*from_le
, *to_le
;
870 assert(to_ledger
->l_template
->lt_cnt
== from_ledger
->l_template
->lt_cnt
);
871 if (ENTRY_VALID(from_ledger
, entry
) && ENTRY_VALID(to_ledger
, entry
)) {
872 from_le
= &from_ledger
->l_entries
[entry
];
873 to_le
= &to_ledger
->l_entries
[entry
];
874 OSAddAtomic64(from_le
->le_credit
, &to_le
->le_credit
);
875 OSAddAtomic64(from_le
->le_debit
, &to_le
->le_debit
);
882 * Zero the balance of a ledger by adding to its credit or debit, whichever is smaller.
883 * Note that some clients of ledgers (notably, task wakeup statistics) require that
884 * le_credit only ever increase as a function of ledger_credit().
887 ledger_zero_balance(ledger_t ledger
, int entry
)
889 struct ledger_entry
*le
;
890 ledger_amount_t debit
, credit
;
892 if (!ENTRY_VALID(ledger
, entry
)) {
893 return KERN_INVALID_VALUE
;
896 le
= &ledger
->l_entries
[entry
];
899 debit
= le
->le_debit
;
900 credit
= le
->le_credit
;
902 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
903 assert(le
->le_debit
== 0);
904 if (!OSCompareAndSwap64(credit
, 0, &le
->le_credit
)) {
907 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le
->le_credit
, 0));
908 } else if (credit
> debit
) {
909 if (!OSCompareAndSwap64(debit
, credit
, &le
->le_debit
)) {
912 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le
->le_debit
, le
->le_credit
));
913 } else if (credit
< debit
) {
914 if (!OSCompareAndSwap64(credit
, debit
, &le
->le_credit
)) {
917 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le
->le_credit
, le
->le_debit
));
924 ledger_get_limit(ledger_t ledger
, int entry
, ledger_amount_t
*limit
)
926 struct ledger_entry
*le
;
928 if (!ENTRY_VALID(ledger
, entry
)) {
929 return KERN_INVALID_VALUE
;
932 le
= &ledger
->l_entries
[entry
];
933 *limit
= le
->le_limit
;
935 lprintf(("ledger_get_limit: %lld\n", *limit
));
941 * Adjust the limit of a limited resource. This does not affect the
942 * current balance, so the change doesn't affect the thread until the
945 * warn_level: If non-zero, causes the callback to be invoked when
946 * the balance exceeds this level. Specified as a percentage [of the limit].
949 ledger_set_limit(ledger_t ledger
, int entry
, ledger_amount_t limit
,
950 uint8_t warn_level_percentage
)
952 struct ledger_entry
*le
;
954 if (!ENTRY_VALID(ledger
, entry
)) {
955 return KERN_INVALID_VALUE
;
958 lprintf(("ledger_set_limit: %lld\n", limit
));
959 le
= &ledger
->l_entries
[entry
];
961 if (limit
== LEDGER_LIMIT_INFINITY
) {
963 * Caller wishes to disable the limit. This will implicitly
964 * disable automatic refill, as refills implicitly depend
967 ledger_disable_refill(ledger
, entry
);
970 le
->le_limit
= limit
;
971 if (le
->le_flags
& LF_REFILL_SCHEDULED
) {
972 assert(!(le
->le_flags
& LF_TRACKING_MAX
));
973 le
->_le
.le_refill
.le_last_refill
= 0;
975 flag_clear(&le
->le_flags
, LF_CALLED_BACK
);
976 flag_clear(&le
->le_flags
, LF_WARNED
);
977 ledger_limit_entry_wakeup(le
);
979 if (warn_level_percentage
!= 0) {
980 assert(warn_level_percentage
<= 100);
981 assert(limit
> 0); /* no negative limit support for warnings */
982 assert(limit
!= LEDGER_LIMIT_INFINITY
); /* warn % without limit makes no sense */
983 le
->le_warn_percent
= warn_level_percentage
* (1u << 16) / 100;
985 le
->le_warn_percent
= LEDGER_PERCENT_NONE
;
991 #if CONFIG_LEDGER_INTERVAL_MAX
993 ledger_get_interval_max(ledger_t ledger
, int entry
,
994 ledger_amount_t
*max_interval_balance
, int reset
)
996 struct ledger_entry
*le
;
997 le
= &ledger
->l_entries
[entry
];
999 if (!ENTRY_VALID(ledger
, entry
) || !(le
->le_flags
& LF_TRACKING_MAX
)) {
1000 return KERN_INVALID_VALUE
;
1003 *max_interval_balance
= le
->_le
._le_max
.le_interval_max
;
1004 lprintf(("ledger_get_interval_max: %lld%s\n", *max_interval_balance
,
1005 (reset
) ? " --> 0" : ""));
1008 le
->_le
._le_max
.le_interval_max
= 0;
1011 return KERN_SUCCESS
;
1013 #endif /* CONFIG_LEDGER_INTERVAL_MAX */
1016 ledger_get_lifetime_max(ledger_t ledger
, int entry
,
1017 ledger_amount_t
*max_lifetime_balance
)
1019 struct ledger_entry
*le
;
1020 le
= &ledger
->l_entries
[entry
];
1022 if (!ENTRY_VALID(ledger
, entry
) || !(le
->le_flags
& LF_TRACKING_MAX
)) {
1023 return KERN_INVALID_VALUE
;
1026 *max_lifetime_balance
= le
->_le
._le_max
.le_lifetime_max
;
1027 lprintf(("ledger_get_lifetime_max: %lld\n", *max_lifetime_balance
));
1029 return KERN_SUCCESS
;
1033 * Enable tracking of periodic maximums for this ledger entry.
1036 ledger_track_maximum(ledger_template_t
template, int entry
,
1037 __unused
int period_in_secs
)
1039 template_lock(template);
1041 if ((entry
< 0) || (entry
>= template->lt_cnt
)) {
1042 template_unlock(template);
1043 return KERN_INVALID_VALUE
;
1046 /* Refill is incompatible with max tracking. */
1047 if (template->lt_entries
[entry
].et_flags
& LF_REFILL_SCHEDULED
) {
1048 return KERN_INVALID_VALUE
;
1051 template->lt_entries
[entry
].et_flags
|= LF_TRACKING_MAX
;
1052 template_unlock(template);
1054 return KERN_SUCCESS
;
1058 ledger_panic_on_negative(ledger_template_t
template, int entry
)
1060 template_lock(template);
1062 if ((entry
< 0) || (entry
>= template->lt_cnt
)) {
1063 template_unlock(template);
1064 return KERN_INVALID_VALUE
;
1067 template->lt_entries
[entry
].et_flags
|= LF_PANIC_ON_NEGATIVE
;
1069 template_unlock(template);
1071 return KERN_SUCCESS
;
1075 ledger_track_credit_only(ledger_template_t
template, int entry
)
1077 template_lock(template);
1079 if ((entry
< 0) || (entry
>= template->lt_cnt
)) {
1080 template_unlock(template);
1081 return KERN_INVALID_VALUE
;
1084 template->lt_entries
[entry
].et_flags
|= LF_TRACK_CREDIT_ONLY
;
1086 template_unlock(template);
1088 return KERN_SUCCESS
;
1092 * Add a callback to be executed when the resource goes into deficit.
1095 ledger_set_callback(ledger_template_t
template, int entry
,
1096 ledger_callback_t func
, const void *param0
, const void *param1
)
1098 struct entry_template
*et
;
1099 struct ledger_callback
*old_cb
, *new_cb
;
1101 if ((entry
< 0) || (entry
>= template->lt_cnt
)) {
1102 return KERN_INVALID_VALUE
;
1106 new_cb
= (struct ledger_callback
*)kalloc(sizeof(*new_cb
));
1107 new_cb
->lc_func
= func
;
1108 new_cb
->lc_param0
= param0
;
1109 new_cb
->lc_param1
= param1
;
1114 template_lock(template);
1115 et
= &template->lt_entries
[entry
];
1116 old_cb
= et
->et_callback
;
1117 et
->et_callback
= new_cb
;
1118 template_unlock(template);
1120 kfree(old_cb
, sizeof(*old_cb
));
1123 return KERN_SUCCESS
;
1127 * Disable callback notification for a specific ledger entry.
1129 * Otherwise, if using a ledger template which specified a
1130 * callback function (ledger_set_callback()), it will be invoked when
1131 * the resource goes into deficit.
1134 ledger_disable_callback(ledger_t ledger
, int entry
)
1136 if (!ENTRY_VALID(ledger
, entry
)) {
1137 return KERN_INVALID_VALUE
;
1141 * le_warn_percent is used to indicate *if* this ledger has a warning configured,
1142 * in addition to what that warning level is set to.
1143 * This means a side-effect of ledger_disable_callback() is that the
1144 * warning level is forgotten.
1146 ledger
->l_entries
[entry
].le_warn_percent
= LEDGER_PERCENT_NONE
;
1147 flag_clear(&ledger
->l_entries
[entry
].le_flags
, LEDGER_ACTION_CALLBACK
);
1148 return KERN_SUCCESS
;
1152 * Enable callback notification for a specific ledger entry.
1154 * This is only needed if ledger_disable_callback() has previously
1155 * been invoked against an entry; there must already be a callback
1159 ledger_enable_callback(ledger_t ledger
, int entry
)
1161 if (!ENTRY_VALID(ledger
, entry
)) {
1162 return KERN_INVALID_VALUE
;
1165 assert(entry_get_callback(ledger
, entry
) != NULL
);
1167 flag_set(&ledger
->l_entries
[entry
].le_flags
, LEDGER_ACTION_CALLBACK
);
1168 return KERN_SUCCESS
;
1172 * Query the automatic refill period for this ledger entry.
1174 * A period of 0 means this entry has none configured.
1177 ledger_get_period(ledger_t ledger
, int entry
, uint64_t *period
)
1179 struct ledger_entry
*le
;
1181 if (!ENTRY_VALID(ledger
, entry
)) {
1182 return KERN_INVALID_VALUE
;
1185 le
= &ledger
->l_entries
[entry
];
1186 *period
= abstime_to_nsecs(le
->_le
.le_refill
.le_refill_period
);
1187 lprintf(("ledger_get_period: %llx\n", *period
));
1188 return KERN_SUCCESS
;
1192 * Adjust the automatic refill period.
1195 ledger_set_period(ledger_t ledger
, int entry
, uint64_t period
)
1197 struct ledger_entry
*le
;
1199 lprintf(("ledger_set_period: %llx\n", period
));
1200 if (!ENTRY_VALID(ledger
, entry
)) {
1201 return KERN_INVALID_VALUE
;
1204 le
= &ledger
->l_entries
[entry
];
1207 * A refill period refills the ledger in multiples of the limit,
1208 * so if you haven't set one yet, you need a lesson on ledgers.
1210 assert(le
->le_limit
!= LEDGER_LIMIT_INFINITY
);
1212 if (le
->le_flags
& LF_TRACKING_MAX
) {
1214 * Refill is incompatible with rolling max tracking.
1216 return KERN_INVALID_VALUE
;
1219 le
->_le
.le_refill
.le_refill_period
= nsecs_to_abstime(period
);
1222 * Set the 'starting time' for the next refill to now. Since
1223 * we're resetting the balance to zero here, we consider this
1224 * moment the starting time for accumulating a balance that
1225 * counts towards the limit.
1227 le
->_le
.le_refill
.le_last_refill
= mach_absolute_time();
1228 ledger_zero_balance(ledger
, entry
);
1230 flag_set(&le
->le_flags
, LF_REFILL_SCHEDULED
);
1232 return KERN_SUCCESS
;
1236 * Disable automatic refill.
1239 ledger_disable_refill(ledger_t ledger
, int entry
)
1241 struct ledger_entry
*le
;
1243 if (!ENTRY_VALID(ledger
, entry
)) {
1244 return KERN_INVALID_VALUE
;
1247 le
= &ledger
->l_entries
[entry
];
1249 flag_clear(&le
->le_flags
, LF_REFILL_SCHEDULED
);
1251 return KERN_SUCCESS
;
1255 ledger_get_actions(ledger_t ledger
, int entry
, int *actions
)
1257 if (!ENTRY_VALID(ledger
, entry
)) {
1258 return KERN_INVALID_VALUE
;
1261 *actions
= ledger
->l_entries
[entry
].le_flags
& LEDGER_ACTION_MASK
;
1262 lprintf(("ledger_get_actions: %#x\n", *actions
));
1263 return KERN_SUCCESS
;
1267 ledger_set_action(ledger_t ledger
, int entry
, int action
)
1269 lprintf(("ledger_set_action: %#x\n", action
));
1270 if (!ENTRY_VALID(ledger
, entry
)) {
1271 return KERN_INVALID_VALUE
;
1274 flag_set(&ledger
->l_entries
[entry
].le_flags
, action
);
1275 return KERN_SUCCESS
;
1279 ledger_debit_thread(thread_t thread
, ledger_t ledger
, int entry
, ledger_amount_t amount
)
1281 struct ledger_entry
*le
;
1282 ledger_amount_t old
, new;
1284 if (!ENTRY_VALID(ledger
, entry
) || (amount
< 0)) {
1285 return KERN_INVALID_ARGUMENT
;
1289 return KERN_SUCCESS
;
1292 le
= &ledger
->l_entries
[entry
];
1294 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
1295 assert(le
->le_debit
== 0);
1296 old
= OSAddAtomic64(-amount
, &le
->le_credit
);
1299 old
= OSAddAtomic64(amount
, &le
->le_debit
);
1302 lprintf(("%p Debit %lld->%lld\n", thread
, old
, new));
1305 ledger_entry_check_new_balance(thread
, ledger
, entry
, le
);
1308 return KERN_SUCCESS
;
1312 ledger_debit(ledger_t ledger
, int entry
, ledger_amount_t amount
)
1314 return ledger_debit_thread(current_thread(), ledger
, entry
, amount
);
1318 ledger_debit_nocheck(ledger_t ledger
, int entry
, ledger_amount_t amount
)
1320 return ledger_debit_thread(NULL
, ledger
, entry
, amount
);
1324 ledger_ast(thread_t thread
)
1326 struct ledger
*l
= thread
->t_ledger
;
1328 struct ledger
*coalition_ledger
;
1332 uint8_t task_percentage
;
1333 uint64_t task_interval
;
1336 task_t task
= thread
->task
;
1338 lprintf(("Ledger AST for %p\n", thread
));
1340 ASSERT(task
!= NULL
);
1341 ASSERT(thread
== current_thread());
1345 * Take a self-consistent snapshot of the CPU usage monitor parameters. The task
1346 * can change them at any point (with the task locked).
1349 task_flags
= task
->rusage_cpu_flags
;
1350 task_percentage
= task
->rusage_cpu_perthr_percentage
;
1351 task_interval
= task
->rusage_cpu_perthr_interval
;
1355 * Make sure this thread is up to date with regards to any task-wide per-thread
1356 * CPU limit, but only if it doesn't have a thread-private blocking CPU limit.
1358 if (((task_flags
& TASK_RUSECPU_FLAGS_PERTHR_LIMIT
) != 0) &&
1359 ((thread
->options
& TH_OPT_PRVT_CPULIMIT
) == 0)) {
1364 thread_get_cpulimit(&action
, &percentage
, &interval
);
1367 * If the thread's CPU limits no longer match the task's, or the
1368 * task has a limit but the thread doesn't, update the limit.
1370 if (((thread
->options
& TH_OPT_PROC_CPULIMIT
) == 0) ||
1371 (interval
!= task_interval
) || (percentage
!= task_percentage
)) {
1372 thread_set_cpulimit(THREAD_CPULIMIT_EXCEPTION
, task_percentage
, task_interval
);
1373 assert((thread
->options
& TH_OPT_PROC_CPULIMIT
) != 0);
1375 } else if (((task_flags
& TASK_RUSECPU_FLAGS_PERTHR_LIMIT
) == 0) &&
1376 (thread
->options
& TH_OPT_PROC_CPULIMIT
)) {
1377 assert((thread
->options
& TH_OPT_PRVT_CPULIMIT
) == 0);
1380 * Task no longer has a per-thread CPU limit; remove this thread's
1381 * corresponding CPU limit.
1383 thread_set_cpulimit(THREAD_CPULIMIT_DISABLE
, 0, 0);
1384 assert((thread
->options
& TH_OPT_PROC_CPULIMIT
) == 0);
1388 * If the task or thread is being terminated, let's just get on with it
1390 if ((l
== NULL
) || !task
->active
|| task
->halting
|| !thread
->active
) {
1395 * Examine all entries in deficit to see which might be eligble for
1396 * an automatic refill, which require callbacks to be issued, and
1397 * which require blocking.
1400 now
= mach_absolute_time();
1403 * Note that thread->t_threadledger may have been changed by the
1404 * thread_set_cpulimit() call above - so don't examine it until afterwards.
1406 thl
= thread
->t_threadledger
;
1407 if (LEDGER_VALID(thl
)) {
1408 block
|= ledger_check_needblock(thl
, now
);
1410 block
|= ledger_check_needblock(l
, now
);
1412 coalition_ledger
= coalition_ledger_get_from_task(task
);
1413 if (LEDGER_VALID(coalition_ledger
)) {
1414 block
|= ledger_check_needblock(coalition_ledger
, now
);
1416 ledger_dereference(coalition_ledger
);
1418 * If we are supposed to block on the availability of one or more
1419 * resources, find the first entry in deficit for which we should wait.
1420 * Schedule a refill if necessary and then sleep until the resource
1421 * becomes available.
1424 if (LEDGER_VALID(thl
)) {
1425 ret
= ledger_perform_blocking(thl
);
1426 if (ret
!= KERN_SUCCESS
) {
1430 ret
= ledger_perform_blocking(l
);
1431 if (ret
!= KERN_SUCCESS
) {
1438 ledger_check_needblock(ledger_t l
, uint64_t now
)
1441 uint32_t flags
, block
= 0;
1442 struct ledger_entry
*le
;
1443 struct ledger_callback
*lc
;
1446 for (i
= 0; i
< l
->l_size
; i
++) {
1447 le
= &l
->l_entries
[i
];
1449 lc
= entry_get_callback(l
, i
);
1451 if (limit_exceeded(le
) == FALSE
) {
1452 if (le
->le_flags
& LEDGER_ACTION_CALLBACK
) {
1454 * If needed, invoke the callback as a warning.
1455 * This needs to happen both when the balance rises above
1456 * the warning level, and also when it dips back below it.
1460 * See comments for matching logic in ledger_check_new_balance().
1462 if (warn_level_exceeded(le
)) {
1463 flags
= flag_set(&le
->le_flags
, LF_WARNED
);
1464 if ((flags
& LF_WARNED
) == 0) {
1465 lc
->lc_func(LEDGER_WARNING_ROSE_ABOVE
, lc
->lc_param0
, lc
->lc_param1
);
1468 flags
= flag_clear(&le
->le_flags
, LF_WARNED
);
1469 if (flags
& LF_WARNED
) {
1470 lc
->lc_func(LEDGER_WARNING_DIPPED_BELOW
, lc
->lc_param0
, lc
->lc_param1
);
1478 /* We're over the limit, so refill if we are eligible and past due. */
1479 if (le
->le_flags
& LF_REFILL_SCHEDULED
) {
1480 assert(!(le
->le_flags
& LF_TRACKING_MAX
));
1482 if ((le
->_le
.le_refill
.le_last_refill
+ le
->_le
.le_refill
.le_refill_period
) <= now
) {
1483 ledger_refill(now
, l
, i
);
1484 if (limit_exceeded(le
) == FALSE
) {
1490 if (le
->le_flags
& LEDGER_ACTION_BLOCK
) {
1493 if ((le
->le_flags
& LEDGER_ACTION_CALLBACK
) == 0) {
1498 * If the LEDGER_ACTION_CALLBACK flag is on, we expect there to
1499 * be a registered callback.
1502 flags
= flag_set(&le
->le_flags
, LF_CALLED_BACK
);
1503 /* Callback has already been called */
1504 if (flags
& LF_CALLED_BACK
) {
1507 lc
->lc_func(FALSE
, lc
->lc_param0
, lc
->lc_param1
);
1513 /* return KERN_SUCCESS to continue, KERN_FAILURE to restart */
1514 static kern_return_t
1515 ledger_perform_blocking(ledger_t l
)
1519 struct ledger_entry
*le
;
1521 for (i
= 0; i
< l
->l_size
; i
++) {
1522 le
= &l
->l_entries
[i
];
1523 if ((!limit_exceeded(le
)) ||
1524 ((le
->le_flags
& LEDGER_ACTION_BLOCK
) == 0)) {
1528 assert(!(le
->le_flags
& LF_TRACKING_MAX
));
1530 /* Prepare to sleep until the resource is refilled */
1531 ret
= assert_wait_deadline(le
, THREAD_INTERRUPTIBLE
,
1532 le
->_le
.le_refill
.le_last_refill
+ le
->_le
.le_refill
.le_refill_period
);
1533 if (ret
!= THREAD_WAITING
) {
1534 return KERN_SUCCESS
;
1537 /* Mark that somebody is waiting on this entry */
1538 flag_set(&le
->le_flags
, LF_WAKE_NEEDED
);
1540 ret
= thread_block_reason(THREAD_CONTINUE_NULL
, NULL
,
1542 if (ret
!= THREAD_AWAKENED
) {
1543 return KERN_SUCCESS
;
1547 * The world may have changed while we were asleep.
1548 * Some other resource we need may have gone into
1549 * deficit. Or maybe we're supposed to die now.
1550 * Go back to the top and reevaluate.
1552 return KERN_FAILURE
;
1554 return KERN_SUCCESS
;
1559 ledger_get_entries(ledger_t ledger
, int entry
, ledger_amount_t
*credit
,
1560 ledger_amount_t
*debit
)
1562 struct ledger_entry
*le
;
1564 if (!ENTRY_VALID(ledger
, entry
)) {
1565 return KERN_INVALID_ARGUMENT
;
1568 le
= &ledger
->l_entries
[entry
];
1570 *credit
= le
->le_credit
;
1571 *debit
= le
->le_debit
;
1573 return KERN_SUCCESS
;
1577 ledger_reset_callback_state(ledger_t ledger
, int entry
)
1579 struct ledger_entry
*le
;
1581 if (!ENTRY_VALID(ledger
, entry
)) {
1582 return KERN_INVALID_ARGUMENT
;
1585 le
= &ledger
->l_entries
[entry
];
1587 flag_clear(&le
->le_flags
, LF_CALLED_BACK
);
1589 return KERN_SUCCESS
;
1593 ledger_disable_panic_on_negative(ledger_t ledger
, int entry
)
1595 struct ledger_entry
*le
;
1597 if (!ENTRY_VALID(ledger
, entry
)) {
1598 return KERN_INVALID_ARGUMENT
;
1601 le
= &ledger
->l_entries
[entry
];
1603 flag_clear(&le
->le_flags
, LF_PANIC_ON_NEGATIVE
);
1605 return KERN_SUCCESS
;
1609 ledger_get_panic_on_negative(ledger_t ledger
, int entry
, int *panic_on_negative
)
1611 struct ledger_entry
*le
;
1613 if (!ENTRY_VALID(ledger
, entry
)) {
1614 return KERN_INVALID_ARGUMENT
;
1617 le
= &ledger
->l_entries
[entry
];
1619 if (le
->le_flags
& LF_PANIC_ON_NEGATIVE
) {
1620 *panic_on_negative
= TRUE
;
1622 *panic_on_negative
= FALSE
;
1625 return KERN_SUCCESS
;
1629 ledger_get_balance(ledger_t ledger
, int entry
, ledger_amount_t
*balance
)
1631 struct ledger_entry
*le
;
1633 if (!ENTRY_VALID(ledger
, entry
)) {
1634 return KERN_INVALID_ARGUMENT
;
1637 le
= &ledger
->l_entries
[entry
];
1639 if (le
->le_flags
& LF_TRACK_CREDIT_ONLY
) {
1640 assert(le
->le_debit
== 0);
1642 assert((le
->le_credit
>= 0) && (le
->le_debit
>= 0));
1645 *balance
= le
->le_credit
- le
->le_debit
;
1647 return KERN_SUCCESS
;
1651 ledger_template_info(void **buf
, int *len
)
1653 struct ledger_template_info
*lti
;
1654 struct entry_template
*et
;
1659 * Since all tasks share a ledger template, we'll just use the
1660 * caller's as the source.
1662 l
= current_task()->ledger
;
1663 if ((*len
< 0) || (l
== NULL
)) {
1667 if (*len
> l
->l_size
) {
1670 lti
= kheap_alloc(KHEAP_DATA_BUFFERS
,
1671 (*len
) * sizeof(struct ledger_template_info
), Z_WAITOK
);
1677 template_lock(l
->l_template
);
1678 et
= l
->l_template
->lt_entries
;
1680 for (i
= 0; i
< *len
; i
++) {
1681 memset(lti
, 0, sizeof(*lti
));
1682 strlcpy(lti
->lti_name
, et
->et_key
, LEDGER_NAME_MAX
);
1683 strlcpy(lti
->lti_group
, et
->et_group
, LEDGER_NAME_MAX
);
1684 strlcpy(lti
->lti_units
, et
->et_units
, LEDGER_NAME_MAX
);
1688 template_unlock(l
->l_template
);
1694 ledger_fill_entry_info(struct ledger_entry
*le
,
1695 struct ledger_entry_info
*lei
,
1699 assert(lei
!= NULL
);
1701 memset(lei
, 0, sizeof(*lei
));
1703 lei
->lei_limit
= le
->le_limit
;
1704 lei
->lei_credit
= le
->le_credit
;
1705 lei
->lei_debit
= le
->le_debit
;
1706 lei
->lei_balance
= lei
->lei_credit
- lei
->lei_debit
;
1707 lei
->lei_refill_period
= (le
->le_flags
& LF_REFILL_SCHEDULED
) ?
1708 abstime_to_nsecs(le
->_le
.le_refill
.le_refill_period
) : 0;
1709 lei
->lei_last_refill
= abstime_to_nsecs(now
- le
->_le
.le_refill
.le_last_refill
);
1713 ledger_get_task_entry_info_multiple(task_t task
, void **buf
, int *len
)
1715 struct ledger_entry_info
*lei
;
1716 struct ledger_entry
*le
;
1717 uint64_t now
= mach_absolute_time();
1721 if ((*len
< 0) || ((l
= task
->ledger
) == NULL
)) {
1725 if (*len
> l
->l_size
) {
1728 lei
= kheap_alloc(KHEAP_DATA_BUFFERS
,
1729 (*len
) * sizeof(struct ledger_entry_info
), Z_WAITOK
);
1737 for (i
= 0; i
< *len
; i
++) {
1738 ledger_fill_entry_info(le
, lei
, now
);
1747 ledger_get_entry_info(ledger_t ledger
,
1749 struct ledger_entry_info
*lei
)
1751 uint64_t now
= mach_absolute_time();
1753 assert(ledger
!= NULL
);
1754 assert(lei
!= NULL
);
1756 if (entry
>= 0 && entry
< ledger
->l_size
) {
1757 struct ledger_entry
*le
= &ledger
->l_entries
[entry
];
1758 ledger_fill_entry_info(le
, lei
, now
);
1763 ledger_info(task_t task
, struct ledger_info
*info
)
1767 if ((l
= task
->ledger
) == NULL
) {
1771 memset(info
, 0, sizeof(*info
));
1773 strlcpy(info
->li_name
, l
->l_template
->lt_name
, LEDGER_NAME_MAX
);
1774 info
->li_id
= l
->l_id
;
1775 info
->li_entries
= l
->l_size
;
1781 ledger_limit(task_t task
, struct ledger_limit_args
*args
)
1787 if ((l
= task
->ledger
) == NULL
) {
1791 idx
= ledger_key_lookup(l
->l_template
, args
->lla_name
);
1792 if ((idx
< 0) || (idx
>= l
->l_size
)) {
1797 * XXX - this doesn't really seem like the right place to have
1798 * a context-sensitive conversion of userspace units into kernel
1799 * units. For now I'll handwave and say that the ledger() system
1800 * call isn't meant for civilians to use - they should be using
1801 * the process policy interfaces.
1803 if (idx
== task_ledgers
.cpu_time
) {
1806 if (args
->lla_refill_period
) {
1808 * If a refill is scheduled, then the limit is
1809 * specified as a percentage of one CPU. The
1810 * syscall specifies the refill period in terms of
1811 * milliseconds, so we need to convert to nsecs.
1813 args
->lla_refill_period
*= 1000000;
1814 nsecs
= args
->lla_limit
*
1815 (args
->lla_refill_period
/ 100);
1816 lprintf(("CPU limited to %lld nsecs per second\n",
1820 * If no refill is scheduled, then this is a
1821 * fixed amount of CPU time (in nsecs) that can
1824 nsecs
= args
->lla_limit
;
1825 lprintf(("CPU limited to %lld nsecs\n", nsecs
));
1827 limit
= nsecs_to_abstime(nsecs
);
1829 limit
= args
->lla_limit
;
1830 lprintf(("%s limited to %lld\n", args
->lla_name
, limit
));
1833 if (args
->lla_refill_period
> 0) {
1834 ledger_set_period(l
, idx
, args
->lla_refill_period
);
1837 ledger_set_limit(l
, idx
, limit
);
1838 flag_set(&l
->l_entries
[idx
].le_flags
, LEDGER_ACTION_BLOCK
);