[BACK]Return to puffs_subr.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / fs / puffs

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/fs/puffs/puffs_subr.c between version 1.44.2.4 and 1.45

version 1.44.2.4, 2007/12/09 19:38:10 version 1.45, 2007/09/04 00:11:38
Line 1 
Line 1 
 /*      $NetBSD$        */  /*      $NetBSD$        */
   
 /*  /*
  * Copyright (c) 2006, 2007  Antti Kantee.  All Rights Reserved.   * Copyright (c) 2005, 2006  Antti Kantee.  All Rights Reserved.
  *   *
  * Development of this software was supported by the   * Development of this software was supported by the
  * Ulla Tuominen Foundation and the Finnish Cultural Foundation.   * Google Summer of Code program and the Ulla Tuominen Foundation.
    * The Google SoC project was mentored by Bill Studenmund.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 32 
Line 33 
 __KERNEL_RCSID(0, "$NetBSD$");  __KERNEL_RCSID(0, "$NetBSD$");
   
 #include <sys/param.h>  #include <sys/param.h>
   #include <sys/conf.h>
   #include <sys/hash.h>
   #include <sys/kauth.h>
 #include <sys/malloc.h>  #include <sys/malloc.h>
 #include <sys/mount.h>  #include <sys/mount.h>
 #include <sys/namei.h>  #include <sys/namei.h>
 #include <sys/poll.h>  #include <sys/poll.h>
   #include <sys/socketvar.h>
   #include <sys/vnode.h>
 #include <sys/proc.h>  #include <sys/proc.h>
   
 #include <fs/puffs/puffs_msgif.h>  #include <fs/puffs/puffs_msgif.h>
 #include <fs/puffs/puffs_sys.h>  #include <fs/puffs/puffs_sys.h>
   
   #include <miscfs/genfs/genfs_node.h>
   #include <miscfs/specfs/specdev.h>
   
   struct pool puffs_pnpool;
   
 #ifdef PUFFSDEBUG  #ifdef PUFFSDEBUG
 int puffsdebug;  int puffsdebug;
 #endif  #endif
   
   static __inline struct puffs_node_hashlist
           *puffs_cookie2hashlist(struct puffs_mount *, void *);
   static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *);
   
   static void puffs_gop_size(struct vnode *, off_t, off_t *, int);
   static void puffs_gop_markupdate(struct vnode *, int);
   
   static const struct genfs_ops puffs_genfsops = {
           .gop_size = puffs_gop_size,
           .gop_write = genfs_gop_write,
           .gop_markupdate = puffs_gop_markupdate,
   #if 0
           .gop_alloc, should ask userspace
   #endif
   };
   
   /*
    * Grab a vnode, intialize all the puffs-dependant stuff.
    */
   int
   puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
           voff_t vsize, dev_t rdev, struct vnode **vpp)
   {
           struct puffs_mount *pmp;
           struct vnode *vp, *nvp;
           struct puffs_node *pnode;
           struct puffs_node_hashlist *plist;
           int error;
   
           if (type <= VNON || type >= VBAD)
                   return EINVAL;
           if (vsize == VSIZENOTSET)
                   return EINVAL;
   
           pmp = MPTOPUFFSMP(mp);
   
           /*
            * XXX: there is a deadlock condition between vfs_busy() and
            * vnode locks.  For an unmounting file system the mountpoint
            * is frozen, but in unmount(FORCE) vflush() wants to access all
            * of the vnodes.  If we are here waiting for the mountpoint
            * lock while holding on to a vnode lock, well, we ain't
            * just pining for the fjords anymore.  If we release the
            * vnode lock, we will be in the situation "mount point
            * is dying" and panic() will ensue in insmntque.  So as a
            * temporary workaround, get a vnode without putting it on
            * the mount point list, check if mount point is still alive
            * and kicking and only then add the vnode to the list.
            */
           error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp);
           if (error)
                   return error;
           vp->v_vnlock = NULL;
           vp->v_type = type;
   
           /*
            * Check what mount point isn't going away.  This will work
            * until we decide to remove biglock or make the kernel
            * preemptive.  But hopefully the real problem will be fixed
            * by then.
            *
            * XXX: yes, should call vfs_busy(), but thar be rabbits with
            * vicious streaks a mile wide ...
            */
           if (mp->mnt_iflag & IMNT_UNMOUNT) {
                   DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
                       "vnode for cookie %p\n", mp, cookie));
                   ungetnewvnode(vp);
                   return ENXIO;
           }
   
           /* So it's not dead yet.. good.. inform new vnode of its master */
           simple_lock(&mntvnode_slock);
           TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
           simple_unlock(&mntvnode_slock);
           vp->v_mount = mp;
   
           /*
            * clerical tasks & footwork
            */
   
           /* default size */
           uvm_vnp_setsize(vp, 0);
   
           /* dances based on vnode type. almost ufs_vinit(), but not quite */
           switch (type) {
           case VCHR:
           case VBLK:
                   /*
                    * replace vnode operation vector with the specops vector.
                    * our user server has very little control over the node
                    * if it decides its a character or block special file
                    */
                   vp->v_op = puffs_specop_p;
   
                   /* do the standard checkalias-dance */
                   if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
                           /*
                            * found: release & unallocate aliased
                            * old (well, actually, new) node
                            */
                           vp->v_op = spec_vnodeop_p;
                           vp->v_flag &= ~VLOCKSWORK;
                           vrele(vp);
                           vgone(vp); /* cya */
   
                           /* init "new" vnode */
                           vp = nvp;
                           vp->v_vnlock = NULL;
                           vp->v_mount = mp;
                   }
                   break;
   
           case VFIFO:
                   vp->v_op = puffs_fifoop_p;
                   break;
   
           case VREG:
                   uvm_vnp_setsize(vp, vsize);
                   break;
   
           case VDIR:
           case VLNK:
           case VSOCK:
                   break;
           default:
   #ifdef DIAGNOSTIC
                   panic("puffs_getvnode: invalid vtype %d", type);
   #endif
                   break;
           }
   
           pnode = pool_get(&puffs_pnpool, PR_WAITOK);
           memset(pnode, 0, sizeof(struct puffs_node));
   
           pnode->pn_cookie = cookie;
           pnode->pn_refcount = 1;
   
           mutex_init(&pnode->pn_mtx, MUTEX_DEFAULT, IPL_NONE);
           SLIST_INIT(&pnode->pn_sel.sel_klist);
   
           plist = puffs_cookie2hashlist(pmp, cookie);
           LIST_INSERT_HEAD(plist, pnode, pn_hashent);
           vp->v_data = pnode;
           vp->v_type = type;
           pnode->pn_vp = vp;
           pnode->pn_serversize = vsize;
   
           genfs_node_init(vp, &puffs_genfsops);
           *vpp = vp;
   
           DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
               pnode, pnode->pn_cookie));
   
           return 0;
   }
   
   /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
   int
   puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
           void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
   {
           struct puffs_mount *pmp = MPTOPUFFSMP(mp);
           struct vnode *vp;
           int error;
   
           /* userspace probably has this as a NULL op */
           if (cookie == NULL) {
                   error = EOPNOTSUPP;
                   return error;
           }
   
           /*
            * Check for previous node with the same designation.
            * Explicitly check the root node cookie, since it might be
            * reclaimed from the kernel when this check is made.
            *
            * XXX: technically this error check should punish the fs,
            * not the caller.
            */
           mutex_enter(&pmp->pmp_lock);
           if (cookie == pmp->pmp_root_cookie
               || puffs_cookie2pnode(pmp, cookie) != NULL) {
                   mutex_exit(&pmp->pmp_lock);
                   error = EEXIST;
                   return error;
           }
           mutex_exit(&pmp->pmp_lock);
   
           error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
           if (error)
                   return error;
   
           vp->v_type = type;
           vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
           *vpp = vp;
   
           if ((cnp->cn_flags & MAKEENTRY) && PUFFS_USE_NAMECACHE(pmp))
                   cache_enter(dvp, vp, cnp);
   
           return 0;
   }
   
   /*
    * Release pnode structure which dealing with references to the
    * puffs_node instead of the vnode.  Can't use vref()/vrele() on
    * the vnode there, since that causes the lovely VOP_INACTIVE(),
    * which in turn causes the lovely deadlock when called by the one
    * who is supposed to handle it.
    */
   void
   puffs_releasenode(struct puffs_node *pn)
   {
   
           mutex_enter(&pn->pn_mtx);
           if (--pn->pn_refcount == 0) {
                   mutex_exit(&pn->pn_mtx);
                   mutex_destroy(&pn->pn_mtx);
                   pool_put(&puffs_pnpool, pn);
           } else {
                   mutex_exit(&pn->pn_mtx);
           }
   }
   
   /*
    * Add reference to node.
    *  mutex held on entry and return
    */
   void
   puffs_referencenode(struct puffs_node *pn)
   {
   
           KASSERT(mutex_owned(&pn->pn_mtx));
           pn->pn_refcount++;
   }
   
   void
   puffs_putvnode(struct vnode *vp)
   {
           struct puffs_mount *pmp;
           struct puffs_node *pnode;
   
           pmp = VPTOPUFFSMP(vp);
           pnode = VPTOPP(vp);
   
   #ifdef DIAGNOSTIC
           if (vp->v_tag != VT_PUFFS)
                   panic("puffs_putvnode: %p not a puffs vnode", vp);
   #endif
   
           LIST_REMOVE(pnode, pn_hashent);
           genfs_node_destroy(vp);
           puffs_releasenode(pnode);
           vp->v_data = NULL;
   
           return;
   }
   
   static __inline struct puffs_node_hashlist *
   puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie)
   {
           uint32_t hash;
   
           hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT);
           return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
   }
   
   /*
    * Translate cookie to puffs_node.  Caller must hold mountpoint
    * lock and it will be held upon return.
    */
   static struct puffs_node *
   puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie)
   {
           struct puffs_node_hashlist *plist;
           struct puffs_node *pnode;
   
           plist = puffs_cookie2hashlist(pmp, cookie);
           LIST_FOREACH(pnode, plist, pn_hashent) {
                   if (pnode->pn_cookie == cookie)
                           break;
           }
   
           return pnode;
   }
   
   /*
    * Make sure root vnode exists and reference it.  Does NOT lock.
    */
   static int
   puffs_makeroot(struct puffs_mount *pmp)
   {
           struct vnode *vp;
           int rv;
   
           /*
            * pmp_lock must be held if vref()'ing or vrele()'ing the
            * root vnode.  the latter is controlled by puffs_inactive().
            *
            * pmp_root is set here and cleared in puffs_reclaim().
            */
    retry:
           mutex_enter(&pmp->pmp_lock);
           vp = pmp->pmp_root;
           if (vp) {
                   simple_lock(&vp->v_interlock);
                   mutex_exit(&pmp->pmp_lock);
                   if (vget(vp, LK_INTERLOCK) == 0)
                           return 0;
           } else
                   mutex_exit(&pmp->pmp_lock);
   
           /*
            * So, didn't have the magic root vnode available.
            * No matter, grab another an stuff it with the cookie.
            */
           if ((rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie,
               pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp)))
                   return rv;
   
           /*
            * Someone magically managed to race us into puffs_getvnode?
            * Put our previous new vnode back and retry.
            */
           mutex_enter(&pmp->pmp_lock);
           if (pmp->pmp_root) {
                   mutex_exit(&pmp->pmp_lock);
                   puffs_putvnode(vp);
                   goto retry;
           }
   
           /* store cache */
           vp->v_flag = VROOT;
           pmp->pmp_root = vp;
           mutex_exit(&pmp->pmp_lock);
   
           return 0;
   }
   
   /*
    * Locate the in-kernel vnode based on the cookie received given
    * from userspace.  Returns a vnode, if found, NULL otherwise.
    * The parameter "lock" control whether to lock the possible or
    * not.  Locking always might cause us to lock against ourselves
    * in situations where we want the vnode but don't care for the
    * vnode lock, e.g. file server issued putpages.
    */
   int
   puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock,
           struct vnode **vpp)
   {
           struct puffs_node *pnode;
           struct vnode *vp;
           int vgetflags, rv;
   
           /*
            * Handle root in a special manner, since we want to make sure
            * pmp_root is properly set.
            */
           if (cookie == pmp->pmp_root_cookie) {
                   if ((rv = puffs_makeroot(pmp)))
                           return rv;
                   if (lock)
                           vn_lock(pmp->pmp_root, LK_EXCLUSIVE | LK_RETRY);
   
                   *vpp = pmp->pmp_root;
                   return 0;
           }
   
           mutex_enter(&pmp->pmp_lock);
           pnode = puffs_cookie2pnode(pmp, cookie);
   
           if (pnode == NULL) {
                   mutex_exit(&pmp->pmp_lock);
                   return ENOENT;
           }
   
           vp = pnode->pn_vp;
           simple_lock(&vp->v_interlock);
           mutex_exit(&pmp->pmp_lock);
   
           vgetflags = LK_INTERLOCK;
           if (lock)
                   vgetflags |= LK_EXCLUSIVE | LK_RETRY;
           if ((rv = vget(vp, vgetflags)))
                   return rv;
   
           *vpp = vp;
           return 0;
   }
   
 void  void
 puffs_makecn(struct puffs_kcn *pkcn, struct puffs_kcred *pkcr,  puffs_makecn(struct puffs_kcn *pkcn, struct puffs_kcred *pkcr,
         const struct componentname *cn, int full)          struct puffs_kcid *pkcid, const struct componentname *cn, int full)
 {  {
   
         pkcn->pkcn_nameiop = cn->cn_nameiop;          pkcn->pkcn_nameiop = cn->cn_nameiop;
         pkcn->pkcn_flags = cn->cn_flags;          pkcn->pkcn_flags = cn->cn_flags;
           puffs_cidcvt(pkcid, cn->cn_lwp);
   
         if (full) {          if (full) {
                 (void)strcpy(pkcn->pkcn_name, cn->cn_nameptr);                  (void)strcpy(pkcn->pkcn_name, cn->cn_nameptr);
Line 87  puffs_credcvt(struct puffs_kcred *pkcr, 
Line 490  puffs_credcvt(struct puffs_kcred *pkcr, 
 }  }
   
 void  void
 puffs_parkdone_asyncbioread(struct puffs_mount *pmp,  puffs_cidcvt(struct puffs_kcid *pkcid, const struct lwp *l)
         struct puffs_req *preq, void *arg)  
 {  {
         struct puffs_vnmsg_read *read_msg = (void *)preq;  
         struct buf *bp = arg;  
         size_t moved;  
   
         DPRINTF(("%s\n", __func__));          if (l) {
                   pkcid->pkcid_type = PUFFCID_TYPE_REAL;
                   pkcid->pkcid_pid = l->l_proc->p_pid;
                   pkcid->pkcid_lwpid = l->l_lid;
           } else {
                   pkcid->pkcid_type = PUFFCID_TYPE_FAKE;
                   pkcid->pkcid_pid = 0;
                   pkcid->pkcid_lwpid = 0;
           }
   }
   
         bp->b_error = checkerr(pmp, preq->preq_rv, __func__);  static void
         if (bp->b_error == 0) {  puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
                 if (read_msg->pvnr_resid > bp->b_bcount) {          int flags)
                         puffs_senderr(pmp, PUFFS_ERR_READ, E2BIG,  {
                             "resid grew", preq->preq_cookie);  
                         bp->b_error = E2BIG;  
                 } else {  
                         moved = bp->b_bcount - read_msg->pvnr_resid;  
                         bp->b_resid = read_msg->pvnr_resid;  
   
                         memcpy(bp->b_data, read_msg->pvnr_data, moved);          *eobp = size;
                 }  }
   
   static void
   puffs_gop_markupdate(struct vnode *vp, int flags)
   {
           int uflags = 0;
   
           if (flags & GOP_UPDATE_ACCESSED)
                   uflags |= PUFFS_UPDATEATIME;
           if (flags & GOP_UPDATE_MODIFIED)
                   uflags |= PUFFS_UPDATEMTIME;
   
           puffs_updatenode(vp, uflags);
   }
   
   void
   puffs_updatenode(struct vnode *vp, int flags)
   {
           struct puffs_node *pn;
           struct timespec ts;
   
           if (flags == 0)
                   return;
   
           pn = VPTOPP(vp);
           nanotime(&ts);
   
           if (flags & PUFFS_UPDATEATIME) {
                   pn->pn_mc_atime = ts;
                   pn->pn_stat |= PNODE_METACACHE_ATIME;
           }
           if (flags & PUFFS_UPDATECTIME) {
                   pn->pn_mc_ctime = ts;
                   pn->pn_stat |= PNODE_METACACHE_CTIME;
         }          }
           if (flags & PUFFS_UPDATEMTIME) {
                   pn->pn_mc_mtime = ts;
                   pn->pn_stat |= PNODE_METACACHE_MTIME;
           }
           if (flags & PUFFS_UPDATESIZE) {
                   pn->pn_mc_size = vp->v_size;
                   pn->pn_stat |= PNODE_METACACHE_SIZE;
           }
   }
   
         biodone(bp);  void
   puffs_updatevpsize(struct vnode *vp)
   {
           struct vattr va;
   
           if (VOP_GETATTR(vp, &va, FSCRED, NULL))
                   return;
   
           if (va.va_size != VNOVAL)
                   vp->v_size = va.va_size;
 }  }
   
 void  void
 puffs_parkdone_asyncbiowrite(struct puffs_mount *pmp,  puffs_parkdone_asyncbioread(struct puffs_req *preq, void *arg)
         struct puffs_req *preq, void *arg)  
 {  {
         struct puffs_vnmsg_write *write_msg = (void *)preq;          struct puffs_vnreq_read *read_argp = (void *)preq;
         struct buf *bp = arg;          struct buf *bp = arg;
           size_t moved;
   
         DPRINTF(("%s\n", __func__));          bp->b_error = preq->preq_rv;
   
         bp->b_error = checkerr(pmp, preq->preq_rv, __func__);  
         if (bp->b_error == 0) {          if (bp->b_error == 0) {
                 if (write_msg->pvnr_resid > bp->b_bcount) {                  moved = bp->b_bcount - read_argp->pvnr_resid;
                         puffs_senderr(pmp, PUFFS_ERR_WRITE, E2BIG,                  bp->b_resid = read_argp->pvnr_resid;
                             "resid grew", preq->preq_cookie);  
                         bp->b_error = E2BIG;                  memcpy(bp->b_data, read_argp->pvnr_data, moved);
                 } else {  
                         bp->b_resid = write_msg->pvnr_resid;  
                 }  
         }          }
   
         biodone(bp);          biodone(bp);
           free(preq, M_PUFFS);
 }  }
   
 /* XXX: userspace can leak kernel resources */  /* XXX: userspace can leak kernel resources */
 void  void
 puffs_parkdone_poll(struct puffs_mount *pmp, struct puffs_req *preq, void *arg)  puffs_parkdone_poll(struct puffs_req *preq, void *arg)
 {  {
         struct puffs_vnmsg_poll *poll_msg = (void *)preq;          struct puffs_vnreq_poll *poll_argp = (void *)preq;
         struct puffs_node *pn = arg;          struct puffs_node *pn = arg;
         int revents, error;          int revents;
   
         error = checkerr(pmp, preq->preq_rv, __func__);          if (preq->preq_rv == 0)
         if (error)                  revents = poll_argp->pvnr_events;
                 revents = poll_msg->pvnr_events;  
         else          else
                 revents = POLLERR;                  revents = POLLERR;
   
Line 155  puffs_parkdone_poll(struct puffs_mount *
Line 604  puffs_parkdone_poll(struct puffs_mount *
         mutex_exit(&pn->pn_mtx);          mutex_exit(&pn->pn_mtx);
   
         selnotify(&pn->pn_sel, 0);          selnotify(&pn->pn_sel, 0);
           free(preq, M_PUFFS);
   
         puffs_releasenode(pn);          puffs_releasenode(pn);
 }  }
Line 175  puffs_mp_release(struct puffs_mount *pmp
Line 625  puffs_mp_release(struct puffs_mount *pmp
         if (--pmp->pmp_refcount == 0)          if (--pmp->pmp_refcount == 0)
                 cv_broadcast(&pmp->pmp_refcount_cv);                  cv_broadcast(&pmp->pmp_refcount_cv);
 }  }
   
 void  
 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,  
         int flags)  
 {  
   
         *eobp = size;  
 }  
   
 void  
 puffs_gop_markupdate(struct vnode *vp, int flags)  
 {  
         int uflags = 0;  
   
         if (flags & GOP_UPDATE_ACCESSED)  
                 uflags |= PUFFS_UPDATEATIME;  
         if (flags & GOP_UPDATE_MODIFIED)  
                 uflags |= PUFFS_UPDATEMTIME;  
   
         puffs_updatenode(VPTOPP(vp), uflags, 0);  
 }  
   
 void  
 puffs_senderr(struct puffs_mount *pmp, int type, int error,  
         const char *str, void *cookie)  
 {  
         struct puffs_msgpark *park;  
         struct puffs_error *perr;  
   
         puffs_msgmem_alloc(sizeof(struct puffs_error), &park, (void**)&perr, 1);  
         puffs_msg_setfaf(park);  
         puffs_msg_setinfo(park, PUFFSOP_ERROR, type, cookie);  
   
         perr->perr_error = error;  
         strlcpy(perr->perr_str, str, sizeof(perr->perr_str));  
   
         puffs_msg_enqueue(pmp, park);  
         puffs_msgmem_release(park);  
 }  

Legend:
Removed from v.1.44.2.4  
changed lines
  Added in v.1.45

CVSweb <webmaster@jp.NetBSD.org>