Home | History | Annotate | Line # | Download | only in dmover
dmover_io.c revision 1.19
      1 /*	$NetBSD: dmover_io.c,v 1.19 2005/12/24 20:27:29 perry 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.19 2005/12/24 20:27:29 perry 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/lock.h>
     68 #include <sys/file.h>
     69 #include <sys/filedesc.h>
     70 #include <sys/filio.h>
     71 #include <sys/select.h>
     72 #include <sys/systm.h>
     73 
     74 #include <dev/dmover/dmovervar.h>
     75 #include <dev/dmover/dmover_io.h>
     76 
     77 struct dmio_usrreq_state {
     78 	TAILQ_ENTRY(dmio_usrreq_state) dus_q;
     79 	struct uio dus_uio_out;
     80 	struct uio *dus_uio_in;
     81 	struct dmover_request *dus_req;
     82 	uint32_t dus_id;
     83 };
     84 
     85 struct dmio_state {
     86 	struct dmover_session *ds_session;
     87 	TAILQ_HEAD(, dmio_usrreq_state) ds_pending;
     88 	TAILQ_HEAD(, dmio_usrreq_state) ds_complete;
     89 	struct selinfo ds_selq;
     90 	volatile int ds_flags;
     91 	u_int ds_nreqs;
     92 	struct simplelock ds_slock;
     93 };
     94 
     95 #define	DMIO_STATE_SEL		0x0001
     96 #define	DMIO_STATE_DEAD		0x0002
     97 #define	DMIO_STATE_LARVAL	0x0004
     98 #define	DMIO_STATE_READ_WAIT	0x0008
     99 #define	DMIO_STATE_WRITE_WAIT	0x0010
    100 
    101 #define	DMIO_NREQS_MAX		64	/* XXX pulled out of a hat */
    102 
    103 struct pool dmio_state_pool;
    104 struct pool dmio_usrreq_state_pool;
    105 
    106 void	dmoverioattach(int);
    107 
    108 dev_type_open(dmoverioopen);
    109 
    110 const struct cdevsw dmoverio_cdevsw = {
    111 	dmoverioopen, noclose, noread, nowrite, noioctl,
    112 	nostop, notty, nopoll, nommap, nokqfilter,
    113 };
    114 
    115 /*
    116  * dmoverioattach:
    117  *
    118  *	Pseudo-device attach routine.
    119  */
    120 void
    121 dmoverioattach(int count)
    122 {
    123 
    124 	pool_init(&dmio_state_pool, sizeof(struct dmio_state),
    125 	    0, 0, 0, "dmiostate", NULL);
    126 	pool_init(&dmio_usrreq_state_pool, sizeof(struct dmio_usrreq_state),
    127 	    0, 0, 0, "dmiourstate", NULL);
    128 }
    129 
    130 /*
    131  * dmio_usrreq_init:
    132  *
    133  *	Build a request structure.
    134  */
    135 static int
    136 dmio_usrreq_init(struct file *fp, struct dmio_usrreq_state *dus,
    137     struct dmio_usrreq *req, struct dmover_request *dreq)
    138 {
    139 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    140 	struct dmover_session *dses = ds->ds_session;
    141 	struct uio *uio_out = &dus->dus_uio_out;
    142 	struct uio *uio_in;
    143 	dmio_buffer inbuf;
    144 	size_t len;
    145 	int i, error;
    146 	u_int j;
    147 
    148 	/* XXX How should malloc interact w/ FNONBLOCK? */
    149 
    150 	if (req->req_outbuf.dmbuf_iovcnt != 0) {
    151 		if (req->req_outbuf.dmbuf_iovcnt > IOV_MAX)
    152 			return (EINVAL);
    153 		len = sizeof(struct iovec) * req->req_outbuf.dmbuf_iovcnt;
    154 		uio_out->uio_iov = malloc(len, M_TEMP, M_WAITOK);
    155 		error = copyin(req->req_outbuf.dmbuf_iov, uio_out->uio_iov,
    156 		    len);
    157 		if (error) {
    158 			free(uio_out->uio_iov, M_TEMP);
    159 			return (error);
    160 		}
    161 
    162 		for (j = 0, len = 0; j < req->req_outbuf.dmbuf_iovcnt; j++) {
    163 			len += uio_out->uio_iov[j].iov_len;
    164 			if (len > SSIZE_MAX) {
    165 				free(uio_out->uio_iov, M_TEMP);
    166 				return (EINVAL);
    167 			}
    168 		}
    169 
    170 		uio_out->uio_iovcnt = req->req_outbuf.dmbuf_iovcnt;
    171 		uio_out->uio_resid = len;
    172 		uio_out->uio_rw = UIO_READ;
    173 		uio_out->uio_segflg = UIO_USERSPACE;
    174 		uio_out->uio_lwp = curlwp;
    175 		dreq->dreq_outbuf_type = DMOVER_BUF_UIO;
    176 		dreq->dreq_outbuf.dmbuf_uio = uio_out;
    177 	} else {
    178 		uio_out->uio_iov = NULL;
    179 		uio_out = NULL;
    180 		dreq->dreq_outbuf_type = DMOVER_BUF_NONE;
    181 	}
    182 
    183 	memcpy(dreq->dreq_immediate, req->req_immediate,
    184 	    sizeof(dreq->dreq_immediate));
    185 
    186 	if (dses->dses_ninputs == 0) {
    187 		/* No inputs; all done. */
    188 		return (0);
    189 	}
    190 
    191 	dreq->dreq_inbuf_type = DMOVER_BUF_UIO;
    192 
    193 	dus->dus_uio_in = malloc(sizeof(struct uio) * dses->dses_ninputs,
    194 	    M_TEMP, M_WAITOK);
    195 	memset(dus->dus_uio_in, 0, sizeof(struct uio) * dses->dses_ninputs);
    196 
    197 	for (i = 0; i < dses->dses_ninputs; i++) {
    198 		uio_in = &dus->dus_uio_in[i];
    199 
    200 		error = copyin(&req->req_inbuf[i], &inbuf, sizeof(inbuf));
    201 		if (error)
    202 			goto bad;
    203 
    204 		if (inbuf.dmbuf_iovcnt > IOV_MAX) {
    205 			error = EINVAL;
    206 			goto bad;
    207 		}
    208 		len = sizeof(struct iovec) * inbuf.dmbuf_iovcnt;
    209 		if (len == 0) {
    210 			error = EINVAL;
    211 			goto bad;
    212 		}
    213 		uio_in->uio_iov = malloc(len, M_TEMP, M_WAITOK);
    214 
    215 		error = copyin(inbuf.dmbuf_iov, uio_in->uio_iov, len);
    216 		if (error) {
    217 			free(uio_in->uio_iov, M_TEMP);
    218 			goto bad;
    219 		}
    220 
    221 		for (j = 0, len = 0; j < inbuf.dmbuf_iovcnt; j++) {
    222 			len += uio_in->uio_iov[j].iov_len;
    223 			if (len > SSIZE_MAX) {
    224 				free(uio_in->uio_iov, M_TEMP);
    225 				error = EINVAL;
    226 				goto bad;
    227 			}
    228 		}
    229 
    230 		if (uio_out != NULL && len != uio_out->uio_resid) {
    231 			free(uio_in->uio_iov, M_TEMP);
    232 			error = EINVAL;
    233 			goto bad;
    234 		}
    235 
    236 		uio_in->uio_iovcnt = inbuf.dmbuf_iovcnt;
    237 		uio_in->uio_resid = len;
    238 		uio_in->uio_rw = UIO_WRITE;
    239 		uio_in->uio_segflg = UIO_USERSPACE;
    240 		uio_in->uio_lwp = curlwp;
    241 
    242 		dreq->dreq_inbuf[i].dmbuf_uio = uio_in;
    243 	}
    244 
    245 	return (0);
    246 
    247  bad:
    248 	if (i > 0) {
    249 		for (--i; i >= 0; i--) {
    250 			uio_in = &dus->dus_uio_in[i];
    251 			free(uio_in->uio_iov, M_TEMP);
    252 		}
    253 	}
    254 	free(dus->dus_uio_in, M_TEMP);
    255 	if (uio_out != NULL)
    256 		free(uio_out->uio_iov, M_TEMP);
    257 	return (error);
    258 }
    259 
    260 /*
    261  * dmio_usrreq_fini:
    262  *
    263  *	Tear down a request.  Must be called at splsoftclock().
    264  */
    265 static void
    266 dmio_usrreq_fini(struct dmio_state *ds, struct dmio_usrreq_state *dus)
    267 {
    268 	struct dmover_session *dses = ds->ds_session;
    269 	struct uio *uio_out = &dus->dus_uio_out;
    270 	struct uio *uio_in;
    271 	int i;
    272 
    273 	if (uio_out->uio_iov != NULL)
    274 		free(uio_out->uio_iov, M_TEMP);
    275 
    276 	if (dses->dses_ninputs == 0) {
    277 		pool_put(&dmio_usrreq_state_pool, dus);
    278 		return;
    279 	}
    280 
    281 	for (i = 0; i < dses->dses_ninputs; i++) {
    282 		uio_in = &dus->dus_uio_in[i];
    283 		free(uio_in->uio_iov, M_TEMP);
    284 	}
    285 
    286 	free(dus->dus_uio_in, M_TEMP);
    287 
    288 	pool_put(&dmio_usrreq_state_pool, dus);
    289 }
    290 
    291 /*
    292  * dmio_read:
    293  *
    294  *	Read file op.
    295  */
    296 static int
    297 dmio_read(struct file *fp, off_t *offp, struct uio *uio,
    298     struct ucred *cred, int flags)
    299 {
    300 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    301 	struct dmio_usrreq_state *dus;
    302 	struct dmover_request *dreq;
    303 	struct dmio_usrresp resp;
    304 	int s, error = 0, progress = 0;
    305 
    306 	if ((uio->uio_resid % sizeof(resp)) != 0)
    307 		return (EINVAL);
    308 
    309 	if (ds->ds_session == NULL)
    310 		return (ENXIO);
    311 
    312 	s = splsoftclock();
    313 	simple_lock(&ds->ds_slock);
    314 
    315 	while (uio->uio_resid != 0) {
    316 
    317 		for (;;) {
    318 			dus = TAILQ_FIRST(&ds->ds_complete);
    319 			if (dus == NULL) {
    320 				if (fp->f_flag & FNONBLOCK) {
    321 					error = progress ? 0 : EWOULDBLOCK;
    322 					goto out;
    323 				}
    324 				ds->ds_flags |= DMIO_STATE_READ_WAIT;
    325 				error = ltsleep(&ds->ds_complete,
    326 				    PRIBIO | PCATCH, "dmvrrd", 0,
    327 				    &ds->ds_slock);
    328 				if (error)
    329 					goto out;
    330 				continue;
    331 			}
    332 			/* Have a completed request. */
    333 			TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
    334 			ds->ds_nreqs--;
    335 			if (ds->ds_flags & DMIO_STATE_WRITE_WAIT) {
    336 				ds->ds_flags &= ~DMIO_STATE_WRITE_WAIT;
    337 				wakeup(&ds->ds_nreqs);
    338 			}
    339 			if (ds->ds_flags & DMIO_STATE_SEL) {
    340 				ds->ds_flags &= ~DMIO_STATE_SEL;
    341 				selwakeup(&ds->ds_selq);
    342 			}
    343 			break;
    344 		}
    345 
    346 		simple_unlock(&ds->ds_slock);
    347 
    348 		dreq = dus->dus_req;
    349 		resp.resp_id = dus->dus_id;
    350 		if (dreq->dreq_flags & DMOVER_REQ_ERROR)
    351 			resp.resp_error = dreq->dreq_error;
    352 		else {
    353 			resp.resp_error = 0;
    354 			memcpy(resp.resp_immediate, dreq->dreq_immediate,
    355 			    sizeof(resp.resp_immediate));
    356 		}
    357 
    358 		dmio_usrreq_fini(ds, dus);
    359 
    360 		splx(s);
    361 
    362 		progress = 1;
    363 
    364 		dmover_request_free(dreq);
    365 
    366 		error = uiomove(&resp, sizeof(resp), uio);
    367 		if (error)
    368 			return (error);
    369 
    370 		s = splsoftclock();
    371 		simple_lock(&ds->ds_slock);
    372 	}
    373 
    374  out:
    375 	simple_unlock(&ds->ds_slock);
    376 	splx(s);
    377 
    378 	return (error);
    379 }
    380 
    381 /*
    382  * dmio_usrreq_done:
    383  *
    384  *	Dmover completion callback.
    385  */
    386 static void
    387 dmio_usrreq_done(struct dmover_request *dreq)
    388 {
    389 	struct dmio_usrreq_state *dus = dreq->dreq_cookie;
    390 	struct dmio_state *ds = dreq->dreq_session->dses_cookie;
    391 
    392 	/* We're already at splsoftclock(). */
    393 
    394 	simple_lock(&ds->ds_slock);
    395 	TAILQ_REMOVE(&ds->ds_pending, dus, dus_q);
    396 	if (ds->ds_flags & DMIO_STATE_DEAD) {
    397 		ds->ds_nreqs--;
    398 		dmio_usrreq_fini(ds, dus);
    399 		dmover_request_free(dreq);
    400 		if (ds->ds_nreqs == 0) {
    401 			simple_unlock(&ds->ds_slock);
    402 			pool_put(&dmio_state_pool, ds);
    403 			return;
    404 		}
    405 	} else {
    406 		TAILQ_INSERT_TAIL(&ds->ds_complete, dus, dus_q);
    407 		if (ds->ds_flags & DMIO_STATE_READ_WAIT) {
    408 			ds->ds_flags &= ~DMIO_STATE_READ_WAIT;
    409 			wakeup(&ds->ds_complete);
    410 		}
    411 		if (ds->ds_flags & DMIO_STATE_SEL) {
    412 			ds->ds_flags &= ~DMIO_STATE_SEL;
    413 			selwakeup(&ds->ds_selq);
    414 		}
    415 	}
    416 	simple_unlock(&ds->ds_slock);
    417 }
    418 
    419 /*
    420  * dmio_write:
    421  *
    422  *	Write file op.
    423  */
    424 static int
    425 dmio_write(struct file *fp, off_t *offp, struct uio *uio,
    426     struct ucred *cred, int flags)
    427 {
    428 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    429 	struct dmio_usrreq_state *dus;
    430 	struct dmover_request *dreq;
    431 	struct dmio_usrreq req;
    432 	int error = 0, s, progress = 0;
    433 
    434 	if ((uio->uio_resid % sizeof(req)) != 0)
    435 		return (EINVAL);
    436 
    437 	if (ds->ds_session == NULL)
    438 		return (ENXIO);
    439 
    440 	s = splsoftclock();
    441 	simple_lock(&ds->ds_slock);
    442 
    443 	while (uio->uio_resid != 0) {
    444 
    445 		if (ds->ds_nreqs == DMIO_NREQS_MAX) {
    446 			if (fp->f_flag & FNONBLOCK) {
    447 				error = progress ? 0 : EWOULDBLOCK;
    448 				break;
    449 			}
    450 			ds->ds_flags |= DMIO_STATE_WRITE_WAIT;
    451 			error = ltsleep(&ds->ds_nreqs, PRIBIO | PCATCH,
    452 			    "dmiowr", 0, &ds->ds_slock);
    453 			if (error)
    454 				break;
    455 			continue;
    456 		}
    457 
    458 		ds->ds_nreqs++;
    459 
    460 		simple_unlock(&ds->ds_slock);
    461 		splx(s);
    462 
    463 		progress = 1;
    464 
    465 		error = uiomove(&req, sizeof(req), uio);
    466 		if (error) {
    467 			s = splsoftclock();
    468 			simple_lock(&ds->ds_slock);
    469 			ds->ds_nreqs--;
    470 			break;
    471 		}
    472 
    473 		/* XXX How should this interact with FNONBLOCK? */
    474 		dreq = dmover_request_alloc(ds->ds_session, NULL);
    475 		if (dreq == NULL) {
    476 			/* XXX */
    477 			s = splsoftclock();
    478 			simple_lock(&ds->ds_slock);
    479 			ds->ds_nreqs--;
    480 			error = ENOMEM;
    481 			break;
    482 		}
    483 		s = splsoftclock();
    484 		dus = pool_get(&dmio_usrreq_state_pool, PR_WAITOK);
    485 		splx(s);
    486 
    487 		error = dmio_usrreq_init(fp, dus, &req, dreq);
    488 		if (error) {
    489 			dmover_request_free(dreq);
    490 			s = splsoftclock();
    491 			pool_put(&dmio_usrreq_state_pool, dus);
    492 			simple_lock(&ds->ds_slock);
    493 			break;
    494 		}
    495 
    496 		dreq->dreq_callback = dmio_usrreq_done;
    497 		dreq->dreq_cookie = dus;
    498 
    499 		dus->dus_req = dreq;
    500 		dus->dus_id = req.req_id;
    501 
    502 		s = splsoftclock();
    503 		simple_lock(&ds->ds_slock);
    504 
    505 		TAILQ_INSERT_TAIL(&ds->ds_pending, dus, dus_q);
    506 
    507 		simple_unlock(&ds->ds_slock);
    508 		splx(s);
    509 
    510 		dmover_process(dreq);
    511 
    512 		s = splsoftclock();
    513 		simple_lock(&ds->ds_slock);
    514 	}
    515 
    516 	simple_unlock(&ds->ds_slock);
    517 	splx(s);
    518 
    519 	return (error);
    520 }
    521 
    522 /*
    523  * dmio_ioctl:
    524  *
    525  *	Ioctl file op.
    526  */
    527 static int
    528 dmio_ioctl(struct file *fp, u_long cmd, void *data, struct lwp *l)
    529 {
    530 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    531 	int error, s;
    532 
    533 	switch (cmd) {
    534 	case FIONBIO:
    535 	case FIOASYNC:
    536 		return (0);
    537 
    538 	case DMIO_SETFUNC:
    539 	    {
    540 		struct dmio_setfunc *dsf = data;
    541 		struct dmover_session *dses;
    542 
    543 		s = splsoftclock();
    544 		simple_lock(&ds->ds_slock);
    545 
    546 		if (ds->ds_session != NULL ||
    547 		    (ds->ds_flags & DMIO_STATE_LARVAL) != 0) {
    548 			simple_unlock(&ds->ds_slock);
    549 			splx(s);
    550 			return (EBUSY);
    551 		}
    552 
    553 		ds->ds_flags |= DMIO_STATE_LARVAL;
    554 
    555 		simple_unlock(&ds->ds_slock);
    556 		splx(s);
    557 
    558 		dsf->dsf_name[DMIO_MAX_FUNCNAME - 1] = '\0';
    559 		error = dmover_session_create(dsf->dsf_name, &dses);
    560 
    561 		s = splsoftclock();
    562 		simple_lock(&ds->ds_slock);
    563 
    564 		if (error == 0) {
    565 			dses->dses_cookie = ds;
    566 			ds->ds_session = dses;
    567 		}
    568 		ds->ds_flags &= ~DMIO_STATE_LARVAL;
    569 
    570 		simple_unlock(&ds->ds_slock);
    571 		splx(s);
    572 		break;
    573 	    }
    574 
    575 	default:
    576 		error = ENOTTY;
    577 	}
    578 
    579 	return (error);
    580 }
    581 
    582 /*
    583  * dmio_poll:
    584  *
    585  *	Poll file op.
    586  */
    587 static int
    588 dmio_poll(struct file *fp, int events, struct lwp *l)
    589 {
    590 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    591 	int s, revents = 0;
    592 
    593 	if ((events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) == 0)
    594 		return (revents);
    595 
    596 	s = splsoftclock();
    597 	simple_lock(&ds->ds_slock);
    598 
    599 	if (ds->ds_flags & DMIO_STATE_DEAD) {
    600 		/* EOF */
    601 		revents |= events & (POLLIN | POLLRDNORM |
    602 		    POLLOUT | POLLWRNORM);
    603 		goto out;
    604 	}
    605 
    606 	/* We can read if there are completed requests. */
    607 	if (events & (POLLIN | POLLRDNORM))
    608 		if (TAILQ_EMPTY(&ds->ds_complete) == 0)
    609 			revents |= events & (POLLIN | POLLRDNORM);
    610 
    611 	/*
    612 	 * We can write if there is there are fewer then DMIO_NREQS_MAX
    613 	 * are already in the queue.
    614 	 */
    615 	if (events & (POLLOUT | POLLWRNORM))
    616 		if (ds->ds_nreqs < DMIO_NREQS_MAX)
    617 			revents |= events & (POLLOUT | POLLWRNORM);
    618 
    619 	if (revents == 0) {
    620 		selrecord(l, &ds->ds_selq);
    621 		ds->ds_flags |= DMIO_STATE_SEL;
    622 	}
    623 
    624  out:
    625 	simple_unlock(&ds->ds_slock);
    626 	splx(s);
    627 
    628 	return (revents);
    629 }
    630 
    631 /*
    632  * dmio_close:
    633  *
    634  *	Close file op.
    635  */
    636 static int
    637 dmio_close(struct file *fp, struct lwp *l)
    638 {
    639 	struct dmio_state *ds = (struct dmio_state *) fp->f_data;
    640 	struct dmio_usrreq_state *dus;
    641 	struct dmover_session *dses;
    642 	int s;
    643 
    644 	s = splsoftclock();
    645 	simple_lock(&ds->ds_slock);
    646 
    647 	ds->ds_flags |= DMIO_STATE_DEAD;
    648 
    649 	/* Garbage-collect all the responses on the queue. */
    650 	while ((dus = TAILQ_FIRST(&ds->ds_complete)) != NULL) {
    651 		TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
    652 		ds->ds_nreqs--;
    653 		dmover_request_free(dus->dus_req);
    654 		dmio_usrreq_fini(ds, dus);
    655 	}
    656 
    657 	/*
    658 	 * If there are any requests pending, we have to wait for
    659 	 * them.  Don't free the dmio_state in this case.
    660 	 */
    661 	if (ds->ds_nreqs == 0) {
    662 		dses = ds->ds_session;
    663 		simple_unlock(&ds->ds_slock);
    664 		pool_put(&dmio_state_pool, ds);
    665 	} else {
    666 		dses = NULL;
    667 		simple_unlock(&ds->ds_slock);
    668 	}
    669 
    670 	splx(s);
    671 
    672 	fp->f_data = NULL;
    673 
    674 	if (dses != NULL)
    675 		dmover_session_destroy(dses);
    676 
    677 	return (0);
    678 }
    679 
    680 static const struct fileops dmio_fileops = {
    681 	dmio_read,
    682 	dmio_write,
    683 	dmio_ioctl,
    684 	fnullop_fcntl,
    685 	dmio_poll,
    686 	fbadop_stat,
    687 	dmio_close,
    688 	fnullop_kqfilter
    689 };
    690 
    691 /*
    692  * dmoverioopen:
    693  *
    694  *	Device switch open routine.
    695  */
    696 int
    697 dmoverioopen(dev_t dev, int flag, int mode, struct lwp *l)
    698 {
    699 	struct dmio_state *ds;
    700 	struct file *fp;
    701 	int error, fd, s;
    702 	struct proc *p = l->l_proc;
    703 
    704 	/* falloc() will use the descriptor for us. */
    705 	if ((error = falloc(p, &fp, &fd)) != 0)
    706 		return (error);
    707 
    708 	s = splsoftclock();
    709 	ds = pool_get(&dmio_state_pool, PR_WAITOK);
    710 	splx(s);
    711 
    712 	memset(ds, 0, sizeof(*ds));
    713 	TAILQ_INIT(&ds->ds_pending);
    714 	TAILQ_INIT(&ds->ds_complete);
    715 
    716 	return fdclone(l, fp, fd, flag, &dmio_fileops, ds);
    717 }
    718