xref: /dflybsd-src/sys/kern/sys_mqueue.c (revision cc8e70bd591c943565dd618d131dcee0027ded02)
1f2df0f7cSStathis Kamperis /*	$NetBSD: sys_mqueue.c,v 1.16 2009/04/11 23:05:26 christos Exp $	*/
2f2df0f7cSStathis Kamperis 
3f2df0f7cSStathis Kamperis /*
4f2df0f7cSStathis Kamperis  * Copyright (c) 2007, 2008 Mindaugas Rasiukevicius <rmind at NetBSD org>
5f2df0f7cSStathis Kamperis  * All rights reserved.
6f2df0f7cSStathis Kamperis  *
7f2df0f7cSStathis Kamperis  * Redistribution and use in source and binary forms, with or without
8f2df0f7cSStathis Kamperis  * modification, are permitted provided that the following conditions
9f2df0f7cSStathis Kamperis  * are met:
10f2df0f7cSStathis Kamperis  * 1. Redistributions of source code must retain the above copyright
11f2df0f7cSStathis Kamperis  *    notice, this list of conditions and the following disclaimer.
12f2df0f7cSStathis Kamperis  * 2. Redistributions in binary form must reproduce the above copyright
13f2df0f7cSStathis Kamperis  *    notice, this list of conditions and the following disclaimer in the
14f2df0f7cSStathis Kamperis  *    documentation and/or other materials provided with the distribution.
15f2df0f7cSStathis Kamperis  *
16f2df0f7cSStathis Kamperis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f2df0f7cSStathis Kamperis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f2df0f7cSStathis Kamperis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f2df0f7cSStathis Kamperis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f2df0f7cSStathis Kamperis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f2df0f7cSStathis Kamperis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f2df0f7cSStathis Kamperis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f2df0f7cSStathis Kamperis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f2df0f7cSStathis Kamperis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f2df0f7cSStathis Kamperis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f2df0f7cSStathis Kamperis  * SUCH DAMAGE.
27f2df0f7cSStathis Kamperis  */
28f2df0f7cSStathis Kamperis 
29f2df0f7cSStathis Kamperis /*
30f2df0f7cSStathis Kamperis  * Implementation of POSIX message queues.
31f2df0f7cSStathis Kamperis  * Defined in the Base Definitions volume of IEEE Std 1003.1-2001.
32f2df0f7cSStathis Kamperis  *
33f2df0f7cSStathis Kamperis  * Locking
34f2df0f7cSStathis Kamperis  *
35f2df0f7cSStathis Kamperis  * Global list of message queues (mqueue_head) and proc_t::p_mqueue_cnt
36f2df0f7cSStathis Kamperis  * counter are protected by mqlist_mtx lock.  The very message queue and
37f2df0f7cSStathis Kamperis  * its members are protected by mqueue::mq_mtx.
38f2df0f7cSStathis Kamperis  *
39f2df0f7cSStathis Kamperis  * Lock order:
40f2df0f7cSStathis Kamperis  *	mqlist_mtx
41f2df0f7cSStathis Kamperis  *	  -> mqueue::mq_mtx
42f2df0f7cSStathis Kamperis  */
43f2df0f7cSStathis Kamperis 
44f2df0f7cSStathis Kamperis #include <sys/param.h>
45f2df0f7cSStathis Kamperis #include <sys/types.h>
46f2df0f7cSStathis Kamperis #include <sys/errno.h>
47f2df0f7cSStathis Kamperis #include <sys/fcntl.h>
48f2df0f7cSStathis Kamperis #include <sys/file.h>
49f2df0f7cSStathis Kamperis #include <sys/filedesc.h>
50f2df0f7cSStathis Kamperis #include <sys/ucred.h>
512b3f93eaSMatthew Dillon #include <sys/caps.h>
52f2df0f7cSStathis Kamperis #include <sys/kernel.h>
53f2df0f7cSStathis Kamperis #include <sys/malloc.h>
54f2df0f7cSStathis Kamperis #include <sys/mqueue.h>
55f2df0f7cSStathis Kamperis #include <sys/proc.h>
56f2df0f7cSStathis Kamperis #include <sys/queue.h>
57b06aab00SSamuel J. Greear #include <sys/event.h>
58f2df0f7cSStathis Kamperis #include <sys/serialize.h>
59f2df0f7cSStathis Kamperis #include <sys/signal.h>
60f2df0f7cSStathis Kamperis #include <sys/signalvar.h>
61f2df0f7cSStathis Kamperis #include <sys/spinlock.h>
62f2df0f7cSStathis Kamperis #include <sys/spinlock2.h>
63f2df0f7cSStathis Kamperis #include <sys/stat.h>
64f2df0f7cSStathis Kamperis #include <sys/sysctl.h>
6580d831e1SMatthew Dillon #include <sys/sysmsg.h>
66f2df0f7cSStathis Kamperis #include <sys/systm.h>
67f2df0f7cSStathis Kamperis #include <sys/lock.h>
68f2df0f7cSStathis Kamperis #include <sys/unistd.h>
69f2df0f7cSStathis Kamperis #include <sys/vnode.h>
70f2df0f7cSStathis Kamperis 
71f2df0f7cSStathis Kamperis /* System-wide limits. */
72f2df0f7cSStathis Kamperis static u_int			mq_open_max = MQ_OPEN_MAX;
73f2df0f7cSStathis Kamperis static u_int			mq_prio_max = MQ_PRIO_MAX;
74f2df0f7cSStathis Kamperis static u_int			mq_max_msgsize = 16 * MQ_DEF_MSGSIZE;
75f2df0f7cSStathis Kamperis static u_int			mq_def_maxmsg = 32;
768658b626SStathis Kamperis static u_int			mq_max_maxmsg = 16 * 32;
77f2df0f7cSStathis Kamperis 
78f4e72adaSSascha Wildner static struct lock		mqlist_mtx;
79f2df0f7cSStathis Kamperis static LIST_HEAD(, mqueue)	mqueue_head =
80f2df0f7cSStathis Kamperis 	LIST_HEAD_INITIALIZER(mqueue_head);
81f2df0f7cSStathis Kamperis 
82f2df0f7cSStathis Kamperis typedef struct	file file_t;	/* XXX: Should we put this in sys/types.h ? */
83f2df0f7cSStathis Kamperis 
84f2df0f7cSStathis Kamperis /* Function prototypes */
85f2df0f7cSStathis Kamperis static int	mq_stat_fop(file_t *, struct stat *, struct ucred *cred);
86f2df0f7cSStathis Kamperis static int	mq_close_fop(file_t *);
87b06aab00SSamuel J. Greear static int	mq_kqfilter_fop(struct file *fp, struct knote *kn);
88b06aab00SSamuel J. Greear static void	mqfilter_read_detach(struct knote *kn);
89b06aab00SSamuel J. Greear static void	mqfilter_write_detach(struct knote *kn);
90b06aab00SSamuel J. Greear static int	mqfilter_read(struct knote *kn, long hint);
91b06aab00SSamuel J. Greear static int	mqfilter_write(struct knote *kn, long hint);
92f2df0f7cSStathis Kamperis 
93f2df0f7cSStathis Kamperis /* Some time-related utility functions */
94f2df0f7cSStathis Kamperis static int	tstohz(const struct timespec *ts);
95f2df0f7cSStathis Kamperis 
96f2df0f7cSStathis Kamperis /* File operations vector */
97f2df0f7cSStathis Kamperis static struct fileops mqops = {
98f2df0f7cSStathis Kamperis 	.fo_read = badfo_readwrite,
99f2df0f7cSStathis Kamperis 	.fo_write = badfo_readwrite,
100f2df0f7cSStathis Kamperis 	.fo_ioctl = badfo_ioctl,
101f2df0f7cSStathis Kamperis 	.fo_stat = mq_stat_fop,
102f2df0f7cSStathis Kamperis 	.fo_close = mq_close_fop,
103b06aab00SSamuel J. Greear 	.fo_kqfilter = mq_kqfilter_fop,
104*cc8e70bdSSergey Zigachev 	.fo_shutdown = badfo_shutdown,
105*cc8e70bdSSergey Zigachev 	.fo_seek = badfo_seek
106f2df0f7cSStathis Kamperis };
107f2df0f7cSStathis Kamperis 
108f2df0f7cSStathis Kamperis /* Define a new malloc type for message queues */
109f2df0f7cSStathis Kamperis MALLOC_DECLARE(M_MQBUF);
110f2df0f7cSStathis Kamperis MALLOC_DEFINE(M_MQBUF, "mqueues", "Buffers to message queues");
111f2df0f7cSStathis Kamperis 
112f2df0f7cSStathis Kamperis /*
113f2df0f7cSStathis Kamperis  * Initialize POSIX message queue subsystem.
114f2df0f7cSStathis Kamperis  */
115f2df0f7cSStathis Kamperis void
mqueue_sysinit(void)116f2df0f7cSStathis Kamperis mqueue_sysinit(void)
117f2df0f7cSStathis Kamperis {
118f2df0f7cSStathis Kamperis 	lockinit(&mqlist_mtx, "mqlist_mtx", 0, LK_CANRECURSE);
119f2df0f7cSStathis Kamperis }
120f2df0f7cSStathis Kamperis 
121f2df0f7cSStathis Kamperis /*
122f2df0f7cSStathis Kamperis  * Free the message.
123f2df0f7cSStathis Kamperis  */
124f2df0f7cSStathis Kamperis static void
mqueue_freemsg(struct mq_msg * msg,const size_t size)125f2df0f7cSStathis Kamperis mqueue_freemsg(struct mq_msg *msg, const size_t size)
126f2df0f7cSStathis Kamperis {
127f2df0f7cSStathis Kamperis 	kfree(msg, M_MQBUF);
128d1d64679SStathis Kamperis }
129f2df0f7cSStathis Kamperis 
130f2df0f7cSStathis Kamperis /*
131f2df0f7cSStathis Kamperis  * Destroy the message queue.
132f2df0f7cSStathis Kamperis  */
133f2df0f7cSStathis Kamperis static void
mqueue_destroy(struct mqueue * mq)134f2df0f7cSStathis Kamperis mqueue_destroy(struct mqueue *mq)
135f2df0f7cSStathis Kamperis {
136f2df0f7cSStathis Kamperis 	struct mq_msg *msg;
137f2df0f7cSStathis Kamperis 	size_t msz;
138f2df0f7cSStathis Kamperis 	u_int i;
139f2df0f7cSStathis Kamperis 
140f2df0f7cSStathis Kamperis 	/* Note MQ_PQSIZE + 1. */
141f2df0f7cSStathis Kamperis 	for (i = 0; i < MQ_PQSIZE + 1; i++) {
142f2df0f7cSStathis Kamperis 		while ((msg = TAILQ_FIRST(&mq->mq_head[i])) != NULL) {
143f2df0f7cSStathis Kamperis 			TAILQ_REMOVE(&mq->mq_head[i], msg, msg_queue);
144f2df0f7cSStathis Kamperis 			msz = sizeof(struct mq_msg) + msg->msg_len;
145f2df0f7cSStathis Kamperis 			mqueue_freemsg(msg, msz);
146f2df0f7cSStathis Kamperis 		}
147f2df0f7cSStathis Kamperis 	}
148f2df0f7cSStathis Kamperis 	lockuninit(&mq->mq_mtx);
149f2df0f7cSStathis Kamperis 	kfree(mq, M_MQBUF);
150f2df0f7cSStathis Kamperis }
151f2df0f7cSStathis Kamperis 
152f2df0f7cSStathis Kamperis /*
153f2df0f7cSStathis Kamperis  * Lookup for file name in general list of message queues.
154f2df0f7cSStathis Kamperis  *  => locks the message queue
155f2df0f7cSStathis Kamperis  */
156f2df0f7cSStathis Kamperis static void *
mqueue_lookup(char * name)157f2df0f7cSStathis Kamperis mqueue_lookup(char *name)
158f2df0f7cSStathis Kamperis {
159f2df0f7cSStathis Kamperis 	struct mqueue *mq;
160f2df0f7cSStathis Kamperis 
161f2df0f7cSStathis Kamperis 	KKASSERT(lockstatus(&mqlist_mtx, curthread));
162f2df0f7cSStathis Kamperis 
163f2df0f7cSStathis Kamperis 	LIST_FOREACH(mq, &mqueue_head, mq_list) {
164f2df0f7cSStathis Kamperis 		if (strncmp(mq->mq_name, name, MQ_NAMELEN) == 0) {
165f2df0f7cSStathis Kamperis 			lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
166f2df0f7cSStathis Kamperis 			return mq;
167f2df0f7cSStathis Kamperis 		}
168f2df0f7cSStathis Kamperis 	}
169f2df0f7cSStathis Kamperis 
170f2df0f7cSStathis Kamperis 	return NULL;
171f2df0f7cSStathis Kamperis }
172f2df0f7cSStathis Kamperis 
173f2df0f7cSStathis Kamperis /*
174f2df0f7cSStathis Kamperis  * mqueue_get: get the mqueue from the descriptor.
175f2df0f7cSStathis Kamperis  *  => locks the message queue, if found.
176d1d64679SStathis Kamperis  *  => holds a reference on the file descriptor.
177f2df0f7cSStathis Kamperis  */
178f2df0f7cSStathis Kamperis static int
mqueue_get(struct lwp * l,mqd_t mqd,file_t ** fpr)179f2df0f7cSStathis Kamperis mqueue_get(struct lwp *l, mqd_t mqd, file_t **fpr)
180f2df0f7cSStathis Kamperis {
181f2df0f7cSStathis Kamperis 	struct mqueue *mq;
182f2df0f7cSStathis Kamperis 	file_t *fp;
183f2df0f7cSStathis Kamperis 
18435949930SMatthew Dillon 	fp = holdfp(curthread, (int)mqd, -1);	/* XXX: Why -1 ? */
185f2df0f7cSStathis Kamperis 	if (__predict_false(fp == NULL))
186f2df0f7cSStathis Kamperis 		return EBADF;
187f2df0f7cSStathis Kamperis 
188f2df0f7cSStathis Kamperis 	if (__predict_false(fp->f_type != DTYPE_MQUEUE)) {
189f2df0f7cSStathis Kamperis 		fdrop(fp);
190f2df0f7cSStathis Kamperis 		return EBADF;
191f2df0f7cSStathis Kamperis 	}
192f2df0f7cSStathis Kamperis 	mq = fp->f_data;
193f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
194f2df0f7cSStathis Kamperis 
195f2df0f7cSStathis Kamperis 	*fpr = fp;
196f2df0f7cSStathis Kamperis 	return 0;
197f2df0f7cSStathis Kamperis }
198f2df0f7cSStathis Kamperis 
199f2df0f7cSStathis Kamperis /*
200f2df0f7cSStathis Kamperis  * mqueue_linear_insert: perform linear insert according to the message
201f2df0f7cSStathis Kamperis  * priority into the reserved queue (MQ_PQRESQ).  Reserved queue is a
202f2df0f7cSStathis Kamperis  * sorted list used only when mq_prio_max is increased via sysctl.
203f2df0f7cSStathis Kamperis  */
204f2df0f7cSStathis Kamperis static inline void
mqueue_linear_insert(struct mqueue * mq,struct mq_msg * msg)205f2df0f7cSStathis Kamperis mqueue_linear_insert(struct mqueue *mq, struct mq_msg *msg)
206f2df0f7cSStathis Kamperis {
207f2df0f7cSStathis Kamperis 	struct mq_msg *mit;
208f2df0f7cSStathis Kamperis 
209f2df0f7cSStathis Kamperis 	TAILQ_FOREACH(mit, &mq->mq_head[MQ_PQRESQ], msg_queue) {
210f2df0f7cSStathis Kamperis 		if (msg->msg_prio > mit->msg_prio)
211f2df0f7cSStathis Kamperis 			break;
212f2df0f7cSStathis Kamperis 	}
213f2df0f7cSStathis Kamperis 	if (mit == NULL) {
214f2df0f7cSStathis Kamperis 		TAILQ_INSERT_TAIL(&mq->mq_head[MQ_PQRESQ], msg, msg_queue);
215f2df0f7cSStathis Kamperis 	} else {
216f2df0f7cSStathis Kamperis 		TAILQ_INSERT_BEFORE(mit, msg, msg_queue);
217f2df0f7cSStathis Kamperis 	}
218f2df0f7cSStathis Kamperis }
219f2df0f7cSStathis Kamperis 
220f2df0f7cSStathis Kamperis /*
221f2df0f7cSStathis Kamperis  * Compute number of ticks in the specified amount of time.
222f2df0f7cSStathis Kamperis  */
22359b728a7SSascha Wildner static int
tstohz(const struct timespec * ts)224f2df0f7cSStathis Kamperis tstohz(const struct timespec *ts)
225f2df0f7cSStathis Kamperis {
226f2df0f7cSStathis Kamperis 	struct timeval tv;
227f2df0f7cSStathis Kamperis 
228f2df0f7cSStathis Kamperis 	/*
229f2df0f7cSStathis Kamperis 	 * usec has great enough resolution for hz, so convert to a
230f2df0f7cSStathis Kamperis 	 * timeval and use tvtohz() above.
231f2df0f7cSStathis Kamperis 	 */
232f2df0f7cSStathis Kamperis 	TIMESPEC_TO_TIMEVAL(&tv, ts);
233f2df0f7cSStathis Kamperis 	return tvtohz_high(&tv);	/* XXX Why _high() and not _low() ? */
234f2df0f7cSStathis Kamperis }
235f2df0f7cSStathis Kamperis 
236f2df0f7cSStathis Kamperis /*
237f2df0f7cSStathis Kamperis  * Converter from struct timespec to the ticks.
238f2df0f7cSStathis Kamperis  * Used by mq_timedreceive(), mq_timedsend().
239f2df0f7cSStathis Kamperis  */
240f2df0f7cSStathis Kamperis int
abstimeout2timo(struct timespec * ts,int * timo)241f2df0f7cSStathis Kamperis abstimeout2timo(struct timespec *ts, int *timo)
242f2df0f7cSStathis Kamperis {
243f2df0f7cSStathis Kamperis 	struct timespec tsd;
244f2df0f7cSStathis Kamperis 	int error;
245f2df0f7cSStathis Kamperis 
246959f863fSStathis Kamperis 	error = itimespecfix(ts);
247959f863fSStathis Kamperis 	if (error) {
248959f863fSStathis Kamperis 		return error;
249a27ab186SStathis Kamperis 	}
250f2df0f7cSStathis Kamperis 	getnanotime(&tsd);
251944cd60cSSascha Wildner 	timespecsub(ts, &tsd, ts);
252f2df0f7cSStathis Kamperis 	if (ts->tv_sec < 0 || (ts->tv_sec == 0 && ts->tv_nsec <= 0)) {
253f2df0f7cSStathis Kamperis 		return ETIMEDOUT;
254f2df0f7cSStathis Kamperis 	}
255f2df0f7cSStathis Kamperis 	*timo = tstohz(ts);
256f2df0f7cSStathis Kamperis 	KKASSERT(*timo != 0);
257f2df0f7cSStathis Kamperis 
258f2df0f7cSStathis Kamperis 	return 0;
259f2df0f7cSStathis Kamperis }
260f2df0f7cSStathis Kamperis 
261f2df0f7cSStathis Kamperis static int
mq_stat_fop(file_t * fp,struct stat * st,struct ucred * cred)262f2df0f7cSStathis Kamperis mq_stat_fop(file_t *fp, struct stat *st, struct ucred *cred)
263f2df0f7cSStathis Kamperis {
264f2df0f7cSStathis Kamperis 	struct mqueue *mq = fp->f_data;
265f2df0f7cSStathis Kamperis 
266f2df0f7cSStathis Kamperis 	(void)memset(st, 0, sizeof(*st));
267f2df0f7cSStathis Kamperis 
268f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
269f2df0f7cSStathis Kamperis 	st->st_mode = mq->mq_mode;
270f2df0f7cSStathis Kamperis 	st->st_uid = mq->mq_euid;
271f2df0f7cSStathis Kamperis 	st->st_gid = mq->mq_egid;
272f2df0f7cSStathis Kamperis 	st->st_atimespec = mq->mq_atime;
273f2df0f7cSStathis Kamperis 	st->st_mtimespec = mq->mq_mtime;
274f2df0f7cSStathis Kamperis 	/*st->st_ctimespec = st->st_birthtimespec = mq->mq_btime;*/
275f2df0f7cSStathis Kamperis 	st->st_uid = fp->f_cred->cr_uid;
276f2df0f7cSStathis Kamperis 	st->st_gid = fp->f_cred->cr_svgid;
277f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
278f2df0f7cSStathis Kamperis 
279f2df0f7cSStathis Kamperis 	return 0;
280f2df0f7cSStathis Kamperis }
281f2df0f7cSStathis Kamperis 
282b06aab00SSamuel J. Greear static struct filterops mqfiltops_read =
28345a3dbb7SVenkatesh Srinivas { FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, mqfilter_read_detach, mqfilter_read };
284b06aab00SSamuel J. Greear static struct filterops mqfiltops_write =
28545a3dbb7SVenkatesh Srinivas { FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, mqfilter_write_detach, mqfilter_write };
286b06aab00SSamuel J. Greear 
287b06aab00SSamuel J. Greear static int
mq_kqfilter_fop(struct file * fp,struct knote * kn)288b06aab00SSamuel J. Greear mq_kqfilter_fop(struct file *fp, struct knote *kn)
289b06aab00SSamuel J. Greear {
290b06aab00SSamuel J. Greear 	struct mqueue *mq = fp->f_data;
291b06aab00SSamuel J. Greear 	struct klist *klist;
292b06aab00SSamuel J. Greear 
293b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
294b06aab00SSamuel J. Greear 
295b06aab00SSamuel J. Greear 	switch (kn->kn_filter) {
296b06aab00SSamuel J. Greear 	case EVFILT_READ:
297b06aab00SSamuel J. Greear 		kn->kn_fop = &mqfiltops_read;
298b06aab00SSamuel J. Greear 		kn->kn_hook = (caddr_t)mq;
2995b22f1a7SSamuel J. Greear 		klist = &mq->mq_rkq.ki_note;
300b06aab00SSamuel J. Greear 		break;
301b06aab00SSamuel J. Greear 	case EVFILT_WRITE:
302b06aab00SSamuel J. Greear 		kn->kn_fop = &mqfiltops_write;
303b06aab00SSamuel J. Greear 		kn->kn_hook = (caddr_t)mq;
3045b22f1a7SSamuel J. Greear 		klist = &mq->mq_wkq.ki_note;
305b06aab00SSamuel J. Greear 		break;
306b06aab00SSamuel J. Greear 	default:
307b06aab00SSamuel J. Greear 		lockmgr(&mq->mq_mtx, LK_RELEASE);
308b06aab00SSamuel J. Greear 		return (EOPNOTSUPP);
309b06aab00SSamuel J. Greear 	}
310b06aab00SSamuel J. Greear 
3115b22f1a7SSamuel J. Greear 	knote_insert(klist, kn);
312b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_RELEASE);
313b06aab00SSamuel J. Greear 
314b06aab00SSamuel J. Greear 	return (0);
315b06aab00SSamuel J. Greear }
316b06aab00SSamuel J. Greear 
317b06aab00SSamuel J. Greear static void
mqfilter_read_detach(struct knote * kn)318b06aab00SSamuel J. Greear mqfilter_read_detach(struct knote *kn)
319b06aab00SSamuel J. Greear {
320b06aab00SSamuel J. Greear 	struct mqueue *mq = (struct mqueue *)kn->kn_hook;
321b06aab00SSamuel J. Greear 
322b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
3235b22f1a7SSamuel J. Greear 	struct klist *klist = &mq->mq_rkq.ki_note;
3245b22f1a7SSamuel J. Greear 	knote_remove(klist, kn);
325b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_RELEASE);
326b06aab00SSamuel J. Greear }
327b06aab00SSamuel J. Greear 
328b06aab00SSamuel J. Greear static void
mqfilter_write_detach(struct knote * kn)329b06aab00SSamuel J. Greear mqfilter_write_detach(struct knote *kn)
330b06aab00SSamuel J. Greear {
331b06aab00SSamuel J. Greear 	struct mqueue *mq = (struct mqueue *)kn->kn_hook;
332b06aab00SSamuel J. Greear 
333b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
3345b22f1a7SSamuel J. Greear 	struct klist *klist = &mq->mq_wkq.ki_note;
3355b22f1a7SSamuel J. Greear 	knote_remove(klist, kn);
336b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_RELEASE);
337b06aab00SSamuel J. Greear }
338b06aab00SSamuel J. Greear 
339b06aab00SSamuel J. Greear static int
mqfilter_read(struct knote * kn,long hint)340b06aab00SSamuel J. Greear mqfilter_read(struct knote *kn, long hint)
341b06aab00SSamuel J. Greear {
342b06aab00SSamuel J. Greear 	struct mqueue *mq = (struct mqueue *)kn->kn_hook;
343b06aab00SSamuel J. Greear 	struct mq_attr *mqattr;
344b06aab00SSamuel J. Greear 	int ready = 0;
345b06aab00SSamuel J. Greear 
346b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
347b06aab00SSamuel J. Greear 	mqattr = &mq->mq_attrib;
348b06aab00SSamuel J. Greear 	/* Ready for receiving, if there are messages in the queue */
349b06aab00SSamuel J. Greear 	if (mqattr->mq_curmsgs)
350b06aab00SSamuel J. Greear 		ready = 1;
351b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_RELEASE);
352b06aab00SSamuel J. Greear 
353b06aab00SSamuel J. Greear 	return (ready);
354b06aab00SSamuel J. Greear }
355b06aab00SSamuel J. Greear 
356b06aab00SSamuel J. Greear static int
mqfilter_write(struct knote * kn,long hint)357b06aab00SSamuel J. Greear mqfilter_write(struct knote *kn, long hint)
358b06aab00SSamuel J. Greear {
359b06aab00SSamuel J. Greear 	struct mqueue *mq = (struct mqueue *)kn->kn_hook;
360b06aab00SSamuel J. Greear 	struct mq_attr *mqattr;
361b06aab00SSamuel J. Greear 	int ready = 0;
362b06aab00SSamuel J. Greear 
363b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
364b06aab00SSamuel J. Greear 	mqattr = &mq->mq_attrib;
365b06aab00SSamuel J. Greear 	/* Ready for sending, if the message queue is not full */
366b06aab00SSamuel J. Greear 	if (mqattr->mq_curmsgs < mqattr->mq_maxmsg)
367b06aab00SSamuel J. Greear 		ready = 1;
368b06aab00SSamuel J. Greear 	lockmgr(&mq->mq_mtx, LK_RELEASE);
369b06aab00SSamuel J. Greear 
370b06aab00SSamuel J. Greear 	return (ready);
371b06aab00SSamuel J. Greear }
372b06aab00SSamuel J. Greear 
373f2df0f7cSStathis Kamperis static int
mq_close_fop(file_t * fp)374f2df0f7cSStathis Kamperis mq_close_fop(file_t *fp)
375f2df0f7cSStathis Kamperis {
376f2df0f7cSStathis Kamperis 	struct proc *p = curproc;
377f2df0f7cSStathis Kamperis 	struct mqueue *mq = fp->f_data;
378f2df0f7cSStathis Kamperis 	bool destroy;
379f2df0f7cSStathis Kamperis 
380f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_EXCLUSIVE);
381f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
382f2df0f7cSStathis Kamperis 
383f2df0f7cSStathis Kamperis 	/* Decrease the counters */
384f2df0f7cSStathis Kamperis 	p->p_mqueue_cnt--;
385f2df0f7cSStathis Kamperis 	mq->mq_refcnt--;
386f2df0f7cSStathis Kamperis 
387f2df0f7cSStathis Kamperis 	/* Remove notification if registered for this process */
388f2df0f7cSStathis Kamperis 	if (mq->mq_notify_proc == p)
389f2df0f7cSStathis Kamperis 		mq->mq_notify_proc = NULL;
390f2df0f7cSStathis Kamperis 
391f2df0f7cSStathis Kamperis 	/*
392f2df0f7cSStathis Kamperis 	 * If this is the last reference and mqueue is marked for unlink,
393f2df0f7cSStathis Kamperis 	 * remove and later destroy the message queue.
394f2df0f7cSStathis Kamperis 	 */
395f2df0f7cSStathis Kamperis 	if (mq->mq_refcnt == 0 && (mq->mq_attrib.mq_flags & MQ_UNLINK)) {
396f2df0f7cSStathis Kamperis 		LIST_REMOVE(mq, mq_list);
397f2df0f7cSStathis Kamperis 		destroy = true;
398f2df0f7cSStathis Kamperis 	} else
399f2df0f7cSStathis Kamperis 		destroy = false;
400f2df0f7cSStathis Kamperis 
401f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
402f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_RELEASE);
403f2df0f7cSStathis Kamperis 
404f2df0f7cSStathis Kamperis 	if (destroy)
405f2df0f7cSStathis Kamperis 		mqueue_destroy(mq);
406f2df0f7cSStathis Kamperis 
407f2df0f7cSStathis Kamperis 	return 0;
408f2df0f7cSStathis Kamperis }
409f2df0f7cSStathis Kamperis 
410f2df0f7cSStathis Kamperis /*
411f2df0f7cSStathis Kamperis  * General mqueue system calls.
412f2df0f7cSStathis Kamperis  */
413f2df0f7cSStathis Kamperis 
414f2df0f7cSStathis Kamperis int
sys_mq_open(struct sysmsg * sysmsg,const struct mq_open_args * uap)41580d831e1SMatthew Dillon sys_mq_open(struct sysmsg *sysmsg, const struct mq_open_args *uap)
416f2df0f7cSStathis Kamperis {
417f2df0f7cSStathis Kamperis 	/* {
418f2df0f7cSStathis Kamperis 		syscallarg(const char *) name;
419f2df0f7cSStathis Kamperis 		syscallarg(int) oflag;
420f2df0f7cSStathis Kamperis 		syscallarg(mode_t) mode;
421f2df0f7cSStathis Kamperis 		syscallarg(struct mq_attr) attr;
422f2df0f7cSStathis Kamperis 	} */
4239910d07bSMatthew Dillon 	struct thread *td = curthread;
4249910d07bSMatthew Dillon 	struct proc *p = td->td_proc;
425f3a2d8c4SMatthew Dillon 	struct filedesc *fdp = p->p_fd;
426f2df0f7cSStathis Kamperis 	struct mqueue *mq, *mq_new = NULL;
427f2df0f7cSStathis Kamperis 	file_t *fp;
428f2df0f7cSStathis Kamperis 	char *name;
429f2df0f7cSStathis Kamperis 	int mqd, error, oflag;
430f2df0f7cSStathis Kamperis 
431f2df0f7cSStathis Kamperis 	/* Check access mode flags */
43203608863SSascha Wildner 	oflag = uap->oflag;
433f2df0f7cSStathis Kamperis 	if ((oflag & O_ACCMODE) == (O_WRONLY | O_RDWR)) {
434f2df0f7cSStathis Kamperis 		return EINVAL;
435f2df0f7cSStathis Kamperis 	}
436f2df0f7cSStathis Kamperis 
437f2df0f7cSStathis Kamperis 	/* Get the name from the user-space */
438d3a2b23dSVenkatesh Srinivas 	name = kmalloc(MQ_NAMELEN, M_MQBUF, M_WAITOK | M_ZERO | M_NULLOK);
439d3a2b23dSVenkatesh Srinivas 	if (name == NULL)
440d3a2b23dSVenkatesh Srinivas 		return (ENOMEM);
44103608863SSascha Wildner 	error = copyinstr(uap->name, name, MQ_NAMELEN - 1, NULL);
442f2df0f7cSStathis Kamperis 	if (error) {
443f2df0f7cSStathis Kamperis 		kfree(name, M_MQBUF);
444f2df0f7cSStathis Kamperis 		return error;
445f2df0f7cSStathis Kamperis 	}
446f2df0f7cSStathis Kamperis 
447f2df0f7cSStathis Kamperis 	if (oflag & O_CREAT) {
448f2df0f7cSStathis Kamperis 		struct mq_attr attr;
449f2df0f7cSStathis Kamperis 		u_int i;
450f2df0f7cSStathis Kamperis 
451f2df0f7cSStathis Kamperis 		/* Check the limit */
452f2df0f7cSStathis Kamperis 		if (p->p_mqueue_cnt == mq_open_max) {
453f2df0f7cSStathis Kamperis 			kfree(name, M_MQBUF);
454f2df0f7cSStathis Kamperis 			return EMFILE;
455f2df0f7cSStathis Kamperis 		}
456f2df0f7cSStathis Kamperis 
457f2df0f7cSStathis Kamperis 		/* Empty name is invalid */
458f2df0f7cSStathis Kamperis 		if (name[0] == '\0') {
459f2df0f7cSStathis Kamperis 			kfree(name, M_MQBUF);
460f2df0f7cSStathis Kamperis 			return EINVAL;
461f2df0f7cSStathis Kamperis 		}
462f2df0f7cSStathis Kamperis 
463f2df0f7cSStathis Kamperis 		/* Check for mqueue attributes */
46403608863SSascha Wildner 		if (uap->attr) {
46503608863SSascha Wildner 			error = copyin(uap->attr, &attr,
466f2df0f7cSStathis Kamperis 				sizeof(struct mq_attr));
467f2df0f7cSStathis Kamperis 			if (error) {
468f2df0f7cSStathis Kamperis 				kfree(name, M_MQBUF);
469f2df0f7cSStathis Kamperis 				return error;
470f2df0f7cSStathis Kamperis 			}
4718658b626SStathis Kamperis 			if (attr.mq_maxmsg <= 0 ||
4728658b626SStathis Kamperis 			    attr.mq_maxmsg > mq_max_maxmsg ||
4738658b626SStathis Kamperis 			    attr.mq_msgsize <= 0 ||
474f2df0f7cSStathis Kamperis 			    attr.mq_msgsize > mq_max_msgsize) {
475f2df0f7cSStathis Kamperis 				kfree(name, M_MQBUF);
476f2df0f7cSStathis Kamperis 				return EINVAL;
477f2df0f7cSStathis Kamperis 			}
478f2df0f7cSStathis Kamperis 			attr.mq_curmsgs = 0;
479f2df0f7cSStathis Kamperis 		} else {
480f2df0f7cSStathis Kamperis 			memset(&attr, 0, sizeof(struct mq_attr));
481f2df0f7cSStathis Kamperis 			attr.mq_maxmsg = mq_def_maxmsg;
482f2df0f7cSStathis Kamperis 			attr.mq_msgsize =
483f2df0f7cSStathis Kamperis 			    MQ_DEF_MSGSIZE - sizeof(struct mq_msg);
484f2df0f7cSStathis Kamperis 		}
485f2df0f7cSStathis Kamperis 
486f2df0f7cSStathis Kamperis 		/*
487f2df0f7cSStathis Kamperis 		 * Allocate new mqueue, initialize data structures,
488f2df0f7cSStathis Kamperis 		 * copy the name, attributes and set the flag.
489f2df0f7cSStathis Kamperis 		 */
490d3a2b23dSVenkatesh Srinivas 		mq_new = kmalloc(sizeof(struct mqueue), M_MQBUF,
491d3a2b23dSVenkatesh Srinivas 					M_WAITOK | M_ZERO | M_NULLOK);
492d3a2b23dSVenkatesh Srinivas 		if (mq_new == NULL) {
493d3a2b23dSVenkatesh Srinivas 			kfree(name, M_MQBUF);
494d3a2b23dSVenkatesh Srinivas 			return (ENOMEM);
495d3a2b23dSVenkatesh Srinivas 		}
496f2df0f7cSStathis Kamperis 
497f2df0f7cSStathis Kamperis 		lockinit(&mq_new->mq_mtx, "mq_new->mq_mtx", 0, LK_CANRECURSE);
498f2df0f7cSStathis Kamperis 		for (i = 0; i < (MQ_PQSIZE + 1); i++) {
499f2df0f7cSStathis Kamperis 			TAILQ_INIT(&mq_new->mq_head[i]);
500f2df0f7cSStathis Kamperis 		}
501f2df0f7cSStathis Kamperis 
502f2df0f7cSStathis Kamperis 		strlcpy(mq_new->mq_name, name, MQ_NAMELEN);
503f2df0f7cSStathis Kamperis 		memcpy(&mq_new->mq_attrib, &attr, sizeof(struct mq_attr));
504f2df0f7cSStathis Kamperis 
505f2df0f7cSStathis Kamperis 		/*CTASSERT((O_MASK & (MQ_UNLINK | MQ_RECEIVE)) == 0);*/
506f2df0f7cSStathis Kamperis 		/* mq_new->mq_attrib.mq_flags = (O_MASK & oflag); */
507f2df0f7cSStathis Kamperis 		mq_new->mq_attrib.mq_flags = oflag;
508f2df0f7cSStathis Kamperis 
509f2df0f7cSStathis Kamperis 		/* Store mode and effective UID with GID */
51003608863SSascha Wildner 		mq_new->mq_mode = ((uap->mode &
511f2df0f7cSStathis Kamperis 		    ~p->p_fd->fd_cmask) & ALLPERMS) & ~S_ISTXT;
5129910d07bSMatthew Dillon 		mq_new->mq_euid = td->td_ucred->cr_uid;
5139910d07bSMatthew Dillon 		mq_new->mq_egid = td->td_ucred->cr_svgid;
514f2df0f7cSStathis Kamperis 	}
515f2df0f7cSStathis Kamperis 
516f2df0f7cSStathis Kamperis 	/* Allocate file structure and descriptor */
517f3a2d8c4SMatthew Dillon 	error = falloc(td->td_lwp, &fp, &mqd);
518f2df0f7cSStathis Kamperis 	if (error) {
519f2df0f7cSStathis Kamperis 		if (mq_new)
520f2df0f7cSStathis Kamperis 			mqueue_destroy(mq_new);
521f2df0f7cSStathis Kamperis 		kfree(name, M_MQBUF);
522f2df0f7cSStathis Kamperis 		return error;
523f2df0f7cSStathis Kamperis 	}
524f2df0f7cSStathis Kamperis 	fp->f_type = DTYPE_MQUEUE;
525f2df0f7cSStathis Kamperis 	fp->f_flag = FFLAGS(oflag) & (FREAD | FWRITE);
526f2df0f7cSStathis Kamperis 	fp->f_ops = &mqops;
527f2df0f7cSStathis Kamperis 
528f2df0f7cSStathis Kamperis 	/* Look up for mqueue with such name */
529f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_EXCLUSIVE);
530f2df0f7cSStathis Kamperis 	mq = mqueue_lookup(name);
531f2df0f7cSStathis Kamperis 	if (mq) {
532f2df0f7cSStathis Kamperis 		int acc_mode;
533f2df0f7cSStathis Kamperis 
534f2df0f7cSStathis Kamperis 		KKASSERT(lockstatus(&mq->mq_mtx, curthread));
535f2df0f7cSStathis Kamperis 
536f2df0f7cSStathis Kamperis 		/* Check if mqueue is not marked as unlinking */
537f2df0f7cSStathis Kamperis 		if (mq->mq_attrib.mq_flags & MQ_UNLINK) {
538f2df0f7cSStathis Kamperis 			error = EACCES;
539f2df0f7cSStathis Kamperis 			goto exit;
540f2df0f7cSStathis Kamperis 		}
541f2df0f7cSStathis Kamperis 		/* Fail if O_EXCL is set, and mqueue already exists */
542f2df0f7cSStathis Kamperis 		if ((oflag & O_CREAT) && (oflag & O_EXCL)) {
543f2df0f7cSStathis Kamperis 			error = EEXIST;
544f2df0f7cSStathis Kamperis 			goto exit;
545f2df0f7cSStathis Kamperis 		}
546f2df0f7cSStathis Kamperis 
547f2df0f7cSStathis Kamperis 		/*
548f2df0f7cSStathis Kamperis 		 * Check the permissions. Note the difference between
549f2df0f7cSStathis Kamperis 		 * VREAD/VWRITE and FREAD/FWRITE.
550f2df0f7cSStathis Kamperis 		 */
551f2df0f7cSStathis Kamperis 		acc_mode = 0;
552f2df0f7cSStathis Kamperis 		if (fp->f_flag & FREAD) {
553f2df0f7cSStathis Kamperis 			acc_mode |= VREAD;
554f2df0f7cSStathis Kamperis 		}
555f2df0f7cSStathis Kamperis 		if (fp->f_flag & FWRITE) {
556f2df0f7cSStathis Kamperis 			acc_mode |= VWRITE;
557f2df0f7cSStathis Kamperis 		}
558f2df0f7cSStathis Kamperis 		if (vaccess(VNON, mq->mq_mode, mq->mq_euid, mq->mq_egid,
5599910d07bSMatthew Dillon 			acc_mode, td->td_ucred)) {
560f2df0f7cSStathis Kamperis 
561f2df0f7cSStathis Kamperis 			error = EACCES;
562f2df0f7cSStathis Kamperis 			goto exit;
563f2df0f7cSStathis Kamperis 		}
564f2df0f7cSStathis Kamperis 	} else {
565f2df0f7cSStathis Kamperis 		/* Fail if mqueue neither exists, nor we create it */
566f2df0f7cSStathis Kamperis 		if ((oflag & O_CREAT) == 0) {
567f2df0f7cSStathis Kamperis 			lockmgr(&mqlist_mtx, LK_RELEASE);
568f2df0f7cSStathis Kamperis 			KKASSERT(mq_new == NULL);
569f3a2d8c4SMatthew Dillon 			fsetfd(fdp, NULL, mqd);
570f2df0f7cSStathis Kamperis 			fp->f_ops = &badfileops;
571f2df0f7cSStathis Kamperis 			fdrop(fp);
572f2df0f7cSStathis Kamperis 			kfree(name, M_MQBUF);
573f2df0f7cSStathis Kamperis 			return ENOENT;
574f2df0f7cSStathis Kamperis 		}
575f2df0f7cSStathis Kamperis 
576f2df0f7cSStathis Kamperis 		/* Check the limit */
577f2df0f7cSStathis Kamperis 		if (p->p_mqueue_cnt == mq_open_max) {
578f2df0f7cSStathis Kamperis 			error = EMFILE;
579f2df0f7cSStathis Kamperis 			goto exit;
580f2df0f7cSStathis Kamperis 		}
581f2df0f7cSStathis Kamperis 
582f2df0f7cSStathis Kamperis 		/* Insert the queue to the list */
583f2df0f7cSStathis Kamperis 		mq = mq_new;
584f2df0f7cSStathis Kamperis 		lockmgr(&mq->mq_mtx, LK_EXCLUSIVE);
585f2df0f7cSStathis Kamperis 		LIST_INSERT_HEAD(&mqueue_head, mq, mq_list);
586f2df0f7cSStathis Kamperis 		mq_new = NULL;
587f2df0f7cSStathis Kamperis 		getnanotime(&mq->mq_btime);
588f2df0f7cSStathis Kamperis 		mq->mq_atime = mq->mq_mtime = mq->mq_btime;
589f2df0f7cSStathis Kamperis 	}
590f2df0f7cSStathis Kamperis 
591f2df0f7cSStathis Kamperis 	/* Increase the counters, and make descriptor ready */
592f2df0f7cSStathis Kamperis 	p->p_mqueue_cnt++;
593f2df0f7cSStathis Kamperis 	mq->mq_refcnt++;
594f2df0f7cSStathis Kamperis 	fp->f_data = mq;
595f2df0f7cSStathis Kamperis exit:
596f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
597f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_RELEASE);
598f2df0f7cSStathis Kamperis 
599f2df0f7cSStathis Kamperis 	if (mq_new)
600f2df0f7cSStathis Kamperis 		mqueue_destroy(mq_new);
601f2df0f7cSStathis Kamperis 	if (error) {
602f3a2d8c4SMatthew Dillon 		fsetfd(fdp, NULL, mqd);
603f2df0f7cSStathis Kamperis 		fp->f_ops = &badfileops;
604f2df0f7cSStathis Kamperis 	} else {
605f3a2d8c4SMatthew Dillon 		fsetfd(fdp, fp, mqd);
60680d831e1SMatthew Dillon 		sysmsg->sysmsg_result = mqd;
607f2df0f7cSStathis Kamperis 	}
608f2df0f7cSStathis Kamperis 	fdrop(fp);
609f2df0f7cSStathis Kamperis 	kfree(name, M_MQBUF);
610f2df0f7cSStathis Kamperis 
611f2df0f7cSStathis Kamperis 	return error;
612f2df0f7cSStathis Kamperis }
613f2df0f7cSStathis Kamperis 
614f2df0f7cSStathis Kamperis int
sys_mq_close(struct sysmsg * sysmsg,const struct mq_close_args * uap)61580d831e1SMatthew Dillon sys_mq_close(struct sysmsg *sysmsg, const struct mq_close_args *uap)
616f2df0f7cSStathis Kamperis {
61780d831e1SMatthew Dillon 	return sys_close(sysmsg, (const void *)uap);
618f2df0f7cSStathis Kamperis }
619f2df0f7cSStathis Kamperis 
620f2df0f7cSStathis Kamperis /*
621f2df0f7cSStathis Kamperis  * Primary mq_receive1() function.
622f2df0f7cSStathis Kamperis  */
623f2df0f7cSStathis Kamperis int
mq_receive1(struct lwp * l,mqd_t mqdes,void * msg_ptr,size_t msg_len,unsigned * msg_prio,struct timespec * ts,ssize_t * mlen)624f2df0f7cSStathis Kamperis mq_receive1(struct lwp *l, mqd_t mqdes, void *msg_ptr, size_t msg_len,
625f2df0f7cSStathis Kamperis     unsigned *msg_prio, struct timespec *ts, ssize_t *mlen)
626f2df0f7cSStathis Kamperis {
627f2df0f7cSStathis Kamperis 	file_t *fp = NULL;
628f2df0f7cSStathis Kamperis 	struct mqueue *mq;
629f2df0f7cSStathis Kamperis 	struct mq_msg *msg = NULL;
630f2df0f7cSStathis Kamperis 	struct mq_attr *mqattr;
631f2df0f7cSStathis Kamperis 	u_int idx;
632f2df0f7cSStathis Kamperis 	int error;
633f2df0f7cSStathis Kamperis 
634f2df0f7cSStathis Kamperis 	/* Get the message queue */
635f2df0f7cSStathis Kamperis 	error = mqueue_get(l, mqdes, &fp);
636f2df0f7cSStathis Kamperis 	if (error) {
637f2df0f7cSStathis Kamperis 		return error;
638f2df0f7cSStathis Kamperis 	}
639f2df0f7cSStathis Kamperis 	mq = fp->f_data;
640f2df0f7cSStathis Kamperis 	if ((fp->f_flag & FREAD) == 0) {
641f2df0f7cSStathis Kamperis 		error = EBADF;
642f2df0f7cSStathis Kamperis 		goto error;
643f2df0f7cSStathis Kamperis 	}
644f2df0f7cSStathis Kamperis 	getnanotime(&mq->mq_atime);
645f2df0f7cSStathis Kamperis 	mqattr = &mq->mq_attrib;
646f2df0f7cSStathis Kamperis 
647f2df0f7cSStathis Kamperis 	/* Check the message size limits */
648f2df0f7cSStathis Kamperis 	if (msg_len < mqattr->mq_msgsize) {
649f2df0f7cSStathis Kamperis 		error = EMSGSIZE;
650f2df0f7cSStathis Kamperis 		goto error;
651f2df0f7cSStathis Kamperis 	}
652f2df0f7cSStathis Kamperis 
653f2df0f7cSStathis Kamperis 	/* Check if queue is empty */
654f2df0f7cSStathis Kamperis 	while (mqattr->mq_curmsgs == 0) {
655f2df0f7cSStathis Kamperis 		int t;
656f2df0f7cSStathis Kamperis 
657f2df0f7cSStathis Kamperis 		if (mqattr->mq_flags & O_NONBLOCK) {
658f2df0f7cSStathis Kamperis 			error = EAGAIN;
659f2df0f7cSStathis Kamperis 			goto error;
660f2df0f7cSStathis Kamperis 		}
661c8b16402SStathis Kamperis 		if (ts) {
662f2df0f7cSStathis Kamperis 			error = abstimeout2timo(ts, &t);
663c8b16402SStathis Kamperis 			if (error)
664f2df0f7cSStathis Kamperis 				goto error;
665c8b16402SStathis Kamperis 		} else
666c8b16402SStathis Kamperis 			t = 0;
667f2df0f7cSStathis Kamperis 		/*
668f2df0f7cSStathis Kamperis 		 * Block until someone sends the message.
669f2df0f7cSStathis Kamperis 		 * While doing this, notification should not be sent.
670f2df0f7cSStathis Kamperis 		 */
671f2df0f7cSStathis Kamperis 		mqattr->mq_flags |= MQ_RECEIVE;
672acb0b16fSStathis Kamperis 		error = lksleep(&mq->mq_send_cv, &mq->mq_mtx, PCATCH, "mqsend", t);
673f2df0f7cSStathis Kamperis 		mqattr->mq_flags &= ~MQ_RECEIVE;
674f2df0f7cSStathis Kamperis 		if (error || (mqattr->mq_flags & MQ_UNLINK)) {
675f2df0f7cSStathis Kamperis 			error = (error == EWOULDBLOCK) ? ETIMEDOUT : EINTR;
676f2df0f7cSStathis Kamperis 			goto error;
677f2df0f7cSStathis Kamperis 		}
678f2df0f7cSStathis Kamperis 	}
679f2df0f7cSStathis Kamperis 
680f2df0f7cSStathis Kamperis 
681f2df0f7cSStathis Kamperis 	/*
682f2df0f7cSStathis Kamperis 	 * Find the highest priority message, and remove it from the queue.
683f2df0f7cSStathis Kamperis 	 * At first, reserved queue is checked, bitmap is next.
684f2df0f7cSStathis Kamperis 	 */
685f2df0f7cSStathis Kamperis 	msg = TAILQ_FIRST(&mq->mq_head[MQ_PQRESQ]);
686f2df0f7cSStathis Kamperis 	if (__predict_true(msg == NULL)) {
687f2df0f7cSStathis Kamperis 		idx = ffs(mq->mq_bitmap);
688f2df0f7cSStathis Kamperis 		msg = TAILQ_FIRST(&mq->mq_head[idx]);
689f2df0f7cSStathis Kamperis 		KKASSERT(msg != NULL);
690f2df0f7cSStathis Kamperis 	} else {
691f2df0f7cSStathis Kamperis 		idx = MQ_PQRESQ;
692f2df0f7cSStathis Kamperis 	}
693f2df0f7cSStathis Kamperis 	TAILQ_REMOVE(&mq->mq_head[idx], msg, msg_queue);
694f2df0f7cSStathis Kamperis 
695f2df0f7cSStathis Kamperis 	/* Unmark the bit, if last message. */
696f2df0f7cSStathis Kamperis 	if (__predict_true(idx) && TAILQ_EMPTY(&mq->mq_head[idx])) {
697f2df0f7cSStathis Kamperis 		KKASSERT((MQ_PQSIZE - idx) == msg->msg_prio);
698f2df0f7cSStathis Kamperis 		mq->mq_bitmap &= ~(1 << --idx);
699f2df0f7cSStathis Kamperis 	}
700f2df0f7cSStathis Kamperis 
701f2df0f7cSStathis Kamperis 	/* Decrement the counter and signal waiter, if any */
702f2df0f7cSStathis Kamperis 	mqattr->mq_curmsgs--;
703f2df0f7cSStathis Kamperis 	wakeup_one(&mq->mq_recv_cv);
704f2df0f7cSStathis Kamperis 
705f2df0f7cSStathis Kamperis 	/* Ready for sending now */
7065b22f1a7SSamuel J. Greear 	KNOTE(&mq->mq_wkq.ki_note, 0);
707f2df0f7cSStathis Kamperis error:
708f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
709f2df0f7cSStathis Kamperis 	fdrop(fp);
710f2df0f7cSStathis Kamperis 	if (error)
711f2df0f7cSStathis Kamperis 		return error;
712f2df0f7cSStathis Kamperis 
713f2df0f7cSStathis Kamperis 	/*
714f2df0f7cSStathis Kamperis 	 * Copy the data to the user-space.
715f2df0f7cSStathis Kamperis 	 * Note: According to POSIX, no message should be removed from the
716f2df0f7cSStathis Kamperis 	 * queue in case of fail - this would be violated.
717f2df0f7cSStathis Kamperis 	 */
718f2df0f7cSStathis Kamperis 	*mlen = msg->msg_len;
719f2df0f7cSStathis Kamperis 	error = copyout(msg->msg_ptr, msg_ptr, msg->msg_len);
720f2df0f7cSStathis Kamperis 	if (error == 0 && msg_prio)
721f2df0f7cSStathis Kamperis 		error = copyout(&msg->msg_prio, msg_prio, sizeof(unsigned));
722f2df0f7cSStathis Kamperis 	mqueue_freemsg(msg, sizeof(struct mq_msg) + msg->msg_len);
723f2df0f7cSStathis Kamperis 
724f2df0f7cSStathis Kamperis 	return error;
725f2df0f7cSStathis Kamperis }
726f2df0f7cSStathis Kamperis 
727f2df0f7cSStathis Kamperis int
sys_mq_receive(struct sysmsg * sysmsg,const struct mq_receive_args * uap)72880d831e1SMatthew Dillon sys_mq_receive(struct sysmsg *sysmsg, const struct mq_receive_args *uap)
729f2df0f7cSStathis Kamperis {
730f2df0f7cSStathis Kamperis 	/* {
731f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
732f2df0f7cSStathis Kamperis 		syscallarg(char *) msg_ptr;
733f2df0f7cSStathis Kamperis 		syscallarg(size_t) msg_len;
734f2df0f7cSStathis Kamperis 		syscallarg(unsigned *) msg_prio;
735f2df0f7cSStathis Kamperis 	} */
736f2df0f7cSStathis Kamperis 	ssize_t mlen;
737f2df0f7cSStathis Kamperis 	int error;
738f2df0f7cSStathis Kamperis 
73903608863SSascha Wildner 	error = mq_receive1(curthread->td_lwp, uap->mqdes, uap->msg_ptr,
74003608863SSascha Wildner 	    uap->msg_len, uap->msg_prio, 0, &mlen);
741f2df0f7cSStathis Kamperis 	if (error == 0)
74280d831e1SMatthew Dillon 		sysmsg->sysmsg_result = mlen;
743f2df0f7cSStathis Kamperis 
744f2df0f7cSStathis Kamperis 	return error;
745f2df0f7cSStathis Kamperis }
746f2df0f7cSStathis Kamperis 
747f2df0f7cSStathis Kamperis int
sys_mq_timedreceive(struct sysmsg * sysmsg,const struct mq_timedreceive_args * uap)74880d831e1SMatthew Dillon sys_mq_timedreceive(struct sysmsg *sysmsg, const struct mq_timedreceive_args *uap)
749f2df0f7cSStathis Kamperis {
750f2df0f7cSStathis Kamperis 	/* {
751f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
752f2df0f7cSStathis Kamperis 		syscallarg(char *) msg_ptr;
753f2df0f7cSStathis Kamperis 		syscallarg(size_t) msg_len;
754f2df0f7cSStathis Kamperis 		syscallarg(unsigned *) msg_prio;
755f2df0f7cSStathis Kamperis 		syscallarg(const struct timespec *) abs_timeout;
756f2df0f7cSStathis Kamperis 	} */
757f2df0f7cSStathis Kamperis 	int error;
758f2df0f7cSStathis Kamperis 	ssize_t mlen;
759f2df0f7cSStathis Kamperis 	struct timespec ts, *tsp;
760f2df0f7cSStathis Kamperis 
761f2df0f7cSStathis Kamperis 	/* Get and convert time value */
76203608863SSascha Wildner 	if (uap->abs_timeout) {
76303608863SSascha Wildner 		error = copyin(uap->abs_timeout, &ts, sizeof(ts));
764f2df0f7cSStathis Kamperis 		if (error)
765f2df0f7cSStathis Kamperis 			return error;
766f2df0f7cSStathis Kamperis 		tsp = &ts;
767f2df0f7cSStathis Kamperis 	} else {
768f2df0f7cSStathis Kamperis 		tsp = NULL;
769f2df0f7cSStathis Kamperis 	}
770f2df0f7cSStathis Kamperis 
77103608863SSascha Wildner 	error = mq_receive1(curthread->td_lwp, uap->mqdes, uap->msg_ptr,
77203608863SSascha Wildner 	    uap->msg_len, uap->msg_prio, tsp, &mlen);
773f2df0f7cSStathis Kamperis 	if (error == 0)
77480d831e1SMatthew Dillon 		sysmsg->sysmsg_result = mlen;
775f2df0f7cSStathis Kamperis 
776f2df0f7cSStathis Kamperis 	return error;
777f2df0f7cSStathis Kamperis }
778f2df0f7cSStathis Kamperis 
779f2df0f7cSStathis Kamperis /*
780f2df0f7cSStathis Kamperis  * Primary mq_send1() function.
781f2df0f7cSStathis Kamperis  */
782f2df0f7cSStathis Kamperis int
mq_send1(struct lwp * l,mqd_t mqdes,const char * msg_ptr,size_t msg_len,unsigned msg_prio,struct timespec * ts)783f2df0f7cSStathis Kamperis mq_send1(struct lwp *l, mqd_t mqdes, const char *msg_ptr, size_t msg_len,
784f2df0f7cSStathis Kamperis     unsigned msg_prio, struct timespec *ts)
785f2df0f7cSStathis Kamperis {
786f2df0f7cSStathis Kamperis 	file_t *fp = NULL;
787f2df0f7cSStathis Kamperis 	struct mqueue *mq;
788f2df0f7cSStathis Kamperis 	struct mq_msg *msg;
789f2df0f7cSStathis Kamperis 	struct mq_attr *mqattr;
790f2df0f7cSStathis Kamperis 	struct proc *notify = NULL;
791f2df0f7cSStathis Kamperis 	/*ksiginfo_t ksi;*/
792f2df0f7cSStathis Kamperis 	size_t size;
793f2df0f7cSStathis Kamperis 	int error;
794f2df0f7cSStathis Kamperis 
795f2df0f7cSStathis Kamperis 	/* Check the priority range */
796f2df0f7cSStathis Kamperis 	if (msg_prio >= mq_prio_max)
797f2df0f7cSStathis Kamperis 		return EINVAL;
798f2df0f7cSStathis Kamperis 
799f2df0f7cSStathis Kamperis 	/* Allocate a new message */
800f2df0f7cSStathis Kamperis 	size = sizeof(struct mq_msg) + msg_len;
801f2df0f7cSStathis Kamperis 	if (size > mq_max_msgsize)
802f2df0f7cSStathis Kamperis 		return EMSGSIZE;
803f2df0f7cSStathis Kamperis 
804d3a2b23dSVenkatesh Srinivas 	msg = kmalloc(size, M_MQBUF, M_WAITOK | M_NULLOK);
805d3a2b23dSVenkatesh Srinivas 	if (msg == NULL)
806d3a2b23dSVenkatesh Srinivas 		return (ENOMEM);
807d3a2b23dSVenkatesh Srinivas 
808f2df0f7cSStathis Kamperis 
809f2df0f7cSStathis Kamperis 	/* Get the data from user-space */
810f2df0f7cSStathis Kamperis 	error = copyin(msg_ptr, msg->msg_ptr, msg_len);
811f2df0f7cSStathis Kamperis 	if (error) {
812f2df0f7cSStathis Kamperis 		mqueue_freemsg(msg, size);
813f2df0f7cSStathis Kamperis 		return error;
814f2df0f7cSStathis Kamperis 	}
815f2df0f7cSStathis Kamperis 	msg->msg_len = msg_len;
816f2df0f7cSStathis Kamperis 	msg->msg_prio = msg_prio;
817f2df0f7cSStathis Kamperis 
818f2df0f7cSStathis Kamperis 	/* Get the mqueue */
819f2df0f7cSStathis Kamperis 	error = mqueue_get(l, mqdes, &fp);
820f2df0f7cSStathis Kamperis 	if (error) {
821f2df0f7cSStathis Kamperis 		mqueue_freemsg(msg, size);
822f2df0f7cSStathis Kamperis 		return error;
823f2df0f7cSStathis Kamperis 	}
824f2df0f7cSStathis Kamperis 	mq = fp->f_data;
825f2df0f7cSStathis Kamperis 	if ((fp->f_flag & FWRITE) == 0) {
826f2df0f7cSStathis Kamperis 		error = EBADF;
827f2df0f7cSStathis Kamperis 		goto error;
828f2df0f7cSStathis Kamperis 	}
829f2df0f7cSStathis Kamperis 	getnanotime(&mq->mq_mtime);
830f2df0f7cSStathis Kamperis 	mqattr = &mq->mq_attrib;
831f2df0f7cSStathis Kamperis 
832f2df0f7cSStathis Kamperis 	/* Check the message size limit */
833f2df0f7cSStathis Kamperis 	if (msg_len <= 0 || msg_len > mqattr->mq_msgsize) {
834f2df0f7cSStathis Kamperis 		error = EMSGSIZE;
835f2df0f7cSStathis Kamperis 		goto error;
836f2df0f7cSStathis Kamperis 	}
837f2df0f7cSStathis Kamperis 
838f2df0f7cSStathis Kamperis 	/* Check if queue is full */
839f2df0f7cSStathis Kamperis 	while (mqattr->mq_curmsgs >= mqattr->mq_maxmsg) {
840f2df0f7cSStathis Kamperis 		int t;
841f2df0f7cSStathis Kamperis 
842f2df0f7cSStathis Kamperis 		if (mqattr->mq_flags & O_NONBLOCK) {
843f2df0f7cSStathis Kamperis 			error = EAGAIN;
844f2df0f7cSStathis Kamperis 			goto error;
845f2df0f7cSStathis Kamperis 		}
846c8b16402SStathis Kamperis 		if (ts) {
847f2df0f7cSStathis Kamperis 			error = abstimeout2timo(ts, &t);
848c8b16402SStathis Kamperis 			if (error)
849f2df0f7cSStathis Kamperis 				goto error;
850c8b16402SStathis Kamperis 		} else
851c8b16402SStathis Kamperis 			t = 0;
852f2df0f7cSStathis Kamperis 		/* Block until queue becomes available */
853acb0b16fSStathis Kamperis 		error = lksleep(&mq->mq_recv_cv, &mq->mq_mtx, PCATCH, "mqrecv", t);
854f2df0f7cSStathis Kamperis 		if (error || (mqattr->mq_flags & MQ_UNLINK)) {
855f2df0f7cSStathis Kamperis 			error = (error == EWOULDBLOCK) ? ETIMEDOUT : error;
856f2df0f7cSStathis Kamperis 			goto error;
857f2df0f7cSStathis Kamperis 		}
858f2df0f7cSStathis Kamperis 	}
859f2df0f7cSStathis Kamperis 	KKASSERT(mq->mq_attrib.mq_curmsgs < mq->mq_attrib.mq_maxmsg);
860f2df0f7cSStathis Kamperis 
861f2df0f7cSStathis Kamperis 	/*
862f2df0f7cSStathis Kamperis 	 * Insert message into the queue, according to the priority.
863f2df0f7cSStathis Kamperis 	 * Note the difference between index and priority.
864f2df0f7cSStathis Kamperis 	 */
865f2df0f7cSStathis Kamperis 	if (__predict_true(msg_prio < MQ_PQSIZE)) {
866f2df0f7cSStathis Kamperis 		u_int idx = MQ_PQSIZE - msg_prio;
867f2df0f7cSStathis Kamperis 
868f2df0f7cSStathis Kamperis 		KKASSERT(idx != MQ_PQRESQ);
869f2df0f7cSStathis Kamperis 		TAILQ_INSERT_TAIL(&mq->mq_head[idx], msg, msg_queue);
870f2df0f7cSStathis Kamperis 		mq->mq_bitmap |= (1 << --idx);
871f2df0f7cSStathis Kamperis 	} else {
872f2df0f7cSStathis Kamperis 		mqueue_linear_insert(mq, msg);
873f2df0f7cSStathis Kamperis 	}
874f2df0f7cSStathis Kamperis 
875f2df0f7cSStathis Kamperis 	/* Check for the notify */
876f2df0f7cSStathis Kamperis 	if (mqattr->mq_curmsgs == 0 && mq->mq_notify_proc &&
877c8b16402SStathis Kamperis 	    (mqattr->mq_flags & MQ_RECEIVE) == 0 &&
878c8b16402SStathis Kamperis 	    mq->mq_sig_notify.sigev_notify == SIGEV_SIGNAL) {
879f2df0f7cSStathis Kamperis 		/* Initialize the signal */
880f2df0f7cSStathis Kamperis 		/*KSI_INIT(&ksi);*/
881f2df0f7cSStathis Kamperis 		/*ksi.ksi_signo = mq->mq_sig_notify.sigev_signo;*/
882f2df0f7cSStathis Kamperis 		/*ksi.ksi_code = SI_MESGQ;*/
883f2df0f7cSStathis Kamperis 		/*ksi.ksi_value = mq->mq_sig_notify.sigev_value;*/
884f2df0f7cSStathis Kamperis 		/* Unregister the process */
885f2df0f7cSStathis Kamperis 		notify = mq->mq_notify_proc;
886f2df0f7cSStathis Kamperis 		mq->mq_notify_proc = NULL;
887f2df0f7cSStathis Kamperis 	}
888f2df0f7cSStathis Kamperis 
889f2df0f7cSStathis Kamperis 	/* Increment the counter and signal waiter, if any */
890f2df0f7cSStathis Kamperis 	mqattr->mq_curmsgs++;
891f2df0f7cSStathis Kamperis 	wakeup_one(&mq->mq_send_cv);
892f2df0f7cSStathis Kamperis 
893f2df0f7cSStathis Kamperis 	/* Ready for receiving now */
8945b22f1a7SSamuel J. Greear 	KNOTE(&mq->mq_rkq.ki_note, 0);
895f2df0f7cSStathis Kamperis error:
896a8d3ab53SMatthew Dillon 	if (error) {
897f2df0f7cSStathis Kamperis 		lockmgr(&mq->mq_mtx, LK_RELEASE);
898f2df0f7cSStathis Kamperis 		fdrop(fp);
899f2df0f7cSStathis Kamperis 		mqueue_freemsg(msg, size);
900f2df0f7cSStathis Kamperis 	} else if (notify) {
901a8d3ab53SMatthew Dillon 		PHOLD(notify);
902a8d3ab53SMatthew Dillon 		lockmgr(&mq->mq_mtx, LK_RELEASE);
903a8d3ab53SMatthew Dillon 		fdrop(fp);
904f2df0f7cSStathis Kamperis 		/* Send the notify, if needed */
905f2df0f7cSStathis Kamperis 		/*kpsignal(notify, &ksi, NULL);*/
906f2df0f7cSStathis Kamperis 		ksignal(notify, mq->mq_sig_notify.sigev_signo);
907a8d3ab53SMatthew Dillon 		PRELE(notify);
908a8d3ab53SMatthew Dillon 	} else {
909a8d3ab53SMatthew Dillon 		lockmgr(&mq->mq_mtx, LK_RELEASE);
910a8d3ab53SMatthew Dillon 		fdrop(fp);
911f2df0f7cSStathis Kamperis 	}
912f2df0f7cSStathis Kamperis 	return error;
913f2df0f7cSStathis Kamperis }
914f2df0f7cSStathis Kamperis 
915f2df0f7cSStathis Kamperis int
sys_mq_send(struct sysmsg * sysmsg,const struct mq_send_args * uap)91680d831e1SMatthew Dillon sys_mq_send(struct sysmsg *sysmsg, const struct mq_send_args *uap)
917f2df0f7cSStathis Kamperis {
918f2df0f7cSStathis Kamperis 	/* {
919f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
920f2df0f7cSStathis Kamperis 		syscallarg(const char *) msg_ptr;
921f2df0f7cSStathis Kamperis 		syscallarg(size_t) msg_len;
922f2df0f7cSStathis Kamperis 		syscallarg(unsigned) msg_prio;
923f2df0f7cSStathis Kamperis 	} */
924f2df0f7cSStathis Kamperis 
92503608863SSascha Wildner 	return mq_send1(curthread->td_lwp, uap->mqdes, uap->msg_ptr,
92603608863SSascha Wildner 	    uap->msg_len, uap->msg_prio, 0);
927f2df0f7cSStathis Kamperis }
928f2df0f7cSStathis Kamperis 
929f2df0f7cSStathis Kamperis int
sys_mq_timedsend(struct sysmsg * sysmsg,const struct mq_timedsend_args * uap)93080d831e1SMatthew Dillon sys_mq_timedsend(struct sysmsg *sysmsg, const struct mq_timedsend_args *uap)
931f2df0f7cSStathis Kamperis {
932f2df0f7cSStathis Kamperis 	/* {
933f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
934f2df0f7cSStathis Kamperis 		syscallarg(const char *) msg_ptr;
935f2df0f7cSStathis Kamperis 		syscallarg(size_t) msg_len;
936f2df0f7cSStathis Kamperis 		syscallarg(unsigned) msg_prio;
937f2df0f7cSStathis Kamperis 		syscallarg(const struct timespec *) abs_timeout;
938f2df0f7cSStathis Kamperis 	} */
939f2df0f7cSStathis Kamperis 	struct timespec ts, *tsp;
940f2df0f7cSStathis Kamperis 	int error;
941f2df0f7cSStathis Kamperis 
942f2df0f7cSStathis Kamperis 	/* Get and convert time value */
94303608863SSascha Wildner 	if (uap->abs_timeout) {
94403608863SSascha Wildner 		error = copyin(uap->abs_timeout, &ts, sizeof(ts));
945f2df0f7cSStathis Kamperis 		if (error)
946f2df0f7cSStathis Kamperis 			return error;
947f2df0f7cSStathis Kamperis 		tsp = &ts;
948f2df0f7cSStathis Kamperis 	} else {
949f2df0f7cSStathis Kamperis 		tsp = NULL;
950f2df0f7cSStathis Kamperis 	}
951f2df0f7cSStathis Kamperis 
95203608863SSascha Wildner 	return mq_send1(curthread->td_lwp, uap->mqdes, uap->msg_ptr,
95303608863SSascha Wildner 	    uap->msg_len, uap->msg_prio, tsp);
954f2df0f7cSStathis Kamperis }
955f2df0f7cSStathis Kamperis 
956f2df0f7cSStathis Kamperis int
sys_mq_notify(struct sysmsg * sysmsg,const struct mq_notify_args * uap)95780d831e1SMatthew Dillon sys_mq_notify(struct sysmsg *sysmsg, const struct mq_notify_args *uap)
958f2df0f7cSStathis Kamperis {
959f2df0f7cSStathis Kamperis 	/* {
960f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
961f2df0f7cSStathis Kamperis 		syscallarg(const struct sigevent *) notification;
962f2df0f7cSStathis Kamperis 	} */
963f2df0f7cSStathis Kamperis 	file_t *fp = NULL;
964f2df0f7cSStathis Kamperis 	struct mqueue *mq;
965f2df0f7cSStathis Kamperis 	struct sigevent sig;
966f2df0f7cSStathis Kamperis 	int error;
967f2df0f7cSStathis Kamperis 
96803608863SSascha Wildner 	if (uap->notification) {
969f2df0f7cSStathis Kamperis 		/* Get the signal from user-space */
97003608863SSascha Wildner 		error = copyin(uap->notification, &sig,
971f2df0f7cSStathis Kamperis 		    sizeof(struct sigevent));
972f2df0f7cSStathis Kamperis 		if (error)
973f2df0f7cSStathis Kamperis 			return error;
974c8b16402SStathis Kamperis 		if (sig.sigev_notify == SIGEV_SIGNAL &&
975c8b16402SStathis Kamperis 		    (sig.sigev_signo <= 0 || sig.sigev_signo >= NSIG))
976c8b16402SStathis Kamperis 			return EINVAL;
977f2df0f7cSStathis Kamperis 	}
978f2df0f7cSStathis Kamperis 
97903608863SSascha Wildner 	error = mqueue_get(curthread->td_lwp, uap->mqdes, &fp);
980f2df0f7cSStathis Kamperis 	if (error)
981f2df0f7cSStathis Kamperis 		return error;
982f2df0f7cSStathis Kamperis 	mq = fp->f_data;
983f2df0f7cSStathis Kamperis 
98403608863SSascha Wildner 	if (uap->notification) {
985f2df0f7cSStathis Kamperis 		/* Register notification: set the signal and target process */
986f2df0f7cSStathis Kamperis 		if (mq->mq_notify_proc == NULL) {
987f2df0f7cSStathis Kamperis 			memcpy(&mq->mq_sig_notify, &sig,
988f2df0f7cSStathis Kamperis 			    sizeof(struct sigevent));
989f2df0f7cSStathis Kamperis 			mq->mq_notify_proc = curproc;
990f2df0f7cSStathis Kamperis 		} else {
991f2df0f7cSStathis Kamperis 			/* Fail if someone else already registered */
992f2df0f7cSStathis Kamperis 			error = EBUSY;
993f2df0f7cSStathis Kamperis 		}
994f2df0f7cSStathis Kamperis 	} else {
995f2df0f7cSStathis Kamperis 		/* Unregister the notification */
996f2df0f7cSStathis Kamperis 		mq->mq_notify_proc = NULL;
997f2df0f7cSStathis Kamperis 	}
998f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
999f2df0f7cSStathis Kamperis 	fdrop(fp);
1000f2df0f7cSStathis Kamperis 
1001f2df0f7cSStathis Kamperis 	return error;
1002f2df0f7cSStathis Kamperis }
1003f2df0f7cSStathis Kamperis 
1004f2df0f7cSStathis Kamperis int
sys_mq_getattr(struct sysmsg * sysmsg,const struct mq_getattr_args * uap)100580d831e1SMatthew Dillon sys_mq_getattr(struct sysmsg *sysmsg, const struct mq_getattr_args *uap)
1006f2df0f7cSStathis Kamperis {
1007f2df0f7cSStathis Kamperis 	/* {
1008f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
1009f2df0f7cSStathis Kamperis 		syscallarg(struct mq_attr *) mqstat;
1010f2df0f7cSStathis Kamperis 	} */
1011f2df0f7cSStathis Kamperis 	file_t *fp = NULL;
1012f2df0f7cSStathis Kamperis 	struct mqueue *mq;
1013f2df0f7cSStathis Kamperis 	struct mq_attr attr;
1014f2df0f7cSStathis Kamperis 	int error;
1015f2df0f7cSStathis Kamperis 
1016f2df0f7cSStathis Kamperis 	/* Get the message queue */
101703608863SSascha Wildner 	error = mqueue_get(curthread->td_lwp, uap->mqdes, &fp);
1018f2df0f7cSStathis Kamperis 	if (error)
1019f2df0f7cSStathis Kamperis 		return error;
1020f2df0f7cSStathis Kamperis 	mq = fp->f_data;
1021f2df0f7cSStathis Kamperis 	memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr));
1022f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
1023f2df0f7cSStathis Kamperis 	fdrop(fp);
1024f2df0f7cSStathis Kamperis 
102503608863SSascha Wildner 	return copyout(&attr, uap->mqstat, sizeof(struct mq_attr));
1026f2df0f7cSStathis Kamperis }
1027f2df0f7cSStathis Kamperis 
1028f2df0f7cSStathis Kamperis int
sys_mq_setattr(struct sysmsg * sysmsg,const struct mq_setattr_args * uap)102980d831e1SMatthew Dillon sys_mq_setattr(struct sysmsg *sysmsg, const struct mq_setattr_args *uap)
1030f2df0f7cSStathis Kamperis {
1031f2df0f7cSStathis Kamperis 	/* {
1032f2df0f7cSStathis Kamperis 		syscallarg(mqd_t) mqdes;
1033f2df0f7cSStathis Kamperis 		syscallarg(const struct mq_attr *) mqstat;
1034f2df0f7cSStathis Kamperis 		syscallarg(struct mq_attr *) omqstat;
1035f2df0f7cSStathis Kamperis 	} */
1036f2df0f7cSStathis Kamperis 	file_t *fp = NULL;
1037f2df0f7cSStathis Kamperis 	struct mqueue *mq;
1038f2df0f7cSStathis Kamperis 	struct mq_attr attr;
1039f2df0f7cSStathis Kamperis 	int error, nonblock;
1040f2df0f7cSStathis Kamperis 
104103608863SSascha Wildner 	error = copyin(uap->mqstat, &attr, sizeof(struct mq_attr));
1042f2df0f7cSStathis Kamperis 	if (error)
1043f2df0f7cSStathis Kamperis 		return error;
1044f2df0f7cSStathis Kamperis 	nonblock = (attr.mq_flags & O_NONBLOCK);
1045f2df0f7cSStathis Kamperis 
1046f2df0f7cSStathis Kamperis 	/* Get the message queue */
104703608863SSascha Wildner 	error = mqueue_get(curthread->td_lwp, uap->mqdes, &fp);
1048f2df0f7cSStathis Kamperis 	if (error)
1049f2df0f7cSStathis Kamperis 		return error;
1050f2df0f7cSStathis Kamperis 	mq = fp->f_data;
1051f2df0f7cSStathis Kamperis 
1052f2df0f7cSStathis Kamperis 	/* Copy the old attributes, if needed */
105303608863SSascha Wildner 	if (uap->omqstat) {
1054f2df0f7cSStathis Kamperis 		memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr));
1055d1d64679SStathis Kamperis 	}
1056f2df0f7cSStathis Kamperis 
1057f2df0f7cSStathis Kamperis 	/* Ignore everything, except O_NONBLOCK */
1058f2df0f7cSStathis Kamperis 	if (nonblock)
1059f2df0f7cSStathis Kamperis 		mq->mq_attrib.mq_flags |= O_NONBLOCK;
1060f2df0f7cSStathis Kamperis 	else
1061f2df0f7cSStathis Kamperis 		mq->mq_attrib.mq_flags &= ~O_NONBLOCK;
1062f2df0f7cSStathis Kamperis 
1063f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
1064f2df0f7cSStathis Kamperis 	fdrop(fp);
1065f2df0f7cSStathis Kamperis 
1066f2df0f7cSStathis Kamperis 	/*
1067f2df0f7cSStathis Kamperis 	 * Copy the data to the user-space.
1068f2df0f7cSStathis Kamperis 	 * Note: According to POSIX, the new attributes should not be set in
1069f2df0f7cSStathis Kamperis 	 * case of fail - this would be violated.
1070f2df0f7cSStathis Kamperis 	 */
107103608863SSascha Wildner 	if (uap->omqstat)
107203608863SSascha Wildner 		error = copyout(&attr, uap->omqstat, sizeof(struct mq_attr));
1073f2df0f7cSStathis Kamperis 
1074f2df0f7cSStathis Kamperis 	return error;
1075f2df0f7cSStathis Kamperis }
1076f2df0f7cSStathis Kamperis 
1077f2df0f7cSStathis Kamperis int
sys_mq_unlink(struct sysmsg * sysmsg,const struct mq_unlink_args * uap)107880d831e1SMatthew Dillon sys_mq_unlink(struct sysmsg *sysmsg, const struct mq_unlink_args *uap)
1079f2df0f7cSStathis Kamperis {
1080f2df0f7cSStathis Kamperis 	/* {
1081f2df0f7cSStathis Kamperis 		syscallarg(const char *) name;
1082f2df0f7cSStathis Kamperis 	} */
10839910d07bSMatthew Dillon 	struct thread *td = curthread;
1084f2df0f7cSStathis Kamperis 	struct mqueue *mq;
1085f2df0f7cSStathis Kamperis 	char *name;
1086f2df0f7cSStathis Kamperis 	int error, refcnt = 0;
1087f2df0f7cSStathis Kamperis 
1088f2df0f7cSStathis Kamperis 	/* Get the name from the user-space */
1089d3a2b23dSVenkatesh Srinivas 	name = kmalloc(MQ_NAMELEN, M_MQBUF, M_WAITOK | M_ZERO | M_NULLOK);
1090d3a2b23dSVenkatesh Srinivas 	if (name == NULL)
1091d3a2b23dSVenkatesh Srinivas 		return (ENOMEM);
109203608863SSascha Wildner 	error = copyinstr(uap->name, name, MQ_NAMELEN - 1, NULL);
1093f2df0f7cSStathis Kamperis 	if (error) {
1094f2df0f7cSStathis Kamperis 		kfree(name, M_MQBUF);
1095f2df0f7cSStathis Kamperis 		return error;
1096f2df0f7cSStathis Kamperis 	}
1097f2df0f7cSStathis Kamperis 
1098f2df0f7cSStathis Kamperis 	/* Lookup for this file */
1099f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_EXCLUSIVE);
1100f2df0f7cSStathis Kamperis 	mq = mqueue_lookup(name);
1101f2df0f7cSStathis Kamperis 	if (mq == NULL) {
1102f2df0f7cSStathis Kamperis 		error = ENOENT;
1103f2df0f7cSStathis Kamperis 		goto error;
1104f2df0f7cSStathis Kamperis 	}
1105f2df0f7cSStathis Kamperis 
1106f2df0f7cSStathis Kamperis 	/* Check the permissions */
11079910d07bSMatthew Dillon 	if (td->td_ucred->cr_uid != mq->mq_euid &&
11082b3f93eaSMatthew Dillon 	    caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT) != 0)
11092b3f93eaSMatthew Dillon 	{
1110f2df0f7cSStathis Kamperis 		lockmgr(&mq->mq_mtx, LK_RELEASE);
1111f2df0f7cSStathis Kamperis 		error = EACCES;
1112f2df0f7cSStathis Kamperis 		goto error;
1113f2df0f7cSStathis Kamperis 	}
1114f2df0f7cSStathis Kamperis 
1115f2df0f7cSStathis Kamperis 	/* Mark message queue as unlinking, before leaving the window */
1116f2df0f7cSStathis Kamperis 	mq->mq_attrib.mq_flags |= MQ_UNLINK;
1117f2df0f7cSStathis Kamperis 
1118f2df0f7cSStathis Kamperis 	/* Wake up all waiters, if there are such */
1119f2df0f7cSStathis Kamperis 	wakeup(&mq->mq_send_cv);
1120f2df0f7cSStathis Kamperis 	wakeup(&mq->mq_recv_cv);
1121f2df0f7cSStathis Kamperis 
11225b22f1a7SSamuel J. Greear 	KNOTE(&mq->mq_rkq.ki_note, 0);
11235b22f1a7SSamuel J. Greear 	KNOTE(&mq->mq_wkq.ki_note, 0);
1124f2df0f7cSStathis Kamperis 
1125f2df0f7cSStathis Kamperis 	refcnt = mq->mq_refcnt;
1126f2df0f7cSStathis Kamperis 	if (refcnt == 0)
1127f2df0f7cSStathis Kamperis 		LIST_REMOVE(mq, mq_list);
1128f2df0f7cSStathis Kamperis 
1129f2df0f7cSStathis Kamperis 	lockmgr(&mq->mq_mtx, LK_RELEASE);
1130f2df0f7cSStathis Kamperis error:
1131f2df0f7cSStathis Kamperis 	lockmgr(&mqlist_mtx, LK_RELEASE);
1132f2df0f7cSStathis Kamperis 
1133f2df0f7cSStathis Kamperis 	/*
1134f2df0f7cSStathis Kamperis 	 * If there are no references - destroy the message
1135f2df0f7cSStathis Kamperis 	 * queue, otherwise, the last mq_close() will do that.
1136f2df0f7cSStathis Kamperis 	 */
1137f2df0f7cSStathis Kamperis 	if (error == 0 && refcnt == 0)
1138f2df0f7cSStathis Kamperis 		mqueue_destroy(mq);
1139f2df0f7cSStathis Kamperis 
1140f2df0f7cSStathis Kamperis 	kfree(name, M_MQBUF);
1141f2df0f7cSStathis Kamperis 	return error;
1142f2df0f7cSStathis Kamperis }
1143f2df0f7cSStathis Kamperis 
1144f2df0f7cSStathis Kamperis /*
1145f2df0f7cSStathis Kamperis  * SysCtl.
1146f2df0f7cSStathis Kamperis  */
1147f2df0f7cSStathis Kamperis SYSCTL_NODE(_kern, OID_AUTO, mqueue,
1148f2df0f7cSStathis Kamperis     CTLFLAG_RW, 0, "Message queue options");
1149f2df0f7cSStathis Kamperis 
1150f2df0f7cSStathis Kamperis SYSCTL_INT(_kern_mqueue, OID_AUTO, mq_open_max,
1151f2df0f7cSStathis Kamperis     CTLFLAG_RW, &mq_open_max, 0,
1152f2df0f7cSStathis Kamperis     "Maximal number of message queue descriptors per process");
1153f2df0f7cSStathis Kamperis 
1154f2df0f7cSStathis Kamperis SYSCTL_INT(_kern_mqueue, OID_AUTO, mq_prio_max,
1155f2df0f7cSStathis Kamperis     CTLFLAG_RW, &mq_prio_max, 0,
1156f2df0f7cSStathis Kamperis     "Maximal priority of the message");
1157f2df0f7cSStathis Kamperis 
1158f2df0f7cSStathis Kamperis SYSCTL_INT(_kern_mqueue, OID_AUTO, mq_max_msgsize,
1159f2df0f7cSStathis Kamperis     CTLFLAG_RW, &mq_max_msgsize, 0,
1160f2df0f7cSStathis Kamperis     "Maximal allowed size of the message");
1161f2df0f7cSStathis Kamperis 
1162f2df0f7cSStathis Kamperis SYSCTL_INT(_kern_mqueue, OID_AUTO, mq_def_maxmsg,
1163f2df0f7cSStathis Kamperis     CTLFLAG_RW, &mq_def_maxmsg, 0,
1164f2df0f7cSStathis Kamperis     "Default maximal message count");
1165f2df0f7cSStathis Kamperis 
11668658b626SStathis Kamperis SYSCTL_INT(_kern_mqueue, OID_AUTO, mq_max_maxmsg,
11678658b626SStathis Kamperis     CTLFLAG_RW, &mq_max_maxmsg, 0,
11688658b626SStathis Kamperis     "Maximal allowed message count");
11698658b626SStathis Kamperis 
1170f2df0f7cSStathis Kamperis SYSINIT(sys_mqueue_init, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, mqueue_sysinit, NULL);
1171