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