/*
* @OSF_FREE_FREE_COPYRIGHT@
*
*/
/*
* HISTORY
* $Log: serv_port.c,v $
* Revision 1.1.2.1 1996/09/09 17:19:04 barbou
* Created.
* [1996/09/02 11:29:02 barbou]
*
* $EndLog$
*/
#include <linux/autoconf.h>
#include <port_obj.h>
#include <mach/mach_port.h>
#include <osfmach3/serv_port.h>
#include <osfmach3/mach_init.h>
#include <osfmach3/mach3_debug.h>
#include <osfmach3/assert.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#define PORTHACK_LITTLE_MAGIC 1685
#define PORTHACK_HASH_SIZE 4096
#define PORTHACK_HASH_POOL 16
struct porthash {
mach_port_t pr_port;
void *pr_value;
struct porthash *pr_next;
} *porthash[PORTHACK_HASH_SIZE],
porthash_pool[PORTHACK_HASH_POOL],
*porthash_poolptr;
#if CONFIG_OSFMACH3_DEBUG
int porthack_allocs;
int porthack_registers;
int debug_port_reuse = 1;
#endif /* CONFIG_OSFMACH3_DEBUG */
#if 1 /* Check mach_port_deallocate. */
kern_return_t
serv_port_deallocate_once(
mach_port_t task,
mach_port_t port)
{
kern_return_t kr;
#if CONFIG_OSFMACH3_DEBUG
mach_port_urefs_t refs;
mach_port_type_t ptype;
mach_port_right_t right;
kr = mach_port_type(task, port, &ptype);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("serv_port_deallocate_once(0x%x,0x%x): "
"mach_port_type", task, port));
panic("serv_port_deallocate_once: mach_port_type failed");
}
switch (ptype) {
case MACH_PORT_TYPE_DEAD_NAME:
right = MACH_PORT_RIGHT_DEAD_NAME;
break;
case MACH_PORT_TYPE_SEND:
right = MACH_PORT_RIGHT_SEND;
break;
default:
printk("type = 0x%x\n", ptype);
panic("serv_port_deallocate_once: bad type");
return mach_port_destroy(task, port);
}
kr = mach_port_get_refs(task, port, right, &refs);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("serv_port_deallocate_once(0x%x,0x%x): "
"mach_port_get_refs", task, port));
panic("serv_port_deallocate_once: can't get refs");
} else if (refs != 1) {
printk("serv_port_dellocate_once(0x%x,0x%x): refs=%d\n",
task, port, refs);
panic("serv_port_deallocate_once: refs != 1");
}
#endif /* CONFIG_OSFMACH3_DEBUG */
kr = mach_port_deallocate(task, port);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("serv_port_deallocate_once(0x%x,0x%x): "
"mach_port_deallocate", task, port));
panic("serv_port_deallocate_once: can't deallocate");
}
#if CONFIG_OSFMACH3_DEBUG
/* Note race condition: someone else might reallocate the port. */
kr = mach_port_type(task, port, &ptype);
if (kr == KERN_SUCCESS) {
printk("serv_port_deallocate_once(0x%x,0x%x): "
"port type = 0x%x\n", task, port, ptype);
panic("serv_port_deallocate_once: "
"port not destroyed on deallocate");
}
#endif /* CONFIG_OSFMACH3_DEBUG */
return KERN_SUCCESS;
}
kern_return_t
serv_port_destroy_receive(
mach_port_t task,
mach_port_t port)
{
kern_return_t kr;
#if CONFIG_OSFMACH3_DEBUG
mach_port_type_t ptype;
kr = mach_port_type(task, port, &ptype);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("serv_port_destroy_receive(0x%x,0x%x): "
"mach_port_type", task, port));
panic("serv_port_destroy_receive: can't get type");
}
if (ptype != MACH_PORT_TYPE_RECEIVE) {
printk("serv_port_destroy_receive(0x%x,0x%x): ptype=0x%x\n",
task, port, ptype);
panic("serv_port_destroy_receive: not (pure) receive right");
}
#endif /* CONFIG_OSFMACH3_DEBUG */
kr = mach_port_mod_refs(task, port, MACH_PORT_RIGHT_RECEIVE, -1);
if (kr != KERN_SUCCESS) {
MACH3_DEBUG(0, kr,
("serv_port_destroy_receive(0x%x,0x%x): "
"mach_port_mod_refs(RECEIVE,-1)",
task, port));
panic("serv_port_destroy_receive: can't destroy");
}
#if CONFIG_OSFMACH3_DEBUG
kr = mach_port_type(task, port, &ptype);
if (kr == KERN_SUCCESS) {
printk("serv_port_destroy_receive(0x%x,0x%x): ptype=0x%x\n",
task, port, ptype);
panic("serv_port_destroy_receive: port not destroyed");
}
#endif /* CONFIG_OSFMACH3_DEBUG */
return KERN_SUCCESS;
}
#endif /* 1 (Checking mach_port_deallocate) */
/*
* The following functions are for managing receive rights. We used to
* rename these to corresponding data structures in the server. Now we
* provide these functions which allow a (void *) to be associated with
* a newly-allocated receive right in a way that makes it efficient to
* retrieve.
*/
kern_return_t
serv_port_rename(
mach_port_t port,
void *server_name)
{
port_set_obj_value_type(port, server_name, PORTHACK_LITTLE_MAGIC);
return KERN_SUCCESS;
}
/*
* Allocate a receive right, setting *mach_name. name is the value we
* will associate with the right, i.e., what used to be the name demanded
* from Mach for the port.
*/
kern_return_t
serv_port_allocate_name(
mach_port_t *mach_name,
void *server_name)
{
kern_return_t kr;
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
mach_name);
if (kr != KERN_SUCCESS)
return kr;
serv_port_rename(*mach_name, server_name);
return KERN_SUCCESS;
}
kern_return_t
serv_port_allocate_subsystem(
mach_port_t subsystem,
mach_port_t *mach_name,
void *server_name)
{
kern_return_t kr;
kr = mach_port_allocate_subsystem(mach_task_self(), subsystem,
mach_name);
if (kr != KERN_SUCCESS)
return kr;
serv_port_rename(*mach_name, server_name);
return KERN_SUCCESS;
}
/*
* Retrieve the value associated with a receive right.
*/
#ifndef serv_port_name /* may be a macro in osfmach3/port.h */
void *
serv_port_name(
mach_port_t name)
{
ASSERT(port_get_obj_type(name) == PORTHACK_LITTLE_MAGIC);
return serv_port_name_macro(name);
}
#endif /* serv_port_name */
/*
* Destroy a port right allocated with serv_port_allocate_name, and the
* associated data structure.
*/
kern_return_t
serv_port_destroy(
mach_port_t name)
{
kern_return_t kr;
ASSERT(port_get_obj_type(name) == PORTHACK_LITTLE_MAGIC);
kr = serv_port_destroy_receive(mach_task_self(), name);
return kr;
}
/*
* The following functions are for managing send rights. We use a hash
* table to allow a value to be associated with any right. This is less
* efficient than the method used for receive rights, so it should be
* avoided on critical paths such as system calls.
*/
/*
* Allocate a porthash structure. Since this function may be called before
* the malloc subsystem is ready, we dole the first few allocations out of
* a static array. It might be better to use zalloc.
*/
struct porthash *
serv_porthash_alloc(void)
{
struct porthash *ph;
static boolean_t inited = FALSE;
if (!inited) {
int i;
porthash_poolptr = porthash_pool;
for (i = 0; i < PORTHACK_HASH_POOL - 1; i++)
porthash_pool[i].pr_next = &porthash_pool[i + 1];
inited = TRUE;
}
if (porthash_poolptr != NULL) {
ph = porthash_poolptr;
porthash_poolptr = ph->pr_next;
return ph;
}
/*
* We don't want to block while holding the spinlock, so first try a
* non-blocking malloc, and if that fails, release the lock and block.
*/
ph = (struct porthash *) kmalloc(sizeof (struct porthash), GFP_KERNEL);
return ph;
}
/*
* Free a porthash structure. Calling FREE might not work here, because
* current_thread() might be undefined. So we hold on to our structures
* forever.
*/
void
serv_porthash_free(
struct porthash *ph)
{
ph->pr_next = porthash_poolptr;
porthash_poolptr = ph;
}
/*
* Look up a value in the hash table and return the place where the
* corresponding structure either is or should be inserted. The hash
* function (stolen from device_reply_hdlr.c) is supposedly good for
* port names allocated by Mach.
*/
struct porthash **
serv_port_hash(
mach_port_t name)
{
unsigned int hash;
struct porthash **p;
ASSERT(MACH_PORT_VALID(name));
hash = (int) name;
hash = ((hash & 0xff) + (hash >> 8)) % PORTHACK_HASH_SIZE;
for (p = &porthash[hash]; *p; p = &(*p)->pr_next) {
if ((*p)->pr_port == name)
break;
}
return p;
}
/*
* Associate a value with a port name.
*/
void
serv_port_register(
mach_port_t name,
void *value)
{
struct porthash **p, *ph;
ph = serv_porthash_alloc();
if (ph == NULL) {
panic("serv_port_register: can't allocate hash entry");
}
p = serv_port_hash(name);
#if CONFIG_OSFMACH3_DEBUG
if (debug_port_reuse && *p != NULL) {
printk("reused port 0x%x, old 0x%x new 0x%x\n",
(int) name, (int) (*p)->pr_value, (int) value);
Debugger("port reuse");
serv_porthash_free(ph);
return;
}
porthack_registers++;
#endif /* CONFIG_OSFMACH3_DEBUG */
ph->pr_port = name;
ph->pr_value = value;
ph->pr_next = NULL;
*p = ph;
}
/*
* Break the association between a port name and its value. A value must
* have been registered.
*/
void
serv_port_unregister(
mach_port_t name)
{
struct porthash **p, *ph;
p = serv_port_hash(name);
if (*p == NULL)
panic("serv_port_unregister(0x%x): not registered", name);
else {
ph = *p;
*p = ph->pr_next;
serv_porthash_free(ph);
#if CONFIG_OSFMACH3_DEBUG
porthack_registers--;
#endif /* CONFIG_OSFMACH3_DEBUG */
}
}
/*
* Return the value associated with a port name, or NULL if there is none.
*/
void *
serv_port_lookup(
mach_port_t name)
{
struct porthash *ph;
ph = *serv_port_hash(name);
if (ph == NULL)
return NULL;
else
return ph->pr_value;
}