1 /* $NetBSD: putter.c,v 1.29 2010/08/27 07:40:01 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Ulla Tuominen Foundation and the Finnish Cultural Foundation and the 8 * Research Foundation of Helsinki University of Technology 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Pass-to-Userspace TransporTER: generic kernel-user request-response 34 * transport interface. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: putter.c,v 1.29 2010/08/27 07:40:01 pooka Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/conf.h> 43 #include <sys/file.h> 44 #include <sys/filedesc.h> 45 #include <sys/kmem.h> 46 #include <sys/poll.h> 47 #include <sys/stat.h> 48 #include <sys/socketvar.h> 49 #include <sys/module.h> 50 #include <sys/kauth.h> 51 52 #include <dev/putter/putter_sys.h> 53 54 /* 55 * Device routines. These are for when /dev/putter is initially 56 * opened before it has been cloned. 57 */ 58 59 dev_type_open(puttercdopen); 60 dev_type_close(puttercdclose); 61 dev_type_ioctl(puttercdioctl); 62 63 /* dev */ 64 const struct cdevsw putter_cdevsw = { 65 puttercdopen, puttercdclose, noread, nowrite, 66 noioctl, nostop, notty, nopoll, 67 nommap, nokqfilter, D_OTHER 68 }; 69 70 /* 71 * Configuration data. 72 * 73 * This is static-size for now. Will be redone for devfs. 74 */ 75 76 #define PUTTER_CONFSIZE 16 77 78 static struct putter_config { 79 int pc_minor; 80 int (*pc_config)(int, int, int); 81 } putterconf[PUTTER_CONFSIZE]; 82 83 static int 84 putter_configure(dev_t dev, int flags, int fmt, int fd) 85 { 86 struct putter_config *pc; 87 88 /* are we the catch-all node? */ 89 if (minor(dev) == PUTTER_MINOR_WILDCARD 90 || minor(dev) == PUTTER_MINOR_COMPAT) 91 return 0; 92 93 /* nopes? try to configure us */ 94 for (pc = putterconf; pc->pc_config; pc++) 95 if (minor(dev) == pc->pc_minor) 96 return pc->pc_config(fd, flags, fmt); 97 return ENXIO; 98 } 99 100 int 101 putter_register(putter_config_fn pcfn, int minor) 102 { 103 int i; 104 105 for (i = 0; i < PUTTER_CONFSIZE; i++) 106 if (putterconf[i].pc_config == NULL) 107 break; 108 if (i == PUTTER_CONFSIZE) 109 return EBUSY; 110 111 putterconf[i].pc_minor = minor; 112 putterconf[i].pc_config = pcfn; 113 return 0; 114 } 115 116 /* 117 * putter instance structures. these are always allocated and freed 118 * from the context of the transport user. 119 */ 120 struct putter_instance { 121 pid_t pi_pid; 122 int pi_idx; 123 int pi_fd; 124 struct selinfo pi_sel; 125 126 void *pi_private; 127 struct putter_ops *pi_pop; 128 129 uint8_t *pi_curput; 130 size_t pi_curres; 131 void *pi_curopaq; 132 struct timespec pi_atime; 133 struct timespec pi_mtime; 134 struct timespec pi_btime; 135 136 TAILQ_ENTRY(putter_instance) pi_entries; 137 }; 138 #define PUTTER_EMBRYO ((void *)-1) /* before attach */ 139 #define PUTTER_DEAD ((void *)-2) /* after detach */ 140 141 static TAILQ_HEAD(, putter_instance) putter_ilist 142 = TAILQ_HEAD_INITIALIZER(putter_ilist); 143 144 static int get_pi_idx(struct putter_instance *); 145 146 #ifdef DEBUG 147 #ifndef PUTTERDEBUG 148 #define PUTTERDEBUG 149 #endif 150 #endif 151 152 #ifdef PUTTERDEBUG 153 int putterdebug = 0; 154 #define DPRINTF(x) if (putterdebug > 0) printf x 155 #define DPRINTF_VERBOSE(x) if (putterdebug > 1) printf x 156 #else 157 #define DPRINTF(x) 158 #define DPRINTF_VERBOSE(x) 159 #endif 160 161 /* 162 * public init / deinit 163 */ 164 165 /* protects both the list and the contents of the list elements */ 166 static kmutex_t pi_mtx; 167 168 void putterattach(void); 169 170 void 171 putterattach(void) 172 { 173 174 mutex_init(&pi_mtx, MUTEX_DEFAULT, IPL_NONE); 175 } 176 177 #if 0 178 void 179 putter_destroy(void) 180 { 181 182 mutex_destroy(&pi_mtx); 183 } 184 #endif 185 186 /* 187 * fd routines, for cloner 188 */ 189 static int putter_fop_read(file_t *, off_t *, struct uio *, 190 kauth_cred_t, int); 191 static int putter_fop_write(file_t *, off_t *, struct uio *, 192 kauth_cred_t, int); 193 static int putter_fop_ioctl(file_t*, u_long, void *); 194 static int putter_fop_poll(file_t *, int); 195 static int putter_fop_stat(file_t *, struct stat *); 196 static int putter_fop_close(file_t *); 197 static int putter_fop_kqfilter(file_t *, struct knote *); 198 199 200 static const struct fileops putter_fileops = { 201 .fo_read = putter_fop_read, 202 .fo_write = putter_fop_write, 203 .fo_ioctl = putter_fop_ioctl, 204 .fo_fcntl = fnullop_fcntl, 205 .fo_poll = putter_fop_poll, 206 .fo_stat = putter_fop_stat, 207 .fo_close = putter_fop_close, 208 .fo_kqfilter = putter_fop_kqfilter, 209 .fo_restart = fnullop_restart, 210 }; 211 212 static int 213 putter_fop_read(file_t *fp, off_t *off, struct uio *uio, 214 kauth_cred_t cred, int flags) 215 { 216 struct putter_instance *pi = fp->f_data; 217 size_t origres, moved; 218 int error; 219 220 KERNEL_LOCK(1, NULL); 221 getnanotime(&pi->pi_atime); 222 223 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 224 printf("putter_fop_read: private %d not inited\n", pi->pi_idx); 225 KERNEL_UNLOCK_ONE(NULL); 226 return ENOENT; 227 } 228 229 if (pi->pi_curput == NULL) { 230 error = pi->pi_pop->pop_getout(pi->pi_private, uio->uio_resid, 231 fp->f_flag & O_NONBLOCK, &pi->pi_curput, 232 &pi->pi_curres, &pi->pi_curopaq); 233 if (error) { 234 KERNEL_UNLOCK_ONE(NULL); 235 return error; 236 } 237 } 238 239 origres = uio->uio_resid; 240 error = uiomove(pi->pi_curput, pi->pi_curres, uio); 241 moved = origres - uio->uio_resid; 242 DPRINTF(("putter_fop_read (%p): moved %zu bytes from %p, error %d\n", 243 pi, moved, pi->pi_curput, error)); 244 245 KASSERT(pi->pi_curres >= moved); 246 pi->pi_curres -= moved; 247 pi->pi_curput += moved; 248 249 if (pi->pi_curres == 0) { 250 pi->pi_pop->pop_releaseout(pi->pi_private, 251 pi->pi_curopaq, error); 252 pi->pi_curput = NULL; 253 } 254 255 KERNEL_UNLOCK_ONE(NULL); 256 return error; 257 } 258 259 static int 260 putter_fop_write(file_t *fp, off_t *off, struct uio *uio, 261 kauth_cred_t cred, int flags) 262 { 263 struct putter_instance *pi = fp->f_data; 264 struct putter_hdr pth; 265 uint8_t *buf; 266 size_t frsize; 267 int error; 268 269 KERNEL_LOCK(1, NULL); 270 getnanotime(&pi->pi_mtime); 271 272 DPRINTF(("putter_fop_write (%p): writing response, resid %zu\n", 273 pi->pi_private, uio->uio_resid)); 274 275 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 276 printf("putter_fop_write: putter %d not inited\n", pi->pi_idx); 277 KERNEL_UNLOCK_ONE(NULL); 278 return ENOENT; 279 } 280 281 error = uiomove(&pth, sizeof(struct putter_hdr), uio); 282 if (error) { 283 KERNEL_UNLOCK_ONE(NULL); 284 return error; 285 } 286 287 /* Sorry mate, the kernel doesn't buffer. */ 288 frsize = pth.pth_framelen - sizeof(struct putter_hdr); 289 if (uio->uio_resid < frsize) { 290 KERNEL_UNLOCK_ONE(NULL); 291 return EINVAL; 292 } 293 294 buf = kmem_alloc(frsize + sizeof(struct putter_hdr), KM_SLEEP); 295 memcpy(buf, &pth, sizeof(pth)); 296 error = uiomove(buf+sizeof(struct putter_hdr), frsize, uio); 297 if (error == 0) { 298 pi->pi_pop->pop_dispatch(pi->pi_private, 299 (struct putter_hdr *)buf); 300 } 301 kmem_free(buf, frsize + sizeof(struct putter_hdr)); 302 303 KERNEL_UNLOCK_ONE(NULL); 304 return error; 305 } 306 307 /* 308 * Poll query interface. The question is only if an event 309 * can be read from us. 310 */ 311 #define PUTTERPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI) 312 static int 313 putter_fop_poll(file_t *fp, int events) 314 { 315 struct putter_instance *pi = fp->f_data; 316 int revents; 317 318 KERNEL_LOCK(1, NULL); 319 320 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 321 printf("putter_fop_ioctl: putter %d not inited\n", pi->pi_idx); 322 KERNEL_UNLOCK_ONE(NULL); 323 return ENOENT; 324 } 325 326 revents = events & (POLLOUT | POLLWRNORM | POLLWRBAND); 327 if ((events & PUTTERPOLL_EVSET) == 0) { 328 KERNEL_UNLOCK_ONE(NULL); 329 return revents; 330 } 331 332 /* check queue */ 333 if (pi->pi_pop->pop_waitcount(pi->pi_private)) 334 revents |= PUTTERPOLL_EVSET; 335 else 336 selrecord(curlwp, &pi->pi_sel); 337 338 KERNEL_UNLOCK_ONE(NULL); 339 return revents; 340 } 341 342 /* 343 * device close = forced unmount. 344 * 345 * unmounting is a frightfully complex operation to avoid races 346 */ 347 static int 348 putter_fop_close(file_t *fp) 349 { 350 struct putter_instance *pi = fp->f_data; 351 int rv; 352 353 DPRINTF(("putter_fop_close: device closed\n")); 354 355 KERNEL_LOCK(1, NULL); 356 357 restart: 358 mutex_enter(&pi_mtx); 359 /* 360 * First check if the driver was never born. In that case 361 * remove the instance from the list. If mount is attempted later, 362 * it will simply fail. 363 */ 364 if (pi->pi_private == PUTTER_EMBRYO) { 365 TAILQ_REMOVE(&putter_ilist, pi, pi_entries); 366 mutex_exit(&pi_mtx); 367 368 DPRINTF(("putter_fop_close: data associated with fp %p was " 369 "embryonic\n", fp)); 370 371 goto out; 372 } 373 374 /* 375 * Next, analyze if unmount was called and the instance is dead. 376 * In this case we can just free the structure and go home, it 377 * was removed from the list by putter_rmprivate(). 378 */ 379 if (pi->pi_private == PUTTER_DEAD) { 380 mutex_exit(&pi_mtx); 381 382 DPRINTF(("putter_fop_close: putter associated with fp %p (%d) " 383 "dead, freeing\n", fp, pi->pi_idx)); 384 385 goto out; 386 } 387 388 /* 389 * So we have a reference. Proceed to unravel the 390 * underlying driver. 391 */ 392 mutex_exit(&pi_mtx); 393 394 /* hmm? suspicious locking? */ 395 while ((rv = pi->pi_pop->pop_close(pi->pi_private)) == ERESTART) 396 goto restart; 397 398 out: 399 KERNEL_UNLOCK_ONE(NULL); 400 /* 401 * Finally, release the instance information. It was already 402 * removed from the list by putter_rmprivate() and we know it's 403 * dead, so no need to lock. 404 */ 405 kmem_free(pi, sizeof(struct putter_instance)); 406 407 return 0; 408 } 409 410 static int 411 putter_fop_stat(file_t *fp, struct stat *st) 412 { 413 struct putter_instance *pi = fp->f_data; 414 415 (void)memset(st, 0, sizeof(*st)); 416 KERNEL_LOCK(1, NULL); 417 st->st_dev = makedev(cdevsw_lookup_major(&putter_cdevsw), pi->pi_idx); 418 st->st_atimespec = pi->pi_atime; 419 st->st_mtimespec = pi->pi_mtime; 420 st->st_ctimespec = st->st_birthtimespec = pi->pi_btime; 421 st->st_uid = kauth_cred_geteuid(fp->f_cred); 422 st->st_gid = kauth_cred_getegid(fp->f_cred); 423 KERNEL_UNLOCK_ONE(NULL); 424 return 0; 425 } 426 427 static int 428 putter_fop_ioctl(file_t *fp, u_long cmd, void *data) 429 { 430 431 /* 432 * work already done in sys_ioctl(). skip sanity checks to enable 433 * setting non-blocking fd on an embryotic driver. 434 */ 435 if (cmd == FIONBIO) 436 return 0; 437 438 return EINVAL; 439 } 440 441 /* kqueue stuff */ 442 443 static void 444 filt_putterdetach(struct knote *kn) 445 { 446 struct putter_instance *pi = kn->kn_hook; 447 448 KERNEL_LOCK(1, NULL); 449 mutex_enter(&pi_mtx); 450 SLIST_REMOVE(&pi->pi_sel.sel_klist, kn, knote, kn_selnext); 451 mutex_exit(&pi_mtx); 452 KERNEL_UNLOCK_ONE(NULL); 453 } 454 455 static int 456 filt_putter(struct knote *kn, long hint) 457 { 458 struct putter_instance *pi = kn->kn_hook; 459 int error, rv; 460 461 KERNEL_LOCK(1, NULL); 462 error = 0; 463 mutex_enter(&pi_mtx); 464 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) 465 error = 1; 466 mutex_exit(&pi_mtx); 467 if (error) { 468 KERNEL_UNLOCK_ONE(NULL); 469 return 0; 470 } 471 472 kn->kn_data = pi->pi_pop->pop_waitcount(pi->pi_private); 473 rv = kn->kn_data != 0; 474 KERNEL_UNLOCK_ONE(NULL); 475 return rv; 476 } 477 478 static const struct filterops putter_filtops = 479 { 1, NULL, filt_putterdetach, filt_putter }; 480 481 static int 482 putter_fop_kqfilter(file_t *fp, struct knote *kn) 483 { 484 struct putter_instance *pi = fp->f_data; 485 struct klist *klist; 486 487 KERNEL_LOCK(1, NULL); 488 489 switch (kn->kn_filter) { 490 case EVFILT_READ: 491 klist = &pi->pi_sel.sel_klist; 492 kn->kn_fop = &putter_filtops; 493 kn->kn_hook = pi; 494 495 mutex_enter(&pi_mtx); 496 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 497 mutex_exit(&pi_mtx); 498 499 break; 500 case EVFILT_WRITE: 501 kn->kn_fop = &seltrue_filtops; 502 break; 503 default: 504 KERNEL_UNLOCK_ONE(NULL); 505 return EINVAL; 506 } 507 508 KERNEL_UNLOCK_ONE(NULL); 509 return 0; 510 } 511 512 int 513 puttercdopen(dev_t dev, int flags, int fmt, struct lwp *l) 514 { 515 struct putter_instance *pi; 516 file_t *fp; 517 int error, fd, idx; 518 proc_t *p; 519 520 p = curproc; 521 pi = kmem_alloc(sizeof(struct putter_instance), KM_SLEEP); 522 mutex_enter(&pi_mtx); 523 idx = get_pi_idx(pi); 524 525 pi->pi_pid = p->p_pid; 526 pi->pi_idx = idx; 527 pi->pi_curput = NULL; 528 pi->pi_curres = 0; 529 pi->pi_curopaq = NULL; 530 getnanotime(&pi->pi_btime); 531 pi->pi_atime = pi->pi_mtime = pi->pi_btime; 532 selinit(&pi->pi_sel); 533 mutex_exit(&pi_mtx); 534 535 if ((error = fd_allocfile(&fp, &fd)) != 0) 536 goto bad1; 537 538 if ((error = putter_configure(dev, flags, fmt, fd)) != 0) 539 goto bad2; 540 541 DPRINTF(("puttercdopen: registered embryonic pmp for pid: %d\n", 542 pi->pi_pid)); 543 544 error = fd_clone(fp, fd, FREAD|FWRITE, &putter_fileops, pi); 545 KASSERT(error == EMOVEFD); 546 return error; 547 548 bad2: 549 fd_abort(p, fp, fd); 550 bad1: 551 putter_detach(pi); 552 kmem_free(pi, sizeof(struct putter_instance)); 553 return error; 554 } 555 556 int 557 puttercdclose(dev_t dev, int flags, int fmt, struct lwp *l) 558 { 559 560 panic("puttercdclose impossible\n"); 561 562 return 0; 563 } 564 565 566 /* 567 * Set the private structure for the file descriptor. This is 568 * typically done immediately when the counterpart has knowledge 569 * about the private structure's address and the file descriptor 570 * (e.g. vfs mount routine). 571 * 572 * We only want to make sure that the caller had the right to open the 573 * device, we don't so much care about which context it gets in case 574 * the same process opened multiple (since they are equal at this point). 575 */ 576 struct putter_instance * 577 putter_attach(pid_t pid, int fd, void *ppriv, struct putter_ops *pop) 578 { 579 struct putter_instance *pi = NULL; 580 581 mutex_enter(&pi_mtx); 582 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) { 583 if (pi->pi_pid == pid && pi->pi_private == PUTTER_EMBRYO) { 584 pi->pi_private = ppriv; 585 pi->pi_fd = fd; 586 pi->pi_pop = pop; 587 break; 588 } 589 } 590 mutex_exit(&pi_mtx); 591 592 DPRINTF(("putter_setprivate: pi at %p (%d/%d)\n", pi, 593 pi ? pi->pi_pid : 0, pi ? pi->pi_fd : 0)); 594 595 return pi; 596 } 597 598 /* 599 * Remove fp <-> private mapping. 600 */ 601 void 602 putter_detach(struct putter_instance *pi) 603 { 604 605 mutex_enter(&pi_mtx); 606 TAILQ_REMOVE(&putter_ilist, pi, pi_entries); 607 pi->pi_private = PUTTER_DEAD; 608 mutex_exit(&pi_mtx); 609 seldestroy(&pi->pi_sel); 610 611 DPRINTF(("putter_nukebypmp: nuked %p\n", pi)); 612 } 613 614 void 615 putter_notify(struct putter_instance *pi) 616 { 617 618 selnotify(&pi->pi_sel, 0, 0); 619 } 620 621 /* search sorted list of instances for free minor, sorted insert arg */ 622 static int 623 get_pi_idx(struct putter_instance *pi_i) 624 { 625 struct putter_instance *pi; 626 int i; 627 628 KASSERT(mutex_owned(&pi_mtx)); 629 630 i = 0; 631 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) { 632 if (i != pi->pi_idx) 633 break; 634 i++; 635 } 636 637 pi_i->pi_private = PUTTER_EMBRYO; 638 639 if (pi == NULL) 640 TAILQ_INSERT_TAIL(&putter_ilist, pi_i, pi_entries); 641 else 642 TAILQ_INSERT_BEFORE(pi, pi_i, pi_entries); 643 644 return i; 645 } 646 647 MODULE(MODULE_CLASS_DRIVER, putter, NULL); 648 649 static int 650 putter_modcmd(modcmd_t cmd, void *arg) 651 { 652 #ifdef _MODULE 653 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 654 655 switch (cmd) { 656 case MODULE_CMD_INIT: 657 putterattach(); 658 return devsw_attach("putter", NULL, &bmajor, 659 &putter_cdevsw, &cmajor); 660 case MODULE_CMD_FINI: 661 return ENOTTY; /* XXX: putterdetach */ 662 default: 663 return ENOTTY; 664 } 665 #else 666 if (cmd == MODULE_CMD_INIT) 667 return 0; 668 return ENOTTY; 669 #endif 670 } 671