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