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