/* * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ /* * 8/13/93 * * This is a half-hearted attempt at providing the parts of the * ledger facility to satisfy the ledger interfaces. * * This implementation basically leaves the (dysfunctional) ledgers * unfunctional and are mearly here to satisfy the Mach spec interface * reqirements. */ #include #include #include #include #include #include #include #include #include #include #include #include #include ledger_t root_wired_ledger; ledger_t root_paged_ledger; /* Utility routine to handle entries to a ledger */ kern_return_t ledger_enter( ledger_t ledger, ledger_item_t amount) { /* Need to lock the ledger */ ledger_lock(ledger); if (amount > 0) { if (ledger->ledger_limit != LEDGER_ITEM_INFINITY && ledger->ledger_balance + amount > ledger->ledger_limit) { /* XXX this is where you do BAD things */ printf("Ledger limit exceeded ! ledger=%p lim=%d balance=%d\n", ledger, ledger->ledger_limit, ledger->ledger_balance); ledger_unlock(ledger); return(KERN_RESOURCE_SHORTAGE); } if ((ledger->ledger_balance + amount) < LEDGER_ITEM_INFINITY) ledger->ledger_balance += amount; else ledger->ledger_balance = LEDGER_ITEM_INFINITY; } else if (amount) { if (ledger->ledger_balance + amount > 0) ledger->ledger_balance += amount; else ledger->ledger_balance = 0; } ledger_unlock(ledger); return(KERN_SUCCESS); } /* Utility routine to create a new ledger */ static ledger_t ledger_allocate( ledger_item_t limit, ledger_t ledger_ledger, ledger_t ledger_parent) { ledger_t ledger; ledger = (ledger_t)kalloc(sizeof(ledger_data_t)); if (ledger == LEDGER_NULL) return(LEDGER_NULL); ledger->ledger_self = ipc_port_alloc_kernel(); if (ledger->ledger_self == IP_NULL) { kfree(ledger, sizeof(ledger_data_t)); return(LEDGER_NULL); } ledger_lock_init(ledger); ledger->ledger_limit = limit; ledger->ledger_balance = 0; ledger->ledger_service_port = MACH_PORT_NULL; ledger->ledger_ledger = ledger_ledger; ledger->ledger_parent = ledger_parent; ipc_kobject_set(ledger->ledger_self, (ipc_kobject_t)ledger, IKOT_LEDGER); return(ledger); } /* Utility routine to destroy a ledger */ static void ledger_deallocate( ledger_t ledger) { /* XXX can be many send rights (copies) of this */ ipc_port_dealloc_kernel(ledger->ledger_self); /* XXX release send right on service port */ kfree(ledger, sizeof(*ledger)); } /* * Inititalize the ledger facility */ void ledger_init(void) { /* * Allocate the root ledgers; wired and paged. */ root_wired_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, LEDGER_NULL, LEDGER_NULL); if (root_wired_ledger == LEDGER_NULL) panic("can't allocate root (wired) ledger"); ipc_port_make_send(root_wired_ledger->ledger_self); root_paged_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, LEDGER_NULL, LEDGER_NULL); if (root_paged_ledger == LEDGER_NULL) panic("can't allocate root (paged) ledger"); ipc_port_make_send(root_paged_ledger->ledger_self); } /* * Create a subordinate ledger */ kern_return_t ledger_create( ledger_t parent_ledger, ledger_t ledger_ledger, ledger_t *new_ledger, ledger_item_t transfer) { if (parent_ledger == LEDGER_NULL) return(KERN_INVALID_ARGUMENT); if (ledger_ledger == LEDGER_NULL) return(KERN_INVALID_LEDGER); /* * Allocate a new ledger and change the ledger_ledger for * its space. */ ledger_lock(ledger_ledger); if ((ledger_ledger->ledger_limit != LEDGER_ITEM_INFINITY) && (ledger_ledger->ledger_balance + sizeof(ledger_data_t) > ledger_ledger->ledger_limit)) { ledger_unlock(ledger_ledger); return(KERN_RESOURCE_SHORTAGE); } *new_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, ledger_ledger, parent_ledger); if (*new_ledger == LEDGER_NULL) { ledger_unlock(ledger_ledger); return(KERN_RESOURCE_SHORTAGE); } /* * Now transfer the limit for the new ledger from the parent */ ledger_lock(parent_ledger); if (parent_ledger->ledger_limit != LEDGER_ITEM_INFINITY) { /* Would the existing balance exceed the new limit ? */ if (parent_ledger->ledger_limit - transfer < parent_ledger->ledger_balance) { ledger_unlock(parent_ledger); ledger_unlock(ledger_ledger); return(KERN_RESOURCE_SHORTAGE); } if (parent_ledger->ledger_limit - transfer > 0) parent_ledger->ledger_limit -= transfer; else parent_ledger->ledger_limit = 0; } (*new_ledger)->ledger_limit = transfer; /* Charge the ledger against the ledger_ledger */ ledger_ledger->ledger_balance += sizeof(ledger_data_t); ledger_unlock(parent_ledger); ledger_unlock(ledger_ledger); return(KERN_SUCCESS); } /* * Destroy a ledger */ kern_return_t ledger_terminate( ledger_t ledger) { if (ledger == LEDGER_NULL) return(KERN_INVALID_ARGUMENT); /* You can't deallocate kernel ledgers */ if (ledger == root_wired_ledger || ledger == root_paged_ledger) return(KERN_INVALID_LEDGER); /* Lock the ledger */ ledger_lock(ledger); /* the parent ledger gets back the limit */ ledger_lock(ledger->ledger_parent); if (ledger->ledger_parent->ledger_limit != LEDGER_ITEM_INFINITY) { assert((natural_t)(ledger->ledger_parent->ledger_limit + ledger->ledger_limit) < LEDGER_ITEM_INFINITY); ledger->ledger_parent->ledger_limit += ledger->ledger_limit; } ledger_unlock(ledger->ledger_parent); /* * XXX The spec says that you have to destroy all objects that * have been created with this ledger. Nice work eh? For now * Transfer the balance to the parent and let it worry about * it. */ /* XXX the parent ledger inherits the debt ?? */ (void) ledger_enter(ledger->ledger_parent, ledger->ledger_balance); /* adjust the balance of the creation ledger */ (void) ledger_enter(ledger->ledger_ledger, -sizeof(*ledger)); /* delete the ledger */ ledger_deallocate(ledger); return(KERN_SUCCESS); } /* * Return the ledger limit and balance */ kern_return_t ledger_read( ledger_t ledger, ledger_item_t *balance, ledger_item_t *limit) { if (ledger == LEDGER_NULL) return(KERN_INVALID_ARGUMENT); ledger_lock(ledger); *balance = ledger->ledger_balance; *limit = ledger->ledger_limit; ledger_unlock(ledger); return(KERN_SUCCESS); } /* * Transfer resources from a parent ledger to a child */ kern_return_t ledger_transfer( ledger_t parent_ledger, ledger_t child_ledger, ledger_item_t transfer) { #define abs(v) ((v) > 0)?(v):-(v) ledger_t src, dest; ledger_item_t amount = abs(transfer); if (parent_ledger == LEDGER_NULL) return(KERN_INVALID_ARGUMENT); if (child_ledger == LEDGER_NULL) return(KERN_INVALID_ARGUMENT); /* Must be different ledgers */ if (parent_ledger == child_ledger) return(KERN_INVALID_ARGUMENT); if (transfer == 0) return(KERN_SUCCESS); ledger_lock(child_ledger); ledger_lock(parent_ledger); /* XXX Should be the parent you created it from ?? */ if (parent_ledger != child_ledger->ledger_parent) { ledger_unlock(parent_ledger); ledger_unlock(child_ledger); return(KERN_INVALID_LEDGER); } if (transfer > 0) { dest = child_ledger; src = parent_ledger; } else { src = child_ledger; dest = parent_ledger; } if (src->ledger_limit != LEDGER_ITEM_INFINITY) { /* Would the existing balance exceed the new limit ? */ if (src->ledger_limit - amount < src->ledger_balance) { ledger_unlock(parent_ledger); ledger_unlock(child_ledger); return(KERN_RESOURCE_SHORTAGE); } if (src->ledger_limit - amount > 0) src->ledger_limit -= amount; else src->ledger_limit = 0; } if (dest->ledger_limit != LEDGER_ITEM_INFINITY) { if ((natural_t)(dest->ledger_limit + amount) < LEDGER_ITEM_INFINITY) dest->ledger_limit += amount; else dest->ledger_limit = (LEDGER_ITEM_INFINITY - 1); } ledger_unlock(parent_ledger); ledger_unlock(child_ledger); return(KERN_SUCCESS); #undef abs } /* * Routine: convert_port_to_ledger * Purpose: * Convert from a port to a ledger. * Doesn't consume the port ref; the ledger produced may be null. * Conditions: * Nothing locked. */ ledger_t convert_port_to_ledger( ipc_port_t port) { ledger_t ledger = LEDGER_NULL; if (IP_VALID(port)) { ip_lock(port); if (ip_active(port) && (ip_kotype(port) == IKOT_LEDGER)) ledger = (ledger_t) port->ip_kobject; ip_unlock(port); } return ledger; } /* * Routine: convert_ledger_to_port * Purpose: * Convert from a ledger to a port. * Produces a naked send right which may be invalid. * Conditions: * Nothing locked. */ ipc_port_t convert_ledger_to_port( ledger_t ledger) { ipc_port_t port; port = ipc_port_make_send(ledger->ledger_self); return port; } /* * Copy a ledger */ ipc_port_t ledger_copy( ledger_t ledger) { /* XXX reference counting */ assert(ledger); return(ipc_port_copy_send(ledger->ledger_self)); }