2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
44 #include <mach/mach_voucher_attr_control.h>
46 static 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)
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)))
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)))
57 ipc_voucher_attr_control_t bank_voucher_attr_control
; /* communication channel from ATM to voucher system */
59 #if DEVELOPMENT || DEBUG
60 queue_head_t bank_tasks_list
;
61 queue_head_t bank_accounts_list
;
64 static ledger_template_t bank_ledger_template
= NULL
;
65 struct _bank_ledger_indices bank_ledgers
= { -1 };
67 static bank_task_t
bank_task_alloc_init(void);
68 static bank_account_t
bank_account_alloc_init(bank_task_t bank_holder
, bank_task_t bank_merchant
);
69 static bank_task_t
get_bank_task_context(task_t task
);
70 static void bank_task_dealloc(bank_task_t bank_task
, mach_voucher_attr_value_reference_t sync
);
71 static kern_return_t
bank_account_dealloc_with_sync(bank_account_t bank_account
, mach_voucher_attr_value_reference_t sync
);
72 static void bank_rollup_chit_to_tasks(ledger_t bill
, bank_task_t bank_holder
, bank_task_t bank_merchant
);
73 static void init_bank_ledgers(void);
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
);
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
);
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
);
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
);
117 bank_release(ipc_voucher_attr_manager_t __assert_only manager
);
120 * communication channel from voucher system to ATM
122 struct 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
,
131 #if DEVELOPMENT || DEBUG
132 decl_lck_mtx_data(, bank_tasks_list_lock
);
133 decl_lck_mtx_data(, bank_accounts_list_lock
);
135 lck_grp_t bank_dev_lock_grp
;
136 lck_attr_t bank_dev_lock_attr
;
137 lck_grp_attr_t bank_dev_lock_grp_attr
;
141 * Lock group attributes for bank sub system.
143 lck_grp_t bank_lock_grp
;
144 lck_attr_t bank_lock_attr
;
145 lck_grp_attr_t bank_lock_grp_attr
;
149 * Purpose: Initialize the BANK subsystem.
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
),
162 bank_account_zone
= zinit(sizeof(struct bank_account
),
163 MAX_BANK_ACCOUNT
* sizeof(struct bank_account
),
164 sizeof(struct bank_account
),
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
);
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
);
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
);
183 queue_init(&bank_tasks_list
);
184 queue_init(&bank_accounts_list
);
187 /* Register the bank manager with the Vouchers sub system. */
188 kr
= ipc_register_well_known_mach_voucher_attr_manager(
191 MACH_VOUCHER_ATTR_KEY_BANK
,
192 &bank_voucher_attr_control
);
193 if (kr
!= KERN_SUCCESS
)
194 panic("BANK subsystem initialization failed");
196 kprintf("BANK subsystem is initialized\n");
202 * BANK Resource Manager Routines.
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.
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
)
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
;
224 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
225 assert(manager
== &bank_manager
);
228 bank_element
= HANDLE_TO_BANK_ELEMENT(value
);
229 if (bank_element
== BANK_DEFAULT_VALUE
) {
230 /* Return success for default value */
235 if (bank_element
->be_type
== BANK_TASK
) {
236 bank_task
= CAST_TO_BANK_TASK(bank_element
);
238 if (bank_task
->bt_made
!= (int)sync
) {
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
);
248 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
256 * Routine: bank_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
)
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
;
278 kern_return_t kr
= KERN_SUCCESS
;
279 mach_msg_type_number_t i
;
281 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
282 assert(manager
== &bank_manager
);
284 /* never an out voucher */
285 *out_value_voucher
= IPC_VOUCHER_NULL
;
289 case MACH_VOUCHER_ATTR_BANK_CREATE
:
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
;
297 bank_task_reference(bank_task
);
298 bank_task_made_reference(bank_task
);
300 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_task
);
303 case MACH_VOUCHER_ATTR_REDEEM
:
305 for (i
= 0; i
< prev_value_count
; i
++) {
306 bank_handle
= prev_values
[i
];
307 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
309 if (bank_element
== BANK_DEFAULT_VALUE
)
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
;
319 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
322 bank_merchant
= get_bank_task_context(task
);
323 if (bank_merchant
== BANK_TASK_NULL
)
324 return KERN_RESOURCE_SHORTAGE
;
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
);
334 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
);
335 if (bank_account
== BANK_ACCOUNT_NULL
)
336 return KERN_RESOURCE_SHORTAGE
;
338 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
342 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
345 kr
= KERN_INVALID_ARGUMENT
;
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.
361 bank_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
)
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
;
377 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
378 assert(manager
== &bank_manager
);
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
)
386 if (MACH_VOUCHER_BANK_CONTENT_SIZE
> *in_out_recipe_size
) {
387 *in_out_recipe_size
= 0;
388 return KERN_NO_SPACE
;
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
);
402 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
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;
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.
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
)
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
;
440 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
441 assert(manager
== &bank_manager
);
444 case BANK_ORIGINATOR_PID
:
446 if ((sizeof(pid
)) > *out_content_size
) {
447 *out_content_size
= 0;
448 return KERN_NO_SPACE
;
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
)
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
;
463 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
465 pid
= bank_task
->bt_pid
;
467 memcpy(&out_content
[0], &pid
, sizeof(pid
));
468 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(pid
);
471 /* In the case of no value, return error KERN_INVALID_VALUE */
472 *out_content_size
= 0;
473 return KERN_INVALID_VALUE
;
477 return KERN_INVALID_ARGUMENT
;
485 ipc_voucher_attr_manager_t __assert_only manager
)
487 assert(manager
== &bank_manager
);
493 * Bank Internal Routines.
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.
505 bank_task_alloc_init(void)
507 bank_task_t new_bank_task
;
509 new_bank_task
= (bank_task_t
) zalloc(bank_task_zone
);
510 if (new_bank_task
== BANK_TASK_NULL
)
511 return BANK_TASK_NULL
;
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
);
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
);
529 return (new_bank_task
);
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.
538 static bank_account_t
539 bank_account_alloc_init(
540 bank_task_t bank_holder
,
541 bank_task_t bank_merchant
)
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
);
548 if (new_ledger
== NULL
)
549 return BANK_ACCOUNT_NULL
;
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
;
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
;
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
)
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
);
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
);
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
);
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
);
590 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
593 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
596 ledger_dereference(new_ledger
);
597 zfree(bank_account_zone
, new_bank_account
);
601 bank_task_reference(bank_holder
);
602 bank_task_reference(bank_merchant
);
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
);
611 return (new_bank_account
);
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.
622 get_bank_task_context(task_t task
)
624 bank_task_t bank_task
;
626 if (task
->bank_context
)
627 return (task
->bank_context
);
629 bank_task
= bank_task_alloc_init();
631 /* Grab the task lock and check if we won the race. */
633 if (task
->bank_context
) {
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
) {
640 return BANK_TASK_NULL
;
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
;
648 ledger_reference(task
->ledger
);
650 task
->bank_context
= bank_task
;
657 * Routine: bank_task_dealloc
658 * Purpose: Drops the reference on bank task.
663 bank_task_t bank_task
,
664 mach_voucher_attr_value_reference_t sync
)
666 assert(bank_task
->bt_refs
>= 0);
668 if (bank_task_release_num(bank_task
, sync
) > (int)sync
)
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
));
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
);
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
);
685 zfree(bank_task_zone
, bank_task
);
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.
695 bank_account_dealloc_with_sync(
696 bank_account_t bank_account
,
697 mach_voucher_attr_value_reference_t sync
)
699 bank_task_t bank_holder
= bank_account
->ba_holder
;
700 bank_task_t bank_merchant
= bank_account
->ba_merchant
;
702 /* Grab the acc to pay list lock and check the sync value */
703 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
705 if (bank_account
->ba_made
!= (int)sync
) {
706 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
710 bank_account_made_release_num(bank_account
, sync
);
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
);
716 /* Grab both the acc to pay and acc to charge locks */
717 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
719 bank_rollup_chit_to_tasks(bank_account
->ba_bill
, bank_holder
, bank_merchant
);
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
);
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
);
727 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
728 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
730 ledger_dereference(bank_account
->ba_bill
);
732 /* Drop the reference of bank holder and merchant */
733 bank_task_dealloc(bank_holder
, 1);
734 bank_task_dealloc(bank_merchant
, 1);
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
);
742 zfree(bank_account_zone
, bank_account
);
747 * Routine: bank_rollup_chit_to_tasks
748 * Purpose: Debit and Credit holder's and merchant's ledgers.
752 bank_rollup_chit_to_tasks(
754 bank_task_t bank_holder
,
755 bank_task_t bank_merchant
)
757 ledger_amount_t credit
;
758 ledger_amount_t debit
;
761 ret
= ledger_get_entries(bill
, bank_ledgers
.cpu_time
, &credit
, &debit
);
762 if (ret
!= KERN_SUCCESS
) {
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);
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
);
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
);
780 * Routine: bank_task_destroy
781 * Purpose: Drops reference on bank task.
785 bank_task_destroy(bank_task_t bank_task
)
787 bank_task_dealloc(bank_task
, 1);
791 * Routine: init_bank_ledgers
792 * Purpose: Initialize template for bank ledgers.
796 init_bank_ledgers(void) {
800 assert(bank_ledger_template
== NULL
);
802 if ((t
= ledger_template_create("Bank ledger")) == NULL
)
803 panic("couldn't create bank ledger template");
805 if ((idx
= ledger_entry_add(t
, "cpu_time", "sched", "ns")) < 0) {
806 panic("couldn't create cpu_time entry for bank ledger template");
809 bank_ledgers
.cpu_time
= idx
;
810 bank_ledger_template
= t
;
814 * Routine: bank_billed_time
815 * Purpose: Walk throught the Accounts need to pay account list and get the current billing balance.
819 bank_billed_time(bank_task_t bank_task
)
823 bank_account_t bank_account
;
826 if (bank_task
== BANK_TASK_NULL
) {
831 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
833 ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, &temp
);
836 queue_iterate(&bank_task
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
838 ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
841 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
843 return (uint64_t)balance
;
847 * Routine: bank_serviced_time
848 * Purpose: Walk throught the Account need to charge account list and get the current balance to be charged.
852 bank_serviced_time(bank_task_t bank_task
)
856 bank_account_t bank_account
;
859 if (bank_task
== BANK_TASK_NULL
) {
864 lck_mtx_lock(&bank_task
->bt_acc_to_charge_lock
);
866 ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, &temp
);
869 queue_iterate(&bank_task
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
) {
871 ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
874 lck_mtx_unlock(&bank_task
->bt_acc_to_charge_lock
);
876 return (uint64_t)balance
;
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.
886 bank_get_voucher_ledger(ipc_voucher_t voucher
)
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
;
895 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
896 kr
= mach_voucher_attr_control_get_values(bank_voucher_attr_control
,
901 if (kr
!= KERN_SUCCESS
)
907 bank_element
= HANDLE_TO_BANK_ELEMENT(vals
[0]);
908 if (bank_element
== BANK_DEFAULT_VALUE
)
911 if (bank_element
->be_type
== BANK_TASK
) {
913 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
914 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
915 bankledger
= bank_account
->ba_bill
;
917 panic("Bogus bank type: %d passed in bank_get_voucher_ledger\n", bank_element
->be_type
);
924 * Routine: bank_swap_thread_bank_ledger
925 * Purpose: swap the bank ledger on the thread.
927 * Note: Should be only called for current thread or thread which is not started.
930 bank_swap_thread_bank_ledger(thread_t thread __unused
, ledger_t new_ledger __unused
)
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;
939 if (old_ledger
== NULL
&& new_ledger
== NULL
)
942 assert((thread
== current_thread() || thread
->started
== 0));
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.
953 * |<- consumed ->|<- remainder ->|
954 * timeline ----------------------------------------------------------------->
956 * thread_dispatch ctime quantum end
958 * |<-effective_ledger_time -> |
959 * deduct_bank_ledger_time
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
;
968 consumed
= thread
->quantum_remaining
- remainder
;
969 effective_ledger_time_consumed
= consumed
- thread
->t_deduct_bank_ledger_time
;
972 thread
->t_deduct_bank_ledger_time
= consumed
;
974 thread
->t_bankledger
= new_ledger
;
976 thread_unlock(thread
);
979 if (old_ledger
!= NULL
)
980 ledger_credit(old_ledger
,
981 bank_ledgers
.cpu_time
,
982 effective_ledger_time_consumed
);