]>
Commit | Line | Data |
---|---|---|
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 | ||
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) | |
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 | ||
57 | ipc_voucher_attr_control_t bank_voucher_attr_control; /* communication channel from ATM to voucher system */ | |
58 | ||
59 | #if DEVELOPMENT || DEBUG | |
60 | queue_head_t bank_tasks_list; | |
61 | queue_head_t bank_accounts_list; | |
62 | #endif | |
63 | ||
64 | static ledger_template_t bank_ledger_template = NULL; | |
65 | struct _bank_ledger_indices bank_ledgers = { -1 }; | |
66 | ||
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); | |
74 | ||
75 | kern_return_t | |
76 | bank_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 | ||
82 | kern_return_t | |
83 | bank_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 | ||
94 | kern_return_t | |
95 | bank_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 | ||
104 | kern_return_t | |
105 | bank_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 | ||
116 | void | |
117 | bank_release(ipc_voucher_attr_manager_t __assert_only manager); | |
118 | ||
119 | /* | |
120 | * communication channel from voucher system to ATM | |
121 | */ | |
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, | |
128 | }; | |
129 | ||
130 | ||
131 | #if DEVELOPMENT || DEBUG | |
132 | decl_lck_mtx_data(, bank_tasks_list_lock); | |
133 | decl_lck_mtx_data(, bank_accounts_list_lock); | |
134 | ||
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; | |
138 | #endif | |
139 | ||
140 | /* | |
141 | * Lock group attributes for bank sub system. | |
142 | */ | |
143 | lck_grp_t bank_lock_grp; | |
144 | lck_attr_t bank_lock_attr; | |
145 | lck_grp_attr_t bank_lock_grp_attr; | |
146 | ||
147 | /* | |
148 | * Routine: bank_init | |
149 | * Purpose: Initialize the BANK subsystem. | |
150 | * Returns: None. | |
151 | */ | |
152 | void | |
153 | bank_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 | */ | |
212 | kern_return_t | |
213 | bank_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 | */ | |
258 | kern_return_t | |
259 | 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) | |
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 | */ | |
360 | kern_return_t | |
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) | |
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 | */ | |
421 | kern_return_t | |
422 | bank_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 | ||
483 | void | |
484 | bank_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 | */ | |
504 | static bank_task_t | |
505 | bank_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 | */ | |
538 | static bank_account_t | |
539 | bank_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 | */ | |
621 | static bank_task_t | |
622 | get_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 | */ | |
661 | static void | |
662 | bank_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 | */ | |
694 | static kern_return_t | |
695 | bank_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 | */ | |
751 | static void | |
752 | bank_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 | */ | |
784 | void | |
785 | bank_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 | */ | |
795 | static void | |
796 | init_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 | */ | |
818 | uint64_t | |
819 | bank_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 | */ | |
851 | uint64_t | |
852 | bank_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 | */ | |
885 | ledger_t | |
886 | bank_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 | */ | |
929 | void | |
930 | bank_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 |