xref: /netbsd-src/sys/dev/dmover/dmover_io.c (revision 9577643dd8fbb55a1cb22a34eee3d4c73b4c1e50)
1*9577643dSchristos /*	$NetBSD: dmover_io.c,v 1.46 2019/02/10 17:13:33 christos Exp $	*/
25d06c0e8Sthorpej 
35d06c0e8Sthorpej /*
45f35784bSthorpej  * Copyright (c) 2002, 2003 Wasabi Systems, Inc.
55d06c0e8Sthorpej  * All rights reserved.
65d06c0e8Sthorpej  *
75d06c0e8Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
85d06c0e8Sthorpej  *
95d06c0e8Sthorpej  * Redistribution and use in source and binary forms, with or without
105d06c0e8Sthorpej  * modification, are permitted provided that the following conditions
115d06c0e8Sthorpej  * are met:
125d06c0e8Sthorpej  * 1. Redistributions of source code must retain the above copyright
135d06c0e8Sthorpej  *    notice, this list of conditions and the following disclaimer.
145d06c0e8Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
155d06c0e8Sthorpej  *    notice, this list of conditions and the following disclaimer in the
165d06c0e8Sthorpej  *    documentation and/or other materials provided with the distribution.
175d06c0e8Sthorpej  * 3. All advertising materials mentioning features or use of this software
185d06c0e8Sthorpej  *    must display the following acknowledgement:
195d06c0e8Sthorpej  *	This product includes software developed for the NetBSD Project by
205d06c0e8Sthorpej  *	Wasabi Systems, Inc.
215d06c0e8Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
225d06c0e8Sthorpej  *    or promote products derived from this software without specific prior
235d06c0e8Sthorpej  *    written permission.
245d06c0e8Sthorpej  *
255d06c0e8Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
265d06c0e8Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
275d06c0e8Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
285d06c0e8Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
295d06c0e8Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
305d06c0e8Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
315d06c0e8Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
325d06c0e8Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
335d06c0e8Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
345d06c0e8Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
355d06c0e8Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
365d06c0e8Sthorpej  */
375d06c0e8Sthorpej 
385d06c0e8Sthorpej /*
395d06c0e8Sthorpej  * dmover_io.c: Support for user-space access to dmover-api
405d06c0e8Sthorpej  *
415d06c0e8Sthorpej  * This interface is quite simple:
425d06c0e8Sthorpej  *
435d06c0e8Sthorpej  *	1.  The user opens /dev/dmover, which is a cloning device.  This
445d06c0e8Sthorpej  *	    allocates internal state for the session.
455d06c0e8Sthorpej  *
465d06c0e8Sthorpej  *	2.  The user does a DMIO_SETFUNC to select the data movement
475d06c0e8Sthorpej  *	    function.  This actually creates the dmover session.
485d06c0e8Sthorpej  *
495d06c0e8Sthorpej  *	3.  The user writes request messages to its dmover handle.
505d06c0e8Sthorpej  *
515d06c0e8Sthorpej  *	4.  The user reads request responses from its dmover handle.
525d06c0e8Sthorpej  *
535d06c0e8Sthorpej  *	5.  The user closes the file descriptor and the session is
545d06c0e8Sthorpej  *	    torn down.
555d06c0e8Sthorpej  */
565d06c0e8Sthorpej 
575d06c0e8Sthorpej #include <sys/cdefs.h>
58*9577643dSchristos __KERNEL_RCSID(0, "$NetBSD: dmover_io.c,v 1.46 2019/02/10 17:13:33 christos Exp $");
595d06c0e8Sthorpej 
605d06c0e8Sthorpej #include <sys/param.h>
615d06c0e8Sthorpej #include <sys/queue.h>
625d06c0e8Sthorpej #include <sys/conf.h>
635d06c0e8Sthorpej #include <sys/pool.h>
645d06c0e8Sthorpej #include <sys/proc.h>
655d06c0e8Sthorpej #include <sys/poll.h>
665d06c0e8Sthorpej #include <sys/malloc.h>
675d06c0e8Sthorpej #include <sys/file.h>
685d06c0e8Sthorpej #include <sys/filedesc.h>
695d06c0e8Sthorpej #include <sys/filio.h>
705d06c0e8Sthorpej #include <sys/select.h>
715d06c0e8Sthorpej #include <sys/systm.h>
72ec5a9318Syamt #include <sys/workqueue.h>
73ec5a9318Syamt #include <sys/once.h>
74a6a538b3Snonaka #include <sys/stat.h>
75a6a538b3Snonaka #include <sys/kauth.h>
764ac4f6b4Sjakllsch #include <sys/mutex.h>
774ac4f6b4Sjakllsch #include <sys/condvar.h>
78ec5a9318Syamt 
79bffc20a6Suebayasi #include <uvm/uvm_extern.h>
80bffc20a6Suebayasi 
815d06c0e8Sthorpej #include <dev/dmover/dmovervar.h>
825d06c0e8Sthorpej #include <dev/dmover/dmover_io.h>
835d06c0e8Sthorpej 
84e7ae23fdSchristos #include "ioconf.h"
85e7ae23fdSchristos 
865d06c0e8Sthorpej struct dmio_usrreq_state {
87ec5a9318Syamt 	union {
88ec5a9318Syamt 		struct work u_work;
89ec5a9318Syamt 		TAILQ_ENTRY(dmio_usrreq_state) u_q;
90ec5a9318Syamt 	} dus_u;
91ec5a9318Syamt #define	dus_q		dus_u.u_q
92ec5a9318Syamt #define	dus_work	dus_u.u_work
935d06c0e8Sthorpej 	struct uio dus_uio_out;
945d06c0e8Sthorpej 	struct uio *dus_uio_in;
955d06c0e8Sthorpej 	struct dmover_request *dus_req;
965d06c0e8Sthorpej 	uint32_t dus_id;
97ec5a9318Syamt 	struct vmspace *dus_vmspace;
985d06c0e8Sthorpej };
995d06c0e8Sthorpej 
1005d06c0e8Sthorpej struct dmio_state {
1015d06c0e8Sthorpej 	struct dmover_session *ds_session;
1025d06c0e8Sthorpej 	TAILQ_HEAD(, dmio_usrreq_state) ds_pending;
1035d06c0e8Sthorpej 	TAILQ_HEAD(, dmio_usrreq_state) ds_complete;
1045d06c0e8Sthorpej 	struct selinfo ds_selq;
10593124077Sperry 	volatile int ds_flags;
1065d06c0e8Sthorpej 	u_int ds_nreqs;
1074ac4f6b4Sjakllsch 	kmutex_t ds_lock;
1084ac4f6b4Sjakllsch 	kcondvar_t ds_complete_cv;
1094ac4f6b4Sjakllsch 	kcondvar_t ds_nreqs_cv;
110b859fbe7Schristos 	struct timespec ds_atime;
111b859fbe7Schristos 	struct timespec ds_mtime;
112b859fbe7Schristos 	struct timespec ds_btime;
1135d06c0e8Sthorpej };
1145d06c0e8Sthorpej 
115ec5a9318Syamt static ONCE_DECL(dmio_cleaner_control);
116ec5a9318Syamt static struct workqueue *dmio_cleaner;
117ec5a9318Syamt static int dmio_cleaner_init(void);
1184ac4f6b4Sjakllsch static struct dmio_state *dmio_state_get(void);
1194ac4f6b4Sjakllsch static void dmio_state_put(struct dmio_state *);
120ec5a9318Syamt static void dmio_usrreq_fini1(struct work *wk, void *);
121ec5a9318Syamt 
1225d06c0e8Sthorpej #define	DMIO_STATE_SEL		0x0001
1235d06c0e8Sthorpej #define	DMIO_STATE_DEAD		0x0002
1245d06c0e8Sthorpej #define	DMIO_STATE_LARVAL	0x0004
1255d06c0e8Sthorpej #define	DMIO_STATE_READ_WAIT	0x0008
1265d06c0e8Sthorpej #define	DMIO_STATE_WRITE_WAIT	0x0010
1275d06c0e8Sthorpej 
1285d06c0e8Sthorpej #define	DMIO_NREQS_MAX		64	/* XXX pulled out of a hat */
1295d06c0e8Sthorpej 
1305d06c0e8Sthorpej struct pool dmio_state_pool;
1315d06c0e8Sthorpej struct pool dmio_usrreq_state_pool;
1325d06c0e8Sthorpej 
13377a6b82bSgehenna dev_type_open(dmoverioopen);
13477a6b82bSgehenna 
13577a6b82bSgehenna const struct cdevsw dmoverio_cdevsw = {
136a68f9396Sdholland 	.d_open = dmoverioopen,
137a68f9396Sdholland 	.d_close = noclose,
138a68f9396Sdholland 	.d_read = noread,
139a68f9396Sdholland 	.d_write = nowrite,
140a68f9396Sdholland 	.d_ioctl = noioctl,
141a68f9396Sdholland 	.d_stop = nostop,
142a68f9396Sdholland 	.d_tty = notty,
143a68f9396Sdholland 	.d_poll = nopoll,
144a68f9396Sdholland 	.d_mmap = nommap,
145a68f9396Sdholland 	.d_kqfilter = nokqfilter,
146f9228f42Sdholland 	.d_discard = nodiscard,
147a68f9396Sdholland 	.d_flag = D_OTHER
14877a6b82bSgehenna };
1495d06c0e8Sthorpej 
1505d06c0e8Sthorpej /*
1515d06c0e8Sthorpej  * dmoverioattach:
1525d06c0e8Sthorpej  *
1535d06c0e8Sthorpej  *	Pseudo-device attach routine.
1545d06c0e8Sthorpej  */
1555d06c0e8Sthorpej void
dmoverioattach(int count)1565d06c0e8Sthorpej dmoverioattach(int count)
1575d06c0e8Sthorpej {
1585d06c0e8Sthorpej 
1595d06c0e8Sthorpej 	pool_init(&dmio_state_pool, sizeof(struct dmio_state),
16059d979c5Sad 	    0, 0, 0, "dmiostate", NULL, IPL_SOFTCLOCK);
1615d06c0e8Sthorpej 	pool_init(&dmio_usrreq_state_pool, sizeof(struct dmio_usrreq_state),
16259d979c5Sad 	    0, 0, 0, "dmiourstate", NULL, IPL_SOFTCLOCK);
1635d06c0e8Sthorpej }
1645d06c0e8Sthorpej 
1655d06c0e8Sthorpej /*
166ec5a9318Syamt  * dmio_cleaner_init:
167ec5a9318Syamt  *
168ec5a9318Syamt  *	Create cleaner thread.
169ec5a9318Syamt  */
170ec5a9318Syamt static int
dmio_cleaner_init(void)171ec5a9318Syamt dmio_cleaner_init(void)
172ec5a9318Syamt {
173ec5a9318Syamt 
174ec5a9318Syamt 	return workqueue_create(&dmio_cleaner, "dmioclean", dmio_usrreq_fini1,
1758bf76628Syamt 	    NULL, PWAIT, IPL_SOFTCLOCK, 0);
176ec5a9318Syamt }
177ec5a9318Syamt 
1784ac4f6b4Sjakllsch static struct dmio_state *
dmio_state_get(void)1794ac4f6b4Sjakllsch dmio_state_get(void)
1804ac4f6b4Sjakllsch {
1814ac4f6b4Sjakllsch 	struct dmio_state *ds;
1824ac4f6b4Sjakllsch 
183*9577643dSchristos 	ds = pool_get(&dmio_state_pool, PR_WAITOK | PR_ZERO);
1844ac4f6b4Sjakllsch 
1854ac4f6b4Sjakllsch 	getnanotime(&ds->ds_btime);
1864ac4f6b4Sjakllsch 	ds->ds_atime = ds->ds_mtime = ds->ds_btime;
1874ac4f6b4Sjakllsch 
1884ac4f6b4Sjakllsch 	mutex_init(&ds->ds_lock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
1894ac4f6b4Sjakllsch 	cv_init(&ds->ds_complete_cv, "dmvrrd");
1904ac4f6b4Sjakllsch 	cv_init(&ds->ds_nreqs_cv, "dmiowr");
1914ac4f6b4Sjakllsch 	TAILQ_INIT(&ds->ds_pending);
1924ac4f6b4Sjakllsch 	TAILQ_INIT(&ds->ds_complete);
1934ac4f6b4Sjakllsch 	selinit(&ds->ds_selq);
1944ac4f6b4Sjakllsch 
1954ac4f6b4Sjakllsch 	return ds;
1964ac4f6b4Sjakllsch }
1974ac4f6b4Sjakllsch 
1984ac4f6b4Sjakllsch static void
dmio_state_put(struct dmio_state * ds)1994ac4f6b4Sjakllsch dmio_state_put(struct dmio_state *ds)
2004ac4f6b4Sjakllsch {
2014ac4f6b4Sjakllsch 
2024ac4f6b4Sjakllsch 	seldestroy(&ds->ds_selq);
2034ac4f6b4Sjakllsch 	cv_destroy(&ds->ds_nreqs_cv);
2044ac4f6b4Sjakllsch 	cv_destroy(&ds->ds_complete_cv);
2054ac4f6b4Sjakllsch 	mutex_destroy(&ds->ds_lock);
2064ac4f6b4Sjakllsch 
2074ac4f6b4Sjakllsch 	pool_put(&dmio_state_pool, ds);
2084ac4f6b4Sjakllsch }
2094ac4f6b4Sjakllsch 
210ec5a9318Syamt /*
2115d06c0e8Sthorpej  * dmio_usrreq_init:
2125d06c0e8Sthorpej  *
2135d06c0e8Sthorpej  *	Build a request structure.
2145d06c0e8Sthorpej  */
2155d06c0e8Sthorpej static int
dmio_usrreq_init(struct file * fp,struct dmio_usrreq_state * dus,struct dmio_usrreq * req,struct dmover_request * dreq)2165d06c0e8Sthorpej dmio_usrreq_init(struct file *fp, struct dmio_usrreq_state *dus,
2175d06c0e8Sthorpej     struct dmio_usrreq *req, struct dmover_request *dreq)
2185d06c0e8Sthorpej {
2195d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
2205d06c0e8Sthorpej 	struct dmover_session *dses = ds->ds_session;
2215d06c0e8Sthorpej 	struct uio *uio_out = &dus->dus_uio_out;
2225d06c0e8Sthorpej 	struct uio *uio_in;
2235d06c0e8Sthorpej 	dmio_buffer inbuf;
2245d06c0e8Sthorpej 	size_t len;
2251cba452dSthorpej 	int i, error;
2261cba452dSthorpej 	u_int j;
2275d06c0e8Sthorpej 
2285d06c0e8Sthorpej 	/* XXX How should malloc interact w/ FNONBLOCK? */
2295d06c0e8Sthorpej 
230ec5a9318Syamt 	error = RUN_ONCE(&dmio_cleaner_control, dmio_cleaner_init);
231ec5a9318Syamt 	if (error) {
232ec5a9318Syamt 		return error;
233ec5a9318Syamt 	}
234ec5a9318Syamt 
235ec5a9318Syamt 	error = proc_vmspace_getref(curproc, &dus->dus_vmspace);
236ec5a9318Syamt 	if (error) {
237ec5a9318Syamt 		return error;
238ec5a9318Syamt 	}
239ec5a9318Syamt 
2405f35784bSthorpej 	if (req->req_outbuf.dmbuf_iovcnt != 0) {
2415d06c0e8Sthorpej 		if (req->req_outbuf.dmbuf_iovcnt > IOV_MAX)
2425d06c0e8Sthorpej 			return (EINVAL);
2435d06c0e8Sthorpej 		len = sizeof(struct iovec) * req->req_outbuf.dmbuf_iovcnt;
2445d06c0e8Sthorpej 		uio_out->uio_iov = malloc(len, M_TEMP, M_WAITOK);
2455f35784bSthorpej 		error = copyin(req->req_outbuf.dmbuf_iov, uio_out->uio_iov,
2465f35784bSthorpej 		    len);
2475d06c0e8Sthorpej 		if (error) {
2485d06c0e8Sthorpej 			free(uio_out->uio_iov, M_TEMP);
2495d06c0e8Sthorpej 			return (error);
2505d06c0e8Sthorpej 		}
2515d06c0e8Sthorpej 
2525d06c0e8Sthorpej 		for (j = 0, len = 0; j < req->req_outbuf.dmbuf_iovcnt; j++) {
2535d06c0e8Sthorpej 			len += uio_out->uio_iov[j].iov_len;
2545d06c0e8Sthorpej 			if (len > SSIZE_MAX) {
2555d06c0e8Sthorpej 				free(uio_out->uio_iov, M_TEMP);
2564c1506f7Sthorpej 				return (EINVAL);
2575d06c0e8Sthorpej 			}
2585d06c0e8Sthorpej 		}
2595d06c0e8Sthorpej 
2605d06c0e8Sthorpej 		uio_out->uio_iovcnt = req->req_outbuf.dmbuf_iovcnt;
2615d06c0e8Sthorpej 		uio_out->uio_resid = len;
2625d06c0e8Sthorpej 		uio_out->uio_rw = UIO_READ;
263ec5a9318Syamt 		uio_out->uio_vmspace = dus->dus_vmspace;
264ec5a9318Syamt 
2655d06c0e8Sthorpej 		dreq->dreq_outbuf_type = DMOVER_BUF_UIO;
2665d06c0e8Sthorpej 		dreq->dreq_outbuf.dmbuf_uio = uio_out;
2675f35784bSthorpej 	} else {
2685f35784bSthorpej 		uio_out->uio_iov = NULL;
2695f35784bSthorpej 		uio_out = NULL;
2705f35784bSthorpej 		dreq->dreq_outbuf_type = DMOVER_BUF_NONE;
2715f35784bSthorpej 	}
2725d06c0e8Sthorpej 
2735d06c0e8Sthorpej 	memcpy(dreq->dreq_immediate, req->req_immediate,
2745d06c0e8Sthorpej 	    sizeof(dreq->dreq_immediate));
2755f35784bSthorpej 
2765f35784bSthorpej 	if (dses->dses_ninputs == 0) {
2775f35784bSthorpej 		/* No inputs; all done. */
2785d06c0e8Sthorpej 		return (0);
2795d06c0e8Sthorpej 	}
2805d06c0e8Sthorpej 
2815d06c0e8Sthorpej 	dreq->dreq_inbuf_type = DMOVER_BUF_UIO;
2825d06c0e8Sthorpej 
2835d06c0e8Sthorpej 	dus->dus_uio_in = malloc(sizeof(struct uio) * dses->dses_ninputs,
2845d06c0e8Sthorpej 	    M_TEMP, M_WAITOK);
2855d06c0e8Sthorpej 	memset(dus->dus_uio_in, 0, sizeof(struct uio) * dses->dses_ninputs);
2865d06c0e8Sthorpej 
2875d06c0e8Sthorpej 	for (i = 0; i < dses->dses_ninputs; i++) {
2885d06c0e8Sthorpej 		uio_in = &dus->dus_uio_in[i];
2895d06c0e8Sthorpej 
2905d06c0e8Sthorpej 		error = copyin(&req->req_inbuf[i], &inbuf, sizeof(inbuf));
2915d06c0e8Sthorpej 		if (error)
2925d06c0e8Sthorpej 			goto bad;
2935d06c0e8Sthorpej 
2945d06c0e8Sthorpej 		if (inbuf.dmbuf_iovcnt > IOV_MAX) {
2955d06c0e8Sthorpej 			error = EINVAL;
2965d06c0e8Sthorpej 			goto bad;
2975d06c0e8Sthorpej 		}
2985d06c0e8Sthorpej 		len = sizeof(struct iovec) * inbuf.dmbuf_iovcnt;
2995d06c0e8Sthorpej 		if (len == 0) {
3005d06c0e8Sthorpej 			error = EINVAL;
3015d06c0e8Sthorpej 			goto bad;
3025d06c0e8Sthorpej 		}
3035d06c0e8Sthorpej 		uio_in->uio_iov = malloc(len, M_TEMP, M_WAITOK);
3045d06c0e8Sthorpej 
3055d06c0e8Sthorpej 		error = copyin(inbuf.dmbuf_iov, uio_in->uio_iov, len);
3065d06c0e8Sthorpej 		if (error) {
3075d06c0e8Sthorpej 			free(uio_in->uio_iov, M_TEMP);
3085d06c0e8Sthorpej 			goto bad;
3095d06c0e8Sthorpej 		}
3105d06c0e8Sthorpej 
3115f35784bSthorpej 		for (j = 0, len = 0; j < inbuf.dmbuf_iovcnt; j++) {
3125d06c0e8Sthorpej 			len += uio_in->uio_iov[j].iov_len;
3135d06c0e8Sthorpej 			if (len > SSIZE_MAX) {
3145d06c0e8Sthorpej 				free(uio_in->uio_iov, M_TEMP);
3155d06c0e8Sthorpej 				error = EINVAL;
3165d06c0e8Sthorpej 				goto bad;
3175d06c0e8Sthorpej 			}
3185d06c0e8Sthorpej 		}
3195d06c0e8Sthorpej 
3205f35784bSthorpej 		if (uio_out != NULL && len != uio_out->uio_resid) {
3215d06c0e8Sthorpej 			free(uio_in->uio_iov, M_TEMP);
3225d06c0e8Sthorpej 			error = EINVAL;
3235d06c0e8Sthorpej 			goto bad;
3245d06c0e8Sthorpej 		}
3255d06c0e8Sthorpej 
3265d06c0e8Sthorpej 		uio_in->uio_iovcnt = inbuf.dmbuf_iovcnt;
3275d06c0e8Sthorpej 		uio_in->uio_resid = len;
3285d06c0e8Sthorpej 		uio_in->uio_rw = UIO_WRITE;
329ec5a9318Syamt 		uio_in->uio_vmspace = dus->dus_vmspace;
3305d06c0e8Sthorpej 
3315d06c0e8Sthorpej 		dreq->dreq_inbuf[i].dmbuf_uio = uio_in;
3325d06c0e8Sthorpej 	}
3335d06c0e8Sthorpej 
3345d06c0e8Sthorpej 	return (0);
3355d06c0e8Sthorpej 
3365d06c0e8Sthorpej  bad:
3375d06c0e8Sthorpej 	if (i > 0) {
3385d06c0e8Sthorpej 		for (--i; i >= 0; i--) {
3395d06c0e8Sthorpej 			uio_in = &dus->dus_uio_in[i];
3405d06c0e8Sthorpej 			free(uio_in->uio_iov, M_TEMP);
3415d06c0e8Sthorpej 		}
3425d06c0e8Sthorpej 	}
3435d06c0e8Sthorpej 	free(dus->dus_uio_in, M_TEMP);
3445f35784bSthorpej 	if (uio_out != NULL)
3455d06c0e8Sthorpej 		free(uio_out->uio_iov, M_TEMP);
346ec5a9318Syamt 	uvmspace_free(dus->dus_vmspace);
3475d06c0e8Sthorpej 	return (error);
3485d06c0e8Sthorpej }
3495d06c0e8Sthorpej 
3505d06c0e8Sthorpej /*
3515d06c0e8Sthorpej  * dmio_usrreq_fini:
3525d06c0e8Sthorpej  *
3535d06c0e8Sthorpej  *	Tear down a request.  Must be called at splsoftclock().
3545d06c0e8Sthorpej  */
3555d06c0e8Sthorpej static void
dmio_usrreq_fini(struct dmio_state * ds,struct dmio_usrreq_state * dus)3565d06c0e8Sthorpej dmio_usrreq_fini(struct dmio_state *ds, struct dmio_usrreq_state *dus)
3575d06c0e8Sthorpej {
3585d06c0e8Sthorpej 	struct dmover_session *dses = ds->ds_session;
3595d06c0e8Sthorpej 	struct uio *uio_out = &dus->dus_uio_out;
3605d06c0e8Sthorpej 	struct uio *uio_in;
3615d06c0e8Sthorpej 	int i;
3625d06c0e8Sthorpej 
3635f35784bSthorpej 	if (uio_out->uio_iov != NULL)
3645d06c0e8Sthorpej 		free(uio_out->uio_iov, M_TEMP);
3655d06c0e8Sthorpej 
366ec5a9318Syamt 	if (dses->dses_ninputs) {
3675d06c0e8Sthorpej 		for (i = 0; i < dses->dses_ninputs; i++) {
3685d06c0e8Sthorpej 			uio_in = &dus->dus_uio_in[i];
3695d06c0e8Sthorpej 			free(uio_in->uio_iov, M_TEMP);
3705d06c0e8Sthorpej 		}
3715d06c0e8Sthorpej 		free(dus->dus_uio_in, M_TEMP);
372ec5a9318Syamt 	}
3735d06c0e8Sthorpej 
37420bbb87eSrmind 	workqueue_enqueue(dmio_cleaner, &dus->dus_work, NULL);
375ec5a9318Syamt }
376ec5a9318Syamt 
377ec5a9318Syamt static void
dmio_usrreq_fini1(struct work * wk,void * dummy)378ec5a9318Syamt dmio_usrreq_fini1(struct work *wk, void *dummy)
379ec5a9318Syamt {
380ec5a9318Syamt 	struct dmio_usrreq_state *dus = (void *)wk;
381ec5a9318Syamt 
382ec5a9318Syamt 	KASSERT(wk == &dus->dus_work);
383ec5a9318Syamt 
384ec5a9318Syamt 	uvmspace_free(dus->dus_vmspace);
3855d06c0e8Sthorpej 	pool_put(&dmio_usrreq_state_pool, dus);
3865d06c0e8Sthorpej }
3875d06c0e8Sthorpej 
3885d06c0e8Sthorpej /*
3895d06c0e8Sthorpej  * dmio_read:
3905d06c0e8Sthorpej  *
3915d06c0e8Sthorpej  *	Read file op.
3925d06c0e8Sthorpej  */
3935d06c0e8Sthorpej static int
dmio_read(struct file * fp,off_t * offp,struct uio * uio,kauth_cred_t cred,int flags)3945d06c0e8Sthorpej dmio_read(struct file *fp, off_t *offp, struct uio *uio,
3952867b68bSelad     kauth_cred_t cred, int flags)
3965d06c0e8Sthorpej {
3975d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
3985d06c0e8Sthorpej 	struct dmio_usrreq_state *dus;
3995d06c0e8Sthorpej 	struct dmover_request *dreq;
4005d06c0e8Sthorpej 	struct dmio_usrresp resp;
4014ac4f6b4Sjakllsch 	int error = 0, progress = 0;
4025d06c0e8Sthorpej 
4035d06c0e8Sthorpej 	if ((uio->uio_resid % sizeof(resp)) != 0)
4045d06c0e8Sthorpej 		return (EINVAL);
4055d06c0e8Sthorpej 
4065d06c0e8Sthorpej 	if (ds->ds_session == NULL)
4075d06c0e8Sthorpej 		return (ENXIO);
4085d06c0e8Sthorpej 
409b859fbe7Schristos 	getnanotime(&ds->ds_atime);
4104ac4f6b4Sjakllsch 	mutex_enter(&ds->ds_lock);
4115d06c0e8Sthorpej 
4125d06c0e8Sthorpej 	while (uio->uio_resid != 0) {
4135d06c0e8Sthorpej 
4145d06c0e8Sthorpej 		for (;;) {
4155d06c0e8Sthorpej 			dus = TAILQ_FIRST(&ds->ds_complete);
4165d06c0e8Sthorpej 			if (dus == NULL) {
4175d06c0e8Sthorpej 				if (fp->f_flag & FNONBLOCK) {
4185d06c0e8Sthorpej 					error = progress ? 0 : EWOULDBLOCK;
4195d06c0e8Sthorpej 					goto out;
4205d06c0e8Sthorpej 				}
42158c736d4Sscw 				ds->ds_flags |= DMIO_STATE_READ_WAIT;
4224ac4f6b4Sjakllsch 				error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock);
4235d06c0e8Sthorpej 				if (error)
4245d06c0e8Sthorpej 					goto out;
4255d06c0e8Sthorpej 				continue;
4265d06c0e8Sthorpej 			}
4275d06c0e8Sthorpej 			/* Have a completed request. */
4285d06c0e8Sthorpej 			TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
4295d06c0e8Sthorpej 			ds->ds_nreqs--;
4305d06c0e8Sthorpej 			if (ds->ds_flags & DMIO_STATE_WRITE_WAIT) {
4315d06c0e8Sthorpej 				ds->ds_flags &= ~DMIO_STATE_WRITE_WAIT;
4324ac4f6b4Sjakllsch 				cv_broadcast(&ds->ds_nreqs_cv);
4335d06c0e8Sthorpej 			}
4345d06c0e8Sthorpej 			if (ds->ds_flags & DMIO_STATE_SEL) {
4355d06c0e8Sthorpej 				ds->ds_flags &= ~DMIO_STATE_SEL;
436c6186facSrmind 				selnotify(&ds->ds_selq, POLLIN | POLLRDNORM, 0);
4375d06c0e8Sthorpej 			}
4385d06c0e8Sthorpej 			break;
4395d06c0e8Sthorpej 		}
4405d06c0e8Sthorpej 
4415d06c0e8Sthorpej 		dreq = dus->dus_req;
4425d06c0e8Sthorpej 		resp.resp_id = dus->dus_id;
4435f35784bSthorpej 		if (dreq->dreq_flags & DMOVER_REQ_ERROR)
4445f35784bSthorpej 			resp.resp_error = dreq->dreq_error;
4455f35784bSthorpej 		else {
4465f35784bSthorpej 			resp.resp_error = 0;
4475f35784bSthorpej 			memcpy(resp.resp_immediate, dreq->dreq_immediate,
4485f35784bSthorpej 			    sizeof(resp.resp_immediate));
4495f35784bSthorpej 		}
4505d06c0e8Sthorpej 
4515d06c0e8Sthorpej 		dmio_usrreq_fini(ds, dus);
4525d06c0e8Sthorpej 
4534ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
4545d06c0e8Sthorpej 
4555d06c0e8Sthorpej 		progress = 1;
4565d06c0e8Sthorpej 
4575d06c0e8Sthorpej 		dmover_request_free(dreq);
4585d06c0e8Sthorpej 
4595d06c0e8Sthorpej 		error = uiomove(&resp, sizeof(resp), uio);
4605d06c0e8Sthorpej 		if (error)
4615d06c0e8Sthorpej 			return (error);
4625d06c0e8Sthorpej 
4634ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
4645d06c0e8Sthorpej 	}
4655d06c0e8Sthorpej 
4665d06c0e8Sthorpej  out:
4674ac4f6b4Sjakllsch 	mutex_exit(&ds->ds_lock);
4685d06c0e8Sthorpej 
4695d06c0e8Sthorpej 	return (error);
4705d06c0e8Sthorpej }
4715d06c0e8Sthorpej 
4725d06c0e8Sthorpej /*
4735d06c0e8Sthorpej  * dmio_usrreq_done:
4745d06c0e8Sthorpej  *
4755d06c0e8Sthorpej  *	Dmover completion callback.
4765d06c0e8Sthorpej  */
4775d06c0e8Sthorpej static void
dmio_usrreq_done(struct dmover_request * dreq)4785d06c0e8Sthorpej dmio_usrreq_done(struct dmover_request *dreq)
4795d06c0e8Sthorpej {
4805d06c0e8Sthorpej 	struct dmio_usrreq_state *dus = dreq->dreq_cookie;
4815d06c0e8Sthorpej 	struct dmio_state *ds = dreq->dreq_session->dses_cookie;
4825d06c0e8Sthorpej 
4835d06c0e8Sthorpej 	/* We're already at splsoftclock(). */
4845d06c0e8Sthorpej 
4854ac4f6b4Sjakllsch 	mutex_enter(&ds->ds_lock);
4865d06c0e8Sthorpej 	TAILQ_REMOVE(&ds->ds_pending, dus, dus_q);
4875d06c0e8Sthorpej 	if (ds->ds_flags & DMIO_STATE_DEAD) {
4884ac4f6b4Sjakllsch 		int nreqs = --ds->ds_nreqs;
4894ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
4905d06c0e8Sthorpej 		dmio_usrreq_fini(ds, dus);
4915d06c0e8Sthorpej 		dmover_request_free(dreq);
4924ac4f6b4Sjakllsch 		if (nreqs == 0) {
4934ac4f6b4Sjakllsch 			dmio_state_put(ds);
4944ac4f6b4Sjakllsch 		}
4955d06c0e8Sthorpej 		return;
4965d06c0e8Sthorpej 	}
4974ac4f6b4Sjakllsch 
4985d06c0e8Sthorpej 	TAILQ_INSERT_TAIL(&ds->ds_complete, dus, dus_q);
4995d06c0e8Sthorpej 	if (ds->ds_flags & DMIO_STATE_READ_WAIT) {
5005d06c0e8Sthorpej 		ds->ds_flags &= ~DMIO_STATE_READ_WAIT;
5014ac4f6b4Sjakllsch 		cv_broadcast(&ds->ds_complete_cv);
5025d06c0e8Sthorpej 	}
5035d06c0e8Sthorpej 	if (ds->ds_flags & DMIO_STATE_SEL) {
5045d06c0e8Sthorpej 		ds->ds_flags &= ~DMIO_STATE_SEL;
505c6186facSrmind 		selnotify(&ds->ds_selq, POLLOUT | POLLWRNORM, 0);
5065d06c0e8Sthorpej 	}
5074ac4f6b4Sjakllsch 	mutex_exit(&ds->ds_lock);
5085d06c0e8Sthorpej }
5095d06c0e8Sthorpej 
5105d06c0e8Sthorpej /*
5115d06c0e8Sthorpej  * dmio_write:
5125d06c0e8Sthorpej  *
5135d06c0e8Sthorpej  *	Write file op.
5145d06c0e8Sthorpej  */
5155d06c0e8Sthorpej static int
dmio_write(struct file * fp,off_t * offp,struct uio * uio,kauth_cred_t cred,int flags)5165d06c0e8Sthorpej dmio_write(struct file *fp, off_t *offp, struct uio *uio,
5172867b68bSelad     kauth_cred_t cred, int flags)
5185d06c0e8Sthorpej {
5195d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
5205d06c0e8Sthorpej 	struct dmio_usrreq_state *dus;
5215d06c0e8Sthorpej 	struct dmover_request *dreq;
5225d06c0e8Sthorpej 	struct dmio_usrreq req;
5234ac4f6b4Sjakllsch 	int error = 0, progress = 0;
5245d06c0e8Sthorpej 
5255d06c0e8Sthorpej 	if ((uio->uio_resid % sizeof(req)) != 0)
5265d06c0e8Sthorpej 		return (EINVAL);
5275d06c0e8Sthorpej 
5285d06c0e8Sthorpej 	if (ds->ds_session == NULL)
5295d06c0e8Sthorpej 		return (ENXIO);
5305d06c0e8Sthorpej 
531b859fbe7Schristos 	getnanotime(&ds->ds_mtime);
5324ac4f6b4Sjakllsch 	mutex_enter(&ds->ds_lock);
5335d06c0e8Sthorpej 
5345d06c0e8Sthorpej 	while (uio->uio_resid != 0) {
5355d06c0e8Sthorpej 
5365d06c0e8Sthorpej 		if (ds->ds_nreqs == DMIO_NREQS_MAX) {
5375d06c0e8Sthorpej 			if (fp->f_flag & FNONBLOCK) {
5385d06c0e8Sthorpej 				error = progress ? 0 : EWOULDBLOCK;
5395d06c0e8Sthorpej 				break;
5405d06c0e8Sthorpej 			}
5415d06c0e8Sthorpej 			ds->ds_flags |= DMIO_STATE_WRITE_WAIT;
5424ac4f6b4Sjakllsch 			error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock);
5435d06c0e8Sthorpej 			if (error)
5445d06c0e8Sthorpej 				break;
5455d06c0e8Sthorpej 			continue;
5465d06c0e8Sthorpej 		}
5475d06c0e8Sthorpej 
5485d06c0e8Sthorpej 		ds->ds_nreqs++;
5495d06c0e8Sthorpej 
5504ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
5515d06c0e8Sthorpej 
5525d06c0e8Sthorpej 		progress = 1;
5535d06c0e8Sthorpej 
5545d06c0e8Sthorpej 		error = uiomove(&req, sizeof(req), uio);
5555d06c0e8Sthorpej 		if (error) {
5564ac4f6b4Sjakllsch 			mutex_enter(&ds->ds_lock);
5575d06c0e8Sthorpej 			ds->ds_nreqs--;
5585d06c0e8Sthorpej 			break;
5595d06c0e8Sthorpej 		}
5605d06c0e8Sthorpej 
5615d06c0e8Sthorpej 		/* XXX How should this interact with FNONBLOCK? */
5625d06c0e8Sthorpej 		dreq = dmover_request_alloc(ds->ds_session, NULL);
5635d06c0e8Sthorpej 		if (dreq == NULL) {
5645d06c0e8Sthorpej 			/* XXX */
5655d06c0e8Sthorpej 			ds->ds_nreqs--;
5665d06c0e8Sthorpej 			error = ENOMEM;
5674ac4f6b4Sjakllsch 			return error;
5685d06c0e8Sthorpej 		}
5695d06c0e8Sthorpej 		dus = pool_get(&dmio_usrreq_state_pool, PR_WAITOK);
5705d06c0e8Sthorpej 
5715d06c0e8Sthorpej 		error = dmio_usrreq_init(fp, dus, &req, dreq);
5725d06c0e8Sthorpej 		if (error) {
5735d06c0e8Sthorpej 			dmover_request_free(dreq);
5745d06c0e8Sthorpej 			pool_put(&dmio_usrreq_state_pool, dus);
5754ac4f6b4Sjakllsch 			return error;
5765d06c0e8Sthorpej 		}
5775d06c0e8Sthorpej 
5785d06c0e8Sthorpej 		dreq->dreq_callback = dmio_usrreq_done;
5795d06c0e8Sthorpej 		dreq->dreq_cookie = dus;
5805d06c0e8Sthorpej 
5815d06c0e8Sthorpej 		dus->dus_req = dreq;
5825d06c0e8Sthorpej 		dus->dus_id = req.req_id;
5835d06c0e8Sthorpej 
5844ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
5855d06c0e8Sthorpej 
5865d06c0e8Sthorpej 		TAILQ_INSERT_TAIL(&ds->ds_pending, dus, dus_q);
5875d06c0e8Sthorpej 
5884ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
5895d06c0e8Sthorpej 
5905d06c0e8Sthorpej 		dmover_process(dreq);
5915d06c0e8Sthorpej 
5924ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
5935d06c0e8Sthorpej 	}
5945d06c0e8Sthorpej 
5954ac4f6b4Sjakllsch 	mutex_exit(&ds->ds_lock);
5965d06c0e8Sthorpej 
5975d06c0e8Sthorpej 	return (error);
5985d06c0e8Sthorpej }
5995d06c0e8Sthorpej 
600b859fbe7Schristos static int
dmio_stat(struct file * fp,struct stat * st)601b859fbe7Schristos dmio_stat(struct file *fp, struct stat *st)
602b859fbe7Schristos {
603b859fbe7Schristos 	struct dmio_state *ds = fp->f_data;
604b859fbe7Schristos 
6057299f27dSmsaitoh 	(void)memset(st, 0, sizeof(*st));
606b859fbe7Schristos 	KERNEL_LOCK(1, NULL);
607b859fbe7Schristos 	st->st_dev = makedev(cdevsw_lookup_major(&dmoverio_cdevsw), 0);
608a6a538b3Snonaka 	st->st_atimespec = ds->ds_atime;
609a6a538b3Snonaka 	st->st_mtimespec = ds->ds_mtime;
610a6a538b3Snonaka 	st->st_ctimespec = st->st_birthtimespec = ds->ds_btime;
61186ba58fdSchristos 	st->st_uid = kauth_cred_geteuid(fp->f_cred);
61286ba58fdSchristos 	st->st_gid = kauth_cred_getegid(fp->f_cred);
613a6a538b3Snonaka 	KERNEL_UNLOCK_ONE(NULL);
614b859fbe7Schristos 	return 0;
615b859fbe7Schristos }
616b859fbe7Schristos 
6175d06c0e8Sthorpej /*
6185d06c0e8Sthorpej  * dmio_ioctl:
6195d06c0e8Sthorpej  *
6205d06c0e8Sthorpej  *	Ioctl file op.
6215d06c0e8Sthorpej  */
6225d06c0e8Sthorpej static int
dmio_ioctl(struct file * fp,u_long cmd,void * data)623c496ce1aSad dmio_ioctl(struct file *fp, u_long cmd, void *data)
6245d06c0e8Sthorpej {
6255d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
6264ac4f6b4Sjakllsch 	int error;
6275d06c0e8Sthorpej 
6285d06c0e8Sthorpej 	switch (cmd) {
6295d06c0e8Sthorpej 	case FIONBIO:
6305d06c0e8Sthorpej 	case FIOASYNC:
6315d06c0e8Sthorpej 		return (0);
6325d06c0e8Sthorpej 
6335d06c0e8Sthorpej 	case DMIO_SETFUNC:
6345d06c0e8Sthorpej 	    {
63560418b39Sdsl 		struct dmio_setfunc *dsf = data;
6365d06c0e8Sthorpej 		struct dmover_session *dses;
6375d06c0e8Sthorpej 
6384ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
6395d06c0e8Sthorpej 
6405d06c0e8Sthorpej 		if (ds->ds_session != NULL ||
6415d06c0e8Sthorpej 		    (ds->ds_flags & DMIO_STATE_LARVAL) != 0) {
6424ac4f6b4Sjakllsch 			mutex_exit(&ds->ds_lock);
6435d06c0e8Sthorpej 			return (EBUSY);
6445d06c0e8Sthorpej 		}
6455d06c0e8Sthorpej 
6465d06c0e8Sthorpej 		ds->ds_flags |= DMIO_STATE_LARVAL;
6475d06c0e8Sthorpej 
6484ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
6495d06c0e8Sthorpej 
6505d06c0e8Sthorpej 		dsf->dsf_name[DMIO_MAX_FUNCNAME - 1] = '\0';
6515d06c0e8Sthorpej 		error = dmover_session_create(dsf->dsf_name, &dses);
6525d06c0e8Sthorpej 
6534ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
6545d06c0e8Sthorpej 
6555d06c0e8Sthorpej 		if (error == 0) {
6565d06c0e8Sthorpej 			dses->dses_cookie = ds;
6575d06c0e8Sthorpej 			ds->ds_session = dses;
6585d06c0e8Sthorpej 		}
6595d06c0e8Sthorpej 		ds->ds_flags &= ~DMIO_STATE_LARVAL;
6605d06c0e8Sthorpej 
6614ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
6625d06c0e8Sthorpej 		break;
6635d06c0e8Sthorpej 	    }
6645d06c0e8Sthorpej 
6655d06c0e8Sthorpej 	default:
6665d06c0e8Sthorpej 		error = ENOTTY;
6675d06c0e8Sthorpej 	}
6685d06c0e8Sthorpej 
6695d06c0e8Sthorpej 	return (error);
6705d06c0e8Sthorpej }
6715d06c0e8Sthorpej 
6725d06c0e8Sthorpej /*
6735d06c0e8Sthorpej  * dmio_poll:
6745d06c0e8Sthorpej  *
6755d06c0e8Sthorpej  *	Poll file op.
6765d06c0e8Sthorpej  */
6775d06c0e8Sthorpej static int
dmio_poll(struct file * fp,int events)678c496ce1aSad dmio_poll(struct file *fp, int events)
6795d06c0e8Sthorpej {
6805d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
6814ac4f6b4Sjakllsch 	int revents = 0;
6825d06c0e8Sthorpej 
6835d06c0e8Sthorpej 	if ((events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) == 0)
6845d06c0e8Sthorpej 		return (revents);
6855d06c0e8Sthorpej 
6864ac4f6b4Sjakllsch 	mutex_enter(&ds->ds_lock);
6875d06c0e8Sthorpej 
6885d06c0e8Sthorpej 	if (ds->ds_flags & DMIO_STATE_DEAD) {
6895d06c0e8Sthorpej 		/* EOF */
6905d06c0e8Sthorpej 		revents |= events & (POLLIN | POLLRDNORM |
6915d06c0e8Sthorpej 		    POLLOUT | POLLWRNORM);
6925d06c0e8Sthorpej 		goto out;
6935d06c0e8Sthorpej 	}
6945d06c0e8Sthorpej 
6955d06c0e8Sthorpej 	/* We can read if there are completed requests. */
6965d06c0e8Sthorpej 	if (events & (POLLIN | POLLRDNORM))
6975d06c0e8Sthorpej 		if (TAILQ_EMPTY(&ds->ds_complete) == 0)
6985d06c0e8Sthorpej 			revents |= events & (POLLIN | POLLRDNORM);
6995d06c0e8Sthorpej 
7005d06c0e8Sthorpej 	/*
7015d06c0e8Sthorpej 	 * We can write if there is there are fewer then DMIO_NREQS_MAX
7025d06c0e8Sthorpej 	 * are already in the queue.
7035d06c0e8Sthorpej 	 */
7045d06c0e8Sthorpej 	if (events & (POLLOUT | POLLWRNORM))
7055d06c0e8Sthorpej 		if (ds->ds_nreqs < DMIO_NREQS_MAX)
7065d06c0e8Sthorpej 			revents |= events & (POLLOUT | POLLWRNORM);
7075d06c0e8Sthorpej 
7085d06c0e8Sthorpej 	if (revents == 0) {
709c496ce1aSad 		selrecord(curlwp, &ds->ds_selq);
7105d06c0e8Sthorpej 		ds->ds_flags |= DMIO_STATE_SEL;
7115d06c0e8Sthorpej 	}
7125d06c0e8Sthorpej 
7135d06c0e8Sthorpej  out:
7144ac4f6b4Sjakllsch 	mutex_exit(&ds->ds_lock);
7155d06c0e8Sthorpej 
7165d06c0e8Sthorpej 	return (revents);
7175d06c0e8Sthorpej }
7185d06c0e8Sthorpej 
7195d06c0e8Sthorpej /*
7205d06c0e8Sthorpej  * dmio_close:
7215d06c0e8Sthorpej  *
7225d06c0e8Sthorpej  *	Close file op.
7235d06c0e8Sthorpej  */
7245d06c0e8Sthorpej static int
dmio_close(struct file * fp)725c496ce1aSad dmio_close(struct file *fp)
7265d06c0e8Sthorpej {
7275d06c0e8Sthorpej 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
7285d06c0e8Sthorpej 	struct dmio_usrreq_state *dus;
7295d06c0e8Sthorpej 	struct dmover_session *dses;
7305d06c0e8Sthorpej 
7314ac4f6b4Sjakllsch 	mutex_enter(&ds->ds_lock);
7325d06c0e8Sthorpej 
7335d06c0e8Sthorpej 	ds->ds_flags |= DMIO_STATE_DEAD;
7345d06c0e8Sthorpej 
7355d06c0e8Sthorpej 	/* Garbage-collect all the responses on the queue. */
7365d06c0e8Sthorpej 	while ((dus = TAILQ_FIRST(&ds->ds_complete)) != NULL) {
7375d06c0e8Sthorpej 		TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
7385d06c0e8Sthorpej 		ds->ds_nreqs--;
7394ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
7405d06c0e8Sthorpej 		dmover_request_free(dus->dus_req);
7415d06c0e8Sthorpej 		dmio_usrreq_fini(ds, dus);
7424ac4f6b4Sjakllsch 		mutex_enter(&ds->ds_lock);
7435d06c0e8Sthorpej 	}
7445d06c0e8Sthorpej 
7455d06c0e8Sthorpej 	/*
7465d06c0e8Sthorpej 	 * If there are any requests pending, we have to wait for
7475d06c0e8Sthorpej 	 * them.  Don't free the dmio_state in this case.
7485d06c0e8Sthorpej 	 */
7495d06c0e8Sthorpej 	if (ds->ds_nreqs == 0) {
7505d06c0e8Sthorpej 		dses = ds->ds_session;
7514ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
7524ac4f6b4Sjakllsch 		dmio_state_put(ds);
7535d06c0e8Sthorpej 	} else {
7545d06c0e8Sthorpej 		dses = NULL;
7554ac4f6b4Sjakllsch 		mutex_exit(&ds->ds_lock);
7565d06c0e8Sthorpej 	}
7575d06c0e8Sthorpej 
7585d06c0e8Sthorpej 	fp->f_data = NULL;
7595d06c0e8Sthorpej 
7605d06c0e8Sthorpej 	if (dses != NULL)
7615d06c0e8Sthorpej 		dmover_session_destroy(dses);
7625d06c0e8Sthorpej 
7635d06c0e8Sthorpej 	return (0);
7645d06c0e8Sthorpej }
7655d06c0e8Sthorpej 
76631c81b28Schristos static const struct fileops dmio_fileops = {
767ea05286dSchristos 	.fo_name = "dmio",
768c6367674Sad 	.fo_read = dmio_read,
769c6367674Sad 	.fo_write = dmio_write,
770c6367674Sad 	.fo_ioctl = dmio_ioctl,
771c6367674Sad 	.fo_fcntl = fnullop_fcntl,
772c6367674Sad 	.fo_poll = dmio_poll,
773b859fbe7Schristos 	.fo_stat = dmio_stat,
774c6367674Sad 	.fo_close = dmio_close,
775c6367674Sad 	.fo_kqfilter = fnullop_kqfilter,
7762a54322cSdsl 	.fo_restart = fnullop_restart,
7775d06c0e8Sthorpej };
7785d06c0e8Sthorpej 
7795d06c0e8Sthorpej /*
7805d06c0e8Sthorpej  * dmoverioopen:
7815d06c0e8Sthorpej  *
7825d06c0e8Sthorpej  *	Device switch open routine.
7835d06c0e8Sthorpej  */
7845d06c0e8Sthorpej int
dmoverioopen(dev_t dev,int flag,int mode,struct lwp * l)78595e1ffb1Schristos dmoverioopen(dev_t dev, int flag, int mode, struct lwp *l)
7865d06c0e8Sthorpej {
7875d06c0e8Sthorpej 	struct dmio_state *ds;
7885d06c0e8Sthorpej 	struct file *fp;
7894ac4f6b4Sjakllsch 	int error, fd;
7905d06c0e8Sthorpej 
791c496ce1aSad 	if ((error = fd_allocfile(&fp, &fd)) != 0)
7925d06c0e8Sthorpej 		return (error);
7935d06c0e8Sthorpej 
7944ac4f6b4Sjakllsch 	ds = dmio_state_get();
7955d06c0e8Sthorpej 
796c496ce1aSad 	return fd_clone(fp, fd, flag, &dmio_fileops, ds);
7975d06c0e8Sthorpej }
798