/* Copyright 1999, 2000, 2001 Johan Rydberg, jrydberg@opencores.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "tm.h" #include "trace.h" #include "ipc-object.h" #include "ipc-port.h" #include "ipc-entry.h" #include "ipc-reverse.h" #include "vm-slab.h" struct kmem_cache *ipc_object_cache; struct kmem_cache *ipc_entry_cache; void ipc_entry_free (struct ipc_entry *entry) { kmem_cache_free (ipc_entry_cache, entry); } /* ??? */ static int convert_name_to_right (name) int name; { switch (name) { case RTMK_MSG_TYPE_COPY_SEND: case RTMK_MSG_TYPE_MAKE_SEND: case RTMK_MSG_TYPE_MOVE_SEND: return RTMK_PORT_TYPE_SEND; case RTMK_MSG_TYPE_MOVE_RECEIVE: return RTMK_PORT_TYPE_RECEIVE; case RTMK_MSG_TYPE_MAKE_SEND_ONCE: case RTMK_MSG_TYPE_MOVE_SEND_ONCE: return RTMK_PORT_TYPE_SEND_ONCE; default: printf ("EY! name = %d\n", name); return 0; } } /* Function for initializing the whole IPC system. */ void ipc_init (void) { ipc_mqueue_init (); #if 0 ipc_kmsg_init (); #endif ipc_reverse_init (); ipc_object_init (); #if 0 ipc_port_init (); #endif } /* Initialize IPC object system. */ void ipc_object_init (void) { ipc_object_cache = kmem_cache_create ("ipc object cache", sizeof (struct ipc_object), 0); ipc_entry_cache = kmem_cache_create ("ipc entry cache", sizeof (struct ipc_entry), 0); assert (ipc_object_cache && ipc_entry_cache); } /* Allocate a new IPC object structure and initialize it. */ struct ipc_object * ipc_object_create (void) { struct ipc_object *object; object = (struct ipc_object *) kmem_cache_alloc (ipc_object_cache); if (! object) return (struct ipc_object *) 0; /* One for the caller, 0 for stayin' alive. */ object->refcount = 1; thread_lock_init (&object->lock, true); /* Initialize the "fast" lookup table. */ ipc_table_object_init (&object->tbl); /* Initialize the splay-tree, for entries that does not fith in the "fast" lookup table. */ ipc_splay_tree_init (&object->splay_tree, 0, 0); return object; } /* Convenience function for removing ENTRY from OBJECT. */ static inline bool remove_entry (struct ipc_object *object, struct ipc_entry *entry) { if (entry->ie_key < MTABLERIGHTS) ipc_table_remove (&object->tbl, entry->ie_key); else ipc_splay_tree_delete_node (&object->splay_tree, entry->ie_key, &entry); return true; } /* Convenience function for reserving space in OBJECT. */ static bool reserve_entry (struct ipc_object *object, struct ipc_entry **entryp, rtmk_port_t *namep) { rtmk_port_t low, high, key; struct ipc_entry *entry; bool result; /* First of all we try to reserve in the table. If we don't succeed with that we go on and try with the splay-tree. */ result = ipc_table_reserve (&object->tbl, entryp, namep); if (result == true) return true; /* Optimize for the case when the splay tree is empty. */ if (object->splay_tree.root == 0) { entry = (struct ipc_entry *) kmem_cache_alloc (ipc_entry_cache); entry->ie_key = MTABLERIGHTS; *entryp = entry; return true; } /* Get the first entry. */ ipc_splay_tree_first (&object->splay_tree, &entry); key = entry->ie_key; result = true; while (result == true) { rtmk_port_t diff; result = ipc_splay_tree_bounds (&object->splay_tree, key, &low, &high); /* If RESULT is false, then we know this was the last entry in tree. So what we do is: NAME = ++LAST . */ if (result == true) /* ??? right? */ { key = high + 1; break; } diff = key - low; if (diff >= 2) { key = low + 1; goto allocate_and_return; } diff = high - key; if (diff >= 2) { key = key + 1; goto allocate_and_return; } result = true; key = high; } allocate_and_return: entry = (struct ipc_entry *) kmem_cache_alloc (ipc_entry_cache); entry->ie_key = key; *entryp = entry; /* We can now insert it into the splay-tree */ ipc_splay_tree_insert (&object->splay_tree, key, entry); return true; } /* Try to reserve space in OBJECT for right named NAME - that is connected to PORT. IPC entry is returned in ENTRYP. Returns true if the process was successful. */ bool ipc_reserve_entry_named (struct ipc_object *object, struct ipc_entry **entryp, struct ipc_port *port, rtmk_port_t name) { struct ipc_entry *entry; if (name < MTABLERIGHTS) return ipc_table_reserve_name (&object->tbl, entryp, name); if (ipc_splay_tree_lookup (&object->splay_tree, name)) return false; entry = (struct ipc_entry *) kmem_cache_alloc (ipc_entry_cache); /* Fill entry structure with needed information. */ entry->ie_key = name; entry->ie_port = port; entry->ie_object = object; /* Set valid bit and right for port. */ entry->ie_bits = IE_BITS_VALID | RTMK_PORT_TYPE_RECEIVE; /* We can insert it into the revese lookup hash table. */ ipc_reverse_insert (object, entry); *entryp = entry; /* We can now insert it into the splay-tree */ ipc_splay_tree_insert (&object->splay_tree, name, entry); return true; } /* Create right for PORT in OBJECT. MSGT describes what right the object should have to the port. Name is returned in NAMEP. */ kern_return_t ipc_object_copyout (struct ipc_object *object, struct ipc_port *port, int msgt, rtmk_port_t *namep) { bool sonce = false, result; struct ipc_entry *entry; /* SONCE is true if we should create send-once right. */ if (msgt == RTMK_MSG_TYPE_MAKE_SEND_ONCE) sonce = true; /* If it's not a send once right we first look in the reverse lookup table. */ if (sonce == false && ipc_reverse_lookup (object, port, &entry) == KERN_SUCCESS) { /* XXX add bits here ??? */ *namep = entry->ie_key; return KERN_SUCCESS; } result = reserve_entry (object, &entry, namep); assert (result); /* Fill entry structure with needed information. */ entry->ie_key = *namep; /* XXX just in case */ entry->ie_port = port; entry->ie_object = object; /* Set valid bit and right for port. */ entry->ie_bits = IE_BITS_VALID | convert_name_to_right (msgt); /* If SONCE is false, then we can insert it into the revese lookup hash table aswell. */ if (sonce == false) ipc_reverse_insert (object, entry); return KERN_SUCCESS; } /* Copy right named NAME from OBJECT into kernel object space. MSGT describes what type of right it is. DEALLOCP is true if *ENTRYP should be deallocated after it is used. */ kern_return_t ipc_object_copyin (struct ipc_object *object, rtmk_port_t name, int msgt, struct ipc_entry **entryp, bool *deallocp) { struct ipc_entry *entry; kern_return_t kr; bool sonce = false; if (msgt == RTMK_MSG_TYPE_MOVE_SEND_ONCE) sonce = true; kr = ipc_object_lookup (object, name, &entry); if (kr != KERN_SUCCESS) return kr; *entryp = entry; *deallocp = false; if (sonce == true) { (void) remove_entry (object, entry); *deallocp = true; } return KERN_SUCCESS; } /* Try to locate entry named NAME in OBJECT. Return pointer to entry structure in ENTRYP if found. Normal kernel return code. */ kern_return_t ipc_object_lookup (struct ipc_object *object, rtmk_port_t name, struct ipc_entry **entryp) { ipc_entry entry; bool result; /* Try the "fast" lookup table first. */ if (name < MTABLERIGHTS) { result = ipc_table_lookup (&object->tbl, name, entryp); if (result == false) return KERN_INVALID_NAME; return KERN_SUCCESS; } /* We look in the splay-tree instead. */ entry = (ipc_entry) ipc_splay_tree_lookup (&object->splay_tree, name); if (entry == 0) return KERN_INVALID_NAME; *entryp = entry; return KERN_SUCCESS; }