xref: /netbsd-src/sys/dev/dmover/dmover_io.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: dmover_io.c,v 1.41 2013/07/25 04:32:37 msaitoh 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.41 2013/07/25 04:32:37 msaitoh 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