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