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