+ struct ledger_entry *le;
+ ledger_amount_t debit, credit;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ le = &ledger->l_entries[entry];
+
+top:
+ debit = le->le_debit;
+ credit = le->le_credit;
+
+ if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
+ assert(le->le_debit == 0);
+ if (!OSCompareAndSwap64(credit, 0, &le->le_credit)) {
+ goto top;
+ }
+ lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, 0));
+ } else if (credit > debit) {
+ if (!OSCompareAndSwap64(debit, credit, &le->le_debit)) {
+ goto top;
+ }
+ lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_debit, le->le_credit));
+ } else if (credit < debit) {
+ if (!OSCompareAndSwap64(credit, debit, &le->le_credit)) {
+ goto top;
+ }
+ lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, le->le_debit));
+ }
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_get_limit(ledger_t ledger, int entry, ledger_amount_t *limit)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ le = &ledger->l_entries[entry];
+ *limit = le->le_limit;
+
+ lprintf(("ledger_get_limit: %lld\n", *limit));
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Adjust the limit of a limited resource. This does not affect the
+ * current balance, so the change doesn't affect the thread until the
+ * next refill.
+ *
+ * warn_level: If non-zero, causes the callback to be invoked when
+ * the balance exceeds this level. Specified as a percentage [of the limit].
+ */
+kern_return_t
+ledger_set_limit(ledger_t ledger, int entry, ledger_amount_t limit,
+ uint8_t warn_level_percentage)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ lprintf(("ledger_set_limit: %lld\n", limit));
+ le = &ledger->l_entries[entry];
+
+ if (limit == LEDGER_LIMIT_INFINITY) {
+ /*
+ * Caller wishes to disable the limit. This will implicitly
+ * disable automatic refill, as refills implicitly depend
+ * on the limit.
+ */
+ ledger_disable_refill(ledger, entry);
+ }
+
+ le->le_limit = limit;
+ if (le->le_flags & LF_REFILL_SCHEDULED) {
+ assert(!(le->le_flags & LF_TRACKING_MAX));
+ le->_le.le_refill.le_last_refill = 0;
+ }
+ flag_clear(&le->le_flags, LF_CALLED_BACK);
+ flag_clear(&le->le_flags, LF_WARNED);
+ ledger_limit_entry_wakeup(le);
+
+ if (warn_level_percentage != 0) {
+ assert(warn_level_percentage <= 100);
+ assert(limit > 0); /* no negative limit support for warnings */
+ assert(limit != LEDGER_LIMIT_INFINITY); /* warn % without limit makes no sense */
+ le->le_warn_percent = warn_level_percentage * (1u << 16) / 100;
+ } else {
+ le->le_warn_percent = LEDGER_PERCENT_NONE;
+ }
+
+ return KERN_SUCCESS;
+}
+
+#if CONFIG_LEDGER_INTERVAL_MAX
+kern_return_t
+ledger_get_interval_max(ledger_t ledger, int entry,
+ ledger_amount_t *max_interval_balance, int reset)
+{
+ struct ledger_entry *le;
+ le = &ledger->l_entries[entry];
+
+ if (!ENTRY_VALID(ledger, entry) || !(le->le_flags & LF_TRACKING_MAX)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ *max_interval_balance = le->_le._le_max.le_interval_max;
+ lprintf(("ledger_get_interval_max: %lld%s\n", *max_interval_balance,
+ (reset) ? " --> 0" : ""));
+
+ if (reset) {
+ le->_le._le_max.le_interval_max = 0;
+ }
+
+ return KERN_SUCCESS;
+}
+#endif /* CONFIG_LEDGER_INTERVAL_MAX */
+
+kern_return_t
+ledger_get_lifetime_max(ledger_t ledger, int entry,
+ ledger_amount_t *max_lifetime_balance)
+{
+ struct ledger_entry *le;
+ le = &ledger->l_entries[entry];
+
+ if (!ENTRY_VALID(ledger, entry) || !(le->le_flags & LF_TRACKING_MAX)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ *max_lifetime_balance = le->_le._le_max.le_lifetime_max;
+ lprintf(("ledger_get_lifetime_max: %lld\n", *max_lifetime_balance));
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Enable tracking of periodic maximums for this ledger entry.
+ */
+kern_return_t
+ledger_track_maximum(ledger_template_t template, int entry,
+ __unused int period_in_secs)
+{
+ template_lock(template);
+
+ if ((entry < 0) || (entry >= template->lt_cnt)) {
+ template_unlock(template);
+ return KERN_INVALID_VALUE;
+ }
+
+ /* Refill is incompatible with max tracking. */
+ if (template->lt_entries[entry].et_flags & LF_REFILL_SCHEDULED) {
+ return KERN_INVALID_VALUE;
+ }
+
+ template->lt_entries[entry].et_flags |= LF_TRACKING_MAX;
+ template_unlock(template);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_panic_on_negative(ledger_template_t template, int entry)
+{
+ template_lock(template);
+
+ if ((entry < 0) || (entry >= template->lt_cnt)) {
+ template_unlock(template);
+ return KERN_INVALID_VALUE;
+ }
+
+ template->lt_entries[entry].et_flags |= LF_PANIC_ON_NEGATIVE;
+
+ template_unlock(template);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_track_credit_only(ledger_template_t template, int entry)
+{
+ template_lock(template);
+
+ if ((entry < 0) || (entry >= template->lt_cnt)) {
+ template_unlock(template);
+ return KERN_INVALID_VALUE;
+ }
+
+ template->lt_entries[entry].et_flags |= LF_TRACK_CREDIT_ONLY;
+
+ template_unlock(template);
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Add a callback to be executed when the resource goes into deficit.
+ */
+kern_return_t
+ledger_set_callback(ledger_template_t template, int entry,
+ ledger_callback_t func, const void *param0, const void *param1)
+{
+ struct entry_template *et;
+ struct ledger_callback *old_cb, *new_cb;
+
+ if ((entry < 0) || (entry >= template->lt_cnt)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ if (func) {
+ new_cb = (struct ledger_callback *)kalloc(sizeof(*new_cb));
+ new_cb->lc_func = func;
+ new_cb->lc_param0 = param0;
+ new_cb->lc_param1 = param1;
+ } else {
+ new_cb = NULL;
+ }
+
+ template_lock(template);
+ et = &template->lt_entries[entry];
+ old_cb = et->et_callback;
+ et->et_callback = new_cb;
+ template_unlock(template);
+ if (old_cb) {
+ kfree(old_cb, sizeof(*old_cb));
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Disable callback notification for a specific ledger entry.
+ *
+ * Otherwise, if using a ledger template which specified a
+ * callback function (ledger_set_callback()), it will be invoked when
+ * the resource goes into deficit.
+ */
+kern_return_t
+ledger_disable_callback(ledger_t ledger, int entry)
+{
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ /*
+ * le_warn_percent is used to indicate *if* this ledger has a warning configured,
+ * in addition to what that warning level is set to.
+ * This means a side-effect of ledger_disable_callback() is that the
+ * warning level is forgotten.
+ */
+ ledger->l_entries[entry].le_warn_percent = LEDGER_PERCENT_NONE;
+ flag_clear(&ledger->l_entries[entry].le_flags, LEDGER_ACTION_CALLBACK);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Enable callback notification for a specific ledger entry.
+ *
+ * This is only needed if ledger_disable_callback() has previously
+ * been invoked against an entry; there must already be a callback
+ * configured.
+ */
+kern_return_t
+ledger_enable_callback(ledger_t ledger, int entry)
+{
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ assert(entry_get_callback(ledger, entry) != NULL);
+
+ flag_set(&ledger->l_entries[entry].le_flags, LEDGER_ACTION_CALLBACK);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Query the automatic refill period for this ledger entry.
+ *
+ * A period of 0 means this entry has none configured.
+ */
+kern_return_t
+ledger_get_period(ledger_t ledger, int entry, uint64_t *period)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ le = &ledger->l_entries[entry];
+ *period = abstime_to_nsecs(le->_le.le_refill.le_refill_period);
+ lprintf(("ledger_get_period: %llx\n", *period));
+ return KERN_SUCCESS;
+}
+
+/*
+ * Adjust the automatic refill period.
+ */
+kern_return_t
+ledger_set_period(ledger_t ledger, int entry, uint64_t period)
+{
+ struct ledger_entry *le;
+
+ lprintf(("ledger_set_period: %llx\n", period));
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ /*
+ * A refill period refills the ledger in multiples of the limit,
+ * so if you haven't set one yet, you need a lesson on ledgers.
+ */
+ assert(le->le_limit != LEDGER_LIMIT_INFINITY);
+
+ if (le->le_flags & LF_TRACKING_MAX) {
+ /*
+ * Refill is incompatible with rolling max tracking.
+ */
+ return KERN_INVALID_VALUE;
+ }
+
+ le->_le.le_refill.le_refill_period = nsecs_to_abstime(period);
+
+ /*
+ * Set the 'starting time' for the next refill to now. Since
+ * we're resetting the balance to zero here, we consider this
+ * moment the starting time for accumulating a balance that
+ * counts towards the limit.
+ */
+ le->_le.le_refill.le_last_refill = mach_absolute_time();
+ ledger_zero_balance(ledger, entry);
+
+ flag_set(&le->le_flags, LF_REFILL_SCHEDULED);
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Disable automatic refill.
+ */
+kern_return_t
+ledger_disable_refill(ledger_t ledger, int entry)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ flag_clear(&le->le_flags, LF_REFILL_SCHEDULED);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_get_actions(ledger_t ledger, int entry, int *actions)
+{
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ *actions = ledger->l_entries[entry].le_flags & LEDGER_ACTION_MASK;
+ lprintf(("ledger_get_actions: %#x\n", *actions));
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_set_action(ledger_t ledger, int entry, int action)
+{
+ lprintf(("ledger_set_action: %#x\n", action));
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_VALUE;
+ }
+
+ flag_set(&ledger->l_entries[entry].le_flags, action);
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_debit_thread(thread_t thread, ledger_t ledger, int entry, ledger_amount_t amount)
+{
+ struct ledger_entry *le;
+ ledger_amount_t old, new;
+
+ if (!ENTRY_VALID(ledger, entry) || (amount < 0)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ if (amount == 0) {
+ return KERN_SUCCESS;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
+ assert(le->le_debit == 0);
+ old = OSAddAtomic64(-amount, &le->le_credit);
+ new = old - amount;
+ } else {
+ old = OSAddAtomic64(amount, &le->le_debit);
+ new = old + amount;
+ }
+ lprintf(("%p Debit %lld->%lld\n", thread, old, new));
+
+ if (thread) {
+ ledger_entry_check_new_balance(thread, ledger, entry, le);
+ }
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_debit(ledger_t ledger, int entry, ledger_amount_t amount)
+{
+ return ledger_debit_thread(current_thread(), ledger, entry, amount);
+}
+
+kern_return_t
+ledger_debit_nocheck(ledger_t ledger, int entry, ledger_amount_t amount)
+{
+ return ledger_debit_thread(NULL, ledger, entry, amount);
+}
+
+void
+ledger_ast(thread_t thread)
+{
+ struct ledger *l = thread->t_ledger;
+ struct ledger *thl;
+ struct ledger *coalition_ledger;
+ uint32_t block;
+ uint64_t now;
+ uint8_t task_flags;
+ uint8_t task_percentage;
+ uint64_t task_interval;
+
+ kern_return_t ret;
+ task_t task = thread->task;
+
+ lprintf(("Ledger AST for %p\n", thread));
+
+ ASSERT(task != NULL);
+ ASSERT(thread == current_thread());
+
+top:
+ /*
+ * Take a self-consistent snapshot of the CPU usage monitor parameters. The task
+ * can change them at any point (with the task locked).
+ */
+ task_lock(task);
+ task_flags = task->rusage_cpu_flags;
+ task_percentage = task->rusage_cpu_perthr_percentage;
+ task_interval = task->rusage_cpu_perthr_interval;
+ task_unlock(task);
+
+ /*
+ * Make sure this thread is up to date with regards to any task-wide per-thread
+ * CPU limit, but only if it doesn't have a thread-private blocking CPU limit.
+ */
+ if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) != 0) &&
+ ((thread->options & TH_OPT_PRVT_CPULIMIT) == 0)) {
+ uint8_t percentage;
+ uint64_t interval;
+ int action;
+
+ thread_get_cpulimit(&action, &percentage, &interval);
+
+ /*
+ * If the thread's CPU limits no longer match the task's, or the
+ * task has a limit but the thread doesn't, update the limit.
+ */
+ if (((thread->options & TH_OPT_PROC_CPULIMIT) == 0) ||
+ (interval != task_interval) || (percentage != task_percentage)) {
+ thread_set_cpulimit(THREAD_CPULIMIT_EXCEPTION, task_percentage, task_interval);
+ assert((thread->options & TH_OPT_PROC_CPULIMIT) != 0);
+ }
+ } else if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) == 0) &&
+ (thread->options & TH_OPT_PROC_CPULIMIT)) {
+ assert((thread->options & TH_OPT_PRVT_CPULIMIT) == 0);
+
+ /*
+ * Task no longer has a per-thread CPU limit; remove this thread's
+ * corresponding CPU limit.
+ */
+ thread_set_cpulimit(THREAD_CPULIMIT_DISABLE, 0, 0);
+ assert((thread->options & TH_OPT_PROC_CPULIMIT) == 0);
+ }
+
+ /*
+ * If the task or thread is being terminated, let's just get on with it
+ */
+ if ((l == NULL) || !task->active || task->halting || !thread->active) {
+ return;
+ }
+
+ /*
+ * Examine all entries in deficit to see which might be eligble for
+ * an automatic refill, which require callbacks to be issued, and
+ * which require blocking.
+ */
+ block = 0;
+ now = mach_absolute_time();
+
+ /*
+ * Note that thread->t_threadledger may have been changed by the
+ * thread_set_cpulimit() call above - so don't examine it until afterwards.
+ */
+ thl = thread->t_threadledger;
+ if (LEDGER_VALID(thl)) {
+ block |= ledger_check_needblock(thl, now);
+ }
+ block |= ledger_check_needblock(l, now);
+
+ coalition_ledger = coalition_ledger_get_from_task(task);
+ if (LEDGER_VALID(coalition_ledger)) {
+ block |= ledger_check_needblock(coalition_ledger, now);
+ }
+ ledger_dereference(coalition_ledger);
+ /*
+ * If we are supposed to block on the availability of one or more
+ * resources, find the first entry in deficit for which we should wait.
+ * Schedule a refill if necessary and then sleep until the resource
+ * becomes available.
+ */
+ if (block) {
+ if (LEDGER_VALID(thl)) {
+ ret = ledger_perform_blocking(thl);
+ if (ret != KERN_SUCCESS) {
+ goto top;
+ }
+ }
+ ret = ledger_perform_blocking(l);
+ if (ret != KERN_SUCCESS) {
+ goto top;
+ }
+ } /* block */
+}
+
+static uint32_t
+ledger_check_needblock(ledger_t l, uint64_t now)
+{
+ int i;
+ uint32_t flags, block = 0;
+ struct ledger_entry *le;
+ struct ledger_callback *lc;
+
+
+ for (i = 0; i < l->l_size; i++) {
+ le = &l->l_entries[i];
+
+ lc = entry_get_callback(l, i);
+
+ if (limit_exceeded(le) == FALSE) {
+ if (le->le_flags & LEDGER_ACTION_CALLBACK) {
+ /*
+ * If needed, invoke the callback as a warning.
+ * This needs to happen both when the balance rises above
+ * the warning level, and also when it dips back below it.
+ */
+ assert(lc != NULL);
+ /*
+ * See comments for matching logic in ledger_check_new_balance().
+ */
+ if (warn_level_exceeded(le)) {
+ flags = flag_set(&le->le_flags, LF_WARNED);
+ if ((flags & LF_WARNED) == 0) {
+ lc->lc_func(LEDGER_WARNING_ROSE_ABOVE, lc->lc_param0, lc->lc_param1);
+ }
+ } else {
+ flags = flag_clear(&le->le_flags, LF_WARNED);
+ if (flags & LF_WARNED) {
+ lc->lc_func(LEDGER_WARNING_DIPPED_BELOW, lc->lc_param0, lc->lc_param1);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ /* We're over the limit, so refill if we are eligible and past due. */
+ if (le->le_flags & LF_REFILL_SCHEDULED) {
+ assert(!(le->le_flags & LF_TRACKING_MAX));
+
+ if ((le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period) <= now) {
+ ledger_refill(now, l, i);
+ if (limit_exceeded(le) == FALSE) {
+ continue;
+ }
+ }
+ }
+
+ if (le->le_flags & LEDGER_ACTION_BLOCK) {
+ block = 1;
+ }
+ if ((le->le_flags & LEDGER_ACTION_CALLBACK) == 0) {
+ continue;
+ }
+
+ /*
+ * If the LEDGER_ACTION_CALLBACK flag is on, we expect there to
+ * be a registered callback.
+ */
+ assert(lc != NULL);
+ flags = flag_set(&le->le_flags, LF_CALLED_BACK);
+ /* Callback has already been called */
+ if (flags & LF_CALLED_BACK) {
+ continue;
+ }
+ lc->lc_func(FALSE, lc->lc_param0, lc->lc_param1);
+ }
+ return block;
+}
+
+
+/* return KERN_SUCCESS to continue, KERN_FAILURE to restart */
+static kern_return_t
+ledger_perform_blocking(ledger_t l)
+{
+ int i;
+ kern_return_t ret;
+ struct ledger_entry *le;
+
+ for (i = 0; i < l->l_size; i++) {
+ le = &l->l_entries[i];
+ if ((!limit_exceeded(le)) ||
+ ((le->le_flags & LEDGER_ACTION_BLOCK) == 0)) {
+ continue;
+ }
+
+ assert(!(le->le_flags & LF_TRACKING_MAX));
+
+ /* Prepare to sleep until the resource is refilled */
+ ret = assert_wait_deadline(le, THREAD_INTERRUPTIBLE,
+ le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period);
+ if (ret != THREAD_WAITING) {
+ return KERN_SUCCESS;
+ }
+
+ /* Mark that somebody is waiting on this entry */
+ flag_set(&le->le_flags, LF_WAKE_NEEDED);
+
+ ret = thread_block_reason(THREAD_CONTINUE_NULL, NULL,
+ AST_LEDGER);
+ if (ret != THREAD_AWAKENED) {
+ return KERN_SUCCESS;
+ }
+
+ /*
+ * The world may have changed while we were asleep.
+ * Some other resource we need may have gone into
+ * deficit. Or maybe we're supposed to die now.
+ * Go back to the top and reevaluate.
+ */
+ return KERN_FAILURE;
+ }
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t
+ledger_get_entries(ledger_t ledger, int entry, ledger_amount_t *credit,
+ ledger_amount_t *debit)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ *credit = le->le_credit;
+ *debit = le->le_debit;
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_reset_callback_state(ledger_t ledger, int entry)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ flag_clear(&le->le_flags, LF_CALLED_BACK);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_disable_panic_on_negative(ledger_t ledger, int entry)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ flag_clear(&le->le_flags, LF_PANIC_ON_NEGATIVE);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_get_panic_on_negative(ledger_t ledger, int entry, int *panic_on_negative)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ if (le->le_flags & LF_PANIC_ON_NEGATIVE) {
+ *panic_on_negative = TRUE;
+ } else {
+ *panic_on_negative = FALSE;
+ }
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+ledger_get_balance(ledger_t ledger, int entry, ledger_amount_t *balance)
+{
+ struct ledger_entry *le;
+
+ if (!ENTRY_VALID(ledger, entry)) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ le = &ledger->l_entries[entry];
+
+ if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
+ assert(le->le_debit == 0);
+ } else {
+ assert((le->le_credit >= 0) && (le->le_debit >= 0));
+ }
+
+ *balance = le->le_credit - le->le_debit;
+
+ return KERN_SUCCESS;
+}
+
+int
+ledger_template_info(void **buf, int *len)
+{
+ struct ledger_template_info *lti;
+ struct entry_template *et;
+ int i;
+ ledger_t l;
+
+ /*
+ * Since all tasks share a ledger template, we'll just use the
+ * caller's as the source.
+ */
+ l = current_task()->ledger;
+ if ((*len < 0) || (l == NULL)) {
+ return EINVAL;
+ }
+
+ if (*len > l->l_size) {
+ *len = l->l_size;
+ }
+ lti = kalloc((*len) * sizeof(struct ledger_template_info));
+ if (lti == NULL) {
+ return ENOMEM;
+ }
+ *buf = lti;
+
+ template_lock(l->l_template);
+ et = l->l_template->lt_entries;
+
+ for (i = 0; i < *len; i++) {
+ memset(lti, 0, sizeof(*lti));
+ strlcpy(lti->lti_name, et->et_key, LEDGER_NAME_MAX);
+ strlcpy(lti->lti_group, et->et_group, LEDGER_NAME_MAX);
+ strlcpy(lti->lti_units, et->et_units, LEDGER_NAME_MAX);
+ et++;
+ lti++;
+ }
+ template_unlock(l->l_template);
+
+ return 0;
+}
+
+static void
+ledger_fill_entry_info(struct ledger_entry *le,
+ struct ledger_entry_info *lei,
+ uint64_t now)
+{
+ assert(le != NULL);
+ assert(lei != NULL);
+
+ memset(lei, 0, sizeof(*lei));
+
+ lei->lei_limit = le->le_limit;
+ lei->lei_credit = le->le_credit;
+ lei->lei_debit = le->le_debit;
+ lei->lei_balance = lei->lei_credit - lei->lei_debit;
+ lei->lei_refill_period = (le->le_flags & LF_REFILL_SCHEDULED) ?
+ abstime_to_nsecs(le->_le.le_refill.le_refill_period) : 0;
+ lei->lei_last_refill = abstime_to_nsecs(now - le->_le.le_refill.le_last_refill);
+}
+
+int
+ledger_get_task_entry_info_multiple(task_t task, void **buf, int *len)
+{
+ struct ledger_entry_info *lei;
+ struct ledger_entry *le;
+ uint64_t now = mach_absolute_time();
+ int i;
+ ledger_t l;
+
+ if ((*len < 0) || ((l = task->ledger) == NULL)) {
+ return EINVAL;
+ }
+
+ if (*len > l->l_size) {
+ *len = l->l_size;
+ }
+ lei = kalloc((*len) * sizeof(struct ledger_entry_info));
+ if (lei == NULL) {
+ return ENOMEM;
+ }
+ *buf = lei;
+
+ le = l->l_entries;
+
+ for (i = 0; i < *len; i++) {
+ ledger_fill_entry_info(le, lei, now);
+ le++;
+ lei++;
+ }
+
+ return 0;
+}
+
+void
+ledger_get_entry_info(ledger_t ledger,
+ int entry,
+ struct ledger_entry_info *lei)
+{
+ uint64_t now = mach_absolute_time();
+
+ assert(ledger != NULL);
+ assert(lei != NULL);
+
+ if (entry >= 0 && entry < ledger->l_size) {
+ struct ledger_entry *le = &ledger->l_entries[entry];
+ ledger_fill_entry_info(le, lei, now);
+ }
+}
+
+int
+ledger_info(task_t task, struct ledger_info *info)
+{
+ ledger_t l;
+
+ if ((l = task->ledger) == NULL) {
+ return ENOENT;
+ }
+
+ memset(info, 0, sizeof(*info));
+
+ strlcpy(info->li_name, l->l_template->lt_name, LEDGER_NAME_MAX);
+ info->li_id = l->l_id;
+ info->li_entries = l->l_size;
+ return 0;
+}
+
+#ifdef LEDGER_DEBUG
+int
+ledger_limit(task_t task, struct ledger_limit_args *args)
+{
+ ledger_t l;
+ int64_t limit;
+ int idx;
+
+ if ((l = task->ledger) == NULL) {
+ return EINVAL;
+ }
+
+ idx = ledger_key_lookup(l->l_template, args->lla_name);
+ if ((idx < 0) || (idx >= l->l_size)) {
+ return EINVAL;
+ }
+
+ /*
+ * XXX - this doesn't really seem like the right place to have
+ * a context-sensitive conversion of userspace units into kernel
+ * units. For now I'll handwave and say that the ledger() system
+ * call isn't meant for civilians to use - they should be using
+ * the process policy interfaces.
+ */
+ if (idx == task_ledgers.cpu_time) {
+ int64_t nsecs;
+
+ if (args->lla_refill_period) {
+ /*
+ * If a refill is scheduled, then the limit is
+ * specified as a percentage of one CPU. The
+ * syscall specifies the refill period in terms of
+ * milliseconds, so we need to convert to nsecs.
+ */
+ args->lla_refill_period *= 1000000;
+ nsecs = args->lla_limit *
+ (args->lla_refill_period / 100);
+ lprintf(("CPU limited to %lld nsecs per second\n",
+ nsecs));
+ } else {
+ /*
+ * If no refill is scheduled, then this is a
+ * fixed amount of CPU time (in nsecs) that can
+ * be consumed.
+ */
+ nsecs = args->lla_limit;
+ lprintf(("CPU limited to %lld nsecs\n", nsecs));
+ }
+ limit = nsecs_to_abstime(nsecs);
+ } else {
+ limit = args->lla_limit;
+ lprintf(("%s limited to %lld\n", args->lla_name, limit));
+ }
+
+ if (args->lla_refill_period > 0) {
+ ledger_set_period(l, idx, args->lla_refill_period);
+ }
+
+ ledger_set_limit(l, idx, limit);
+ flag_set(&l->l_entries[idx].le_flags, LEDGER_ACTION_BLOCK);
+ return 0;