1 /*
2 * Copyright 1991-1998 by Open Software Foundation, Inc.
3 * All Rights Reserved
4 *
5 * Permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby granted,
7 * provided that the above copyright notice appears in all copies and
8 * that both the copyright notice and this permission notice appear in
9 * supporting documentation.
10 *
11 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
12 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
16 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
18 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
19 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21 /*
22 * MkLinux
23 */
24 /* CMU_HIST */
25 /*
26 * Revision 2.5 91/05/14 16:35:47 mrt
27 * Correcting copyright
28 *
29 * Revision 2.4 91/02/05 17:23:15 mrt
30 * Changed to new Mach copyright
31 * [91/02/01 15:50:24 mrt]
32 *
33 * Revision 2.3 90/11/05 14:29:47 rpd
34 * Use new ips_reference and ips_release.
35 * [90/10/29 rpd]
36 *
37 * Revision 2.2 90/06/02 14:51:19 rpd
38 * Created for new IPC.
39 * [90/03/26 21:01:53 rpd]
40 *
41 */
42 /* CMU_ENDHIST */
43 /*
44 * Mach Operating System
45 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
46 * All Rights Reserved.
47 *
48 * Permission to use, copy, modify and distribute this software and its
49 * documentation is hereby granted, provided that both the copyright
50 * notice and this permission notice appear in all copies of the
51 * software, derivative works or modified versions, and any portions
52 * thereof, and that both notices appear in supporting documentation.
53 *
54 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
55 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
56 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
57 *
58 * Carnegie Mellon requests users of this software to return to
59 *
60 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
61 * School of Computer Science
62 * Carnegie Mellon University
63 * Pittsburgh PA 15213-3890
64 *
65 * any improvements or extensions that they make and grant Carnegie Mellon
66 * the rights to redistribute these changes.
67 */
68 /*
69 */
70 /*
71 * File: ipc/ipc_pset.c
72 * Author: Rich Draves
73 * Date: 1989
74 *
75 * Functions to manipulate IPC port sets.
76 */
77
78 #include <sys/cdefs.h>
79 #include <sys/types.h>
80 #include <sys/event.h>
81
82
83
84 #define MACH_INTERNAL
85 #include <sys/mach/port.h>
86 #include <sys/mach/kern_return.h>
87 #include <sys/mach/message.h>
88 #include <sys/mach/ipc/ipc_kmsg.h>
89 #include <sys/mach/ipc/ipc_mqueue.h>
90 #include <sys/mach/ipc/ipc_object.h>
91 #include <sys/mach/ipc/ipc_port.h>
92 #include <sys/mach/ipc/ipc_pset.h>
93 #include <sys/mach/ipc/ipc_right.h>
94 #include <sys/mach/ipc/ipc_space.h>
95 #include <sys/mach/ipc/ipc_print.h>
96
97 #include <sys/mach/thread.h>
98
99 static void
kn_sx_lock(void * arg)100 kn_sx_lock(void *arg)
101 {
102 struct sx *lock = arg;
103
104 sx_xlock(lock);
105 }
106
107 static void
kn_sx_unlock(void * arg)108 kn_sx_unlock(void *arg)
109 {
110 struct sx *lock = arg;
111
112 sx_xunlock(lock);
113 }
114
115 static void
sx_assert_locked(void * arg)116 sx_assert_locked(void *arg)
117 {
118 sx_assert((struct sx *)arg, SX_LOCKED);
119 }
120
121 static void
sx_assert_unlocked(void * arg)122 sx_assert_unlocked(void *arg)
123 {
124 sx_assert((struct sx *)arg, SX_UNLOCKED);
125 }
126
127 void
io_validate(ipc_object_t io)128 io_validate(ipc_object_t io)
129 {
130 ipc_port_t port;
131 ipc_pset_t pset;
132
133 if (io_otype(io) == IOT_PORT) {
134 port = (ipc_port_t)io;
135 MPASS(port->ip_pset == NULL);
136 assert(!ip_active(port));
137 } else {
138 pset = (ipc_pset_t)io;
139 MPASS(TAILQ_EMPTY(&pset->ips_ports));
140 }
141
142 }
143
144 /*
145 * Forward declarations
146 */
147 void ipc_pset_add(
148 ipc_pset_t pset,
149 ipc_port_t port);
150
151 /*
152 * Routine: ipc_pset_alloc
153 * Purpose:
154 * Allocate a port set.
155 * Conditions:
156 * Nothing locked. If successful, the port set is returned
157 * locked. (The caller doesn't have a reference.)
158 * Returns:
159 * KERN_SUCCESS The port set is allocated.
160 * KERN_INVALID_TASK The space is dead.
161 * KERN_NO_SPACE No room for an entry in the space.
162 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
163 */
164
165 kern_return_t
ipc_pset_alloc(ipc_space_t space,mach_port_name_t * namep,ipc_pset_t * psetp)166 ipc_pset_alloc(
167 ipc_space_t space,
168 mach_port_name_t *namep,
169 ipc_pset_t *psetp)
170 {
171 ipc_pset_t pset;
172 mach_port_name_t name;
173 kern_return_t kr;
174
175 kr = ipc_object_alloc(space, IOT_PORT_SET,
176 MACH_PORT_TYPE_PORT_SET,
177 &name, (ipc_object_t *) &pset);
178 if (kr != KERN_SUCCESS)
179 return kr;
180 /* pset is locked */
181
182 pset->ips_local_name = name;
183 TAILQ_INIT(&pset->ips_ports);
184 sx_init(&pset->ips_note_lock, "pset knote lock");
185 knlist_init(&pset->ips_note, &pset->ips_note_lock,
186 kn_sx_lock, kn_sx_unlock, sx_assert_locked, sx_assert_unlocked);
187 thread_pool_init(&pset->ips_thread_pool);
188 *namep = name;
189 *psetp = pset;
190 return KERN_SUCCESS;
191 }
192
193 /*
194 * Routine: ipc_pset_alloc_name
195 * Purpose:
196 * Allocate a port set, with a specific name.
197 * Conditions:
198 * Nothing locked. If successful, the port set is returned
199 * locked. (The caller doesn't have a reference.)
200 * Returns:
201 * KERN_SUCCESS The port set is allocated.
202 * KERN_INVALID_TASK The space is dead.
203 * KERN_NAME_EXISTS The name already denotes a right.
204 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
205 */
206
207 kern_return_t
ipc_pset_alloc_name(ipc_space_t space,mach_port_name_t name,ipc_pset_t * psetp)208 ipc_pset_alloc_name(
209 ipc_space_t space,
210 mach_port_name_t name,
211 ipc_pset_t *psetp)
212 {
213 ipc_pset_t pset;
214 kern_return_t kr;
215
216
217 kr = ipc_object_alloc_name(space, IOT_PORT_SET,
218 MACH_PORT_TYPE_PORT_SET,
219 name, (ipc_object_t *) &pset);
220 if (kr != KERN_SUCCESS)
221 return kr;
222 /* pset is locked */
223
224 pset->ips_local_name = name;
225 thread_pool_init(&pset->ips_thread_pool);
226 *psetp = pset;
227 return KERN_SUCCESS;
228 }
229
230 /*
231 * Routine: ipc_pset_add
232 * Purpose:
233 * Puts a port into a port set.
234 * The port set gains a reference.
235 * Conditions:
236 * Both port and port set are locked and active.
237 * The port isn't already in a set.
238 * The owner of the port set is also receiver for the port.
239 */
240
241 void
ipc_pset_add(ipc_pset_t pset,ipc_port_t port)242 ipc_pset_add(
243 ipc_pset_t pset,
244 ipc_port_t port)
245 {
246 assert(ips_active(pset));
247 assert(ip_active(port));
248 assert(port->ip_pset == IPS_NULL);
249
250 port->ip_pset = pset;
251 ips_reference(pset);
252 TAILQ_INSERT_TAIL(&pset->ips_ports, port, ip_next);
253 }
254
255 /*
256 * Routine: ipc_pset_remove
257 * Purpose:
258 * Removes a port from a port set.
259 * The port set loses a reference.
260 * Conditions:
261 * Both port and port set are locked.
262 * The port must be active.
263 */
264
265 void
ipc_pset_remove(ipc_pset_t pset,ipc_port_t port)266 ipc_pset_remove(
267 ipc_pset_t pset,
268 ipc_port_t port)
269 {
270 assert(ip_active(port));
271 assert(port->ip_pset == pset);
272
273 port->ip_pset = IPS_NULL;
274 TAILQ_REMOVE(&pset->ips_ports, port, ip_next);
275 }
276
277 /*
278 * Routine: ipc_pset_move
279 * Purpose:
280 * If nset is IPS_NULL, removes port
281 * from the port set it is in. Otherwise, adds
282 * port to nset, removing it from any set
283 * it might already be in.
284 * Conditions:
285 * The space is read-locked.
286 * Returns:
287 * KERN_SUCCESS Moved the port.
288 * KERN_NOT_IN_SET nset is null and port isn't in a set.
289 */
290
291 kern_return_t
ipc_pset_move(ipc_space_t space,ipc_port_t port,ipc_pset_t nset)292 ipc_pset_move(
293 ipc_space_t space,
294 ipc_port_t port,
295 ipc_pset_t nset)
296 {
297 ipc_pset_t oset;
298 int active;
299 int knotify;
300 /*
301 * While we've got the space locked, it holds refs for
302 * the port and nset (because of the entries). Also,
303 * they must be alive. While we've got port locked, it
304 * holds a ref for oset, which might not be alive.
305 */
306
307 ip_lock(port);
308 assert(ip_active(port));
309
310 knotify = FALSE;
311 oset = port->ip_pset;
312
313 if (oset == nset) {
314 /* the port is already in the new set: a noop */
315
316 is_read_unlock(space);
317 } else if (oset == IPS_NULL) {
318 /* just add port to the new set */
319
320 ips_lock(nset);
321 assert(ips_active(nset));
322 is_read_unlock(space);
323
324 ipc_pset_add(nset, port);
325 if (port->ip_msgcount != 0)
326 knotify = TRUE;
327 ips_unlock(nset);
328 } else if (nset == IPS_NULL) {
329 /* just remove port from the old set */
330
331 is_read_unlock(space);
332 ips_lock(oset);
333
334 ipc_pset_remove(oset, port);
335 active = ips_active(oset);
336 ips_unlock(oset);
337 ips_release(oset);
338 if (!active)
339 oset = IPS_NULL; /* trigger KERN_NOT_IN_SET */
340 } else {
341 /* atomically move port from oset to nset */
342
343 if (oset < nset) {
344 ips_lock(oset);
345 ips_lock(nset);
346 } else {
347 ips_lock(nset);
348 ips_lock(oset);
349 }
350
351 is_read_unlock(space);
352 assert(ips_active(nset));
353
354 ipc_pset_remove(oset, port);
355 ipc_pset_add(nset, port);
356
357 if (port->ip_msgcount != 0)
358 knotify = TRUE;
359
360 ips_unlock(nset);
361 ips_unlock(oset); /* KERN_NOT_IN_SET not a possibility */
362 ips_release(oset);
363 }
364
365 ip_unlock(port);
366
367 if (knotify == TRUE)
368 ipc_pset_signal(nset);
369 return (((nset == IPS_NULL) && (oset == IPS_NULL)) ?
370 KERN_NOT_IN_SET : KERN_SUCCESS);
371 }
372
373
374
375 /*
376 * Routine: ipc_pset_changed
377 * Purpose:
378 * Wake up receivers waiting on pset.
379 * Conditions:
380 * The pset is locked.
381 */
382
383 static void
ipc_pset_changed(ipc_pset_t pset,mach_msg_return_t mr)384 ipc_pset_changed(
385 ipc_pset_t pset,
386 mach_msg_return_t mr)
387 {
388 ipc_thread_t th;
389
390 while ((th = thread_pool_get_act((ipc_object_t)pset, 0)) != ITH_NULL) {
391 th->ith_state = mr;
392 thread_go(th);
393 }
394 }
395
396 /*
397 * Routine: ipc_pset_destroy
398 * Purpose:
399 * Destroys a port_set.
400 *
401 * Doesn't remove members from the port set;
402 * that happens lazily. As members are removed,
403 * their messages are removed from the queue.
404 * Conditions:
405 * The port_set is locked and alive.
406 * The caller has a reference, which is consumed.
407 * Afterwards, the port_set is unlocked and dead.
408 */
409
410 void
ipc_pset_destroy(ipc_pset_t pset)411 ipc_pset_destroy(
412 ipc_pset_t pset)
413 {
414 ipc_port_t port;
415
416 pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
417 while (!TAILQ_EMPTY(&pset->ips_ports)) {
418 port = TAILQ_FIRST(&pset->ips_ports);
419 MPASS(port->ip_pset == pset);
420 if (ip_lock_try(port) == 0) {
421 ips_unlock(pset);
422 ip_lock(port);
423 ips_lock(pset);
424 }
425 TAILQ_REMOVE(&pset->ips_ports, port, ip_next);
426 port->ip_pset = NULL;
427 ip_unlock(port);
428 ips_release(pset);
429 }
430 ipc_pset_changed(pset, MACH_RCV_PORT_DIED);
431 ips_unlock(pset);
432 ips_release(pset); /* consume the ref our caller gave us */
433 }
434
435 /**
436 *
437 * KQ handling
438 */
439
440 #include <sys/file.h>
441 #include <sys/selinfo.h>
442 #include <sys/eventvar.h>
443
444 void knote_enqueue(struct knote *kn);
445
446 #define KQ_LOCK(kq) do { \
447 mtx_lock(&(kq)->kq_lock); \
448 } while (0)
449 #define KQ_UNLOCK(kq) do { \
450 mtx_unlock(&(kq)->kq_lock); \
451 } while (0)
452
453 void
ipc_pset_signal(ipc_pset_t pset)454 ipc_pset_signal(ipc_pset_t pset)
455 {
456 struct kqueue *kq, *kq_prev;
457 struct knote *kn;
458 struct knlist *list;
459
460 sx_slock(&pset->ips_note_lock);
461 if (KNLIST_EMPTY(&pset->ips_note)) {
462 sx_sunlock(&pset->ips_note_lock);
463 return;
464 }
465 list = &pset->ips_note;
466 kq = kq_prev = NULL;
467 SLIST_FOREACH(kn, &list->kl_list, kn_selnext) {
468 kq = kn->kn_kq;
469 if (kq != kq_prev) {
470 if (kq_prev)
471 KQ_UNLOCK(kq_prev);
472 KQ_LOCK(kq);
473 }
474 (kn)->kn_status |= KN_ACTIVE;
475 if (((kn)->kn_status & (KN_QUEUED | KN_DISABLED)) == 0)
476 knote_enqueue(kn);
477 kq_prev = kq;
478 }
479 MPASS(kq != NULL);
480 KQ_UNLOCK(kq);
481 sx_sunlock(&pset->ips_note_lock);
482 }
483
484
485 static int filt_machportattach(struct knote *kn);
486 static void filt_machportdetach(struct knote *kn);
487 static int filt_machport(struct knote *kn, long hint);
488 struct filterops machport_filtops = {
489 .f_isfd = 1,
490 .f_attach = filt_machportattach,
491 .f_detach = filt_machportdetach,
492 .f_event = filt_machport,
493 };
494
495 static int
filt_machportattach(struct knote * kn)496 filt_machportattach(struct knote *kn)
497 {
498 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
499 ipc_pset_t pset = IPS_NULL;
500 ipc_entry_t entry;
501 kern_return_t kr;
502 struct knlist *note;
503
504 kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET,
505 (ipc_object_t *)&pset);
506
507 if (kr != KERN_SUCCESS)
508 return (kr == KERN_INVALID_NAME ? ENOENT : ENOTSUP);
509 note = &pset->ips_note;
510 ips_unlock(pset);
511
512 /* need the actual entry for knote */
513 if ((entry = ipc_entry_lookup(current_space(), name)) == NULL)
514 return (ENOENT);
515 KASSERT(entry->ie_object == (ipc_object_t)pset, ("entry->ie_object == pset"));
516
517 kn->kn_fp = entry->ie_fp;
518 knlist_add(note, kn, 0);
519 return (0);
520 }
521
522 extern void kdb_backtrace(void);
523
524 static void
filt_machportdetach(struct knote * kn)525 filt_machportdetach(struct knote *kn)
526 {
527 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
528 ipc_pset_t pset = IPS_NULL;
529 ipc_entry_t entry = NULL;;
530
531
532
533 if (kn->kn_fp->f_type != DTYPE_MACH_IPC)
534 goto fail;
535
536 entry = kn->kn_fp->f_data;
537 if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0)
538 goto fail;
539 if ((pset = (ipc_pset_t)entry->ie_object) == NULL)
540 goto fail;
541
542
543 knlist_remove(&pset->ips_note, kn, 0);
544 return;
545 fail:
546 kdb_backtrace();
547 printf("kqdetach fail for: %d pset: %p entry: %p\n", name, pset, entry);
548 }
549
550
551 static int
filt_machport(struct knote * kn,long hint)552 filt_machport(struct knote *kn, long hint)
553 {
554
555 mach_port_name_t name = (mach_port_name_t)kn->kn_kevent.ident;
556 ipc_entry_t entry = kn->kn_fp->f_data;
557 ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
558 thread_t self = current_thread();
559 kern_return_t kr;
560 mach_msg_option_t option;
561 mach_msg_size_t size;
562
563 if (hint == EV_EOF) {
564 kn->kn_data = 0;
565 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
566 return (1);
567 } else if (hint == 0) {
568
569 kr = ipc_object_translate(current_space(), name, MACH_PORT_RIGHT_PORT_SET,
570 (ipc_object_t *)&pset);
571 if (kr != KERN_SUCCESS || !ips_active(pset)) {
572 kdb_backtrace();
573 printf("%s: filt_machport kr=%d ips_active=%d name=%d\n", curproc->p_comm, kr, !!ips_active(pset), name);
574 kn->kn_data = 0;
575 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
576 return (1);
577 }
578
579 ips_reference(pset);
580
581 if (pset != (ipc_pset_t)entry->ie_object)
582 ips_unlock(pset);
583
584 } else
585 panic("invalid hint %ld\n", hint);
586
587
588 option = kn->kn_sfflags & (MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_LARGE_IDENTITY|
589 MACH_RCV_TRAILER_MASK|MACH_RCV_VOUCHER);
590
591 if (option & MACH_RCV_MSG) {
592 self->ith_msg_addr = (mach_vm_address_t) kn->kn_ext[0];
593 size = (mach_msg_size_t)kn->kn_ext[1];
594 printf("%s:%d: filt_machport option: %d \n", curproc->p_comm, curproc->p_pid, option);
595 } else {
596 option = MACH_RCV_LARGE;
597 self->ith_msg_addr = 0;
598 size = 0;
599 }
600
601 self->ith_object = (ipc_object_t)pset;
602 self->ith_msize = size;
603 self->ith_option = option;
604 self->ith_scatter_list_size = 0;
605 self->ith_receiver_name = MACH_PORT_NAME_NULL;
606 option |= MACH_RCV_TIMEOUT;
607 self->ith_state = MACH_RCV_IN_PROGRESS;
608
609
610 ips_lock(pset);
611 kr = ipc_mqueue_pset_receive(MACH_PORT_TYPE_PORT_SET, option, size,
612 0/* immediate timeout */, self);
613
614 ips_unlock(pset);
615 assert(kr == THREAD_NOT_WAITING);
616 assert(self->ith_state != MACH_RCV_IN_PROGRESS);
617 ips_release(pset);
618
619 if (self->ith_state == MACH_RCV_TIMED_OUT) {
620 return (0);
621 }
622 if ((option & MACH_RCV_MSG) != MACH_RCV_MSG) {
623 assert(self->ith_state == MACH_RCV_TOO_LARGE);
624 assert(self->ith_kmsg == IKM_NULL);
625 kn->kn_data = self->ith_receiver_name;
626 #ifdef __LP64__
627 printf("%s:%d, receiver_name %ld\n", curproc->p_comm, curproc->p_pid, kn->kn_data);
628 #endif
629 return (1);
630 }
631
632 assert(option & MACH_RCV_MSG);
633 kn->kn_ext[1] = self->ith_msize;
634 kn->kn_data = MACH_PORT_NAME_NULL;
635 #ifdef __LP64__
636 printf("%s:%d receive result size: %d to: %lx \n", curproc->p_comm, curproc->p_pid, self->ith_msize, self->ith_msg_addr);
637 #endif
638 kn->kn_fflags = mach_msg_receive_results(self);
639
640 if ((kn->kn_fflags == MACH_RCV_TOO_LARGE) &&
641 (option & MACH_RCV_LARGE_IDENTITY))
642 kn->kn_data = self->ith_receiver_name;
643
644 return (1);
645 }
646
647 #if MACH_KDB
648 #include <mach_kdb.h>
649
650 #include <ddb/db_output.h>
651
652 #define printf kdbprintf
653
654 int
ipc_list_count(struct ipc_kmsg * base)655 ipc_list_count(
656 struct ipc_kmsg *base)
657 {
658 register int count = 0;
659
660 if (base) {
661 struct ipc_kmsg *kmsg = base;
662
663 ++count;
664 while (kmsg && kmsg->ikm_next != base
665 && kmsg->ikm_next != IKM_BOGUS){
666 kmsg = kmsg->ikm_next;
667 ++count;
668 }
669 }
670 return(count);
671 }
672
673 /*
674 * Routine: ipc_pset_print
675 * Purpose:
676 * Pretty-print a port set for kdb.
677 */
678
679 void
ipc_pset_print(ipc_pset_t pset)680 ipc_pset_print(
681 ipc_pset_t pset)
682 {
683 extern int indent;
684
685 printf("pset 0x%x\n", pset);
686
687 indent += 2;
688
689 ipc_object_print(&pset->ips_object);
690 iprintf("local_name = 0x%x\n", pset->ips_local_name);
691 iprintf("%d kmsgs => 0x%x",
692 ipc_list_count(pset->ips_messages.imq_messages.ikmq_base),
693 pset->ips_messages.imq_messages.ikmq_base);
694 printf(",rcvrs = 0x%x\n", pset->ips_messages.imq_threads.ithq_base);
695
696 indent -=2;
697 }
698
699 #endif /* MACH_KDB */
700