xref: /netbsd-src/sys/kern/sysv_msg.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
1 /*	$NetBSD: sysv_msg.c,v 1.74 2019/04/10 10:03:50 pgoyette Exp $	*/
2 
3 /*-
4  * Copyright (c) 1999, 2006, 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center, and by Andrew Doran.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Implementation of SVID messages
35  *
36  * Author: Daniel Boulet
37  *
38  * Copyright 1993 Daniel Boulet and RTMX Inc.
39  *
40  * This system call was implemented by Daniel Boulet under contract from RTMX.
41  *
42  * Redistribution and use in source forms, with and without modification,
43  * are permitted provided that this entire comment appears intact.
44  *
45  * Redistribution in binary form may occur without any restrictions.
46  * Obviously, it would be nice if you gave credit where credit is due
47  * but requiring it would be too onerous.
48  *
49  * This software is provided ``AS IS'' without any warranties of any kind.
50  */
51 
52 #include <sys/cdefs.h>
53 __KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.74 2019/04/10 10:03:50 pgoyette Exp $");
54 
55 #ifdef _KERNEL_OPT
56 #include "opt_sysv.h"
57 #endif
58 
59 #include <sys/param.h>
60 #include <sys/kernel.h>
61 #include <sys/msg.h>
62 #include <sys/sysctl.h>
63 #include <sys/mount.h>		/* XXX for <sys/syscallargs.h> */
64 #include <sys/syscallargs.h>
65 #include <sys/kauth.h>
66 
67 #define MSG_DEBUG
68 #undef MSG_DEBUG_OK
69 
70 #ifdef MSG_DEBUG_OK
71 #define MSG_PRINTF(a)	printf a
72 #else
73 #define MSG_PRINTF(a)
74 #endif
75 
76 static int	nfree_msgmaps;		/* # of free map entries */
77 static short	free_msgmaps;	/* head of linked list of free map entries */
78 static struct	__msg *free_msghdrs;	/* list of free msg headers */
79 static char	*msgpool;		/* MSGMAX byte long msg buffer pool */
80 static struct	msgmap *msgmaps;	/* MSGSEG msgmap structures */
81 static struct __msg *msghdrs;		/* MSGTQL msg headers */
82 
83 kmsq_t	*msqs;				/* MSGMNI msqid_ds struct's */
84 kmutex_t msgmutex;			/* subsystem lock */
85 
86 static u_int	msg_waiters = 0;	/* total number of msgrcv waiters */
87 static bool	msg_realloc_state;
88 static kcondvar_t msg_realloc_cv;
89 
90 static void msg_freehdr(struct __msg *);
91 
92 extern int kern_has_sysvmsg;
93 
94 SYSCTL_SETUP_PROTO(sysctl_ipc_msg_setup);
95 
96 int
97 msginit(struct sysctllog **clog)
98 {
99 	int i, sz;
100 	vaddr_t v;
101 
102 	/*
103 	 * msginfo.msgssz should be a power of two for efficiency reasons.
104 	 * It is also pretty silly if msginfo.msgssz is less than 8
105 	 * or greater than about 256 so ...
106 	 */
107 
108 	i = 8;
109 	while (i < 1024 && i != msginfo.msgssz)
110 		i <<= 1;
111 	if (i != msginfo.msgssz) {
112 		printf("msginfo.msgssz = %d, not a small power of 2",
113 		    msginfo.msgssz);
114 		return EINVAL;
115 	}
116 
117 	if (msginfo.msgseg > 32767) {
118 		printf("msginfo.msgseg = %d > 32767", msginfo.msgseg);
119 		return EINVAL;
120 	}
121 
122 	/* Allocate the wired memory for our structures */
123 	sz = ALIGN(msginfo.msgmax) +
124 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
125 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
126 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
127 	sz = round_page(sz);
128 	v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
129 	if (v == 0) {
130 		printf("sysv_msg: cannot allocate memory");
131 		return ENOMEM;
132 	}
133 	msgpool = (void *)v;
134 	msgmaps = (void *)((uintptr_t)msgpool + ALIGN(msginfo.msgmax));
135 	msghdrs = (void *)((uintptr_t)msgmaps +
136 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)));
137 	msqs = (void *)((uintptr_t)msghdrs +
138 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)));
139 
140 	for (i = 0; i < (msginfo.msgseg - 1); i++)
141 		msgmaps[i].next = i + 1;
142 	msgmaps[msginfo.msgseg - 1].next = -1;
143 
144 	free_msgmaps = 0;
145 	nfree_msgmaps = msginfo.msgseg;
146 
147 	for (i = 0; i < (msginfo.msgtql - 1); i++) {
148 		msghdrs[i].msg_type = 0;
149 		msghdrs[i].msg_next = &msghdrs[i + 1];
150 	}
151 	i = msginfo.msgtql - 1;
152 	msghdrs[i].msg_type = 0;
153 	msghdrs[i].msg_next = NULL;
154 	free_msghdrs = &msghdrs[0];
155 
156 	for (i = 0; i < msginfo.msgmni; i++) {
157 		cv_init(&msqs[i].msq_cv, "msgwait");
158 		/* Implies entry is available */
159 		msqs[i].msq_u.msg_qbytes = 0;
160 		/* Reset to a known value */
161 		msqs[i].msq_u.msg_perm._seq = 0;
162 	}
163 
164 	mutex_init(&msgmutex, MUTEX_DEFAULT, IPL_NONE);
165 	cv_init(&msg_realloc_cv, "msgrealc");
166 	msg_realloc_state = false;
167 
168 	kern_has_sysvmsg = 1;
169 
170 #ifdef _MODULE
171 	if (clog)
172 		sysctl_ipc_msg_setup(clog);
173 #endif
174 	return 0;
175 }
176 
177 int
178 msgfini(void)
179 {
180 	int i, sz;
181 	vaddr_t v = (vaddr_t)msgpool;
182 
183 	mutex_enter(&msgmutex);
184 	for (i = 0; i < msginfo.msgmni; i++) {
185 		if (msqs[i].msq_u.msg_qbytes != 0) {
186 			mutex_exit(&msgmutex);
187 			return 1; /* queue not available, prevent unload! */
188 		}
189 	}
190 /*
191  * Destroy all condvars and free the memory we're using
192  */
193 	for (i = 0; i < msginfo.msgmni; i++) {
194 		cv_destroy(&msqs[i].msq_cv);
195 	}
196 	sz = ALIGN(msginfo.msgmax) +
197 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
198 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
199 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
200 	sz = round_page(sz);
201 	uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
202 
203 	cv_destroy(&msg_realloc_cv);
204 	mutex_exit(&msgmutex);
205 	mutex_destroy(&msgmutex);
206 
207 	kern_has_sysvmsg = 0;
208 
209 	return 0;
210 }
211 
212 static int
213 msgrealloc(int newmsgmni, int newmsgseg)
214 {
215 	struct msgmap *new_msgmaps;
216 	struct __msg *new_msghdrs, *new_free_msghdrs;
217 	char *old_msgpool, *new_msgpool;
218 	kmsq_t *new_msqs;
219 	vaddr_t v;
220 	int i, sz, msqid, newmsgmax, new_nfree_msgmaps;
221 	short new_free_msgmaps;
222 
223 	if (newmsgmni < 1 || newmsgseg < 1)
224 		return EINVAL;
225 
226 	/* Allocate the wired memory for our structures */
227 	newmsgmax = msginfo.msgssz * newmsgseg;
228 	sz = ALIGN(newmsgmax) +
229 	    ALIGN(newmsgseg * sizeof(struct msgmap)) +
230 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
231 	    ALIGN(newmsgmni * sizeof(kmsq_t));
232 	sz = round_page(sz);
233 	v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
234 	if (v == 0)
235 		return ENOMEM;
236 
237 	mutex_enter(&msgmutex);
238 	if (msg_realloc_state) {
239 		mutex_exit(&msgmutex);
240 		uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
241 		return EBUSY;
242 	}
243 	msg_realloc_state = true;
244 	if (msg_waiters) {
245 		/*
246 		 * Mark reallocation state, wake-up all waiters,
247 		 * and wait while they will all exit.
248 		 */
249 		for (i = 0; i < msginfo.msgmni; i++)
250 			cv_broadcast(&msqs[i].msq_cv);
251 		while (msg_waiters)
252 			cv_wait(&msg_realloc_cv, &msgmutex);
253 	}
254 	old_msgpool = msgpool;
255 
256 	/* We cannot reallocate less memory than we use */
257 	i = 0;
258 	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
259 		struct msqid_ds *mptr;
260 		kmsq_t *msq;
261 
262 		msq = &msqs[msqid];
263 		mptr = &msq->msq_u;
264 		if (mptr->msg_qbytes || (mptr->msg_perm.mode & MSG_LOCKED))
265 			i = msqid;
266 	}
267 	if (i >= newmsgmni || (msginfo.msgseg - nfree_msgmaps) > newmsgseg) {
268 		mutex_exit(&msgmutex);
269 		uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED);
270 		return EBUSY;
271 	}
272 
273 	new_msgpool = (void *)v;
274 	new_msgmaps = (void *)((uintptr_t)new_msgpool + ALIGN(newmsgmax));
275 	new_msghdrs = (void *)((uintptr_t)new_msgmaps +
276 	    ALIGN(newmsgseg * sizeof(struct msgmap)));
277 	new_msqs = (void *)((uintptr_t)new_msghdrs +
278 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)));
279 
280 	/* Initialize the structures */
281 	for (i = 0; i < (newmsgseg - 1); i++)
282 		new_msgmaps[i].next = i + 1;
283 	new_msgmaps[newmsgseg - 1].next = -1;
284 	new_free_msgmaps = 0;
285 	new_nfree_msgmaps = newmsgseg;
286 
287 	for (i = 0; i < (msginfo.msgtql - 1); i++) {
288 		new_msghdrs[i].msg_type = 0;
289 		new_msghdrs[i].msg_next = &new_msghdrs[i + 1];
290 	}
291 	i = msginfo.msgtql - 1;
292 	new_msghdrs[i].msg_type = 0;
293 	new_msghdrs[i].msg_next = NULL;
294 	new_free_msghdrs = &new_msghdrs[0];
295 
296 	for (i = 0; i < newmsgmni; i++) {
297 		new_msqs[i].msq_u.msg_qbytes = 0;
298 		new_msqs[i].msq_u.msg_perm._seq = 0;
299 		cv_init(&new_msqs[i].msq_cv, "msgwait");
300 	}
301 
302 	/*
303 	 * Copy all message queue identifiers, message headers and buffer
304 	 * pools to the new memory location.
305 	 */
306 	for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
307 		struct __msg *nmsghdr, *msghdr, *pmsghdr;
308 		struct msqid_ds *nmptr, *mptr;
309 		kmsq_t *nmsq, *msq;
310 
311 		msq = &msqs[msqid];
312 		mptr = &msq->msq_u;
313 
314 		if (mptr->msg_qbytes == 0 &&
315 		    (mptr->msg_perm.mode & MSG_LOCKED) == 0)
316 			continue;
317 
318 		nmsq = &new_msqs[msqid];
319 		nmptr = &nmsq->msq_u;
320 		memcpy(nmptr, mptr, sizeof(struct msqid_ds));
321 
322 		/*
323 		 * Go through the message headers, and copy each one
324 		 * by taking the new ones, and thus defragmenting.
325 		 */
326 		nmsghdr = pmsghdr = NULL;
327 		msghdr = mptr->_msg_first;
328 		while (msghdr) {
329 			short nnext = 0, next;
330 			u_short msgsz, segcnt;
331 
332 			/* Take an entry from the new list of free msghdrs */
333 			nmsghdr = new_free_msghdrs;
334 			KASSERT(nmsghdr != NULL);
335 			new_free_msghdrs = nmsghdr->msg_next;
336 
337 			nmsghdr->msg_next = NULL;
338 			if (pmsghdr) {
339 				pmsghdr->msg_next = nmsghdr;
340 			} else {
341 				nmptr->_msg_first = nmsghdr;
342 				pmsghdr = nmsghdr;
343 			}
344 			nmsghdr->msg_ts = msghdr->msg_ts;
345 			nmsghdr->msg_spot = -1;
346 
347 			/* Compute the amount of segments and reserve them */
348 			msgsz = msghdr->msg_ts;
349 			segcnt = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
350 			if (segcnt == 0)
351 				continue;
352 			while (segcnt--) {
353 				nnext = new_free_msgmaps;
354 				new_free_msgmaps = new_msgmaps[nnext].next;
355 				new_nfree_msgmaps--;
356 				new_msgmaps[nnext].next = nmsghdr->msg_spot;
357 				nmsghdr->msg_spot = nnext;
358 			}
359 
360 			/* Copy all segments */
361 			KASSERT(nnext == nmsghdr->msg_spot);
362 			next = msghdr->msg_spot;
363 			while (msgsz > 0) {
364 				size_t tlen;
365 
366 				if (msgsz >= msginfo.msgssz) {
367 					tlen = msginfo.msgssz;
368 					msgsz -= msginfo.msgssz;
369 				} else {
370 					tlen = msgsz;
371 					msgsz = 0;
372 				}
373 
374 				/* Copy the message buffer */
375 				memcpy(&new_msgpool[nnext * msginfo.msgssz],
376 				    &msgpool[next * msginfo.msgssz], tlen);
377 
378 				/* Next entry of the map */
379 				nnext = msgmaps[nnext].next;
380 				next = msgmaps[next].next;
381 			}
382 
383 			/* Next message header */
384 			msghdr = msghdr->msg_next;
385 		}
386 		nmptr->_msg_last = nmsghdr;
387 	}
388 	KASSERT((msginfo.msgseg - nfree_msgmaps) ==
389 	    (newmsgseg - new_nfree_msgmaps));
390 
391 	sz = ALIGN(msginfo.msgmax) +
392 	    ALIGN(msginfo.msgseg * sizeof(struct msgmap)) +
393 	    ALIGN(msginfo.msgtql * sizeof(struct __msg)) +
394 	    ALIGN(msginfo.msgmni * sizeof(kmsq_t));
395 	sz = round_page(sz);
396 
397 	for (i = 0; i < msginfo.msgmni; i++)
398 		cv_destroy(&msqs[i].msq_cv);
399 
400 	/* Set the pointers and update the new values */
401 	msgpool = new_msgpool;
402 	msgmaps = new_msgmaps;
403 	msghdrs = new_msghdrs;
404 	msqs = new_msqs;
405 
406 	free_msghdrs = new_free_msghdrs;
407 	free_msgmaps = new_free_msgmaps;
408 	nfree_msgmaps = new_nfree_msgmaps;
409 	msginfo.msgmni = newmsgmni;
410 	msginfo.msgseg = newmsgseg;
411 	msginfo.msgmax = newmsgmax;
412 
413 	/* Reallocation completed - notify all waiters, if any */
414 	msg_realloc_state = false;
415 	cv_broadcast(&msg_realloc_cv);
416 	mutex_exit(&msgmutex);
417 
418 	uvm_km_free(kernel_map, (vaddr_t)old_msgpool, sz, UVM_KMF_WIRED);
419 	return 0;
420 }
421 
422 static void
423 msg_freehdr(struct __msg *msghdr)
424 {
425 
426 	KASSERT(mutex_owned(&msgmutex));
427 
428 	while (msghdr->msg_ts > 0) {
429 		short next;
430 		KASSERT(msghdr->msg_spot >= 0);
431 		KASSERT(msghdr->msg_spot < msginfo.msgseg);
432 
433 		next = msgmaps[msghdr->msg_spot].next;
434 		msgmaps[msghdr->msg_spot].next = free_msgmaps;
435 		free_msgmaps = msghdr->msg_spot;
436 		nfree_msgmaps++;
437 		msghdr->msg_spot = next;
438 		if (msghdr->msg_ts >= msginfo.msgssz)
439 			msghdr->msg_ts -= msginfo.msgssz;
440 		else
441 			msghdr->msg_ts = 0;
442 	}
443 	KASSERT(msghdr->msg_spot == -1);
444 	msghdr->msg_next = free_msghdrs;
445 	free_msghdrs = msghdr;
446 }
447 
448 int
449 sys___msgctl50(struct lwp *l, const struct sys___msgctl50_args *uap,
450     register_t *retval)
451 {
452 	/* {
453 		syscallarg(int) msqid;
454 		syscallarg(int) cmd;
455 		syscallarg(struct msqid_ds *) buf;
456 	} */
457 	struct msqid_ds msqbuf;
458 	int cmd, error;
459 
460 	cmd = SCARG(uap, cmd);
461 
462 	if (cmd == IPC_SET) {
463 		error = copyin(SCARG(uap, buf), &msqbuf, sizeof(msqbuf));
464 		if (error)
465 			return (error);
466 	}
467 
468 	error = msgctl1(l, SCARG(uap, msqid), cmd,
469 	    (cmd == IPC_SET || cmd == IPC_STAT) ? &msqbuf : NULL);
470 
471 	if (error == 0 && cmd == IPC_STAT)
472 		error = copyout(&msqbuf, SCARG(uap, buf), sizeof(msqbuf));
473 
474 	return (error);
475 }
476 
477 int
478 msgctl1(struct lwp *l, int msqid, int cmd, struct msqid_ds *msqbuf)
479 {
480 	kauth_cred_t cred = l->l_cred;
481 	struct msqid_ds *msqptr;
482 	kmsq_t *msq;
483 	int error = 0, ix;
484 
485 	MSG_PRINTF(("call to msgctl1(%d, %d)\n", msqid, cmd));
486 
487 	ix = IPCID_TO_IX(msqid);
488 
489 	mutex_enter(&msgmutex);
490 
491 	if (ix < 0 || ix >= msginfo.msgmni) {
492 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix,
493 		    msginfo.msgmni));
494 		error = EINVAL;
495 		goto unlock;
496 	}
497 
498 	msq = &msqs[ix];
499 	msqptr = &msq->msq_u;
500 
501 	if (msqptr->msg_qbytes == 0) {
502 		MSG_PRINTF(("no such msqid\n"));
503 		error = EINVAL;
504 		goto unlock;
505 	}
506 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqid)) {
507 		MSG_PRINTF(("wrong sequence number\n"));
508 		error = EINVAL;
509 		goto unlock;
510 	}
511 
512 	switch (cmd) {
513 	case IPC_RMID:
514 	{
515 		struct __msg *msghdr;
516 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0)
517 			break;
518 		/* Free the message headers */
519 		msghdr = msqptr->_msg_first;
520 		while (msghdr != NULL) {
521 			struct __msg *msghdr_tmp;
522 
523 			/* Free the segments of each message */
524 			msqptr->_msg_cbytes -= msghdr->msg_ts;
525 			msqptr->msg_qnum--;
526 			msghdr_tmp = msghdr;
527 			msghdr = msghdr->msg_next;
528 			msg_freehdr(msghdr_tmp);
529 		}
530 		KASSERT(msqptr->_msg_cbytes == 0);
531 		KASSERT(msqptr->msg_qnum == 0);
532 
533 		/* Mark it as free */
534 		msqptr->msg_qbytes = 0;
535 		cv_broadcast(&msq->msq_cv);
536 	}
537 		break;
538 
539 	case IPC_SET:
540 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
541 			break;
542 		if (msqbuf->msg_qbytes > msqptr->msg_qbytes &&
543 		    kauth_authorize_system(cred, KAUTH_SYSTEM_SYSVIPC,
544 		    KAUTH_REQ_SYSTEM_SYSVIPC_MSGQ_OVERSIZE,
545 		    KAUTH_ARG(msqbuf->msg_qbytes),
546 		    KAUTH_ARG(msqptr->msg_qbytes), NULL) != 0) {
547 			error = EPERM;
548 			break;
549 		}
550 		if (msqbuf->msg_qbytes > msginfo.msgmnb) {
551 			MSG_PRINTF(("can't increase msg_qbytes beyond %d "
552 			    "(truncating)\n", msginfo.msgmnb));
553 			/* silently restrict qbytes to system limit */
554 			msqbuf->msg_qbytes = msginfo.msgmnb;
555 		}
556 		if (msqbuf->msg_qbytes == 0) {
557 			MSG_PRINTF(("can't reduce msg_qbytes to 0\n"));
558 			error = EINVAL;		/* XXX non-standard errno! */
559 			break;
560 		}
561 		msqptr->msg_perm.uid = msqbuf->msg_perm.uid;
562 		msqptr->msg_perm.gid = msqbuf->msg_perm.gid;
563 		msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
564 		    (msqbuf->msg_perm.mode & 0777);
565 		msqptr->msg_qbytes = msqbuf->msg_qbytes;
566 		msqptr->msg_ctime = time_second;
567 		break;
568 
569 	case IPC_STAT:
570 		if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
571 			MSG_PRINTF(("requester doesn't have read access\n"));
572 			break;
573 		}
574 		memset(msqbuf, 0, sizeof *msqbuf);
575 		msqbuf->msg_perm = msqptr->msg_perm;
576 		msqbuf->msg_perm.mode &= 0777;
577 		msqbuf->msg_qnum = msqptr->msg_qnum;
578 		msqbuf->msg_qbytes = msqptr->msg_qbytes;
579 		msqbuf->msg_lspid = msqptr->msg_lspid;
580 		msqbuf->msg_lrpid = msqptr->msg_lrpid;
581 		msqbuf->msg_stime = msqptr->msg_stime;
582 		msqbuf->msg_rtime = msqptr->msg_rtime;
583 		msqbuf->msg_ctime = msqptr->msg_ctime;
584 		break;
585 
586 	default:
587 		MSG_PRINTF(("invalid command %d\n", cmd));
588 		error = EINVAL;
589 		break;
590 	}
591 
592 unlock:
593 	mutex_exit(&msgmutex);
594 	return (error);
595 }
596 
597 int
598 sys_msgget(struct lwp *l, const struct sys_msgget_args *uap, register_t *retval)
599 {
600 	/* {
601 		syscallarg(key_t) key;
602 		syscallarg(int) msgflg;
603 	} */
604 	int msqid, error = 0;
605 	int key = SCARG(uap, key);
606 	int msgflg = SCARG(uap, msgflg);
607 	kauth_cred_t cred = l->l_cred;
608 	struct msqid_ds *msqptr = NULL;
609 	kmsq_t *msq;
610 
611 	mutex_enter(&msgmutex);
612 
613 	MSG_PRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
614 
615 	if (key != IPC_PRIVATE) {
616 		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
617 			msq = &msqs[msqid];
618 			msqptr = &msq->msq_u;
619 			if (msqptr->msg_qbytes != 0 &&
620 			    msqptr->msg_perm._key == key)
621 				break;
622 		}
623 		if (msqid < msginfo.msgmni) {
624 			MSG_PRINTF(("found public key\n"));
625 			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
626 				MSG_PRINTF(("not exclusive\n"));
627 				error = EEXIST;
628 				goto unlock;
629 			}
630 			if ((error = ipcperm(cred, &msqptr->msg_perm,
631 			    msgflg & 0700 ))) {
632 				MSG_PRINTF(("requester doesn't have 0%o access\n",
633 				    msgflg & 0700));
634 				goto unlock;
635 			}
636 			goto found;
637 		}
638 	}
639 
640 	MSG_PRINTF(("need to allocate the msqid_ds\n"));
641 	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
642 		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
643 			/*
644 			 * Look for an unallocated and unlocked msqid_ds.
645 			 * msqid_ds's can be locked by msgsnd or msgrcv while
646 			 * they are copying the message in/out.  We can't
647 			 * re-use the entry until they release it.
648 			 */
649 			msq = &msqs[msqid];
650 			msqptr = &msq->msq_u;
651 			if (msqptr->msg_qbytes == 0 &&
652 			    (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
653 				break;
654 		}
655 		if (msqid == msginfo.msgmni) {
656 			MSG_PRINTF(("no more msqid_ds's available\n"));
657 			error = ENOSPC;
658 			goto unlock;
659 		}
660 		MSG_PRINTF(("msqid %d is available\n", msqid));
661 		msqptr->msg_perm._key = key;
662 		msqptr->msg_perm.cuid = kauth_cred_geteuid(cred);
663 		msqptr->msg_perm.uid = kauth_cred_geteuid(cred);
664 		msqptr->msg_perm.cgid = kauth_cred_getegid(cred);
665 		msqptr->msg_perm.gid = kauth_cred_getegid(cred);
666 		msqptr->msg_perm.mode = (msgflg & 0777);
667 		/* Make sure that the returned msqid is unique */
668 		msqptr->msg_perm._seq++;
669 		msqptr->_msg_first = NULL;
670 		msqptr->_msg_last = NULL;
671 		msqptr->_msg_cbytes = 0;
672 		msqptr->msg_qnum = 0;
673 		msqptr->msg_qbytes = msginfo.msgmnb;
674 		msqptr->msg_lspid = 0;
675 		msqptr->msg_lrpid = 0;
676 		msqptr->msg_stime = 0;
677 		msqptr->msg_rtime = 0;
678 		msqptr->msg_ctime = time_second;
679 	} else {
680 		MSG_PRINTF(("didn't find it and wasn't asked to create it\n"));
681 		error = ENOENT;
682 		goto unlock;
683 	}
684 
685 found:
686 	/* Construct the unique msqid */
687 	*retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
688 
689 unlock:
690 	mutex_exit(&msgmutex);
691 	return (error);
692 }
693 
694 int
695 sys_msgsnd(struct lwp *l, const struct sys_msgsnd_args *uap, register_t *retval)
696 {
697 	/* {
698 		syscallarg(int) msqid;
699 		syscallarg(const void *) msgp;
700 		syscallarg(size_t) msgsz;
701 		syscallarg(int) msgflg;
702 	} */
703 
704 	return msgsnd1(l, SCARG(uap, msqid), SCARG(uap, msgp),
705 	    SCARG(uap, msgsz), SCARG(uap, msgflg), sizeof(long), copyin);
706 }
707 
708 int
709 msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz,
710     int msgflg, size_t typesz, copyin_t fetch_type)
711 {
712 	int segs_needed, error = 0, msqid;
713 	kauth_cred_t cred = l->l_cred;
714 	struct msqid_ds *msqptr;
715 	struct __msg *msghdr;
716 	kmsq_t *msq;
717 	short next;
718 
719 	MSG_PRINTF(("call to msgsnd(%d, %p, %lld, %d)\n", msqidr,
720 	     user_msgp, (long long)msgsz, msgflg));
721 
722 	if ((ssize_t)msgsz < 0)
723 		return EINVAL;
724 
725 restart:
726 	msqid = IPCID_TO_IX(msqidr);
727 
728 	mutex_enter(&msgmutex);
729 	/* In case of reallocation, we will wait for completion */
730 	while (__predict_false(msg_realloc_state))
731 		cv_wait(&msg_realloc_cv, &msgmutex);
732 
733 	if (msqid < 0 || msqid >= msginfo.msgmni) {
734 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
735 		    msginfo.msgmni));
736 		error = EINVAL;
737 		goto unlock;
738 	}
739 
740 	msq = &msqs[msqid];
741 	msqptr = &msq->msq_u;
742 
743 	if (msqptr->msg_qbytes == 0) {
744 		MSG_PRINTF(("no such message queue id\n"));
745 		error = EINVAL;
746 		goto unlock;
747 	}
748 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
749 		MSG_PRINTF(("wrong sequence number\n"));
750 		error = EINVAL;
751 		goto unlock;
752 	}
753 
754 	if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
755 		MSG_PRINTF(("requester doesn't have write access\n"));
756 		goto unlock;
757 	}
758 
759 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
760 	MSG_PRINTF(("msgsz=%lld, msgssz=%d, segs_needed=%d\n",
761 	    (long long)msgsz, msginfo.msgssz, segs_needed));
762 	for (;;) {
763 		int need_more_resources = 0;
764 
765 		/*
766 		 * check msgsz [cannot be negative since it is unsigned]
767 		 * (inside this loop in case msg_qbytes changes while we sleep)
768 		 */
769 
770 		if (msgsz > msqptr->msg_qbytes) {
771 			MSG_PRINTF(("msgsz > msqptr->msg_qbytes\n"));
772 			error = EINVAL;
773 			goto unlock;
774 		}
775 
776 		if (msqptr->msg_perm.mode & MSG_LOCKED) {
777 			MSG_PRINTF(("msqid is locked\n"));
778 			need_more_resources = 1;
779 		}
780 		if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes) {
781 			MSG_PRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
782 			need_more_resources = 1;
783 		}
784 		if (segs_needed > nfree_msgmaps) {
785 			MSG_PRINTF(("segs_needed > nfree_msgmaps\n"));
786 			need_more_resources = 1;
787 		}
788 		if (free_msghdrs == NULL) {
789 			MSG_PRINTF(("no more msghdrs\n"));
790 			need_more_resources = 1;
791 		}
792 
793 		if (need_more_resources) {
794 			int we_own_it;
795 
796 			if ((msgflg & IPC_NOWAIT) != 0) {
797 				MSG_PRINTF(("need more resources but caller "
798 				    "doesn't want to wait\n"));
799 				error = EAGAIN;
800 				goto unlock;
801 			}
802 
803 			if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
804 				MSG_PRINTF(("we don't own the msqid_ds\n"));
805 				we_own_it = 0;
806 			} else {
807 				/* Force later arrivals to wait for our
808 				   request */
809 				MSG_PRINTF(("we own the msqid_ds\n"));
810 				msqptr->msg_perm.mode |= MSG_LOCKED;
811 				we_own_it = 1;
812 			}
813 
814 			msg_waiters++;
815 			MSG_PRINTF(("goodnight\n"));
816 			error = cv_wait_sig(&msq->msq_cv, &msgmutex);
817 			MSG_PRINTF(("good morning, error=%d\n", error));
818 			msg_waiters--;
819 
820 			if (we_own_it)
821 				msqptr->msg_perm.mode &= ~MSG_LOCKED;
822 
823 			/*
824 			 * In case of such state, notify reallocator and
825 			 * restart the call.
826 			 */
827 			if (msg_realloc_state) {
828 				cv_broadcast(&msg_realloc_cv);
829 				mutex_exit(&msgmutex);
830 				goto restart;
831 			}
832 
833 			if (error != 0) {
834 				MSG_PRINTF(("msgsnd: interrupted system "
835 				    "call\n"));
836 				error = EINTR;
837 				goto unlock;
838 			}
839 
840 			/*
841 			 * Make sure that the msq queue still exists
842 			 */
843 
844 			if (msqptr->msg_qbytes == 0) {
845 				MSG_PRINTF(("msqid deleted\n"));
846 				error = EIDRM;
847 				goto unlock;
848 			}
849 		} else {
850 			MSG_PRINTF(("got all the resources that we need\n"));
851 			break;
852 		}
853 	}
854 
855 	/*
856 	 * We have the resources that we need.
857 	 * Make sure!
858 	 */
859 
860 	KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0);
861 	KASSERT(segs_needed <= nfree_msgmaps);
862 	KASSERT(msgsz + msqptr->_msg_cbytes <= msqptr->msg_qbytes);
863 	KASSERT(free_msghdrs != NULL);
864 
865 	/*
866 	 * Re-lock the msqid_ds in case we page-fault when copying in the
867 	 * message
868 	 */
869 
870 	KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0);
871 	msqptr->msg_perm.mode |= MSG_LOCKED;
872 
873 	/*
874 	 * Allocate a message header
875 	 */
876 
877 	msghdr = free_msghdrs;
878 	free_msghdrs = msghdr->msg_next;
879 	msghdr->msg_spot = -1;
880 	msghdr->msg_ts = msgsz;
881 
882 	/*
883 	 * Allocate space for the message
884 	 */
885 
886 	while (segs_needed > 0) {
887 		KASSERT(nfree_msgmaps > 0);
888 		KASSERT(free_msgmaps != -1);
889 		KASSERT(free_msgmaps < msginfo.msgseg);
890 
891 		next = free_msgmaps;
892 		MSG_PRINTF(("allocating segment %d to message\n", next));
893 		free_msgmaps = msgmaps[next].next;
894 		nfree_msgmaps--;
895 		msgmaps[next].next = msghdr->msg_spot;
896 		msghdr->msg_spot = next;
897 		segs_needed--;
898 	}
899 
900 	/*
901 	 * Copy in the message type
902 	 */
903 	mutex_exit(&msgmutex);
904 	error = (*fetch_type)(user_msgp, &msghdr->msg_type, typesz);
905 	mutex_enter(&msgmutex);
906 	if (error != 0) {
907 		MSG_PRINTF(("error %d copying the message type\n", error));
908 		msg_freehdr(msghdr);
909 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
910 		cv_broadcast(&msq->msq_cv);
911 		goto unlock;
912 	}
913 	user_msgp += typesz;
914 
915 	/*
916 	 * Validate the message type
917 	 */
918 
919 	if (msghdr->msg_type < 1) {
920 		msg_freehdr(msghdr);
921 		msqptr->msg_perm.mode &= ~MSG_LOCKED;
922 		cv_broadcast(&msq->msq_cv);
923 		MSG_PRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
924 		error = EINVAL;
925 		goto unlock;
926 	}
927 
928 	/*
929 	 * Copy in the message body
930 	 */
931 
932 	next = msghdr->msg_spot;
933 	while (msgsz > 0) {
934 		size_t tlen;
935 		KASSERT(next > -1);
936 		KASSERT(next < msginfo.msgseg);
937 
938 		if (msgsz > msginfo.msgssz)
939 			tlen = msginfo.msgssz;
940 		else
941 			tlen = msgsz;
942 		mutex_exit(&msgmutex);
943 		error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen);
944 		mutex_enter(&msgmutex);
945 		if (error != 0) {
946 			MSG_PRINTF(("error %d copying in message segment\n",
947 			    error));
948 			msg_freehdr(msghdr);
949 			msqptr->msg_perm.mode &= ~MSG_LOCKED;
950 			cv_broadcast(&msq->msq_cv);
951 			goto unlock;
952 		}
953 		msgsz -= tlen;
954 		user_msgp += tlen;
955 		next = msgmaps[next].next;
956 	}
957 	KASSERT(next == -1);
958 
959 	/*
960 	 * We've got the message.  Unlock the msqid_ds.
961 	 */
962 
963 	msqptr->msg_perm.mode &= ~MSG_LOCKED;
964 
965 	/*
966 	 * Make sure that the msqid_ds is still allocated.
967 	 */
968 
969 	if (msqptr->msg_qbytes == 0) {
970 		msg_freehdr(msghdr);
971 		cv_broadcast(&msq->msq_cv);
972 		error = EIDRM;
973 		goto unlock;
974 	}
975 
976 	/*
977 	 * Put the message into the queue
978 	 */
979 
980 	if (msqptr->_msg_first == NULL) {
981 		msqptr->_msg_first = msghdr;
982 		msqptr->_msg_last = msghdr;
983 	} else {
984 		msqptr->_msg_last->msg_next = msghdr;
985 		msqptr->_msg_last = msghdr;
986 	}
987 	msqptr->_msg_last->msg_next = NULL;
988 
989 	msqptr->_msg_cbytes += msghdr->msg_ts;
990 	msqptr->msg_qnum++;
991 	msqptr->msg_lspid = l->l_proc->p_pid;
992 	msqptr->msg_stime = time_second;
993 
994 	cv_broadcast(&msq->msq_cv);
995 
996 unlock:
997 	mutex_exit(&msgmutex);
998 	return error;
999 }
1000 
1001 int
1002 sys_msgrcv(struct lwp *l, const struct sys_msgrcv_args *uap, register_t *retval)
1003 {
1004 	/* {
1005 		syscallarg(int) msqid;
1006 		syscallarg(void *) msgp;
1007 		syscallarg(size_t) msgsz;
1008 		syscallarg(long) msgtyp;
1009 		syscallarg(int) msgflg;
1010 	} */
1011 
1012 	return msgrcv1(l, SCARG(uap, msqid), SCARG(uap, msgp),
1013 	    SCARG(uap, msgsz), SCARG(uap, msgtyp), SCARG(uap, msgflg),
1014 	    sizeof(long), copyout, retval);
1015 }
1016 
1017 int
1018 msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
1019     int msgflg, size_t typesz, copyout_t put_type, register_t *retval)
1020 {
1021 	size_t len;
1022 	kauth_cred_t cred = l->l_cred;
1023 	struct msqid_ds *msqptr;
1024 	struct __msg *msghdr;
1025 	int error = 0, msqid;
1026 	kmsq_t *msq;
1027 	short next;
1028 
1029 	MSG_PRINTF(("call to msgrcv(%d, %p, %lld, %ld, %d)\n", msqidr,
1030 	    user_msgp, (long long)msgsz, msgtyp, msgflg));
1031 
1032 	if ((ssize_t)msgsz < 0)
1033 		return EINVAL;
1034 
1035 restart:
1036 	msqid = IPCID_TO_IX(msqidr);
1037 
1038 	mutex_enter(&msgmutex);
1039 	/* In case of reallocation, we will wait for completion */
1040 	while (__predict_false(msg_realloc_state))
1041 		cv_wait(&msg_realloc_cv, &msgmutex);
1042 
1043 	if (msqid < 0 || msqid >= msginfo.msgmni) {
1044 		MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1045 		    msginfo.msgmni));
1046 		error = EINVAL;
1047 		goto unlock;
1048 	}
1049 
1050 	msq = &msqs[msqid];
1051 	msqptr = &msq->msq_u;
1052 
1053 	if (msqptr->msg_qbytes == 0) {
1054 		MSG_PRINTF(("no such message queue id\n"));
1055 		error = EINVAL;
1056 		goto unlock;
1057 	}
1058 	if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1059 		MSG_PRINTF(("wrong sequence number\n"));
1060 		error = EINVAL;
1061 		goto unlock;
1062 	}
1063 
1064 	if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
1065 		MSG_PRINTF(("requester doesn't have read access\n"));
1066 		goto unlock;
1067 	}
1068 
1069 	msghdr = NULL;
1070 	while (msghdr == NULL) {
1071 		if (msgtyp == 0) {
1072 			msghdr = msqptr->_msg_first;
1073 			if (msghdr != NULL) {
1074 				if (msgsz < msghdr->msg_ts &&
1075 				    (msgflg & MSG_NOERROR) == 0) {
1076 					MSG_PRINTF(("first msg on the queue "
1077 					    "is too big (want %lld, got %d)\n",
1078 					    (long long)msgsz, msghdr->msg_ts));
1079 					error = E2BIG;
1080 					goto unlock;
1081 				}
1082 				if (msqptr->_msg_first == msqptr->_msg_last) {
1083 					msqptr->_msg_first = NULL;
1084 					msqptr->_msg_last = NULL;
1085 				} else {
1086 					msqptr->_msg_first = msghdr->msg_next;
1087 					KASSERT(msqptr->_msg_first != NULL);
1088 				}
1089 			}
1090 		} else {
1091 			struct __msg *previous;
1092 			struct __msg **prev;
1093 
1094 			for (previous = NULL, prev = &msqptr->_msg_first;
1095 			     (msghdr = *prev) != NULL;
1096 			     previous = msghdr, prev = &msghdr->msg_next) {
1097 				/*
1098 				 * Is this message's type an exact match or is
1099 				 * this message's type less than or equal to
1100 				 * the absolute value of a negative msgtyp?
1101 				 * Note that the second half of this test can
1102 				 * NEVER be true if msgtyp is positive since
1103 				 * msg_type is always positive!
1104 				 */
1105 
1106 				if (msgtyp != msghdr->msg_type &&
1107 				    msghdr->msg_type > -msgtyp)
1108 					continue;
1109 
1110 				MSG_PRINTF(("found message type %ld, requested %ld\n",
1111 				    msghdr->msg_type, msgtyp));
1112 				if (msgsz < msghdr->msg_ts &&
1113 				     (msgflg & MSG_NOERROR) == 0) {
1114 					MSG_PRINTF(("requested message on the queue "
1115 					    "is too big (want %lld, got %d)\n",
1116 					    (long long)msgsz, msghdr->msg_ts));
1117 					error = E2BIG;
1118 					goto unlock;
1119 				}
1120 				*prev = msghdr->msg_next;
1121 				if (msghdr != msqptr->_msg_last)
1122 					break;
1123 				if (previous == NULL) {
1124 					KASSERT(prev == &msqptr->_msg_first);
1125 					msqptr->_msg_first = NULL;
1126 					msqptr->_msg_last = NULL;
1127 				} else {
1128 					KASSERT(prev != &msqptr->_msg_first);
1129 					msqptr->_msg_last = previous;
1130 				}
1131 				break;
1132 			}
1133 		}
1134 
1135 		/*
1136 		 * We've either extracted the msghdr for the appropriate
1137 		 * message or there isn't one.
1138 		 * If there is one then bail out of this loop.
1139 		 */
1140 		if (msghdr != NULL)
1141 			break;
1142 
1143 		/*
1144 		 * Hmph!  No message found.  Does the user want to wait?
1145 		 */
1146 
1147 		if ((msgflg & IPC_NOWAIT) != 0) {
1148 			MSG_PRINTF(("no appropriate message found (msgtyp=%ld)\n",
1149 			    msgtyp));
1150 			error = ENOMSG;
1151 			goto unlock;
1152 		}
1153 
1154 		/*
1155 		 * Wait for something to happen
1156 		 */
1157 
1158 		msg_waiters++;
1159 		MSG_PRINTF(("msgrcv:  goodnight\n"));
1160 		error = cv_wait_sig(&msq->msq_cv, &msgmutex);
1161 		MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error));
1162 		msg_waiters--;
1163 
1164 		/*
1165 		 * In case of such state, notify reallocator and
1166 		 * restart the call.
1167 		 */
1168 		if (msg_realloc_state) {
1169 			cv_broadcast(&msg_realloc_cv);
1170 			mutex_exit(&msgmutex);
1171 			goto restart;
1172 		}
1173 
1174 		if (error != 0) {
1175 			MSG_PRINTF(("msgsnd: interrupted system call\n"));
1176 			error = EINTR;
1177 			goto unlock;
1178 		}
1179 
1180 		/*
1181 		 * Make sure that the msq queue still exists
1182 		 */
1183 
1184 		if (msqptr->msg_qbytes == 0 ||
1185 		    msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1186 			MSG_PRINTF(("msqid deleted\n"));
1187 			error = EIDRM;
1188 			goto unlock;
1189 		}
1190 	}
1191 
1192 	/*
1193 	 * Return the message to the user.
1194 	 *
1195 	 * First, do the bookkeeping (before we risk being interrupted).
1196 	 */
1197 
1198 	msqptr->_msg_cbytes -= msghdr->msg_ts;
1199 	msqptr->msg_qnum--;
1200 	msqptr->msg_lrpid = l->l_proc->p_pid;
1201 	msqptr->msg_rtime = time_second;
1202 
1203 	/*
1204 	 * Make msgsz the actual amount that we'll be returning.
1205 	 * Note that this effectively truncates the message if it is too long
1206 	 * (since msgsz is never increased).
1207 	 */
1208 
1209 	MSG_PRINTF(("found a message, msgsz=%lld, msg_ts=%d\n",
1210 	    (long long)msgsz, msghdr->msg_ts));
1211 	if (msgsz > msghdr->msg_ts)
1212 		msgsz = msghdr->msg_ts;
1213 
1214 	/*
1215 	 * Return the type to the user.
1216 	 */
1217 	mutex_exit(&msgmutex);
1218 	error = (*put_type)(&msghdr->msg_type, user_msgp, typesz);
1219 	mutex_enter(&msgmutex);
1220 	if (error != 0) {
1221 		MSG_PRINTF(("error (%d) copying out message type\n", error));
1222 		msg_freehdr(msghdr);
1223 		cv_broadcast(&msq->msq_cv);
1224 		goto unlock;
1225 	}
1226 	user_msgp += typesz;
1227 
1228 	/*
1229 	 * Return the segments to the user
1230 	 */
1231 
1232 	next = msghdr->msg_spot;
1233 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
1234 		size_t tlen;
1235 		KASSERT(next > -1);
1236 		KASSERT(next < msginfo.msgseg);
1237 
1238 		if (msgsz - len > msginfo.msgssz)
1239 			tlen = msginfo.msgssz;
1240 		else
1241 			tlen = msgsz - len;
1242 		mutex_exit(&msgmutex);
1243 		error = copyout(&msgpool[next * msginfo.msgssz],
1244 		    user_msgp, tlen);
1245 		mutex_enter(&msgmutex);
1246 		if (error != 0) {
1247 			MSG_PRINTF(("error (%d) copying out message segment\n",
1248 			    error));
1249 			msg_freehdr(msghdr);
1250 			cv_broadcast(&msq->msq_cv);
1251 			goto unlock;
1252 		}
1253 		user_msgp += tlen;
1254 		next = msgmaps[next].next;
1255 	}
1256 
1257 	/*
1258 	 * Done, return the actual number of bytes copied out.
1259 	 */
1260 
1261 	msg_freehdr(msghdr);
1262 	cv_broadcast(&msq->msq_cv);
1263 	*retval = msgsz;
1264 
1265 unlock:
1266 	mutex_exit(&msgmutex);
1267 	return error;
1268 }
1269 
1270 /*
1271  * Sysctl initialization and nodes.
1272  */
1273 
1274 static int
1275 sysctl_ipc_msgmni(SYSCTLFN_ARGS)
1276 {
1277 	int newsize, error;
1278 	struct sysctlnode node;
1279 	node = *rnode;
1280 	node.sysctl_data = &newsize;
1281 
1282 	newsize = msginfo.msgmni;
1283 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1284 	if (error || newp == NULL)
1285 		return error;
1286 
1287 	sysctl_unlock();
1288 	error = msgrealloc(newsize, msginfo.msgseg);
1289 	sysctl_relock();
1290 	return error;
1291 }
1292 
1293 static int
1294 sysctl_ipc_msgseg(SYSCTLFN_ARGS)
1295 {
1296 	int newsize, error;
1297 	struct sysctlnode node;
1298 	node = *rnode;
1299 	node.sysctl_data = &newsize;
1300 
1301 	newsize = msginfo.msgseg;
1302 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1303 	if (error || newp == NULL)
1304 		return error;
1305 
1306 	sysctl_unlock();
1307 	error = msgrealloc(msginfo.msgmni, newsize);
1308 	sysctl_relock();
1309 	return error;
1310 }
1311 
1312 SYSCTL_SETUP(sysctl_ipc_msg_setup, "sysctl kern.ipc subtree setup")
1313 {
1314 	const struct sysctlnode *node = NULL;
1315 
1316 	sysctl_createv(clog, 0, NULL, &node,
1317 		CTLFLAG_PERMANENT,
1318 		CTLTYPE_NODE, "ipc",
1319 		SYSCTL_DESCR("SysV IPC options"),
1320 		NULL, 0, NULL, 0,
1321 		CTL_KERN, KERN_SYSVIPC, CTL_EOL);
1322 
1323 	if (node == NULL)
1324 		return;
1325 
1326 	sysctl_createv(clog, 0, &node, NULL,
1327 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1328 		CTLTYPE_INT, "msgmni",
1329 		SYSCTL_DESCR("Max number of message queue identifiers"),
1330 		sysctl_ipc_msgmni, 0, &msginfo.msgmni, 0,
1331 		CTL_CREATE, CTL_EOL);
1332 	sysctl_createv(clog, 0, &node, NULL,
1333 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
1334 		CTLTYPE_INT, "msgseg",
1335 		SYSCTL_DESCR("Max number of number of message segments"),
1336 		sysctl_ipc_msgseg, 0, &msginfo.msgseg, 0,
1337 		CTL_CREATE, CTL_EOL);
1338 }
1339