1 /*
2 * Mach Operating System
3 * Copyright (c) 1991,1992 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: ipc_transit.c,v $
29 * Revision 2.6 92/03/10 16:28:25 jsb
30 * Merged in norma branch changes as of NORMA_MK7.
31 * [92/03/09 12:50:27 jsb]
32 *
33 * Revision 2.5.2.4 92/02/21 11:24:59 jsb
34 * Norma_ipc_send_{port,soright,sright,rright} now return uids.
35 * Use new norma_ipc_remove_try routine.
36 * [92/02/20 17:13:19 jsb]
37 *
38 * Changed reference counting in norma_ipc_send_*_dest, now that they
39 * are called only after successful acknowledgement of message.
40 * Removed calls to db_show_all_uids, now that 'show all uids' exists.
41 * Added rearm of ip_nsrequest in norma_ipc_notify_no_senders.
42 * [92/02/18 08:58:56 jsb]
43 *
44 * Added checks for losing all send rights without stransit becoming zero,
45 * in which case we must generate a no-local-senders notification.
46 * [92/02/16 15:22:29 jsb]
47 *
48 * Revision 2.5.2.3 92/01/21 21:52:42 jsb
49 * More de-linting.
50 * [92/01/17 11:42:23 jsb]
51 *
52 * De-linted.
53 * [92/01/16 22:16:55 jsb]
54 *
55 * Revision 2.5.2.2 92/01/09 18:45:59 jsb
56 * Use quieter debugging printf when queue limit exceeded.
57 * [92/01/09 16:13:18 jsb]
58 *
59 * Use remote_host_priv() instead of norma_get_special_port().
60 * Use new routines norma_unset_special_port and {local,remote}_special.
61 * [92/01/04 18:20:55 jsb]
62 *
63 * Revision 2.5.2.1 92/01/03 16:38:04 jsb
64 * Use ipc_port_release instead of ip_release to allow port deallocation.
65 * Added ip_reference to norma_ipc_send_migrating_dest so that
66 * norma_ipc_send can always release a reference.
67 * Changed ndproxy macros to nsproxy.
68 * [91/12/31 21:34:24 jsb]
69 *
70 * Added code to use norma_port_tabled function, which separates the
71 * issues of whether a port has a uid and whether it is accessible
72 * via norma_port_lookup.
73 * Added a hard panic on a failed lookup in norma_ipc_no_local_senders.
74 * Test for and ignore special uids in norma_ipc_notify_no_senders.
75 * [91/12/31 17:14:19 jsb]
76 *
77 * Added code to destroy active principal when no remote refs are left.
78 * Added forwarding and absorbtion code to norma_ipc_no_local_senders.
79 * Declared norma_ipc_no_local_senders void; changed returns accordingly.
80 * Split norma_ipc_send_no_local_senders from norma_ipc_notify_no_senders.
81 * Changed printfs to debugging printfs.
82 * [91/12/31 11:57:31 jsb]
83 *
84 * Changes for IP_NORMA_REQUEST macros being renamed to ip_ndproxy{,m,p}.
85 * [91/12/30 10:18:08 jsb]
86 *
87 * Test for proxies in norma_ipc_port_destroy.
88 * [91/12/29 21:24:18 jsb]
89 *
90 * Distinguish between dead and unfound ports in norma_ipc_receive_dest.
91 * [91/12/29 15:58:20 jsb]
92 *
93 * Use IP_NORMA_NSREQUEST macros. Release proxies in all cases.
94 * Simulate copyin in receive_migrating_dest to account for
95 * migrated messages.
96 * [91/12/28 18:05:27 jsb]
97 *
98 * Added source_node parameter to norma_ipc_receive_port so that it can
99 * be passed to norma_ipc_receive_rright.
100 * [91/12/27 21:33:34 jsb]
101 *
102 * Norma_ipc_send_migrating_dest now expects a proxy.
103 * [91/12/27 17:00:19 jsb]
104 *
105 * Added norma_ipc_{send,receive}_migrating_dest routines.
106 * Removed migrated parameter from norma_ipc_receive_dest.
107 * Removed migration/retarget debugging code.
108 * [91/12/26 20:45:44 jsb]
109 *
110 * Replaced token-based implementation with stransit_request based one.
111 * Added norma_ipc_send_dest. Removed norma_port_wire hack.
112 * Moved norma_port_list routines to norma/ipc_list.c. Corrected log.
113 * [91/12/24 14:23:14 jsb]
114 *
115 * Revision 2.5 91/12/14 14:35:10 jsb
116 * Removed ipc_fields.h hack.
117 *
118 * Revision 2.4 91/12/13 14:09:42 jsb
119 * Changed printfs to debugging printfs.
120 *
121 * Revision 2.3 91/11/19 09:41:28 rvb
122 * Added norma_port_wire hack, to avoid reference counting bug.
123 * Added code to test receive right migration.
124 * [91/11/00 jsb]
125 *
126 * Revision 2.2 91/11/14 16:46:18 rpd
127 * Created.
128 */
129 /*
130 * File: norma/ipc_transit.c
131 * Author: Joseph S. Barrera III
132 * Date: 1991
133 *
134 * Functions for movement of rights between nodes, excluding receive
135 * rights (for which see norma/ipc_migrate.c).
136 */
137
138 #include <ipc/ipc_port.h>
139 #include <ipc/ipc_space.h>
140 #include <kern/host.h>
141 #include <norma/ipc_node.h>
142
143 extern ipc_port_t local_special();
144 extern ipc_port_t remote_special();
145 extern ipc_port_t remote_host_priv();
146 extern unsigned long norma_new_uid();
147
148 extern unsigned long norma_ipc_send_port();
149 extern unsigned long norma_ipc_send_soright();
150 extern unsigned long norma_ipc_send_sright();
151 extern unsigned long norma_ipc_send_rright();
152 extern void norma_ipc_send_dest();
153 extern void norma_ipc_send_migrating_dest();
154
155 extern ipc_port_t norma_ipc_receive_port();
156 extern ipc_port_t norma_ipc_receive_sright();
157 extern ipc_port_t norma_ipc_receive_soright();
158 extern ipc_port_t norma_ipc_receive_rright();
159 extern kern_return_t norma_ipc_receive_dest();
160 extern kern_return_t norma_ipc_receive_migrating_dest();
161
162 extern ipc_port_t norma_port_lookup();
163 extern ipc_port_t norma_port_lookup_locked();
164 extern void norma_port_insert();
165 extern void norma_port_remove();
166 extern void norma_port_remove_locked();
167
168 /*
169 * Note: no decisions should be made based on who you think the receiver
170 * will be, since it might not be who you think it will be.
171 * (Receive rights move.) The only exception is norma_ipc_send_dest.
172 * For example, if you have a send right and you are sending it to
173 * who you think the receiver is, XXX finish this comment
174 */
175
176 /*
177 * Called when a port right of any flavor is sent to another node.
178 * Port must be unlocked. Port may be a local or proxy port.
179 */
180 unsigned long
181 norma_ipc_send_port(port, type_name)
182 ipc_port_t port;
183 mach_msg_type_name_t type_name;
184 {
185 unsigned long uid;
186
187 ip_lock(port);
188 if (type_name == MACH_MSG_TYPE_PORT_SEND_ONCE) {
189 uid = norma_ipc_send_soright(port);
190 } else if (type_name == MACH_MSG_TYPE_PORT_SEND) {
191 uid = norma_ipc_send_sright(port);
192 } else if (type_name == MACH_MSG_TYPE_PORT_RECEIVE) {
193 uid = norma_ipc_send_rright(port);
194 } else {
195 panic("norma_ipc_send_port: bad type %d\n", type_name);
196 }
197 ip_unlock(port);
198 return uid;
199 }
200
201 /*
202 * Called whenever a send right is sent to another node.
203 * Port must be locked. Port may be a local or proxy port.
204 */
205 unsigned long
206 norma_ipc_send_sright(port)
207 ipc_port_t port;
208 {
209 unsigned long uid;
210
211 /*
212 * Port must be active.
213 */
214 assert(ip_active(port));
215
216 if (port->ip_norma_is_proxy) {
217 /*
218 * A proxy must always have a nonpositive stransit,
219 * so that total stransit is no greater than the
220 * principal's stransit.
221 *
222 * Indeed, a proxy must have a negative stransit as
223 * long as it holds send rights, so that the principal
224 * can trust its stransit (in the face of proxies
225 * sending send rights to it).
226 * If we're here, then we must have send rights.
227 */
228 mumble("norma_ipc_send_sright: sender sending sright\n");
229 assert(port->ip_srights > 0);
230 assert(port->ip_norma_stransit < 0);
231 if (port->ip_srights == 1) {
232 /*
233 * We are about to lose all send rights.
234 * It is therefore okay to let stransit
235 * drop to zero, as an implicit no-local-senders
236 * notification. (If stransit does not drop
237 * to zero, then we have to do an explicit
238 * no-local-senders notification.) This works
239 * since either we are sending to the receiver,
240 * who will be able to figure it out (since
241 * he will decr stransit accordingly), or
242 * we are not sending to receiver, in which
243 * case no-senders isn't true.
244 */
245 } else {
246 /*
247 * We will still retain some send rights,
248 * and must therefore keep stransit negative.
249 */
250 assert(port->ip_srights > 1);
251 assert(port->ip_norma_stransit <= -1);
252 if (port->ip_norma_stransit == -1) {
253 norma_ipc_stransit_wait(port);
254 }
255 }
256 port->ip_norma_stransit++;
257 assert(port->ip_norma_stransit <= 0);
258
259 /*
260 * Save uid
261 */
262 uid = port->ip_norma_uid;
263
264 /*
265 * Release send right. If this is the last send right,
266 * and stransit is not zero, then we must generate
267 * an explicit no-local-senders notification.
268 */
269 if (port->ip_srights == 1 && port->ip_norma_stransit != 0) {
270 ipc_port_release_send(port);
271 } else {
272 port->ip_srights--;
273 ipc_port_release(port);
274 }
275 } else {
276 /*
277 * The principal can always immediately increase stransit.
278 * He does not release send right here, but rather when
279 * stransit comes back via no-local-senders notification.
280 * This allows the principal's send right count to be an
281 * upper bound on the true number of send rights in the
282 * system, which among other things keeps the a lack of
283 * senders on the principal's node from triggering a
284 * premature no-senders notification.
285 */
286 assert(port->ip_norma_stransit >= 0);
287 port->ip_norma_stransit++;
288
289 /*
290 * If this port has never been exported, assign it a uid
291 * and place it on the norma port list.
292 */
293 if ((uid = port->ip_norma_uid) == 0) {
294 uid = port->ip_norma_uid = norma_new_uid();
295 norma_port_insert(port);
296 } else if (! norma_port_tabled(port)) {
297 norma_port_insert(port);
298 }
299 assert(! port->ip_norma_is_proxy);
300 }
301 assert(uid != 0);
302 return uid;
303 }
304
305 /*
306 * Called whenever a send-once right is sent to another node.
307 * Port must be locked. Port must be a local port.
308 */
309 unsigned long
310 norma_ipc_send_soright(port)
311 ipc_port_t port;
312 {
313 unsigned long uid;
314
315 /*
316 * Port must be active.
317 */
318 assert(ip_active(port));
319
320 if (port->ip_norma_is_proxy) {
321 /*
322 * Save uid
323 */
324 uid = port->ip_norma_uid;
325
326 /*
327 * A proxy releases the send-once right.
328 * This case occurs when moving a send-once right
329 * to another node.
330 */
331 port->ip_sorights--;
332 ipc_port_release(port);
333 norma_port_remove_try(port);
334 } else {
335 /*
336 * The principal does not release send-once right here, but
337 * rather when the send-once right is used as a destination
338 * (of either a reply or a send-once notification).
339 * This allows the principal's send-once right count to
340 * be an accurate count of the true number of send-once
341 * rights in the system.
342 */
343 port->ip_norma_sotransit++;
344
345 /*
346 * If this port has never been exported, assign it a uid
347 * and place it on the norma port list.
348 */
349 if ((uid = port->ip_norma_uid) == 0) {
350 uid = port->ip_norma_uid = norma_new_uid();
351 norma_port_insert(port);
352 } else if (! norma_port_tabled(port)) {
353 norma_port_insert(port);
354 }
355 assert(! port->ip_norma_is_proxy);
356 }
357 assert(uid != 0);
358 return uid;
359 }
360
361 void
362 norma_ipc_send_dest(port, type_name)
363 ipc_port_t port;
364 mach_msg_type_name_t type_name;
365 {
366 /*
367 * ipc_kmsg_copyin_header asserts that this is so.
368 */
369 assert(type_name == MACH_MSG_TYPE_PORT_SEND ||
370 type_name == MACH_MSG_TYPE_PORT_SEND_ONCE);
371
372 /*
373 * This port must be a proxy!
374 */
375 assert(port->ip_norma_uid != 0);
376 assert(port->ip_norma_is_proxy);
377
378 /*
379 * This is different from norma_ipc_send_port, because we know
380 * we are sending to receiver. We just need to undo refcount
381 * changes performed by copyin; we don't need to modify
382 * stransit, and neither does the receiver. However, if we lose
383 * all send rights here (e.g., from a move_send destination),
384 * then we must generate an explicit no-local-senders notification.
385 */
386 if (type_name == MACH_MSG_TYPE_PORT_SEND) {
387 if (port->ip_srights == 1) {
388 printf1("norma_ipc_send_dest.s: release %x\n", port);
389 ipc_port_release_send(port);
390 printf1("norma_ipc_send_dest.s: released %x\n", port);
391 } else {
392 assert(port->ip_srights > 1);
393 ip_release(port);
394 port->ip_srights--;
395 }
396 } else {
397 ip_release(port);
398 port->ip_sorights--;
399 norma_port_remove_try(port);
400 }
401 }
402
403 void
404 norma_ipc_send_migrating_dest(port)
405 ipc_port_t port;
406 {
407 /*
408 * This port must be a proxy with a uid.
409 */
410 assert(port->ip_norma_uid != 0);
411 assert(port->ip_norma_is_proxy);
412 assert(port->ip_norma_dest_node != node_self());
413 }
414
415 /*
416 * Called when a port right of any flavor is received from another node.
417 */
418 ipc_port_t
419 norma_ipc_receive_port(uid, type_name, source_node)
420 unsigned long uid;
421 mach_msg_type_name_t type_name;
422 unsigned long source_node;
423 {
424 /*
425 * A null uid maps to a null port.
426 */
427 if (uid == 0) {
428 return IP_NULL;
429 }
430
431 if (type_name == MACH_MSG_TYPE_PORT_SEND_ONCE) {
432 return norma_ipc_receive_soright(uid);
433 } else if (type_name == MACH_MSG_TYPE_PORT_SEND) {
434 return norma_ipc_receive_sright(uid);
435 } else if (type_name == MACH_MSG_TYPE_PORT_RECEIVE) {
436 return norma_ipc_receive_rright(uid, source_node);
437 } else {
438 panic("norma_ipc_receive_port: bad type %d\n", type_name);
439 return IP_NULL;
440 }
441 }
442
443 /*
444 * Find or create proxy for given uid, and add a send right reference.
445 */
446 ipc_port_t
447 norma_ipc_receive_sright(uid)
448 unsigned long uid;
449 {
450 ipc_port_t port;
451
452 /*
453 * Try to find the port.
454 */
455 port = norma_port_lookup(uid);
456
457 /*
458 * If we don't have a port, then we must create a proxy.
459 */
460 if (port == IP_NULL) {
461 assert(IP_NORMA_NODE(uid) != node_self());
462 port = ipc_port_alloc_special(ipc_space_remote);
463 if (port == IP_NULL) {
464 panic("receive_sright: ipc_port_alloc_special");
465 }
466 port->ip_nsrequest = ip_nsproxym(port);
467 port->ip_norma_stransit = -1;
468 port->ip_srights = 1;
469 port->ip_norma_uid = uid;
470 port->ip_norma_dest_node = IP_NORMA_NODE(uid);
471 port->ip_norma_is_proxy = TRUE;
472 norma_port_insert(port);
473 return port;
474 }
475
476 /*
477 * Is it a proxy?
478 */
479 if (port->ip_norma_is_proxy) {
480 /*
481 * Just adjust srights and stransit.
482 */
483 assert(port->ip_nsrequest != IP_NULL);
484 assert(ip_nsproxyp(port->ip_nsrequest));
485 assert(ip_active(port)); /* XXX How could it be otherwise? */
486 port->ip_srights++;
487 port->ip_norma_stransit--;
488 ip_reference(port);
489 return port;
490 }
491
492 /*
493 * It is a principal. Is it dead?
494 */
495 if (! ip_active(port)) {
496 printf1("norma_ipc_receive_sright: dead port %x\n", uid);
497 /*
498 * Adjust stransit, since sender used up an stransit in
499 * sending to us. This may enable us to free the port.
500 * Consume immediately the sright associated with stransit.
501 */
502 assert(port->ip_norma_stransit > 0);
503 assert(port->ip_srights > 0);
504 port->ip_norma_stransit--;
505 port->ip_srights--;
506 norma_port_remove_try(port);
507 return IP_DEAD;
508 }
509
510 /*
511 * It is a living principal.
512 * Decrement stransit, since sender incremented stransit,
513 * since it could not be sure that we were still the principal.
514 * Consume sright that was associated with stransit.
515 */
516 assert(port->ip_srights > 0);
517 assert(port->ip_norma_stransit > 0);
518 port->ip_norma_stransit--;
519 return port;
520 }
521
522 /*
523 * Find or create proxy for given uid, and add a send-once right reference.
524 */
525 ipc_port_t
526 norma_ipc_receive_soright(uid)
527 unsigned long uid;
528 {
529 ipc_port_t port;
530
531 /*
532 * Try to find the port.
533 */
534 port = norma_port_lookup(uid);
535
536 /*
537 * If we don't have a port, then we must create a proxy.
538 */
539 if (port == IP_NULL) {
540 assert(IP_NORMA_NODE(uid) != node_self());
541 port = ipc_port_alloc_special(ipc_space_remote);
542 if (port == IP_NULL) {
543 panic("receive_soright: ipc_port_alloc_special");
544 }
545 port->ip_nsrequest = ip_nsproxym(port);
546 port->ip_sorights = 1;
547 port->ip_norma_uid = uid;
548 port->ip_norma_dest_node = IP_NORMA_NODE(uid);
549 port->ip_norma_is_proxy = TRUE;
550 norma_port_insert(port);
551 return port;
552 }
553
554 /*
555 * Is it a proxy?
556 */
557 if (port->ip_norma_is_proxy) {
558 /*
559 * Just adjust sorights.
560 * We increment, because we are inheriting a remote count.
561 */
562 assert(port->ip_nsrequest != IP_NULL);
563 assert(ip_nsproxyp(port->ip_nsrequest));
564 assert(ip_active(port)); /* XXX How could it be otherwise? */
565 port->ip_sorights++;
566 ip_reference(port);
567 return port;
568 }
569
570 /*
571 * It is a principal. Is it dead?
572 */
573 if (! ip_active(port)) {
574 /*
575 * We decrement sorights, because it has come home.
576 */
577 printf1("norma_ipc_receive_soright: dead port %x\n", uid);
578 port->ip_sorights--;
579 port->ip_norma_sotransit--;
580 assert((long) port->ip_sorights >= 0);
581 assert(port->ip_norma_sotransit >= 0);
582 norma_port_remove_try(port);
583 return IP_DEAD;
584 }
585
586 /*
587 * It is a living principal.
588 * Leave sorights alone; we consume the one we left for remote node.
589 */
590 port->ip_norma_sotransit--;
591 assert((long) port->ip_sorights >= 0);
592 assert(port->ip_norma_sotransit >= 0);
593 ip_reference(port);
594 return port;
595 }
596
597 /*
598 * Find destination port for given uid.
599 * XXX need to make safe to call from interrupt level!
600 * XXX perhaps make_send/sonce should be deferred? (XXX no longer make_send)
601 */
602 kern_return_t
603 norma_ipc_receive_dest(uid, type_name, remote_port)
604 unsigned long uid;
605 mach_msg_type_name_t type_name;
606 ipc_port_t *remote_port;
607 {
608 ipc_port_t port;
609
610 /*
611 * This can only be some flavor of send or send-once right.
612 */
613 assert(type_name == MACH_MSG_TYPE_PORT_SEND ||
614 type_name == MACH_MSG_TYPE_PORT_SEND_ONCE);
615
616 /*
617 * Handle the special port case.
618 */
619 if (IP_NORMA_SPECIAL(uid)) {
620 if (IP_NORMA_NODE(uid) == node_self()) {
621 *remote_port = local_special(IP_NORMA_LID(uid));
622 } else {
623 *remote_port = remote_special(IP_NORMA_NODE(uid),
624 IP_NORMA_LID(uid));
625 }
626 return KERN_SUCCESS;
627 }
628
629 /*
630 * Find associated port.
631 */
632 port = norma_port_lookup_locked(uid);
633 if (port == IP_NULL) {
634 /*
635 * Must be an invalid uid; otherwise, we would have
636 * found something with norma_port_lookup.
637 */
638 panic("norma_ipc_receive_dest: invalid uid %x!\n", uid);
639 return KERN_INVALID_NAME;
640 }
641
642 /*
643 * Check to see whether we are the correct receiver for this message.
644 * If not, try to say where the correct receiver would be.
645 */
646 if (port->ip_norma_is_proxy) {
647 /*
648 * Tell sender to use new node.
649 */
650 printf1("norma_ipc_receive_dest: migrated dest %d -> %d\n",
651 node_self(), port->ip_norma_dest_node);
652 * (unsigned long *) remote_port = port->ip_norma_dest_node;
653 return KERN_NOT_RECEIVER;
654 }
655
656 /*
657 * We have a local principal. Make sure it is active.
658 */
659 if (! ip_active(port)) {
660 /*
661 * If it is not active, it is a dead port, kept alive
662 * by remote send and send-once references.
663 */
664 printf1("norma_ipc_receive_dest: dead port %x\n", uid);
665 if (type_name == MACH_MSG_TYPE_PORT_SEND_ONCE) {
666 assert(port->ip_sorights > 0);
667 port->ip_norma_sotransit--;
668 port->ip_sorights--;
669 if (port->ip_sorights == 0 &&
670 port->ip_norma_stransit == 0) {
671 /*
672 * The last outside send/send-once reference
673 * has been consumed; release port.
674 */
675 printf1("norma_ipc_receive_dest: send_once: ");
676 printf1("releasing port %x\n", port);
677 norma_port_remove_locked(port);
678 }
679 }
680 return KERN_INVALID_RIGHT;
681 }
682
683 /*
684 * Check queue limit.
685 */
686 #if 1
687 /*
688 * There are locking issues here that need to be adressed.
689 * In the meantime, don't even bother looking at ip_msgcount.
690 */
691 #else
692 if (port->ip_msgcount >= port->ip_qlimit) {
693 mumble("norma_ipc_receive_dest: queue=%d >= limit=%d uid=%x\n",
694 port->ip_msgcount, port->ip_qlimit, uid);
695 /*
696 * XXX
697 * Should tell sender to block, and remember to wake him up.
698 */
699 }
700 #endif
701
702 /*
703 * Return port. Simulate copyin.
704 */
705 if (type_name == MACH_MSG_TYPE_PORT_SEND) {
706 /*
707 * Create a send right reference.
708 */
709 ip_reference(port);
710 port->ip_srights++;
711 } else {
712 /*
713 * Consume a preexisting send-once reference,
714 * created when send-once right was sent to current sender.
715 */
716 assert(type_name == MACH_MSG_TYPE_PORT_SEND_ONCE);
717 }
718 *remote_port = port;
719 return KERN_SUCCESS;
720 }
721
722 norma_ipc_receive_migrating_dest(uid, type_name, remote_port)
723 unsigned long uid;
724 mach_msg_type_name_t type_name;
725 ipc_port_t *remote_port;
726 {
727 ipc_port_t port;
728
729 assert(! IP_NORMA_SPECIAL(uid));
730
731 /*
732 * Find associated port.
733 * It must be a proxy with an atrium.
734 */
735 port = norma_port_lookup_locked(uid);
736 if (port == IP_NULL) {
737 panic("norma_ipc_receive_migrating_dest: invalid uid %x!\n",
738 uid);
739 return KERN_INVALID_RIGHT;
740 }
741 if (! port->ip_norma_is_proxy) {
742 panic("norma_ipc_receive_migrating_dest: %x not proxy!\n",
743 uid);
744 return KERN_INVALID_RIGHT;
745 }
746 if (port->ip_norma_atrium == IP_NULL) {
747 panic("norma_ipc_receive_migrating_dest: %x not migrating!\n",
748 uid);
749 return KERN_INVALID_RIGHT;
750 }
751 assert(ip_active(port));
752
753 /*
754 * Return port. Simulate copyin.
755 */
756 if (type_name == MACH_MSG_TYPE_PORT_SEND) {
757 /*
758 * Create a send right reference.
759 */
760 ip_reference(port);
761 port->ip_srights++;
762 } else {
763 /*
764 * Consume a preexisting send-once reference,
765 * created when send-once right was sent to current sender.
766 */
767 assert(type_name == MACH_MSG_TYPE_PORT_SEND_ONCE);
768 }
769 *remote_port = port;
770 return KERN_SUCCESS;
771 }
772
773 /*
774 * Called from ipc_port_destroy.
775 */
776 norma_ipc_port_destroy(port)
777 ipc_port_t port;
778 {
779 /*
780 * Don't bother with non-norma-affected ports.
781 */
782 #if 0
783 if (port->ip_norma_uid == 0) {
784 return;
785 }
786 #else
787 /*
788 * A port can be tabled but not have a uid
789 * if external refcounts dropped to zero.
790 */
791 if (! norma_port_tabled(port)) {
792 return;
793 }
794 #endif
795
796 /*
797 * Don't deal with proxies here. The only way that this could be
798 * a proxy is if norma_ipc_proxy_destroy called ipc_port_destroy,
799 * in which case we'll let norma_ipc_proxy_destroy deal with it.
800 *
801 * Proxies are destroyed in this module, either in send_dest
802 * or send_soright for send-once rights, or in
803 * norma_ipc_notify_no_local_senders for send rights.
804 */
805 if (port->ip_norma_is_proxy) {
806 return;
807 }
808
809 /*
810 * This routine cannot be called on a migrating port, since
811 * the receive right is held by the kernel.
812 * However, it's hard for us to tell if a port is migrating,
813 * and thus hard to assert anything here.
814 */
815
816 /*
817 * If this is a special port (principal), remove it from the
818 * special port table. Note that it may be in more than one slot.
819 * Release send right reference for each slot.
820 *
821 * No-senders is never true as long as a port is in the list,
822 * since the list holds a send right for each occurance of the
823 * port in the list.
824 */
825 if (port->ip_norma_is_special) {
826 norma_unset_special_port(port);
827 assert(port->ip_norma_is_special == FALSE);
828 }
829
830 /*
831 * Remove the port from the norma port list, but
832 * only if there are no outstanding references.
833 * If there are, we will release the port when they
834 * are all used up. We detect this when we receive
835 * a (dead) send-once right or when we receive
836 * a no_local_senders notification.
837 *
838 * If we destroyed the port immediately despite
839 * outstanding references, someone might send us
840 * a send right to it, and we would blindly create
841 * a proxy instead of realizing that the port was dead.
842 *
843 * Now of course, we should also at that point notice
844 * that IP_NORMA_NODE(uid) is node_self() and that we
845 * should have some knowledge of the port. We might be
846 * able to take advantage of this fact.
847 *
848 * Normally, we can check ip_srights intead of ip_norma_stransit.
849 * However, we are about to forcibly trash all local srights...
850 * urg. see ipc_port_destroy for ordering to see what I mean.
851 * XXX fix this comment
852 */
853 norma_port_remove_try(port);
854 /* XXX port->ip_norma_uid = 0; ??? used to do this if we removed it */
855 }
856
857 void
858 norma_ipc_send_no_local_senders(dest_node, uid, stransit)
859 unsigned long dest_node;
860 unsigned long uid;
861 int stransit;
862 {
863 kern_return_t kr;
864
865 if (IP_NORMA_SPECIAL(uid)) {
866 return;
867 }
868 kr = r_norma_ipc_no_local_senders(remote_host_priv(dest_node), uid,
869 stransit);
870 if (kr != KERN_SUCCESS) {
871 panic("norma_ipc_notify_no_senders: no_local_senders: %d/%x\n",
872 kr, kr);
873 }
874 }
875
876 void
877 norma_ipc_notify_no_senders(port)
878 ipc_port_t port;
879 {
880 unsigned long dest_node;
881 unsigned long uid;
882 int stransit;
883
884 assert(port->ip_nsrequest == IP_NULL);
885 assert(port->ip_norma_is_proxy);
886 assert(port->ip_norma_stransit < 0);
887 uid = port->ip_norma_uid;
888 dest_node = port->ip_norma_dest_node;
889 stransit = -port->ip_norma_stransit;
890 port->ip_norma_stransit = 0;
891 printf1("notify no_senders(0x%x:%x) s=0 so=%d\n",
892 port, uid, port->ip_sorights);
893 assert(port->ip_srights == 0);
894
895 /*
896 * There are no local send rights...
897 * if there are also no local send-once rights, then destroy
898 * the proxy; otherwise, rearm nsrequest in case this proxy
899 * acquires send rights in the future.
900 */
901 if (port->ip_sorights == 0) {
902 norma_port_remove(port);
903 } else {
904 port->ip_nsrequest = ip_nsproxym(port);
905 }
906 if (IP_NORMA_SPECIAL(uid)) {
907 /*
908 * Don't generate no-senders notifications for special uids.
909 * Remote node wouldn't even know what to do with one.
910 */
911 printf1("norma_ipc_notify_no_senders: special port %x\n", uid);
912 return;
913 }
914 norma_ipc_send_no_local_senders(dest_node, uid, stransit);
915 }
916
917 void
918 norma_ipc_no_local_senders(host_priv, uid, stransit)
919 host_t host_priv;
920 unsigned long uid;
921 int stransit;
922 {
923 ipc_port_t port;
924
925 if (host_priv == HOST_NULL) {
926 fret("norma_ipc_no_local_senders: invalid host\n");
927 return;
928 }
929 port = norma_port_lookup(uid);
930 if (port == IP_NULL) {
931 if (IP_NORMA_NODE(uid) == node_self()) {
932 /*
933 * XXX
934 * This can happen if this node rebooted.
935 * Otherwise, it is supposed to have saved
936 * a port for forwarding purposes.
937 */
938 printf("norma_ipc_no_local_senders: failed lookup!\n");
939 panic("uid %x stransit %d\n", uid, stransit);
940 } else {
941 fret("norma_ipc_no_local_senders: trying node %d\n",
942 IP_NORMA_NODE(uid));
943 norma_ipc_send_no_local_senders(IP_NORMA_NODE(uid),
944 uid, stransit);
945 }
946 return;
947 }
948 printf1("norma_ipc_no_local_senders(uid=%x port=0x%x stransit=%d)\n",
949 uid, port, stransit);
950 if (port->ip_norma_is_proxy) {
951 /*
952 * Instead of forwarding, we simply absorb the stransit.
953 */
954 fret("norma_ipc_no_local_senders: absorb(%d) %x!\n",
955 stransit, port);
956 assert(port->ip_srights > 0 || port->ip_sorights > 0);
957 port->ip_norma_stransit -= stransit;
958 return;
959 }
960 assert(stransit > 0);
961 assert(port->ip_norma_stransit >= stransit);
962 assert(port->ip_srights >= stransit);
963 port->ip_norma_stransit -= stransit;
964 port->ip_srights -= (stransit - 1);
965 port->ip_references -= (stransit - 1);
966 if (ip_active(port)) {
967 ipc_port_release_send(port);
968 } else {
969 port->ip_srights--;
970 port->ip_references--;
971 }
972 norma_port_remove_try(port);
973 }
974
975 /*
976 * An obvious improvement here would be to somehow, in the initial
977 * sending of send rights from receiver to sender, pass along some
978 * initial negative stransit. The current scheme is a lose for send
979 * rights that are used as capabilities, i.e., passed in the body
980 * of the message instead of being used as a destination.
981 */
982 kern_return_t
983 norma_ipc_stransit_wait(port)
984 ipc_port_t port;
985 {
986 kern_return_t kr;
987 int stransit;
988
989 kr = r_norma_ipc_stransit_request(remote_host_priv(port->
990 ip_norma_dest_node),
991 port->ip_norma_uid, &stransit);
992 if (kr != KERN_SUCCESS) {
993 panic("norma_ipc_stransit_wait: stransit_request: %d/%x\n",
994 kr, kr);
995 return;
996 }
997 mumble("ip_stransit_wait: stransit += %d\n", stransit);
998 port->ip_norma_stransit -= stransit;
999 }
1000
1001 kern_return_t
1002 norma_ipc_stransit_request(host_priv, uid, stransitp)
1003 host_t host_priv;
1004 unsigned long uid;
1005 int *stransitp;
1006 {
1007 ipc_port_t port;
1008
1009 mumble("norma_ipc_stransit_request: called\n");
1010 if (host_priv == HOST_NULL) {
1011 fret("norma_ipc_stransit_request: invalid host\n");
1012 return KERN_INVALID_HOST;
1013 }
1014 port = norma_port_lookup(uid);
1015 if (port == IP_NULL) {
1016 fret("norma_ipc_stransit_request: failed lookup %x\n",
1017 uid);
1018 return KERN_INVALID_NAME;
1019 }
1020 if (port->ip_norma_is_proxy) {
1021 /* XXX should probably forward to current principal */
1022 fret("norma_ipc_stransit_request: is proxy\n");
1023 return KERN_INVALID_RIGHT;
1024 }
1025 if (! ip_active(port)) {
1026 fret("norma_ipc_stransit_request: is not active\n");
1027 return KERN_FAILURE;
1028 }
1029 *stransitp = 10000;
1030 port->ip_norma_stransit += *stransitp;
1031 port->ip_srights += *stransitp;
1032 port->ip_references += *stransitp;
1033 mumble("norma_ipc_stransit_request: success\n");
1034 return KERN_SUCCESS;
1035 }
Cache object: 0a365309cbd06344cd6927fc1f0ca744
|