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