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