xref: /titanic_52/usr/src/uts/common/os/msg.c (revision 10db1377dafab8ba3feedef26db9c5d8539a5cd1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  * Inter-Process Communication Message Facility.
35  *
36  * See os/ipc.c for a description of common IPC functionality.
37  *
38  * Resource controls
39  * -----------------
40  *
41  * Control:      project.max-msg-ids (rc_project_msgmni)
42  * Description:  Maximum number of message queue ids allowed a project.
43  *
44  *   When msgget() is used to allocate a message queue, one id is
45  *   allocated.  If the id allocation doesn't succeed, msgget() fails
46  *   and errno is set to ENOSPC.  Upon successful msgctl(, IPC_RMID)
47  *   the id is deallocated.
48  *
49  * Control:      process.max-msg-qbytes (rc_process_msgmnb)
50  * Description:  Maximum number of bytes of messages on a message queue.
51  *
52  *   When msgget() successfully allocates a message queue, the minimum
53  *   enforced value of this limit is used to initialize msg_qbytes.
54  *
55  * Control:      process.max-msg-messages (rc_process_msgtql)
56  * Description:  Maximum number of messages on a message queue.
57  *
58  *   When msgget() successfully allocates a message queue, the minimum
59  *   enforced value of this limit is used to initialize a per-queue
60  *   limit on the number of messages.
61  */
62 
63 #include <sys/types.h>
64 #include <sys/t_lock.h>
65 #include <sys/param.h>
66 #include <sys/cred.h>
67 #include <sys/user.h>
68 #include <sys/proc.h>
69 #include <sys/time.h>
70 #include <sys/ipc.h>
71 #include <sys/ipc_impl.h>
72 #include <sys/msg.h>
73 #include <sys/msg_impl.h>
74 #include <sys/list.h>
75 #include <sys/systm.h>
76 #include <sys/sysmacros.h>
77 #include <sys/cpuvar.h>
78 #include <sys/kmem.h>
79 #include <sys/ddi.h>
80 #include <sys/errno.h>
81 #include <sys/cmn_err.h>
82 #include <sys/debug.h>
83 #include <sys/project.h>
84 #include <sys/modctl.h>
85 #include <sys/syscall.h>
86 #include <sys/policy.h>
87 #include <sys/zone.h>
88 
89 #include <c2/audit.h>
90 
91 /*
92  * The following tunables are obsolete.  Though for compatibility we
93  * still read and interpret msginfo_msgmnb, msginfo_msgmni, and
94  * msginfo_msgtql (see os/project.c and os/rctl_proc.c), the preferred
95  * mechanism for administrating the IPC Message facility is through the
96  * resource controls described at the top of this file.
97  */
98 size_t	msginfo_msgmax = 2048;	/* (obsolete) */
99 size_t	msginfo_msgmnb = 4096;	/* (obsolete) */
100 int	msginfo_msgmni = 50;	/* (obsolete) */
101 int	msginfo_msgtql = 40;	/* (obsolete) */
102 int	msginfo_msgssz = 8;	/* (obsolete) */
103 int	msginfo_msgmap = 0;	/* (obsolete) */
104 ushort_t msginfo_msgseg = 1024;	/* (obsolete) */
105 
106 extern rctl_hndl_t rc_project_msgmni;
107 extern rctl_hndl_t rc_process_msgmnb;
108 extern rctl_hndl_t rc_process_msgtql;
109 static ipc_service_t *msq_svc;
110 static zone_key_t msg_zone_key;
111 
112 static void msg_dtor(kipc_perm_t *);
113 static void msg_rmid(kipc_perm_t *);
114 static void msg_remove_zone(zoneid_t, void *);
115 
116 /*
117  * Module linkage information for the kernel.
118  */
119 static ssize_t msgsys(int opcode, uintptr_t a0, uintptr_t a1, uintptr_t a2,
120 	uintptr_t a4, uintptr_t a5);
121 
122 static struct sysent ipcmsg_sysent = {
123 	6,
124 #ifdef	_LP64
125 	SE_ARGC | SE_NOUNLOAD | SE_64RVAL,
126 #else
127 	SE_ARGC | SE_NOUNLOAD | SE_32RVAL1,
128 #endif
129 	(int (*)())msgsys
130 };
131 
132 #ifdef	_SYSCALL32_IMPL
133 static ssize32_t msgsys32(int opcode, uint32_t a0, uint32_t a1, uint32_t a2,
134 	uint32_t a4, uint32_t a5);
135 
136 static struct sysent ipcmsg_sysent32 = {
137 	6,
138 	SE_ARGC | SE_NOUNLOAD | SE_32RVAL1,
139 	(int (*)())msgsys32
140 };
141 #endif	/* _SYSCALL32_IMPL */
142 
143 static struct modlsys modlsys = {
144 	&mod_syscallops, "System V message facility", &ipcmsg_sysent
145 };
146 
147 #ifdef _SYSCALL32_IMPL
148 static struct modlsys modlsys32 = {
149 	&mod_syscallops32, "32-bit System V message facility", &ipcmsg_sysent32
150 };
151 #endif
152 
153 static struct modlinkage modlinkage = {
154 	MODREV_1,
155 	&modlsys,
156 #ifdef _SYSCALL32_IMPL
157 	&modlsys32,
158 #endif
159 	NULL
160 };
161 
162 
163 int
164 _init(void)
165 {
166 	int result;
167 
168 	msq_svc = ipcs_create("msqids", rc_project_msgmni, sizeof (kmsqid_t),
169 	    msg_dtor, msg_rmid, AT_IPC_MSG,
170 	    offsetof(kproject_data_t, kpd_msgmni));
171 	zone_key_create(&msg_zone_key, NULL, msg_remove_zone, NULL);
172 
173 	if ((result = mod_install(&modlinkage)) == 0)
174 		return (0);
175 
176 	(void) zone_key_delete(msg_zone_key);
177 	ipcs_destroy(msq_svc);
178 
179 	return (result);
180 }
181 
182 int
183 _fini(void)
184 {
185 	return (EBUSY);
186 }
187 
188 int
189 _info(struct modinfo *modinfop)
190 {
191 	return (mod_info(&modlinkage, modinfop));
192 }
193 
194 static void
195 msg_dtor(kipc_perm_t *perm)
196 {
197 	kmsqid_t *qp = (kmsqid_t *)perm;
198 
199 	ASSERT(qp->msg_rcv_cnt == 0);
200 	ASSERT(qp->msg_snd_cnt == 0);
201 	ASSERT(qp->msg_cbytes == 0);
202 	list_destroy(&qp->msg_list);
203 }
204 
205 
206 #define	msg_hold(mp)	(mp)->msg_copycnt++
207 
208 /*
209  * msg_rele - decrement the reference count on the message.  When count
210  * reaches zero, free message header and contents.
211  */
212 static void
213 msg_rele(struct msg *mp)
214 {
215 	ASSERT(mp->msg_copycnt > 0);
216 	if (mp->msg_copycnt-- == 1) {
217 		if (mp->msg_addr)
218 			kmem_free(mp->msg_addr, mp->msg_size);
219 		kmem_free(mp, sizeof (struct msg));
220 	}
221 }
222 
223 /*
224  * msgunlink - Unlink msg from queue, decrement byte count and wake up anyone
225  * waiting for free bytes on queue.
226  *
227  * Called with queue locked.
228  */
229 static void
230 msgunlink(kmsqid_t *qp, struct msg *mp)
231 {
232 	list_remove(&qp->msg_list, mp);
233 	qp->msg_qnum--;
234 	qp->msg_cbytes -= mp->msg_size;
235 	msg_rele(mp);
236 
237 	/* Wake up waiting writers */
238 	if (qp->msg_snd_cnt)
239 		cv_broadcast(&qp->msg_snd_cv);
240 }
241 
242 static void
243 msg_rmid(kipc_perm_t *perm)
244 {
245 	kmsqid_t *qp = (kmsqid_t *)perm;
246 	struct msg *mp;
247 
248 
249 	while ((mp = list_head(&qp->msg_list)) != NULL)
250 		msgunlink(qp, mp);
251 	ASSERT(qp->msg_cbytes == 0);
252 
253 	if (qp->msg_rcv_cnt)
254 		cv_broadcast(&qp->msg_rcv_cv);
255 	if (qp->msg_snd_cnt)
256 		cv_broadcast(&qp->msg_snd_cv);
257 }
258 
259 /*
260  * msgctl system call.
261  *
262  * gets q lock (via ipc_lookup), releases before return.
263  * may call users of msg_lock
264  */
265 static int
266 msgctl(int msgid, int cmd, void *arg)
267 {
268 	STRUCT_DECL(msqid_ds, ds);		/* SVR4 queue work area */
269 	kmsqid_t		*qp;		/* ptr to associated q */
270 	int			error;
271 	struct	cred		*cr;
272 	model_t	mdl = get_udatamodel();
273 	struct msqid_ds64	ds64;
274 	kmutex_t		*lock;
275 	proc_t			*pp = curproc;
276 
277 	STRUCT_INIT(ds, mdl);
278 	cr = CRED();
279 
280 	/*
281 	 * Perform pre- or non-lookup actions (e.g. copyins, RMID).
282 	 */
283 	switch (cmd) {
284 	case IPC_SET:
285 		if (copyin(arg, STRUCT_BUF(ds), STRUCT_SIZE(ds)))
286 			return (set_errno(EFAULT));
287 		break;
288 
289 	case IPC_SET64:
290 		if (copyin(arg, &ds64, sizeof (struct msqid_ds64)))
291 			return (set_errno(EFAULT));
292 		break;
293 
294 	case IPC_RMID:
295 		if (error = ipc_rmid(msq_svc, msgid, cr))
296 			return (set_errno(error));
297 		return (0);
298 	}
299 
300 	/*
301 	 * get msqid_ds for this msgid
302 	 */
303 	if ((lock = ipc_lookup(msq_svc, msgid, (kipc_perm_t **)&qp)) == NULL)
304 		return (set_errno(EINVAL));
305 
306 	switch (cmd) {
307 	case IPC_SET:
308 		if (STRUCT_FGET(ds, msg_qbytes) > qp->msg_qbytes &&
309 		    secpolicy_ipc_config(cr) != 0) {
310 			mutex_exit(lock);
311 			return (set_errno(EPERM));
312 		}
313 		if (error = ipcperm_set(msq_svc, cr, &qp->msg_perm,
314 		    &STRUCT_BUF(ds)->msg_perm, mdl)) {
315 			mutex_exit(lock);
316 			return (set_errno(error));
317 		}
318 		qp->msg_qbytes = STRUCT_FGET(ds, msg_qbytes);
319 		qp->msg_ctime = gethrestime_sec();
320 		break;
321 
322 	case IPC_STAT:
323 		if (error = ipcperm_access(&qp->msg_perm, MSG_R, cr)) {
324 			mutex_exit(lock);
325 			return (set_errno(error));
326 		}
327 
328 		if (qp->msg_rcv_cnt)
329 			qp->msg_perm.ipc_mode |= MSG_RWAIT;
330 		if (qp->msg_snd_cnt)
331 			qp->msg_perm.ipc_mode |= MSG_WWAIT;
332 		ipcperm_stat(&STRUCT_BUF(ds)->msg_perm, &qp->msg_perm, mdl);
333 		qp->msg_perm.ipc_mode &= ~(MSG_RWAIT|MSG_WWAIT);
334 		STRUCT_FSETP(ds, msg_first, NULL); 	/* kernel addr */
335 		STRUCT_FSETP(ds, msg_last, NULL);
336 		STRUCT_FSET(ds, msg_cbytes, qp->msg_cbytes);
337 		STRUCT_FSET(ds, msg_qnum, qp->msg_qnum);
338 		STRUCT_FSET(ds, msg_qbytes, qp->msg_qbytes);
339 		STRUCT_FSET(ds, msg_lspid, qp->msg_lspid);
340 		STRUCT_FSET(ds, msg_lrpid, qp->msg_lrpid);
341 		STRUCT_FSET(ds, msg_stime, qp->msg_stime);
342 		STRUCT_FSET(ds, msg_rtime, qp->msg_rtime);
343 		STRUCT_FSET(ds, msg_ctime, qp->msg_ctime);
344 		break;
345 
346 	case IPC_SET64:
347 		mutex_enter(&pp->p_lock);
348 		if ((ds64.msgx_qbytes > qp->msg_qbytes) &&
349 		    secpolicy_ipc_config(cr) != 0 &&
350 		    rctl_test(rc_process_msgmnb, pp->p_rctls, pp,
351 		    ds64.msgx_qbytes, RCA_SAFE) & RCT_DENY) {
352 			mutex_exit(&pp->p_lock);
353 			mutex_exit(lock);
354 			return (set_errno(EPERM));
355 		}
356 		mutex_exit(&pp->p_lock);
357 		if (error = ipcperm_set64(msq_svc, cr, &qp->msg_perm,
358 		    &ds64.msgx_perm)) {
359 			mutex_exit(lock);
360 			return (set_errno(error));
361 		}
362 		qp->msg_qbytes = ds64.msgx_qbytes;
363 		qp->msg_ctime = gethrestime_sec();
364 		break;
365 
366 	case IPC_STAT64:
367 		if (qp->msg_rcv_cnt)
368 			qp->msg_perm.ipc_mode |= MSG_RWAIT;
369 		if (qp->msg_snd_cnt)
370 			qp->msg_perm.ipc_mode |= MSG_WWAIT;
371 		ipcperm_stat64(&ds64.msgx_perm, &qp->msg_perm);
372 		qp->msg_perm.ipc_mode &= ~(MSG_RWAIT|MSG_WWAIT);
373 		ds64.msgx_cbytes = qp->msg_cbytes;
374 		ds64.msgx_qnum = qp->msg_qnum;
375 		ds64.msgx_qbytes = qp->msg_qbytes;
376 		ds64.msgx_lspid = qp->msg_lspid;
377 		ds64.msgx_lrpid = qp->msg_lrpid;
378 		ds64.msgx_stime = qp->msg_stime;
379 		ds64.msgx_rtime = qp->msg_rtime;
380 		ds64.msgx_ctime = qp->msg_ctime;
381 		break;
382 
383 	default:
384 		mutex_exit(lock);
385 		return (set_errno(EINVAL));
386 	}
387 
388 	mutex_exit(lock);
389 
390 	/*
391 	 * Do copyout last (after releasing mutex).
392 	 */
393 	switch (cmd) {
394 	case IPC_STAT:
395 		if (copyout(STRUCT_BUF(ds), arg, STRUCT_SIZE(ds)))
396 			return (set_errno(EFAULT));
397 		break;
398 
399 	case IPC_STAT64:
400 		if (copyout(&ds64, arg, sizeof (struct msqid_ds64)))
401 			return (set_errno(EFAULT));
402 		break;
403 	}
404 
405 	return (0);
406 }
407 
408 /*
409  * Remove all message queues associated with a given zone.  Called by
410  * zone_shutdown when the zone is halted.
411  */
412 /*ARGSUSED1*/
413 static void
414 msg_remove_zone(zoneid_t zoneid, void *arg)
415 {
416 	ipc_remove_zone(msq_svc, zoneid);
417 }
418 
419 /*
420  * msgget system call.
421  */
422 static int
423 msgget(key_t key, int msgflg)
424 {
425 	kmsqid_t	*qp;
426 	kmutex_t	*lock;
427 	int		id, error;
428 	proc_t		*pp = curproc;
429 
430 top:
431 	if (error = ipc_get(msq_svc, key, msgflg, (kipc_perm_t **)&qp, &lock))
432 		return (set_errno(error));
433 
434 	if (IPC_FREE(&qp->msg_perm)) {
435 		mutex_exit(lock);
436 		mutex_exit(&pp->p_lock);
437 
438 		list_create(&qp->msg_list, sizeof (struct msg),
439 		    offsetof(struct msg, msg_node));
440 		qp->msg_qnum = 0;
441 		qp->msg_lspid = qp->msg_lrpid = 0;
442 		qp->msg_stime = qp->msg_rtime = 0;
443 		qp->msg_ctime = gethrestime_sec();
444 		qp->msg_rcv_cnt = qp->msg_snd_cnt = 0;
445 
446 		if (error = ipc_commit_begin(msq_svc, key, msgflg,
447 		    (kipc_perm_t *)qp)) {
448 			if (error == EAGAIN)
449 				goto top;
450 			return (set_errno(error));
451 		}
452 		qp->msg_qbytes = rctl_enforced_value(rc_process_msgmnb,
453 		    pp->p_rctls, pp);
454 		qp->msg_qmax = rctl_enforced_value(rc_process_msgtql,
455 		    pp->p_rctls, pp);
456 		lock = ipc_commit_end(msq_svc, &qp->msg_perm);
457 	}
458 #ifdef C2_AUDIT
459 	if (audit_active)
460 		audit_ipcget(AT_IPC_MSG, (void *)qp);
461 #endif
462 	id = qp->msg_perm.ipc_id;
463 	mutex_exit(lock);
464 	return (id);
465 }
466 
467 /*
468  * msgrcv system call.
469  */
470 static ssize_t
471 msgrcv(int msqid, struct ipcmsgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
472 {
473 	struct msg	*mp;	/* ptr to msg on q */
474 	struct msg	*smp;	/* ptr to best msg on q */
475 	kmsqid_t	*qp;	/* ptr to associated q */
476 	kmutex_t	*lock;
477 	size_t		xtsz;	/* transfer byte count */
478 	int		error = 0, copyerror = 0;
479 	int		cvres;
480 	STRUCT_HANDLE(ipcmsgbuf, umsgp);
481 	model_t		mdl = get_udatamodel();
482 
483 	CPU_STATS_ADDQ(CPU, sys, msg, 1);	/* bump msg send/rcv count */
484 	STRUCT_SET_HANDLE(umsgp, mdl, msgp);
485 
486 	if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL)
487 		return ((ssize_t)set_errno(EINVAL));
488 	ipc_hold(msq_svc, (kipc_perm_t *)qp);
489 
490 	if (error = ipcperm_access(&qp->msg_perm, MSG_R, CRED()))
491 		goto msgrcv_out;
492 
493 findmsg:
494 	smp = NULL;
495 	mp = list_head(&qp->msg_list);
496 	if (msgtyp == 0) {
497 		smp = mp;
498 	} else {
499 		for (; mp; mp = list_next(&qp->msg_list, mp)) {
500 			if (msgtyp > 0) {
501 				if (msgtyp != mp->msg_type)
502 					continue;
503 				smp = mp;
504 				break;
505 			}
506 			if (mp->msg_type <= -msgtyp) {
507 				if (smp && smp->msg_type <= mp->msg_type)
508 					continue;
509 				smp = mp;
510 			}
511 		}
512 	}
513 
514 	if (smp) {
515 		/*
516 		 * Message found.
517 		 */
518 		if ((smp->msg_flags & MSG_RCVCOPY) == 0) {
519 			/*
520 			 * No one else is copying this message. Copy it.
521 			 */
522 			if (msgsz < smp->msg_size) {
523 				if ((msgflg & MSG_NOERROR) == 0) {
524 					error = E2BIG;
525 					goto msgrcv_out;
526 				} else {
527 					xtsz = msgsz;
528 				}
529 			} else {
530 				xtsz = smp->msg_size;
531 			}
532 
533 			/*
534 			 * Mark message as being copied out. Release mutex
535 			 * while copying out.
536 			 */
537 			ASSERT((smp->msg_flags & MSG_RCVCOPY) == 0);
538 			smp->msg_flags |= MSG_RCVCOPY;
539 			msg_hold(smp);
540 			mutex_exit(lock);
541 
542 			if (mdl == DATAMODEL_NATIVE) {
543 				copyerror = copyout(&smp->msg_type, msgp,
544 				    sizeof (smp->msg_type));
545 			} else {
546 				/*
547 				 * 32-bit callers need an imploded msg type.
548 				 */
549 				int32_t	msg_type32 = smp->msg_type;
550 
551 				copyerror = copyout(&msg_type32, msgp,
552 				    sizeof (msg_type32));
553 			}
554 
555 			if (copyerror == 0 && xtsz)
556 				copyerror = copyout(smp->msg_addr,
557 				    STRUCT_FADDR(umsgp, mtext), xtsz);
558 
559 			/*
560 			 * Reclaim mutex, make sure queue still exists,
561 			 * and remove message.
562 			 */
563 			lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id);
564 			ASSERT(smp->msg_flags & MSG_RCVCOPY);
565 			smp->msg_flags &= ~MSG_RCVCOPY;
566 			msg_rele(smp);
567 
568 			if (IPC_FREE(&qp->msg_perm)) {
569 				error = EIDRM;
570 				goto msgrcv_out;
571 			}
572 			cv_broadcast(&qp->msg_rcv_cv);
573 
574 			if (copyerror) {
575 				error = EFAULT;
576 				goto msgrcv_out;
577 			}
578 			qp->msg_lrpid = ttoproc(curthread)->p_pid;
579 			qp->msg_rtime = gethrestime_sec();
580 			msgunlink(qp, smp);
581 			goto msgrcv_out;
582 		}
583 
584 	} else {
585 		/*
586 		 * No message found.
587 		 */
588 		if (msgflg & IPC_NOWAIT) {
589 			error = ENOMSG;
590 			goto msgrcv_out;
591 		}
592 	}
593 
594 	/* Wait for new message */
595 	qp->msg_rcv_cnt++;
596 	cvres = cv_wait_sig(&qp->msg_rcv_cv, lock);
597 	lock = ipc_relock(msq_svc, qp->msg_perm.ipc_id, lock);
598 	qp->msg_rcv_cnt--;
599 
600 	if (IPC_FREE(&qp->msg_perm)) {
601 		error = EIDRM;
602 		goto msgrcv_out;
603 	}
604 	if (cvres == 0) {
605 		error = EINTR;
606 		goto msgrcv_out;
607 	}
608 
609 	goto findmsg;
610 
611 msgrcv_out:
612 	ipc_rele(msq_svc, (kipc_perm_t *)qp);
613 	if (error)
614 		return ((ssize_t)set_errno(error));
615 	return ((ssize_t)xtsz);
616 }
617 
618 /*
619  * msgids system call.
620  */
621 static int
622 msgids(int *buf, uint_t nids, uint_t *pnids)
623 {
624 	int error;
625 
626 	if (error = ipc_ids(msq_svc, buf, nids, pnids))
627 		return (set_errno(error));
628 
629 	return (0);
630 }
631 
632 #define	RND(x)		roundup((x), sizeof (size_t))
633 #define	RND32(x)	roundup((x), sizeof (size32_t))
634 
635 /*
636  * msgsnap system call.
637  */
638 static int
639 msgsnap(int msqid, caddr_t buf, size_t bufsz, long msgtyp)
640 {
641 	struct msg	*mp;	/* ptr to msg on q */
642 	kmsqid_t	*qp;	/* ptr to associated q */
643 	kmutex_t	*lock;
644 	size_t		size;
645 	size_t		nmsg;
646 	struct msg	**snaplist;
647 	int		error, i;
648 	model_t		mdl = get_udatamodel();
649 	STRUCT_DECL(msgsnap_head, head);
650 	STRUCT_DECL(msgsnap_mhead, mhead);
651 
652 	STRUCT_INIT(head, mdl);
653 	STRUCT_INIT(mhead, mdl);
654 
655 	if (bufsz < STRUCT_SIZE(head))
656 		return (set_errno(EINVAL));
657 
658 	if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL)
659 		return (set_errno(EINVAL));
660 
661 	if (error = ipcperm_access(&qp->msg_perm, MSG_R, CRED())) {
662 		mutex_exit(lock);
663 		return (set_errno(error));
664 	}
665 	ipc_hold(msq_svc, (kipc_perm_t *)qp);
666 
667 	/*
668 	 * First compute the required buffer size and
669 	 * the number of messages on the queue.
670 	 */
671 	size = nmsg = 0;
672 	for (mp = list_head(&qp->msg_list); mp;
673 	    mp = list_next(&qp->msg_list, mp)) {
674 		if (msgtyp == 0 ||
675 		    (msgtyp > 0 && msgtyp == mp->msg_type) ||
676 		    (msgtyp < 0 && mp->msg_type <= -msgtyp)) {
677 			nmsg++;
678 			if (mdl == DATAMODEL_NATIVE)
679 				size += RND(mp->msg_size);
680 			else
681 				size += RND32(mp->msg_size);
682 		}
683 	}
684 
685 	size += STRUCT_SIZE(head) + nmsg * STRUCT_SIZE(mhead);
686 	if (size > bufsz)
687 		nmsg = 0;
688 
689 	if (nmsg > 0) {
690 		/*
691 		 * Mark the messages as being copied.
692 		 */
693 		snaplist = (struct msg **)kmem_alloc(nmsg *
694 		    sizeof (struct msg *), KM_SLEEP);
695 		i = 0;
696 		for (mp = list_head(&qp->msg_list); mp;
697 		    mp = list_next(&qp->msg_list, mp)) {
698 			if (msgtyp == 0 ||
699 			    (msgtyp > 0 && msgtyp == mp->msg_type) ||
700 			    (msgtyp < 0 && mp->msg_type <= -msgtyp)) {
701 				msg_hold(mp);
702 				snaplist[i] = mp;
703 				i++;
704 			}
705 		}
706 	}
707 	mutex_exit(lock);
708 
709 	/*
710 	 * Copy out the buffer header.
711 	 */
712 	STRUCT_FSET(head, msgsnap_size, size);
713 	STRUCT_FSET(head, msgsnap_nmsg, nmsg);
714 	if (copyout(STRUCT_BUF(head), buf, STRUCT_SIZE(head)))
715 		error = EFAULT;
716 
717 	buf += STRUCT_SIZE(head);
718 
719 	/*
720 	 * Now copy out the messages one by one.
721 	 */
722 	for (i = 0; i < nmsg; i++) {
723 		mp = snaplist[i];
724 		if (error == 0) {
725 			STRUCT_FSET(mhead, msgsnap_mlen, mp->msg_size);
726 			STRUCT_FSET(mhead, msgsnap_mtype, mp->msg_type);
727 			if (copyout(STRUCT_BUF(mhead), buf, STRUCT_SIZE(mhead)))
728 				error = EFAULT;
729 			buf += STRUCT_SIZE(mhead);
730 
731 			if (error == 0 &&
732 			    mp->msg_size != 0 &&
733 			    copyout(mp->msg_addr, buf, mp->msg_size))
734 				error = EFAULT;
735 			if (mdl == DATAMODEL_NATIVE)
736 				buf += RND(mp->msg_size);
737 			else
738 				buf += RND32(mp->msg_size);
739 		}
740 		lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id);
741 		msg_rele(mp);
742 		/* Check for msg q deleted or reallocated */
743 		if (IPC_FREE(&qp->msg_perm))
744 			error = EIDRM;
745 		mutex_exit(lock);
746 	}
747 
748 	(void) ipc_lock(msq_svc, qp->msg_perm.ipc_id);
749 	ipc_rele(msq_svc, (kipc_perm_t *)qp);
750 
751 	if (nmsg > 0)
752 		kmem_free(snaplist, nmsg * sizeof (struct msg *));
753 
754 	if (error)
755 		return (set_errno(error));
756 	return (0);
757 }
758 
759 /*
760  * msgsnd system call.
761  */
762 static int
763 msgsnd(int msqid, struct ipcmsgbuf *msgp, size_t msgsz, int msgflg)
764 {
765 	kmsqid_t	*qp;
766 	kmutex_t	*lock;
767 	struct msg	*mp = NULL;
768 	long		type;
769 	int		error = 0;
770 	model_t		mdl = get_udatamodel();
771 	STRUCT_HANDLE(ipcmsgbuf, umsgp);
772 
773 	CPU_STATS_ADDQ(CPU, sys, msg, 1);	/* bump msg send/rcv count */
774 	STRUCT_SET_HANDLE(umsgp, mdl, msgp);
775 
776 	if (mdl == DATAMODEL_NATIVE) {
777 		if (copyin(msgp, &type, sizeof (type)))
778 			return (set_errno(EFAULT));
779 	} else {
780 		int32_t	type32;
781 		if (copyin(msgp, &type32, sizeof (type32)))
782 			return (set_errno(EFAULT));
783 		type = type32;
784 	}
785 
786 	if (type < 1)
787 		return (set_errno(EINVAL));
788 
789 	if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL)
790 		return (set_errno(EINVAL));
791 	ipc_hold(msq_svc, (kipc_perm_t *)qp);
792 
793 	if (msgsz > qp->msg_qbytes) {
794 		error = EINVAL;
795 		goto msgsnd_out;
796 	}
797 
798 	if (error = ipcperm_access(&qp->msg_perm, MSG_W, CRED()))
799 		goto msgsnd_out;
800 
801 top:
802 	/*
803 	 * Allocate space on q, message header, & buffer space.
804 	 */
805 	ASSERT(qp->msg_qnum <= qp->msg_qmax);
806 	while ((msgsz > qp->msg_qbytes - qp->msg_cbytes) ||
807 	    (qp->msg_qnum == qp->msg_qmax)) {
808 		int cvres;
809 
810 		if (msgflg & IPC_NOWAIT) {
811 			error = EAGAIN;
812 			goto msgsnd_out;
813 		}
814 
815 		qp->msg_snd_cnt++;
816 		cvres = cv_wait_sig(&qp->msg_snd_cv, lock);
817 		lock = ipc_relock(msq_svc, qp->msg_perm.ipc_id, lock);
818 		qp->msg_snd_cnt--;
819 
820 		if (IPC_FREE(&qp->msg_perm)) {
821 			error = EIDRM;
822 			goto msgsnd_out;
823 		}
824 
825 		if (cvres == 0) {
826 			error = EINTR;
827 			goto msgsnd_out;
828 		}
829 	}
830 
831 	if (mp == NULL) {
832 		int failure;
833 
834 		mutex_exit(lock);
835 		mp = kmem_zalloc(sizeof (struct msg), KM_SLEEP);
836 		mp->msg_addr = kmem_zalloc(msgsz, KM_SLEEP);
837 		mp->msg_size = msgsz;
838 		mp->msg_copycnt = 1;
839 
840 		failure = msgsz && (copyin(STRUCT_FADDR(umsgp, mtext),
841 		    mp->msg_addr, msgsz) == -1);
842 		lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id);
843 		if (IPC_FREE(&qp->msg_perm)) {
844 			error = EIDRM;
845 			goto msgsnd_out;
846 		}
847 		if (failure) {
848 			error = EFAULT;
849 			goto msgsnd_out;
850 		}
851 		goto top;
852 	}
853 
854 	/*
855 	 * Everything is available, put msg on q.
856 	 */
857 	qp->msg_qnum++;
858 	qp->msg_cbytes += msgsz;
859 	qp->msg_lspid = curproc->p_pid;
860 	qp->msg_stime = gethrestime_sec();
861 	mp->msg_type = type;
862 	mp->msg_flags = 0;
863 	list_insert_tail(&qp->msg_list, mp);
864 	if (qp->msg_rcv_cnt)
865 		cv_broadcast(&qp->msg_rcv_cv);
866 
867 msgsnd_out:
868 	ipc_rele(msq_svc, (kipc_perm_t *)qp);	/* drops lock */
869 
870 	if (error) {
871 		if (mp)
872 			msg_rele(mp);
873 		return (set_errno(error));
874 	}
875 
876 	return (0);
877 }
878 
879 /*
880  * msgsys - System entry point for msgctl, msgget, msgrcv, and msgsnd
881  * system calls.
882  */
883 static ssize_t
884 msgsys(int opcode, uintptr_t a1, uintptr_t a2, uintptr_t a3,
885 	uintptr_t a4, uintptr_t a5)
886 {
887 	ssize_t error;
888 
889 	switch (opcode) {
890 	case MSGGET:
891 		error = msgget((key_t)a1, (int)a2);
892 		break;
893 	case MSGCTL:
894 		error = msgctl((int)a1, (int)a2, (void *)a3);
895 		break;
896 	case MSGRCV:
897 		error = msgrcv((int)a1, (struct ipcmsgbuf *)a2,
898 		    (size_t)a3, (long)a4, (int)a5);
899 		break;
900 	case MSGSND:
901 		error = msgsnd((int)a1, (struct ipcmsgbuf *)a2,
902 		    (size_t)a3, (int)a4);
903 		break;
904 	case MSGIDS:
905 		error = msgids((int *)a1, (uint_t)a2, (uint_t *)a3);
906 		break;
907 	case MSGSNAP:
908 		error = msgsnap((int)a1, (caddr_t)a2, (size_t)a3, (long)a4);
909 		break;
910 	default:
911 		error = set_errno(EINVAL);
912 		break;
913 	}
914 
915 	return (error);
916 }
917 
918 #ifdef	_SYSCALL32_IMPL
919 /*
920  * msgsys32 - System entry point for msgctl, msgget, msgrcv, and msgsnd
921  * system calls for 32-bit callers on LP64 kernel.
922  */
923 static ssize32_t
924 msgsys32(int opcode, uint32_t a1, uint32_t a2, uint32_t a3,
925 	uint32_t a4, uint32_t a5)
926 {
927 	ssize_t error;
928 
929 	switch (opcode) {
930 	case MSGGET:
931 		error = msgget((key_t)a1, (int)a2);
932 		break;
933 	case MSGCTL:
934 		error = msgctl((int)a1, (int)a2, (void *)(uintptr_t)a3);
935 		break;
936 	case MSGRCV:
937 		error = msgrcv((int)a1, (struct ipcmsgbuf *)(uintptr_t)a2,
938 		    (size_t)a3, (long)(int32_t)a4, (int)a5);
939 		break;
940 	case MSGSND:
941 		error = msgsnd((int)a1, (struct ipcmsgbuf *)(uintptr_t)a2,
942 		    (size_t)(int32_t)a3, (int)a4);
943 		break;
944 	case MSGIDS:
945 		error = msgids((int *)(uintptr_t)a1, (uint_t)a2,
946 		    (uint_t *)(uintptr_t)a3);
947 		break;
948 	case MSGSNAP:
949 		error = msgsnap((int)a1, (caddr_t)(uintptr_t)a2, (size_t)a3,
950 		    (long)(int32_t)a4);
951 		break;
952 	default:
953 		error = set_errno(EINVAL);
954 		break;
955 	}
956 
957 	return (error);
958 }
959 #endif	/* SYSCALL32_IMPL */
960