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