1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- /*
- * Mach Operating System
- * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
- * Copyright (c) 1993,1994 The University of Utah and
- * the Computer Systems Laboratory (CSL).
- * All rights reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
- * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
- * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
- * THIS SOFTWARE.
- *
- * Carnegie Mellon requests users of this software to return to
- *
- * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- * School of Computer Science
- * Carnegie Mellon University
- * Pittsburgh PA 15213-3890
- *
- * any improvements or extensions that they make and grant Carnegie Mellon
- * the rights to redistribute these changes.
- */
- /*
- * File: ipc/ipc_port.c
- * Author: Rich Draves
- * Date: 1989
- *
- * Functions to manipulate IPC ports.
- */
-
- #pragma GCC diagnostic error "-Wundef"
- #include <glue/gnulinux.h>
- #include <kern/printf.h>
- #include <string.h>
- #include <mach/port.h>
- #include <mach/kern_return.h>
- #include <kern/lock.h>
- #include <kern/ipc_sched.h>
- #include <kern/ipc_kobject.h>
- #include <ipc/ipc_entry.h>
- #include <ipc/ipc_space.h>
- #include <ipc/ipc_object.h>
- #include <ipc/ipc_port.h>
- #include <ipc/ipc_pset.h>
- #include <ipc/ipc_thread.h>
- #include <ipc/ipc_mqueue.h>
- #include <ipc/ipc_notify.h>
- #if MACH_KDB
- #include <ddb/db_output.h>
- #include <ipc/ipc_print.h>
- #endif /* MACH_KDB */
- decl_simple_lock_data(, ipc_port_multiple_lock_data)
- decl_simple_lock_data(, ipc_port_timestamp_lock_data)
- ipc_port_timestamp_t ipc_port_timestamp_data;
- /*
- * Routine: ipc_port_timestamp
- * Purpose:
- * Retrieve a timestamp value.
- */
- ipc_port_timestamp_t
- ipc_port_timestamp(void)
- {
- ipc_port_timestamp_t timestamp;
- ipc_port_timestamp_lock();
- timestamp = ipc_port_timestamp_data++;
- ipc_port_timestamp_unlock();
- return timestamp;
- }
- /*
- * Routine: ipc_port_dnrequest
- * Purpose:
- * Try to allocate a dead-name request slot.
- * If successful, returns the request index.
- * Otherwise returns zero.
- * Conditions:
- * The port is locked and active.
- * Returns:
- * KERN_SUCCESS A request index was found.
- * KERN_NO_SPACE No index allocated.
- */
- kern_return_t
- ipc_port_dnrequest(
- ipc_port_t port,
- mach_port_t name,
- ipc_port_t soright,
- ipc_port_request_index_t *indexp)
- {
- ipc_port_request_t ipr, table;
- ipc_port_request_index_t index;
- assert(ip_active(port));
- assert(name != MACH_PORT_NULL);
- assert(soright != IP_NULL);
- table = port->ip_dnrequests;
- if (table == IPR_NULL)
- return KERN_NO_SPACE;
- index = table->ipr_next;
- if (index == 0)
- return KERN_NO_SPACE;
- ipr = &table[index];
- assert(ipr->ipr_name == MACH_PORT_NULL);
- table->ipr_next = ipr->ipr_next;
- ipr->ipr_name = name;
- ipr->ipr_soright = soright;
- *indexp = index;
- return KERN_SUCCESS;
- }
- /*
- * Routine: ipc_port_dngrow
- * Purpose:
- * Grow a port's table of dead-name requests.
- * Conditions:
- * The port must be locked and active.
- * Nothing else locked; will allocate memory.
- * Upon return the port is unlocked.
- * Returns:
- * KERN_SUCCESS Grew the table.
- * KERN_SUCCESS Somebody else grew the table.
- * KERN_SUCCESS The port died.
- * KERN_RESOURCE_SHORTAGE Couldn't allocate new table.
- */
- kern_return_t
- ipc_port_dngrow(ipc_port_t port)
- {
- ipc_table_size_t its;
- ipc_port_request_t otable, ntable;
- assert(ip_active(port));
- otable = port->ip_dnrequests;
- if (otable == IPR_NULL)
- its = &ipc_table_dnrequests[0];
- else
- its = otable->ipr_size + 1;
- ip_reference(port);
- ip_unlock(port);
- if ((its->its_size == 0) ||
- ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) {
- ipc_port_release(port);
- return KERN_RESOURCE_SHORTAGE;
- }
- ip_lock(port);
- ip_release(port);
- /*
- * Check that port is still active and that nobody else
- * has slipped in and grown the table on us. Note that
- * just checking port->ip_dnrequests == otable isn't
- * sufficient; must check ipr_size.
- */
- if (ip_active(port) &&
- (port->ip_dnrequests == otable) &&
- ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
- ipc_table_size_t oits = 0; /* '=0' to shut up lint */
- ipc_table_elems_t osize, nsize;
- ipc_port_request_index_t free, i;
- /* copy old table to new table */
- if (otable != IPR_NULL) {
- oits = otable->ipr_size;
- osize = oits->its_size;
- free = otable->ipr_next;
- memcpy((ntable + 1), (otable + 1),
- (osize - 1) * sizeof(struct ipc_port_request));
- } else {
- osize = 1;
- free = 0;
- }
- nsize = its->its_size;
- assert(nsize > osize);
- /* add new elements to the new table's free list */
- for (i = osize; i < nsize; i++) {
- ipc_port_request_t ipr = &ntable[i];
- ipr->ipr_name = MACH_PORT_NULL;
- ipr->ipr_next = free;
- free = i;
- }
- ntable->ipr_next = free;
- ntable->ipr_size = its;
- port->ip_dnrequests = ntable;
- ip_unlock(port);
- if (otable != IPR_NULL)
- it_dnrequests_free(oits, otable);
- } else {
- ip_check_unlock(port);
- it_dnrequests_free(its, ntable);
- }
- return KERN_SUCCESS;
- }
- /*
- * Routine: ipc_port_dncancel
- * Purpose:
- * Cancel a dead-name request and return the send-once right.
- * Conditions:
- * The port must locked and active.
- */
- ipc_port_t
- ipc_port_dncancel(
- ipc_port_t port,
- mach_port_t name,
- ipc_port_request_index_t index)
- {
- ipc_port_request_t ipr, table;
- ipc_port_t dnrequest;
- assert(ip_active(port));
- assert(name != MACH_PORT_NULL);
- assert(index != 0);
- table = port->ip_dnrequests;
- assert(table != IPR_NULL);
- ipr = &table[index];
- dnrequest = ipr->ipr_soright;
- assert(ipr->ipr_name == name);
- /* return ipr to the free list inside the table */
- ipr->ipr_name = MACH_PORT_NULL;
- ipr->ipr_next = table->ipr_next;
- table->ipr_next = index;
- return dnrequest;
- }
- /*
- * Routine: ipc_port_pdrequest
- * Purpose:
- * Make a port-deleted request, returning the
- * previously registered send-once right.
- * Just cancels the previous request if notify is IP_NULL.
- * Conditions:
- * The port is locked and active. It is unlocked.
- * Consumes a ref for notify (if non-null), and
- * returns previous with a ref (if non-null).
- */
- void
- ipc_port_pdrequest(
- ipc_port_t port,
- const ipc_port_t notify,
- ipc_port_t *previousp)
- {
- ipc_port_t previous;
- assert(ip_active(port));
- previous = port->ip_pdrequest;
- port->ip_pdrequest = notify;
- ip_unlock(port);
- *previousp = previous;
- }
- /*
- * Routine: ipc_port_nsrequest
- * Purpose:
- * Make a no-senders request, returning the
- * previously registered send-once right.
- * Just cancels the previous request if notify is IP_NULL.
- * Conditions:
- * The port is locked and active. It is unlocked.
- * Consumes a ref for notify (if non-null), and
- * returns previous with a ref (if non-null).
- */
- void
- ipc_port_nsrequest(
- ipc_port_t port,
- mach_port_mscount_t sync,
- ipc_port_t notify,
- ipc_port_t *previousp)
- {
- ipc_port_t previous;
- mach_port_mscount_t mscount;
- assert(ip_active(port));
- previous = port->ip_nsrequest;
- mscount = port->ip_mscount;
- if ((port->ip_srights == 0) &&
- (sync <= mscount) &&
- (notify != IP_NULL)) {
- port->ip_nsrequest = IP_NULL;
- ip_unlock(port);
- ipc_notify_no_senders(notify, mscount);
- } else {
- port->ip_nsrequest = notify;
- ip_unlock(port);
- }
- *previousp = previous;
- }
- /*
- * Routine: ipc_port_set_qlimit
- * Purpose:
- * Changes a port's queue limit; the maximum number
- * of messages which may be queued to the port.
- * Conditions:
- * The port is locked and active.
- */
- void
- ipc_port_set_qlimit(
- ipc_port_t port,
- mach_port_msgcount_t qlimit)
- {
- assert(ip_active(port));
- /* wake up senders allowed by the new qlimit */
- if (qlimit > port->ip_qlimit) {
- mach_port_msgcount_t i, wakeup;
- /* caution: wakeup, qlimit are unsigned */
- wakeup = qlimit - port->ip_qlimit;
- for (i = 0; i < wakeup; i++) {
- ipc_thread_t th;
- th = ipc_thread_dequeue(&port->ip_blocked);
- if (th == ITH_NULL)
- break;
- th->ith_state = MACH_MSG_SUCCESS;
- thread_go(th);
- }
- }
- port->ip_qlimit = qlimit;
- }
- /*
- * Routine: ipc_port_lock_mqueue
- * Purpose:
- * Locks and returns the message queue that the port is using.
- * The message queue may be in the port or in its port set.
- * Conditions:
- * The port is locked and active.
- * Port set, message queue locks may be taken.
- */
- ipc_mqueue_t
- ipc_port_lock_mqueue(ipc_port_t port)
- {
- if (port->ip_pset != IPS_NULL) {
- ipc_pset_t pset = port->ip_pset;
- ips_lock(pset);
- if (ips_active(pset)) {
- imq_lock(&pset->ips_messages);
- ips_unlock(pset);
- return &pset->ips_messages;
- }
- ipc_pset_remove(pset, port);
- ips_check_unlock(pset);
- }
- imq_lock(&port->ip_messages);
- return &port->ip_messages;
- }
- /*
- * Routine: ipc_port_set_seqno
- * Purpose:
- * Changes a port's sequence number.
- * Conditions:
- * The port is locked and active.
- * Port set, message queue locks may be taken.
- */
- void
- ipc_port_set_seqno(
- ipc_port_t port,
- mach_port_seqno_t seqno)
- {
- ipc_mqueue_t mqueue;
- mqueue = ipc_port_lock_mqueue(port);
- port->ip_seqno = seqno;
- imq_unlock(mqueue);
- }
- /*
- * Routine: ipc_port_set_protected_payload
- * Purpose:
- * Changes a port's protected payload.
- * Conditions:
- * The port is locked and active.
- */
- void
- ipc_port_set_protected_payload(ipc_port_t port, unsigned long payload)
- {
- ipc_mqueue_t mqueue;
- mqueue = ipc_port_lock_mqueue(port);
- port->ip_protected_payload = payload;
- ipc_port_flag_protected_payload_set(port);
- imq_unlock(mqueue);
- }
- /*
- * Routine: ipc_port_clear_protected_payload
- * Purpose:
- * Clear a port's protected payload.
- * Conditions:
- * The port is locked and active.
- */
- void
- ipc_port_clear_protected_payload(ipc_port_t port)
- {
- ipc_mqueue_t mqueue;
- mqueue = ipc_port_lock_mqueue(port);
- ipc_port_flag_protected_payload_clear(port);
- imq_unlock(mqueue);
- }
- /*
- * Routine: ipc_port_clear_receiver
- * Purpose:
- * Prepares a receive right for transmission/destruction.
- * Conditions:
- * The port is locked and active.
- */
- void
- ipc_port_clear_receiver(
- ipc_port_t port)
- {
- ipc_pset_t pset;
- assert(ip_active(port));
- pset = port->ip_pset;
- if (pset != IPS_NULL) {
- /* No threads receiving from port, but must remove from set. */
- ips_lock(pset);
- ipc_pset_remove(pset, port);
- ips_check_unlock(pset);
- } else {
- /* Else, wake up all receivers, indicating why. */
- imq_lock(&port->ip_messages);
- ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_DIED);
- imq_unlock(&port->ip_messages);
- }
- ipc_port_set_mscount(port, 0);
- imq_lock(&port->ip_messages);
- port->ip_seqno = 0;
- imq_unlock(&port->ip_messages);
- }
- /*
- * Routine: ipc_port_init
- * Purpose:
- * Initializes a newly-allocated port.
- * Doesn't touch the ip_object fields.
- */
- void
- ipc_port_init(
- ipc_port_t port,
- ipc_space_t space,
- mach_port_t name)
- {
- /* port->ip_kobject doesn't have to be initialized */
- ipc_target_init(&port->ip_target, name);
- port->ip_receiver = space;
- port->ip_mscount = 0;
- port->ip_srights = 0;
- port->ip_sorights = 0;
- port->ip_nsrequest = IP_NULL;
- port->ip_pdrequest = IP_NULL;
- port->ip_dnrequests = IPR_NULL;
- port->ip_pset = IPS_NULL;
- port->ip_cur_target = &port->ip_target;
- port->ip_seqno = 0;
- port->ip_msgcount = 0;
- port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT;
- ipc_port_flag_protected_payload_clear(port);
- port->ip_protected_payload = 0;
- ipc_mqueue_init(&port->ip_messages);
- ipc_thread_queue_init(&port->ip_blocked);
- }
- /*
- * Routine: ipc_port_alloc
- * Purpose:
- * Allocate a port.
- * Conditions:
- * Nothing locked. If successful, the port is returned
- * locked. (The caller doesn't have a reference.)
- * Returns:
- * KERN_SUCCESS The port is allocated.
- * KERN_INVALID_TASK The space is dead.
- * KERN_NO_SPACE No room for an entry in the space.
- * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
- */
- kern_return_t
- ipc_port_alloc(
- ipc_space_t space,
- mach_port_t *namep,
- ipc_port_t *portp)
- {
- ipc_port_t port;
- mach_port_t name;
- kern_return_t kr;
- kr = ipc_object_alloc(space, IOT_PORT,
- MACH_PORT_TYPE_RECEIVE, 0,
- &name, (ipc_object_t *) &port);
- if (kr != KERN_SUCCESS)
- return kr;
- /* port is locked */
- ipc_port_init(port, space, name);
- *namep = name;
- *portp = port;
- return KERN_SUCCESS;
- }
- /*
- * Routine: ipc_port_alloc_name
- * Purpose:
- * Allocate a port, with a specific name.
- * Conditions:
- * Nothing locked. If successful, the port is returned
- * locked. (The caller doesn't have a reference.)
- * Returns:
- * KERN_SUCCESS The port is allocated.
- * KERN_INVALID_TASK The space is dead.
- * KERN_NAME_EXISTS The name already denotes a right.
- * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
- */
- kern_return_t
- ipc_port_alloc_name(
- ipc_space_t space,
- mach_port_t name,
- ipc_port_t *portp)
- {
- ipc_port_t port;
- kern_return_t kr;
- kr = ipc_object_alloc_name(space, IOT_PORT,
- MACH_PORT_TYPE_RECEIVE, 0,
- name, (ipc_object_t *) &port);
- if (kr != KERN_SUCCESS)
- return kr;
- /* port is locked */
- ipc_port_init(port, space, name);
- *portp = port;
- return KERN_SUCCESS;
- }
- /*
- * Routine: ipc_port_destroy
- * Purpose:
- * Destroys a port. Cleans up queued messages.
- *
- * If the port has a backup, it doesn't get destroyed,
- * but is sent in a port-destroyed notification to the backup.
- * Conditions:
- * The port is locked and alive; nothing else locked.
- * The caller has a reference, which is consumed.
- * Afterwards, the port is unlocked and dead.
- */
- void
- ipc_port_destroy(
- ipc_port_t port)
- {
- ipc_port_t pdrequest, nsrequest;
- ipc_mqueue_t mqueue;
- ipc_kmsg_queue_t kmqueue;
- ipc_kmsg_t kmsg;
- ipc_thread_t sender;
- ipc_port_request_t dnrequests;
- assert(ip_active(port));
- /* port->ip_receiver_name is garbage */
- /* port->ip_receiver/port->ip_destination is garbage */
- assert(port->ip_pset == IPS_NULL);
- assert(port->ip_mscount == 0);
- assert(port->ip_seqno == 0);
- /* first check for a backup port */
- pdrequest = port->ip_pdrequest;
- if (pdrequest != IP_NULL) {
- /* we assume the ref for pdrequest */
- port->ip_pdrequest = IP_NULL;
- /* make port be in limbo */
- port->ip_receiver_name = MACH_PORT_NULL;
- port->ip_destination = IP_NULL;
- ipc_port_flag_protected_payload_clear(port);
- ip_unlock(port);
- if (!ipc_port_check_circularity(port, pdrequest)) {
- /* consumes our refs for port and pdrequest */
- ipc_notify_port_destroyed(pdrequest, port);
- return;
- } else {
- /* consume pdrequest and destroy port */
- ipc_port_release_sonce(pdrequest);
- }
- ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_pset == IPS_NULL);
- assert(port->ip_mscount == 0);
- assert(port->ip_seqno == 0);
- assert(port->ip_pdrequest == IP_NULL);
- assert(port->ip_receiver_name == MACH_PORT_NULL);
- assert(port->ip_destination == IP_NULL);
- /* fall through and destroy the port */
- }
- /*
- * rouse all blocked senders
- *
- * This must be done with the port locked, because
- * ipc_mqueue_send can play with the ip_blocked queue
- * of a dead port.
- */
- while ((sender = ipc_thread_dequeue(&port->ip_blocked)) != ITH_NULL) {
- sender->ith_state = MACH_MSG_SUCCESS;
- thread_go(sender);
- }
- /* once port is dead, we don't need to keep it locked */
- port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
- port->ip_timestamp = ipc_port_timestamp();
- ip_unlock(port);
- /* throw away no-senders request */
- nsrequest = port->ip_nsrequest;
- if (nsrequest != IP_NULL)
- ipc_notify_send_once(nsrequest); /* consumes ref */
- /* destroy any queued messages */
- mqueue = &port->ip_messages;
- imq_lock(mqueue);
- assert(ipc_thread_queue_empty(&mqueue->imq_threads));
- kmqueue = &mqueue->imq_messages;
- while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
- imq_unlock(mqueue);
- assert(kmsg->ikm_header.msgh_remote_port ==
- (mach_port_t) port);
- ipc_port_release(port);
- kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
- ipc_kmsg_destroy(kmsg);
- imq_lock(mqueue);
- }
- imq_unlock(mqueue);
- /* generate dead-name notifications */
- dnrequests = port->ip_dnrequests;
- if (dnrequests != IPR_NULL) {
- ipc_table_size_t its = dnrequests->ipr_size;
- ipc_table_elems_t size = its->its_size;
- ipc_port_request_index_t index;
- for (index = 1; index < size; index++) {
- ipc_port_request_t ipr = &dnrequests[index];
- mach_port_t name = ipr->ipr_name;
- ipc_port_t soright;
- if (name == MACH_PORT_NULL)
- continue;
- soright = ipr->ipr_soright;
- assert(soright != IP_NULL);
- ipc_notify_dead_name(soright, name);
- }
- it_dnrequests_free(its, dnrequests);
- }
- if (ip_kotype(port) != IKOT_NONE)
- ipc_kobject_destroy(port);
- /* Common destruction for the IPC target. */
- ipc_target_terminate(&port->ip_target);
- ipc_port_release(port); /* consume caller's ref */
- }
- /*
- * Routine: ipc_port_check_circularity
- * Purpose:
- * Check if queueing "port" in a message for "dest"
- * would create a circular group of ports and messages.
- *
- * If no circularity (FALSE returned), then "port"
- * is changed from "in limbo" to "in transit".
- *
- * That is, we want to set port->ip_destination == dest,
- * but guaranteeing that this doesn't create a circle
- * port->ip_destination->ip_destination->... == port
- * Conditions:
- * No ports locked. References held for "port" and "dest".
- */
- boolean_t
- ipc_port_check_circularity(
- ipc_port_t port,
- ipc_port_t dest)
- {
- ipc_port_t base;
- assert(port != IP_NULL);
- assert(dest != IP_NULL);
- if (port == dest)
- return TRUE;
- base = dest;
- /*
- * First try a quick check that can run in parallel.
- * No circularity if dest is not in transit.
- */
- ip_lock(port);
- if (ip_lock_try(dest)) {
- if (!ip_active(dest) ||
- (dest->ip_receiver_name != MACH_PORT_NULL) ||
- (dest->ip_destination == IP_NULL))
- goto not_circular;
- /* dest is in transit; further checking necessary */
- ip_unlock(dest);
- }
- ip_unlock(port);
- ipc_port_multiple_lock(); /* massive serialization */
- /*
- * Search for the end of the chain (a port not in transit),
- * acquiring locks along the way.
- */
- for (;;) {
- ip_lock(base);
- if (!ip_active(base) ||
- (base->ip_receiver_name != MACH_PORT_NULL) ||
- (base->ip_destination == IP_NULL))
- break;
- base = base->ip_destination;
- }
- /* all ports in chain from dest to base, inclusive, are locked */
- if (port == base) {
- /* circularity detected! */
- ipc_port_multiple_unlock();
- /* port (== base) is in limbo */
- assert(ip_active(port));
- assert(port->ip_receiver_name == MACH_PORT_NULL);
- assert(port->ip_destination == IP_NULL);
- while (dest != IP_NULL) {
- ipc_port_t next;
- /* dest is in transit or in limbo */
- assert(ip_active(dest));
- assert(dest->ip_receiver_name == MACH_PORT_NULL);
- next = dest->ip_destination;
- ip_unlock(dest);
- dest = next;
- }
- return TRUE;
- }
- /*
- * The guarantee: lock port while the entire chain is locked.
- * Once port is locked, we can take a reference to dest,
- * add port to the chain, and unlock everything.
- */
- ip_lock(port);
- ipc_port_multiple_unlock();
- not_circular:
- /* port is in limbo */
- assert(ip_active(port));
- assert(port->ip_receiver_name == MACH_PORT_NULL);
- assert(port->ip_destination == IP_NULL);
- ip_reference(dest);
- port->ip_destination = dest;
- /* now unlock chain */
- while (port != base) {
- ipc_port_t next;
- /* port is in transit */
- assert(ip_active(port));
- assert(port->ip_receiver_name == MACH_PORT_NULL);
- assert(port->ip_destination != IP_NULL);
- next = port->ip_destination;
- ip_unlock(port);
- port = next;
- }
- /* base is not in transit */
- assert(!ip_active(base) ||
- (base->ip_receiver_name != MACH_PORT_NULL) ||
- (base->ip_destination == IP_NULL));
- ip_unlock(base);
- return FALSE;
- }
- /*
- * Routine: ipc_port_lookup_notify
- * Purpose:
- * Make a send-once notify port from a receive right.
- * Returns IP_NULL if name doesn't denote a receive right.
- * Conditions:
- * The space must be locked (read or write) and active.
- */
- ipc_port_t
- ipc_port_lookup_notify(
- ipc_space_t space,
- mach_port_t name)
- {
- ipc_port_t port;
- ipc_entry_t entry;
- assert(space->is_active);
- entry = ipc_entry_lookup(space, name);
- if (entry == IE_NULL)
- return IP_NULL;
- if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
- return IP_NULL;
- port = (ipc_port_t) entry->ie_object;
- assert(port != IP_NULL);
- ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_receiver_name == name);
- assert(port->ip_receiver == space);
- ip_reference(port);
- port->ip_sorights++;
- ip_unlock(port);
- return port;
- }
- /*
- * Routine: ipc_port_make_send
- * Purpose:
- * Make a naked send right from a receive right.
- * Conditions:
- * The port is not locked but it is active.
- */
- ipc_port_t
- ipc_port_make_send(
- ipc_port_t port)
- {
- assert(IP_VALID(port));
- ip_lock(port);
- assert(ip_active(port));
- port->ip_mscount++;
- port->ip_srights++;
- ip_reference(port);
- ip_unlock(port);
- return port;
- }
- /*
- * Routine: ipc_port_copy_send
- * Purpose:
- * Make a naked send right from another naked send right.
- * IP_NULL -> IP_NULL
- * IP_DEAD -> IP_DEAD
- * dead port -> IP_DEAD
- * live port -> port + ref
- * Conditions:
- * Nothing locked except possibly a space.
- */
- ipc_port_t
- ipc_port_copy_send(
- ipc_port_t port)
- {
- ipc_port_t sright;
- if (!IP_VALID(port))
- return port;
- ip_lock(port);
- if (ip_active(port)) {
- assert(port->ip_srights > 0);
- ip_reference(port);
- port->ip_srights++;
- sright = port;
- } else
- sright = IP_DEAD;
- ip_unlock(port);
- return sright;
- }
- /*
- * Routine: ipc_port_copyout_send
- * Purpose:
- * Copyout a naked send right (possibly null/dead),
- * or if that fails, destroy the right.
- * Conditions:
- * Nothing locked.
- */
- mach_port_t
- ipc_port_copyout_send(
- ipc_port_t sright,
- ipc_space_t space)
- {
- mach_port_t name;
- if (IP_VALID(sright)) {
- kern_return_t kr;
- kr = ipc_object_copyout(space, (ipc_object_t) sright,
- MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
- if (kr != KERN_SUCCESS) {
- ipc_port_release_send(sright);
- if (kr == KERN_INVALID_CAPABILITY)
- name = MACH_PORT_DEAD;
- else
- name = MACH_PORT_NULL;
- }
- } else
- name = (mach_port_t) sright;
- return name;
- }
- /*
- * Routine: ipc_port_release_send
- * Purpose:
- * Release a (valid) naked send right.
- * Consumes a ref for the port.
- * Conditions:
- * Nothing locked.
- */
- void
- ipc_port_release_send(
- ipc_port_t port)
- {
- ipc_port_t nsrequest = IP_NULL;
- mach_port_mscount_t mscount;
- assert(IP_VALID(port));
- ip_lock(port);
- ip_release(port);
- if (!ip_active(port)) {
- ip_check_unlock(port);
- return;
- }
- assert(port->ip_srights > 0);
- if (--port->ip_srights == 0) {
- nsrequest = port->ip_nsrequest;
- if (nsrequest != IP_NULL) {
- port->ip_nsrequest = IP_NULL;
- mscount = port->ip_mscount;
- }
- }
- ip_unlock(port);
- if (nsrequest != IP_NULL)
- ipc_notify_no_senders(nsrequest, mscount);
- }
- /*
- * Routine: ipc_port_make_sonce
- * Purpose:
- * Make a naked send-once right from a receive right.
- * Conditions:
- * The port is not locked but it is active.
- */
- ipc_port_t
- ipc_port_make_sonce(
- ipc_port_t port)
- {
- assert(IP_VALID(port));
- ip_lock(port);
- assert(ip_active(port));
- port->ip_sorights++;
- ip_reference(port);
- ip_unlock(port);
- return port;
- }
- /*
- * Routine: ipc_port_release_sonce
- * Purpose:
- * Release a naked send-once right.
- * Consumes a ref for the port.
- *
- * In normal situations, this is never used.
- * Send-once rights are only consumed when
- * a message (possibly a send-once notification)
- * is sent to them.
- * Conditions:
- * Nothing locked except possibly a space.
- */
- void
- ipc_port_release_sonce(
- ipc_port_t port)
- {
- assert(IP_VALID(port));
- ip_lock(port);
- assert(port->ip_sorights > 0);
- port->ip_sorights--;
- ip_release(port);
- if (!ip_active(port)) {
- ip_check_unlock(port);
- return;
- }
- ip_unlock(port);
- }
- /*
- * Routine: ipc_port_release_receive
- * Purpose:
- * Release a naked (in limbo or in transit) receive right.
- * Consumes a ref for the port; destroys the port.
- * Conditions:
- * Nothing locked.
- */
- void
- ipc_port_release_receive(
- ipc_port_t port)
- {
- ipc_port_t dest;
- assert(IP_VALID(port));
- ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_receiver_name == MACH_PORT_NULL);
- dest = port->ip_destination;
- ipc_port_destroy(port); /* consumes ref, unlocks */
- if (dest != IP_NULL)
- ipc_port_release(dest);
- }
- /*
- * Routine: ipc_port_alloc_special
- * Purpose:
- * Allocate a port in a special space.
- * The new port is returned with one ref.
- * If unsuccessful, IP_NULL is returned.
- * Conditions:
- * Nothing locked.
- */
- ipc_port_t
- ipc_port_alloc_special(ipc_space_t space)
- {
- ipc_port_t port;
- port = ip_alloc();
- if (port == IP_NULL)
- return IP_NULL;
- ip_lock_init(port);
- port->ip_references = 1;
- port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
- /*
- * The actual values of ip_receiver_name aren't important,
- * as long as they are valid (not null/dead).
- *
- * Mach4: we set it to the internal port structure address
- * so we can always just pass on ip_receiver_name during
- * an rpc regardless of whether the destination is user or
- * kernel (i.e. no special-casing code for the kernel along
- * the fast rpc path).
- */
- ipc_port_init(port, space, (mach_port_t)port);
- return port;
- }
- /*
- * Routine: ipc_port_dealloc_special
- * Purpose:
- * Deallocate a port in a special space.
- * Consumes one ref for the port.
- * Conditions:
- * Nothing locked.
- */
- void
- ipc_port_dealloc_special(
- ipc_port_t port,
- ipc_space_t space)
- {
- ip_lock(port);
- assert(ip_active(port));
- assert(port->ip_receiver_name != MACH_PORT_NULL);
- assert(port->ip_receiver == space);
- /*
- * We clear ip_receiver_name and ip_receiver to simplify
- * the ipc_space_kernel check in ipc_mqueue_send.
- */
- port->ip_receiver_name = MACH_PORT_NULL;
- port->ip_receiver = IS_NULL;
- /*
- * For ipc_space_kernel, all ipc_port_clear_receiver does
- * is clean things up for the assertions in ipc_port_destroy.
- * For ipc_space_reply, there might be a waiting receiver.
- */
- ipc_port_clear_receiver(port);
- ipc_port_destroy(port);
- }
- #if MACH_KDB
- #define printf kdbprintf
- /*
- * Routine: ipc_port_print
- * Purpose:
- * Pretty-print a port for kdb.
- */
- void
- ipc_port_print(port)
- const ipc_port_t port;
- {
- printf("port 0x%x\n", port);
- indent += 2;
- iprintf("flags ");
- printf("has_protected_payload=%d",
- ipc_port_flag_protected_payload(port));
- printf("\n");
- ipc_object_print(&port->ip_object);
- iprintf("receiver=0x%x", port->ip_receiver);
- printf(", receiver_name=0x%x\n", port->ip_receiver_name);
- iprintf("mscount=%d", port->ip_mscount);
- printf(", srights=%d", port->ip_srights);
- printf(", sorights=%d\n", port->ip_sorights);
- iprintf("nsrequest=0x%x", port->ip_nsrequest);
- printf(", pdrequest=0x%x", port->ip_pdrequest);
- printf(", dnrequests=0x%x\n", port->ip_dnrequests);
- iprintf("pset=0x%x", port->ip_pset);
- printf(", seqno=%d", port->ip_seqno);
- printf(", msgcount=%d", port->ip_msgcount);
- printf(", qlimit=%d\n", port->ip_qlimit);
- iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base);
- printf(", rcvrs=0x%x", port->ip_messages.imq_threads.ithq_base);
- printf(", sndrs=0x%x", port->ip_blocked.ithq_base);
- printf(", kobj=0x%x\n", port->ip_kobject);
- iprintf("protected_payload=%p\n", (void *) port->ip_protected_payload);
- indent -= 2;
- }
- #endif /* MACH_KDB */
|