xref: /freebsd/sys/fs/cuse/cuse.c (revision fa0f6e62c69e1920680eceb514976ef5c5d8f4da)
1*fa0f6e62SHans Petter Selasky /* $FreeBSD$ */
2*fa0f6e62SHans Petter Selasky /*-
3*fa0f6e62SHans Petter Selasky  * Copyright (c) 2010-2013 Hans Petter Selasky. All rights reserved.
4*fa0f6e62SHans Petter Selasky  *
5*fa0f6e62SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
6*fa0f6e62SHans Petter Selasky  * modification, are permitted provided that the following conditions
7*fa0f6e62SHans Petter Selasky  * are met:
8*fa0f6e62SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
9*fa0f6e62SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer.
10*fa0f6e62SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
11*fa0f6e62SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
12*fa0f6e62SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
13*fa0f6e62SHans Petter Selasky  *
14*fa0f6e62SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*fa0f6e62SHans Petter Selasky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*fa0f6e62SHans Petter Selasky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*fa0f6e62SHans Petter Selasky  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*fa0f6e62SHans Petter Selasky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*fa0f6e62SHans Petter Selasky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*fa0f6e62SHans Petter Selasky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*fa0f6e62SHans Petter Selasky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*fa0f6e62SHans Petter Selasky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*fa0f6e62SHans Petter Selasky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*fa0f6e62SHans Petter Selasky  * SUCH DAMAGE.
25*fa0f6e62SHans Petter Selasky  */
26*fa0f6e62SHans Petter Selasky 
27*fa0f6e62SHans Petter Selasky #include "opt_compat.h"
28*fa0f6e62SHans Petter Selasky 
29*fa0f6e62SHans Petter Selasky #include <sys/stdint.h>
30*fa0f6e62SHans Petter Selasky #include <sys/stddef.h>
31*fa0f6e62SHans Petter Selasky #include <sys/param.h>
32*fa0f6e62SHans Petter Selasky #include <sys/types.h>
33*fa0f6e62SHans Petter Selasky #include <sys/systm.h>
34*fa0f6e62SHans Petter Selasky #include <sys/conf.h>
35*fa0f6e62SHans Petter Selasky #include <sys/kernel.h>
36*fa0f6e62SHans Petter Selasky #include <sys/bus.h>
37*fa0f6e62SHans Petter Selasky #include <sys/linker_set.h>
38*fa0f6e62SHans Petter Selasky #include <sys/module.h>
39*fa0f6e62SHans Petter Selasky #include <sys/lock.h>
40*fa0f6e62SHans Petter Selasky #include <sys/mutex.h>
41*fa0f6e62SHans Petter Selasky #include <sys/condvar.h>
42*fa0f6e62SHans Petter Selasky #include <sys/sysctl.h>
43*fa0f6e62SHans Petter Selasky #include <sys/unistd.h>
44*fa0f6e62SHans Petter Selasky #include <sys/malloc.h>
45*fa0f6e62SHans Petter Selasky #include <sys/priv.h>
46*fa0f6e62SHans Petter Selasky #include <sys/uio.h>
47*fa0f6e62SHans Petter Selasky #include <sys/poll.h>
48*fa0f6e62SHans Petter Selasky #include <sys/sx.h>
49*fa0f6e62SHans Petter Selasky #include <sys/queue.h>
50*fa0f6e62SHans Petter Selasky #include <sys/fcntl.h>
51*fa0f6e62SHans Petter Selasky #include <sys/proc.h>
52*fa0f6e62SHans Petter Selasky #include <sys/vnode.h>
53*fa0f6e62SHans Petter Selasky #include <sys/selinfo.h>
54*fa0f6e62SHans Petter Selasky #include <sys/ptrace.h>
55*fa0f6e62SHans Petter Selasky 
56*fa0f6e62SHans Petter Selasky #include <machine/bus.h>
57*fa0f6e62SHans Petter Selasky 
58*fa0f6e62SHans Petter Selasky #include <vm/vm.h>
59*fa0f6e62SHans Petter Selasky #include <vm/pmap.h>
60*fa0f6e62SHans Petter Selasky 
61*fa0f6e62SHans Petter Selasky #include <fs/cuse/cuse_defs.h>
62*fa0f6e62SHans Petter Selasky #include <fs/cuse/cuse_ioctl.h>
63*fa0f6e62SHans Petter Selasky 
64*fa0f6e62SHans Petter Selasky MODULE_VERSION(cuse, 1);
65*fa0f6e62SHans Petter Selasky 
66*fa0f6e62SHans Petter Selasky #define	NBUSY	((uint8_t *)1)
67*fa0f6e62SHans Petter Selasky 
68*fa0f6e62SHans Petter Selasky #ifdef FEATURE
69*fa0f6e62SHans Petter Selasky FEATURE(cuse, "Userspace character devices");
70*fa0f6e62SHans Petter Selasky #endif
71*fa0f6e62SHans Petter Selasky 
72*fa0f6e62SHans Petter Selasky struct cuse_command;
73*fa0f6e62SHans Petter Selasky struct cuse_server;
74*fa0f6e62SHans Petter Selasky struct cuse_client;
75*fa0f6e62SHans Petter Selasky 
76*fa0f6e62SHans Petter Selasky struct cuse_client_command {
77*fa0f6e62SHans Petter Selasky 	TAILQ_ENTRY(cuse_client_command) entry;
78*fa0f6e62SHans Petter Selasky 	struct cuse_command sub;
79*fa0f6e62SHans Petter Selasky 	struct sx sx;
80*fa0f6e62SHans Petter Selasky 	struct cv cv;
81*fa0f6e62SHans Petter Selasky 	struct thread *entered;
82*fa0f6e62SHans Petter Selasky 	struct cuse_client *client;
83*fa0f6e62SHans Petter Selasky 	struct proc *proc_curr;
84*fa0f6e62SHans Petter Selasky 	int	proc_refs;
85*fa0f6e62SHans Petter Selasky 	int	got_signal;
86*fa0f6e62SHans Petter Selasky 	int	error;
87*fa0f6e62SHans Petter Selasky 	int	command;
88*fa0f6e62SHans Petter Selasky };
89*fa0f6e62SHans Petter Selasky 
90*fa0f6e62SHans Petter Selasky struct cuse_memory {
91*fa0f6e62SHans Petter Selasky 	struct cuse_server *owner;
92*fa0f6e62SHans Petter Selasky 	uint8_t *virtaddr;
93*fa0f6e62SHans Petter Selasky 	uint32_t page_count;
94*fa0f6e62SHans Petter Selasky 	uint32_t is_allocated;
95*fa0f6e62SHans Petter Selasky };
96*fa0f6e62SHans Petter Selasky 
97*fa0f6e62SHans Petter Selasky struct cuse_server_dev {
98*fa0f6e62SHans Petter Selasky 	TAILQ_ENTRY(cuse_server_dev) entry;
99*fa0f6e62SHans Petter Selasky 	struct cuse_server *server;
100*fa0f6e62SHans Petter Selasky 	struct cdev *kern_dev;
101*fa0f6e62SHans Petter Selasky 	struct cuse_dev *user_dev;
102*fa0f6e62SHans Petter Selasky };
103*fa0f6e62SHans Petter Selasky 
104*fa0f6e62SHans Petter Selasky struct cuse_server {
105*fa0f6e62SHans Petter Selasky 	TAILQ_ENTRY(cuse_server) entry;
106*fa0f6e62SHans Petter Selasky 	TAILQ_HEAD(, cuse_client_command) head;
107*fa0f6e62SHans Petter Selasky 	TAILQ_HEAD(, cuse_server_dev) hdev;
108*fa0f6e62SHans Petter Selasky 	TAILQ_HEAD(, cuse_client) hcli;
109*fa0f6e62SHans Petter Selasky 	struct cv cv;
110*fa0f6e62SHans Petter Selasky 	struct selinfo selinfo;
111*fa0f6e62SHans Petter Selasky 	int	is_closing;
112*fa0f6e62SHans Petter Selasky 	int	refs;
113*fa0f6e62SHans Petter Selasky };
114*fa0f6e62SHans Petter Selasky 
115*fa0f6e62SHans Petter Selasky struct cuse_client {
116*fa0f6e62SHans Petter Selasky 	TAILQ_ENTRY(cuse_client) entry;
117*fa0f6e62SHans Petter Selasky 	TAILQ_ENTRY(cuse_client) entry_ref;
118*fa0f6e62SHans Petter Selasky 	struct cuse_client_command cmds[CUSE_CMD_MAX];
119*fa0f6e62SHans Petter Selasky 	struct cuse_server *server;
120*fa0f6e62SHans Petter Selasky 	struct cuse_server_dev *server_dev;
121*fa0f6e62SHans Petter Selasky 
122*fa0f6e62SHans Petter Selasky 	uint8_t	ioctl_buffer[CUSE_BUFFER_MAX] __aligned(4);
123*fa0f6e62SHans Petter Selasky 
124*fa0f6e62SHans Petter Selasky 	int	fflags;		/* file flags */
125*fa0f6e62SHans Petter Selasky 	int	cflags;		/* client flags */
126*fa0f6e62SHans Petter Selasky #define	CUSE_CLI_IS_CLOSING 0x01
127*fa0f6e62SHans Petter Selasky #define	CUSE_CLI_KNOTE_NEED_READ 0x02
128*fa0f6e62SHans Petter Selasky #define	CUSE_CLI_KNOTE_NEED_WRITE 0x04
129*fa0f6e62SHans Petter Selasky #define	CUSE_CLI_KNOTE_HAS_READ 0x08
130*fa0f6e62SHans Petter Selasky #define	CUSE_CLI_KNOTE_HAS_WRITE 0x10
131*fa0f6e62SHans Petter Selasky };
132*fa0f6e62SHans Petter Selasky 
133*fa0f6e62SHans Petter Selasky #define	CUSE_CLIENT_CLOSING(pcc) \
134*fa0f6e62SHans Petter Selasky     ((pcc)->cflags & CUSE_CLI_IS_CLOSING)
135*fa0f6e62SHans Petter Selasky 
136*fa0f6e62SHans Petter Selasky static MALLOC_DEFINE(M_CUSE, "cuse", "CUSE memory");
137*fa0f6e62SHans Petter Selasky 
138*fa0f6e62SHans Petter Selasky static TAILQ_HEAD(, cuse_server) cuse_server_head;
139*fa0f6e62SHans Petter Selasky static struct mtx cuse_mtx;
140*fa0f6e62SHans Petter Selasky static struct cdev *cuse_dev;
141*fa0f6e62SHans Petter Selasky static struct cuse_server *cuse_alloc_unit[CUSE_DEVICES_MAX];
142*fa0f6e62SHans Petter Selasky static int cuse_alloc_unit_id[CUSE_DEVICES_MAX];
143*fa0f6e62SHans Petter Selasky static struct cuse_memory cuse_mem[CUSE_ALLOC_UNIT_MAX];
144*fa0f6e62SHans Petter Selasky 
145*fa0f6e62SHans Petter Selasky static void cuse_client_kqfilter_read_detach(struct knote *kn);
146*fa0f6e62SHans Petter Selasky static void cuse_client_kqfilter_write_detach(struct knote *kn);
147*fa0f6e62SHans Petter Selasky static int cuse_client_kqfilter_read_event(struct knote *kn, long hint);
148*fa0f6e62SHans Petter Selasky static int cuse_client_kqfilter_write_event(struct knote *kn, long hint);
149*fa0f6e62SHans Petter Selasky 
150*fa0f6e62SHans Petter Selasky static struct filterops cuse_client_kqfilter_read_ops = {
151*fa0f6e62SHans Petter Selasky 	.f_isfd = 1,
152*fa0f6e62SHans Petter Selasky 	.f_detach = cuse_client_kqfilter_read_detach,
153*fa0f6e62SHans Petter Selasky 	.f_event = cuse_client_kqfilter_read_event,
154*fa0f6e62SHans Petter Selasky };
155*fa0f6e62SHans Petter Selasky 
156*fa0f6e62SHans Petter Selasky static struct filterops cuse_client_kqfilter_write_ops = {
157*fa0f6e62SHans Petter Selasky 	.f_isfd = 1,
158*fa0f6e62SHans Petter Selasky 	.f_detach = cuse_client_kqfilter_write_detach,
159*fa0f6e62SHans Petter Selasky 	.f_event = cuse_client_kqfilter_write_event,
160*fa0f6e62SHans Petter Selasky };
161*fa0f6e62SHans Petter Selasky 
162*fa0f6e62SHans Petter Selasky static d_open_t cuse_client_open;
163*fa0f6e62SHans Petter Selasky static d_close_t cuse_client_close;
164*fa0f6e62SHans Petter Selasky static d_ioctl_t cuse_client_ioctl;
165*fa0f6e62SHans Petter Selasky static d_read_t cuse_client_read;
166*fa0f6e62SHans Petter Selasky static d_write_t cuse_client_write;
167*fa0f6e62SHans Petter Selasky static d_poll_t cuse_client_poll;
168*fa0f6e62SHans Petter Selasky static d_mmap_t cuse_client_mmap;
169*fa0f6e62SHans Petter Selasky static d_kqfilter_t cuse_client_kqfilter;
170*fa0f6e62SHans Petter Selasky 
171*fa0f6e62SHans Petter Selasky static struct cdevsw cuse_client_devsw = {
172*fa0f6e62SHans Petter Selasky 	.d_version = D_VERSION,
173*fa0f6e62SHans Petter Selasky 	.d_open = cuse_client_open,
174*fa0f6e62SHans Petter Selasky 	.d_close = cuse_client_close,
175*fa0f6e62SHans Petter Selasky 	.d_ioctl = cuse_client_ioctl,
176*fa0f6e62SHans Petter Selasky 	.d_name = "cuse_client",
177*fa0f6e62SHans Petter Selasky 	.d_flags = D_TRACKCLOSE,
178*fa0f6e62SHans Petter Selasky 	.d_read = cuse_client_read,
179*fa0f6e62SHans Petter Selasky 	.d_write = cuse_client_write,
180*fa0f6e62SHans Petter Selasky 	.d_poll = cuse_client_poll,
181*fa0f6e62SHans Petter Selasky 	.d_mmap = cuse_client_mmap,
182*fa0f6e62SHans Petter Selasky 	.d_kqfilter = cuse_client_kqfilter,
183*fa0f6e62SHans Petter Selasky };
184*fa0f6e62SHans Petter Selasky 
185*fa0f6e62SHans Petter Selasky static d_open_t cuse_server_open;
186*fa0f6e62SHans Petter Selasky static d_close_t cuse_server_close;
187*fa0f6e62SHans Petter Selasky static d_ioctl_t cuse_server_ioctl;
188*fa0f6e62SHans Petter Selasky static d_read_t cuse_server_read;
189*fa0f6e62SHans Petter Selasky static d_write_t cuse_server_write;
190*fa0f6e62SHans Petter Selasky static d_poll_t cuse_server_poll;
191*fa0f6e62SHans Petter Selasky static d_mmap_t cuse_server_mmap;
192*fa0f6e62SHans Petter Selasky 
193*fa0f6e62SHans Petter Selasky static struct cdevsw cuse_server_devsw = {
194*fa0f6e62SHans Petter Selasky 	.d_version = D_VERSION,
195*fa0f6e62SHans Petter Selasky 	.d_open = cuse_server_open,
196*fa0f6e62SHans Petter Selasky 	.d_close = cuse_server_close,
197*fa0f6e62SHans Petter Selasky 	.d_ioctl = cuse_server_ioctl,
198*fa0f6e62SHans Petter Selasky 	.d_name = "cuse_server",
199*fa0f6e62SHans Petter Selasky 	.d_flags = D_TRACKCLOSE,
200*fa0f6e62SHans Petter Selasky 	.d_read = cuse_server_read,
201*fa0f6e62SHans Petter Selasky 	.d_write = cuse_server_write,
202*fa0f6e62SHans Petter Selasky 	.d_poll = cuse_server_poll,
203*fa0f6e62SHans Petter Selasky 	.d_mmap = cuse_server_mmap,
204*fa0f6e62SHans Petter Selasky };
205*fa0f6e62SHans Petter Selasky 
206*fa0f6e62SHans Petter Selasky static void cuse_client_is_closing(struct cuse_client *);
207*fa0f6e62SHans Petter Selasky static int cuse_free_unit_by_id_locked(struct cuse_server *, int);
208*fa0f6e62SHans Petter Selasky 
209*fa0f6e62SHans Petter Selasky static void
210*fa0f6e62SHans Petter Selasky cuse_lock(void)
211*fa0f6e62SHans Petter Selasky {
212*fa0f6e62SHans Petter Selasky 	mtx_lock(&cuse_mtx);
213*fa0f6e62SHans Petter Selasky }
214*fa0f6e62SHans Petter Selasky 
215*fa0f6e62SHans Petter Selasky static void
216*fa0f6e62SHans Petter Selasky cuse_unlock(void)
217*fa0f6e62SHans Petter Selasky {
218*fa0f6e62SHans Petter Selasky 	mtx_unlock(&cuse_mtx);
219*fa0f6e62SHans Petter Selasky }
220*fa0f6e62SHans Petter Selasky 
221*fa0f6e62SHans Petter Selasky static void
222*fa0f6e62SHans Petter Selasky cuse_cmd_lock(struct cuse_client_command *pccmd)
223*fa0f6e62SHans Petter Selasky {
224*fa0f6e62SHans Petter Selasky 	sx_xlock(&pccmd->sx);
225*fa0f6e62SHans Petter Selasky }
226*fa0f6e62SHans Petter Selasky 
227*fa0f6e62SHans Petter Selasky static void
228*fa0f6e62SHans Petter Selasky cuse_cmd_unlock(struct cuse_client_command *pccmd)
229*fa0f6e62SHans Petter Selasky {
230*fa0f6e62SHans Petter Selasky 	sx_xunlock(&pccmd->sx);
231*fa0f6e62SHans Petter Selasky }
232*fa0f6e62SHans Petter Selasky 
233*fa0f6e62SHans Petter Selasky static void
234*fa0f6e62SHans Petter Selasky cuse_kern_init(void *arg)
235*fa0f6e62SHans Petter Selasky {
236*fa0f6e62SHans Petter Selasky 	TAILQ_INIT(&cuse_server_head);
237*fa0f6e62SHans Petter Selasky 
238*fa0f6e62SHans Petter Selasky 	mtx_init(&cuse_mtx, "cuse-mtx", NULL, MTX_DEF);
239*fa0f6e62SHans Petter Selasky 
240*fa0f6e62SHans Petter Selasky 	cuse_dev = make_dev(&cuse_server_devsw, 0,
241*fa0f6e62SHans Petter Selasky 	    UID_ROOT, GID_OPERATOR, 0600, "cuse");
242*fa0f6e62SHans Petter Selasky 
243*fa0f6e62SHans Petter Selasky 	printf("Cuse v%d.%d.%d @ /dev/cuse\n",
244*fa0f6e62SHans Petter Selasky 	    (CUSE_VERSION >> 16) & 0xFF, (CUSE_VERSION >> 8) & 0xFF,
245*fa0f6e62SHans Petter Selasky 	    (CUSE_VERSION >> 0) & 0xFF);
246*fa0f6e62SHans Petter Selasky }
247*fa0f6e62SHans Petter Selasky 
248*fa0f6e62SHans Petter Selasky SYSINIT(cuse_kern_init, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_init, 0);
249*fa0f6e62SHans Petter Selasky 
250*fa0f6e62SHans Petter Selasky static void
251*fa0f6e62SHans Petter Selasky cuse_kern_uninit(void *arg)
252*fa0f6e62SHans Petter Selasky {
253*fa0f6e62SHans Petter Selasky 	void *ptr;
254*fa0f6e62SHans Petter Selasky 
255*fa0f6e62SHans Petter Selasky 	while (1) {
256*fa0f6e62SHans Petter Selasky 
257*fa0f6e62SHans Petter Selasky 		printf("Cuse: Please exit all /dev/cuse instances "
258*fa0f6e62SHans Petter Selasky 		    "and processes which have used this device.\n");
259*fa0f6e62SHans Petter Selasky 
260*fa0f6e62SHans Petter Selasky 		pause("DRAIN", 2 * hz);
261*fa0f6e62SHans Petter Selasky 
262*fa0f6e62SHans Petter Selasky 		cuse_lock();
263*fa0f6e62SHans Petter Selasky 		ptr = TAILQ_FIRST(&cuse_server_head);
264*fa0f6e62SHans Petter Selasky 		cuse_unlock();
265*fa0f6e62SHans Petter Selasky 
266*fa0f6e62SHans Petter Selasky 		if (ptr == NULL)
267*fa0f6e62SHans Petter Selasky 			break;
268*fa0f6e62SHans Petter Selasky 	}
269*fa0f6e62SHans Petter Selasky 
270*fa0f6e62SHans Petter Selasky 	if (cuse_dev != NULL)
271*fa0f6e62SHans Petter Selasky 		destroy_dev(cuse_dev);
272*fa0f6e62SHans Petter Selasky 
273*fa0f6e62SHans Petter Selasky 	mtx_destroy(&cuse_mtx);
274*fa0f6e62SHans Petter Selasky }
275*fa0f6e62SHans Petter Selasky 
276*fa0f6e62SHans Petter Selasky SYSUNINIT(cuse_kern_uninit, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_uninit, 0);
277*fa0f6e62SHans Petter Selasky 
278*fa0f6e62SHans Petter Selasky static int
279*fa0f6e62SHans Petter Selasky cuse_server_get(struct cuse_server **ppcs)
280*fa0f6e62SHans Petter Selasky {
281*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
282*fa0f6e62SHans Petter Selasky 	int error;
283*fa0f6e62SHans Petter Selasky 
284*fa0f6e62SHans Petter Selasky 	error = devfs_get_cdevpriv((void **)&pcs);
285*fa0f6e62SHans Petter Selasky 	if (error != 0) {
286*fa0f6e62SHans Petter Selasky 		*ppcs = NULL;
287*fa0f6e62SHans Petter Selasky 		return (error);
288*fa0f6e62SHans Petter Selasky 	}
289*fa0f6e62SHans Petter Selasky 	/* check if closing */
290*fa0f6e62SHans Petter Selasky 	cuse_lock();
291*fa0f6e62SHans Petter Selasky 	if (pcs->is_closing) {
292*fa0f6e62SHans Petter Selasky 		cuse_unlock();
293*fa0f6e62SHans Petter Selasky 		*ppcs = NULL;
294*fa0f6e62SHans Petter Selasky 		return (EINVAL);
295*fa0f6e62SHans Petter Selasky 	}
296*fa0f6e62SHans Petter Selasky 	cuse_unlock();
297*fa0f6e62SHans Petter Selasky 	*ppcs = pcs;
298*fa0f6e62SHans Petter Selasky 	return (0);
299*fa0f6e62SHans Petter Selasky }
300*fa0f6e62SHans Petter Selasky 
301*fa0f6e62SHans Petter Selasky static void
302*fa0f6e62SHans Petter Selasky cuse_server_is_closing(struct cuse_server *pcs)
303*fa0f6e62SHans Petter Selasky {
304*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
305*fa0f6e62SHans Petter Selasky 
306*fa0f6e62SHans Petter Selasky 	if (pcs->is_closing)
307*fa0f6e62SHans Petter Selasky 		return;
308*fa0f6e62SHans Petter Selasky 
309*fa0f6e62SHans Petter Selasky 	pcs->is_closing = 1;
310*fa0f6e62SHans Petter Selasky 
311*fa0f6e62SHans Petter Selasky 	TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
312*fa0f6e62SHans Petter Selasky 		cuse_client_is_closing(pcc);
313*fa0f6e62SHans Petter Selasky 	}
314*fa0f6e62SHans Petter Selasky }
315*fa0f6e62SHans Petter Selasky 
316*fa0f6e62SHans Petter Selasky static struct cuse_client_command *
317*fa0f6e62SHans Petter Selasky cuse_server_find_command(struct cuse_server *pcs, struct thread *td)
318*fa0f6e62SHans Petter Selasky {
319*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
320*fa0f6e62SHans Petter Selasky 	int n;
321*fa0f6e62SHans Petter Selasky 
322*fa0f6e62SHans Petter Selasky 	if (pcs->is_closing)
323*fa0f6e62SHans Petter Selasky 		goto done;
324*fa0f6e62SHans Petter Selasky 
325*fa0f6e62SHans Petter Selasky 	TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
326*fa0f6e62SHans Petter Selasky 		if (CUSE_CLIENT_CLOSING(pcc))
327*fa0f6e62SHans Petter Selasky 			continue;
328*fa0f6e62SHans Petter Selasky 		for (n = 0; n != CUSE_CMD_MAX; n++) {
329*fa0f6e62SHans Petter Selasky 			if (pcc->cmds[n].entered == td)
330*fa0f6e62SHans Petter Selasky 				return (&pcc->cmds[n]);
331*fa0f6e62SHans Petter Selasky 		}
332*fa0f6e62SHans Petter Selasky 	}
333*fa0f6e62SHans Petter Selasky done:
334*fa0f6e62SHans Petter Selasky 	return (NULL);
335*fa0f6e62SHans Petter Selasky }
336*fa0f6e62SHans Petter Selasky 
337*fa0f6e62SHans Petter Selasky static void
338*fa0f6e62SHans Petter Selasky cuse_str_filter(char *ptr)
339*fa0f6e62SHans Petter Selasky {
340*fa0f6e62SHans Petter Selasky 	int c;
341*fa0f6e62SHans Petter Selasky 
342*fa0f6e62SHans Petter Selasky 	while (((c = *ptr) != 0)) {
343*fa0f6e62SHans Petter Selasky 
344*fa0f6e62SHans Petter Selasky 		if ((c >= 'a') && (c <= 'z')) {
345*fa0f6e62SHans Petter Selasky 			ptr++;
346*fa0f6e62SHans Petter Selasky 			continue;
347*fa0f6e62SHans Petter Selasky 		}
348*fa0f6e62SHans Petter Selasky 		if ((c >= 'A') && (c <= 'Z')) {
349*fa0f6e62SHans Petter Selasky 			ptr++;
350*fa0f6e62SHans Petter Selasky 			continue;
351*fa0f6e62SHans Petter Selasky 		}
352*fa0f6e62SHans Petter Selasky 		if ((c >= '0') && (c <= '9')) {
353*fa0f6e62SHans Petter Selasky 			ptr++;
354*fa0f6e62SHans Petter Selasky 			continue;
355*fa0f6e62SHans Petter Selasky 		}
356*fa0f6e62SHans Petter Selasky 		if ((c == '.') || (c == '_') || (c == '/')) {
357*fa0f6e62SHans Petter Selasky 			ptr++;
358*fa0f6e62SHans Petter Selasky 			continue;
359*fa0f6e62SHans Petter Selasky 		}
360*fa0f6e62SHans Petter Selasky 		*ptr = '_';
361*fa0f6e62SHans Petter Selasky 
362*fa0f6e62SHans Petter Selasky 		ptr++;
363*fa0f6e62SHans Petter Selasky 	}
364*fa0f6e62SHans Petter Selasky }
365*fa0f6e62SHans Petter Selasky 
366*fa0f6e62SHans Petter Selasky static int
367*fa0f6e62SHans Petter Selasky cuse_convert_error(int error)
368*fa0f6e62SHans Petter Selasky {
369*fa0f6e62SHans Petter Selasky 	;				/* indent fix */
370*fa0f6e62SHans Petter Selasky 	switch (error) {
371*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_NONE:
372*fa0f6e62SHans Petter Selasky 		return (0);
373*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_BUSY:
374*fa0f6e62SHans Petter Selasky 		return (EBUSY);
375*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_WOULDBLOCK:
376*fa0f6e62SHans Petter Selasky 		return (EWOULDBLOCK);
377*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_INVALID:
378*fa0f6e62SHans Petter Selasky 		return (EINVAL);
379*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_NO_MEMORY:
380*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
381*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_FAULT:
382*fa0f6e62SHans Petter Selasky 		return (EFAULT);
383*fa0f6e62SHans Petter Selasky 	case CUSE_ERR_SIGNAL:
384*fa0f6e62SHans Petter Selasky 		return (EINTR);
385*fa0f6e62SHans Petter Selasky 	default:
386*fa0f6e62SHans Petter Selasky 		return (ENXIO);
387*fa0f6e62SHans Petter Selasky 	}
388*fa0f6e62SHans Petter Selasky }
389*fa0f6e62SHans Petter Selasky 
390*fa0f6e62SHans Petter Selasky static void
391*fa0f6e62SHans Petter Selasky cuse_server_free_memory(struct cuse_server *pcs)
392*fa0f6e62SHans Petter Selasky {
393*fa0f6e62SHans Petter Selasky 	struct cuse_memory *mem;
394*fa0f6e62SHans Petter Selasky 	uint32_t n;
395*fa0f6e62SHans Petter Selasky 
396*fa0f6e62SHans Petter Selasky 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
397*fa0f6e62SHans Petter Selasky 		mem = &cuse_mem[n];
398*fa0f6e62SHans Petter Selasky 
399*fa0f6e62SHans Petter Selasky 		/* this memory is never freed */
400*fa0f6e62SHans Petter Selasky 		if (mem->owner == pcs) {
401*fa0f6e62SHans Petter Selasky 			mem->owner = NULL;
402*fa0f6e62SHans Petter Selasky 			mem->is_allocated = 0;
403*fa0f6e62SHans Petter Selasky 		}
404*fa0f6e62SHans Petter Selasky 	}
405*fa0f6e62SHans Petter Selasky }
406*fa0f6e62SHans Petter Selasky 
407*fa0f6e62SHans Petter Selasky static int
408*fa0f6e62SHans Petter Selasky cuse_server_alloc_memory(struct cuse_server *pcs,
409*fa0f6e62SHans Petter Selasky     struct cuse_memory *mem, uint32_t page_count)
410*fa0f6e62SHans Petter Selasky {
411*fa0f6e62SHans Petter Selasky 	void *ptr;
412*fa0f6e62SHans Petter Selasky 	int error;
413*fa0f6e62SHans Petter Selasky 
414*fa0f6e62SHans Petter Selasky 	cuse_lock();
415*fa0f6e62SHans Petter Selasky 
416*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr == NBUSY) {
417*fa0f6e62SHans Petter Selasky 		cuse_unlock();
418*fa0f6e62SHans Petter Selasky 		return (EBUSY);
419*fa0f6e62SHans Petter Selasky 	}
420*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr != NULL) {
421*fa0f6e62SHans Petter Selasky 		if (mem->is_allocated != 0) {
422*fa0f6e62SHans Petter Selasky 			cuse_unlock();
423*fa0f6e62SHans Petter Selasky 			return (EBUSY);
424*fa0f6e62SHans Petter Selasky 		}
425*fa0f6e62SHans Petter Selasky 		if (mem->page_count == page_count) {
426*fa0f6e62SHans Petter Selasky 			mem->is_allocated = 1;
427*fa0f6e62SHans Petter Selasky 			mem->owner = pcs;
428*fa0f6e62SHans Petter Selasky 			cuse_unlock();
429*fa0f6e62SHans Petter Selasky 			return (0);
430*fa0f6e62SHans Petter Selasky 		}
431*fa0f6e62SHans Petter Selasky 		cuse_unlock();
432*fa0f6e62SHans Petter Selasky 		return (EBUSY);
433*fa0f6e62SHans Petter Selasky 	}
434*fa0f6e62SHans Petter Selasky 	memset(mem, 0, sizeof(*mem));
435*fa0f6e62SHans Petter Selasky 
436*fa0f6e62SHans Petter Selasky 	mem->virtaddr = NBUSY;
437*fa0f6e62SHans Petter Selasky 
438*fa0f6e62SHans Petter Selasky 	cuse_unlock();
439*fa0f6e62SHans Petter Selasky 
440*fa0f6e62SHans Petter Selasky 	ptr = malloc(page_count * PAGE_SIZE, M_CUSE, M_WAITOK | M_ZERO);
441*fa0f6e62SHans Petter Selasky 	if (ptr == NULL)
442*fa0f6e62SHans Petter Selasky 		error = ENOMEM;
443*fa0f6e62SHans Petter Selasky 	else
444*fa0f6e62SHans Petter Selasky 		error = 0;
445*fa0f6e62SHans Petter Selasky 
446*fa0f6e62SHans Petter Selasky 	cuse_lock();
447*fa0f6e62SHans Petter Selasky 
448*fa0f6e62SHans Petter Selasky 	if (error) {
449*fa0f6e62SHans Petter Selasky 		mem->virtaddr = NULL;
450*fa0f6e62SHans Petter Selasky 		cuse_unlock();
451*fa0f6e62SHans Petter Selasky 		return (error);
452*fa0f6e62SHans Petter Selasky 	}
453*fa0f6e62SHans Petter Selasky 	mem->virtaddr = ptr;
454*fa0f6e62SHans Petter Selasky 	mem->page_count = page_count;
455*fa0f6e62SHans Petter Selasky 	mem->is_allocated = 1;
456*fa0f6e62SHans Petter Selasky 	mem->owner = pcs;
457*fa0f6e62SHans Petter Selasky 	cuse_unlock();
458*fa0f6e62SHans Petter Selasky 
459*fa0f6e62SHans Petter Selasky 	return (0);
460*fa0f6e62SHans Petter Selasky }
461*fa0f6e62SHans Petter Selasky 
462*fa0f6e62SHans Petter Selasky static int
463*fa0f6e62SHans Petter Selasky cuse_client_get(struct cuse_client **ppcc)
464*fa0f6e62SHans Petter Selasky {
465*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
466*fa0f6e62SHans Petter Selasky 	int error;
467*fa0f6e62SHans Petter Selasky 
468*fa0f6e62SHans Petter Selasky 	/* try to get private data */
469*fa0f6e62SHans Petter Selasky 	error = devfs_get_cdevpriv((void **)&pcc);
470*fa0f6e62SHans Petter Selasky 	if (error != 0) {
471*fa0f6e62SHans Petter Selasky 		*ppcc = NULL;
472*fa0f6e62SHans Petter Selasky 		return (error);
473*fa0f6e62SHans Petter Selasky 	}
474*fa0f6e62SHans Petter Selasky 	/* check if closing */
475*fa0f6e62SHans Petter Selasky 	cuse_lock();
476*fa0f6e62SHans Petter Selasky 	if (CUSE_CLIENT_CLOSING(pcc) || pcc->server->is_closing) {
477*fa0f6e62SHans Petter Selasky 		cuse_unlock();
478*fa0f6e62SHans Petter Selasky 		*ppcc = NULL;
479*fa0f6e62SHans Petter Selasky 		return (EINVAL);
480*fa0f6e62SHans Petter Selasky 	}
481*fa0f6e62SHans Petter Selasky 	cuse_unlock();
482*fa0f6e62SHans Petter Selasky 	*ppcc = pcc;
483*fa0f6e62SHans Petter Selasky 	return (0);
484*fa0f6e62SHans Petter Selasky }
485*fa0f6e62SHans Petter Selasky 
486*fa0f6e62SHans Petter Selasky static void
487*fa0f6e62SHans Petter Selasky cuse_client_is_closing(struct cuse_client *pcc)
488*fa0f6e62SHans Petter Selasky {
489*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
490*fa0f6e62SHans Petter Selasky 	uint32_t n;
491*fa0f6e62SHans Petter Selasky 
492*fa0f6e62SHans Petter Selasky 	if (CUSE_CLIENT_CLOSING(pcc))
493*fa0f6e62SHans Petter Selasky 		return;
494*fa0f6e62SHans Petter Selasky 
495*fa0f6e62SHans Petter Selasky 	pcc->cflags |= CUSE_CLI_IS_CLOSING;
496*fa0f6e62SHans Petter Selasky 	pcc->server_dev = NULL;
497*fa0f6e62SHans Petter Selasky 
498*fa0f6e62SHans Petter Selasky 	for (n = 0; n != CUSE_CMD_MAX; n++) {
499*fa0f6e62SHans Petter Selasky 
500*fa0f6e62SHans Petter Selasky 		pccmd = &pcc->cmds[n];
501*fa0f6e62SHans Petter Selasky 
502*fa0f6e62SHans Petter Selasky 		if (pccmd->entry.tqe_prev != NULL) {
503*fa0f6e62SHans Petter Selasky 			TAILQ_REMOVE(&pcc->server->head, pccmd, entry);
504*fa0f6e62SHans Petter Selasky 			pccmd->entry.tqe_prev = NULL;
505*fa0f6e62SHans Petter Selasky 		}
506*fa0f6e62SHans Petter Selasky 		cv_broadcast(&pccmd->cv);
507*fa0f6e62SHans Petter Selasky 	}
508*fa0f6e62SHans Petter Selasky }
509*fa0f6e62SHans Petter Selasky 
510*fa0f6e62SHans Petter Selasky static void
511*fa0f6e62SHans Petter Selasky cuse_client_send_command_locked(struct cuse_client_command *pccmd,
512*fa0f6e62SHans Petter Selasky     unsigned long data_ptr, unsigned long arg, int fflags, int ioflag)
513*fa0f6e62SHans Petter Selasky {
514*fa0f6e62SHans Petter Selasky 	unsigned long cuse_fflags = 0;
515*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
516*fa0f6e62SHans Petter Selasky 
517*fa0f6e62SHans Petter Selasky 	if (fflags & FREAD)
518*fa0f6e62SHans Petter Selasky 		cuse_fflags |= CUSE_FFLAG_READ;
519*fa0f6e62SHans Petter Selasky 
520*fa0f6e62SHans Petter Selasky 	if (fflags & FWRITE)
521*fa0f6e62SHans Petter Selasky 		cuse_fflags |= CUSE_FFLAG_WRITE;
522*fa0f6e62SHans Petter Selasky 
523*fa0f6e62SHans Petter Selasky 	if (ioflag & IO_NDELAY)
524*fa0f6e62SHans Petter Selasky 		cuse_fflags |= CUSE_FFLAG_NONBLOCK;
525*fa0f6e62SHans Petter Selasky 
526*fa0f6e62SHans Petter Selasky 	pccmd->sub.fflags = cuse_fflags;
527*fa0f6e62SHans Petter Selasky 	pccmd->sub.data_pointer = data_ptr;
528*fa0f6e62SHans Petter Selasky 	pccmd->sub.argument = arg;
529*fa0f6e62SHans Petter Selasky 
530*fa0f6e62SHans Petter Selasky 	pcs = pccmd->client->server;
531*fa0f6e62SHans Petter Selasky 
532*fa0f6e62SHans Petter Selasky 	if ((pccmd->entry.tqe_prev == NULL) &&
533*fa0f6e62SHans Petter Selasky 	    (CUSE_CLIENT_CLOSING(pccmd->client) == 0) &&
534*fa0f6e62SHans Petter Selasky 	    (pcs->is_closing == 0)) {
535*fa0f6e62SHans Petter Selasky 		TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry);
536*fa0f6e62SHans Petter Selasky 		cv_signal(&pcs->cv);
537*fa0f6e62SHans Petter Selasky 	}
538*fa0f6e62SHans Petter Selasky }
539*fa0f6e62SHans Petter Selasky 
540*fa0f6e62SHans Petter Selasky static void
541*fa0f6e62SHans Petter Selasky cuse_client_got_signal(struct cuse_client_command *pccmd)
542*fa0f6e62SHans Petter Selasky {
543*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
544*fa0f6e62SHans Petter Selasky 
545*fa0f6e62SHans Petter Selasky 	pccmd->got_signal = 1;
546*fa0f6e62SHans Petter Selasky 
547*fa0f6e62SHans Petter Selasky 	pccmd = &pccmd->client->cmds[CUSE_CMD_SIGNAL];
548*fa0f6e62SHans Petter Selasky 
549*fa0f6e62SHans Petter Selasky 	pcs = pccmd->client->server;
550*fa0f6e62SHans Petter Selasky 
551*fa0f6e62SHans Petter Selasky 	if ((pccmd->entry.tqe_prev == NULL) &&
552*fa0f6e62SHans Petter Selasky 	    (CUSE_CLIENT_CLOSING(pccmd->client) == 0) &&
553*fa0f6e62SHans Petter Selasky 	    (pcs->is_closing == 0)) {
554*fa0f6e62SHans Petter Selasky 		TAILQ_INSERT_TAIL(&pcs->head, pccmd, entry);
555*fa0f6e62SHans Petter Selasky 		cv_signal(&pcs->cv);
556*fa0f6e62SHans Petter Selasky 	}
557*fa0f6e62SHans Petter Selasky }
558*fa0f6e62SHans Petter Selasky 
559*fa0f6e62SHans Petter Selasky static int
560*fa0f6e62SHans Petter Selasky cuse_client_receive_command_locked(struct cuse_client_command *pccmd,
561*fa0f6e62SHans Petter Selasky     uint8_t *arg_ptr, uint32_t arg_len)
562*fa0f6e62SHans Petter Selasky {
563*fa0f6e62SHans Petter Selasky 	int error;
564*fa0f6e62SHans Petter Selasky 
565*fa0f6e62SHans Petter Selasky 	error = 0;
566*fa0f6e62SHans Petter Selasky 
567*fa0f6e62SHans Petter Selasky 	pccmd->proc_curr = curthread->td_proc;
568*fa0f6e62SHans Petter Selasky 
569*fa0f6e62SHans Petter Selasky 	if (CUSE_CLIENT_CLOSING(pccmd->client) ||
570*fa0f6e62SHans Petter Selasky 	    pccmd->client->server->is_closing) {
571*fa0f6e62SHans Petter Selasky 		error = CUSE_ERR_OTHER;
572*fa0f6e62SHans Petter Selasky 		goto done;
573*fa0f6e62SHans Petter Selasky 	}
574*fa0f6e62SHans Petter Selasky 	while (pccmd->command == CUSE_CMD_NONE) {
575*fa0f6e62SHans Petter Selasky 		if (error != 0) {
576*fa0f6e62SHans Petter Selasky 			cv_wait(&pccmd->cv, &cuse_mtx);
577*fa0f6e62SHans Petter Selasky 		} else {
578*fa0f6e62SHans Petter Selasky 			error = cv_wait_sig(&pccmd->cv, &cuse_mtx);
579*fa0f6e62SHans Petter Selasky 
580*fa0f6e62SHans Petter Selasky 			if (error != 0)
581*fa0f6e62SHans Petter Selasky 				cuse_client_got_signal(pccmd);
582*fa0f6e62SHans Petter Selasky 		}
583*fa0f6e62SHans Petter Selasky 		if (CUSE_CLIENT_CLOSING(pccmd->client) ||
584*fa0f6e62SHans Petter Selasky 		    pccmd->client->server->is_closing) {
585*fa0f6e62SHans Petter Selasky 			error = CUSE_ERR_OTHER;
586*fa0f6e62SHans Petter Selasky 			goto done;
587*fa0f6e62SHans Petter Selasky 		}
588*fa0f6e62SHans Petter Selasky 	}
589*fa0f6e62SHans Petter Selasky 
590*fa0f6e62SHans Petter Selasky 	error = pccmd->error;
591*fa0f6e62SHans Petter Selasky 	pccmd->command = CUSE_CMD_NONE;
592*fa0f6e62SHans Petter Selasky 	cv_signal(&pccmd->cv);
593*fa0f6e62SHans Petter Selasky 
594*fa0f6e62SHans Petter Selasky done:
595*fa0f6e62SHans Petter Selasky 
596*fa0f6e62SHans Petter Selasky 	/* wait until all process references are gone */
597*fa0f6e62SHans Petter Selasky 
598*fa0f6e62SHans Petter Selasky 	pccmd->proc_curr = NULL;
599*fa0f6e62SHans Petter Selasky 
600*fa0f6e62SHans Petter Selasky 	while (pccmd->proc_refs != 0)
601*fa0f6e62SHans Petter Selasky 		cv_wait(&pccmd->cv, &cuse_mtx);
602*fa0f6e62SHans Petter Selasky 
603*fa0f6e62SHans Petter Selasky 	return (error);
604*fa0f6e62SHans Petter Selasky }
605*fa0f6e62SHans Petter Selasky 
606*fa0f6e62SHans Petter Selasky /*------------------------------------------------------------------------*
607*fa0f6e62SHans Petter Selasky  *	CUSE SERVER PART
608*fa0f6e62SHans Petter Selasky  *------------------------------------------------------------------------*/
609*fa0f6e62SHans Petter Selasky 
610*fa0f6e62SHans Petter Selasky static void
611*fa0f6e62SHans Petter Selasky cuse_server_free_dev(struct cuse_server_dev *pcsd)
612*fa0f6e62SHans Petter Selasky {
613*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
614*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
615*fa0f6e62SHans Petter Selasky 
616*fa0f6e62SHans Petter Selasky 	/* get server pointer */
617*fa0f6e62SHans Petter Selasky 	pcs = pcsd->server;
618*fa0f6e62SHans Petter Selasky 
619*fa0f6e62SHans Petter Selasky 	/* prevent creation of more devices */
620*fa0f6e62SHans Petter Selasky 	cuse_lock();
621*fa0f6e62SHans Petter Selasky 	if (pcsd->kern_dev != NULL)
622*fa0f6e62SHans Petter Selasky 		pcsd->kern_dev->si_drv1 = NULL;
623*fa0f6e62SHans Petter Selasky 
624*fa0f6e62SHans Petter Selasky 	TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
625*fa0f6e62SHans Petter Selasky 		if (pcc->server_dev == pcsd)
626*fa0f6e62SHans Petter Selasky 			cuse_client_is_closing(pcc);
627*fa0f6e62SHans Petter Selasky 	}
628*fa0f6e62SHans Petter Selasky 	cuse_unlock();
629*fa0f6e62SHans Petter Selasky 
630*fa0f6e62SHans Petter Selasky 	/* destroy device, if any */
631*fa0f6e62SHans Petter Selasky 	if (pcsd->kern_dev != NULL) {
632*fa0f6e62SHans Petter Selasky 		/* destroy device synchronously */
633*fa0f6e62SHans Petter Selasky 		destroy_dev(pcsd->kern_dev);
634*fa0f6e62SHans Petter Selasky 	}
635*fa0f6e62SHans Petter Selasky 	free(pcsd, M_CUSE);
636*fa0f6e62SHans Petter Selasky }
637*fa0f6e62SHans Petter Selasky 
638*fa0f6e62SHans Petter Selasky static void
639*fa0f6e62SHans Petter Selasky cuse_server_free(void *arg)
640*fa0f6e62SHans Petter Selasky {
641*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs = arg;
642*fa0f6e62SHans Petter Selasky 	struct cuse_server_dev *pcsd;
643*fa0f6e62SHans Petter Selasky 
644*fa0f6e62SHans Petter Selasky 	cuse_lock();
645*fa0f6e62SHans Petter Selasky 	pcs->refs--;
646*fa0f6e62SHans Petter Selasky 	if (pcs->refs != 0) {
647*fa0f6e62SHans Petter Selasky 		cuse_unlock();
648*fa0f6e62SHans Petter Selasky 		return;
649*fa0f6e62SHans Petter Selasky 	}
650*fa0f6e62SHans Petter Selasky 	cuse_server_is_closing(pcs);
651*fa0f6e62SHans Petter Selasky 
652*fa0f6e62SHans Petter Selasky 	TAILQ_REMOVE(&cuse_server_head, pcs, entry);
653*fa0f6e62SHans Petter Selasky 
654*fa0f6e62SHans Petter Selasky 	cuse_free_unit_by_id_locked(pcs, -1);
655*fa0f6e62SHans Petter Selasky 
656*fa0f6e62SHans Petter Selasky 	while ((pcsd = TAILQ_FIRST(&pcs->hdev)) != NULL) {
657*fa0f6e62SHans Petter Selasky 		TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
658*fa0f6e62SHans Petter Selasky 		cuse_unlock();
659*fa0f6e62SHans Petter Selasky 		cuse_server_free_dev(pcsd);
660*fa0f6e62SHans Petter Selasky 		cuse_lock();
661*fa0f6e62SHans Petter Selasky 	}
662*fa0f6e62SHans Petter Selasky 
663*fa0f6e62SHans Petter Selasky 	cuse_server_free_memory(pcs);
664*fa0f6e62SHans Petter Selasky 
665*fa0f6e62SHans Petter Selasky 	knlist_clear(&pcs->selinfo.si_note, 1);
666*fa0f6e62SHans Petter Selasky 	knlist_destroy(&pcs->selinfo.si_note);
667*fa0f6e62SHans Petter Selasky 
668*fa0f6e62SHans Petter Selasky 	cuse_unlock();
669*fa0f6e62SHans Petter Selasky 
670*fa0f6e62SHans Petter Selasky 	seldrain(&pcs->selinfo);
671*fa0f6e62SHans Petter Selasky 
672*fa0f6e62SHans Petter Selasky 	cv_destroy(&pcs->cv);
673*fa0f6e62SHans Petter Selasky 
674*fa0f6e62SHans Petter Selasky 	free(pcs, M_CUSE);
675*fa0f6e62SHans Petter Selasky }
676*fa0f6e62SHans Petter Selasky 
677*fa0f6e62SHans Petter Selasky static int
678*fa0f6e62SHans Petter Selasky cuse_server_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
679*fa0f6e62SHans Petter Selasky {
680*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
681*fa0f6e62SHans Petter Selasky 
682*fa0f6e62SHans Petter Selasky 	pcs = malloc(sizeof(*pcs), M_CUSE, M_WAITOK | M_ZERO);
683*fa0f6e62SHans Petter Selasky 	if (pcs == NULL)
684*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
685*fa0f6e62SHans Petter Selasky 
686*fa0f6e62SHans Petter Selasky 	if (devfs_set_cdevpriv(pcs, &cuse_server_free)) {
687*fa0f6e62SHans Petter Selasky 		printf("Cuse: Cannot set cdevpriv.\n");
688*fa0f6e62SHans Petter Selasky 		free(pcs, M_CUSE);
689*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
690*fa0f6e62SHans Petter Selasky 	}
691*fa0f6e62SHans Petter Selasky 	TAILQ_INIT(&pcs->head);
692*fa0f6e62SHans Petter Selasky 	TAILQ_INIT(&pcs->hdev);
693*fa0f6e62SHans Petter Selasky 	TAILQ_INIT(&pcs->hcli);
694*fa0f6e62SHans Petter Selasky 
695*fa0f6e62SHans Petter Selasky 	cv_init(&pcs->cv, "cuse-server-cv");
696*fa0f6e62SHans Petter Selasky 
697*fa0f6e62SHans Petter Selasky 	knlist_init_mtx(&pcs->selinfo.si_note, &cuse_mtx);
698*fa0f6e62SHans Petter Selasky 
699*fa0f6e62SHans Petter Selasky 	cuse_lock();
700*fa0f6e62SHans Petter Selasky 	pcs->refs++;
701*fa0f6e62SHans Petter Selasky 	TAILQ_INSERT_TAIL(&cuse_server_head, pcs, entry);
702*fa0f6e62SHans Petter Selasky 	cuse_unlock();
703*fa0f6e62SHans Petter Selasky 
704*fa0f6e62SHans Petter Selasky 	return (0);
705*fa0f6e62SHans Petter Selasky }
706*fa0f6e62SHans Petter Selasky 
707*fa0f6e62SHans Petter Selasky static int
708*fa0f6e62SHans Petter Selasky cuse_server_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
709*fa0f6e62SHans Petter Selasky {
710*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
711*fa0f6e62SHans Petter Selasky 	int error;
712*fa0f6e62SHans Petter Selasky 
713*fa0f6e62SHans Petter Selasky 	error = cuse_server_get(&pcs);
714*fa0f6e62SHans Petter Selasky 	if (error != 0)
715*fa0f6e62SHans Petter Selasky 		goto done;
716*fa0f6e62SHans Petter Selasky 
717*fa0f6e62SHans Petter Selasky 	cuse_lock();
718*fa0f6e62SHans Petter Selasky 	cuse_server_is_closing(pcs);
719*fa0f6e62SHans Petter Selasky 	knlist_clear(&pcs->selinfo.si_note, 1);
720*fa0f6e62SHans Petter Selasky 	cuse_unlock();
721*fa0f6e62SHans Petter Selasky 
722*fa0f6e62SHans Petter Selasky done:
723*fa0f6e62SHans Petter Selasky 	return (0);
724*fa0f6e62SHans Petter Selasky }
725*fa0f6e62SHans Petter Selasky 
726*fa0f6e62SHans Petter Selasky static int
727*fa0f6e62SHans Petter Selasky cuse_server_read(struct cdev *dev, struct uio *uio, int ioflag)
728*fa0f6e62SHans Petter Selasky {
729*fa0f6e62SHans Petter Selasky 	return (ENXIO);
730*fa0f6e62SHans Petter Selasky }
731*fa0f6e62SHans Petter Selasky 
732*fa0f6e62SHans Petter Selasky static int
733*fa0f6e62SHans Petter Selasky cuse_server_write(struct cdev *dev, struct uio *uio, int ioflag)
734*fa0f6e62SHans Petter Selasky {
735*fa0f6e62SHans Petter Selasky 	return (ENXIO);
736*fa0f6e62SHans Petter Selasky }
737*fa0f6e62SHans Petter Selasky 
738*fa0f6e62SHans Petter Selasky static int
739*fa0f6e62SHans Petter Selasky cuse_server_ioctl_copy_locked(struct cuse_client_command *pccmd,
740*fa0f6e62SHans Petter Selasky     struct cuse_data_chunk *pchk, int isread)
741*fa0f6e62SHans Petter Selasky {
742*fa0f6e62SHans Petter Selasky 	struct proc *p_proc;
743*fa0f6e62SHans Petter Selasky 	uint32_t offset;
744*fa0f6e62SHans Petter Selasky 	int error;
745*fa0f6e62SHans Petter Selasky 
746*fa0f6e62SHans Petter Selasky 	offset = pchk->peer_ptr - CUSE_BUF_MIN_PTR;
747*fa0f6e62SHans Petter Selasky 
748*fa0f6e62SHans Petter Selasky 	if (pchk->length > CUSE_BUFFER_MAX)
749*fa0f6e62SHans Petter Selasky 		return (EFAULT);
750*fa0f6e62SHans Petter Selasky 
751*fa0f6e62SHans Petter Selasky 	if (offset >= CUSE_BUFFER_MAX)
752*fa0f6e62SHans Petter Selasky 		return (EFAULT);
753*fa0f6e62SHans Petter Selasky 
754*fa0f6e62SHans Petter Selasky 	if ((offset + pchk->length) > CUSE_BUFFER_MAX)
755*fa0f6e62SHans Petter Selasky 		return (EFAULT);
756*fa0f6e62SHans Petter Selasky 
757*fa0f6e62SHans Petter Selasky 	p_proc = pccmd->proc_curr;
758*fa0f6e62SHans Petter Selasky 	if (p_proc == NULL)
759*fa0f6e62SHans Petter Selasky 		return (ENXIO);
760*fa0f6e62SHans Petter Selasky 
761*fa0f6e62SHans Petter Selasky 	if (pccmd->proc_refs < 0)
762*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
763*fa0f6e62SHans Petter Selasky 
764*fa0f6e62SHans Petter Selasky 	pccmd->proc_refs++;
765*fa0f6e62SHans Petter Selasky 
766*fa0f6e62SHans Petter Selasky 	cuse_unlock();
767*fa0f6e62SHans Petter Selasky 
768*fa0f6e62SHans Petter Selasky 	if (isread == 0) {
769*fa0f6e62SHans Petter Selasky 		error = copyin(
770*fa0f6e62SHans Petter Selasky 		    (void *)pchk->local_ptr,
771*fa0f6e62SHans Petter Selasky 		    pccmd->client->ioctl_buffer + offset,
772*fa0f6e62SHans Petter Selasky 		    pchk->length);
773*fa0f6e62SHans Petter Selasky 	} else {
774*fa0f6e62SHans Petter Selasky 		error = copyout(
775*fa0f6e62SHans Petter Selasky 		    pccmd->client->ioctl_buffer + offset,
776*fa0f6e62SHans Petter Selasky 		    (void *)pchk->local_ptr,
777*fa0f6e62SHans Petter Selasky 		    pchk->length);
778*fa0f6e62SHans Petter Selasky 	}
779*fa0f6e62SHans Petter Selasky 
780*fa0f6e62SHans Petter Selasky 	cuse_lock();
781*fa0f6e62SHans Petter Selasky 
782*fa0f6e62SHans Petter Selasky 	pccmd->proc_refs--;
783*fa0f6e62SHans Petter Selasky 
784*fa0f6e62SHans Petter Selasky 	if (pccmd->proc_curr == NULL)
785*fa0f6e62SHans Petter Selasky 		cv_signal(&pccmd->cv);
786*fa0f6e62SHans Petter Selasky 
787*fa0f6e62SHans Petter Selasky 	return (error);
788*fa0f6e62SHans Petter Selasky }
789*fa0f6e62SHans Petter Selasky 
790*fa0f6e62SHans Petter Selasky static int
791*fa0f6e62SHans Petter Selasky cuse_proc2proc_copy(struct proc *proc_s, vm_offset_t data_s,
792*fa0f6e62SHans Petter Selasky     struct proc *proc_d, vm_offset_t data_d, size_t len)
793*fa0f6e62SHans Petter Selasky {
794*fa0f6e62SHans Petter Selasky 	struct thread *td;
795*fa0f6e62SHans Petter Selasky 	struct proc *proc_cur;
796*fa0f6e62SHans Petter Selasky 	int error;
797*fa0f6e62SHans Petter Selasky 
798*fa0f6e62SHans Petter Selasky 	td = curthread;
799*fa0f6e62SHans Petter Selasky 	proc_cur = td->td_proc;
800*fa0f6e62SHans Petter Selasky 
801*fa0f6e62SHans Petter Selasky 	if (proc_cur == proc_d) {
802*fa0f6e62SHans Petter Selasky 		struct iovec iov = {
803*fa0f6e62SHans Petter Selasky 			.iov_base = (caddr_t)data_d,
804*fa0f6e62SHans Petter Selasky 			.iov_len = len,
805*fa0f6e62SHans Petter Selasky 		};
806*fa0f6e62SHans Petter Selasky 		struct uio uio = {
807*fa0f6e62SHans Petter Selasky 			.uio_iov = &iov,
808*fa0f6e62SHans Petter Selasky 			.uio_iovcnt = 1,
809*fa0f6e62SHans Petter Selasky 			.uio_offset = (off_t)data_s,
810*fa0f6e62SHans Petter Selasky 			.uio_resid = len,
811*fa0f6e62SHans Petter Selasky 			.uio_segflg = UIO_USERSPACE,
812*fa0f6e62SHans Petter Selasky 			.uio_rw = UIO_READ,
813*fa0f6e62SHans Petter Selasky 			.uio_td = td,
814*fa0f6e62SHans Petter Selasky 		};
815*fa0f6e62SHans Petter Selasky 
816*fa0f6e62SHans Petter Selasky 		PROC_LOCK(proc_s);
817*fa0f6e62SHans Petter Selasky 		_PHOLD(proc_s);
818*fa0f6e62SHans Petter Selasky 		PROC_UNLOCK(proc_s);
819*fa0f6e62SHans Petter Selasky 
820*fa0f6e62SHans Petter Selasky 		error = proc_rwmem(proc_s, &uio);
821*fa0f6e62SHans Petter Selasky 
822*fa0f6e62SHans Petter Selasky 		PROC_LOCK(proc_s);
823*fa0f6e62SHans Petter Selasky 		_PRELE(proc_s);
824*fa0f6e62SHans Petter Selasky 		PROC_UNLOCK(proc_s);
825*fa0f6e62SHans Petter Selasky 
826*fa0f6e62SHans Petter Selasky 	} else if (proc_cur == proc_s) {
827*fa0f6e62SHans Petter Selasky 		struct iovec iov = {
828*fa0f6e62SHans Petter Selasky 			.iov_base = (caddr_t)data_s,
829*fa0f6e62SHans Petter Selasky 			.iov_len = len,
830*fa0f6e62SHans Petter Selasky 		};
831*fa0f6e62SHans Petter Selasky 		struct uio uio = {
832*fa0f6e62SHans Petter Selasky 			.uio_iov = &iov,
833*fa0f6e62SHans Petter Selasky 			.uio_iovcnt = 1,
834*fa0f6e62SHans Petter Selasky 			.uio_offset = (off_t)data_d,
835*fa0f6e62SHans Petter Selasky 			.uio_resid = len,
836*fa0f6e62SHans Petter Selasky 			.uio_segflg = UIO_USERSPACE,
837*fa0f6e62SHans Petter Selasky 			.uio_rw = UIO_WRITE,
838*fa0f6e62SHans Petter Selasky 			.uio_td = td,
839*fa0f6e62SHans Petter Selasky 		};
840*fa0f6e62SHans Petter Selasky 
841*fa0f6e62SHans Petter Selasky 		PROC_LOCK(proc_d);
842*fa0f6e62SHans Petter Selasky 		_PHOLD(proc_d);
843*fa0f6e62SHans Petter Selasky 		PROC_UNLOCK(proc_d);
844*fa0f6e62SHans Petter Selasky 
845*fa0f6e62SHans Petter Selasky 		error = proc_rwmem(proc_d, &uio);
846*fa0f6e62SHans Petter Selasky 
847*fa0f6e62SHans Petter Selasky 		PROC_LOCK(proc_d);
848*fa0f6e62SHans Petter Selasky 		_PRELE(proc_d);
849*fa0f6e62SHans Petter Selasky 		PROC_UNLOCK(proc_d);
850*fa0f6e62SHans Petter Selasky 	} else {
851*fa0f6e62SHans Petter Selasky 		error = EINVAL;
852*fa0f6e62SHans Petter Selasky 	}
853*fa0f6e62SHans Petter Selasky 	return (error);
854*fa0f6e62SHans Petter Selasky }
855*fa0f6e62SHans Petter Selasky 
856*fa0f6e62SHans Petter Selasky static int
857*fa0f6e62SHans Petter Selasky cuse_server_data_copy_locked(struct cuse_client_command *pccmd,
858*fa0f6e62SHans Petter Selasky     struct cuse_data_chunk *pchk, int isread)
859*fa0f6e62SHans Petter Selasky {
860*fa0f6e62SHans Petter Selasky 	struct proc *p_proc;
861*fa0f6e62SHans Petter Selasky 	int error;
862*fa0f6e62SHans Petter Selasky 
863*fa0f6e62SHans Petter Selasky 	p_proc = pccmd->proc_curr;
864*fa0f6e62SHans Petter Selasky 	if (p_proc == NULL)
865*fa0f6e62SHans Petter Selasky 		return (ENXIO);
866*fa0f6e62SHans Petter Selasky 
867*fa0f6e62SHans Petter Selasky 	if (pccmd->proc_refs < 0)
868*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
869*fa0f6e62SHans Petter Selasky 
870*fa0f6e62SHans Petter Selasky 	pccmd->proc_refs++;
871*fa0f6e62SHans Petter Selasky 
872*fa0f6e62SHans Petter Selasky 	cuse_unlock();
873*fa0f6e62SHans Petter Selasky 
874*fa0f6e62SHans Petter Selasky 	if (isread == 0) {
875*fa0f6e62SHans Petter Selasky 		error = cuse_proc2proc_copy(
876*fa0f6e62SHans Petter Selasky 		    curthread->td_proc, pchk->local_ptr,
877*fa0f6e62SHans Petter Selasky 		    p_proc, pchk->peer_ptr,
878*fa0f6e62SHans Petter Selasky 		    pchk->length);
879*fa0f6e62SHans Petter Selasky 	} else {
880*fa0f6e62SHans Petter Selasky 		error = cuse_proc2proc_copy(
881*fa0f6e62SHans Petter Selasky 		    p_proc, pchk->peer_ptr,
882*fa0f6e62SHans Petter Selasky 		    curthread->td_proc, pchk->local_ptr,
883*fa0f6e62SHans Petter Selasky 		    pchk->length);
884*fa0f6e62SHans Petter Selasky 	}
885*fa0f6e62SHans Petter Selasky 
886*fa0f6e62SHans Petter Selasky 	cuse_lock();
887*fa0f6e62SHans Petter Selasky 
888*fa0f6e62SHans Petter Selasky 	pccmd->proc_refs--;
889*fa0f6e62SHans Petter Selasky 
890*fa0f6e62SHans Petter Selasky 	if (pccmd->proc_curr == NULL)
891*fa0f6e62SHans Petter Selasky 		cv_signal(&pccmd->cv);
892*fa0f6e62SHans Petter Selasky 
893*fa0f6e62SHans Petter Selasky 	return (error);
894*fa0f6e62SHans Petter Selasky }
895*fa0f6e62SHans Petter Selasky 
896*fa0f6e62SHans Petter Selasky static int
897*fa0f6e62SHans Petter Selasky cuse_alloc_unit_by_id_locked(struct cuse_server *pcs, int id)
898*fa0f6e62SHans Petter Selasky {
899*fa0f6e62SHans Petter Selasky 	int n;
900*fa0f6e62SHans Petter Selasky 	int x = 0;
901*fa0f6e62SHans Petter Selasky 	int match;
902*fa0f6e62SHans Petter Selasky 
903*fa0f6e62SHans Petter Selasky 	do {
904*fa0f6e62SHans Petter Selasky 		for (match = n = 0; n != CUSE_DEVICES_MAX; n++) {
905*fa0f6e62SHans Petter Selasky 			if (cuse_alloc_unit[n] != NULL) {
906*fa0f6e62SHans Petter Selasky 				if ((cuse_alloc_unit_id[n] ^ id) & CUSE_ID_MASK)
907*fa0f6e62SHans Petter Selasky 					continue;
908*fa0f6e62SHans Petter Selasky 				if ((cuse_alloc_unit_id[n] & ~CUSE_ID_MASK) == x) {
909*fa0f6e62SHans Petter Selasky 					x++;
910*fa0f6e62SHans Petter Selasky 					match = 1;
911*fa0f6e62SHans Petter Selasky 				}
912*fa0f6e62SHans Petter Selasky 			}
913*fa0f6e62SHans Petter Selasky 		}
914*fa0f6e62SHans Petter Selasky 	} while (match);
915*fa0f6e62SHans Petter Selasky 
916*fa0f6e62SHans Petter Selasky 	if (x < 256) {
917*fa0f6e62SHans Petter Selasky 		for (n = 0; n != CUSE_DEVICES_MAX; n++) {
918*fa0f6e62SHans Petter Selasky 			if (cuse_alloc_unit[n] == NULL) {
919*fa0f6e62SHans Petter Selasky 				cuse_alloc_unit[n] = pcs;
920*fa0f6e62SHans Petter Selasky 				cuse_alloc_unit_id[n] = id | x;
921*fa0f6e62SHans Petter Selasky 				return (x);
922*fa0f6e62SHans Petter Selasky 			}
923*fa0f6e62SHans Petter Selasky 		}
924*fa0f6e62SHans Petter Selasky 	}
925*fa0f6e62SHans Petter Selasky 	return (-1);
926*fa0f6e62SHans Petter Selasky }
927*fa0f6e62SHans Petter Selasky 
928*fa0f6e62SHans Petter Selasky static void
929*fa0f6e62SHans Petter Selasky cuse_server_wakeup_locked(struct cuse_server *pcs)
930*fa0f6e62SHans Petter Selasky {
931*fa0f6e62SHans Petter Selasky 	selwakeup(&pcs->selinfo);
932*fa0f6e62SHans Petter Selasky 	KNOTE_LOCKED(&pcs->selinfo.si_note, 0);
933*fa0f6e62SHans Petter Selasky }
934*fa0f6e62SHans Petter Selasky 
935*fa0f6e62SHans Petter Selasky static int
936*fa0f6e62SHans Petter Selasky cuse_free_unit_by_id_locked(struct cuse_server *pcs, int id)
937*fa0f6e62SHans Petter Selasky {
938*fa0f6e62SHans Petter Selasky 	int n;
939*fa0f6e62SHans Petter Selasky 	int found = 0;
940*fa0f6e62SHans Petter Selasky 
941*fa0f6e62SHans Petter Selasky 	for (n = 0; n != CUSE_DEVICES_MAX; n++) {
942*fa0f6e62SHans Petter Selasky 		if (cuse_alloc_unit[n] == pcs) {
943*fa0f6e62SHans Petter Selasky 			if (cuse_alloc_unit_id[n] == id || id == -1) {
944*fa0f6e62SHans Petter Selasky 				cuse_alloc_unit[n] = NULL;
945*fa0f6e62SHans Petter Selasky 				cuse_alloc_unit_id[n] = 0;
946*fa0f6e62SHans Petter Selasky 				found = 1;
947*fa0f6e62SHans Petter Selasky 			}
948*fa0f6e62SHans Petter Selasky 		}
949*fa0f6e62SHans Petter Selasky 	}
950*fa0f6e62SHans Petter Selasky 
951*fa0f6e62SHans Petter Selasky 	return (found ? 0 : EINVAL);
952*fa0f6e62SHans Petter Selasky }
953*fa0f6e62SHans Petter Selasky 
954*fa0f6e62SHans Petter Selasky static int
955*fa0f6e62SHans Petter Selasky cuse_server_ioctl(struct cdev *dev, unsigned long cmd,
956*fa0f6e62SHans Petter Selasky     caddr_t data, int fflag, struct thread *td)
957*fa0f6e62SHans Petter Selasky {
958*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
959*fa0f6e62SHans Petter Selasky 	int error;
960*fa0f6e62SHans Petter Selasky 
961*fa0f6e62SHans Petter Selasky 	error = cuse_server_get(&pcs);
962*fa0f6e62SHans Petter Selasky 	if (error != 0)
963*fa0f6e62SHans Petter Selasky 		return (error);
964*fa0f6e62SHans Petter Selasky 
965*fa0f6e62SHans Petter Selasky 	switch (cmd) {
966*fa0f6e62SHans Petter Selasky 		struct cuse_client_command *pccmd;
967*fa0f6e62SHans Petter Selasky 		struct cuse_client *pcc;
968*fa0f6e62SHans Petter Selasky 		struct cuse_command *pcmd;
969*fa0f6e62SHans Petter Selasky 		struct cuse_alloc_info *pai;
970*fa0f6e62SHans Petter Selasky 		struct cuse_create_dev *pcd;
971*fa0f6e62SHans Petter Selasky 		struct cuse_server_dev *pcsd;
972*fa0f6e62SHans Petter Selasky 		struct cuse_data_chunk *pchk;
973*fa0f6e62SHans Petter Selasky 		int n;
974*fa0f6e62SHans Petter Selasky 
975*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_GET_COMMAND:
976*fa0f6e62SHans Petter Selasky 		pcmd = (void *)data;
977*fa0f6e62SHans Petter Selasky 
978*fa0f6e62SHans Petter Selasky 		cuse_lock();
979*fa0f6e62SHans Petter Selasky 
980*fa0f6e62SHans Petter Selasky 		while ((pccmd = TAILQ_FIRST(&pcs->head)) == NULL) {
981*fa0f6e62SHans Petter Selasky 			error = cv_wait_sig(&pcs->cv, &cuse_mtx);
982*fa0f6e62SHans Petter Selasky 
983*fa0f6e62SHans Petter Selasky 			if (pcs->is_closing)
984*fa0f6e62SHans Petter Selasky 				error = ENXIO;
985*fa0f6e62SHans Petter Selasky 
986*fa0f6e62SHans Petter Selasky 			if (error) {
987*fa0f6e62SHans Petter Selasky 				cuse_unlock();
988*fa0f6e62SHans Petter Selasky 				return (error);
989*fa0f6e62SHans Petter Selasky 			}
990*fa0f6e62SHans Petter Selasky 		}
991*fa0f6e62SHans Petter Selasky 
992*fa0f6e62SHans Petter Selasky 		TAILQ_REMOVE(&pcs->head, pccmd, entry);
993*fa0f6e62SHans Petter Selasky 		pccmd->entry.tqe_prev = NULL;
994*fa0f6e62SHans Petter Selasky 
995*fa0f6e62SHans Petter Selasky 		pccmd->entered = curthread;
996*fa0f6e62SHans Petter Selasky 
997*fa0f6e62SHans Petter Selasky 		*pcmd = pccmd->sub;
998*fa0f6e62SHans Petter Selasky 
999*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1000*fa0f6e62SHans Petter Selasky 
1001*fa0f6e62SHans Petter Selasky 		break;
1002*fa0f6e62SHans Petter Selasky 
1003*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_SYNC_COMMAND:
1004*fa0f6e62SHans Petter Selasky 
1005*fa0f6e62SHans Petter Selasky 		cuse_lock();
1006*fa0f6e62SHans Petter Selasky 		while ((pccmd = cuse_server_find_command(pcs, curthread)) != NULL) {
1007*fa0f6e62SHans Petter Selasky 
1008*fa0f6e62SHans Petter Selasky 			/* send sync command */
1009*fa0f6e62SHans Petter Selasky 			pccmd->entered = NULL;
1010*fa0f6e62SHans Petter Selasky 			pccmd->error = *(int *)data;
1011*fa0f6e62SHans Petter Selasky 			pccmd->command = CUSE_CMD_SYNC;
1012*fa0f6e62SHans Petter Selasky 
1013*fa0f6e62SHans Petter Selasky 			/* signal peer, if any */
1014*fa0f6e62SHans Petter Selasky 			cv_signal(&pccmd->cv);
1015*fa0f6e62SHans Petter Selasky 		}
1016*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1017*fa0f6e62SHans Petter Selasky 
1018*fa0f6e62SHans Petter Selasky 		break;
1019*fa0f6e62SHans Petter Selasky 
1020*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_ALLOC_UNIT:
1021*fa0f6e62SHans Petter Selasky 
1022*fa0f6e62SHans Petter Selasky 		cuse_lock();
1023*fa0f6e62SHans Petter Selasky 		n = cuse_alloc_unit_by_id_locked(pcs,
1024*fa0f6e62SHans Petter Selasky 		    CUSE_ID_DEFAULT(0));
1025*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1026*fa0f6e62SHans Petter Selasky 
1027*fa0f6e62SHans Petter Selasky 		if (n < 0)
1028*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1029*fa0f6e62SHans Petter Selasky 		else
1030*fa0f6e62SHans Petter Selasky 			*(int *)data = n;
1031*fa0f6e62SHans Petter Selasky 		break;
1032*fa0f6e62SHans Petter Selasky 
1033*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_ALLOC_UNIT_BY_ID:
1034*fa0f6e62SHans Petter Selasky 
1035*fa0f6e62SHans Petter Selasky 		n = *(int *)data;
1036*fa0f6e62SHans Petter Selasky 
1037*fa0f6e62SHans Petter Selasky 		n = (n & CUSE_ID_MASK);
1038*fa0f6e62SHans Petter Selasky 
1039*fa0f6e62SHans Petter Selasky 		cuse_lock();
1040*fa0f6e62SHans Petter Selasky 		n = cuse_alloc_unit_by_id_locked(pcs, n);
1041*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1042*fa0f6e62SHans Petter Selasky 
1043*fa0f6e62SHans Petter Selasky 		if (n < 0)
1044*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1045*fa0f6e62SHans Petter Selasky 		else
1046*fa0f6e62SHans Petter Selasky 			*(int *)data = n;
1047*fa0f6e62SHans Petter Selasky 		break;
1048*fa0f6e62SHans Petter Selasky 
1049*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_FREE_UNIT:
1050*fa0f6e62SHans Petter Selasky 
1051*fa0f6e62SHans Petter Selasky 		n = *(int *)data;
1052*fa0f6e62SHans Petter Selasky 
1053*fa0f6e62SHans Petter Selasky 		n = CUSE_ID_DEFAULT(n);
1054*fa0f6e62SHans Petter Selasky 
1055*fa0f6e62SHans Petter Selasky 		cuse_lock();
1056*fa0f6e62SHans Petter Selasky 		error = cuse_free_unit_by_id_locked(pcs, n);
1057*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1058*fa0f6e62SHans Petter Selasky 		break;
1059*fa0f6e62SHans Petter Selasky 
1060*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_FREE_UNIT_BY_ID:
1061*fa0f6e62SHans Petter Selasky 
1062*fa0f6e62SHans Petter Selasky 		n = *(int *)data;
1063*fa0f6e62SHans Petter Selasky 
1064*fa0f6e62SHans Petter Selasky 		cuse_lock();
1065*fa0f6e62SHans Petter Selasky 		error = cuse_free_unit_by_id_locked(pcs, n);
1066*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1067*fa0f6e62SHans Petter Selasky 		break;
1068*fa0f6e62SHans Petter Selasky 
1069*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_ALLOC_MEMORY:
1070*fa0f6e62SHans Petter Selasky 
1071*fa0f6e62SHans Petter Selasky 		pai = (void *)data;
1072*fa0f6e62SHans Petter Selasky 
1073*fa0f6e62SHans Petter Selasky 		if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) {
1074*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1075*fa0f6e62SHans Petter Selasky 			break;
1076*fa0f6e62SHans Petter Selasky 		}
1077*fa0f6e62SHans Petter Selasky 		if (pai->page_count > CUSE_ALLOC_PAGES_MAX) {
1078*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1079*fa0f6e62SHans Petter Selasky 			break;
1080*fa0f6e62SHans Petter Selasky 		}
1081*fa0f6e62SHans Petter Selasky 		error = cuse_server_alloc_memory(pcs,
1082*fa0f6e62SHans Petter Selasky 		    &cuse_mem[pai->alloc_nr], pai->page_count);
1083*fa0f6e62SHans Petter Selasky 		break;
1084*fa0f6e62SHans Petter Selasky 
1085*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_FREE_MEMORY:
1086*fa0f6e62SHans Petter Selasky 		pai = (void *)data;
1087*fa0f6e62SHans Petter Selasky 
1088*fa0f6e62SHans Petter Selasky 		if (pai->alloc_nr >= CUSE_ALLOC_UNIT_MAX) {
1089*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1090*fa0f6e62SHans Petter Selasky 			break;
1091*fa0f6e62SHans Petter Selasky 		}
1092*fa0f6e62SHans Petter Selasky 		/* we trust the character device driver in this case */
1093*fa0f6e62SHans Petter Selasky 
1094*fa0f6e62SHans Petter Selasky 		cuse_lock();
1095*fa0f6e62SHans Petter Selasky 		if (cuse_mem[pai->alloc_nr].owner == pcs) {
1096*fa0f6e62SHans Petter Selasky 			cuse_mem[pai->alloc_nr].is_allocated = 0;
1097*fa0f6e62SHans Petter Selasky 			cuse_mem[pai->alloc_nr].owner = NULL;
1098*fa0f6e62SHans Petter Selasky 		} else {
1099*fa0f6e62SHans Petter Selasky 			error = EINVAL;
1100*fa0f6e62SHans Petter Selasky 		}
1101*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1102*fa0f6e62SHans Petter Selasky 		break;
1103*fa0f6e62SHans Petter Selasky 
1104*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_GET_SIG:
1105*fa0f6e62SHans Petter Selasky 
1106*fa0f6e62SHans Petter Selasky 		cuse_lock();
1107*fa0f6e62SHans Petter Selasky 		pccmd = cuse_server_find_command(pcs, curthread);
1108*fa0f6e62SHans Petter Selasky 
1109*fa0f6e62SHans Petter Selasky 		if (pccmd != NULL) {
1110*fa0f6e62SHans Petter Selasky 			n = pccmd->got_signal;
1111*fa0f6e62SHans Petter Selasky 			pccmd->got_signal = 0;
1112*fa0f6e62SHans Petter Selasky 		} else {
1113*fa0f6e62SHans Petter Selasky 			n = 0;
1114*fa0f6e62SHans Petter Selasky 		}
1115*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1116*fa0f6e62SHans Petter Selasky 
1117*fa0f6e62SHans Petter Selasky 		*(int *)data = n;
1118*fa0f6e62SHans Petter Selasky 
1119*fa0f6e62SHans Petter Selasky 		break;
1120*fa0f6e62SHans Petter Selasky 
1121*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_SET_PFH:
1122*fa0f6e62SHans Petter Selasky 
1123*fa0f6e62SHans Petter Selasky 		cuse_lock();
1124*fa0f6e62SHans Petter Selasky 		pccmd = cuse_server_find_command(pcs, curthread);
1125*fa0f6e62SHans Petter Selasky 
1126*fa0f6e62SHans Petter Selasky 		if (pccmd != NULL) {
1127*fa0f6e62SHans Petter Selasky 			pcc = pccmd->client;
1128*fa0f6e62SHans Petter Selasky 			for (n = 0; n != CUSE_CMD_MAX; n++) {
1129*fa0f6e62SHans Petter Selasky 				pcc->cmds[n].sub.per_file_handle = *(unsigned long *)data;
1130*fa0f6e62SHans Petter Selasky 			}
1131*fa0f6e62SHans Petter Selasky 		} else {
1132*fa0f6e62SHans Petter Selasky 			error = ENXIO;
1133*fa0f6e62SHans Petter Selasky 		}
1134*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1135*fa0f6e62SHans Petter Selasky 		break;
1136*fa0f6e62SHans Petter Selasky 
1137*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_CREATE_DEV:
1138*fa0f6e62SHans Petter Selasky 
1139*fa0f6e62SHans Petter Selasky 		error = priv_check(curthread, PRIV_DRIVER);
1140*fa0f6e62SHans Petter Selasky 		if (error)
1141*fa0f6e62SHans Petter Selasky 			break;
1142*fa0f6e62SHans Petter Selasky 
1143*fa0f6e62SHans Petter Selasky 		pcd = (void *)data;
1144*fa0f6e62SHans Petter Selasky 
1145*fa0f6e62SHans Petter Selasky 		/* filter input */
1146*fa0f6e62SHans Petter Selasky 
1147*fa0f6e62SHans Petter Selasky 		pcd->devname[sizeof(pcd->devname) - 1] = 0;
1148*fa0f6e62SHans Petter Selasky 
1149*fa0f6e62SHans Petter Selasky 		if (pcd->devname[0] == 0) {
1150*fa0f6e62SHans Petter Selasky 			error = EINVAL;
1151*fa0f6e62SHans Petter Selasky 			break;
1152*fa0f6e62SHans Petter Selasky 		}
1153*fa0f6e62SHans Petter Selasky 		cuse_str_filter(pcd->devname);
1154*fa0f6e62SHans Petter Selasky 
1155*fa0f6e62SHans Petter Selasky 		pcd->permissions &= 0777;
1156*fa0f6e62SHans Petter Selasky 
1157*fa0f6e62SHans Petter Selasky 		/* try to allocate a character device */
1158*fa0f6e62SHans Petter Selasky 
1159*fa0f6e62SHans Petter Selasky 		pcsd = malloc(sizeof(*pcsd), M_CUSE, M_WAITOK | M_ZERO);
1160*fa0f6e62SHans Petter Selasky 
1161*fa0f6e62SHans Petter Selasky 		if (pcsd == NULL) {
1162*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1163*fa0f6e62SHans Petter Selasky 			break;
1164*fa0f6e62SHans Petter Selasky 		}
1165*fa0f6e62SHans Petter Selasky 		pcsd->server = pcs;
1166*fa0f6e62SHans Petter Selasky 
1167*fa0f6e62SHans Petter Selasky 		pcsd->user_dev = pcd->dev;
1168*fa0f6e62SHans Petter Selasky 
1169*fa0f6e62SHans Petter Selasky 		pcsd->kern_dev = make_dev_credf(MAKEDEV_CHECKNAME,
1170*fa0f6e62SHans Petter Selasky 		    &cuse_client_devsw, 0, NULL, pcd->user_id, pcd->group_id,
1171*fa0f6e62SHans Petter Selasky 		    pcd->permissions, "%s", pcd->devname);
1172*fa0f6e62SHans Petter Selasky 
1173*fa0f6e62SHans Petter Selasky 		if (pcsd->kern_dev == NULL) {
1174*fa0f6e62SHans Petter Selasky 			free(pcsd, M_CUSE);
1175*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1176*fa0f6e62SHans Petter Selasky 			break;
1177*fa0f6e62SHans Petter Selasky 		}
1178*fa0f6e62SHans Petter Selasky 		pcsd->kern_dev->si_drv1 = pcsd;
1179*fa0f6e62SHans Petter Selasky 
1180*fa0f6e62SHans Petter Selasky 		cuse_lock();
1181*fa0f6e62SHans Petter Selasky 		TAILQ_INSERT_TAIL(&pcs->hdev, pcsd, entry);
1182*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1183*fa0f6e62SHans Petter Selasky 
1184*fa0f6e62SHans Petter Selasky 		break;
1185*fa0f6e62SHans Petter Selasky 
1186*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_DESTROY_DEV:
1187*fa0f6e62SHans Petter Selasky 
1188*fa0f6e62SHans Petter Selasky 		error = priv_check(curthread, PRIV_DRIVER);
1189*fa0f6e62SHans Petter Selasky 		if (error)
1190*fa0f6e62SHans Petter Selasky 			break;
1191*fa0f6e62SHans Petter Selasky 
1192*fa0f6e62SHans Petter Selasky 		cuse_lock();
1193*fa0f6e62SHans Petter Selasky 
1194*fa0f6e62SHans Petter Selasky 		error = EINVAL;
1195*fa0f6e62SHans Petter Selasky 
1196*fa0f6e62SHans Petter Selasky 		pcsd = TAILQ_FIRST(&pcs->hdev);
1197*fa0f6e62SHans Petter Selasky 		while (pcsd != NULL) {
1198*fa0f6e62SHans Petter Selasky 			if (pcsd->user_dev == *(struct cuse_dev **)data) {
1199*fa0f6e62SHans Petter Selasky 				TAILQ_REMOVE(&pcs->hdev, pcsd, entry);
1200*fa0f6e62SHans Petter Selasky 				cuse_unlock();
1201*fa0f6e62SHans Petter Selasky 				cuse_server_free_dev(pcsd);
1202*fa0f6e62SHans Petter Selasky 				cuse_lock();
1203*fa0f6e62SHans Petter Selasky 				error = 0;
1204*fa0f6e62SHans Petter Selasky 				pcsd = TAILQ_FIRST(&pcs->hdev);
1205*fa0f6e62SHans Petter Selasky 			} else {
1206*fa0f6e62SHans Petter Selasky 				pcsd = TAILQ_NEXT(pcsd, entry);
1207*fa0f6e62SHans Petter Selasky 			}
1208*fa0f6e62SHans Petter Selasky 		}
1209*fa0f6e62SHans Petter Selasky 
1210*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1211*fa0f6e62SHans Petter Selasky 		break;
1212*fa0f6e62SHans Petter Selasky 
1213*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_WRITE_DATA:
1214*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_READ_DATA:
1215*fa0f6e62SHans Petter Selasky 
1216*fa0f6e62SHans Petter Selasky 		cuse_lock();
1217*fa0f6e62SHans Petter Selasky 		pchk = (struct cuse_data_chunk *)data;
1218*fa0f6e62SHans Petter Selasky 
1219*fa0f6e62SHans Petter Selasky 		pccmd = cuse_server_find_command(pcs, curthread);
1220*fa0f6e62SHans Petter Selasky 
1221*fa0f6e62SHans Petter Selasky 		if (pccmd == NULL) {
1222*fa0f6e62SHans Petter Selasky 			error = ENXIO;	/* invalid request */
1223*fa0f6e62SHans Petter Selasky 		} else if (pchk->peer_ptr < CUSE_BUF_MIN_PTR) {
1224*fa0f6e62SHans Petter Selasky 			error = EFAULT;	/* NULL pointer */
1225*fa0f6e62SHans Petter Selasky 		} else if (pchk->peer_ptr < CUSE_BUF_MAX_PTR) {
1226*fa0f6e62SHans Petter Selasky 			error = cuse_server_ioctl_copy_locked(pccmd,
1227*fa0f6e62SHans Petter Selasky 			    pchk, cmd == CUSE_IOCTL_READ_DATA);
1228*fa0f6e62SHans Petter Selasky 		} else {
1229*fa0f6e62SHans Petter Selasky 			error = cuse_server_data_copy_locked(pccmd,
1230*fa0f6e62SHans Petter Selasky 			    pchk, cmd == CUSE_IOCTL_READ_DATA);
1231*fa0f6e62SHans Petter Selasky 		}
1232*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1233*fa0f6e62SHans Petter Selasky 		break;
1234*fa0f6e62SHans Petter Selasky 
1235*fa0f6e62SHans Petter Selasky 	case CUSE_IOCTL_SELWAKEUP:
1236*fa0f6e62SHans Petter Selasky 		cuse_lock();
1237*fa0f6e62SHans Petter Selasky 		/*
1238*fa0f6e62SHans Petter Selasky 		 * We don't know which direction caused the event.
1239*fa0f6e62SHans Petter Selasky 		 * Wakeup both!
1240*fa0f6e62SHans Petter Selasky 		 */
1241*fa0f6e62SHans Petter Selasky 		TAILQ_FOREACH(pcc, &pcs->hcli, entry) {
1242*fa0f6e62SHans Petter Selasky 			pcc->cflags |= (CUSE_CLI_KNOTE_NEED_READ |
1243*fa0f6e62SHans Petter Selasky 			    CUSE_CLI_KNOTE_NEED_WRITE);
1244*fa0f6e62SHans Petter Selasky 		}
1245*fa0f6e62SHans Petter Selasky 		cuse_server_wakeup_locked(pcs);
1246*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1247*fa0f6e62SHans Petter Selasky 		break;
1248*fa0f6e62SHans Petter Selasky 
1249*fa0f6e62SHans Petter Selasky 	default:
1250*fa0f6e62SHans Petter Selasky 		error = ENXIO;
1251*fa0f6e62SHans Petter Selasky 		break;
1252*fa0f6e62SHans Petter Selasky 	}
1253*fa0f6e62SHans Petter Selasky 	return (error);
1254*fa0f6e62SHans Petter Selasky }
1255*fa0f6e62SHans Petter Selasky 
1256*fa0f6e62SHans Petter Selasky static int
1257*fa0f6e62SHans Petter Selasky cuse_server_poll(struct cdev *dev, int events, struct thread *td)
1258*fa0f6e62SHans Petter Selasky {
1259*fa0f6e62SHans Petter Selasky 	return (events & (POLLHUP | POLLPRI | POLLIN |
1260*fa0f6e62SHans Petter Selasky 	    POLLRDNORM | POLLOUT | POLLWRNORM));
1261*fa0f6e62SHans Petter Selasky }
1262*fa0f6e62SHans Petter Selasky 
1263*fa0f6e62SHans Petter Selasky static int
1264*fa0f6e62SHans Petter Selasky cuse_server_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
1265*fa0f6e62SHans Petter Selasky {
1266*fa0f6e62SHans Petter Selasky 	uint32_t page_nr = offset / PAGE_SIZE;
1267*fa0f6e62SHans Petter Selasky 	uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX;
1268*fa0f6e62SHans Petter Selasky 	struct cuse_memory *mem;
1269*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
1270*fa0f6e62SHans Petter Selasky 	uint8_t *ptr;
1271*fa0f6e62SHans Petter Selasky 	int error;
1272*fa0f6e62SHans Petter Selasky 
1273*fa0f6e62SHans Petter Selasky 	if (alloc_nr >= CUSE_ALLOC_UNIT_MAX)
1274*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1275*fa0f6e62SHans Petter Selasky 
1276*fa0f6e62SHans Petter Selasky 	error = cuse_server_get(&pcs);
1277*fa0f6e62SHans Petter Selasky 	if (error != 0)
1278*fa0f6e62SHans Petter Selasky 		pcs = NULL;
1279*fa0f6e62SHans Petter Selasky 
1280*fa0f6e62SHans Petter Selasky 	cuse_lock();
1281*fa0f6e62SHans Petter Selasky 	mem = &cuse_mem[alloc_nr];
1282*fa0f6e62SHans Petter Selasky 
1283*fa0f6e62SHans Petter Selasky 	/* try to enforce slight ownership */
1284*fa0f6e62SHans Petter Selasky 	if ((pcs != NULL) && (mem->owner != pcs)) {
1285*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1286*fa0f6e62SHans Petter Selasky 		return (EINVAL);
1287*fa0f6e62SHans Petter Selasky 	}
1288*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr == NULL) {
1289*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1290*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1291*fa0f6e62SHans Petter Selasky 	}
1292*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr == NBUSY) {
1293*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1294*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1295*fa0f6e62SHans Petter Selasky 	}
1296*fa0f6e62SHans Petter Selasky 	page_nr %= CUSE_ALLOC_PAGES_MAX;
1297*fa0f6e62SHans Petter Selasky 
1298*fa0f6e62SHans Petter Selasky 	if (page_nr >= mem->page_count) {
1299*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1300*fa0f6e62SHans Petter Selasky 		return (ENXIO);
1301*fa0f6e62SHans Petter Selasky 	}
1302*fa0f6e62SHans Petter Selasky 	ptr = mem->virtaddr + (page_nr * PAGE_SIZE);
1303*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1304*fa0f6e62SHans Petter Selasky 
1305*fa0f6e62SHans Petter Selasky 	*paddr = vtophys(ptr);
1306*fa0f6e62SHans Petter Selasky 
1307*fa0f6e62SHans Petter Selasky 	return (0);
1308*fa0f6e62SHans Petter Selasky }
1309*fa0f6e62SHans Petter Selasky 
1310*fa0f6e62SHans Petter Selasky /*------------------------------------------------------------------------*
1311*fa0f6e62SHans Petter Selasky  *	CUSE CLIENT PART
1312*fa0f6e62SHans Petter Selasky  *------------------------------------------------------------------------*/
1313*fa0f6e62SHans Petter Selasky static void
1314*fa0f6e62SHans Petter Selasky cuse_client_free(void *arg)
1315*fa0f6e62SHans Petter Selasky {
1316*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc = arg;
1317*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1318*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
1319*fa0f6e62SHans Petter Selasky 	int n;
1320*fa0f6e62SHans Petter Selasky 
1321*fa0f6e62SHans Petter Selasky 	cuse_lock();
1322*fa0f6e62SHans Petter Selasky 	cuse_client_is_closing(pcc);
1323*fa0f6e62SHans Petter Selasky 	TAILQ_REMOVE(&pcc->server->hcli, pcc, entry);
1324*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1325*fa0f6e62SHans Petter Selasky 
1326*fa0f6e62SHans Petter Selasky 	for (n = 0; n != CUSE_CMD_MAX; n++) {
1327*fa0f6e62SHans Petter Selasky 
1328*fa0f6e62SHans Petter Selasky 		pccmd = &pcc->cmds[n];
1329*fa0f6e62SHans Petter Selasky 
1330*fa0f6e62SHans Petter Selasky 		sx_destroy(&pccmd->sx);
1331*fa0f6e62SHans Petter Selasky 		cv_destroy(&pccmd->cv);
1332*fa0f6e62SHans Petter Selasky 	}
1333*fa0f6e62SHans Petter Selasky 
1334*fa0f6e62SHans Petter Selasky 	pcs = pcc->server;
1335*fa0f6e62SHans Petter Selasky 
1336*fa0f6e62SHans Petter Selasky 	free(pcc, M_CUSE);
1337*fa0f6e62SHans Petter Selasky 
1338*fa0f6e62SHans Petter Selasky 	/* drop reference on server */
1339*fa0f6e62SHans Petter Selasky 	cuse_server_free(pcs);
1340*fa0f6e62SHans Petter Selasky }
1341*fa0f6e62SHans Petter Selasky 
1342*fa0f6e62SHans Petter Selasky static int
1343*fa0f6e62SHans Petter Selasky cuse_client_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
1344*fa0f6e62SHans Petter Selasky {
1345*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1346*fa0f6e62SHans Petter Selasky 	struct cuse_server_dev *pcsd;
1347*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1348*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
1349*fa0f6e62SHans Petter Selasky 	struct cuse_dev *pcd;
1350*fa0f6e62SHans Petter Selasky 	int error;
1351*fa0f6e62SHans Petter Selasky 	int n;
1352*fa0f6e62SHans Petter Selasky 
1353*fa0f6e62SHans Petter Selasky 	cuse_lock();
1354*fa0f6e62SHans Petter Selasky 	pcsd = dev->si_drv1;
1355*fa0f6e62SHans Petter Selasky 	if (pcsd != NULL) {
1356*fa0f6e62SHans Petter Selasky 		pcs = pcsd->server;
1357*fa0f6e62SHans Petter Selasky 		pcd = pcsd->user_dev;
1358*fa0f6e62SHans Petter Selasky 		pcs->refs++;
1359*fa0f6e62SHans Petter Selasky 		if (pcs->refs < 0) {
1360*fa0f6e62SHans Petter Selasky 			/* overflow */
1361*fa0f6e62SHans Petter Selasky 			pcs->refs--;
1362*fa0f6e62SHans Petter Selasky 			pcsd = NULL;
1363*fa0f6e62SHans Petter Selasky 		}
1364*fa0f6e62SHans Petter Selasky 	} else {
1365*fa0f6e62SHans Petter Selasky 		pcs = NULL;
1366*fa0f6e62SHans Petter Selasky 		pcd = NULL;
1367*fa0f6e62SHans Petter Selasky 	}
1368*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1369*fa0f6e62SHans Petter Selasky 
1370*fa0f6e62SHans Petter Selasky 	if (pcsd == NULL)
1371*fa0f6e62SHans Petter Selasky 		return (EINVAL);
1372*fa0f6e62SHans Petter Selasky 
1373*fa0f6e62SHans Petter Selasky 	pcc = malloc(sizeof(*pcc), M_CUSE, M_WAITOK | M_ZERO);
1374*fa0f6e62SHans Petter Selasky 	if (pcc == NULL) {
1375*fa0f6e62SHans Petter Selasky 		/* drop reference on server */
1376*fa0f6e62SHans Petter Selasky 		cuse_server_free(pcs);
1377*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1378*fa0f6e62SHans Petter Selasky 	}
1379*fa0f6e62SHans Petter Selasky 	if (devfs_set_cdevpriv(pcc, &cuse_client_free)) {
1380*fa0f6e62SHans Petter Selasky 		printf("Cuse: Cannot set cdevpriv.\n");
1381*fa0f6e62SHans Petter Selasky 		/* drop reference on server */
1382*fa0f6e62SHans Petter Selasky 		cuse_server_free(pcs);
1383*fa0f6e62SHans Petter Selasky 		free(pcc, M_CUSE);
1384*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1385*fa0f6e62SHans Petter Selasky 	}
1386*fa0f6e62SHans Petter Selasky 	pcc->fflags = fflags;
1387*fa0f6e62SHans Petter Selasky 	pcc->server_dev = pcsd;
1388*fa0f6e62SHans Petter Selasky 	pcc->server = pcs;
1389*fa0f6e62SHans Petter Selasky 
1390*fa0f6e62SHans Petter Selasky 	for (n = 0; n != CUSE_CMD_MAX; n++) {
1391*fa0f6e62SHans Petter Selasky 
1392*fa0f6e62SHans Petter Selasky 		pccmd = &pcc->cmds[n];
1393*fa0f6e62SHans Petter Selasky 
1394*fa0f6e62SHans Petter Selasky 		pccmd->sub.dev = pcd;
1395*fa0f6e62SHans Petter Selasky 		pccmd->sub.command = n;
1396*fa0f6e62SHans Petter Selasky 		pccmd->client = pcc;
1397*fa0f6e62SHans Petter Selasky 
1398*fa0f6e62SHans Petter Selasky 		sx_init(&pccmd->sx, "cuse-client-sx");
1399*fa0f6e62SHans Petter Selasky 		cv_init(&pccmd->cv, "cuse-client-cv");
1400*fa0f6e62SHans Petter Selasky 	}
1401*fa0f6e62SHans Petter Selasky 
1402*fa0f6e62SHans Petter Selasky 	cuse_lock();
1403*fa0f6e62SHans Petter Selasky 
1404*fa0f6e62SHans Petter Selasky 	/* cuse_client_free() assumes that the client is listed somewhere! */
1405*fa0f6e62SHans Petter Selasky 	/* always enqueue */
1406*fa0f6e62SHans Petter Selasky 
1407*fa0f6e62SHans Petter Selasky 	TAILQ_INSERT_TAIL(&pcs->hcli, pcc, entry);
1408*fa0f6e62SHans Petter Selasky 
1409*fa0f6e62SHans Petter Selasky 	/* check if server is closing */
1410*fa0f6e62SHans Petter Selasky 	if ((pcs->is_closing != 0) || (dev->si_drv1 == NULL)) {
1411*fa0f6e62SHans Petter Selasky 		error = EINVAL;
1412*fa0f6e62SHans Petter Selasky 	} else {
1413*fa0f6e62SHans Petter Selasky 		error = 0;
1414*fa0f6e62SHans Petter Selasky 	}
1415*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1416*fa0f6e62SHans Petter Selasky 
1417*fa0f6e62SHans Petter Selasky 	if (error) {
1418*fa0f6e62SHans Petter Selasky 		devfs_clear_cdevpriv();	/* XXX bugfix */
1419*fa0f6e62SHans Petter Selasky 		return (error);
1420*fa0f6e62SHans Petter Selasky 	}
1421*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_OPEN];
1422*fa0f6e62SHans Petter Selasky 
1423*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1424*fa0f6e62SHans Petter Selasky 
1425*fa0f6e62SHans Petter Selasky 	cuse_lock();
1426*fa0f6e62SHans Petter Selasky 	cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0);
1427*fa0f6e62SHans Petter Selasky 
1428*fa0f6e62SHans Petter Selasky 	error = cuse_client_receive_command_locked(pccmd, 0, 0);
1429*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1430*fa0f6e62SHans Petter Selasky 
1431*fa0f6e62SHans Petter Selasky 	if (error < 0) {
1432*fa0f6e62SHans Petter Selasky 		error = cuse_convert_error(error);
1433*fa0f6e62SHans Petter Selasky 	} else {
1434*fa0f6e62SHans Petter Selasky 		error = 0;
1435*fa0f6e62SHans Petter Selasky 	}
1436*fa0f6e62SHans Petter Selasky 
1437*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1438*fa0f6e62SHans Petter Selasky 
1439*fa0f6e62SHans Petter Selasky 	if (error)
1440*fa0f6e62SHans Petter Selasky 		devfs_clear_cdevpriv();	/* XXX bugfix */
1441*fa0f6e62SHans Petter Selasky 
1442*fa0f6e62SHans Petter Selasky 	return (error);
1443*fa0f6e62SHans Petter Selasky }
1444*fa0f6e62SHans Petter Selasky 
1445*fa0f6e62SHans Petter Selasky static int
1446*fa0f6e62SHans Petter Selasky cuse_client_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
1447*fa0f6e62SHans Petter Selasky {
1448*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1449*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1450*fa0f6e62SHans Petter Selasky 	int error;
1451*fa0f6e62SHans Petter Selasky 
1452*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1453*fa0f6e62SHans Petter Selasky 	if (error != 0)
1454*fa0f6e62SHans Petter Selasky 		return (0);
1455*fa0f6e62SHans Petter Selasky 
1456*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_CLOSE];
1457*fa0f6e62SHans Petter Selasky 
1458*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1459*fa0f6e62SHans Petter Selasky 
1460*fa0f6e62SHans Petter Selasky 	cuse_lock();
1461*fa0f6e62SHans Petter Selasky 	cuse_client_send_command_locked(pccmd, 0, 0, pcc->fflags, 0);
1462*fa0f6e62SHans Petter Selasky 
1463*fa0f6e62SHans Petter Selasky 	error = cuse_client_receive_command_locked(pccmd, 0, 0);
1464*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1465*fa0f6e62SHans Petter Selasky 
1466*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1467*fa0f6e62SHans Petter Selasky 
1468*fa0f6e62SHans Petter Selasky 	cuse_lock();
1469*fa0f6e62SHans Petter Selasky 	cuse_client_is_closing(pcc);
1470*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1471*fa0f6e62SHans Petter Selasky 
1472*fa0f6e62SHans Petter Selasky 	return (0);
1473*fa0f6e62SHans Petter Selasky }
1474*fa0f6e62SHans Petter Selasky 
1475*fa0f6e62SHans Petter Selasky static void
1476*fa0f6e62SHans Petter Selasky cuse_client_kqfilter_poll(struct cdev *dev, struct cuse_client *pcc)
1477*fa0f6e62SHans Petter Selasky {
1478*fa0f6e62SHans Petter Selasky 	int temp;
1479*fa0f6e62SHans Petter Selasky 
1480*fa0f6e62SHans Petter Selasky 	cuse_lock();
1481*fa0f6e62SHans Petter Selasky 	temp = (pcc->cflags & (CUSE_CLI_KNOTE_HAS_READ |
1482*fa0f6e62SHans Petter Selasky 	    CUSE_CLI_KNOTE_HAS_WRITE));
1483*fa0f6e62SHans Petter Selasky 	pcc->cflags &= ~(CUSE_CLI_KNOTE_NEED_READ |
1484*fa0f6e62SHans Petter Selasky 	    CUSE_CLI_KNOTE_NEED_WRITE);
1485*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1486*fa0f6e62SHans Petter Selasky 
1487*fa0f6e62SHans Petter Selasky 	if (temp != 0) {
1488*fa0f6e62SHans Petter Selasky 		/* get the latest polling state from the server */
1489*fa0f6e62SHans Petter Selasky 		temp = cuse_client_poll(dev, POLLIN | POLLOUT, NULL);
1490*fa0f6e62SHans Petter Selasky 
1491*fa0f6e62SHans Petter Selasky 		cuse_lock();
1492*fa0f6e62SHans Petter Selasky 		if (temp & (POLLIN | POLLOUT)) {
1493*fa0f6e62SHans Petter Selasky 			if (temp & POLLIN)
1494*fa0f6e62SHans Petter Selasky 				pcc->cflags |= CUSE_CLI_KNOTE_NEED_READ;
1495*fa0f6e62SHans Petter Selasky 			if (temp & POLLOUT)
1496*fa0f6e62SHans Petter Selasky 				pcc->cflags |= CUSE_CLI_KNOTE_NEED_WRITE;
1497*fa0f6e62SHans Petter Selasky 
1498*fa0f6e62SHans Petter Selasky 			/* make sure the "knote" gets woken up */
1499*fa0f6e62SHans Petter Selasky 			cuse_server_wakeup_locked(pcc->server);
1500*fa0f6e62SHans Petter Selasky 		}
1501*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1502*fa0f6e62SHans Petter Selasky 	}
1503*fa0f6e62SHans Petter Selasky }
1504*fa0f6e62SHans Petter Selasky 
1505*fa0f6e62SHans Petter Selasky static int
1506*fa0f6e62SHans Petter Selasky cuse_client_read(struct cdev *dev, struct uio *uio, int ioflag)
1507*fa0f6e62SHans Petter Selasky {
1508*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1509*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1510*fa0f6e62SHans Petter Selasky 	int error;
1511*fa0f6e62SHans Petter Selasky 	int len;
1512*fa0f6e62SHans Petter Selasky 
1513*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1514*fa0f6e62SHans Petter Selasky 	if (error != 0)
1515*fa0f6e62SHans Petter Selasky 		return (error);
1516*fa0f6e62SHans Petter Selasky 
1517*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_READ];
1518*fa0f6e62SHans Petter Selasky 
1519*fa0f6e62SHans Petter Selasky 	if (uio->uio_segflg != UIO_USERSPACE) {
1520*fa0f6e62SHans Petter Selasky 		return (EINVAL);
1521*fa0f6e62SHans Petter Selasky 	}
1522*fa0f6e62SHans Petter Selasky 	uio->uio_segflg = UIO_NOCOPY;
1523*fa0f6e62SHans Petter Selasky 
1524*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1525*fa0f6e62SHans Petter Selasky 
1526*fa0f6e62SHans Petter Selasky 	while (uio->uio_resid != 0) {
1527*fa0f6e62SHans Petter Selasky 
1528*fa0f6e62SHans Petter Selasky 		if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
1529*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1530*fa0f6e62SHans Petter Selasky 			break;
1531*fa0f6e62SHans Petter Selasky 		}
1532*fa0f6e62SHans Petter Selasky 
1533*fa0f6e62SHans Petter Selasky 		len = uio->uio_iov->iov_len;
1534*fa0f6e62SHans Petter Selasky 
1535*fa0f6e62SHans Petter Selasky 		cuse_lock();
1536*fa0f6e62SHans Petter Selasky 		cuse_client_send_command_locked(pccmd,
1537*fa0f6e62SHans Petter Selasky 		    (unsigned long)uio->uio_iov->iov_base,
1538*fa0f6e62SHans Petter Selasky 		    (unsigned long)(unsigned int)len, pcc->fflags, ioflag);
1539*fa0f6e62SHans Petter Selasky 
1540*fa0f6e62SHans Petter Selasky 		error = cuse_client_receive_command_locked(pccmd, 0, 0);
1541*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1542*fa0f6e62SHans Petter Selasky 
1543*fa0f6e62SHans Petter Selasky 		if (error < 0) {
1544*fa0f6e62SHans Petter Selasky 			error = cuse_convert_error(error);
1545*fa0f6e62SHans Petter Selasky 			break;
1546*fa0f6e62SHans Petter Selasky 		} else if (error == len) {
1547*fa0f6e62SHans Petter Selasky 			error = uiomove(NULL, error, uio);
1548*fa0f6e62SHans Petter Selasky 			if (error)
1549*fa0f6e62SHans Petter Selasky 				break;
1550*fa0f6e62SHans Petter Selasky 		} else {
1551*fa0f6e62SHans Petter Selasky 			error = uiomove(NULL, error, uio);
1552*fa0f6e62SHans Petter Selasky 			break;
1553*fa0f6e62SHans Petter Selasky 		}
1554*fa0f6e62SHans Petter Selasky 	}
1555*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1556*fa0f6e62SHans Petter Selasky 
1557*fa0f6e62SHans Petter Selasky 	uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */
1558*fa0f6e62SHans Petter Selasky 
1559*fa0f6e62SHans Petter Selasky 	if (error == EWOULDBLOCK)
1560*fa0f6e62SHans Petter Selasky 		cuse_client_kqfilter_poll(dev, pcc);
1561*fa0f6e62SHans Petter Selasky 
1562*fa0f6e62SHans Petter Selasky 	return (error);
1563*fa0f6e62SHans Petter Selasky }
1564*fa0f6e62SHans Petter Selasky 
1565*fa0f6e62SHans Petter Selasky static int
1566*fa0f6e62SHans Petter Selasky cuse_client_write(struct cdev *dev, struct uio *uio, int ioflag)
1567*fa0f6e62SHans Petter Selasky {
1568*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1569*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1570*fa0f6e62SHans Petter Selasky 	int error;
1571*fa0f6e62SHans Petter Selasky 	int len;
1572*fa0f6e62SHans Petter Selasky 
1573*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1574*fa0f6e62SHans Petter Selasky 	if (error != 0)
1575*fa0f6e62SHans Petter Selasky 		return (error);
1576*fa0f6e62SHans Petter Selasky 
1577*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_WRITE];
1578*fa0f6e62SHans Petter Selasky 
1579*fa0f6e62SHans Petter Selasky 	if (uio->uio_segflg != UIO_USERSPACE) {
1580*fa0f6e62SHans Petter Selasky 		return (EINVAL);
1581*fa0f6e62SHans Petter Selasky 	}
1582*fa0f6e62SHans Petter Selasky 	uio->uio_segflg = UIO_NOCOPY;
1583*fa0f6e62SHans Petter Selasky 
1584*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1585*fa0f6e62SHans Petter Selasky 
1586*fa0f6e62SHans Petter Selasky 	while (uio->uio_resid != 0) {
1587*fa0f6e62SHans Petter Selasky 
1588*fa0f6e62SHans Petter Selasky 		if (uio->uio_iov->iov_len > CUSE_LENGTH_MAX) {
1589*fa0f6e62SHans Petter Selasky 			error = ENOMEM;
1590*fa0f6e62SHans Petter Selasky 			break;
1591*fa0f6e62SHans Petter Selasky 		}
1592*fa0f6e62SHans Petter Selasky 
1593*fa0f6e62SHans Petter Selasky 		len = uio->uio_iov->iov_len;
1594*fa0f6e62SHans Petter Selasky 
1595*fa0f6e62SHans Petter Selasky 		cuse_lock();
1596*fa0f6e62SHans Petter Selasky 		cuse_client_send_command_locked(pccmd,
1597*fa0f6e62SHans Petter Selasky 		    (unsigned long)uio->uio_iov->iov_base,
1598*fa0f6e62SHans Petter Selasky 		    (unsigned long)(unsigned int)len, pcc->fflags, ioflag);
1599*fa0f6e62SHans Petter Selasky 
1600*fa0f6e62SHans Petter Selasky 		error = cuse_client_receive_command_locked(pccmd, 0, 0);
1601*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1602*fa0f6e62SHans Petter Selasky 
1603*fa0f6e62SHans Petter Selasky 		if (error < 0) {
1604*fa0f6e62SHans Petter Selasky 			error = cuse_convert_error(error);
1605*fa0f6e62SHans Petter Selasky 			break;
1606*fa0f6e62SHans Petter Selasky 		} else if (error == len) {
1607*fa0f6e62SHans Petter Selasky 			error = uiomove(NULL, error, uio);
1608*fa0f6e62SHans Petter Selasky 			if (error)
1609*fa0f6e62SHans Petter Selasky 				break;
1610*fa0f6e62SHans Petter Selasky 		} else {
1611*fa0f6e62SHans Petter Selasky 			error = uiomove(NULL, error, uio);
1612*fa0f6e62SHans Petter Selasky 			break;
1613*fa0f6e62SHans Petter Selasky 		}
1614*fa0f6e62SHans Petter Selasky 	}
1615*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1616*fa0f6e62SHans Petter Selasky 
1617*fa0f6e62SHans Petter Selasky 	uio->uio_segflg = UIO_USERSPACE;/* restore segment flag */
1618*fa0f6e62SHans Petter Selasky 
1619*fa0f6e62SHans Petter Selasky 	if (error == EWOULDBLOCK)
1620*fa0f6e62SHans Petter Selasky 		cuse_client_kqfilter_poll(dev, pcc);
1621*fa0f6e62SHans Petter Selasky 
1622*fa0f6e62SHans Petter Selasky 	return (error);
1623*fa0f6e62SHans Petter Selasky }
1624*fa0f6e62SHans Petter Selasky 
1625*fa0f6e62SHans Petter Selasky int
1626*fa0f6e62SHans Petter Selasky cuse_client_ioctl(struct cdev *dev, unsigned long cmd,
1627*fa0f6e62SHans Petter Selasky     caddr_t data, int fflag, struct thread *td)
1628*fa0f6e62SHans Petter Selasky {
1629*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1630*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1631*fa0f6e62SHans Petter Selasky 	int error;
1632*fa0f6e62SHans Petter Selasky 	int len;
1633*fa0f6e62SHans Petter Selasky 
1634*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1635*fa0f6e62SHans Petter Selasky 	if (error != 0)
1636*fa0f6e62SHans Petter Selasky 		return (error);
1637*fa0f6e62SHans Petter Selasky 
1638*fa0f6e62SHans Petter Selasky 	len = IOCPARM_LEN(cmd);
1639*fa0f6e62SHans Petter Selasky 	if (len > CUSE_BUFFER_MAX)
1640*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1641*fa0f6e62SHans Petter Selasky 
1642*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_IOCTL];
1643*fa0f6e62SHans Petter Selasky 
1644*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1645*fa0f6e62SHans Petter Selasky 
1646*fa0f6e62SHans Petter Selasky 	if (cmd & IOC_IN)
1647*fa0f6e62SHans Petter Selasky 		memcpy(pcc->ioctl_buffer, data, len);
1648*fa0f6e62SHans Petter Selasky 
1649*fa0f6e62SHans Petter Selasky 	/*
1650*fa0f6e62SHans Petter Selasky 	 * When the ioctl-length is zero drivers can pass information
1651*fa0f6e62SHans Petter Selasky 	 * through the data pointer of the ioctl. Make sure this information
1652*fa0f6e62SHans Petter Selasky 	 * is forwarded to the driver.
1653*fa0f6e62SHans Petter Selasky 	 */
1654*fa0f6e62SHans Petter Selasky 
1655*fa0f6e62SHans Petter Selasky 	cuse_lock();
1656*fa0f6e62SHans Petter Selasky 	cuse_client_send_command_locked(pccmd,
1657*fa0f6e62SHans Petter Selasky 	    (len == 0) ? *(long *)data : CUSE_BUF_MIN_PTR,
1658*fa0f6e62SHans Petter Selasky 	    (unsigned long)cmd, pcc->fflags,
1659*fa0f6e62SHans Petter Selasky 	    (fflag & O_NONBLOCK) ? IO_NDELAY : 0);
1660*fa0f6e62SHans Petter Selasky 
1661*fa0f6e62SHans Petter Selasky 	error = cuse_client_receive_command_locked(pccmd, data, len);
1662*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1663*fa0f6e62SHans Petter Selasky 
1664*fa0f6e62SHans Petter Selasky 	if (error < 0) {
1665*fa0f6e62SHans Petter Selasky 		error = cuse_convert_error(error);
1666*fa0f6e62SHans Petter Selasky 	} else {
1667*fa0f6e62SHans Petter Selasky 		error = 0;
1668*fa0f6e62SHans Petter Selasky 	}
1669*fa0f6e62SHans Petter Selasky 
1670*fa0f6e62SHans Petter Selasky 	if (cmd & IOC_OUT)
1671*fa0f6e62SHans Petter Selasky 		memcpy(data, pcc->ioctl_buffer, len);
1672*fa0f6e62SHans Petter Selasky 
1673*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1674*fa0f6e62SHans Petter Selasky 
1675*fa0f6e62SHans Petter Selasky 	if (error == EWOULDBLOCK)
1676*fa0f6e62SHans Petter Selasky 		cuse_client_kqfilter_poll(dev, pcc);
1677*fa0f6e62SHans Petter Selasky 
1678*fa0f6e62SHans Petter Selasky 	return (error);
1679*fa0f6e62SHans Petter Selasky }
1680*fa0f6e62SHans Petter Selasky 
1681*fa0f6e62SHans Petter Selasky static int
1682*fa0f6e62SHans Petter Selasky cuse_client_poll(struct cdev *dev, int events, struct thread *td)
1683*fa0f6e62SHans Petter Selasky {
1684*fa0f6e62SHans Petter Selasky 	struct cuse_client_command *pccmd;
1685*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1686*fa0f6e62SHans Petter Selasky 	unsigned long temp;
1687*fa0f6e62SHans Petter Selasky 	int error;
1688*fa0f6e62SHans Petter Selasky 	int revents;
1689*fa0f6e62SHans Petter Selasky 
1690*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1691*fa0f6e62SHans Petter Selasky 	if (error != 0)
1692*fa0f6e62SHans Petter Selasky 		return (POLLNVAL);
1693*fa0f6e62SHans Petter Selasky 
1694*fa0f6e62SHans Petter Selasky 	temp = 0;
1695*fa0f6e62SHans Petter Selasky 
1696*fa0f6e62SHans Petter Selasky 	if (events & (POLLPRI | POLLIN | POLLRDNORM))
1697*fa0f6e62SHans Petter Selasky 		temp |= CUSE_POLL_READ;
1698*fa0f6e62SHans Petter Selasky 
1699*fa0f6e62SHans Petter Selasky 	if (events & (POLLOUT | POLLWRNORM))
1700*fa0f6e62SHans Petter Selasky 		temp |= CUSE_POLL_WRITE;
1701*fa0f6e62SHans Petter Selasky 
1702*fa0f6e62SHans Petter Selasky 	if (events & POLLHUP)
1703*fa0f6e62SHans Petter Selasky 		temp |= CUSE_POLL_ERROR;
1704*fa0f6e62SHans Petter Selasky 
1705*fa0f6e62SHans Petter Selasky 	pccmd = &pcc->cmds[CUSE_CMD_POLL];
1706*fa0f6e62SHans Petter Selasky 
1707*fa0f6e62SHans Petter Selasky 	cuse_cmd_lock(pccmd);
1708*fa0f6e62SHans Petter Selasky 
1709*fa0f6e62SHans Petter Selasky 	/* Need to selrecord() first to not loose any events. */
1710*fa0f6e62SHans Petter Selasky 	if (temp != 0 && td != NULL)
1711*fa0f6e62SHans Petter Selasky 		selrecord(td, &pcc->server->selinfo);
1712*fa0f6e62SHans Petter Selasky 
1713*fa0f6e62SHans Petter Selasky 	cuse_lock();
1714*fa0f6e62SHans Petter Selasky 	cuse_client_send_command_locked(pccmd,
1715*fa0f6e62SHans Petter Selasky 	    0, temp, pcc->fflags, IO_NDELAY);
1716*fa0f6e62SHans Petter Selasky 
1717*fa0f6e62SHans Petter Selasky 	error = cuse_client_receive_command_locked(pccmd, 0, 0);
1718*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1719*fa0f6e62SHans Petter Selasky 
1720*fa0f6e62SHans Petter Selasky 	if (error < 0) {
1721*fa0f6e62SHans Petter Selasky 		revents = POLLNVAL;
1722*fa0f6e62SHans Petter Selasky 	} else {
1723*fa0f6e62SHans Petter Selasky 		revents = 0;
1724*fa0f6e62SHans Petter Selasky 		if (error & CUSE_POLL_READ)
1725*fa0f6e62SHans Petter Selasky 			revents |= (events & (POLLPRI | POLLIN | POLLRDNORM));
1726*fa0f6e62SHans Petter Selasky 		if (error & CUSE_POLL_WRITE)
1727*fa0f6e62SHans Petter Selasky 			revents |= (events & (POLLOUT | POLLWRNORM));
1728*fa0f6e62SHans Petter Selasky 		if (error & CUSE_POLL_ERROR)
1729*fa0f6e62SHans Petter Selasky 			revents |= (events & POLLHUP);
1730*fa0f6e62SHans Petter Selasky 	}
1731*fa0f6e62SHans Petter Selasky 
1732*fa0f6e62SHans Petter Selasky 	cuse_cmd_unlock(pccmd);
1733*fa0f6e62SHans Petter Selasky 
1734*fa0f6e62SHans Petter Selasky 	return (revents);
1735*fa0f6e62SHans Petter Selasky }
1736*fa0f6e62SHans Petter Selasky 
1737*fa0f6e62SHans Petter Selasky static int
1738*fa0f6e62SHans Petter Selasky cuse_client_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
1739*fa0f6e62SHans Petter Selasky {
1740*fa0f6e62SHans Petter Selasky 	uint32_t page_nr = offset / PAGE_SIZE;
1741*fa0f6e62SHans Petter Selasky 	uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX;
1742*fa0f6e62SHans Petter Selasky 	struct cuse_memory *mem;
1743*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
1744*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1745*fa0f6e62SHans Petter Selasky 	uint8_t *ptr;
1746*fa0f6e62SHans Petter Selasky 	int error;
1747*fa0f6e62SHans Petter Selasky 
1748*fa0f6e62SHans Petter Selasky 	if (alloc_nr >= CUSE_ALLOC_UNIT_MAX)
1749*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1750*fa0f6e62SHans Petter Selasky 
1751*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1752*fa0f6e62SHans Petter Selasky 	if (error != 0)
1753*fa0f6e62SHans Petter Selasky 		pcs = NULL;
1754*fa0f6e62SHans Petter Selasky 	else
1755*fa0f6e62SHans Petter Selasky 		pcs = pcc->server;
1756*fa0f6e62SHans Petter Selasky 
1757*fa0f6e62SHans Petter Selasky 	cuse_lock();
1758*fa0f6e62SHans Petter Selasky 	mem = &cuse_mem[alloc_nr];
1759*fa0f6e62SHans Petter Selasky 
1760*fa0f6e62SHans Petter Selasky 	/* try to enforce slight ownership */
1761*fa0f6e62SHans Petter Selasky 	if ((pcs != NULL) && (mem->owner != pcs)) {
1762*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1763*fa0f6e62SHans Petter Selasky 		return (EINVAL);
1764*fa0f6e62SHans Petter Selasky 	}
1765*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr == NULL) {
1766*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1767*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1768*fa0f6e62SHans Petter Selasky 	}
1769*fa0f6e62SHans Petter Selasky 	if (mem->virtaddr == NBUSY) {
1770*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1771*fa0f6e62SHans Petter Selasky 		return (ENOMEM);
1772*fa0f6e62SHans Petter Selasky 	}
1773*fa0f6e62SHans Petter Selasky 	page_nr %= CUSE_ALLOC_PAGES_MAX;
1774*fa0f6e62SHans Petter Selasky 
1775*fa0f6e62SHans Petter Selasky 	if (page_nr >= mem->page_count) {
1776*fa0f6e62SHans Petter Selasky 		cuse_unlock();
1777*fa0f6e62SHans Petter Selasky 		return (ENXIO);
1778*fa0f6e62SHans Petter Selasky 	}
1779*fa0f6e62SHans Petter Selasky 	ptr = mem->virtaddr + (page_nr * PAGE_SIZE);
1780*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1781*fa0f6e62SHans Petter Selasky 
1782*fa0f6e62SHans Petter Selasky 	*paddr = vtophys(ptr);
1783*fa0f6e62SHans Petter Selasky 
1784*fa0f6e62SHans Petter Selasky 	return (0);
1785*fa0f6e62SHans Petter Selasky }
1786*fa0f6e62SHans Petter Selasky 
1787*fa0f6e62SHans Petter Selasky static void
1788*fa0f6e62SHans Petter Selasky cuse_client_kqfilter_read_detach(struct knote *kn)
1789*fa0f6e62SHans Petter Selasky {
1790*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1791*fa0f6e62SHans Petter Selasky 
1792*fa0f6e62SHans Petter Selasky 	cuse_lock();
1793*fa0f6e62SHans Petter Selasky 	pcc = kn->kn_hook;
1794*fa0f6e62SHans Petter Selasky 	knlist_remove(&pcc->server->selinfo.si_note, kn, 1);
1795*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1796*fa0f6e62SHans Petter Selasky }
1797*fa0f6e62SHans Petter Selasky 
1798*fa0f6e62SHans Petter Selasky static void
1799*fa0f6e62SHans Petter Selasky cuse_client_kqfilter_write_detach(struct knote *kn)
1800*fa0f6e62SHans Petter Selasky {
1801*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1802*fa0f6e62SHans Petter Selasky 
1803*fa0f6e62SHans Petter Selasky 	cuse_lock();
1804*fa0f6e62SHans Petter Selasky 	pcc = kn->kn_hook;
1805*fa0f6e62SHans Petter Selasky 	knlist_remove(&pcc->server->selinfo.si_note, kn, 1);
1806*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1807*fa0f6e62SHans Petter Selasky }
1808*fa0f6e62SHans Petter Selasky 
1809*fa0f6e62SHans Petter Selasky static int
1810*fa0f6e62SHans Petter Selasky cuse_client_kqfilter_read_event(struct knote *kn, long hint)
1811*fa0f6e62SHans Petter Selasky {
1812*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1813*fa0f6e62SHans Petter Selasky 
1814*fa0f6e62SHans Petter Selasky 	mtx_assert(&cuse_mtx, MA_OWNED);
1815*fa0f6e62SHans Petter Selasky 
1816*fa0f6e62SHans Petter Selasky 	pcc = kn->kn_hook;
1817*fa0f6e62SHans Petter Selasky 	return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_READ) ? 1 : 0);
1818*fa0f6e62SHans Petter Selasky }
1819*fa0f6e62SHans Petter Selasky 
1820*fa0f6e62SHans Petter Selasky static int
1821*fa0f6e62SHans Petter Selasky cuse_client_kqfilter_write_event(struct knote *kn, long hint)
1822*fa0f6e62SHans Petter Selasky {
1823*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1824*fa0f6e62SHans Petter Selasky 
1825*fa0f6e62SHans Petter Selasky 	mtx_assert(&cuse_mtx, MA_OWNED);
1826*fa0f6e62SHans Petter Selasky 
1827*fa0f6e62SHans Petter Selasky 	pcc = kn->kn_hook;
1828*fa0f6e62SHans Petter Selasky 	return ((pcc->cflags & CUSE_CLI_KNOTE_NEED_WRITE) ? 1 : 0);
1829*fa0f6e62SHans Petter Selasky }
1830*fa0f6e62SHans Petter Selasky 
1831*fa0f6e62SHans Petter Selasky static int
1832*fa0f6e62SHans Petter Selasky cuse_client_kqfilter(struct cdev *dev, struct knote *kn)
1833*fa0f6e62SHans Petter Selasky {
1834*fa0f6e62SHans Petter Selasky 	struct cuse_client *pcc;
1835*fa0f6e62SHans Petter Selasky 	struct cuse_server *pcs;
1836*fa0f6e62SHans Petter Selasky 	int error;
1837*fa0f6e62SHans Petter Selasky 
1838*fa0f6e62SHans Petter Selasky 	error = cuse_client_get(&pcc);
1839*fa0f6e62SHans Petter Selasky 	if (error != 0)
1840*fa0f6e62SHans Petter Selasky 		return (error);
1841*fa0f6e62SHans Petter Selasky 
1842*fa0f6e62SHans Petter Selasky 	cuse_lock();
1843*fa0f6e62SHans Petter Selasky 	pcs = pcc->server;
1844*fa0f6e62SHans Petter Selasky 	switch (kn->kn_filter) {
1845*fa0f6e62SHans Petter Selasky 	case EVFILT_READ:
1846*fa0f6e62SHans Petter Selasky 		pcc->cflags |= CUSE_CLI_KNOTE_HAS_READ;
1847*fa0f6e62SHans Petter Selasky 		kn->kn_hook = pcc;
1848*fa0f6e62SHans Petter Selasky 		kn->kn_fop = &cuse_client_kqfilter_read_ops;
1849*fa0f6e62SHans Petter Selasky 		knlist_add(&pcs->selinfo.si_note, kn, 1);
1850*fa0f6e62SHans Petter Selasky 		break;
1851*fa0f6e62SHans Petter Selasky 	case EVFILT_WRITE:
1852*fa0f6e62SHans Petter Selasky 		pcc->cflags |= CUSE_CLI_KNOTE_HAS_WRITE;
1853*fa0f6e62SHans Petter Selasky 		kn->kn_hook = pcc;
1854*fa0f6e62SHans Petter Selasky 		kn->kn_fop = &cuse_client_kqfilter_write_ops;
1855*fa0f6e62SHans Petter Selasky 		knlist_add(&pcs->selinfo.si_note, kn, 1);
1856*fa0f6e62SHans Petter Selasky 		break;
1857*fa0f6e62SHans Petter Selasky 	default:
1858*fa0f6e62SHans Petter Selasky 		error = EINVAL;
1859*fa0f6e62SHans Petter Selasky 		break;
1860*fa0f6e62SHans Petter Selasky 	}
1861*fa0f6e62SHans Petter Selasky 	cuse_unlock();
1862*fa0f6e62SHans Petter Selasky 
1863*fa0f6e62SHans Petter Selasky 	if (error == 0)
1864*fa0f6e62SHans Petter Selasky 		cuse_client_kqfilter_poll(dev, pcc);
1865*fa0f6e62SHans Petter Selasky 	return (error);
1866*fa0f6e62SHans Petter Selasky }
1867