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