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