|
|
1.1 root 1: /*
2: * Mach Operating System
3: * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
4: * Copyright (c) 1993,1994 The University of Utah and
5: * the Computer Systems Laboratory (CSL).
6: * All rights reserved.
7: *
8: * Permission to use, copy, modify and distribute this software and its
9: * documentation is hereby granted, provided that both the copyright
10: * notice and this permission notice appear in all copies of the
11: * software, derivative works or modified versions, and any portions
12: * thereof, and that both notices appear in supporting documentation.
13: *
14: * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
15: * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
16: * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
17: * THIS SOFTWARE.
18: *
19: * Carnegie Mellon requests users of this software to return to
20: *
21: * Software Distribution Coordinator or [email protected]
22: * School of Computer Science
23: * Carnegie Mellon University
24: * Pittsburgh PA 15213-3890
25: *
26: * any improvements or extensions that they make and grant Carnegie Mellon
27: * the rights to redistribute these changes.
28: */
29: /*
30: * File: ipc/ipc_port.c
31: * Author: Rich Draves
32: * Date: 1989
33: *
34: * Functions to manipulate IPC ports.
35: */
36:
37: #include <mach_ipc_compat.h>
38:
39: #include <mach/port.h>
40: #include <mach/kern_return.h>
41: #include <kern/lock.h>
42: #include <kern/ipc_sched.h>
43: #include <kern/ipc_kobject.h>
44: #include <ipc/ipc_entry.h>
45: #include <ipc/ipc_space.h>
46: #include <ipc/ipc_object.h>
47: #include <ipc/ipc_port.h>
48: #include <ipc/ipc_pset.h>
49: #include <ipc/ipc_thread.h>
50: #include <ipc/ipc_mqueue.h>
51: #include <ipc/ipc_notify.h>
52: #if NORMA_IPC
53: #include <norma/ipc_node.h>
1.1.1.2 ! root 54: #endif /* NORMA_IPC */
1.1 root 55:
56:
57:
58: decl_simple_lock_data(, ipc_port_multiple_lock_data)
59:
60: decl_simple_lock_data(, ipc_port_timestamp_lock_data)
61: ipc_port_timestamp_t ipc_port_timestamp_data;
62:
63: /*
64: * Routine: ipc_port_timestamp
65: * Purpose:
66: * Retrieve a timestamp value.
67: */
68:
69: ipc_port_timestamp_t
70: ipc_port_timestamp(void)
71: {
72: ipc_port_timestamp_t timestamp;
73:
74: ipc_port_timestamp_lock();
75: timestamp = ipc_port_timestamp_data++;
76: ipc_port_timestamp_unlock();
77:
78: return timestamp;
79: }
80:
81: /*
82: * Routine: ipc_port_dnrequest
83: * Purpose:
84: * Try to allocate a dead-name request slot.
85: * If successful, returns the request index.
86: * Otherwise returns zero.
87: * Conditions:
88: * The port is locked and active.
89: * Returns:
90: * KERN_SUCCESS A request index was found.
91: * KERN_NO_SPACE No index allocated.
92: */
93:
94: kern_return_t
95: ipc_port_dnrequest(port, name, soright, indexp)
96: ipc_port_t port;
97: mach_port_t name;
98: ipc_port_t soright;
99: ipc_port_request_index_t *indexp;
100: {
101: ipc_port_request_t ipr, table;
102: ipc_port_request_index_t index;
103:
104: assert(ip_active(port));
105: assert(name != MACH_PORT_NULL);
106: assert(soright != IP_NULL);
107:
108: table = port->ip_dnrequests;
109: if (table == IPR_NULL)
110: return KERN_NO_SPACE;
111:
112: index = table->ipr_next;
113: if (index == 0)
114: return KERN_NO_SPACE;
115:
116: ipr = &table[index];
117: assert(ipr->ipr_name == MACH_PORT_NULL);
118:
119: table->ipr_next = ipr->ipr_next;
120: ipr->ipr_name = name;
121: ipr->ipr_soright = soright;
122:
123: *indexp = index;
124: return KERN_SUCCESS;
125: }
126:
127: /*
128: * Routine: ipc_port_dngrow
129: * Purpose:
130: * Grow a port's table of dead-name requests.
131: * Conditions:
132: * The port must be locked and active.
133: * Nothing else locked; will allocate memory.
134: * Upon return the port is unlocked.
135: * Returns:
136: * KERN_SUCCESS Grew the table.
137: * KERN_SUCCESS Somebody else grew the table.
138: * KERN_SUCCESS The port died.
139: * KERN_RESOURCE_SHORTAGE Couldn't allocate new table.
140: */
141:
142: kern_return_t
143: ipc_port_dngrow(port)
144: ipc_port_t port;
145: {
146: ipc_table_size_t its;
147: ipc_port_request_t otable, ntable;
148:
149: assert(ip_active(port));
150:
151: otable = port->ip_dnrequests;
152: if (otable == IPR_NULL)
153: its = &ipc_table_dnrequests[0];
154: else
155: its = otable->ipr_size + 1;
156:
157: ip_reference(port);
158: ip_unlock(port);
159:
160: if ((its->its_size == 0) ||
161: ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) {
162: ipc_port_release(port);
163: return KERN_RESOURCE_SHORTAGE;
164: }
165:
166: ip_lock(port);
167: ip_release(port);
168:
169: /*
170: * Check that port is still active and that nobody else
171: * has slipped in and grown the table on us. Note that
172: * just checking port->ip_dnrequests == otable isn't
173: * sufficient; must check ipr_size.
174: */
175:
176: if (ip_active(port) &&
177: (port->ip_dnrequests == otable) &&
178: ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
179: ipc_table_size_t oits = 0; /* '=0' to shut up lint */
180: ipc_table_elems_t osize, nsize;
181: ipc_port_request_index_t free, i;
182:
183: /* copy old table to new table */
184:
185: if (otable != IPR_NULL) {
186: oits = otable->ipr_size;
187: osize = oits->its_size;
188: free = otable->ipr_next;
189:
190: bcopy((char *)(otable + 1), (char *)(ntable + 1),
191: (osize - 1) * sizeof(struct ipc_port_request));
192: } else {
193: osize = 1;
194: free = 0;
195: }
196:
197: nsize = its->its_size;
198: assert(nsize > osize);
199:
200: /* add new elements to the new table's free list */
201:
202: for (i = osize; i < nsize; i++) {
203: ipc_port_request_t ipr = &ntable[i];
204:
205: ipr->ipr_name = MACH_PORT_NULL;
206: ipr->ipr_next = free;
207: free = i;
208: }
209:
210: ntable->ipr_next = free;
211: ntable->ipr_size = its;
212: port->ip_dnrequests = ntable;
213: ip_unlock(port);
214:
215: if (otable != IPR_NULL)
216: it_dnrequests_free(oits, otable);
217: } else {
218: ip_check_unlock(port);
219: it_dnrequests_free(its, ntable);
220: }
221:
222: return KERN_SUCCESS;
223: }
1.1.1.2 ! root 224:
1.1 root 225: /*
226: * Routine: ipc_port_dncancel
227: * Purpose:
228: * Cancel a dead-name request and return the send-once right.
229: * Conditions:
230: * The port must locked and active.
231: */
232:
233: ipc_port_t
234: ipc_port_dncancel(
235: ipc_port_t port,
236: mach_port_t name,
237: ipc_port_request_index_t index)
238: {
239: ipc_port_request_t ipr, table;
240: ipc_port_t dnrequest;
241:
242: assert(ip_active(port));
243: assert(name != MACH_PORT_NULL);
244: assert(index != 0);
245:
246: table = port->ip_dnrequests;
247: assert(table != IPR_NULL);
248:
249: ipr = &table[index];
250: dnrequest = ipr->ipr_soright;
251: assert(ipr->ipr_name == name);
252:
253: /* return ipr to the free list inside the table */
254:
255: ipr->ipr_name = MACH_PORT_NULL;
256: ipr->ipr_next = table->ipr_next;
257: table->ipr_next = index;
258:
259: return dnrequest;
260: }
261:
262: /*
263: * Routine: ipc_port_pdrequest
264: * Purpose:
265: * Make a port-deleted request, returning the
266: * previously registered send-once right.
267: * Just cancels the previous request if notify is IP_NULL.
268: * Conditions:
269: * The port is locked and active. It is unlocked.
270: * Consumes a ref for notify (if non-null), and
271: * returns previous with a ref (if non-null).
272: */
273:
274: void
275: ipc_port_pdrequest(
276: ipc_port_t port,
277: ipc_port_t notify,
278: ipc_port_t *previousp)
279: {
280: ipc_port_t previous;
281:
282: assert(ip_active(port));
283:
284: previous = port->ip_pdrequest;
285: port->ip_pdrequest = notify;
286: ip_unlock(port);
287:
288: *previousp = previous;
289: }
290:
291: /*
292: * Routine: ipc_port_nsrequest
293: * Purpose:
294: * Make a no-senders request, returning the
295: * previously registered send-once right.
296: * Just cancels the previous request if notify is IP_NULL.
297: * Conditions:
298: * The port is locked and active. It is unlocked.
299: * Consumes a ref for notify (if non-null), and
300: * returns previous with a ref (if non-null).
301: */
302:
303: void
304: ipc_port_nsrequest(
305: ipc_port_t port,
306: mach_port_mscount_t sync,
307: ipc_port_t notify,
308: ipc_port_t *previousp)
309: {
310: ipc_port_t previous;
311: mach_port_mscount_t mscount;
312:
313: assert(ip_active(port));
314:
315: previous = port->ip_nsrequest;
316: mscount = port->ip_mscount;
317:
318: if ((port->ip_srights == 0) &&
319: (sync <= mscount) &&
320: (notify != IP_NULL)) {
321: port->ip_nsrequest = IP_NULL;
322: ip_unlock(port);
323: ipc_notify_no_senders(notify, mscount);
324: } else {
325: port->ip_nsrequest = notify;
326: ip_unlock(port);
327: }
328:
329: *previousp = previous;
330: }
331:
332: /*
333: * Routine: ipc_port_set_qlimit
334: * Purpose:
335: * Changes a port's queue limit; the maximum number
336: * of messages which may be queued to the port.
337: * Conditions:
338: * The port is locked and active.
339: */
340:
341: void
342: ipc_port_set_qlimit(
343: ipc_port_t port,
344: mach_port_msgcount_t qlimit)
345: {
346: assert(ip_active(port));
347:
348: /* wake up senders allowed by the new qlimit */
349:
350: if (qlimit > port->ip_qlimit) {
351: mach_port_msgcount_t i, wakeup;
352:
353: /* caution: wakeup, qlimit are unsigned */
354:
355: wakeup = qlimit - port->ip_qlimit;
356:
357: for (i = 0; i < wakeup; i++) {
358: ipc_thread_t th;
359:
360: th = ipc_thread_dequeue(&port->ip_blocked);
361: if (th == ITH_NULL)
362: break;
363:
364: th->ith_state = MACH_MSG_SUCCESS;
365: thread_go(th);
366: }
367: }
368:
369: port->ip_qlimit = qlimit;
370: }
371:
372: /*
373: * Routine: ipc_port_lock_mqueue
374: * Purpose:
375: * Locks and returns the message queue that the port is using.
376: * The message queue may be in the port or in its port set.
377: * Conditions:
378: * The port is locked and active.
379: * Port set, message queue locks may be taken.
380: */
381:
382: ipc_mqueue_t
383: ipc_port_lock_mqueue(port)
384: ipc_port_t port;
385: {
386: if (port->ip_pset != IPS_NULL) {
387: ipc_pset_t pset = port->ip_pset;
388:
389: ips_lock(pset);
390: if (ips_active(pset)) {
391: imq_lock(&pset->ips_messages);
392: ips_unlock(pset);
393: return &pset->ips_messages;
394: }
395:
396: ipc_pset_remove(pset, port);
397: ips_check_unlock(pset);
398: }
399:
400: imq_lock(&port->ip_messages);
401: return &port->ip_messages;
402: }
403:
404: /*
405: * Routine: ipc_port_set_seqno
406: * Purpose:
407: * Changes a port's sequence number.
408: * Conditions:
409: * The port is locked and active.
410: * Port set, message queue locks may be taken.
411: */
412:
413: void
414: ipc_port_set_seqno(port, seqno)
415: ipc_port_t port;
416: mach_port_seqno_t seqno;
417: {
418: ipc_mqueue_t mqueue;
419:
420: mqueue = ipc_port_lock_mqueue(port);
421: port->ip_seqno = seqno;
422: imq_unlock(mqueue);
423: }
424:
425: /*
426: * Routine: ipc_port_clear_receiver
427: * Purpose:
428: * Prepares a receive right for transmission/destruction.
429: * Conditions:
430: * The port is locked and active.
431: */
432:
433: void
434: ipc_port_clear_receiver(
435: ipc_port_t port)
436: {
437: ipc_pset_t pset;
438:
439: assert(ip_active(port));
440:
441: pset = port->ip_pset;
442: if (pset != IPS_NULL) {
443: /* No threads receiving from port, but must remove from set. */
444:
445: ips_lock(pset);
446: ipc_pset_remove(pset, port);
447: ips_check_unlock(pset);
448: } else {
449: /* Else, wake up all receivers, indicating why. */
450:
451: imq_lock(&port->ip_messages);
452: ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_DIED);
453: imq_unlock(&port->ip_messages);
454: }
455:
456: ipc_port_set_mscount(port, 0);
457: imq_lock(&port->ip_messages);
458: port->ip_seqno = 0;
459: imq_unlock(&port->ip_messages);
460: }
461:
462: /*
463: * Routine: ipc_port_init
464: * Purpose:
465: * Initializes a newly-allocated port.
466: * Doesn't touch the ip_object fields.
467: */
468:
469: void
470: ipc_port_init(
471: ipc_port_t port,
472: ipc_space_t space,
473: mach_port_t name)
474: {
475: /* port->ip_kobject doesn't have to be initialized */
476:
477: ipc_target_init(&port->ip_target, name);
478:
479: port->ip_receiver = space;
480:
481: port->ip_mscount = 0;
482: port->ip_srights = 0;
483: port->ip_sorights = 0;
484:
485: port->ip_nsrequest = IP_NULL;
486: port->ip_pdrequest = IP_NULL;
487: port->ip_dnrequests = IPR_NULL;
488:
489: port->ip_pset = IPS_NULL;
490: port->ip_cur_target = &port->ip_target;
491: port->ip_seqno = 0;
492: port->ip_msgcount = 0;
493: port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT;
494:
495: #if NORMA_IPC
496: port->ip_norma_uid = 0;
497: port->ip_norma_dest_node = 0;
498: port->ip_norma_stransit = 0;
499: port->ip_norma_sotransit = 0;
500: port->ip_norma_xmm_object_refs = 0;
501: port->ip_norma_is_proxy = FALSE;
502: port->ip_norma_is_special = FALSE;
503: port->ip_norma_atrium = IP_NULL;
504: port->ip_norma_queue_next = port;
505: port->ip_norma_xmm_object = IP_NULL;
506: port->ip_norma_next = port;
507: port->ip_norma_spare1 = 0L;
508: port->ip_norma_spare2 = 0L;
509: port->ip_norma_spare3 = 0L;
510: port->ip_norma_spare4 = 0L;
1.1.1.2 ! root 511: #endif /* NORMA_IPC */
1.1 root 512:
513: ipc_mqueue_init(&port->ip_messages);
514: ipc_thread_queue_init(&port->ip_blocked);
515: }
516:
517: /*
518: * Routine: ipc_port_alloc
519: * Purpose:
520: * Allocate a port.
521: * Conditions:
522: * Nothing locked. If successful, the port is returned
523: * locked. (The caller doesn't have a reference.)
524: * Returns:
525: * KERN_SUCCESS The port is allocated.
526: * KERN_INVALID_TASK The space is dead.
527: * KERN_NO_SPACE No room for an entry in the space.
528: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
529: */
530:
531: kern_return_t
532: ipc_port_alloc(
533: ipc_space_t space,
534: mach_port_t *namep,
535: ipc_port_t *portp)
536: {
537: ipc_port_t port;
538: mach_port_t name;
539: kern_return_t kr;
540:
541: kr = ipc_object_alloc(space, IOT_PORT,
542: MACH_PORT_TYPE_RECEIVE, 0,
543: &name, (ipc_object_t *) &port);
544: if (kr != KERN_SUCCESS)
545: return kr;
546:
547: /* port is locked */
548:
549: ipc_port_init(port, space, name);
550:
551: *namep = name;
552: *portp = port;
553:
554: return KERN_SUCCESS;
555: }
556:
557: /*
558: * Routine: ipc_port_alloc_name
559: * Purpose:
560: * Allocate a port, with a specific name.
561: * Conditions:
562: * Nothing locked. If successful, the port is returned
563: * locked. (The caller doesn't have a reference.)
564: * Returns:
565: * KERN_SUCCESS The port is allocated.
566: * KERN_INVALID_TASK The space is dead.
567: * KERN_NAME_EXISTS The name already denotes a right.
568: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
569: */
570:
571: kern_return_t
572: ipc_port_alloc_name(
573: ipc_space_t space,
574: mach_port_t name,
575: ipc_port_t *portp)
576: {
577: ipc_port_t port;
578: kern_return_t kr;
579:
580: kr = ipc_object_alloc_name(space, IOT_PORT,
581: MACH_PORT_TYPE_RECEIVE, 0,
582: name, (ipc_object_t *) &port);
583: if (kr != KERN_SUCCESS)
584: return kr;
585: /* port is locked */
586:
587: ipc_port_init(port, space, name);
588:
589: *portp = port;
590: return KERN_SUCCESS;
591: }
592:
593: #if MACH_IPC_COMPAT
594: /*
595: * Routine: ipc_port_delete_compat
596: * Purpose:
597: * Find and destroy a compat entry for a dead port.
598: * If successful, generate a port-deleted notification.
599: * Conditions:
600: * Nothing locked; the port is dead.
601: * Frees a ref for the space.
602: */
603:
604: void
605: ipc_port_delete_compat(port, space, name)
606: ipc_port_t port;
607: ipc_space_t space;
608: mach_port_t name;
609: {
610: ipc_entry_t entry;
611: kern_return_t kr;
612:
613: assert(!ip_active(port));
614:
615: kr = ipc_right_lookup_write(space, name, &entry);
616: if (kr == KERN_SUCCESS) {
617: ipc_port_t sright;
618:
619: /* space is write-locked and active */
620:
621: if ((ipc_port_t) entry->ie_object == port) {
622: assert(entry->ie_bits & IE_BITS_COMPAT);
623:
624: sright = ipc_space_make_notify(space);
625:
626: kr = ipc_right_destroy(space, name, entry);
627: /* space is unlocked */
628: assert(kr == KERN_INVALID_NAME);
629: } else {
630: is_write_unlock(space);
631: sright = IP_NULL;
632: }
633:
634: if (IP_VALID(sright))
635: ipc_notify_port_deleted_compat(sright, name);
636: }
637:
638: is_release(space);
639: }
1.1.1.2 ! root 640: #endif /* MACH_IPC_COMPAT */
1.1 root 641:
642: /*
643: * Routine: ipc_port_destroy
644: * Purpose:
645: * Destroys a port. Cleans up queued messages.
646: *
647: * If the port has a backup, it doesn't get destroyed,
648: * but is sent in a port-destroyed notification to the backup.
649: * Conditions:
650: * The port is locked and alive; nothing else locked.
651: * The caller has a reference, which is consumed.
652: * Afterwards, the port is unlocked and dead.
653: */
654:
655: void
656: ipc_port_destroy(
657: ipc_port_t port)
658: {
659: ipc_port_t pdrequest, nsrequest;
660: ipc_mqueue_t mqueue;
661: ipc_kmsg_queue_t kmqueue;
662: ipc_kmsg_t kmsg;
663: ipc_thread_t sender;
664: ipc_port_request_t dnrequests;
665:
666: assert(ip_active(port));
667: /* port->ip_receiver_name is garbage */
668: /* port->ip_receiver/port->ip_destination is garbage */
669: assert(port->ip_pset == IPS_NULL);
670: assert(port->ip_mscount == 0);
671: assert(port->ip_seqno == 0);
672:
673: /* first check for a backup port */
674:
675: pdrequest = port->ip_pdrequest;
676: if (pdrequest != IP_NULL) {
677: /* we assume the ref for pdrequest */
678: port->ip_pdrequest = IP_NULL;
679:
680: /* make port be in limbo */
681: port->ip_receiver_name = MACH_PORT_NULL;
682: port->ip_destination = IP_NULL;
683: ip_unlock(port);
684:
685: #if MACH_IPC_COMPAT
686: /*
687: * pdrequest might actually be a send right instead
688: * of a send-once right, indicated by the low bit
689: * of the pointer value. If this is the case,
690: * we must use ipc_notify_port_destroyed_compat.
691: */
692:
693: if (ip_pdsendp(pdrequest)) {
694: ipc_port_t sright = ip_pdsend(pdrequest);
695:
696: if (!ipc_port_check_circularity(port, sright)) {
697: /* consumes our refs for port and sright */
698: ipc_notify_port_destroyed_compat(sright, port);
699: return;
700: } else {
701: /* consume sright and destroy port */
702: ipc_port_release_send(sright);
703: }
704: } else
1.1.1.2 ! root 705: #endif /* MACH_IPC_COMPAT */
1.1 root 706:
707: if (!ipc_port_check_circularity(port, pdrequest)) {
708: /* consumes our refs for port and pdrequest */
709: ipc_notify_port_destroyed(pdrequest, port);
710: return;
711: } else {
712: /* consume pdrequest and destroy port */
713: ipc_port_release_sonce(pdrequest);
714: }
715:
716: ip_lock(port);
717: assert(ip_active(port));
718: assert(port->ip_pset == IPS_NULL);
719: assert(port->ip_mscount == 0);
720: assert(port->ip_seqno == 0);
721: assert(port->ip_pdrequest == IP_NULL);
722: assert(port->ip_receiver_name == MACH_PORT_NULL);
723: assert(port->ip_destination == IP_NULL);
724:
725: /* fall through and destroy the port */
726: }
727:
728: #if NORMA_IPC
729: /*
730: * destroy any NORMA_IPC state associated with port
731: */
732: norma_ipc_port_destroy(port);
1.1.1.2 ! root 733: #endif /* NORMA_IPC */
1.1 root 734:
735: /*
736: * rouse all blocked senders
737: *
738: * This must be done with the port locked, because
739: * ipc_mqueue_send can play with the ip_blocked queue
740: * of a dead port.
741: */
742:
743: while ((sender = ipc_thread_dequeue(&port->ip_blocked)) != ITH_NULL) {
744: sender->ith_state = MACH_MSG_SUCCESS;
745: thread_go(sender);
746: }
747:
748: /* once port is dead, we don't need to keep it locked */
749:
750: port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
751: port->ip_timestamp = ipc_port_timestamp();
752: ip_unlock(port);
753:
754: /* throw away no-senders request */
755:
756: nsrequest = port->ip_nsrequest;
757: if (nsrequest != IP_NULL)
758: ipc_notify_send_once(nsrequest); /* consumes ref */
759:
760: /* destroy any queued messages */
761:
762: mqueue = &port->ip_messages;
763: imq_lock(mqueue);
764: assert(ipc_thread_queue_empty(&mqueue->imq_threads));
765: kmqueue = &mqueue->imq_messages;
766:
767: while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
768: imq_unlock(mqueue);
769:
770: assert(kmsg->ikm_header.msgh_remote_port ==
771: (mach_port_t) port);
772:
773: ipc_port_release(port);
774: kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
775: ipc_kmsg_destroy(kmsg);
776:
777: imq_lock(mqueue);
778: }
779:
780: imq_unlock(mqueue);
781:
782: /* generate dead-name notifications */
783:
784: dnrequests = port->ip_dnrequests;
785: if (dnrequests != IPR_NULL) {
786: ipc_table_size_t its = dnrequests->ipr_size;
787: ipc_table_elems_t size = its->its_size;
788: ipc_port_request_index_t index;
789:
790: for (index = 1; index < size; index++) {
791: ipc_port_request_t ipr = &dnrequests[index];
792: mach_port_t name = ipr->ipr_name;
793: ipc_port_t soright;
794:
795: if (name == MACH_PORT_NULL)
796: continue;
797:
798: soright = ipr->ipr_soright;
799: assert(soright != IP_NULL);
800:
801: #if MACH_IPC_COMPAT
802: if (ipr_spacep(soright)) {
803: ipc_port_delete_compat(port,
804: ipr_space(soright), name);
805: continue;
806: }
1.1.1.2 ! root 807: #endif /* MACH_IPC_COMPAT */
1.1 root 808:
809: ipc_notify_dead_name(soright, name);
810: }
811:
812: it_dnrequests_free(its, dnrequests);
813: }
814:
815: if (ip_kotype(port) != IKOT_NONE)
816: ipc_kobject_destroy(port);
817:
818: /* Common destruction for the IPC target. */
819: ipc_target_terminate(&port->ip_target);
820:
821: ipc_port_release(port); /* consume caller's ref */
822: }
823:
824: /*
825: * Routine: ipc_port_check_circularity
826: * Purpose:
827: * Check if queueing "port" in a message for "dest"
828: * would create a circular group of ports and messages.
829: *
830: * If no circularity (FALSE returned), then "port"
831: * is changed from "in limbo" to "in transit".
832: *
833: * That is, we want to set port->ip_destination == dest,
834: * but guaranteeing that this doesn't create a circle
835: * port->ip_destination->ip_destination->... == port
836: * Conditions:
837: * No ports locked. References held for "port" and "dest".
838: */
839:
840: boolean_t
841: ipc_port_check_circularity(
842: ipc_port_t port,
843: ipc_port_t dest)
844: {
845: ipc_port_t base;
846:
847: assert(port != IP_NULL);
848: assert(dest != IP_NULL);
849:
850: if (port == dest)
851: return TRUE;
852: base = dest;
853:
854: /*
855: * First try a quick check that can run in parallel.
856: * No circularity if dest is not in transit.
857: */
858:
859: ip_lock(port);
860: if (ip_lock_try(dest)) {
861: if (!ip_active(dest) ||
862: (dest->ip_receiver_name != MACH_PORT_NULL) ||
863: (dest->ip_destination == IP_NULL))
864: goto not_circular;
865:
866: /* dest is in transit; further checking necessary */
867:
868: ip_unlock(dest);
869: }
870: ip_unlock(port);
871:
872: ipc_port_multiple_lock(); /* massive serialization */
873:
874: /*
875: * Search for the end of the chain (a port not in transit),
876: * acquiring locks along the way.
877: */
878:
879: for (;;) {
880: ip_lock(base);
881:
882: if (!ip_active(base) ||
883: (base->ip_receiver_name != MACH_PORT_NULL) ||
884: (base->ip_destination == IP_NULL))
885: break;
886:
887: base = base->ip_destination;
888: }
889:
890: /* all ports in chain from dest to base, inclusive, are locked */
891:
892: if (port == base) {
893: /* circularity detected! */
894:
895: ipc_port_multiple_unlock();
896:
897: /* port (== base) is in limbo */
898:
899: assert(ip_active(port));
900: assert(port->ip_receiver_name == MACH_PORT_NULL);
901: assert(port->ip_destination == IP_NULL);
902:
903: while (dest != IP_NULL) {
904: ipc_port_t next;
905:
906: /* dest is in transit or in limbo */
907:
908: assert(ip_active(dest));
909: assert(dest->ip_receiver_name == MACH_PORT_NULL);
910:
911: next = dest->ip_destination;
912: ip_unlock(dest);
913: dest = next;
914: }
915:
916: return TRUE;
917: }
918:
919: /*
920: * The guarantee: lock port while the entire chain is locked.
921: * Once port is locked, we can take a reference to dest,
922: * add port to the chain, and unlock everything.
923: */
924:
925: ip_lock(port);
926: ipc_port_multiple_unlock();
927:
928: not_circular:
929:
930: /* port is in limbo */
931:
932: assert(ip_active(port));
933: assert(port->ip_receiver_name == MACH_PORT_NULL);
934: assert(port->ip_destination == IP_NULL);
935:
936: ip_reference(dest);
937: port->ip_destination = dest;
938:
939: /* now unlock chain */
940:
941: while (port != base) {
942: ipc_port_t next;
943:
944: /* port is in transit */
945:
946: assert(ip_active(port));
947: assert(port->ip_receiver_name == MACH_PORT_NULL);
948: assert(port->ip_destination != IP_NULL);
949:
950: next = port->ip_destination;
951: ip_unlock(port);
952: port = next;
953: }
954:
955: /* base is not in transit */
956:
957: assert(!ip_active(base) ||
958: (base->ip_receiver_name != MACH_PORT_NULL) ||
959: (base->ip_destination == IP_NULL));
960: ip_unlock(base);
961:
962: return FALSE;
963: }
964:
965: /*
966: * Routine: ipc_port_lookup_notify
967: * Purpose:
968: * Make a send-once notify port from a receive right.
969: * Returns IP_NULL if name doesn't denote a receive right.
970: * Conditions:
971: * The space must be locked (read or write) and active.
972: */
973:
974: ipc_port_t
975: ipc_port_lookup_notify(
976: ipc_space_t space,
977: mach_port_t name)
978: {
979: ipc_port_t port;
980: ipc_entry_t entry;
981:
982: assert(space->is_active);
983:
984: entry = ipc_entry_lookup(space, name);
985: if (entry == IE_NULL)
986: return IP_NULL;
987:
988: if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
989: return IP_NULL;
990:
991: port = (ipc_port_t) entry->ie_object;
992: assert(port != IP_NULL);
993:
994: ip_lock(port);
995: assert(ip_active(port));
996: assert(port->ip_receiver_name == name);
997: assert(port->ip_receiver == space);
998:
999: ip_reference(port);
1000: port->ip_sorights++;
1001: ip_unlock(port);
1002:
1003: return port;
1004: }
1005:
1006: /*
1007: * Routine: ipc_port_make_send
1008: * Purpose:
1009: * Make a naked send right from a receive right.
1010: * Conditions:
1011: * The port is not locked but it is active.
1012: */
1013:
1014: ipc_port_t
1015: ipc_port_make_send(
1016: ipc_port_t port)
1017: {
1018: assert(IP_VALID(port));
1019:
1020: ip_lock(port);
1021: assert(ip_active(port));
1022: port->ip_mscount++;
1023: port->ip_srights++;
1024: ip_reference(port);
1025: ip_unlock(port);
1026:
1027: return port;
1028: }
1029:
1030: /*
1031: * Routine: ipc_port_copy_send
1032: * Purpose:
1033: * Make a naked send right from another naked send right.
1034: * IP_NULL -> IP_NULL
1035: * IP_DEAD -> IP_DEAD
1036: * dead port -> IP_DEAD
1037: * live port -> port + ref
1038: * Conditions:
1039: * Nothing locked except possibly a space.
1040: */
1041:
1042: ipc_port_t
1043: ipc_port_copy_send(
1044: ipc_port_t port)
1045: {
1046: ipc_port_t sright;
1047:
1048: if (!IP_VALID(port))
1049: return port;
1050:
1051: ip_lock(port);
1052: if (ip_active(port)) {
1053: assert(port->ip_srights > 0);
1054:
1055: ip_reference(port);
1056: port->ip_srights++;
1057: sright = port;
1058: } else
1059: sright = IP_DEAD;
1060: ip_unlock(port);
1061:
1062: return sright;
1063: }
1064:
1065: /*
1066: * Routine: ipc_port_copyout_send
1067: * Purpose:
1068: * Copyout a naked send right (possibly null/dead),
1069: * or if that fails, destroy the right.
1070: * Conditions:
1071: * Nothing locked.
1072: */
1073:
1074: mach_port_t
1075: ipc_port_copyout_send(
1076: ipc_port_t sright,
1077: ipc_space_t space)
1078: {
1079: mach_port_t name;
1080:
1081: if (IP_VALID(sright)) {
1082: kern_return_t kr;
1083:
1084: kr = ipc_object_copyout(space, (ipc_object_t) sright,
1085: MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
1086: if (kr != KERN_SUCCESS) {
1087: ipc_port_release_send(sright);
1088:
1089: if (kr == KERN_INVALID_CAPABILITY)
1090: name = MACH_PORT_DEAD;
1091: else
1092: name = MACH_PORT_NULL;
1093: }
1094: } else
1095: name = (mach_port_t) sright;
1096:
1097: return name;
1098: }
1099:
1100: /*
1101: * Routine: ipc_port_release_send
1102: * Purpose:
1103: * Release a (valid) naked send right.
1104: * Consumes a ref for the port.
1105: * Conditions:
1106: * Nothing locked.
1107: */
1108:
1109: void
1110: ipc_port_release_send(
1111: ipc_port_t port)
1112: {
1113: ipc_port_t nsrequest = IP_NULL;
1114: mach_port_mscount_t mscount;
1115:
1116: assert(IP_VALID(port));
1117:
1118: ip_lock(port);
1119: ip_release(port);
1120:
1121: if (!ip_active(port)) {
1122: ip_check_unlock(port);
1123: return;
1124: }
1125:
1126: assert(port->ip_srights > 0);
1127:
1128: if (--port->ip_srights == 0) {
1129: nsrequest = port->ip_nsrequest;
1130: if (nsrequest != IP_NULL) {
1131: port->ip_nsrequest = IP_NULL;
1132: mscount = port->ip_mscount;
1133: }
1134: }
1135:
1136: ip_unlock(port);
1137:
1138: if (nsrequest != IP_NULL)
1139: ipc_notify_no_senders(nsrequest, mscount);
1140: }
1141:
1142: /*
1143: * Routine: ipc_port_make_sonce
1144: * Purpose:
1145: * Make a naked send-once right from a receive right.
1146: * Conditions:
1147: * The port is not locked but it is active.
1148: */
1149:
1150: ipc_port_t
1151: ipc_port_make_sonce(
1152: ipc_port_t port)
1153: {
1154: assert(IP_VALID(port));
1155:
1156: ip_lock(port);
1157: assert(ip_active(port));
1158: port->ip_sorights++;
1159: ip_reference(port);
1160: ip_unlock(port);
1161:
1162: return port;
1163: }
1164:
1165: /*
1166: * Routine: ipc_port_release_sonce
1167: * Purpose:
1168: * Release a naked send-once right.
1169: * Consumes a ref for the port.
1170: *
1171: * In normal situations, this is never used.
1172: * Send-once rights are only consumed when
1173: * a message (possibly a send-once notification)
1174: * is sent to them.
1175: * Conditions:
1176: * Nothing locked except possibly a space.
1177: */
1178:
1179: void
1180: ipc_port_release_sonce(
1181: ipc_port_t port)
1182: {
1183: assert(IP_VALID(port));
1184:
1185: ip_lock(port);
1186:
1187: assert(port->ip_sorights > 0);
1188:
1189: port->ip_sorights--;
1190:
1191: ip_release(port);
1192:
1193: if (!ip_active(port)) {
1194: ip_check_unlock(port);
1195: return;
1196: }
1197:
1198: ip_unlock(port);
1199: }
1200:
1201: /*
1202: * Routine: ipc_port_release_receive
1203: * Purpose:
1204: * Release a naked (in limbo or in transit) receive right.
1205: * Consumes a ref for the port; destroys the port.
1206: * Conditions:
1207: * Nothing locked.
1208: */
1209:
1210: void
1211: ipc_port_release_receive(
1212: ipc_port_t port)
1213: {
1214: ipc_port_t dest;
1215:
1216: assert(IP_VALID(port));
1217:
1218: ip_lock(port);
1219: assert(ip_active(port));
1220: assert(port->ip_receiver_name == MACH_PORT_NULL);
1221: dest = port->ip_destination;
1222:
1223: ipc_port_destroy(port); /* consumes ref, unlocks */
1224:
1225: if (dest != IP_NULL)
1226: ipc_port_release(dest);
1227: }
1228:
1229: /*
1230: * Routine: ipc_port_alloc_special
1231: * Purpose:
1232: * Allocate a port in a special space.
1233: * The new port is returned with one ref.
1234: * If unsuccessful, IP_NULL is returned.
1235: * Conditions:
1236: * Nothing locked.
1237: */
1238:
1239: ipc_port_t
1240: ipc_port_alloc_special(space)
1241: ipc_space_t space;
1242: {
1243: #if NORMA_IPC
1244: #if i386
1245: int ret = (&ret)[2]; /* where we were called from */
1246: #else
1247: int ret = (int) ipc_port_alloc_special;
1248: #endif
1249: extern int input_msgh_id;
1.1.1.2 ! root 1250: #endif /* NORMA_IPC */
1.1 root 1251: ipc_port_t port;
1252:
1253: port = (ipc_port_t) io_alloc(IOT_PORT);
1254: if (port == IP_NULL)
1255: return IP_NULL;
1256:
1257: io_lock_init(&port->ip_object);
1258: port->ip_references = 1;
1259: port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
1260:
1261: /*
1262: * The actual values of ip_receiver_name aren't important,
1263: * as long as they are valid (not null/dead).
1264: *
1265: * Mach4: we set it to the internal port structure address
1266: * so we can always just pass on ip_receiver_name during
1267: * an rpc regardless of whether the destination is user or
1268: * kernel (i.e. no special-casing code for the kernel along
1269: * the fast rpc path).
1270: */
1271:
1272: ipc_port_init(port, space, (mach_port_t)port);
1273:
1274: #if NORMA_IPC
1275: port->ip_norma_spare1 = ret;
1276: port->ip_norma_spare2 = input_msgh_id;
1.1.1.2 ! root 1277: #endif /* NORMA_IPC */
1.1 root 1278: return port;
1279: }
1280:
1281: /*
1282: * Routine: ipc_port_dealloc_special
1283: * Purpose:
1284: * Deallocate a port in a special space.
1285: * Consumes one ref for the port.
1286: * Conditions:
1287: * Nothing locked.
1288: */
1289:
1290: void
1291: ipc_port_dealloc_special(
1292: ipc_port_t port,
1293: ipc_space_t space)
1294: {
1295: ip_lock(port);
1296: assert(ip_active(port));
1297: assert(port->ip_receiver_name != MACH_PORT_NULL);
1298: assert(port->ip_receiver == space);
1299:
1300: /*
1301: * We clear ip_receiver_name and ip_receiver to simplify
1302: * the ipc_space_kernel check in ipc_mqueue_send.
1303: */
1304:
1305: port->ip_receiver_name = MACH_PORT_NULL;
1306: port->ip_receiver = IS_NULL;
1307:
1308: /*
1309: * For ipc_space_kernel, all ipc_port_clear_receiver does
1310: * is clean things up for the assertions in ipc_port_destroy.
1311: * For ipc_space_reply, there might be a waiting receiver.
1312: */
1313:
1314: ipc_port_clear_receiver(port);
1315: ipc_port_destroy(port);
1316: }
1317:
1318: #if MACH_IPC_COMPAT
1319:
1320: /*
1321: * Routine: ipc_port_alloc_compat
1322: * Purpose:
1323: * Allocate a port.
1324: * Conditions:
1325: * Nothing locked. If successful, the port is returned
1326: * locked. (The caller doesn't have a reference.)
1327: *
1328: * Like ipc_port_alloc, except that the new entry
1329: * is IE_BITS_COMPAT.
1330: * Returns:
1331: * KERN_SUCCESS The port is allocated.
1332: * KERN_INVALID_TASK The space is dead.
1333: * KERN_NO_SPACE No room for an entry in the space.
1334: * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1335: */
1336:
1337: kern_return_t
1338: ipc_port_alloc_compat(space, namep, portp)
1339: ipc_space_t space;
1340: mach_port_t *namep;
1341: ipc_port_t *portp;
1342: {
1343: ipc_port_t port;
1344: ipc_entry_t entry;
1345: mach_port_t name;
1346: ipc_table_size_t its;
1347: ipc_port_request_t table;
1348: ipc_table_elems_t size;
1349: ipc_port_request_index_t free, i;
1350: kern_return_t kr;
1351:
1352: port = ip_alloc();
1353: if (port == IP_NULL)
1354: return KERN_RESOURCE_SHORTAGE;
1355:
1356: its = &ipc_table_dnrequests[0];
1357: table = it_dnrequests_alloc(its);
1358: if (table == IPR_NULL) {
1359: ip_free(port);
1360: return KERN_RESOURCE_SHORTAGE;
1361: }
1362:
1363: kr = ipc_entry_alloc(space, &name, &entry);
1364: if (kr != KERN_SUCCESS) {
1365: ip_free(port);
1366: it_dnrequests_free(its, table);
1367: return kr;
1368: }
1369: /* space is write-locked */
1370:
1371: entry->ie_object = (ipc_object_t) port;
1372: entry->ie_request = 1;
1373: entry->ie_bits |= IE_BITS_COMPAT|MACH_PORT_TYPE_RECEIVE;
1374:
1375: ip_lock_init(port);
1376: ip_lock(port);
1377: is_write_unlock(space);
1378:
1379: port->ip_references = 1; /* for entry, not caller */
1380: port->ip_bits = io_makebits(TRUE, IOT_PORT, 0);
1381:
1382: ipc_port_init(port, space, name);
1383:
1384: size = its->its_size;
1385: assert(size > 1);
1386: free = 0;
1387:
1388: for (i = 2; i < size; i++) {
1389: ipc_port_request_t ipr = &table[i];
1390:
1391: ipr->ipr_name = MACH_PORT_NULL;
1392: ipr->ipr_next = free;
1393: free = i;
1394: }
1395:
1396: table->ipr_next = free;
1397: table->ipr_size = its;
1398: port->ip_dnrequests = table;
1399:
1400: table[1].ipr_name = name;
1401: table[1].ipr_soright = ipr_spacem(space);
1402: is_reference(space);
1403:
1404: *namep = name;
1405: *portp = port;
1406: return KERN_SUCCESS;
1407: }
1408:
1409: /*
1410: * Routine: ipc_port_copyout_send_compat
1411: * Purpose:
1412: * Copyout a naked send right (possibly null/dead),
1413: * or if that fails, destroy the right.
1414: * Like ipc_port_copyout_send, except that if a
1415: * new translation is created it has the compat bit.
1416: * Conditions:
1417: * Nothing locked.
1418: */
1419:
1420: mach_port_t
1421: ipc_port_copyout_send_compat(sright, space)
1422: ipc_port_t sright;
1423: ipc_space_t space;
1424: {
1425: mach_port_t name;
1426:
1427: if (IP_VALID(sright)) {
1428: kern_return_t kr;
1429:
1430: kr = ipc_object_copyout_compat(space, (ipc_object_t) sright,
1431: MACH_MSG_TYPE_PORT_SEND, &name);
1432: if (kr != KERN_SUCCESS) {
1433: ipc_port_release_send(sright);
1434: name = MACH_PORT_NULL;
1435: }
1436: } else
1437: name = (mach_port_t) sright;
1438:
1439: return name;
1440: }
1441:
1442: /*
1443: * Routine: ipc_port_copyout_receiver
1444: * Purpose:
1445: * Copyout a port reference (possibly null)
1446: * by giving the caller his name for the port,
1447: * if he is the receiver.
1448: * Conditions:
1449: * Nothing locked. Consumes a ref for the port.
1450: */
1451:
1452: mach_port_t
1453: ipc_port_copyout_receiver(port, space)
1454: ipc_port_t port;
1455: ipc_space_t space;
1456: {
1457: mach_port_t name;
1458:
1459: if (!IP_VALID(port))
1460: return MACH_PORT_NULL;
1461:
1462: ip_lock(port);
1463: if (port->ip_receiver == space) {
1464: name = port->ip_receiver_name;
1465: assert(MACH_PORT_VALID(name));
1466: } else
1467: name = MACH_PORT_NULL;
1468:
1469: ip_release(port);
1470: ip_check_unlock(port);
1471:
1472: return name;
1473: }
1474:
1.1.1.2 ! root 1475: #endif /* MACH_IPC_COMPAT */
1.1 root 1476:
1477: #include <mach_kdb.h>
1478:
1479:
1480: #if MACH_KDB
1481: #define printf kdbprintf
1482:
1483: /*
1484: * Routine: ipc_port_print
1485: * Purpose:
1486: * Pretty-print a port for kdb.
1487: */
1488:
1489: void
1490: ipc_port_print(port)
1491: ipc_port_t port;
1492: {
1493: extern int indent;
1494:
1495: printf("port 0x%x\n", port);
1496:
1497: indent += 2;
1498:
1499: ipc_object_print(&port->ip_object);
1500: iprintf("receiver=0x%x", port->ip_receiver);
1501: printf(", receiver_name=0x%x\n", port->ip_receiver_name);
1502:
1503: iprintf("mscount=%d", port->ip_mscount);
1504: printf(", srights=%d", port->ip_srights);
1505: printf(", sorights=%d\n", port->ip_sorights);
1506:
1507: iprintf("nsrequest=0x%x", port->ip_nsrequest);
1508: printf(", pdrequest=0x%x", port->ip_pdrequest);
1509: printf(", dnrequests=0x%x\n", port->ip_dnrequests);
1510:
1511: iprintf("pset=0x%x", port->ip_pset);
1512: printf(", seqno=%d", port->ip_seqno);
1513: printf(", msgcount=%d", port->ip_msgcount);
1514: printf(", qlimit=%d\n", port->ip_qlimit);
1515:
1516: iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base);
1517: printf(", rcvrs=0x%x", port->ip_messages.imq_threads.ithq_base);
1518: printf(", sndrs=0x%x", port->ip_blocked.ithq_base);
1519: printf(", kobj=0x%x\n", port->ip_kobject);
1520:
1521: #if NORMA_IPC
1522: iprintf("norma_uid=%x", port->ip_norma_uid);
1523: printf(", dest_node=%d", port->ip_norma_dest_node);
1524: printf(", stransit=%d", port->ip_norma_stransit);
1525: printf(", xorefs=%d", port->ip_norma_xmm_object_refs);
1526: printf(", sotransit=%d\n", port->ip_norma_sotransit);
1527:
1528: iprintf("norma_is_proxy=%d", port->ip_norma_is_proxy);
1529: printf(", is_special=%d\n", port->ip_norma_is_special);
1530:
1531: iprintf("norma_atrium=0x%x", port->ip_norma_atrium);
1532: printf(", queue_next=0x%x", port->ip_norma_queue_next);
1533: printf(", xmm_object=0x%x", port->ip_norma_xmm_object);
1534: printf(", next=0x%x\n", port->ip_norma_next);
1535:
1536: iprintf("norma_spare1=0x%x", port->ip_norma_spare1);
1537: printf(", norma_spare2=0x%x", port->ip_norma_spare2);
1538: printf(", norma_spare3=0x%x", port->ip_norma_spare3);
1539: printf(", norma_spare4=0x%x\n", port->ip_norma_spare4);
1.1.1.2 ! root 1540: #endif /* NORMA_IPC */
1.1 root 1541:
1542: indent -=2;
1543: }
1544:
1.1.1.2 ! root 1545: #endif /* MACH_KDB */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.