Annotation of src/sys/fs/puffs/puffs_transport.c, Revision 1.1
1.1 ! pooka 1: /* $NetBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
! 5: *
! 6: * Development of this software was supported by the
! 7: * Google Summer of Code program and the Ulla Tuominen Foundation.
! 8: * The Google SoC project was mentored by Bill Studenmund.
! 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: * 3. The name of the company nor the name of the author may be used to
! 19: * endorse or promote products derived from this software without specific
! 20: * prior written permission.
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
! 23: * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 24: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
! 25: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! 28: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 32: * SUCH DAMAGE.
! 33: */
! 34:
! 35: #include <sys/param.h>
! 36: #include <sys/conf.h>
! 37: #include <sys/file.h>
! 38: #include <sys/filedesc.h>
! 39: #include <sys/malloc.h>
! 40: #include <sys/poll.h>
! 41: #include <sys/socketvar.h>
! 42:
! 43: #include <fs/puffs/puffs_sys.h>
! 44:
! 45: #include <miscfs/syncfs/syncfs.h> /* XXX: for syncer_lock reference */
! 46:
! 47:
! 48: /*
! 49: * puffs instance structures. these are always allocated and freed
! 50: * from the context of the device node / fileop code.
! 51: */
! 52: struct puffs_instance {
! 53: pid_t pi_pid;
! 54: int pi_idx;
! 55: int pi_fd;
! 56: struct puffs_mount *pi_pmp;
! 57: struct selinfo pi_sel;
! 58:
! 59: TAILQ_ENTRY(puffs_instance) pi_entries;
! 60: };
! 61: #define PMP_EMBRYO ((struct puffs_mount *)-1) /* before mount */
! 62: #define PMP_DEAD ((struct puffs_mount *)-2) /* goner */
! 63:
! 64: static TAILQ_HEAD(, puffs_instance) puffs_ilist
! 65: = TAILQ_HEAD_INITIALIZER(puffs_ilist);
! 66:
! 67: /* protects both the list and the contents of the list elements */
! 68: static struct simplelock pi_lock = SIMPLELOCK_INITIALIZER;
! 69:
! 70: static int get_pi_idx(struct puffs_instance *);
! 71:
! 72:
! 73: /*
! 74: * fd routines, for cloner
! 75: */
! 76: static int puffs_fop_read(struct file *, off_t *, struct uio *,
! 77: kauth_cred_t, int);
! 78: static int puffs_fop_write(struct file *, off_t *, struct uio *,
! 79: kauth_cred_t, int);
! 80: static int puffs_fop_ioctl(struct file*, u_long, void *, struct lwp *);
! 81: static int puffs_fop_poll(struct file *, int, struct lwp *);
! 82: static int puffs_fop_close(struct file *, struct lwp *);
! 83: static int puffs_fop_kqfilter(struct file *, struct knote *);
! 84:
! 85:
! 86: static const struct fileops puffs_fileops = {
! 87: puffs_fop_read,
! 88: puffs_fop_write,
! 89: puffs_fop_ioctl,
! 90: fnullop_fcntl,
! 91: puffs_fop_poll,
! 92: fbadop_stat,
! 93: puffs_fop_close,
! 94: puffs_fop_kqfilter
! 95: };
! 96:
! 97: static int
! 98: puffs_fop_read(struct file *fp, off_t *off, struct uio *uio,
! 99: kauth_cred_t cred, int flags)
! 100: {
! 101:
! 102: return ENODEV;
! 103: }
! 104:
! 105: static int
! 106: puffs_fop_write(struct file *fp, off_t *off, struct uio *uio,
! 107: kauth_cred_t cred, int flags)
! 108: {
! 109:
! 110: return ENODEV;
! 111: }
! 112:
! 113: /*
! 114: * Poll query interface. The question is only if an event
! 115: * can be read from us (and by read I mean ioctl... ugh).
! 116: */
! 117: #define PUFFPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
! 118: static int
! 119: puffs_fop_poll(struct file *fp, int events, struct lwp *l)
! 120: {
! 121: struct puffs_mount *pmp = FPTOPMP(fp);
! 122: int revents;
! 123:
! 124: if (pmp == PMP_EMBRYO || pmp == PMP_DEAD) {
! 125: printf("puffs_fop_ioctl: puffs %p, not mounted\n", pmp);
! 126: return ENOENT;
! 127: }
! 128:
! 129: revents = events & (POLLOUT | POLLWRNORM | POLLWRBAND);
! 130: if ((events & PUFFPOLL_EVSET) == 0)
! 131: return revents;
! 132:
! 133: /* check queue */
! 134: simple_lock(&pmp->pmp_lock);
! 135: if (!TAILQ_EMPTY(&pmp->pmp_req_touser))
! 136: revents |= PUFFPOLL_EVSET;
! 137: else
! 138: selrecord(l, pmp->pmp_sel);
! 139: simple_unlock(&pmp->pmp_lock);
! 140:
! 141: return revents;
! 142: }
! 143:
! 144: /*
! 145: * device close = forced unmount.
! 146: *
! 147: * unmounting is a frightfully complex operation to avoid races
! 148: *
! 149: * XXX: if userspace is terminated by a signal, this will be
! 150: * called only after the signal is delivered (i.e. after someone tries
! 151: * to access the file system). Also, the first one for a delivery
! 152: * will get a free bounce-bounce ride before it can be notified
! 153: * that the fs is dead. I'm not terribly concerned about optimizing
! 154: * this for speed ...
! 155: */
! 156: static int
! 157: puffs_fop_close(struct file *fp, struct lwp *l)
! 158: {
! 159: struct puffs_instance *pi;
! 160: struct puffs_mount *pmp;
! 161: struct mount *mp;
! 162: int gone;
! 163:
! 164: DPRINTF(("puffs_fop_close: device closed, force filesystem unmount\n"));
! 165:
! 166: simple_lock(&pi_lock);
! 167: pmp = FPTOPMP(fp);
! 168: /*
! 169: * First check if the fs was never mounted. In that case
! 170: * remove the instance from the list. If mount is attempted later,
! 171: * it will simply fail.
! 172: */
! 173: if (pmp == PMP_EMBRYO) {
! 174: pi = FPTOPI(fp);
! 175: TAILQ_REMOVE(&puffs_ilist, pi, pi_entries);
! 176: simple_unlock(&pi_lock);
! 177: FREE(pi, M_PUFFS);
! 178: return 0;
! 179: }
! 180:
! 181: /*
! 182: * Next, analyze unmount was called and the instance is dead.
! 183: * In this case we can just free the structure and go home, it
! 184: * was removed from the list by puffs_nukebypmp().
! 185: */
! 186: if (pmp == PMP_DEAD) {
! 187: /* would be nice, but don't have a reference to it ... */
! 188: /* KASSERT(pmp_status == PUFFSTAT_DYING); */
! 189: simple_unlock(&pi_lock);
! 190: pi = FPTOPI(fp);
! 191: FREE(pi, M_PUFFS);
! 192: return 0;
! 193: }
! 194:
! 195: /*
! 196: * So we have a reference. Proceed to unwrap the file system.
! 197: */
! 198: mp = PMPTOMP(pmp);
! 199: simple_unlock(&pi_lock);
! 200:
! 201: /*
! 202: * Free the waiting callers before proceeding any further.
! 203: * The syncer might be jogging around in this file system
! 204: * currently. If we allow it to go to the userspace of no
! 205: * return while trying to get the syncer lock, well ...
! 206: * synclk: I feel happy, I feel fine.
! 207: * lockmgr: You're not fooling anyone, you know.
! 208: */
! 209: puffs_userdead(pmp);
! 210:
! 211: /*
! 212: * Detach from VFS. First do necessary XXX-dance (from
! 213: * sys_unmount() & other callers of dounmount()
! 214: *
! 215: * XXX Freeze syncer. Must do this before locking the
! 216: * mount point. See dounmount() for details.
! 217: *
! 218: * XXX2: take a reference to the mountpoint before starting to
! 219: * wait for syncer_lock. Otherwise the mointpoint can be
! 220: * wiped out while we wait.
! 221: */
! 222: simple_lock(&mp->mnt_slock);
! 223: mp->mnt_wcnt++;
! 224: simple_unlock(&mp->mnt_slock);
! 225:
! 226: lockmgr(&syncer_lock, LK_EXCLUSIVE, NULL);
! 227:
! 228: simple_lock(&mp->mnt_slock);
! 229: mp->mnt_wcnt--;
! 230: if (mp->mnt_wcnt == 0)
! 231: wakeup(&mp->mnt_wcnt);
! 232: gone = mp->mnt_iflag & IMNT_GONE;
! 233: simple_unlock(&mp->mnt_slock);
! 234: if (gone) {
! 235: lockmgr(&syncer_lock, LK_RELEASE, NULL);
! 236: return 0;
! 237: }
! 238:
! 239: /*
! 240: * microscopic race condition here (although not with the current
! 241: * kernel), but can't really fix it without starting a crusade
! 242: * against vfs_busy(), so let it be, let it be, let it be
! 243: */
! 244:
! 245: /*
! 246: * The only way vfs_busy() will fail for us is if the filesystem
! 247: * is already a goner.
! 248: * XXX: skating on the thin ice of modern calling conventions ...
! 249: */
! 250: if (vfs_busy(mp, 0, 0)) {
! 251: lockmgr(&syncer_lock, LK_RELEASE, NULL);
! 252: return 0;
! 253: }
! 254:
! 255: /* Once we have the mount point, unmount() can't interfere */
! 256: dounmount(mp, MNT_FORCE, l);
! 257:
! 258: return 0;
! 259: }
! 260:
! 261: static int
! 262: puffs_fop_ioctl(struct file *fp, u_long cmd, void *data, struct lwp *l)
! 263: {
! 264: struct puffs_mount *pmp = FPTOPMP(fp);
! 265: int rv;
! 266:
! 267: if (pmp == PMP_EMBRYO || pmp == PMP_DEAD) {
! 268: printf("puffs_fop_ioctl: puffs %p, not mounted\n", pmp);
! 269: return ENOENT;
! 270: }
! 271:
! 272: switch (cmd) {
! 273: case PUFFSGETOP:
! 274: rv = puffs_getop(pmp, data, fp->f_flag & FNONBLOCK);
! 275: break;
! 276:
! 277: case PUFFSPUTOP:
! 278: rv = puffs_putop(pmp, data);
! 279: break;
! 280:
! 281: #if 0 /* bitrot */
! 282: case PUFFSSIZEOP:
! 283: rv = puffssizeop(pmp, data);
! 284: break;
! 285: #endif
! 286:
! 287: case PUFFSSTARTOP:
! 288: rv = puffs_start2(pmp, data);
! 289: break;
! 290:
! 291: /* already done in sys_ioctl() */
! 292: case FIONBIO:
! 293: rv = 0;
! 294: break;
! 295:
! 296: default:
! 297: rv = EINVAL;
! 298: break;
! 299: }
! 300:
! 301: return rv;
! 302: }
! 303:
! 304: /* kqueue stuff */
! 305:
! 306: static void
! 307: filt_puffsdetach(struct knote *kn)
! 308: {
! 309: struct puffs_instance *pi = kn->kn_hook;
! 310:
! 311: simple_lock(&pi_lock);
! 312: SLIST_REMOVE(&pi->pi_sel.sel_klist, kn, knote, kn_selnext);
! 313: simple_unlock(&pi_lock);
! 314: }
! 315:
! 316: static int
! 317: filt_puffsioctl(struct knote *kn, long hint)
! 318: {
! 319: struct puffs_instance *pi = kn->kn_hook;
! 320: struct puffs_mount *pmp;
! 321: int error;
! 322:
! 323: error = 0;
! 324: simple_lock(&pi_lock);
! 325: pmp = pi->pi_pmp;
! 326: if (pmp == PMP_EMBRYO || pmp == PMP_DEAD)
! 327: error = 1;
! 328: simple_unlock(&pi_lock);
! 329: if (error)
! 330: return 0;
! 331:
! 332: simple_lock(&pmp->pmp_lock);
! 333: kn->kn_data = pmp->pmp_req_touser_waiters;
! 334: simple_unlock(&pmp->pmp_lock);
! 335:
! 336: return kn->kn_data != 0;
! 337: }
! 338:
! 339: static const struct filterops puffsioctl_filtops =
! 340: { 1, NULL, filt_puffsdetach, filt_puffsioctl };
! 341:
! 342: static int
! 343: puffs_fop_kqfilter(struct file *fp, struct knote *kn)
! 344: {
! 345: struct puffs_instance *pi = fp->f_data;
! 346: struct klist *klist;
! 347:
! 348: if (kn->kn_filter != EVFILT_READ)
! 349: return 1;
! 350:
! 351: klist = &pi->pi_sel.sel_klist;
! 352: kn->kn_fop = &puffsioctl_filtops;
! 353: kn->kn_hook = pi;
! 354:
! 355: simple_lock(&pi_lock);
! 356: SLIST_INSERT_HEAD(klist, kn, kn_selnext);
! 357: simple_unlock(&pi_lock);
! 358:
! 359: return 0;
! 360: }
! 361:
! 362:
! 363: /*
! 364: * Device routines
! 365: */
! 366:
! 367: dev_type_open(puffscdopen);
! 368: dev_type_close(puffscdclose);
! 369: dev_type_ioctl(puffscdioctl);
! 370:
! 371: /* dev */
! 372: const struct cdevsw puffs_cdevsw = {
! 373: puffscdopen, puffscdclose, noread, nowrite,
! 374: noioctl, nostop, notty, nopoll,
! 375: nommap, nokqfilter, D_OTHER
! 376: };
! 377: int
! 378: puffscdopen(dev_t dev, int flags, int fmt, struct lwp *l)
! 379: {
! 380: struct puffs_instance *pi;
! 381: struct file *fp;
! 382: int error, fd, idx;
! 383:
! 384: /*
! 385: * XXX: decide on some security model and check permissions
! 386: */
! 387:
! 388: if (minor(dev) != PUFFS_CLONER)
! 389: return ENXIO;
! 390:
! 391: if ((error = falloc(l, &fp, &fd)) != 0)
! 392: return error;
! 393:
! 394: MALLOC(pi, struct puffs_instance *, sizeof(struct puffs_instance),
! 395: M_PUFFS, M_WAITOK | M_ZERO);
! 396:
! 397: simple_lock(&pi_lock);
! 398: idx = get_pi_idx(pi);
! 399: if (idx == PUFFS_CLONER) {
! 400: simple_unlock(&pi_lock);
! 401: FREE(pi, M_PUFFS);
! 402: FILE_UNUSE(fp, l);
! 403: ffree(fp);
! 404: return EBUSY;
! 405: }
! 406:
! 407: pi->pi_pid = l->l_proc->p_pid;
! 408: pi->pi_idx = idx;
! 409: simple_unlock(&pi_lock);
! 410:
! 411: DPRINTF(("puffscdopen: registered embryonic pmp for pid: %d\n",
! 412: pi->pi_pid));
! 413:
! 414: return fdclone(l, fp, fd, FREAD|FWRITE, &puffs_fileops, pi);
! 415: }
! 416:
! 417: int
! 418: puffscdclose(dev_t dev, int flags, int fmt, struct lwp *l)
! 419: {
! 420:
! 421: panic("puffscdclose impossible\n");
! 422:
! 423: return 0;
! 424: }
! 425:
! 426:
! 427: /*
! 428: * Set puffs_mount -pointer. Called from puffs_mount(), which is the
! 429: * earliest place that knows about this.
! 430: *
! 431: * We only want to make sure that the caller had the right to open the
! 432: * device, we don't so much care about which context it gets in case
! 433: * the same process opened multiple (since they are equal at this point).
! 434: */
! 435: int
! 436: puffs_setpmp(pid_t pid, int fd, struct puffs_mount *pmp)
! 437: {
! 438: struct puffs_instance *pi;
! 439: int rv = 1;
! 440:
! 441: simple_lock(&pi_lock);
! 442: TAILQ_FOREACH(pi, &puffs_ilist, pi_entries) {
! 443: if (pi->pi_pid == pid && pi->pi_pmp == PMP_EMBRYO) {
! 444: pi->pi_pmp = pmp;
! 445: pi->pi_fd = fd;
! 446: pmp->pmp_sel = &pi->pi_sel;
! 447: rv = 0;
! 448: break;
! 449: }
! 450: }
! 451: simple_unlock(&pi_lock);
! 452:
! 453: return rv;
! 454: }
! 455:
! 456: /*
! 457: * Remove mount point from list of instances. Called from unmount.
! 458: */
! 459: void
! 460: puffs_nukebypmp(struct puffs_mount *pmp)
! 461: {
! 462: struct puffs_instance *pi;
! 463:
! 464: simple_lock(&pi_lock);
! 465: TAILQ_FOREACH(pi, &puffs_ilist, pi_entries) {
! 466: if (pi->pi_pmp == pmp) {
! 467: TAILQ_REMOVE(&puffs_ilist, pi, pi_entries);
! 468: break;
! 469: }
! 470: }
! 471: if (pi)
! 472: pi->pi_pmp = PMP_DEAD;
! 473:
! 474: #ifdef DIAGNOSTIC
! 475: else
! 476: panic("puffs_nukebypmp: invalid puffs_mount\n");
! 477: #endif /* DIAGNOSTIC */
! 478:
! 479: simple_unlock(&pi_lock);
! 480:
! 481: DPRINTF(("puffs_nukebypmp: nuked %p\n", pi));
! 482: }
! 483:
! 484: /* search sorted list of instances for free minor, sorted insert arg */
! 485: static int
! 486: get_pi_idx(struct puffs_instance *pi_i)
! 487: {
! 488: struct puffs_instance *pi;
! 489: int i;
! 490:
! 491: i = 0;
! 492: TAILQ_FOREACH(pi, &puffs_ilist, pi_entries) {
! 493: if (i == PUFFS_CLONER)
! 494: return PUFFS_CLONER;
! 495: if (i != pi->pi_idx)
! 496: break;
! 497: i++;
! 498: }
! 499:
! 500: pi_i->pi_pmp = PMP_EMBRYO;
! 501:
! 502: if (pi == NULL)
! 503: TAILQ_INSERT_TAIL(&puffs_ilist, pi_i, pi_entries);
! 504: else
! 505: TAILQ_INSERT_BEFORE(pi, pi_i, pi_entries);
! 506:
! 507: return i;
! 508: }
CVSweb <webmaster@jp.NetBSD.org>