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