Home | History | Annotate | Line # | Download | only in dmover
dmover_io.c revision 1.40.18.1
      1 /*	$NetBSD: dmover_io.c,v 1.40.18.1 2013/08/28 23:59:25 rmind Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2002, 2003 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *	This product includes software developed for the NetBSD Project by
     20  *	Wasabi Systems, Inc.
     21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 
     38 /*
     39  * dmover_io.c: Support for user-space access to dmover-api
     40  *
     41  * This interface is quite simple:
     42  *
     43  *	1.  The user opens /dev/dmover, which is a cloning device.  This
     44  *	    allocates internal state for the session.
     45  *
     46  *	2.  The user does a DMIO_SETFUNC to select the data movement
     47  *	    function.  This actually creates the dmover session.
     48  *
     49  *	3.  The user writes request messages to its dmover handle.
     50  *
     51  *	4.  The user reads request responses from its dmover handle.
     52  *
     53  *	5.  The user closes the file descriptor and the session is
     54  *	    torn down.
     55  */
     56 
     57 #include <sys/cdefs.h>
     58 __KERNEL_RCSID(0, "$NetBSD: dmover_io.c,v 1.40.18.1 2013/08/28 23:59:25 rmind Exp $");
     59 
     60 #include <sys/param.h>
     61 #include <sys/queue.h>
     62 #include <sys/conf.h>
     63 #include <sys/pool.h>
     64 #include <sys/proc.h>
     65 #include <sys/poll.h>
     66 #include <sys/malloc.h>
     67 #include <sys/file.h>
     68 #include <sys/filedesc.h>
     69 #include <sys/filio.h>
     70 #include <sys/select.h>
     71 #include <sys/systm.h>
     72 #include <sys/workqueue.h>
     73 #include <sys/once.h>
     74 #include <sys/stat.h>
     75 #include <sys/kauth.h>
     76 #include <sys/mutex.h>
     77 #include <sys/condvar.h>
     78 
     79 #include <uvm/uvm_extern.h>
     80 
     81 #include <dev/dmover/dmovervar.h>
     82 #include <dev/dmover/dmover_io.h>
     83 
     84 struct dmio_usrreq_state {
     85 	union {
     86 		struct work u_work;
     87 		TAILQ_ENTRY(dmio_usrreq_state) u_q;
     88 	} dus_u;
     89 #define	dus_q		dus_u.u_q
     90 #define	dus_work	dus_u.u_work
     91 	struct uio dus_uio_out;
     92 	struct uio *dus_uio_in;
     93 	struct dmover_request *dus_req;
     94 	uint32_t dus_id;
     95 	struct vmspace *dus_vmspace;
     96 };
     97 
     98 struct dmio_state {
     99 	struct dmover_session *ds_session;
    100 	TAILQ_HEAD(, dmio_usrreq_state) ds_pending;
    101 	TAILQ_HEAD(, dmio_usrreq_state) ds_complete;
    102 	struct selinfo ds_selq;
    103 	volatile int ds_flags;
    104 	u_int ds_nreqs;
    105 	kmutex_t ds_lock;
    106 	kcondvar_t ds_complete_cv;
    107 	kcondvar_t ds_nreqs_cv;
    108 	struct timespec ds_atime;
    109 	struct timespec ds_mtime;
    110 	struct timespec ds_btime;
    111 };
    112 
    113 static ONCE_DECL(dmio_cleaner_control);
    114 static struct workqueue *dmio_cleaner;
    115 static int dmio_cleaner_init(void);
    116 static struct dmio_state *dmio_state_get(void);
    117 static void dmio_state_put(struct dmio_state *);
    118 static void dmio_usrreq_fini1(struct work *wk, void *);
    119 
    120 #define	DMIO_STATE_SEL		0x0001
    121 #define	DMIO_STATE_DEAD		0x0002
    122 #define	DMIO_STATE_LARVAL	0x0004
    123 #define	DMIO_STATE_READ_WAIT	0x0008
    124 #define	DMIO_STATE_WRITE_WAIT	0x0010
    125 
    126 #define	DMIO_NREQS_MAX		64	/* XXX pulled out of a hat */
    127 
    128 struct pool dmio_state_pool;
    129 struct pool dmio_usrreq_state_pool;
    130 
    131 void	dmoverioattach(int);
    132 
    133 dev_type_open(dmoverioopen);
    134 
    135 const struct cdevsw dmoverio_cdevsw = {
    136 	dmoverioopen, noclose, noread, nowrite, noioctl,
    137 	nostop, notty, nopoll, nommap, nokqfilter,
    138 	D_OTHER
    139 };
    140 
    141 /*
    142  * dmoverioattach:
    143  *
    144  *	Pseudo-device attach routine.
    145  */
    146 void
    147 dmoverioattach(int count)
    148 {
    149 
    150 	pool_init(&dmio_state_pool, sizeof(struct dmio_state),
    151 	    0, 0, 0, "dmiostate", NULL, IPL_SOFTCLOCK);
    152 	pool_init(&dmio_usrreq_state_pool, sizeof(struct dmio_usrreq_state),
    153 	    0, 0, 0, "dmiourstate", NULL, IPL_SOFTCLOCK);
    154 }
    155 
    156 /*
    157  * dmio_cleaner_init:
    158  *
    159  *	Create cleaner thread.
    160  */
    161 static int
    162 dmio_cleaner_init(void)
    163 {
    164 
    165 	return workqueue_create(&dmio_cleaner, "dmioclean", dmio_usrreq_fini1,
    166 	    NULL, PWAIT, IPL_SOFTCLOCK, 0);
    167 }
    168 
    169 static struct dmio_state *
    170 dmio_state_get(void)
    171 {
    172 	struct dmio_state *ds;
    173 
    174 	ds = pool_get(&dmio_state_pool, PR_WAITOK);
    175 
    176 	memset(ds, 0, sizeof(*ds));
    177 
    178 	getnanotime(&ds->ds_btime);
    179 	ds->ds_atime = ds->ds_mtime = ds->ds_btime;
    180 
    181 	mutex_init(&ds->ds_lock, MUTEX_DEFAULT, IPL_SOFTCLOCK);
    182 	cv_init(&ds->ds_complete_cv, "dmvrrd");
    183 	cv_init(&ds->ds_nreqs_cv, "dmiowr");
    184 	TAILQ_INIT(&ds->ds_pending);
    185 	TAILQ_INIT(&ds->ds_complete);
    186 	selinit(&ds->ds_selq);
    187 
    188 	return ds;
    189 }
    190 
    191 static void
    192 dmio_state_put(struct dmio_state *ds)
    193 {
    194 
    195 	seldestroy(&ds->ds_selq);
    196 	cv_destroy(&ds->ds_nreqs_cv);
    197 	cv_destroy(&ds->ds_complete_cv);
    198 	mutex_destroy(&ds->ds_lock);
    199 
    200 	pool_put(&dmio_state_pool, ds);
    201 }
    202 
    203 /*
    204  * dmio_usrreq_init:
    205  *
    206  *	Build a request structure.
    207  */
    208 static int
    209 dmio_usrreq_init(struct file *fp, struct dmio_usrreq_state *dus,
    210     struct dmio_usrreq *req, struct dmover_request *dreq)
    211 {
    212 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    213 	struct dmover_session *dses = ds->ds_session;
    214 	struct uio *uio_out = &dus->dus_uio_out;
    215 	struct uio *uio_in;
    216 	dmio_buffer inbuf;
    217 	size_t len;
    218 	int i, error;
    219 	u_int j;
    220 
    221 	/* XXX How should malloc interact w/ FNONBLOCK? */
    222 
    223 	error = RUN_ONCE(&dmio_cleaner_control, dmio_cleaner_init);
    224 	if (error) {
    225 		return error;
    226 	}
    227 
    228 	error = proc_vmspace_getref(curproc, &dus->dus_vmspace);
    229 	if (error) {
    230 		return error;
    231 	}
    232 
    233 	if (req->req_outbuf.dmbuf_iovcnt != 0) {
    234 		if (req->req_outbuf.dmbuf_iovcnt > IOV_MAX)
    235 			return (EINVAL);
    236 		len = sizeof(struct iovec) * req->req_outbuf.dmbuf_iovcnt;
    237 		uio_out->uio_iov = malloc(len, M_TEMP, M_WAITOK);
    238 		error = copyin(req->req_outbuf.dmbuf_iov, uio_out->uio_iov,
    239 		    len);
    240 		if (error) {
    241 			free(uio_out->uio_iov, M_TEMP);
    242 			return (error);
    243 		}
    244 
    245 		for (j = 0, len = 0; j < req->req_outbuf.dmbuf_iovcnt; j++) {
    246 			len += uio_out->uio_iov[j].iov_len;
    247 			if (len > SSIZE_MAX) {
    248 				free(uio_out->uio_iov, M_TEMP);
    249 				return (EINVAL);
    250 			}
    251 		}
    252 
    253 		uio_out->uio_iovcnt = req->req_outbuf.dmbuf_iovcnt;
    254 		uio_out->uio_resid = len;
    255 		uio_out->uio_rw = UIO_READ;
    256 		uio_out->uio_vmspace = dus->dus_vmspace;
    257 
    258 		dreq->dreq_outbuf_type = DMOVER_BUF_UIO;
    259 		dreq->dreq_outbuf.dmbuf_uio = uio_out;
    260 	} else {
    261 		uio_out->uio_iov = NULL;
    262 		uio_out = NULL;
    263 		dreq->dreq_outbuf_type = DMOVER_BUF_NONE;
    264 	}
    265 
    266 	memcpy(dreq->dreq_immediate, req->req_immediate,
    267 	    sizeof(dreq->dreq_immediate));
    268 
    269 	if (dses->dses_ninputs == 0) {
    270 		/* No inputs; all done. */
    271 		return (0);
    272 	}
    273 
    274 	dreq->dreq_inbuf_type = DMOVER_BUF_UIO;
    275 
    276 	dus->dus_uio_in = malloc(sizeof(struct uio) * dses->dses_ninputs,
    277 	    M_TEMP, M_WAITOK);
    278 	memset(dus->dus_uio_in, 0, sizeof(struct uio) * dses->dses_ninputs);
    279 
    280 	for (i = 0; i < dses->dses_ninputs; i++) {
    281 		uio_in = &dus->dus_uio_in[i];
    282 
    283 		error = copyin(&req->req_inbuf[i], &inbuf, sizeof(inbuf));
    284 		if (error)
    285 			goto bad;
    286 
    287 		if (inbuf.dmbuf_iovcnt > IOV_MAX) {
    288 			error = EINVAL;
    289 			goto bad;
    290 		}
    291 		len = sizeof(struct iovec) * inbuf.dmbuf_iovcnt;
    292 		if (len == 0) {
    293 			error = EINVAL;
    294 			goto bad;
    295 		}
    296 		uio_in->uio_iov = malloc(len, M_TEMP, M_WAITOK);
    297 
    298 		error = copyin(inbuf.dmbuf_iov, uio_in->uio_iov, len);
    299 		if (error) {
    300 			free(uio_in->uio_iov, M_TEMP);
    301 			goto bad;
    302 		}
    303 
    304 		for (j = 0, len = 0; j < inbuf.dmbuf_iovcnt; j++) {
    305 			len += uio_in->uio_iov[j].iov_len;
    306 			if (len > SSIZE_MAX) {
    307 				free(uio_in->uio_iov, M_TEMP);
    308 				error = EINVAL;
    309 				goto bad;
    310 			}
    311 		}
    312 
    313 		if (uio_out != NULL && len != uio_out->uio_resid) {
    314 			free(uio_in->uio_iov, M_TEMP);
    315 			error = EINVAL;
    316 			goto bad;
    317 		}
    318 
    319 		uio_in->uio_iovcnt = inbuf.dmbuf_iovcnt;
    320 		uio_in->uio_resid = len;
    321 		uio_in->uio_rw = UIO_WRITE;
    322 		uio_in->uio_vmspace = dus->dus_vmspace;
    323 
    324 		dreq->dreq_inbuf[i].dmbuf_uio = uio_in;
    325 	}
    326 
    327 	return (0);
    328 
    329  bad:
    330 	if (i > 0) {
    331 		for (--i; i >= 0; i--) {
    332 			uio_in = &dus->dus_uio_in[i];
    333 			free(uio_in->uio_iov, M_TEMP);
    334 		}
    335 	}
    336 	free(dus->dus_uio_in, M_TEMP);
    337 	if (uio_out != NULL)
    338 		free(uio_out->uio_iov, M_TEMP);
    339 	uvmspace_free(dus->dus_vmspace);
    340 	return (error);
    341 }
    342 
    343 /*
    344  * dmio_usrreq_fini:
    345  *
    346  *	Tear down a request.  Must be called at splsoftclock().
    347  */
    348 static void
    349 dmio_usrreq_fini(struct dmio_state *ds, struct dmio_usrreq_state *dus)
    350 {
    351 	struct dmover_session *dses = ds->ds_session;
    352 	struct uio *uio_out = &dus->dus_uio_out;
    353 	struct uio *uio_in;
    354 	int i;
    355 
    356 	if (uio_out->uio_iov != NULL)
    357 		free(uio_out->uio_iov, M_TEMP);
    358 
    359 	if (dses->dses_ninputs) {
    360 		for (i = 0; i < dses->dses_ninputs; i++) {
    361 			uio_in = &dus->dus_uio_in[i];
    362 			free(uio_in->uio_iov, M_TEMP);
    363 		}
    364 		free(dus->dus_uio_in, M_TEMP);
    365 	}
    366 
    367 	workqueue_enqueue(dmio_cleaner, &dus->dus_work, NULL);
    368 }
    369 
    370 static void
    371 dmio_usrreq_fini1(struct work *wk, void *dummy)
    372 {
    373 	struct dmio_usrreq_state *dus = (void *)wk;
    374 
    375 	KASSERT(wk == &dus->dus_work);
    376 
    377 	uvmspace_free(dus->dus_vmspace);
    378 	pool_put(&dmio_usrreq_state_pool, dus);
    379 }
    380 
    381 /*
    382  * dmio_read:
    383  *
    384  *	Read file op.
    385  */
    386 static int
    387 dmio_read(struct file *fp, off_t *offp, struct uio *uio,
    388     kauth_cred_t cred, int flags)
    389 {
    390 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    391 	struct dmio_usrreq_state *dus;
    392 	struct dmover_request *dreq;
    393 	struct dmio_usrresp resp;
    394 	int error = 0, progress = 0;
    395 
    396 	if ((uio->uio_resid % sizeof(resp)) != 0)
    397 		return (EINVAL);
    398 
    399 	if (ds->ds_session == NULL)
    400 		return (ENXIO);
    401 
    402 	getnanotime(&ds->ds_atime);
    403 	mutex_enter(&ds->ds_lock);
    404 
    405 	while (uio->uio_resid != 0) {
    406 
    407 		for (;;) {
    408 			dus = TAILQ_FIRST(&ds->ds_complete);
    409 			if (dus == NULL) {
    410 				if (fp->f_flag & FNONBLOCK) {
    411 					error = progress ? 0 : EWOULDBLOCK;
    412 					goto out;
    413 				}
    414 				ds->ds_flags |= DMIO_STATE_READ_WAIT;
    415 				error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock);
    416 				if (error)
    417 					goto out;
    418 				continue;
    419 			}
    420 			/* Have a completed request. */
    421 			TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
    422 			ds->ds_nreqs--;
    423 			if (ds->ds_flags & DMIO_STATE_WRITE_WAIT) {
    424 				ds->ds_flags &= ~DMIO_STATE_WRITE_WAIT;
    425 				cv_broadcast(&ds->ds_nreqs_cv);
    426 			}
    427 			if (ds->ds_flags & DMIO_STATE_SEL) {
    428 				ds->ds_flags &= ~DMIO_STATE_SEL;
    429 				selnotify(&ds->ds_selq, POLLIN | POLLRDNORM, 0);
    430 			}
    431 			break;
    432 		}
    433 
    434 		dreq = dus->dus_req;
    435 		resp.resp_id = dus->dus_id;
    436 		if (dreq->dreq_flags & DMOVER_REQ_ERROR)
    437 			resp.resp_error = dreq->dreq_error;
    438 		else {
    439 			resp.resp_error = 0;
    440 			memcpy(resp.resp_immediate, dreq->dreq_immediate,
    441 			    sizeof(resp.resp_immediate));
    442 		}
    443 
    444 		dmio_usrreq_fini(ds, dus);
    445 
    446 		mutex_exit(&ds->ds_lock);
    447 
    448 		progress = 1;
    449 
    450 		dmover_request_free(dreq);
    451 
    452 		error = uiomove(&resp, sizeof(resp), uio);
    453 		if (error)
    454 			return (error);
    455 
    456 		mutex_enter(&ds->ds_lock);
    457 	}
    458 
    459  out:
    460 	mutex_exit(&ds->ds_lock);
    461 
    462 	return (error);
    463 }
    464 
    465 /*
    466  * dmio_usrreq_done:
    467  *
    468  *	Dmover completion callback.
    469  */
    470 static void
    471 dmio_usrreq_done(struct dmover_request *dreq)
    472 {
    473 	struct dmio_usrreq_state *dus = dreq->dreq_cookie;
    474 	struct dmio_state *ds = dreq->dreq_session->dses_cookie;
    475 
    476 	/* We're already at splsoftclock(). */
    477 
    478 	mutex_enter(&ds->ds_lock);
    479 	TAILQ_REMOVE(&ds->ds_pending, dus, dus_q);
    480 	if (ds->ds_flags & DMIO_STATE_DEAD) {
    481 		int nreqs = --ds->ds_nreqs;
    482 		mutex_exit(&ds->ds_lock);
    483 		dmio_usrreq_fini(ds, dus);
    484 		dmover_request_free(dreq);
    485 		if (nreqs == 0) {
    486 			dmio_state_put(ds);
    487 		}
    488 		return;
    489 	}
    490 
    491 	TAILQ_INSERT_TAIL(&ds->ds_complete, dus, dus_q);
    492 	if (ds->ds_flags & DMIO_STATE_READ_WAIT) {
    493 		ds->ds_flags &= ~DMIO_STATE_READ_WAIT;
    494 		cv_broadcast(&ds->ds_complete_cv);
    495 	}
    496 	if (ds->ds_flags & DMIO_STATE_SEL) {
    497 		ds->ds_flags &= ~DMIO_STATE_SEL;
    498 		selnotify(&ds->ds_selq, POLLOUT | POLLWRNORM, 0);
    499 	}
    500 	mutex_exit(&ds->ds_lock);
    501 }
    502 
    503 /*
    504  * dmio_write:
    505  *
    506  *	Write file op.
    507  */
    508 static int
    509 dmio_write(struct file *fp, off_t *offp, struct uio *uio,
    510     kauth_cred_t cred, int flags)
    511 {
    512 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    513 	struct dmio_usrreq_state *dus;
    514 	struct dmover_request *dreq;
    515 	struct dmio_usrreq req;
    516 	int error = 0, progress = 0;
    517 
    518 	if ((uio->uio_resid % sizeof(req)) != 0)
    519 		return (EINVAL);
    520 
    521 	if (ds->ds_session == NULL)
    522 		return (ENXIO);
    523 
    524 	getnanotime(&ds->ds_mtime);
    525 	mutex_enter(&ds->ds_lock);
    526 
    527 	while (uio->uio_resid != 0) {
    528 
    529 		if (ds->ds_nreqs == DMIO_NREQS_MAX) {
    530 			if (fp->f_flag & FNONBLOCK) {
    531 				error = progress ? 0 : EWOULDBLOCK;
    532 				break;
    533 			}
    534 			ds->ds_flags |= DMIO_STATE_WRITE_WAIT;
    535 			error = cv_wait_sig(&ds->ds_complete_cv, &ds->ds_lock);
    536 			if (error)
    537 				break;
    538 			continue;
    539 		}
    540 
    541 		ds->ds_nreqs++;
    542 
    543 		mutex_exit(&ds->ds_lock);
    544 
    545 		progress = 1;
    546 
    547 		error = uiomove(&req, sizeof(req), uio);
    548 		if (error) {
    549 			mutex_enter(&ds->ds_lock);
    550 			ds->ds_nreqs--;
    551 			break;
    552 		}
    553 
    554 		/* XXX How should this interact with FNONBLOCK? */
    555 		dreq = dmover_request_alloc(ds->ds_session, NULL);
    556 		if (dreq == NULL) {
    557 			/* XXX */
    558 			ds->ds_nreqs--;
    559 			error = ENOMEM;
    560 			return error;
    561 		}
    562 		dus = pool_get(&dmio_usrreq_state_pool, PR_WAITOK);
    563 
    564 		error = dmio_usrreq_init(fp, dus, &req, dreq);
    565 		if (error) {
    566 			dmover_request_free(dreq);
    567 			pool_put(&dmio_usrreq_state_pool, dus);
    568 			return error;
    569 		}
    570 
    571 		dreq->dreq_callback = dmio_usrreq_done;
    572 		dreq->dreq_cookie = dus;
    573 
    574 		dus->dus_req = dreq;
    575 		dus->dus_id = req.req_id;
    576 
    577 		mutex_enter(&ds->ds_lock);
    578 
    579 		TAILQ_INSERT_TAIL(&ds->ds_pending, dus, dus_q);
    580 
    581 		mutex_exit(&ds->ds_lock);
    582 
    583 		dmover_process(dreq);
    584 
    585 		mutex_enter(&ds->ds_lock);
    586 	}
    587 
    588 	mutex_exit(&ds->ds_lock);
    589 
    590 	return (error);
    591 }
    592 
    593 static int
    594 dmio_stat(struct file *fp, struct stat *st)
    595 {
    596 	struct dmio_state *ds = fp->f_data;
    597 
    598 	(void)memset(st, 0, sizeof(*st));
    599 	KERNEL_LOCK(1, NULL);
    600 	st->st_dev = makedev(cdevsw_lookup_major(&dmoverio_cdevsw), 0);
    601 	st->st_atimespec = ds->ds_atime;
    602 	st->st_mtimespec = ds->ds_mtime;
    603 	st->st_ctimespec = st->st_birthtimespec = ds->ds_btime;
    604 	st->st_uid = kauth_cred_geteuid(fp->f_cred);
    605 	st->st_gid = kauth_cred_getegid(fp->f_cred);
    606 	KERNEL_UNLOCK_ONE(NULL);
    607 	return 0;
    608 }
    609 
    610 /*
    611  * dmio_ioctl:
    612  *
    613  *	Ioctl file op.
    614  */
    615 static int
    616 dmio_ioctl(struct file *fp, u_long cmd, void *data)
    617 {
    618 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    619 	int error;
    620 
    621 	switch (cmd) {
    622 	case FIONBIO:
    623 	case FIOASYNC:
    624 		return (0);
    625 
    626 	case DMIO_SETFUNC:
    627 	    {
    628 		struct dmio_setfunc *dsf = data;
    629 		struct dmover_session *dses;
    630 
    631 		mutex_enter(&ds->ds_lock);
    632 
    633 		if (ds->ds_session != NULL ||
    634 		    (ds->ds_flags & DMIO_STATE_LARVAL) != 0) {
    635 			mutex_exit(&ds->ds_lock);
    636 			return (EBUSY);
    637 		}
    638 
    639 		ds->ds_flags |= DMIO_STATE_LARVAL;
    640 
    641 		mutex_exit(&ds->ds_lock);
    642 
    643 		dsf->dsf_name[DMIO_MAX_FUNCNAME - 1] = '\0';
    644 		error = dmover_session_create(dsf->dsf_name, &dses);
    645 
    646 		mutex_enter(&ds->ds_lock);
    647 
    648 		if (error == 0) {
    649 			dses->dses_cookie = ds;
    650 			ds->ds_session = dses;
    651 		}
    652 		ds->ds_flags &= ~DMIO_STATE_LARVAL;
    653 
    654 		mutex_exit(&ds->ds_lock);
    655 		break;
    656 	    }
    657 
    658 	default:
    659 		error = ENOTTY;
    660 	}
    661 
    662 	return (error);
    663 }
    664 
    665 /*
    666  * dmio_poll:
    667  *
    668  *	Poll file op.
    669  */
    670 static int
    671 dmio_poll(struct file *fp, int events)
    672 {
    673 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    674 	int revents = 0;
    675 
    676 	if ((events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) == 0)
    677 		return (revents);
    678 
    679 	mutex_enter(&ds->ds_lock);
    680 
    681 	if (ds->ds_flags & DMIO_STATE_DEAD) {
    682 		/* EOF */
    683 		revents |= events & (POLLIN | POLLRDNORM |
    684 		    POLLOUT | POLLWRNORM);
    685 		goto out;
    686 	}
    687 
    688 	/* We can read if there are completed requests. */
    689 	if (events & (POLLIN | POLLRDNORM))
    690 		if (TAILQ_EMPTY(&ds->ds_complete) == 0)
    691 			revents |= events & (POLLIN | POLLRDNORM);
    692 
    693 	/*
    694 	 * We can write if there is there are fewer then DMIO_NREQS_MAX
    695 	 * are already in the queue.
    696 	 */
    697 	if (events & (POLLOUT | POLLWRNORM))
    698 		if (ds->ds_nreqs < DMIO_NREQS_MAX)
    699 			revents |= events & (POLLOUT | POLLWRNORM);
    700 
    701 	if (revents == 0) {
    702 		selrecord(curlwp, &ds->ds_selq);
    703 		ds->ds_flags |= DMIO_STATE_SEL;
    704 	}
    705 
    706  out:
    707 	mutex_exit(&ds->ds_lock);
    708 
    709 	return (revents);
    710 }
    711 
    712 /*
    713  * dmio_close:
    714  *
    715  *	Close file op.
    716  */
    717 static int
    718 dmio_close(struct file *fp)
    719 {
    720 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    721 	struct dmio_usrreq_state *dus;
    722 	struct dmover_session *dses;
    723 
    724 	mutex_enter(&ds->ds_lock);
    725 
    726 	ds->ds_flags |= DMIO_STATE_DEAD;
    727 
    728 	/* Garbage-collect all the responses on the queue. */
    729 	while ((dus = TAILQ_FIRST(&ds->ds_complete)) != NULL) {
    730 		TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
    731 		ds->ds_nreqs--;
    732 		mutex_exit(&ds->ds_lock);
    733 		dmover_request_free(dus->dus_req);
    734 		dmio_usrreq_fini(ds, dus);
    735 		mutex_enter(&ds->ds_lock);
    736 	}
    737 
    738 	/*
    739 	 * If there are any requests pending, we have to wait for
    740 	 * them.  Don't free the dmio_state in this case.
    741 	 */
    742 	if (ds->ds_nreqs == 0) {
    743 		dses = ds->ds_session;
    744 		mutex_exit(&ds->ds_lock);
    745 		dmio_state_put(ds);
    746 	} else {
    747 		dses = NULL;
    748 		mutex_exit(&ds->ds_lock);
    749 	}
    750 
    751 	fp->f_data = NULL;
    752 
    753 	if (dses != NULL)
    754 		dmover_session_destroy(dses);
    755 
    756 	return (0);
    757 }
    758 
    759 static const struct fileops dmio_fileops = {
    760 	.fo_read = dmio_read,
    761 	.fo_write = dmio_write,
    762 	.fo_ioctl = dmio_ioctl,
    763 	.fo_fcntl = fnullop_fcntl,
    764 	.fo_poll = dmio_poll,
    765 	.fo_stat = dmio_stat,
    766 	.fo_close = dmio_close,
    767 	.fo_kqfilter = fnullop_kqfilter,
    768 	.fo_restart = fnullop_restart,
    769 };
    770 
    771 /*
    772  * dmoverioopen:
    773  *
    774  *	Device switch open routine.
    775  */
    776 int
    777 dmoverioopen(dev_t dev, int flag, int mode, struct lwp *l)
    778 {
    779 	struct dmio_state *ds;
    780 	struct file *fp;
    781 	int error, fd;
    782 
    783 	if ((error = fd_allocfile(&fp, &fd)) != 0)
    784 		return (error);
    785 
    786 	ds = dmio_state_get();
    787 
    788 	return fd_clone(fp, fd, flag, &dmio_fileops, ds);
    789 }
    790