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