1 /*
2 * Mach Operating System
3 * Copyright (c) 1991 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_migrate.c,v $
29 * Revision 2.6 92/03/10 16:27:43 jsb
30 * Merged in norma branch changes as of NORMA_MK7.
31 * [92/03/09 12:49:26 jsb]
32 *
33 * Revision 2.5.2.4 92/02/21 11:24:29 jsb
34 * Don't convert migrated kmsgs to network format.
35 * [92/02/21 09:06:25 jsb]
36 *
37 * Changed norma_ipc_send_rright to return uid.
38 * [92/02/20 17:14:09 jsb]
39 *
40 * Revision 2.5.2.3 92/01/21 21:51:25 jsb
41 * From dlb@osf.org: Fixed ip_kotype assertions and stransit logic.
42 * [92/01/17 14:39:09 jsb]
43 *
44 * More de-linting.
45 * [92/01/17 11:39:52 jsb]
46 *
47 * De-linted.
48 * [92/01/16 22:11:26 jsb]
49 *
50 * Revision 2.5.2.2 92/01/09 18:45:27 jsb
51 * Corrected setting of ip_norma_stransit for migrated-from port.
52 * [92/01/09 13:17:34 jsb]
53 *
54 * Use remote_host_priv() instead of norma_get_special_port().
55 * [92/01/04 18:24:21 jsb]
56 *
57 * Revision 2.5.2.1 92/01/03 16:37:33 jsb
58 * Use ipc_port_release instead of ip_release to allow port deallocation.
59 * Changed ndproxy macros to nsproxy.
60 * [91/12/31 21:35:52 jsb]
61 *
62 * Added code to use norma_port_tabled function.
63 * Added missing ip_reference when receiving a receive right.
64 * [91/12/31 17:19:11 jsb]
65 *
66 * Changed printfs to debugging printfs.
67 * Changes for IP_NORMA_REQUEST macros being renamed to ip_ndproxy{,m,p}.
68 * [91/12/31 12:20:36 jsb]
69 *
70 * Fixed reference counting. No-senders now works for migrated ports.
71 * [91/12/28 18:44:05 jsb]
72 *
73 * Added source_node parameter to norma_ipc_receive_rright so that
74 * we know exactly where to send norma_ipc_pull_receive request.
75 * [91/12/27 21:32:31 jsb]
76 *
77 * Norma_ipc_pull_receive now uses a temporary proxy for forwarded
78 * messages, since norma_ipc_send now uses port queue.
79 * Still need to wait for proxy to drain, and receive_rright still
80 * needs to not clobber any proxy-queued messages when moving over
81 * messages received on atrium.
82 * [91/12/27 17:07:10 jsb]
83 *
84 * A new implementation that migrates queued messages and port state
85 * (such as qlimit) when migrating receive rights. Contains reference
86 * counting workarounds; no-senders does not yet work on migrated ports.
87 * This implementation does not change the migrated port's uid.
88 * [91/12/26 18:23:25 jsb]
89 *
90 * Corrected log. Removed uses of obsolete ipc_port fields.
91 * Added a debugging printf in place of an assertion failure.
92 * [91/12/24 14:24:46 jsb]
93 *
94 * Revision 2.4 91/12/13 14:00:04 jsb
95 * Changed debugging printf routines.
96 *
97 * Revision 2.3 91/12/10 13:26:05 jsb
98 * Make sure a port has a uid before migrating its receive right.
99 * Set ip_receiver_name and ip_destination for migrated right.
100 * [91/12/10 11:28:53 jsb]
101 *
102 * Revision 2.2 91/11/14 16:45:56 rpd
103 * Created.
104 *
105 */
106 /*
107 * File: norma/ipc_migrate.c
108 * Author: Joseph S. Barrera III
109 * Date: 1991
110 *
111 * Functions for migration of receive rights between nodes.
112 */
113
114 #include <norma_vm.h>
115 #include <norma_ether.h>
116
117 #include <vm/vm_kern.h>
118 #include <mach/vm_param.h>
119 #include <mach/port.h>
120 #include <mach/message.h>
121 #include <kern/assert.h>
122 #include <kern/host.h>
123 #include <kern/sched_prim.h>
124 #include <kern/ipc_sched.h>
125 #include <kern/ipc_kobject.h>
126 #include <kern/zalloc.h>
127 #include <device/device_port.h>
128 #include <ipc/ipc_mqueue.h>
129 #include <ipc/ipc_thread.h>
130 #include <ipc/ipc_kmsg.h>
131 #include <ipc/ipc_port.h>
132 #include <ipc/ipc_pset.h>
133 #include <ipc/ipc_space.h>
134 #include <ipc/ipc_marequest.h>
135 #include <norma/ipc_node.h>
136
137 extern ipc_port_t norma_port_lookup();
138 extern ipc_port_t remote_host_priv();
139 extern unsigned long norma_new_uid();
140 extern void norma_port_insert();
141 extern void norma_port_remove();
142
143 /*
144 * Called whenever a receive right is sent to another node.
145 * Port must be locked. Port must be a local port.
146 */
147 unsigned long
148 norma_ipc_send_rright(port)
149 ipc_port_t port;
150 {
151 unsigned long uid;
152
153 /*
154 * If this port has never been exported (for either srights or
155 * sorights), assign it a uid and place it on the norma port list.
156 */
157 if (port->ip_norma_uid == 0) {
158 port->ip_norma_uid = norma_new_uid();
159 norma_port_insert(port);
160 } else if (! norma_port_tabled(port)) {
161 norma_port_insert(port);
162 }
163 assert(! port->ip_norma_is_proxy);
164 uid = port->ip_norma_uid;
165
166 /*
167 * The only reference after this will be one in the norma port table.
168 */
169 ipc_port_release(port);
170
171 /*
172 * For now, we continue to accept messages here.
173 * We turn into a proxy only when we have a dest_node to give senders,
174 * which will be sent by the receiver of this receive right.
175 * (We may not yet know where dest_node is, since the message
176 * carrying this receive right may be indirected...)
177 */
178 return uid;
179 }
180
181 /*
182 * When a node receives a migrated receive right, it sends this message
183 * to the source of the receive right. The source then starts sending
184 * queued messages until it runs out...
185 * Note of course that if this port is busy enough, the migration will
186 * never happen. Not easy to fix this, but it's not a correctness problem
187 * and I don't think it will be a problem in practice.
188 *
189 * This call is executed in a kserver thread context.
190 */
191 norma_ipc_pull_receive(host, uid, dest_node, stransit, sotransit, nsrequest,
192 pdrequest, dnrequest_list, dnrequest_count,
193 seqno, qlimit)
194 host_t host;
195 unsigned long uid;
196 unsigned long dest_node;
197 int *stransit;
198 int *sotransit;
199 ipc_port_t *nsrequest;
200 ipc_port_t *pdrequest;
201 ipc_port_t *dnrequest_list;
202 int *dnrequest_count;
203 int *seqno;
204 int *qlimit;
205 {
206 ipc_port_t port, proxy;
207 ipc_mqueue_t mqueue;
208 ipc_kmsg_queue_t kmqueue;
209 ipc_kmsg_t kmsg;
210 int proxy_stransit;
211
212 printf1("norma_ipc_pull_receive(uid=%x dest=%d)\n", uid, dest_node);
213 /*
214 * Guard against random bozos calling this routine.
215 */
216 if (host == HOST_NULL) {
217 return KERN_INVALID_ARGUMENT;
218 }
219
220 /*
221 * Find the port.
222 */
223 port = norma_port_lookup(uid);
224 assert(port != IP_NULL);
225 assert(! port->ip_norma_is_proxy);
226
227 /*
228 * Create a private proxy for forwarding messages via norma_ipc_send.
229 * XXX
230 * Need to wait for it to drain!
231 * XXXO
232 * Could move initial queue of messages from port to proxy
233 */
234 proxy = ipc_port_alloc_special(ipc_space_remote);
235 if (proxy == IP_NULL) {
236 panic("norma_ipc_pull_receive: ipc_port_alloc_special");
237 }
238 proxy->ip_norma_uid = uid;
239 proxy->ip_norma_dest_node = dest_node;
240 proxy->ip_norma_is_proxy = TRUE;
241
242 /*
243 * Migrate messages to destination.
244 * New messages may show up. We could turn
245 * them off, perhaps, by adjusting queue
246 * limit, but it's probably not worth it,
247 * and what do we do about SEND_ALWAYS?
248 *
249 * The lack of port locking is particularly conspicuous here.
250 */
251 printf1("norma_ipc_pull_receive: moving %d msgs\n", port->ip_msgcount);
252 mqueue = &port->ip_messages;
253 imq_lock(mqueue);
254 assert(ipc_thread_queue_empty(&mqueue->imq_threads));
255 kmqueue = &mqueue->imq_messages;
256 while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
257 imq_unlock(mqueue);
258 assert(kmsg->ikm_header.msgh_remote_port == (mach_port_t)port);
259
260 /*
261 * Undo norma_ipc_receive_dest so that we can compute
262 * local ip_srights below.
263 */
264 if (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits)
265 == MACH_MSG_TYPE_PORT_SEND) {
266 port->ip_srights--;
267 ipc_port_release(port);
268 }
269
270 /*
271 * Send the message.
272 */
273 kmsg->ikm_header.msgh_remote_port = (mach_port_t) proxy;
274 kmsg->ikm_header.msgh_bits |= MACH_MSGH_BITS_MIGRATED;
275
276 (void) norma_ipc_send(kmsg);
277 printf1("sent kmsg 0x%x\n", kmsg);
278 imq_lock(mqueue);
279 }
280 imq_unlock(mqueue);
281
282 /*
283 * Copy out port state.
284 * Some assertions came from ipc_port_clear_receiver.
285 */
286 assert(port->ip_mscount == 0);
287 assert(port->ip_seqno == 0);
288 assert((ip_kotype(port) == IKOT_NONE) ||
289 (ip_kotype(port) == IKOT_PAGER));
290 assert(port->ip_pset == IPS_NULL);
291 *nsrequest = port->ip_nsrequest;
292 *pdrequest = port->ip_pdrequest;
293 *dnrequest_list = (ipc_port_t) 0; /* XXX */
294 *dnrequest_count = 0; /* XXX */
295 *seqno = port->ip_seqno;
296 *qlimit = port->ip_qlimit;
297
298 /*
299 * If there are any local send rights, increment stransit by 1
300 * so that recipient of receiver right knows they exist.
301 *
302 * Note that any send rights associated with messages
303 * (as destinations) have already been accounted for above.
304 *
305 * The true number of local send rights is
306 * ip_srights - ip_norma_stransit.
307 */
308 assert(port->ip_srights >= port->ip_norma_stransit);
309 if (port->ip_srights - port->ip_norma_stransit > 0) {
310 proxy_stransit = 1;
311 } else {
312 proxy_stransit = 0;
313 }
314 *stransit = port->ip_norma_stransit + proxy_stransit;
315
316 /*
317 * Send count of non-local send-once rights.
318 *
319 * The true number of local send-once rights is
320 * ip_sorights - ip_norma_sotransit.
321 */
322 *sotransit = port->ip_norma_sotransit - port->ip_sorights;
323
324 /*
325 * Change port into a proxy.
326 */
327 assert((ip_kotype(port) == IKOT_NONE) ||
328 (ip_kotype(port) == IKOT_PAGER));
329 port->ip_receiver = ipc_space_remote;
330 port->ip_receiver_name = 1; /* name used by ipc_port_alloc_special */
331
332 port->ip_mscount = 0;
333 port->ip_srights -= port->ip_norma_stransit;
334 port->ip_sorights -= port->ip_norma_sotransit;
335 port->ip_references -= (port->ip_norma_stransit +
336 port->ip_norma_sotransit);
337
338 port->ip_nsrequest = ip_nsproxym(port);
339 port->ip_pdrequest = IP_NULL;
340 port->ip_dnrequests = IPR_NULL;
341
342 port->ip_pset = IPS_NULL;
343 port->ip_seqno = 0;
344 port->ip_msgcount = 0;
345 port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT;
346
347 port->ip_norma_stransit = -proxy_stransit;
348 port->ip_norma_sotransit = 0;
349 port->ip_norma_dest_node = dest_node;
350 port->ip_norma_is_proxy = TRUE;
351 port->ip_norma_is_special = FALSE;
352
353 /*
354 * XXX
355 * What do we do with:
356 * ip_blocked?
357 *
358 */
359
360 /*
361 * We could probably have done this earlier...
362 */
363 if (port->ip_srights == 0 && port->ip_sorights == 0) {
364 printf1("pull_receive: destroying 0x%x:%x\n",
365 port, port->ip_norma_uid);
366 norma_port_remove(port);
367 }
368
369 return KERN_SUCCESS;
370 }
371
372 /*
373 * Receive a receive right.
374 * We create a port and export it, giving the uid that we pick
375 * to the node that sent us the receive right. We enter a forwarding
376 * from its uid to ours in our forwarding table. We will first receive
377 * all the messages queued at its port, then it will stop queueing
378 * messages and instead tell nodes to send messages to us.
379 *
380 * What happens if we had a proxy for this uid?
381 * Note that we still have to send messages to the old uid
382 * until the real migration happens.
383 *
384 * This routine is always called from a thread context.
385 */
386 ipc_port_t
387 norma_ipc_receive_rright(uid, source_node)
388 unsigned long uid;
389 unsigned long source_node;
390 {
391 kern_return_t kr;
392 ipc_port_t port, atrium;
393 ipc_port_t *dnrequest_list = (ipc_port_t *) 0;
394 int dnrequest_count = 0;
395 long stransit, sotransit;
396
397 mumble("receive_rright %x\n", uid);
398 assert(source_node != node_self());
399 /*
400 * Find or allocate proxy for this uid.
401 * XXX
402 * What keeps this port from being deallocated?
403 * Perhaps we should hack up another sright/reference.
404 */
405 port = norma_port_lookup(uid);
406 if (port == IP_NULL) {
407 mumble("receive_rright: new proxy\n");
408 port = ipc_port_alloc_special(ipc_space_remote);
409 if (port == IP_NULL) {
410 panic("receive_rright: ipc_port_alloc_special");
411 }
412 port->ip_nsrequest = ip_nsproxym(port);
413 port->ip_norma_uid = uid;
414 port->ip_norma_dest_node = source_node;
415 port->ip_norma_is_proxy = TRUE;
416 norma_port_insert(port);
417 } else {
418 mumble("receive_rright: old proxy\n");
419 assert(ip_active(port));
420 assert(port->ip_norma_is_proxy);
421 }
422
423 /*
424 * I believe there is one port reference associated with
425 * the receive right itself. This ip_reference matches the
426 * ipc_port_release in norma_ipc_send_rright.
427 */
428 ip_reference(port);
429
430
431 /*
432 * Allocate an atrium port to hold forwarded messages.
433 * This gives us one queue for forwarded messages and another
434 * for proxy usage.
435 */
436 atrium = ip_alloc();
437 printf1("ip_alloc:atrium=0x%x\n", atrium);
438 if (atrium == IP_NULL) {
439 panic("receive_rright: ip_alloc");
440 }
441 atrium->ip_receiver = ipc_space_remote;
442 atrium->ip_msgcount = 0;
443 ipc_kmsg_queue_init(&atrium->ip_messages.imq_messages);
444 port->ip_norma_atrium = atrium;
445
446 /*
447 * Pull over the receive right (and all of its messages).
448 */
449 printf1("about to call r_norma_ipc_pull_receive\n");
450 kr = r_norma_ipc_pull_receive(remote_host_priv(source_node),
451 uid,
452 node_self(),
453 &stransit,
454 &sotransit,
455 &port->ip_nsrequest,
456 &port->ip_pdrequest,
457 &dnrequest_list,
458 &dnrequest_count,
459 &port->ip_seqno,
460 &port->ip_qlimit);
461 printf1("r_norma_ipc_pull_receive returns %d/%x\n", kr, kr);
462 assert(kr == KERN_SUCCESS);
463
464 /*
465 * Should block in case not all messages have arrived!
466 * (If that's possible, and I don't think it is.)
467 */
468
469 /*
470 *
471 */
472 port->ip_norma_stransit = stransit + -(port->ip_norma_stransit);
473 port->ip_norma_sotransit = sotransit + -(port->ip_norma_sotransit);
474
475 /*
476 * Set any remaining fields.
477 * XXX Does ip_receiver's value matter here?
478 */
479 port->ip_norma_is_proxy = FALSE;
480 port->ip_receiver_name = MACH_PORT_NULL;
481 port->ip_destination = IP_NULL;
482 port->ip_srights += port->ip_norma_stransit;
483 port->ip_sorights += port->ip_norma_sotransit;
484 port->ip_references += (port->ip_norma_stransit +
485 port->ip_norma_sotransit);
486
487 /*
488 * Move messages from atrium to new principal.
489 * Deallocate atrium.
490 *
491 * XXX
492 * Should not lose messages still queued on proxy!
493 */
494 printf1("port=0x%x, ->atrium=0x%x atrium=0x%x count=%d\n",
495 port,
496 port->ip_norma_atrium,
497 atrium,
498 atrium->ip_msgcount);
499 printf1("--- old count=%d\n", port->ip_msgcount);
500 port->ip_msgcount = atrium->ip_msgcount;
501 port->ip_messages.imq_messages = atrium->ip_messages.imq_messages;
502 ip_free(atrium);
503 port->ip_norma_atrium = IP_NULL;
504
505 /*
506 * Return port.
507 */
508 mumble("receive_rright: returning port 0x%x\n", port);
509 return port;
510 }
Cache object: ce4cdb10d1916a934bb95abb70400573
|