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