]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/ledger.c
xnu-4570.51.1.tar.gz
[apple/xnu.git] / osfmk / kern / ledger.c
CommitLineData
1c79356b 1/*
316670eb 2 * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
91447636 31
fe8ab488 32#include <kern/kern_types.h>
1c79356b 33#include <kern/ledger.h>
91447636 34#include <kern/kalloc.h>
316670eb 35#include <kern/task.h>
3e170ce0 36#include <kern/thread.h>
91447636 37
316670eb
A
38#include <kern/processor.h>
39#include <kern/machine.h>
40#include <kern/queue.h>
39037602
A
41#include <kern/policy_internal.h>
42
316670eb 43#include <sys/errno.h>
1c79356b 44
316670eb
A
45#include <libkern/OSAtomic.h>
46#include <mach/mach_types.h>
813fb2f6 47#include <os/overflow.h>
1c79356b 48
316670eb
A
49/*
50 * Ledger entry flags. Bits in second nibble (masked by 0xF0) are used for
51 * ledger actions (LEDGER_ACTION_BLOCK, etc).
52 */
39236c6e
A
53#define LF_ENTRY_ACTIVE 0x0001 /* entry is active if set */
54#define LF_WAKE_NEEDED 0x0100 /* one or more threads are asleep */
55#define LF_WAKE_INPROGRESS 0x0200 /* the wait queue is being processed */
56#define LF_REFILL_SCHEDULED 0x0400 /* a refill timer has been set */
57#define LF_REFILL_INPROGRESS 0x0800 /* the ledger is being refilled */
58#define LF_CALLED_BACK 0x1000 /* callback was called for balance in deficit */
59#define LF_WARNED 0x2000 /* callback was called for balance warning */
5ba3f43e 60#define LF_TRACKING_MAX 0x4000 /* track max balance. Exclusive w.r.t refill */
fe8ab488 61#define LF_PANIC_ON_NEGATIVE 0x8000 /* panic if it goes negative */
39037602 62#define LF_TRACK_CREDIT_ONLY 0x10000 /* only update "credit" */
1c79356b 63
316670eb
A
64/* Determine whether a ledger entry exists and has been initialized and active */
65#define ENTRY_VALID(l, e) \
66 (((l) != NULL) && ((e) >= 0) && ((e) < (l)->l_size) && \
39236c6e 67 (((l)->l_entries[e].le_flags & LF_ENTRY_ACTIVE) == LF_ENTRY_ACTIVE))
316670eb 68
fe8ab488
A
69#define ASSERT(a) assert(a)
70
316670eb
A
71#ifdef LEDGER_DEBUG
72int ledger_debug = 0;
73
316670eb
A
74#define lprintf(a) if (ledger_debug) { \
75 printf("%lld ", abstime_to_nsecs(mach_absolute_time() / 1000000)); \
76 printf a ; \
77}
78#else
79#define lprintf(a)
316670eb
A
80#endif
81
82struct ledger_callback {
83 ledger_callback_t lc_func;
84 const void *lc_param0;
85 const void *lc_param1;
86};
87
88struct entry_template {
89 char et_key[LEDGER_NAME_MAX];
90 char et_group[LEDGER_NAME_MAX];
91 char et_units[LEDGER_NAME_MAX];
92 uint32_t et_flags;
93 struct ledger_callback *et_callback;
94};
95
96lck_grp_t ledger_lck_grp;
97
98/*
99 * Modifying the reference count, table size, or table contents requires
100 * holding the lt_lock. Modfying the table address requires both lt_lock
101 * and setting the inuse bit. This means that the lt_entries field can be
102 * safely dereferenced if you hold either the lock or the inuse bit. The
103 * inuse bit exists solely to allow us to swap in a new, larger entries
104 * table without requiring a full lock to be acquired on each lookup.
105 * Accordingly, the inuse bit should never be held for longer than it takes
106 * to extract a value from the table - i.e., 2 or 3 memory references.
107 */
108struct ledger_template {
109 const char *lt_name;
110 int lt_refs;
111 int lt_cnt;
112 int lt_table_size;
113 volatile uint32_t lt_inuse;
114 lck_mtx_t lt_lock;
5ba3f43e 115 zone_t lt_zone;
316670eb
A
116 struct entry_template *lt_entries;
117};
118
119#define template_lock(template) lck_mtx_lock(&(template)->lt_lock)
120#define template_unlock(template) lck_mtx_unlock(&(template)->lt_lock)
121
122#define TEMPLATE_INUSE(s, t) { \
123 s = splsched(); \
124 while (OSCompareAndSwap(0, 1, &((t)->lt_inuse))) \
125 ; \
126}
127
128#define TEMPLATE_IDLE(s, t) { \
129 (t)->lt_inuse = 0; \
130 splx(s); \
131}
132
39236c6e 133/*
5ba3f43e 134 * Use NTOCKS "tocks" to track the rolling maximum balance of a ledger entry.
39236c6e 135 */
5ba3f43e 136#define NTOCKS 1
316670eb
A
137/*
138 * The explicit alignment is to ensure that atomic operations don't panic
139 * on ARM.
140 */
141struct ledger_entry {
39236c6e
A
142 volatile uint32_t le_flags;
143 ledger_amount_t le_limit;
144 ledger_amount_t le_warn_level;
145 volatile ledger_amount_t le_credit __attribute__((aligned(8)));
146 volatile ledger_amount_t le_debit __attribute__((aligned(8)));
147 union {
148 struct {
149 /*
150 * XXX - the following two fields can go away if we move all of
151 * the refill logic into process policy
152 */
5ba3f43e
A
153 uint64_t le_refill_period;
154 uint64_t le_last_refill;
39236c6e 155 } le_refill;
5ba3f43e
A
156 struct _le_maxtracking {
157 struct _le_peak {
158 uint32_t le_max; /* Lower 32-bits of observed max balance */
159 uint32_t le_time; /* time when this peak was observed */
160 } le_peaks[NTOCKS];
161 ledger_amount_t le_lifetime_max; /* greatest peak ever observed */
162 } le_maxtracking;
39236c6e 163 } _le;
316670eb
A
164} __attribute__((aligned(8)));
165
166struct ledger {
39037602
A
167 uint64_t l_id;
168 int32_t l_refs;
169 int32_t l_size;
316670eb 170 struct ledger_template *l_template;
39037602 171 struct ledger_entry l_entries[0] __attribute__((aligned(8)));
316670eb
A
172};
173
174static int ledger_cnt = 0;
175/* ledger ast helper functions */
176static uint32_t ledger_check_needblock(ledger_t l, uint64_t now);
177static kern_return_t ledger_perform_blocking(ledger_t l);
178static uint32_t flag_set(volatile uint32_t *flags, uint32_t bit);
179static uint32_t flag_clear(volatile uint32_t *flags, uint32_t bit);
180
5c9f4661
A
181static void ledger_entry_check_new_balance(thread_t thread, ledger_t ledger,
182 int entry, struct ledger_entry *le);
39037602 183
316670eb
A
184#if 0
185static void
186debug_callback(const void *p0, __unused const void *p1)
1c79356b 187{
316670eb
A
188 printf("ledger: resource exhausted [%s] for task %p\n",
189 (const char *)p0, p1);
190}
191#endif
b0d623f7 192
316670eb
A
193/************************************/
194
195static uint64_t
196abstime_to_nsecs(uint64_t abstime)
197{
198 uint64_t nsecs;
199
200 absolutetime_to_nanoseconds(abstime, &nsecs);
201 return (nsecs);
202}
203
204static uint64_t
205nsecs_to_abstime(uint64_t nsecs)
206{
207 uint64_t abstime;
208
209 nanoseconds_to_absolutetime(nsecs, &abstime);
210 return (abstime);
211}
212
213void
214ledger_init(void)
215{
216 lck_grp_init(&ledger_lck_grp, "ledger", LCK_GRP_ATTR_NULL);
217}
218
219ledger_template_t
220ledger_template_create(const char *name)
221{
222 ledger_template_t template;
223
224 template = (ledger_template_t)kalloc(sizeof (*template));
225 if (template == NULL)
226 return (NULL);
227
228 template->lt_name = name;
229 template->lt_refs = 1;
230 template->lt_cnt = 0;
231 template->lt_table_size = 1;
232 template->lt_inuse = 0;
5ba3f43e 233 template->lt_zone = NULL;
316670eb
A
234 lck_mtx_init(&template->lt_lock, &ledger_lck_grp, LCK_ATTR_NULL);
235
236 template->lt_entries = (struct entry_template *)
237 kalloc(sizeof (struct entry_template) * template->lt_table_size);
238 if (template->lt_entries == NULL) {
239 kfree(template, sizeof (*template));
240 template = NULL;
241 }
242
243 return (template);
244}
245
246void
247ledger_template_dereference(ledger_template_t template)
248{
249 template_lock(template);
250 template->lt_refs--;
251 template_unlock(template);
252
253 if (template->lt_refs == 0)
254 kfree(template, sizeof (*template));
255}
256
257/*
258 * Add a new entry to the list of entries in a ledger template. There is
259 * currently no mechanism to remove an entry. Implementing such a mechanism
260 * would require us to maintain per-entry reference counts, which we would
261 * prefer to avoid if possible.
262 */
263int
264ledger_entry_add(ledger_template_t template, const char *key,
265 const char *group, const char *units)
266{
267 int idx;
268 struct entry_template *et;
269
5ba3f43e 270 if ((key == NULL) || (strlen(key) >= LEDGER_NAME_MAX) || (template->lt_zone != NULL))
316670eb
A
271 return (-1);
272
273 template_lock(template);
274
275 /* If the table is full, attempt to double its size */
276 if (template->lt_cnt == template->lt_table_size) {
277 struct entry_template *new_entries, *old_entries;
813fb2f6 278 int old_cnt, old_sz, new_sz = 0;
316670eb
A
279 spl_t s;
280
281 old_cnt = template->lt_table_size;
813fb2f6
A
282 old_sz = old_cnt * (int)(sizeof(struct entry_template));
283 /* double old_sz allocation, but check for overflow */
284 if (os_mul_overflow(old_sz, 2, &new_sz)) {
285 template_unlock(template);
286 return -1;
287 }
288 new_entries = kalloc(new_sz);
316670eb
A
289 if (new_entries == NULL) {
290 template_unlock(template);
813fb2f6 291 return -1;
1c79356b 292 }
316670eb
A
293 memcpy(new_entries, template->lt_entries, old_sz);
294 memset(((char *)new_entries) + old_sz, 0, old_sz);
813fb2f6 295 /* assume: if the sz didn't overflow, neither will the count */
316670eb
A
296 template->lt_table_size = old_cnt * 2;
297
298 old_entries = template->lt_entries;
299
300 TEMPLATE_INUSE(s, template);
301 template->lt_entries = new_entries;
302 TEMPLATE_IDLE(s, template);
303
304 kfree(old_entries, old_sz);
1c79356b 305 }
316670eb
A
306
307 et = &template->lt_entries[template->lt_cnt];
308 strlcpy(et->et_key, key, LEDGER_NAME_MAX);
309 strlcpy(et->et_group, group, LEDGER_NAME_MAX);
310 strlcpy(et->et_units, units, LEDGER_NAME_MAX);
39236c6e 311 et->et_flags = LF_ENTRY_ACTIVE;
316670eb
A
312 et->et_callback = NULL;
313
314 idx = template->lt_cnt++;
315 template_unlock(template);
316
317 return (idx);
318}
319
320
321kern_return_t
322ledger_entry_setactive(ledger_t ledger, int entry)
323{
324 struct ledger_entry *le;
325
326 if ((ledger == NULL) || (entry < 0) || (entry >= ledger->l_size))
327 return (KERN_INVALID_ARGUMENT);
328
329 le = &ledger->l_entries[entry];
39236c6e
A
330 if ((le->le_flags & LF_ENTRY_ACTIVE) == 0) {
331 flag_set(&le->le_flags, LF_ENTRY_ACTIVE);
1c79356b 332 }
316670eb 333 return (KERN_SUCCESS);
1c79356b
A
334}
335
316670eb
A
336
337int
338ledger_key_lookup(ledger_template_t template, const char *key)
1c79356b 339{
316670eb
A
340 int idx;
341
342 template_lock(template);
343 for (idx = 0; idx < template->lt_cnt; idx++)
3e170ce0 344 if (template->lt_entries != NULL &&
316670eb
A
345 (strcmp(key, template->lt_entries[idx].et_key) == 0))
346 break;
1c79356b 347
316670eb
A
348 if (idx >= template->lt_cnt)
349 idx = -1;
350 template_unlock(template);
351
352 return (idx);
353}
1c79356b 354
5ba3f43e
A
355/*
356 * Complete the initialization of ledger template
357 * by initializing ledger zone. After initializing
358 * the ledger zone, adding an entry in the ledger
359 * template would fail.
360 */
361void
362ledger_template_complete(ledger_template_t template)
363{
364 size_t ledger_size;
365 ledger_size = sizeof(struct ledger) + (template->lt_cnt * sizeof(struct ledger_entry));
366 template->lt_zone = zinit(ledger_size, CONFIG_TASK_MAX * ledger_size,
367 ledger_size,
368 template->lt_name);
369}
370
316670eb
A
371/*
372 * Create a new ledger based on the specified template. As part of the
373 * ledger creation we need to allocate space for a table of ledger entries.
374 * The size of the table is based on the size of the template at the time
375 * the ledger is created. If additional entries are added to the template
376 * after the ledger is created, they will not be tracked in this ledger.
377 */
378ledger_t
379ledger_instantiate(ledger_template_t template, int entry_type)
380{
381 ledger_t ledger;
5ba3f43e 382 size_t cnt;
316670eb
A
383 int i;
384
316670eb
A
385 template_lock(template);
386 template->lt_refs++;
39037602 387 cnt = template->lt_cnt;
5ba3f43e 388 assert(template->lt_zone);
316670eb
A
389 template_unlock(template);
390
5ba3f43e 391 ledger = (ledger_t)zalloc(template->lt_zone);
39037602 392 if (ledger == NULL) {
316670eb 393 ledger_template_dereference(template);
39037602 394 return LEDGER_NULL;
2d21ac55 395 }
1c79356b 396
39037602
A
397 ledger->l_template = template;
398 ledger->l_id = ledger_cnt++;
399 ledger->l_refs = 1;
400 ledger->l_size = (int32_t)cnt;
401
316670eb
A
402 template_lock(template);
403 assert(ledger->l_size <= template->lt_cnt);
404 for (i = 0; i < ledger->l_size; i++) {
405 struct ledger_entry *le = &ledger->l_entries[i];
406 struct entry_template *et = &template->lt_entries[i];
1c79356b 407
316670eb
A
408 le->le_flags = et->et_flags;
409 /* make entry inactive by removing active bit */
410 if (entry_type == LEDGER_CREATE_INACTIVE_ENTRIES)
39236c6e 411 flag_clear(&le->le_flags, LF_ENTRY_ACTIVE);
316670eb
A
412 /*
413 * If template has a callback, this entry is opted-in,
414 * by default.
415 */
416 if (et->et_callback != NULL)
417 flag_set(&le->le_flags, LEDGER_ACTION_CALLBACK);
39236c6e
A
418 le->le_credit = 0;
419 le->le_debit = 0;
420 le->le_limit = LEDGER_LIMIT_INFINITY;
421 le->le_warn_level = LEDGER_LIMIT_INFINITY;
422 le->_le.le_refill.le_refill_period = 0;
423 le->_le.le_refill.le_last_refill = 0;
316670eb
A
424 }
425 template_unlock(template);
426
427 return (ledger);
1c79356b
A
428}
429
316670eb
A
430static uint32_t
431flag_set(volatile uint32_t *flags, uint32_t bit)
1c79356b 432{
316670eb
A
433 return (OSBitOrAtomic(bit, flags));
434}
1c79356b 435
316670eb
A
436static uint32_t
437flag_clear(volatile uint32_t *flags, uint32_t bit)
438{
439 return (OSBitAndAtomic(~bit, flags));
440}
441
442/*
443 * Take a reference on a ledger
444 */
445kern_return_t
446ledger_reference(ledger_t ledger)
447{
448 if (!LEDGER_VALID(ledger))
449 return (KERN_INVALID_ARGUMENT);
450 OSIncrementAtomic(&ledger->l_refs);
451 return (KERN_SUCCESS);
1c79356b
A
452}
453
316670eb
A
454int
455ledger_reference_count(ledger_t ledger)
456{
457 if (!LEDGER_VALID(ledger))
458 return (-1);
459
460 return (ledger->l_refs);
461}
1c79356b
A
462
463/*
316670eb
A
464 * Remove a reference on a ledger. If this is the last reference,
465 * deallocate the unused ledger.
1c79356b 466 */
316670eb
A
467kern_return_t
468ledger_dereference(ledger_t ledger)
1c79356b 469{
316670eb
A
470 int v;
471
472 if (!LEDGER_VALID(ledger))
473 return (KERN_INVALID_ARGUMENT);
474
475 v = OSDecrementAtomic(&ledger->l_refs);
476 ASSERT(v >= 1);
1c79356b 477
316670eb
A
478 /* Just released the last reference. Free it. */
479 if (v == 1) {
5ba3f43e 480 zfree(ledger->l_template->lt_zone, ledger);
316670eb
A
481 }
482
483 return (KERN_SUCCESS);
484}
485
39236c6e
A
486/*
487 * Determine whether an entry has exceeded its warning level.
488 */
489static inline int
490warn_level_exceeded(struct ledger_entry *le)
491{
492 ledger_amount_t balance;
493
39037602
A
494 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
495 assert(le->le_debit == 0);
496 } else {
497 assert((le->le_credit >= 0) && (le->le_debit >= 0));
498 }
39236c6e
A
499
500 /*
501 * XXX - Currently, we only support warnings for ledgers which
502 * use positive limits.
503 */
504 balance = le->le_credit - le->le_debit;
505 if ((le->le_warn_level != LEDGER_LIMIT_INFINITY) && (balance > le->le_warn_level))
506 return (1);
507 return (0);
508}
509
316670eb
A
510/*
511 * Determine whether an entry has exceeded its limit.
512 */
513static inline int
514limit_exceeded(struct ledger_entry *le)
515{
516 ledger_amount_t balance;
517
39037602
A
518 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
519 assert(le->le_debit == 0);
520 } else {
521 assert((le->le_credit >= 0) && (le->le_debit >= 0));
522 }
39236c6e 523
316670eb
A
524 balance = le->le_credit - le->le_debit;
525 if ((le->le_limit <= 0) && (balance < le->le_limit))
526 return (1);
527
528 if ((le->le_limit > 0) && (balance > le->le_limit))
529 return (1);
530 return (0);
531}
532
533static inline struct ledger_callback *
534entry_get_callback(ledger_t ledger, int entry)
535{
536 struct ledger_callback *callback;
537 spl_t s;
538
539 TEMPLATE_INUSE(s, ledger->l_template);
540 callback = ledger->l_template->lt_entries[entry].et_callback;
541 TEMPLATE_IDLE(s, ledger->l_template);
542
543 return (callback);
544}
545
546/*
547 * If the ledger value is positive, wake up anybody waiting on it.
548 */
549static inline void
550ledger_limit_entry_wakeup(struct ledger_entry *le)
551{
552 uint32_t flags;
553
554 if (!limit_exceeded(le)) {
39236c6e 555 flags = flag_clear(&le->le_flags, LF_CALLED_BACK);
316670eb 556
39236c6e
A
557 while (le->le_flags & LF_WAKE_NEEDED) {
558 flag_clear(&le->le_flags, LF_WAKE_NEEDED);
316670eb
A
559 thread_wakeup((event_t)le);
560 }
561 }
1c79356b
A
562}
563
564/*
316670eb 565 * Refill the coffers.
1c79356b 566 */
316670eb
A
567static void
568ledger_refill(uint64_t now, ledger_t ledger, int entry)
1c79356b 569{
316670eb
A
570 uint64_t elapsed, period, periods;
571 struct ledger_entry *le;
572 ledger_amount_t balance, due;
1c79356b 573
39037602
A
574 assert(entry >= 0 && entry < ledger->l_size);
575
316670eb 576 le = &ledger->l_entries[entry];
1c79356b 577
39236c6e
A
578 assert(le->le_limit != LEDGER_LIMIT_INFINITY);
579
39037602
A
580 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
581 assert(le->le_debit == 0);
582 return;
583 }
584
1c79356b 585 /*
316670eb 586 * If another thread is handling the refill already, we're not
39236c6e
A
587 * needed.
588 */
589 if (flag_set(&le->le_flags, LF_REFILL_INPROGRESS) & LF_REFILL_INPROGRESS) {
590 return;
591 }
592
593 /*
594 * If the timestamp we're about to use to refill is older than the
595 * last refill, then someone else has already refilled this ledger
596 * and there's nothing for us to do here.
1c79356b 597 */
39236c6e
A
598 if (now <= le->_le.le_refill.le_last_refill) {
599 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
316670eb 600 return;
1c79356b
A
601 }
602
316670eb
A
603 /*
604 * See how many refill periods have passed since we last
605 * did a refill.
606 */
39236c6e
A
607 period = le->_le.le_refill.le_refill_period;
608 elapsed = now - le->_le.le_refill.le_last_refill;
316670eb 609 if ((period == 0) || (elapsed < period)) {
39236c6e 610 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
316670eb 611 return;
1c79356b 612 }
316670eb
A
613
614 /*
615 * Optimize for the most common case of only one or two
616 * periods elapsing.
617 */
618 periods = 0;
619 while ((periods < 2) && (elapsed > 0)) {
620 periods++;
621 elapsed -= period;
622 }
623
624 /*
625 * OK, it's been a long time. Do a divide to figure out
626 * how long.
627 */
628 if (elapsed > 0)
39236c6e 629 periods = (now - le->_le.le_refill.le_last_refill) / period;
316670eb
A
630
631 balance = le->le_credit - le->le_debit;
632 due = periods * le->le_limit;
813fb2f6 633
316670eb
A
634 if (balance - due < 0)
635 due = balance;
39236c6e 636
813fb2f6 637 assertf(due >= 0,"now=%llu, ledger=%p, entry=%d, balance=%lld, due=%lld", now, ledger, entry, balance, due);
39236c6e 638
316670eb
A
639 OSAddAtomic64(due, &le->le_debit);
640
39236c6e
A
641 assert(le->le_debit >= 0);
642
1c79356b 643 /*
316670eb
A
644 * If we've completely refilled the pool, set the refill time to now.
645 * Otherwise set it to the time at which it last should have been
646 * fully refilled.
1c79356b 647 */
316670eb 648 if (balance == due)
39236c6e 649 le->_le.le_refill.le_last_refill = now;
316670eb 650 else
39236c6e 651 le->_le.le_refill.le_last_refill += (le->_le.le_refill.le_refill_period * periods);
316670eb 652
39236c6e 653 flag_clear(&le->le_flags, LF_REFILL_INPROGRESS);
316670eb
A
654
655 lprintf(("Refill %lld %lld->%lld\n", periods, balance, balance - due));
656 if (!limit_exceeded(le))
657 ledger_limit_entry_wakeup(le);
658}
659
39236c6e
A
660/*
661 * In tenths of a second, the length of one lookback period (a "tock") for
662 * ledger rolling maximum calculations. The effective lookback window will be this times
663 * NTOCKS.
664 *
665 * Use a tock length of 2.5 seconds to get a total lookback period of 5 seconds.
666 *
667 * XXX Could make this caller-definable, at the point that rolling max tracking
668 * is enabled for the entry.
669 */
670#define TOCKLEN 25
671
672/*
673 * How many sched_tick's are there in one tock (one of our lookback periods)?
674 *
675 * X sched_ticks 2.5 sec N sched_ticks
676 * --------------- = ---------- * -------------
677 * tock tock sec
678 *
679 * where N sched_ticks/sec is calculated via 1 << SCHED_TICK_SHIFT (see sched_prim.h)
680 *
681 * This should give us 20 sched_tick's in one 2.5 second-long tock.
682 */
683#define SCHED_TICKS_PER_TOCK ((TOCKLEN * (1 << SCHED_TICK_SHIFT)) / 10)
684
685/*
686 * Rolling max timestamps use their own unit (let's call this a "tock"). One tock is the
687 * length of one lookback period that we use for our rolling max calculation.
688 *
689 * Calculate the current time in tocks from sched_tick (which runs at a some
690 * fixed rate).
691 */
692#define CURRENT_TOCKSTAMP() (sched_tick / SCHED_TICKS_PER_TOCK)
693
694/*
695 * Does the given tockstamp fall in either the current or the previous tocks?
696 */
697#define TOCKSTAMP_IS_STALE(now, tock) ((((now) - (tock)) < NTOCKS) ? FALSE : TRUE)
698
3e170ce0 699void
5c9f4661
A
700ledger_entry_check_new_balance(thread_t thread, ledger_t ledger,
701 int entry, struct ledger_entry *le)
316670eb 702{
39037602 703 ledger_amount_t credit, debit;
316670eb 704
39236c6e
A
705 if (le->le_flags & LF_TRACKING_MAX) {
706 ledger_amount_t balance = le->le_credit - le->le_debit;
707 uint32_t now = CURRENT_TOCKSTAMP();
5ba3f43e 708 struct _le_peak *p = &le->_le.le_maxtracking.le_peaks[now % NTOCKS];
39236c6e
A
709
710 if (!TOCKSTAMP_IS_STALE(now, p->le_time) || (balance > p->le_max)) {
711 /*
712 * The current balance is greater than the previously
713 * observed peak for the current time block, *or* we
714 * haven't yet recorded a peak for the current time block --
715 * so this is our new peak.
716 *
717 * (We only track the lower 32-bits of a balance for rolling
718 * max purposes.)
719 */
720 p->le_max = (uint32_t)balance;
721 p->le_time = now;
722 }
5ba3f43e
A
723
724 struct _le_maxtracking *m = &le->_le.le_maxtracking;
725 if(balance > m->le_lifetime_max){
726 m->le_lifetime_max = balance;
727 }
39236c6e
A
728 }
729
316670eb 730 /* Check to see whether we're due a refill */
39236c6e 731 if (le->le_flags & LF_REFILL_SCHEDULED) {
5ba3f43e
A
732 assert(!(le->le_flags & LF_TRACKING_MAX));
733
39236c6e
A
734 uint64_t now = mach_absolute_time();
735 if ((now - le->_le.le_refill.le_last_refill) > le->_le.le_refill.le_refill_period)
316670eb
A
736 ledger_refill(now, ledger, entry);
737 }
738
739 if (limit_exceeded(le)) {
740 /*
741 * We've exceeded the limit for this entry. There
742 * are several possible ways to handle it. We can block,
743 * we can execute a callback, or we can ignore it. In
744 * either of the first two cases, we want to set the AST
745 * flag so we can take the appropriate action just before
746 * leaving the kernel. The one caveat is that if we have
747 * already called the callback, we don't want to do it
748 * again until it gets rearmed.
749 */
750 if ((le->le_flags & LEDGER_ACTION_BLOCK) ||
39236c6e 751 (!(le->le_flags & LF_CALLED_BACK) &&
316670eb 752 entry_get_callback(ledger, entry))) {
5c9f4661 753 act_set_astledger_async(thread);
1c79356b 754 }
316670eb
A
755 } else {
756 /*
39236c6e
A
757 * The balance on the account is below the limit.
758 *
759 * If there are any threads blocked on this entry, now would
316670eb
A
760 * be a good time to wake them up.
761 */
39236c6e 762 if (le->le_flags & LF_WAKE_NEEDED)
316670eb 763 ledger_limit_entry_wakeup(le);
39236c6e
A
764
765 if (le->le_flags & LEDGER_ACTION_CALLBACK) {
766 /*
767 * Client has requested that a callback be invoked whenever
768 * the ledger's balance crosses into or out of the warning
769 * level.
770 */
771 if (warn_level_exceeded(le)) {
772 /*
773 * This ledger's balance is above the warning level.
774 */
775 if ((le->le_flags & LF_WARNED) == 0) {
776 /*
777 * If we are above the warning level and
778 * have not yet invoked the callback,
779 * set the AST so it can be done before returning
780 * to userland.
781 */
5c9f4661 782 act_set_astledger_async(thread);
39236c6e
A
783 }
784 } else {
785 /*
786 * This ledger's balance is below the warning level.
787 */
788 if (le->le_flags & LF_WARNED) {
789 /*
790 * If we are below the warning level and
791 * the LF_WARNED flag is still set, we need
792 * to invoke the callback to let the client
793 * know the ledger balance is now back below
794 * the warning level.
795 */
5c9f4661 796 act_set_astledger_async(thread);
39236c6e
A
797 }
798 }
799 }
1c79356b 800 }
fe8ab488 801
39037602
A
802 credit = le->le_credit;
803 debit = le->le_debit;
fe8ab488 804 if ((le->le_flags & LF_PANIC_ON_NEGATIVE) &&
39037602
A
805 ((credit < debit) ||
806 (le->le_credit < le->le_debit))) {
807 panic("ledger_entry_check_new_balance(%p,%d): negative ledger %p credit:%lld/%lld debit:%lld/%lld balance:%lld/%lld\n",
808 ledger, entry, le,
809 credit, le->le_credit,
810 debit, le->le_debit,
811 credit - debit, le->le_credit - le->le_debit);
fe8ab488 812 }
316670eb 813}
1c79356b 814
39037602 815void
5c9f4661 816ledger_check_new_balance(thread_t thread, ledger_t ledger, int entry)
39037602
A
817{
818 struct ledger_entry *le;
819 assert(entry > 0 && entry <= ledger->l_size);
820 le = &ledger->l_entries[entry];
5c9f4661 821 ledger_entry_check_new_balance(thread, ledger, entry, le);
39037602
A
822}
823
316670eb 824/*
5c9f4661 825 * Add value to an entry in a ledger for a specific thread.
316670eb
A
826 */
827kern_return_t
5c9f4661 828ledger_credit_thread(thread_t thread, ledger_t ledger, int entry, ledger_amount_t amount)
316670eb
A
829{
830 ledger_amount_t old, new;
831 struct ledger_entry *le;
1c79356b 832
316670eb
A
833 if (!ENTRY_VALID(ledger, entry) || (amount < 0))
834 return (KERN_INVALID_VALUE);
835
836 if (amount == 0)
837 return (KERN_SUCCESS);
838
839 le = &ledger->l_entries[entry];
840
841 old = OSAddAtomic64(amount, &le->le_credit);
842 new = old + amount;
5c9f4661
A
843 lprintf(("%p Credit %lld->%lld\n", thread, old, new));
844
845 ledger_entry_check_new_balance(thread, ledger, entry, le);
316670eb
A
846
847 return (KERN_SUCCESS);
1c79356b
A
848}
849
5c9f4661
A
850/*
851 * Add value to an entry in a ledger.
852 */
853kern_return_t
854ledger_credit(ledger_t ledger, int entry, ledger_amount_t amount)
855{
856 return ledger_credit_thread(current_thread(), ledger, entry, amount);
857}
858
fe8ab488
A
859/* Add all of one ledger's values into another.
860 * They must have been created from the same template.
861 * This is not done atomically. Another thread (if not otherwise synchronized)
862 * may see bogus values when comparing one entry to another.
863 * As each entry's credit & debit are modified one at a time, the warning/limit
864 * may spuriously trip, or spuriously fail to trip, or another thread (if not
865 * otherwise synchronized) may see a bogus balance.
866 */
867kern_return_t
868ledger_rollup(ledger_t to_ledger, ledger_t from_ledger)
869{
870 int i;
fe8ab488
A
871
872 assert(to_ledger->l_template == from_ledger->l_template);
873
874 for (i = 0; i < to_ledger->l_size; i++) {
743345f9
A
875 ledger_rollup_entry(to_ledger, from_ledger, i);
876 }
877
878 return (KERN_SUCCESS);
879}
880
881/* Add one ledger entry value to another.
882 * They must have been created from the same template.
883 * Since the credit and debit values are added one
884 * at a time, other thread might read the a bogus value.
885 */
886kern_return_t
887ledger_rollup_entry(ledger_t to_ledger, ledger_t from_ledger, int entry)
888{
889 struct ledger_entry *from_le, *to_le;
890
891 assert(to_ledger->l_template == from_ledger->l_template);
892 if (ENTRY_VALID(from_ledger, entry) && ENTRY_VALID(to_ledger, entry)) {
893 from_le = &from_ledger->l_entries[entry];
894 to_le = &to_ledger->l_entries[entry];
895 OSAddAtomic64(from_le->le_credit, &to_le->le_credit);
896 OSAddAtomic64(from_le->le_debit, &to_le->le_debit);
fe8ab488
A
897 }
898
899 return (KERN_SUCCESS);
900}
901
39236c6e
A
902/*
903 * Zero the balance of a ledger by adding to its credit or debit, whichever is smaller.
904 * Note that some clients of ledgers (notably, task wakeup statistics) require that
905 * le_credit only ever increase as a function of ledger_credit().
906 */
907kern_return_t
908ledger_zero_balance(ledger_t ledger, int entry)
909{
910 struct ledger_entry *le;
813fb2f6 911 ledger_amount_t debit, credit;
39236c6e
A
912
913 if (!ENTRY_VALID(ledger, entry))
914 return (KERN_INVALID_VALUE);
915
916 le = &ledger->l_entries[entry];
917
918top:
813fb2f6
A
919 debit = le->le_debit;
920 credit = le->le_credit;
921
39037602
A
922 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
923 assert(le->le_debit == 0);
813fb2f6 924 if (!OSCompareAndSwap64(credit, 0, &le->le_credit)) {
39037602
A
925 goto top;
926 }
927 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, 0));
813fb2f6
A
928 } else if (credit > debit) {
929 if (!OSCompareAndSwap64(debit, credit, &le->le_debit))
39236c6e
A
930 goto top;
931 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_debit, le->le_credit));
813fb2f6
A
932 } else if (credit < debit) {
933 if (!OSCompareAndSwap64(credit, debit, &le->le_credit))
39236c6e
A
934 goto top;
935 lprintf(("%p zeroed %lld->%lld\n", current_thread(), le->le_credit, le->le_debit));
936 }
937
938 return (KERN_SUCCESS);
939}
940
941kern_return_t
942ledger_get_limit(ledger_t ledger, int entry, ledger_amount_t *limit)
943{
944 struct ledger_entry *le;
945
946 if (!ENTRY_VALID(ledger, entry))
947 return (KERN_INVALID_VALUE);
948
949 le = &ledger->l_entries[entry];
950 *limit = le->le_limit;
951
952 lprintf(("ledger_get_limit: %lld\n", *limit));
953
954 return (KERN_SUCCESS);
955}
316670eb 956
1c79356b 957/*
316670eb
A
958 * Adjust the limit of a limited resource. This does not affect the
959 * current balance, so the change doesn't affect the thread until the
960 * next refill.
39236c6e
A
961 *
962 * warn_level: If non-zero, causes the callback to be invoked when
963 * the balance exceeds this level. Specified as a percentage [of the limit].
1c79356b 964 */
316670eb 965kern_return_t
39236c6e
A
966ledger_set_limit(ledger_t ledger, int entry, ledger_amount_t limit,
967 uint8_t warn_level_percentage)
1c79356b 968{
316670eb 969 struct ledger_entry *le;
1c79356b 970
316670eb
A
971 if (!ENTRY_VALID(ledger, entry))
972 return (KERN_INVALID_VALUE);
973
39236c6e 974 lprintf(("ledger_set_limit: %lld\n", limit));
316670eb 975 le = &ledger->l_entries[entry];
39236c6e
A
976
977 if (limit == LEDGER_LIMIT_INFINITY) {
978 /*
979 * Caller wishes to disable the limit. This will implicitly
980 * disable automatic refill, as refills implicitly depend
981 * on the limit.
982 */
983 ledger_disable_refill(ledger, entry);
984 }
985
316670eb 986 le->le_limit = limit;
5ba3f43e
A
987 if (le->le_flags & LF_REFILL_SCHEDULED) {
988 assert(!(le->le_flags & LF_TRACKING_MAX));
989 le->_le.le_refill.le_last_refill = 0;
990 }
39236c6e
A
991 flag_clear(&le->le_flags, LF_CALLED_BACK);
992 flag_clear(&le->le_flags, LF_WARNED);
316670eb
A
993 ledger_limit_entry_wakeup(le);
994
39236c6e
A
995 if (warn_level_percentage != 0) {
996 assert(warn_level_percentage <= 100);
997 assert(limit > 0); /* no negative limit support for warnings */
998 assert(limit != LEDGER_LIMIT_INFINITY); /* warn % without limit makes no sense */
999 le->le_warn_level = (le->le_limit * warn_level_percentage) / 100;
1000 } else {
1001 le->le_warn_level = LEDGER_LIMIT_INFINITY;
1002 }
1003
1004 return (KERN_SUCCESS);
1005}
1006
1007kern_return_t
5ba3f43e 1008ledger_get_recent_max(ledger_t ledger, int entry,
39236c6e
A
1009 ledger_amount_t *max_observed_balance)
1010{
1011 struct ledger_entry *le;
1012 uint32_t now = CURRENT_TOCKSTAMP();
1013 int i;
1014
1015 le = &ledger->l_entries[entry];
1016
1017 if (!ENTRY_VALID(ledger, entry) || !(le->le_flags & LF_TRACKING_MAX)) {
1018 return (KERN_INVALID_VALUE);
1019 }
1020
1021 /*
1022 * Start with the current balance; if neither of the recorded peaks are
1023 * within recent history, we use this.
1024 */
1025 *max_observed_balance = le->le_credit - le->le_debit;
1026
1027 for (i = 0; i < NTOCKS; i++) {
5ba3f43e
A
1028 if (!TOCKSTAMP_IS_STALE(now, le->_le.le_maxtracking.le_peaks[i].le_time) &&
1029 (le->_le.le_maxtracking.le_peaks[i].le_max > *max_observed_balance)) {
39236c6e
A
1030 /*
1031 * The peak for this time block isn't stale, and it
1032 * is greater than the current balance -- so use it.
1033 */
5ba3f43e 1034 *max_observed_balance = le->_le.le_maxtracking.le_peaks[i].le_max;
39236c6e
A
1035 }
1036 }
5ba3f43e 1037
39236c6e
A
1038 lprintf(("ledger_get_maximum: %lld\n", *max_observed_balance));
1039
1040 return (KERN_SUCCESS);
1041}
1042
5ba3f43e
A
1043kern_return_t
1044ledger_get_lifetime_max(ledger_t ledger, int entry,
1045 ledger_amount_t *max_lifetime_balance)
1046{
1047 struct ledger_entry *le;
1048 le = &ledger->l_entries[entry];
1049
1050 if (!ENTRY_VALID(ledger, entry) || !(le->le_flags & LF_TRACKING_MAX)) {
1051 return (KERN_INVALID_VALUE);
1052 }
1053
1054 *max_lifetime_balance = le->_le.le_maxtracking.le_lifetime_max;
1055 lprintf(("ledger_get_lifetime_max: %lld\n", *max_lifetime_balance));
1056
1057 return (KERN_SUCCESS);
1058}
1059
39236c6e
A
1060/*
1061 * Enable tracking of periodic maximums for this ledger entry.
1062 */
1063kern_return_t
1064ledger_track_maximum(ledger_template_t template, int entry,
1065 __unused int period_in_secs)
1066{
1067 template_lock(template);
1068
1069 if ((entry < 0) || (entry >= template->lt_cnt)) {
1070 template_unlock(template);
1071 return (KERN_INVALID_VALUE);
1072 }
1073
5ba3f43e
A
1074 /* Refill is incompatible with max tracking. */
1075 if (template->lt_entries[entry].et_flags & LF_REFILL_SCHEDULED) {
1076 return (KERN_INVALID_VALUE);
1077 }
1078
39236c6e 1079 template->lt_entries[entry].et_flags |= LF_TRACKING_MAX;
5ba3f43e 1080 template_unlock(template);
39236c6e 1081
316670eb
A
1082 return (KERN_SUCCESS);
1083}
1084
fe8ab488
A
1085kern_return_t
1086ledger_panic_on_negative(ledger_template_t template, int entry)
1087{
1088 template_lock(template);
1089
1090 if ((entry < 0) || (entry >= template->lt_cnt)) {
39037602 1091 template_unlock(template);
fe8ab488
A
1092 return (KERN_INVALID_VALUE);
1093 }
1094
1095 template->lt_entries[entry].et_flags |= LF_PANIC_ON_NEGATIVE;
1096
39037602 1097 template_unlock(template);
fe8ab488
A
1098
1099 return (KERN_SUCCESS);
1100}
39037602
A
1101
1102kern_return_t
1103ledger_track_credit_only(ledger_template_t template, int entry)
1104{
1105 template_lock(template);
1106
1107 if ((entry < 0) || (entry >= template->lt_cnt)) {
1108 template_unlock(template);
1109 return (KERN_INVALID_VALUE);
1110 }
1111
1112 template->lt_entries[entry].et_flags |= LF_TRACK_CREDIT_ONLY;
1113
1114 template_unlock(template);
1115
1116 return (KERN_SUCCESS);
1117}
1118
316670eb 1119/*
39236c6e 1120 * Add a callback to be executed when the resource goes into deficit.
316670eb
A
1121 */
1122kern_return_t
1123ledger_set_callback(ledger_template_t template, int entry,
1124 ledger_callback_t func, const void *param0, const void *param1)
1125{
1126 struct entry_template *et;
1127 struct ledger_callback *old_cb, *new_cb;
1128
1129 if ((entry < 0) || (entry >= template->lt_cnt))
1130 return (KERN_INVALID_VALUE);
1131
1132 if (func) {
1133 new_cb = (struct ledger_callback *)kalloc(sizeof (*new_cb));
1134 new_cb->lc_func = func;
1135 new_cb->lc_param0 = param0;
1136 new_cb->lc_param1 = param1;
1137 } else {
1138 new_cb = NULL;
1c79356b 1139 }
1c79356b 1140
316670eb
A
1141 template_lock(template);
1142 et = &template->lt_entries[entry];
1143 old_cb = et->et_callback;
1144 et->et_callback = new_cb;
1145 template_unlock(template);
1146 if (old_cb)
1147 kfree(old_cb, sizeof (*old_cb));
1c79356b 1148
316670eb
A
1149 return (KERN_SUCCESS);
1150}
1c79356b 1151
316670eb
A
1152/*
1153 * Disable callback notification for a specific ledger entry.
1154 *
1155 * Otherwise, if using a ledger template which specified a
1156 * callback function (ledger_set_callback()), it will be invoked when
1157 * the resource goes into deficit.
1158 */
1159kern_return_t
1160ledger_disable_callback(ledger_t ledger, int entry)
1161{
1162 if (!ENTRY_VALID(ledger, entry))
1163 return (KERN_INVALID_VALUE);
1164
39236c6e
A
1165 /*
1166 * le_warn_level is used to indicate *if* this ledger has a warning configured,
1167 * in addition to what that warning level is set to.
1168 * This means a side-effect of ledger_disable_callback() is that the
1169 * warning level is forgotten.
1170 */
1171 ledger->l_entries[entry].le_warn_level = LEDGER_LIMIT_INFINITY;
316670eb
A
1172 flag_clear(&ledger->l_entries[entry].le_flags, LEDGER_ACTION_CALLBACK);
1173 return (KERN_SUCCESS);
1c79356b
A
1174}
1175
1176/*
39236c6e
A
1177 * Enable callback notification for a specific ledger entry.
1178 *
1179 * This is only needed if ledger_disable_callback() has previously
1180 * been invoked against an entry; there must already be a callback
1181 * configured.
1c79356b 1182 */
316670eb 1183kern_return_t
39236c6e 1184ledger_enable_callback(ledger_t ledger, int entry)
1c79356b 1185{
316670eb
A
1186 if (!ENTRY_VALID(ledger, entry))
1187 return (KERN_INVALID_VALUE);
1c79356b 1188
39236c6e
A
1189 assert(entry_get_callback(ledger, entry) != NULL);
1190
1191 flag_set(&ledger->l_entries[entry].le_flags, LEDGER_ACTION_CALLBACK);
1192 return (KERN_SUCCESS);
1193}
1194
1195/*
1196 * Query the automatic refill period for this ledger entry.
1197 *
1198 * A period of 0 means this entry has none configured.
1199 */
1200kern_return_t
1201ledger_get_period(ledger_t ledger, int entry, uint64_t *period)
1202{
1203 struct ledger_entry *le;
1204
1205 if (!ENTRY_VALID(ledger, entry))
1206 return (KERN_INVALID_VALUE);
1207
1208 le = &ledger->l_entries[entry];
1209 *period = abstime_to_nsecs(le->_le.le_refill.le_refill_period);
1210 lprintf(("ledger_get_period: %llx\n", *period));
316670eb 1211 return (KERN_SUCCESS);
1c79356b
A
1212}
1213
1214/*
316670eb 1215 * Adjust the automatic refill period.
1c79356b 1216 */
316670eb
A
1217kern_return_t
1218ledger_set_period(ledger_t ledger, int entry, uint64_t period)
1c79356b 1219{
316670eb 1220 struct ledger_entry *le;
1c79356b 1221
316670eb
A
1222 lprintf(("ledger_set_period: %llx\n", period));
1223 if (!ENTRY_VALID(ledger, entry))
1224 return (KERN_INVALID_VALUE);
1c79356b 1225
316670eb 1226 le = &ledger->l_entries[entry];
1c79356b 1227
39236c6e
A
1228 /*
1229 * A refill period refills the ledger in multiples of the limit,
1230 * so if you haven't set one yet, you need a lesson on ledgers.
1231 */
1232 assert(le->le_limit != LEDGER_LIMIT_INFINITY);
1233
1234 if (le->le_flags & LF_TRACKING_MAX) {
1235 /*
1236 * Refill is incompatible with rolling max tracking.
1237 */
1238 return (KERN_INVALID_VALUE);
1239 }
1240
1241 le->_le.le_refill.le_refill_period = nsecs_to_abstime(period);
1242
1243 /*
1244 * Set the 'starting time' for the next refill to now. Since
1245 * we're resetting the balance to zero here, we consider this
1246 * moment the starting time for accumulating a balance that
1247 * counts towards the limit.
1248 */
1249 le->_le.le_refill.le_last_refill = mach_absolute_time();
1250 ledger_zero_balance(ledger, entry);
1251
1252 flag_set(&le->le_flags, LF_REFILL_SCHEDULED);
1253
1254 return (KERN_SUCCESS);
1255}
1256
1257/*
1258 * Disable automatic refill.
1259 */
1260kern_return_t
1261ledger_disable_refill(ledger_t ledger, int entry)
1262{
1263 struct ledger_entry *le;
1264
1265 if (!ENTRY_VALID(ledger, entry))
1266 return (KERN_INVALID_VALUE);
1267
1268 le = &ledger->l_entries[entry];
1269
1270 flag_clear(&le->le_flags, LF_REFILL_SCHEDULED);
1271
1272 return (KERN_SUCCESS);
1273}
1274
1275kern_return_t
1276ledger_get_actions(ledger_t ledger, int entry, int *actions)
1277{
1278 if (!ENTRY_VALID(ledger, entry))
1279 return (KERN_INVALID_VALUE);
1280
1281 *actions = ledger->l_entries[entry].le_flags & LEDGER_ACTION_MASK;
1282 lprintf(("ledger_get_actions: %#x\n", *actions));
316670eb
A
1283 return (KERN_SUCCESS);
1284}
1285
1286kern_return_t
1287ledger_set_action(ledger_t ledger, int entry, int action)
1288{
39236c6e 1289 lprintf(("ledger_set_action: %#x\n", action));
316670eb
A
1290 if (!ENTRY_VALID(ledger, entry))
1291 return (KERN_INVALID_VALUE);
1292
1293 flag_set(&ledger->l_entries[entry].le_flags, action);
1294 return (KERN_SUCCESS);
1295}
1296
316670eb 1297kern_return_t
5c9f4661 1298ledger_debit_thread(thread_t thread, ledger_t ledger, int entry, ledger_amount_t amount)
316670eb
A
1299{
1300 struct ledger_entry *le;
1301 ledger_amount_t old, new;
1302
1303 if (!ENTRY_VALID(ledger, entry) || (amount < 0))
1304 return (KERN_INVALID_ARGUMENT);
1305
1306 if (amount == 0)
1307 return (KERN_SUCCESS);
1308
1309 le = &ledger->l_entries[entry];
1310
39037602
A
1311 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
1312 assert(le->le_debit == 0);
1313 old = OSAddAtomic64(-amount, &le->le_credit);
1314 new = old - amount;
1315 } else {
1316 old = OSAddAtomic64(amount, &le->le_debit);
1317 new = old + amount;
1318 }
316670eb 1319 lprintf(("%p Debit %lld->%lld\n", thread, old, new));
39037602 1320
5c9f4661
A
1321 ledger_entry_check_new_balance(thread, ledger, entry, le);
1322
316670eb 1323 return (KERN_SUCCESS);
5c9f4661 1324}
1c79356b 1325
5c9f4661
A
1326kern_return_t
1327ledger_debit(ledger_t ledger, int entry, ledger_amount_t amount)
1328{
1329 return ledger_debit_thread(current_thread(), ledger, entry, amount);
316670eb
A
1330}
1331
1332void
1333ledger_ast(thread_t thread)
1334{
39236c6e
A
1335 struct ledger *l = thread->t_ledger;
1336 struct ledger *thl;
1337 uint32_t block;
1338 uint64_t now;
1339 uint8_t task_flags;
1340 uint8_t task_percentage;
1341 uint64_t task_interval;
1342
316670eb
A
1343 kern_return_t ret;
1344 task_t task = thread->task;
1345
1346 lprintf(("Ledger AST for %p\n", thread));
1347
1348 ASSERT(task != NULL);
1349 ASSERT(thread == current_thread());
1350
1351top:
39236c6e
A
1352 /*
1353 * Take a self-consistent snapshot of the CPU usage monitor parameters. The task
1354 * can change them at any point (with the task locked).
1355 */
1356 task_lock(task);
1357 task_flags = task->rusage_cpu_flags;
1358 task_percentage = task->rusage_cpu_perthr_percentage;
1359 task_interval = task->rusage_cpu_perthr_interval;
1360 task_unlock(task);
1361
316670eb
A
1362 /*
1363 * Make sure this thread is up to date with regards to any task-wide per-thread
39236c6e 1364 * CPU limit, but only if it doesn't have a thread-private blocking CPU limit.
316670eb 1365 */
39236c6e
A
1366 if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) != 0) &&
1367 ((thread->options & TH_OPT_PRVT_CPULIMIT) == 0)) {
1368 uint8_t percentage;
1369 uint64_t interval;
1370 int action;
1371
1372 thread_get_cpulimit(&action, &percentage, &interval);
1373
316670eb 1374 /*
39236c6e
A
1375 * If the thread's CPU limits no longer match the task's, or the
1376 * task has a limit but the thread doesn't, update the limit.
316670eb 1377 */
39236c6e
A
1378 if (((thread->options & TH_OPT_PROC_CPULIMIT) == 0) ||
1379 (interval != task_interval) || (percentage != task_percentage)) {
1380 thread_set_cpulimit(THREAD_CPULIMIT_EXCEPTION, task_percentage, task_interval);
1381 assert((thread->options & TH_OPT_PROC_CPULIMIT) != 0);
1382 }
1383 } else if (((task_flags & TASK_RUSECPU_FLAGS_PERTHR_LIMIT) == 0) &&
1384 (thread->options & TH_OPT_PROC_CPULIMIT)) {
1385 assert((thread->options & TH_OPT_PRVT_CPULIMIT) == 0);
1386
316670eb
A
1387 /*
1388 * Task no longer has a per-thread CPU limit; remove this thread's
1389 * corresponding CPU limit.
1390 */
39236c6e 1391 thread_set_cpulimit(THREAD_CPULIMIT_DISABLE, 0, 0);
316670eb 1392 assert((thread->options & TH_OPT_PROC_CPULIMIT) == 0);
1c79356b 1393 }
316670eb
A
1394
1395 /*
1396 * If the task or thread is being terminated, let's just get on with it
1397 */
1398 if ((l == NULL) || !task->active || task->halting || !thread->active)
1399 return;
1400
1401 /*
1402 * Examine all entries in deficit to see which might be eligble for
1403 * an automatic refill, which require callbacks to be issued, and
1404 * which require blocking.
1405 */
1406 block = 0;
1407 now = mach_absolute_time();
1408
39236c6e
A
1409 /*
1410 * Note that thread->t_threadledger may have been changed by the
1411 * thread_set_cpulimit() call above - so don't examine it until afterwards.
1412 */
1413 thl = thread->t_threadledger;
316670eb
A
1414 if (LEDGER_VALID(thl)) {
1415 block |= ledger_check_needblock(thl, now);
1c79356b 1416 }
316670eb 1417 block |= ledger_check_needblock(l, now);
1c79356b 1418
316670eb
A
1419 /*
1420 * If we are supposed to block on the availability of one or more
1421 * resources, find the first entry in deficit for which we should wait.
1422 * Schedule a refill if necessary and then sleep until the resource
1423 * becomes available.
1424 */
1425 if (block) {
1426 if (LEDGER_VALID(thl)) {
1427 ret = ledger_perform_blocking(thl);
1428 if (ret != KERN_SUCCESS)
1429 goto top;
1c79356b 1430 }
316670eb
A
1431 ret = ledger_perform_blocking(l);
1432 if (ret != KERN_SUCCESS)
1433 goto top;
1434 } /* block */
1435}
1c79356b 1436
316670eb
A
1437static uint32_t
1438ledger_check_needblock(ledger_t l, uint64_t now)
1439{
1440 int i;
1441 uint32_t flags, block = 0;
1442 struct ledger_entry *le;
1443 struct ledger_callback *lc;
1444
1445
1446 for (i = 0; i < l->l_size; i++) {
1447 le = &l->l_entries[i];
39236c6e
A
1448
1449 lc = entry_get_callback(l, i);
1450
1451 if (limit_exceeded(le) == FALSE) {
1452 if (le->le_flags & LEDGER_ACTION_CALLBACK) {
1453 /*
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.
1457 */
1458 assert(lc != NULL);
1459 /*
1460 * See comments for matching logic in ledger_check_new_balance().
1461 */
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);
1466 }
1467 } else {
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);
1471 }
1472 }
1473 }
1474
316670eb 1475 continue;
39236c6e 1476 }
316670eb 1477
39236c6e
A
1478 /* We're over the limit, so refill if we are eligible and past due. */
1479 if (le->le_flags & LF_REFILL_SCHEDULED) {
5ba3f43e
A
1480 assert(!(le->le_flags & LF_TRACKING_MAX));
1481
39236c6e 1482 if ((le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period) > now) {
316670eb
A
1483 ledger_refill(now, l, i);
1484 if (limit_exceeded(le) == FALSE)
1485 continue;
1486 }
1487 }
1488
1489 if (le->le_flags & LEDGER_ACTION_BLOCK)
1490 block = 1;
1491 if ((le->le_flags & LEDGER_ACTION_CALLBACK) == 0)
1492 continue;
39236c6e
A
1493
1494 /*
1495 * If the LEDGER_ACTION_CALLBACK flag is on, we expect there to
1496 * be a registered callback.
1497 */
316670eb 1498 assert(lc != NULL);
39236c6e 1499 flags = flag_set(&le->le_flags, LF_CALLED_BACK);
316670eb 1500 /* Callback has already been called */
39236c6e 1501 if (flags & LF_CALLED_BACK)
316670eb 1502 continue;
39236c6e 1503 lc->lc_func(FALSE, lc->lc_param0, lc->lc_param1);
1c79356b 1504 }
316670eb
A
1505 return(block);
1506}
1c79356b 1507
316670eb
A
1508
1509/* return KERN_SUCCESS to continue, KERN_FAILURE to restart */
1510static kern_return_t
1511ledger_perform_blocking(ledger_t l)
1512{
1513 int i;
1514 kern_return_t ret;
1515 struct ledger_entry *le;
1516
1517 for (i = 0; i < l->l_size; i++) {
1518 le = &l->l_entries[i];
1519 if ((!limit_exceeded(le)) ||
1520 ((le->le_flags & LEDGER_ACTION_BLOCK) == 0))
1521 continue;
1522
5ba3f43e
A
1523 assert(!(le->le_flags & LF_TRACKING_MAX));
1524
316670eb
A
1525 /* Prepare to sleep until the resource is refilled */
1526 ret = assert_wait_deadline(le, TRUE,
39236c6e 1527 le->_le.le_refill.le_last_refill + le->_le.le_refill.le_refill_period);
316670eb
A
1528 if (ret != THREAD_WAITING)
1529 return(KERN_SUCCESS);
1530
1531 /* Mark that somebody is waiting on this entry */
39236c6e 1532 flag_set(&le->le_flags, LF_WAKE_NEEDED);
316670eb
A
1533
1534 ret = thread_block_reason(THREAD_CONTINUE_NULL, NULL,
1535 AST_LEDGER);
1536 if (ret != THREAD_AWAKENED)
1537 return(KERN_SUCCESS);
1538
1539 /*
1540 * The world may have changed while we were asleep.
1541 * Some other resource we need may have gone into
1542 * deficit. Or maybe we're supposed to die now.
1543 * Go back to the top and reevaluate.
1544 */
1545 return(KERN_FAILURE);
1546 }
1c79356b 1547 return(KERN_SUCCESS);
1c79356b
A
1548}
1549
1c79356b 1550
316670eb
A
1551kern_return_t
1552ledger_get_entries(ledger_t ledger, int entry, ledger_amount_t *credit,
1553 ledger_amount_t *debit)
1554{
1555 struct ledger_entry *le;
1556
1557 if (!ENTRY_VALID(ledger, entry))
1558 return (KERN_INVALID_ARGUMENT);
1559
1560 le = &ledger->l_entries[entry];
1561
1562 *credit = le->le_credit;
1563 *debit = le->le_debit;
1564
1565 return (KERN_SUCCESS);
1566}
1567
fe8ab488
A
1568kern_return_t
1569ledger_reset_callback_state(ledger_t ledger, int entry)
1570{
1571 struct ledger_entry *le;
1572
1573 if (!ENTRY_VALID(ledger, entry))
1574 return (KERN_INVALID_ARGUMENT);
1575
1576 le = &ledger->l_entries[entry];
1577
1578 flag_clear(&le->le_flags, LF_CALLED_BACK);
1579
1580 return (KERN_SUCCESS);
1581}
1582
1583kern_return_t
1584ledger_disable_panic_on_negative(ledger_t ledger, int entry)
1585{
1586 struct ledger_entry *le;
1587
1588 if (!ENTRY_VALID(ledger, entry))
1589 return (KERN_INVALID_ARGUMENT);
1590
1591 le = &ledger->l_entries[entry];
1592
1593 flag_clear(&le->le_flags, LF_PANIC_ON_NEGATIVE);
1594
1595 return (KERN_SUCCESS);
1596}
1597
39236c6e
A
1598kern_return_t
1599ledger_get_balance(ledger_t ledger, int entry, ledger_amount_t *balance)
1600{
1601 struct ledger_entry *le;
1602
1603 if (!ENTRY_VALID(ledger, entry))
1604 return (KERN_INVALID_ARGUMENT);
1605
1606 le = &ledger->l_entries[entry];
1607
39037602
A
1608 if (le->le_flags & LF_TRACK_CREDIT_ONLY) {
1609 assert(le->le_debit == 0);
1610 } else {
1611 assert((le->le_credit >= 0) && (le->le_debit >= 0));
1612 }
39236c6e
A
1613
1614 *balance = le->le_credit - le->le_debit;
1615
1616 return (KERN_SUCCESS);
1617}
1618
316670eb
A
1619int
1620ledger_template_info(void **buf, int *len)
1c79356b 1621{
316670eb
A
1622 struct ledger_template_info *lti;
1623 struct entry_template *et;
1624 int i;
1625 ledger_t l;
1c79356b 1626
316670eb
A
1627 /*
1628 * Since all tasks share a ledger template, we'll just use the
1629 * caller's as the source.
1630 */
1631 l = current_task()->ledger;
1632 if ((*len < 0) || (l == NULL))
1633 return (EINVAL);
1634
1635 if (*len > l->l_size)
1636 *len = l->l_size;
1637 lti = kalloc((*len) * sizeof (struct ledger_template_info));
1638 if (lti == NULL)
1639 return (ENOMEM);
1640 *buf = lti;
1641
1642 template_lock(l->l_template);
1643 et = l->l_template->lt_entries;
1644
1645 for (i = 0; i < *len; i++) {
1646 memset(lti, 0, sizeof (*lti));
1647 strlcpy(lti->lti_name, et->et_key, LEDGER_NAME_MAX);
1648 strlcpy(lti->lti_group, et->et_group, LEDGER_NAME_MAX);
1649 strlcpy(lti->lti_units, et->et_units, LEDGER_NAME_MAX);
1650 et++;
1651 lti++;
1c79356b 1652 }
316670eb 1653 template_unlock(l->l_template);
1c79356b 1654
316670eb 1655 return (0);
1c79356b
A
1656}
1657
39236c6e
A
1658static void
1659ledger_fill_entry_info(struct ledger_entry *le,
1660 struct ledger_entry_info *lei,
1661 uint64_t now)
1662{
1663 assert(le != NULL);
1664 assert(lei != NULL);
1665
1666 memset(lei, 0, sizeof (*lei));
1667
1668 lei->lei_limit = le->le_limit;
1669 lei->lei_credit = le->le_credit;
1670 lei->lei_debit = le->le_debit;
1671 lei->lei_balance = lei->lei_credit - lei->lei_debit;
1672 lei->lei_refill_period = (le->le_flags & LF_REFILL_SCHEDULED) ?
1673 abstime_to_nsecs(le->_le.le_refill.le_refill_period) : 0;
1674 lei->lei_last_refill = abstime_to_nsecs(now - le->_le.le_refill.le_last_refill);
1675}
1676
316670eb 1677int
39236c6e 1678ledger_get_task_entry_info_multiple(task_t task, void **buf, int *len)
316670eb
A
1679{
1680 struct ledger_entry_info *lei;
1681 struct ledger_entry *le;
1682 uint64_t now = mach_absolute_time();
1683 int i;
1684 ledger_t l;
1685
1686 if ((*len < 0) || ((l = task->ledger) == NULL))
1687 return (EINVAL);
1c79356b 1688
316670eb
A
1689 if (*len > l->l_size)
1690 *len = l->l_size;
1691 lei = kalloc((*len) * sizeof (struct ledger_entry_info));
1692 if (lei == NULL)
1693 return (ENOMEM);
1694 *buf = lei;
1695
1696 le = l->l_entries;
1697
1698 for (i = 0; i < *len; i++) {
39236c6e 1699 ledger_fill_entry_info(le, lei, now);
316670eb
A
1700 le++;
1701 lei++;
1702 }
1703
1704 return (0);
1705}
1706
39236c6e
A
1707void
1708ledger_get_entry_info(ledger_t ledger,
1709 int entry,
1710 struct ledger_entry_info *lei)
1711{
1712 uint64_t now = mach_absolute_time();
1713
1714 assert(ledger != NULL);
1715 assert(lei != NULL);
39236c6e 1716
39037602
A
1717 if (entry >= 0 && entry < ledger->l_size) {
1718 struct ledger_entry *le = &ledger->l_entries[entry];
1719 ledger_fill_entry_info(le, lei, now);
1720 }
39236c6e
A
1721}
1722
316670eb
A
1723int
1724ledger_info(task_t task, struct ledger_info *info)
1c79356b 1725{
316670eb
A
1726 ledger_t l;
1727
1728 if ((l = task->ledger) == NULL)
1729 return (ENOENT);
1c79356b 1730
316670eb 1731 memset(info, 0, sizeof (*info));
1c79356b 1732
316670eb
A
1733 strlcpy(info->li_name, l->l_template->lt_name, LEDGER_NAME_MAX);
1734 info->li_id = l->l_id;
1735 info->li_entries = l->l_size;
1736 return (0);
1c79356b
A
1737}
1738
316670eb
A
1739#ifdef LEDGER_DEBUG
1740int
1741ledger_limit(task_t task, struct ledger_limit_args *args)
1c79356b 1742{
316670eb
A
1743 ledger_t l;
1744 int64_t limit;
1745 int idx;
1746
1747 if ((l = task->ledger) == NULL)
1748 return (EINVAL);
1749
1750 idx = ledger_key_lookup(l->l_template, args->lla_name);
1751 if ((idx < 0) || (idx >= l->l_size))
1752 return (EINVAL);
1753
1754 /*
1755 * XXX - this doesn't really seem like the right place to have
1756 * a context-sensitive conversion of userspace units into kernel
1757 * units. For now I'll handwave and say that the ledger() system
1758 * call isn't meant for civilians to use - they should be using
1759 * the process policy interfaces.
1760 */
1761 if (idx == task_ledgers.cpu_time) {
1762 int64_t nsecs;
1763
1764 if (args->lla_refill_period) {
1765 /*
1766 * If a refill is scheduled, then the limit is
1767 * specified as a percentage of one CPU. The
1768 * syscall specifies the refill period in terms of
1769 * milliseconds, so we need to convert to nsecs.
1770 */
1771 args->lla_refill_period *= 1000000;
1772 nsecs = args->lla_limit *
1773 (args->lla_refill_period / 100);
1774 lprintf(("CPU limited to %lld nsecs per second\n",
1775 nsecs));
1776 } else {
1777 /*
1778 * If no refill is scheduled, then this is a
1779 * fixed amount of CPU time (in nsecs) that can
1780 * be consumed.
1781 */
1782 nsecs = args->lla_limit;
1783 lprintf(("CPU limited to %lld nsecs\n", nsecs));
1784 }
1785 limit = nsecs_to_abstime(nsecs);
1786 } else {
1787 limit = args->lla_limit;
1788 lprintf(("%s limited to %lld\n", args->lla_name, limit));
1789 }
1790
1791 if (args->lla_refill_period > 0)
1792 ledger_set_period(l, idx, args->lla_refill_period);
b0d623f7 1793
316670eb
A
1794 ledger_set_limit(l, idx, limit);
1795 flag_set(&l->l_entries[idx].le_flags, LEDGER_ACTION_BLOCK);
1796 return (0);
1c79356b 1797}
316670eb 1798#endif