]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/ledger.c
c97771d04a3a0288e2f9b8329651c5dbfce499b9
[apple/xnu.git] / osfmk / kern / ledger.c
1 /*
2 * Copyright (c) 2000-2006 Apple Computer, 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 * @OSF_COPYRIGHT@
30 */
31 /*
32 * 8/13/93
33 *
34 * This is a half-hearted attempt at providing the parts of the
35 * ledger facility to satisfy the ledger interfaces.
36 *
37 * This implementation basically leaves the (dysfunctional) ledgers
38 * unfunctional and are mearly here to satisfy the Mach spec interface
39 * reqirements.
40 */
41
42 #include <mach/mach_types.h>
43 #include <mach/message.h>
44 #include <mach/port.h>
45 #include <mach/ledger_server.h>
46
47 #include <kern/mach_param.h>
48 #include <kern/misc_protos.h>
49 #include <kern/lock.h>
50 #include <kern/ipc_kobject.h>
51 #include <kern/host.h>
52 #include <kern/ledger.h>
53 #include <kern/kalloc.h>
54
55 #include <ipc/ipc_space.h>
56 #include <ipc/ipc_port.h>
57
58 ledger_t root_wired_ledger;
59 ledger_t root_paged_ledger;
60
61
62 /* Utility routine to handle entries to a ledger */
63 kern_return_t
64 ledger_enter(
65 ledger_t ledger,
66 ledger_item_t amount)
67 {
68 if (ledger == LEDGER_NULL)
69 return KERN_SUCCESS;
70
71 /* Need to lock the ledger */
72 ledger_lock(ledger);
73
74 if (amount > 0) {
75 if (ledger->ledger_limit != LEDGER_ITEM_INFINITY &&
76 ledger->ledger_balance + amount > ledger->ledger_limit) {
77 /* XXX this is where you do BAD things */
78 printf("Ledger limit exceeded ! ledger=%p lim=%d balance=%d\n",
79 ledger, ledger->ledger_limit,
80 ledger->ledger_balance);
81 ledger_unlock(ledger);
82 return(KERN_RESOURCE_SHORTAGE);
83 }
84 if ((ledger->ledger_balance + amount)
85 < LEDGER_ITEM_INFINITY)
86 ledger->ledger_balance += amount;
87 else
88 ledger->ledger_balance = LEDGER_ITEM_INFINITY;
89 }
90 else if (amount) {
91 if (ledger->ledger_balance + amount > 0)
92 ledger->ledger_balance += amount;
93 else
94 ledger->ledger_balance = 0;
95 }
96 ledger_unlock(ledger);
97 return(KERN_SUCCESS);
98 }
99
100 /* Utility routine to create a new ledger */
101 static ledger_t
102 ledger_allocate(
103 ledger_item_t limit,
104 ledger_t ledger_ledger,
105 ledger_t ledger_parent)
106 {
107 ledger_t ledger;
108
109 ledger = (ledger_t)kalloc(sizeof(ledger_data_t));
110 if (ledger == LEDGER_NULL)
111 return(LEDGER_NULL);
112
113 ledger->ledger_self = ipc_port_alloc_kernel();
114 if (ledger->ledger_self == IP_NULL) {
115 kfree(ledger, sizeof(ledger_data_t));
116 return(LEDGER_NULL);
117 }
118
119 ledger_lock_init(ledger);
120 ledger->ledger_limit = limit;
121 ledger->ledger_balance = 0;
122 ledger->ledger_service_port = MACH_PORT_NULL;
123 ledger->ledger_ledger = ledger_ledger;
124 ledger->ledger_parent = ledger_parent;
125 ipc_kobject_set(ledger->ledger_self, (ipc_kobject_t)ledger,
126 IKOT_LEDGER);
127
128 return(ledger);
129 }
130
131 /* Utility routine to destroy a ledger */
132 static void
133 ledger_deallocate(
134 ledger_t ledger)
135 {
136 /* XXX can be many send rights (copies) of this */
137 ipc_port_dealloc_kernel(ledger->ledger_self);
138
139 /* XXX release send right on service port */
140 kfree(ledger, sizeof(*ledger));
141 }
142
143
144 /*
145 * Inititalize the ledger facility
146 */
147 void ledger_init(void)
148 {
149 /*
150 * Allocate the root ledgers; wired and paged.
151 */
152 root_wired_ledger = ledger_allocate(LEDGER_ITEM_INFINITY,
153 LEDGER_NULL, LEDGER_NULL);
154 if (root_wired_ledger == LEDGER_NULL)
155 panic("can't allocate root (wired) ledger");
156 ipc_port_make_send(root_wired_ledger->ledger_self);
157
158 root_paged_ledger = ledger_allocate(LEDGER_ITEM_INFINITY,
159 LEDGER_NULL, LEDGER_NULL);
160 if (root_paged_ledger == LEDGER_NULL)
161 panic("can't allocate root (paged) ledger");
162 ipc_port_make_send(root_paged_ledger->ledger_self);
163 }
164
165 /*
166 * Create a subordinate ledger
167 */
168 kern_return_t ledger_create(
169 ledger_t parent_ledger,
170 ledger_t ledger_ledger,
171 ledger_t *new_ledger,
172 ledger_item_t transfer)
173 {
174 if (parent_ledger == LEDGER_NULL)
175 return(KERN_INVALID_ARGUMENT);
176
177 if (ledger_ledger == LEDGER_NULL)
178 return(KERN_INVALID_LEDGER);
179
180 /*
181 * Allocate a new ledger and change the ledger_ledger for
182 * its space.
183 */
184 ledger_lock(ledger_ledger);
185 if ((ledger_ledger->ledger_limit != LEDGER_ITEM_INFINITY) &&
186 (ledger_ledger->ledger_balance + sizeof(ledger_data_t) >
187 ledger_ledger->ledger_limit)) {
188 ledger_unlock(ledger_ledger);
189 return(KERN_RESOURCE_SHORTAGE);
190 }
191
192 *new_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, ledger_ledger, parent_ledger);
193 if (*new_ledger == LEDGER_NULL) {
194 ledger_unlock(ledger_ledger);
195 return(KERN_RESOURCE_SHORTAGE);
196 }
197
198 /*
199 * Now transfer the limit for the new ledger from the parent
200 */
201 ledger_lock(parent_ledger);
202 if (parent_ledger->ledger_limit != LEDGER_ITEM_INFINITY) {
203 /* Would the existing balance exceed the new limit ? */
204 if (parent_ledger->ledger_limit - transfer < parent_ledger->ledger_balance) {
205 ledger_unlock(parent_ledger);
206 ledger_unlock(ledger_ledger);
207 return(KERN_RESOURCE_SHORTAGE);
208 }
209 if (parent_ledger->ledger_limit - transfer > 0)
210 parent_ledger->ledger_limit -= transfer;
211 else
212 parent_ledger->ledger_limit = 0;
213 }
214 (*new_ledger)->ledger_limit = transfer;
215
216 /* Charge the ledger against the ledger_ledger */
217 ledger_ledger->ledger_balance += (ledger_item_t)sizeof(ledger_data_t);
218 ledger_unlock(parent_ledger);
219
220 ledger_unlock(ledger_ledger);
221
222 return(KERN_SUCCESS);
223 }
224
225 /*
226 * Destroy a ledger
227 */
228 kern_return_t ledger_terminate(
229 ledger_t ledger)
230 {
231 if (ledger == LEDGER_NULL)
232 return(KERN_INVALID_ARGUMENT);
233
234 /* You can't deallocate kernel ledgers */
235 if (ledger == root_wired_ledger ||
236 ledger == root_paged_ledger)
237 return(KERN_INVALID_LEDGER);
238
239 /* Lock the ledger */
240 ledger_lock(ledger);
241
242 /* the parent ledger gets back the limit */
243 ledger_lock(ledger->ledger_parent);
244 if (ledger->ledger_parent->ledger_limit != LEDGER_ITEM_INFINITY) {
245 assert((natural_t)(ledger->ledger_parent->ledger_limit +
246 ledger->ledger_limit) <
247 LEDGER_ITEM_INFINITY);
248 ledger->ledger_parent->ledger_limit += ledger->ledger_limit;
249 }
250 ledger_unlock(ledger->ledger_parent);
251
252 /*
253 * XXX The spec says that you have to destroy all objects that
254 * have been created with this ledger. Nice work eh? For now
255 * Transfer the balance to the parent and let it worry about
256 * it.
257 */
258 /* XXX the parent ledger inherits the debt ?? */
259 (void) ledger_enter(ledger->ledger_parent, ledger->ledger_balance);
260
261 /* adjust the balance of the creation ledger */
262 (void) ledger_enter(ledger->ledger_ledger, (ledger_item_t)-sizeof(*ledger));
263
264 /* delete the ledger */
265 ledger_deallocate(ledger);
266
267 return(KERN_SUCCESS);
268 }
269
270 /*
271 * Return the ledger limit and balance
272 */
273 kern_return_t ledger_read(
274 ledger_t ledger,
275 ledger_item_t *balance,
276 ledger_item_t *limit)
277 {
278 if (ledger == LEDGER_NULL)
279 return(KERN_INVALID_ARGUMENT);
280
281 ledger_lock(ledger);
282 *balance = ledger->ledger_balance;
283 *limit = ledger->ledger_limit;
284 ledger_unlock(ledger);
285
286 return(KERN_SUCCESS);
287 }
288
289 /*
290 * Transfer resources from a parent ledger to a child
291 */
292 kern_return_t ledger_transfer(
293 ledger_t parent_ledger,
294 ledger_t child_ledger,
295 ledger_item_t transfer)
296 {
297 #define abs(v) ((v) > 0)?(v):-(v)
298
299 ledger_t src, dest;
300 ledger_item_t amount = abs(transfer);
301
302 if (parent_ledger == LEDGER_NULL)
303 return(KERN_INVALID_ARGUMENT);
304
305 if (child_ledger == LEDGER_NULL)
306 return(KERN_INVALID_ARGUMENT);
307
308 /* Must be different ledgers */
309 if (parent_ledger == child_ledger)
310 return(KERN_INVALID_ARGUMENT);
311
312 if (transfer == 0)
313 return(KERN_SUCCESS);
314
315 ledger_lock(child_ledger);
316 ledger_lock(parent_ledger);
317
318 /* XXX Should be the parent you created it from ?? */
319 if (parent_ledger != child_ledger->ledger_parent) {
320 ledger_unlock(parent_ledger);
321 ledger_unlock(child_ledger);
322 return(KERN_INVALID_LEDGER);
323 }
324
325 if (transfer > 0) {
326 dest = child_ledger;
327 src = parent_ledger;
328 }
329 else {
330 src = child_ledger;
331 dest = parent_ledger;
332 }
333
334 if (src->ledger_limit != LEDGER_ITEM_INFINITY) {
335 /* Would the existing balance exceed the new limit ? */
336 if (src->ledger_limit - amount < src->ledger_balance) {
337 ledger_unlock(parent_ledger);
338 ledger_unlock(child_ledger);
339 return(KERN_RESOURCE_SHORTAGE);
340 }
341 if (src->ledger_limit - amount > 0)
342 src->ledger_limit -= amount;
343 else
344 src->ledger_limit = 0;
345 }
346
347 if (dest->ledger_limit != LEDGER_ITEM_INFINITY) {
348 if ((natural_t)(dest->ledger_limit + amount)
349 < LEDGER_ITEM_INFINITY)
350 dest->ledger_limit += amount;
351 else
352 dest->ledger_limit = (LEDGER_ITEM_INFINITY - 1);
353 }
354
355 ledger_unlock(parent_ledger);
356 ledger_unlock(child_ledger);
357
358 return(KERN_SUCCESS);
359 #undef abs
360 }
361
362 /*
363 * Routine: convert_port_to_ledger
364 * Purpose:
365 * Convert from a port to a ledger.
366 * Doesn't consume the port ref; the ledger produced may be null.
367 * Conditions:
368 * Nothing locked.
369 */
370
371 ledger_t
372 convert_port_to_ledger(
373 ipc_port_t port)
374 {
375 ledger_t ledger = LEDGER_NULL;
376
377 if (IP_VALID(port)) {
378 ip_lock(port);
379 if (ip_active(port) &&
380 (ip_kotype(port) == IKOT_LEDGER))
381 ledger = (ledger_t) port->ip_kobject;
382 ip_unlock(port);
383 }
384
385 return ledger;
386 }
387
388 /*
389 * Routine: convert_ledger_to_port
390 * Purpose:
391 * Convert from a ledger to a port.
392 * Produces a naked send right which may be invalid.
393 * Conditions:
394 * Nothing locked.
395 */
396
397 ipc_port_t
398 convert_ledger_to_port(
399 ledger_t ledger)
400 {
401 ipc_port_t port;
402
403 if (ledger == LEDGER_NULL)
404 return IP_NULL;
405
406 port = ipc_port_make_send(ledger->ledger_self);
407 return port;
408 }
409
410 /*
411 * Copy a ledger
412 */
413 ipc_port_t
414 ledger_copy(
415 ledger_t ledger)
416 {
417 if (ledger == LEDGER_NULL)
418 return IP_NULL;
419
420 return(ipc_port_copy_send(ledger->ledger_self));
421 }