]> git.saurik.com Git - apple/xnu.git/blame - osfmk/bank/bank.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / osfmk / bank / bank.c
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <bank/bank_internal.h>
30#include <bank/bank_types.h>
31#include <mach/mach_types.h>
32#include <mach/kern_return.h>
33#include <ipc/ipc_port.h>
34#include <mach/mach_vm.h>
35#include <mach/vm_map.h>
36#include <vm/vm_map.h>
37#include <mach/host_priv.h>
38#include <mach/host_special_ports.h>
39#include <kern/host.h>
40#include <kern/kalloc.h>
41#include <kern/ledger.h>
42#include <sys/kdebug.h>
43
44#include <mach/mach_voucher_attr_control.h>
45
46static zone_t bank_task_zone, bank_account_zone;
47#define MAX_BANK_TASK (CONFIG_TASK_MAX)
48#define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
49
50#define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
51#define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
52
53/* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
54#define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
55#define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
56
57ipc_voucher_attr_control_t bank_voucher_attr_control; /* communication channel from ATM to voucher system */
58
59#if DEVELOPMENT || DEBUG
60queue_head_t bank_tasks_list;
61queue_head_t bank_accounts_list;
62#endif
63
64static ledger_template_t bank_ledger_template = NULL;
65struct _bank_ledger_indices bank_ledgers = { -1 };
66
67static bank_task_t bank_task_alloc_init(void);
68static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant);
69static bank_task_t get_bank_task_context(task_t task);
70static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync);
71static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync);
72static void bank_rollup_chit_to_tasks(ledger_t bill, bank_task_t bank_holder, bank_task_t bank_merchant);
73static void init_bank_ledgers(void);
74
75kern_return_t
76bank_release_value(
77 ipc_voucher_attr_manager_t __assert_only manager,
78 mach_voucher_attr_key_t __assert_only key,
79 mach_voucher_attr_value_handle_t value,
80 mach_voucher_attr_value_reference_t sync);
81
82kern_return_t
83bank_get_value(
84 ipc_voucher_attr_manager_t __assert_only manager,
85 mach_voucher_attr_key_t __assert_only key,
86 mach_voucher_attr_recipe_command_t command,
87 mach_voucher_attr_value_handle_array_t prev_values,
88 mach_msg_type_number_t __assert_only prev_value_count,
89 mach_voucher_attr_content_t recipe,
90 mach_voucher_attr_content_size_t recipe_size,
91 mach_voucher_attr_value_handle_t *out_value,
92 ipc_voucher_t *out_value_voucher);
93
94kern_return_t
95bank_extract_content(
96 ipc_voucher_attr_manager_t __assert_only manager,
97 mach_voucher_attr_key_t __assert_only key,
98 mach_voucher_attr_value_handle_array_t values,
99 mach_msg_type_number_t value_count,
100 mach_voucher_attr_recipe_command_t *out_command,
101 mach_voucher_attr_content_t out_recipe,
102 mach_voucher_attr_content_size_t *in_out_recipe_size);
103
104kern_return_t
105bank_command(
106 ipc_voucher_attr_manager_t __assert_only manager,
107 mach_voucher_attr_key_t __assert_only key,
108 mach_voucher_attr_value_handle_array_t values,
109 mach_msg_type_number_t value_count,
110 mach_voucher_attr_command_t command,
111 mach_voucher_attr_content_t in_content,
112 mach_voucher_attr_content_size_t in_content_size,
113 mach_voucher_attr_content_t out_content,
114 mach_voucher_attr_content_size_t *in_out_content_size);
115
116void
117bank_release(ipc_voucher_attr_manager_t __assert_only manager);
118
119/*
120 * communication channel from voucher system to ATM
121 */
122struct ipc_voucher_attr_manager bank_manager = {
123 .ivam_release_value = bank_release_value,
124 .ivam_get_value = bank_get_value,
125 .ivam_extract_content = bank_extract_content,
126 .ivam_command = bank_command,
127 .ivam_release = bank_release,
128};
129
130
131#if DEVELOPMENT || DEBUG
132decl_lck_mtx_data(, bank_tasks_list_lock);
133decl_lck_mtx_data(, bank_accounts_list_lock);
134
135lck_grp_t bank_dev_lock_grp;
136lck_attr_t bank_dev_lock_attr;
137lck_grp_attr_t bank_dev_lock_grp_attr;
138#endif
139
140/*
141 * Lock group attributes for bank sub system.
142 */
143lck_grp_t bank_lock_grp;
144lck_attr_t bank_lock_attr;
145lck_grp_attr_t bank_lock_grp_attr;
146
147/*
148 * Routine: bank_init
149 * Purpose: Initialize the BANK subsystem.
150 * Returns: None.
151 */
152void
153bank_init()
154{
155 kern_return_t kr = KERN_SUCCESS;
156 /* setup zones for bank_task and bank_account objects */
157 bank_task_zone = zinit(sizeof(struct bank_task),
158 MAX_BANK_TASK * sizeof(struct bank_task),
159 sizeof(struct bank_task),
160 "bank_task");
161
162 bank_account_zone = zinit(sizeof(struct bank_account),
163 MAX_BANK_ACCOUNT * sizeof(struct bank_account),
164 sizeof(struct bank_account),
165 "bank_account");
166
167 init_bank_ledgers();
168
169 /* Initialize bank lock group and lock attributes. */
170 lck_grp_attr_setdefault(&bank_lock_grp_attr);
171 lck_grp_init(&bank_lock_grp, "bank_lock", &bank_lock_grp_attr);
172 lck_attr_setdefault(&bank_lock_attr);
173
174#if DEVELOPMENT || DEBUG
175 /* Initialize global bank development lock group and lock attributes. */
176 lck_grp_attr_setdefault(&bank_dev_lock_grp_attr);
177 lck_grp_init(&bank_dev_lock_grp, "bank_dev_lock", &bank_dev_lock_grp_attr);
178 lck_attr_setdefault(&bank_dev_lock_attr);
179
180 lck_mtx_init(&bank_tasks_list_lock, &bank_dev_lock_grp, &bank_dev_lock_attr);
181 lck_mtx_init(&bank_accounts_list_lock, &bank_dev_lock_grp, &bank_dev_lock_attr);
182
183 queue_init(&bank_tasks_list);
184 queue_init(&bank_accounts_list);
185#endif
186
187 /* Register the bank manager with the Vouchers sub system. */
188 kr = ipc_register_well_known_mach_voucher_attr_manager(
189 &bank_manager,
190 0,
191 MACH_VOUCHER_ATTR_KEY_BANK,
192 &bank_voucher_attr_control);
193 if (kr != KERN_SUCCESS )
194 panic("BANK subsystem initialization failed");
195
196 kprintf("BANK subsystem is initialized\n");
197 return ;
198}
199
200
201/*
202 * BANK Resource Manager Routines.
203 */
204
205
206/*
207 * Routine: bank_release_value
208 * Purpose: Release a value, if sync matches the sync count in value.
209 * Returns: KERN_SUCCESS: on Successful deletion.
210 * KERN_FAILURE: if sync value does not matches.
211 */
212kern_return_t
213bank_release_value(
214 ipc_voucher_attr_manager_t __assert_only manager,
215 mach_voucher_attr_key_t __assert_only key,
216 mach_voucher_attr_value_handle_t value,
217 mach_voucher_attr_value_reference_t sync)
218{
219 bank_task_t bank_task = BANK_TASK_NULL;
220 bank_element_t bank_element = BANK_ELEMENT_NULL;
221 bank_account_t bank_account = BANK_ACCOUNT_NULL;
222 kern_return_t kr = KERN_SUCCESS;
223
224 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
225 assert(manager == &bank_manager);
226
227
228 bank_element = HANDLE_TO_BANK_ELEMENT(value);
229 if (bank_element == BANK_DEFAULT_VALUE) {
230 /* Return success for default value */
231 return KERN_SUCCESS;
232 }
233
234
235 if (bank_element->be_type == BANK_TASK) {
236 bank_task = CAST_TO_BANK_TASK(bank_element);
237
238 if (bank_task->bt_made != (int)sync) {
239 return KERN_FAILURE;
240 }
241
242 bank_task_made_release_num(bank_task, sync);
243 bank_task_dealloc(bank_task, sync);
244 } else if (bank_element->be_type == BANK_ACCOUNT) {
245 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
246 kr = bank_account_dealloc_with_sync(bank_account, sync);
247 } else {
248 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
249 }
250
251 return kr;
252}
253
254
255/*
256 * Routine: bank_get_value
257 */
258kern_return_t
259bank_get_value(
260 ipc_voucher_attr_manager_t __assert_only manager,
261 mach_voucher_attr_key_t __assert_only key,
262 mach_voucher_attr_recipe_command_t command,
263 mach_voucher_attr_value_handle_array_t prev_values,
264 mach_msg_type_number_t prev_value_count,
265 mach_voucher_attr_content_t __unused recipe,
266 mach_voucher_attr_content_size_t __unused recipe_size,
267 mach_voucher_attr_value_handle_t *out_value,
268 ipc_voucher_t *out_value_voucher)
269{
270 bank_task_t bank_task = BANK_TASK_NULL;
271 bank_task_t bank_holder = BANK_TASK_NULL;
272 bank_task_t bank_merchant = BANK_TASK_NULL;
273 bank_element_t bank_element = BANK_ELEMENT_NULL;
274 bank_account_t bank_account = BANK_ACCOUNT_NULL;
275 bank_account_t old_bank_account = BANK_ACCOUNT_NULL;
276 mach_voucher_attr_value_handle_t bank_handle;
277 task_t task;
278 kern_return_t kr = KERN_SUCCESS;
279 mach_msg_type_number_t i;
280
281 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
282 assert(manager == &bank_manager);
283
284 /* never an out voucher */
285 *out_value_voucher = IPC_VOUCHER_NULL;
286
287 switch (command) {
288
289 case MACH_VOUCHER_ATTR_BANK_CREATE:
290
291 /* Get the bank context from the current task and take a reference on it. */
292 task = current_task();
293 bank_task = get_bank_task_context(task);
294 if (bank_task == BANK_TASK_NULL)
295 return KERN_RESOURCE_SHORTAGE;
296
297 bank_task_reference(bank_task);
298 bank_task_made_reference(bank_task);
299
300 *out_value = BANK_ELEMENT_TO_HANDLE(bank_task);
301 break;
302
303 case MACH_VOUCHER_ATTR_REDEEM:
304
305 for (i = 0; i < prev_value_count; i++) {
306 bank_handle = prev_values[i];
307 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
308
309 if (bank_element == BANK_DEFAULT_VALUE)
310 continue;
311
312 task = current_task();
313 if (bank_element->be_type == BANK_TASK) {
314 bank_holder = CAST_TO_BANK_TASK(bank_element);
315 } else if (bank_element->be_type == BANK_ACCOUNT) {
316 old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
317 bank_holder = old_bank_account->ba_holder;
318 } else {
319 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
320 }
321
322 bank_merchant = get_bank_task_context(task);
323 if (bank_merchant == BANK_TASK_NULL)
324 return KERN_RESOURCE_SHORTAGE;
325
326 /* Check if trying to redeem for self task, return the bank task */
327 if (bank_holder == bank_merchant) {
328 bank_task_reference(bank_holder);
329 bank_task_made_reference(bank_holder);
330 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
331 return kr;
332 }
333
334 bank_account = bank_account_alloc_init(bank_holder, bank_merchant);
335 if (bank_account == BANK_ACCOUNT_NULL)
336 return KERN_RESOURCE_SHORTAGE;
337
338 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
339 return kr;
340 }
341
342 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
343 break;
344 default:
345 kr = KERN_INVALID_ARGUMENT;
346 break;
347 }
348
349 return kr;
350}
351
352
353/*
354 * Routine: bank_extract_content
355 * Purpose: Extract a set of aid from an array of voucher values.
356 * Returns: KERN_SUCCESS: on Success.
357 * KERN_FAILURE: one of the value is not present in the hash.
358 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
359 */
360kern_return_t
361bank_extract_content(
362 ipc_voucher_attr_manager_t __assert_only manager,
363 mach_voucher_attr_key_t __assert_only key,
364 mach_voucher_attr_value_handle_array_t values,
365 mach_msg_type_number_t value_count,
366 mach_voucher_attr_recipe_command_t *out_command,
367 mach_voucher_attr_content_t out_recipe,
368 mach_voucher_attr_content_size_t *in_out_recipe_size)
369{
370 bank_task_t bank_task = BANK_TASK_NULL;
371 bank_element_t bank_element = BANK_ELEMENT_NULL;
372 bank_account_t bank_account = BANK_ACCOUNT_NULL;
373 mach_voucher_attr_value_handle_t bank_handle;
374 char buf[MACH_VOUCHER_BANK_CONTENT_SIZE];
375 mach_msg_type_number_t i;
376
377 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
378 assert(manager == &bank_manager);
379
380 for (i = 0; i < value_count; i++) {
381 bank_handle = values[i];
382 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
383 if (bank_element == BANK_DEFAULT_VALUE)
384 continue;
385
386 if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
387 *in_out_recipe_size = 0;
388 return KERN_NO_SPACE;
389 }
390
391 if (bank_element->be_type == BANK_TASK) {
392 bank_task = CAST_TO_BANK_TASK(bank_element);
393 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
394 " Bank Context for a pid %d\n", bank_task->bt_pid);
395 } else if (bank_element->be_type == BANK_ACCOUNT) {
396 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
397 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
398 " Bank Account linking holder pid %d with merchant pid %d\n",
399 bank_account->ba_holder->bt_pid,
400 bank_account->ba_merchant->bt_pid);
401 } else {
402 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
403 }
404
405
406 memcpy(&out_recipe[0], buf, strlen(buf) + 1);
407 *out_command = MACH_VOUCHER_ATTR_BANK_NULL;
408 *in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1;
409 return KERN_SUCCESS;
410 }
411
412 return KERN_SUCCESS;
413}
414
415/*
416 * Routine: bank_command
417 * Purpose: Execute a command against a set of ATM values.
418 * Returns: KERN_SUCCESS: On successful execution of command.
419 KERN_FAILURE: On failure.
420 */
421kern_return_t
422bank_command(
423 ipc_voucher_attr_manager_t __assert_only manager,
424 mach_voucher_attr_key_t __assert_only key,
425 mach_voucher_attr_value_handle_array_t __unused values,
426 mach_msg_type_number_t __unused value_count,
427 mach_voucher_attr_command_t __unused command,
428 mach_voucher_attr_content_t __unused in_content,
429 mach_voucher_attr_content_size_t __unused in_content_size,
430 mach_voucher_attr_content_t __unused out_content,
431 mach_voucher_attr_content_size_t __unused *out_content_size)
432{
433 bank_task_t bank_task = BANK_TASK_NULL;
434 bank_element_t bank_element = BANK_ELEMENT_NULL;
435 bank_account_t bank_account = BANK_ACCOUNT_NULL;
436 mach_voucher_attr_value_handle_t bank_handle;
437 mach_msg_type_number_t i;
438 int32_t pid;
439
440 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
441 assert(manager == &bank_manager);
442
443 switch (command) {
444 case BANK_ORIGINATOR_PID:
445
446 if ((sizeof(pid)) > *out_content_size) {
447 *out_content_size = 0;
448 return KERN_NO_SPACE;
449 }
450
451 for (i = 0; i < value_count; i++) {
452 bank_handle = values[i];
453 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
454 if (bank_element == BANK_DEFAULT_VALUE)
455 continue;
456
457 if (bank_element->be_type == BANK_TASK) {
458 bank_task = CAST_TO_BANK_TASK(bank_element);
459 } else if (bank_element->be_type == BANK_ACCOUNT) {
460 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
461 bank_task = bank_account->ba_holder;
462 } else {
463 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
464 }
465 pid = bank_task->bt_pid;
466
467 memcpy(&out_content[0], &pid, sizeof(pid));
468 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid);
469 return KERN_SUCCESS;
470 }
471 /* In the case of no value, return error KERN_INVALID_VALUE */
472 *out_content_size = 0;
473 return KERN_INVALID_VALUE;
474
475 break;
476 default:
477 return KERN_INVALID_ARGUMENT;
478 }
479 return KERN_SUCCESS;
480}
481
482
483void
484bank_release(
485 ipc_voucher_attr_manager_t __assert_only manager)
486{
487 assert(manager == &bank_manager);
488}
489
490
491
492/*
493 * Bank Internal Routines.
494 */
495
496/*
497 * Routine: bank_task_alloc_init
498 * Purpose: Allocate and initialize a bank task structure.
499 * Returns: bank_task_t on Success.
500 * BANK_TASK_NULL: on Failure.
501 * Notes: Leaves the task and creditcard blank and has only 1 ref,
502 needs to take 1 extra ref after the task field is initialized.
503 */
504static bank_task_t
505bank_task_alloc_init(void)
506{
507 bank_task_t new_bank_task;
508
509 new_bank_task = (bank_task_t) zalloc(bank_task_zone);
510 if (new_bank_task == BANK_TASK_NULL)
511 return BANK_TASK_NULL;
512
513 new_bank_task->bt_type = BANK_TASK;
514 new_bank_task->bt_refs = 1;
515 new_bank_task->bt_made = 0;
516 new_bank_task->bt_pid = 0;
517 new_bank_task->bt_creditcard = NULL;
518 queue_init(&new_bank_task->bt_accounts_to_pay);
519 queue_init(&new_bank_task->bt_accounts_to_charge);
520 lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
521 lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
522
523#if DEVELOPMENT || DEBUG
524 new_bank_task->bt_task = NULL;
525 lck_mtx_lock(&bank_tasks_list_lock);
526 queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt);
527 lck_mtx_unlock(&bank_tasks_list_lock);
528#endif
529 return (new_bank_task);
530}
531
532/*
533 * Routine: bank_account_alloc_init
534 * Purpose: Allocate and Initialize the bank account struct.
535 * Returns: bank_account_t : On Success.
536 * BANK_ACCOUNT_NULL: On Failure.
537 */
538static bank_account_t
539bank_account_alloc_init(
540 bank_task_t bank_holder,
541 bank_task_t bank_merchant)
542{
543 bank_account_t new_bank_account;
544 bank_account_t bank_account;
545 boolean_t entry_found = FALSE;
546 ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES);
547
548 if (new_ledger == NULL)
549 return BANK_ACCOUNT_NULL;
550
551 ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time);
552 new_bank_account = (bank_account_t) zalloc(bank_account_zone);
553 if (new_bank_account == BANK_ACCOUNT_NULL) {
554 ledger_dereference(new_ledger);
555 return BANK_ACCOUNT_NULL;
556 }
557
558 new_bank_account->ba_type = BANK_ACCOUNT;
559 new_bank_account->ba_refs = 1;
560 new_bank_account->ba_made = 1;
561 new_bank_account->ba_pid = 0;
562 new_bank_account->ba_bill = new_ledger;
563 new_bank_account->ba_merchant = bank_merchant;
564 new_bank_account->ba_holder = bank_holder;
565
566 /* Iterate through accounts need to pay list to find the existing entry */
567 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
568 queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
569 if (bank_account->ba_merchant != bank_merchant)
570 continue;
571
572 entry_found = TRUE;
573 /* Take a made ref, since this value would be returned to voucher system. */
574 bank_account_reference(bank_account);
575 bank_account_made_reference(bank_account);
576 break;
577 }
578
579 if (!entry_found) {
580
581 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
582 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
583
584 /* Add the account entry into Accounts need to pay account link list. */
585 queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay);
586
587 /* Add the account entry into Accounts need to charge account link list. */
588 queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge);
589
590 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
591 }
592
593 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
594
595 if (entry_found) {
596 ledger_dereference(new_ledger);
597 zfree(bank_account_zone, new_bank_account);
598 return bank_account;
599 }
600
601 bank_task_reference(bank_holder);
602 bank_task_reference(bank_merchant);
603
604#if DEVELOPMENT || DEBUG
605 new_bank_account->ba_task = NULL;
606 lck_mtx_lock(&bank_accounts_list_lock);
607 queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt);
608 lck_mtx_unlock(&bank_accounts_list_lock);
609#endif
610
611 return (new_bank_account);
612}
613
614/*
615 * Routine: get_bank_task_context
616 * Purpose: Get the bank context of the given task
617 * Returns: bank_task_t on Success.
618 * BANK_TASK_NULL: on Failure.
619 * Note: Initialize bank context if NULL.
620 */
621static bank_task_t
622get_bank_task_context(task_t task)
623{
624 bank_task_t bank_task;
625
626 if (task->bank_context)
627 return (task->bank_context);
628
629 bank_task = bank_task_alloc_init();
630
631 /* Grab the task lock and check if we won the race. */
632 task_lock(task);
633 if (task->bank_context) {
634 task_unlock(task);
635 if (bank_task != BANK_TASK_NULL)
636 bank_task_dealloc(bank_task, 1);
637 return (task->bank_context);
638 } else if (bank_task == BANK_TASK_NULL) {
639 task_unlock(task);
640 return BANK_TASK_NULL;
641 }
642 /* We won the race. Take a ref on the ledger and initialize bank task. */
643 bank_task->bt_creditcard = task->ledger;
644 bank_task->bt_pid = audit_token_pid_from_task(task);
645#if DEVELOPMENT || DEBUG
646 bank_task->bt_task = task;
647#endif
648 ledger_reference(task->ledger);
649
650 task->bank_context = bank_task;
651 task_unlock(task);
652
653 return (bank_task);
654}
655
656/*
657 * Routine: bank_task_dealloc
658 * Purpose: Drops the reference on bank task.
659 * Returns: None.
660 */
661static void
662bank_task_dealloc(
663 bank_task_t bank_task,
664 mach_voucher_attr_value_reference_t sync)
665{
666 assert(bank_task->bt_refs >= 0);
667
668 if (bank_task_release_num(bank_task, sync) > (int)sync)
669 return;
670
671 assert(bank_task->bt_refs == 0);
672 assert(queue_empty(&bank_task->bt_accounts_to_pay));
673 assert(queue_empty(&bank_task->bt_accounts_to_charge));
674
675 ledger_dereference(bank_task->bt_creditcard);
676 lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp);
677 lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp);
678
679#if DEVELOPMENT || DEBUG
680 lck_mtx_lock(&bank_tasks_list_lock);
681 queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt);
682 lck_mtx_unlock(&bank_tasks_list_lock);
683#endif
684
685 zfree(bank_task_zone, bank_task);
686}
687
688/*
689 * Routine: bank_account_dealloc_with_sync
690 * Purpose: Drop the reference on bank account if the sync matches.
691 * Returns: KERN_SUCCESS if sync matches.
692 * KERN_FAILURE on mismatch.
693 */
694static kern_return_t
695bank_account_dealloc_with_sync(
696 bank_account_t bank_account,
697 mach_voucher_attr_value_reference_t sync)
698{
699 bank_task_t bank_holder = bank_account->ba_holder;
700 bank_task_t bank_merchant = bank_account->ba_merchant;
701
702 /* Grab the acc to pay list lock and check the sync value */
703 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
704
705 if (bank_account->ba_made != (int)sync) {
706 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
707 return KERN_FAILURE;
708 }
709
710 bank_account_made_release_num(bank_account, sync);
711
712 if (bank_account_release_num(bank_account, sync) > (int)sync)
713 panic("Sync and ref value did not match for bank account %p\n", bank_account);
714
715
716 /* Grab both the acc to pay and acc to charge locks */
717 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
718
719 bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder, bank_merchant);
720
721 /* Remove the account entry from Accounts need to pay account link list. */
722 queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay);
723
724 /* Remove the account entry from Accounts need to charge account link list. */
725 queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge);
726
727 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
728 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
729
730 ledger_dereference(bank_account->ba_bill);
731
732 /* Drop the reference of bank holder and merchant */
733 bank_task_dealloc(bank_holder, 1);
734 bank_task_dealloc(bank_merchant, 1);
735
736#if DEVELOPMENT || DEBUG
737 lck_mtx_lock(&bank_accounts_list_lock);
738 queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt);
739 lck_mtx_unlock(&bank_accounts_list_lock);
740#endif
741
742 zfree(bank_account_zone, bank_account);
743 return KERN_SUCCESS;
744}
745
746/*
747 * Routine: bank_rollup_chit_to_tasks
748 * Purpose: Debit and Credit holder's and merchant's ledgers.
749 * Returns: None.
750 */
751static void
752bank_rollup_chit_to_tasks(
753 ledger_t bill,
754 bank_task_t bank_holder,
755 bank_task_t bank_merchant)
756{
757 ledger_amount_t credit;
758 ledger_amount_t debit;
759 kern_return_t ret;
760
761 ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
762 if (ret != KERN_SUCCESS) {
763 return;
764 }
765
766 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE,
767 bank_merchant->bt_pid, bank_holder->bt_pid, credit, debit, 0);
768#if CONFIG_BANK
769 ledger_credit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, credit);
770 ledger_debit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, debit);
771
772 ledger_credit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, credit);
773 ledger_debit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, debit);
774#endif
775}
776
777
778
779/*
780 * Routine: bank_task_destroy
781 * Purpose: Drops reference on bank task.
782 * Returns: None.
783 */
784void
785bank_task_destroy(bank_task_t bank_task)
786{
787 bank_task_dealloc(bank_task, 1);
788}
789
790/*
791 * Routine: init_bank_ledgers
792 * Purpose: Initialize template for bank ledgers.
793 * Returns: None.
794 */
795static void
796init_bank_ledgers(void) {
797 ledger_template_t t;
798 int idx;
799
800 assert(bank_ledger_template == NULL);
801
802 if ((t = ledger_template_create("Bank ledger")) == NULL)
803 panic("couldn't create bank ledger template");
804
805 if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
806 panic("couldn't create cpu_time entry for bank ledger template");
807 }
808
809 bank_ledgers.cpu_time = idx;
810 bank_ledger_template = t;
811}
812
813/*
814 * Routine: bank_billed_time
815 * Purpose: Walk throught the Accounts need to pay account list and get the current billing balance.
816 * Returns: balance.
817 */
818uint64_t
819bank_billed_time(bank_task_t bank_task)
820{
821 int64_t balance = 0;
822#ifdef CONFIG_BANK
823 bank_account_t bank_account;
824 int64_t temp = 0;
825#endif
826 if (bank_task == BANK_TASK_NULL) {
827 return balance;
828 }
829
830#ifdef CONFIG_BANK
831 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
832
833 ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_me, &temp);
834 balance +=temp;
835
836 queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
837 temp = 0;
838 ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
839 balance += temp;
840 }
841 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
842#endif
843 return (uint64_t)balance;
844}
845
846/*
847 * Routine: bank_serviced_time
848 * Purpose: Walk throught the Account need to charge account list and get the current balance to be charged.
849 * Returns: balance.
850 */
851uint64_t
852bank_serviced_time(bank_task_t bank_task)
853{
854 int64_t balance = 0;
855#ifdef CONFIG_BANK
856 bank_account_t bank_account;
857 int64_t temp = 0;
858#endif
859 if (bank_task == BANK_TASK_NULL) {
860 return balance;
861 }
862
863#ifdef CONFIG_BANK
864 lck_mtx_lock(&bank_task->bt_acc_to_charge_lock);
865
866 ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_others, &temp);
867 balance +=temp;
868
869 queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) {
870 temp = 0;
871 ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
872 balance += temp;
873 }
874 lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock);
875#endif
876 return (uint64_t)balance;
877}
878
879/*
880 * Routine: bank_get_voucher_ledger
881 * Purpose: Get the bankledger (chit) from the voucher.
882 * Returns: bank_ledger if bank_account attribute present in voucher.
883 * NULL on no attribute ot bank_task attribute.
884 */
885ledger_t
886bank_get_voucher_ledger(ipc_voucher_t voucher)
887{
888 bank_element_t bank_element = BANK_ELEMENT_NULL;
889 bank_account_t bank_account = BANK_ACCOUNT_NULL;
890 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
891 mach_voucher_attr_value_handle_array_size_t val_count;
892 ledger_t bankledger = NULL;
893 kern_return_t kr;
894
895 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
896 kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
897 voucher,
898 vals,
899 &val_count);
900
901 if (kr != KERN_SUCCESS)
902 return NULL;
903
904 if (val_count == 0)
905 return NULL;
906
907 bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
908 if (bank_element == BANK_DEFAULT_VALUE)
909 return NULL;
910
911 if (bank_element->be_type == BANK_TASK) {
912 bankledger = NULL;
913 } else if (bank_element->be_type == BANK_ACCOUNT) {
914 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
915 bankledger = bank_account->ba_bill;
916 } else {
917 panic("Bogus bank type: %d passed in bank_get_voucher_ledger\n", bank_element->be_type);
918 }
919
920 return (bankledger);
921}
922
923/*
924 * Routine: bank_swap_thread_bank_ledger
925 * Purpose: swap the bank ledger on the thread.
926 * Retunrs: None.
927 * Note: Should be only called for current thread or thread which is not started.
928 */
929void
930bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused)
931{
932#ifdef CONFIG_BANK
933 spl_t s;
934 processor_t processor;
935 ledger_t old_ledger = thread->t_bankledger;
936 int64_t ctime, effective_ledger_time_consumed = 0;
937 int64_t remainder = 0, consumed = 0;
938
939 if (old_ledger == NULL && new_ledger == NULL)
940 return;
941
942 assert((thread == current_thread() || thread->started == 0));
943
944 s = splsched();
945 thread_lock(thread);
946
947 /*
948 * Calculation of time elapsed by the thread before voucher swap.
949 * Following is the timeline which shows all the variables used in the calculation below.
950 *
951 * thread ledger
952 * cpu_time
953 * |<- consumed ->|<- remainder ->|
954 * timeline ----------------------------------------------------------------->
955 * | | |
956 * thread_dispatch ctime quantum end
957 *
958 * |<-effective_ledger_time -> |
959 * deduct_bank_ledger_time
960 */
961
962 ctime = mach_absolute_time();
963 processor = thread->last_processor;
964 if (processor != NULL) {
965 if ((int64_t)processor->quantum_end > ctime)
966 remainder = (int64_t)processor->quantum_end - ctime;
967
968 consumed = thread->quantum_remaining - remainder;
969 effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time;
970 }
971
972 thread->t_deduct_bank_ledger_time = consumed;
973
974 thread->t_bankledger = new_ledger;
975
976 thread_unlock(thread);
977 splx(s);
978
979 if (old_ledger != NULL)
980 ledger_credit(old_ledger,
981 bank_ledgers.cpu_time,
982 effective_ledger_time_consumed);
983#endif
984}
985