1 /* $NetBSD: dmover_io.c,v 1.13 2003/09/13 08:32:12 jdolecek 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.13 2003/09/13 08:32:12 jdolecek 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_procp = curproc;
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_procp = curproc;
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 proc *p)
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_fcntl:
584 *
585 * Fcntl file op.
586 */
587 static int
588 dmio_fcntl(struct file *fp, u_int cmd, void *data, struct proc *p)
589 {
590
591 if (cmd == FNONBLOCK || cmd == FASYNC)
592 return (0);
593
594 return (EOPNOTSUPP);
595 }
596
597 /*
598 * dmio_poll:
599 *
600 * Poll file op.
601 */
602 static int
603 dmio_poll(struct file *fp, int events, struct proc *p)
604 {
605 struct dmio_state *ds = (struct dmio_state *) fp->f_data;
606 int s, revents = 0;
607
608 if ((events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) == 0)
609 return (revents);
610
611 s = splsoftclock();
612 simple_lock(&ds->ds_slock);
613
614 if (ds->ds_flags & DMIO_STATE_DEAD) {
615 /* EOF */
616 revents |= events & (POLLIN | POLLRDNORM |
617 POLLOUT | POLLWRNORM);
618 goto out;
619 }
620
621 /* We can read if there are completed requests. */
622 if (events & (POLLIN | POLLRDNORM))
623 if (TAILQ_EMPTY(&ds->ds_complete) == 0)
624 revents |= events & (POLLIN | POLLRDNORM);
625
626 /*
627 * We can write if there is there are fewer then DMIO_NREQS_MAX
628 * are already in the queue.
629 */
630 if (events & (POLLOUT | POLLWRNORM))
631 if (ds->ds_nreqs < DMIO_NREQS_MAX)
632 revents |= events & (POLLOUT | POLLWRNORM);
633
634 if (revents == 0) {
635 selrecord(p, &ds->ds_selq);
636 ds->ds_flags |= DMIO_STATE_SEL;
637 }
638
639 out:
640 simple_unlock(&ds->ds_slock);
641 splx(s);
642
643 return (revents);
644 }
645
646 /*
647 * dmio_stat:
648 *
649 * Stat file op.
650 */
651 static int
652 dmio_stat(struct file *fp, struct stat *sb, struct proc *p)
653 {
654
655 return (EOPNOTSUPP);
656 }
657
658 /*
659 * dmio_close:
660 *
661 * Close file op.
662 */
663 static int
664 dmio_close(struct file *fp, struct proc *p)
665 {
666 struct dmio_state *ds = (struct dmio_state *) fp->f_data;
667 struct dmio_usrreq_state *dus;
668 struct dmover_session *dses;
669 int s;
670
671 s = splsoftclock();
672 simple_lock(&ds->ds_slock);
673
674 ds->ds_flags |= DMIO_STATE_DEAD;
675
676 /* Garbage-collect all the responses on the queue. */
677 while ((dus = TAILQ_FIRST(&ds->ds_complete)) != NULL) {
678 TAILQ_REMOVE(&ds->ds_complete, dus, dus_q);
679 ds->ds_nreqs--;
680 dmover_request_free(dus->dus_req);
681 dmio_usrreq_fini(ds, dus);
682 }
683
684 /*
685 * If there are any requests pending, we have to wait for
686 * them. Don't free the dmio_state in this case.
687 */
688 if (ds->ds_nreqs == 0) {
689 dses = ds->ds_session;
690 simple_unlock(&ds->ds_slock);
691 pool_put(&dmio_state_pool, ds);
692 } else {
693 dses = NULL;
694 simple_unlock(&ds->ds_slock);
695 }
696
697 splx(s);
698
699 fp->f_data = NULL;
700
701 if (dses != NULL)
702 dmover_session_destroy(dses);
703
704 return (0);
705 }
706
707 static struct fileops dmio_fileops = {
708 dmio_read,
709 dmio_write,
710 dmio_ioctl,
711 dmio_fcntl,
712 dmio_poll,
713 dmio_stat,
714 dmio_close,
715 };
716
717 /*
718 * dmoverioopen:
719 *
720 * Device switch open routine.
721 */
722 int
723 dmoverioopen(dev_t dev, int flag, int mode, struct proc *p)
724 {
725 struct dmio_state *ds;
726 struct file *fp;
727 int error, fd, s;
728
729 /* falloc() will use the descriptor for us. */
730 if ((error = falloc(p, &fp, &fd)) != 0)
731 return (error);
732
733 s = splsoftclock();
734 ds = pool_get(&dmio_state_pool, PR_WAITOK);
735 splx(s);
736
737 memset(ds, 0, sizeof(*ds));
738 TAILQ_INIT(&ds->ds_pending);
739 TAILQ_INIT(&ds->ds_complete);
740
741 fp->f_flag = FREAD | FWRITE;
742 fp->f_type = DTYPE_MISC;
743 fp->f_ops = &dmio_fileops;
744 fp->f_data = (caddr_t) ds;
745
746 curlwp->l_dupfd = fd; /* XXX */
747 FILE_SET_MATURE(fp);
748 FILE_UNUSE(fp, p);
749
750 return (ENXIO);
751 }
Cache object: 1c8bd6650de5dba0f305b191464c79c2
|