X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..ff6e181ae92fc6f1e89841290f461d1f2f9badd9:/bsd/nfs/nfs_node.c diff --git a/bsd/nfs/nfs_node.c b/bsd/nfs/nfs_node.c index 383428171..61af3f475 100644 --- a/bsd/nfs/nfs_node.c +++ b/bsd/nfs/nfs_node.c @@ -1,21 +1,22 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -63,9 +64,10 @@ #include #include #include -#include -#include +#include +#include #include +#include #include #include @@ -74,42 +76,44 @@ #include #include -#ifdef MALLOC_DEFINE -static MALLOC_DEFINE(M_NFSNODE, "NFS node", "NFS vnode private part"); -#endif - LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl; u_long nfsnodehash; -#define TRUE 1 -#define FALSE 0 +lck_grp_t * nfs_node_hash_lck_grp; +lck_grp_attr_t * nfs_node_hash_lck_grp_attr; +lck_attr_t * nfs_node_hash_lck_attr; +lck_mtx_t *nfs_node_hash_mutex; /* * Initialize hash links for nfsnodes * and build nfsnode free list. */ void -nfs_nhinit() +nfs_nhinit(void) { nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash); + + nfs_node_hash_lck_grp_attr = lck_grp_attr_alloc_init(); + lck_grp_attr_setstat(nfs_node_hash_lck_grp_attr); + nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", nfs_node_hash_lck_grp_attr); + + nfs_node_hash_lck_attr = lck_attr_alloc_init(); + + nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, nfs_node_hash_lck_attr); } /* * Compute an entry in the NFS hash table structure */ u_long -nfs_hash(fhp, fhsize) - register nfsfh_t *fhp; - int fhsize; +nfs_hash(u_char *fhp, int fhsize) { - register u_char *fhpp; - register u_long fhsum; - register int i; + u_long fhsum; + int i; - fhpp = &fhp->fh_bytes[0]; fhsum = 0; for (i = 0; i < fhsize; i++) - fhsum += *fhpp++; + fhsum += *fhp++; return (fhsum); } @@ -119,115 +123,199 @@ nfs_hash(fhp, fhsize) * In all cases, a pointer to a * nfsnode structure is returned. */ -int nfs_node_hash_lock; - int -nfs_nget(mntp, fhp, fhsize, npp) - struct mount *mntp; - register nfsfh_t *fhp; - int fhsize; - struct nfsnode **npp; +nfs_nget( + mount_t mntp, + vnode_t dvp, + struct componentname *cnp, + u_char *fhp, + int fhsize, + struct nfs_vattr *nvap, + u_int64_t *xidp, + int flags, + struct nfsnode **npp) { - struct proc *p = current_proc(); /* XXX */ struct nfsnode *np; struct nfsnodehashhead *nhpp; - register struct vnode *vp; - struct vnode *nvp; + vnode_t vp, nvp; int error; + mount_t mp; + struct vnode_fsparam vfsp; + uint32_t vid; /* Check for unmount in progress */ - if (mntp->mnt_kern_flag & MNTK_UNMOUNT) { + if (!mntp || (mntp->mnt_kern_flag & MNTK_UNMOUNT)) { *npp = 0; - return (EPERM); + return (!mntp ? ENXIO : EPERM); } nhpp = NFSNOHASH(nfs_hash(fhp, fhsize)); loop: + lck_mtx_lock(nfs_node_hash_mutex); for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { - if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize || - bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) + mp = (np->n_flag & NINIT) ? np->n_mount : vnode_mount(NFSTOV(np)); + if (mntp != mp || np->n_fhsize != fhsize || + bcmp(fhp, np->n_fhp, fhsize)) continue; - vp = NFSTOV(np); - if (vget(vp, LK_EXCLUSIVE, p)) + /* if the node is still being initialized, sleep on it */ + if (np->n_flag & NINIT) { + np->n_flag |= NWINIT; + msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", 0); goto loop; - *npp = np; - return(0); - } - /* - * Obtain a lock to prevent a race condition if the getnewvnode() - * or MALLOC() below happens to block. - */ - if (nfs_node_hash_lock) { - while (nfs_node_hash_lock) { - nfs_node_hash_lock = -1; - tsleep(&nfs_node_hash_lock, PVM, "nfsngt", 0); } - goto loop; + vp = NFSTOV(np); + vid = vnode_vid(vp); + lck_mtx_unlock(nfs_node_hash_mutex); + if ((error = vnode_getwithvid(vp, vid))) { + /* + * If vnode is being reclaimed or has already + * changed identity, no need to wait. + */ + return (error); + } + /* update attributes */ + error = nfs_loadattrcache(np, nvap, xidp, 0); + if (error) { + vnode_put(vp); + } else { + if (dvp && cnp && (flags & NG_MAKEENTRY)) + cache_enter(dvp, vp, cnp); + *npp = np; + } + return(error); } - nfs_node_hash_lock = 1; /* - * Do the MALLOC before the getnewvnode since doing so afterward - * might cause a bogus v_data pointer to get dereferenced - * elsewhere if MALLOC should block. + * allocate and initialize nfsnode and stick it in the hash + * before calling getnewvnode(). Anyone finding it in the + * hash before initialization is complete will wait for it. */ MALLOC_ZONE(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK); - - error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp); - if (error) { - if (nfs_node_hash_lock < 0) - wakeup(&nfs_node_hash_lock); - nfs_node_hash_lock = 0; + if (!np) { + lck_mtx_unlock(nfs_node_hash_mutex); *npp = 0; - FREE_ZONE(np, sizeof *np, M_NFSNODE); - return (error); + return (ENOMEM); } - vp = nvp; bzero((caddr_t)np, sizeof *np); - vp->v_data = np; - np->n_vnode = vp; - /* - * Insert the nfsnode in the hash queue for its new file handle - */ - LIST_INSERT_HEAD(nhpp, np, n_hash); + np->n_flag |= NINIT; + np->n_mount = mntp; + + /* setup node's file handle */ if (fhsize > NFS_SMALLFH) { - MALLOC_ZONE(np->n_fhp, nfsfh_t *, + MALLOC_ZONE(np->n_fhp, u_char *, fhsize, M_NFSBIGFH, M_WAITOK); - } else - np->n_fhp = &np->n_fh; - bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize); + if (!np->n_fhp) { + lck_mtx_unlock(nfs_node_hash_mutex); + FREE_ZONE(np, sizeof *np, M_NFSNODE); + *npp = 0; + return (ENOMEM); + } + } else { + np->n_fhp = &np->n_fh[0]; + } + bcopy(fhp, np->n_fhp, fhsize); np->n_fhsize = fhsize; - *npp = np; - if (nfs_node_hash_lock < 0) - wakeup(&nfs_node_hash_lock); - nfs_node_hash_lock = 0; + /* Insert the nfsnode in the hash queue for its new file handle */ + np->n_flag |= NHASHED; + LIST_INSERT_HEAD(nhpp, np, n_hash); - /* - * Lock the new nfsnode. - */ - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + /* release lock on hash table */ + lck_mtx_unlock(nfs_node_hash_mutex); - return (0); + /* do initial loading of attributes */ + error = nfs_loadattrcache(np, nvap, xidp, 1); + if (error) { + lck_mtx_lock(nfs_node_hash_mutex); + LIST_REMOVE(np, n_hash); + np->n_flag &= ~(NHASHED|NINIT); + if (np->n_flag & NWINIT) { + np->n_flag &= ~NWINIT; + wakeup((caddr_t)np); + } + lck_mtx_unlock(nfs_node_hash_mutex); + if (np->n_fhsize > NFS_SMALLFH) + FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); + FREE_ZONE(np, sizeof *np, M_NFSNODE); + *npp = 0; + return (error); + } + np->n_mtime = nvap->nva_mtime; + if (nvap->nva_type == VDIR) + np->n_ncmtime = nvap->nva_mtime; + NMODEINVALIDATE(np); + + /* now, attempt to get a new vnode */ + vfsp.vnfs_mp = mntp; + vfsp.vnfs_vtype = nvap->nva_type; + vfsp.vnfs_str = "nfs"; + vfsp.vnfs_dvp = dvp; + vfsp.vnfs_fsnode = np; + if (nvap->nva_type == VFIFO) + vfsp.vnfs_vops = fifo_nfsv2nodeop_p; + else if (nvap->nva_type == VBLK || nvap->nva_type == VCHR) + vfsp.vnfs_vops = spec_nfsv2nodeop_p; + else + vfsp.vnfs_vops = nfsv2_vnodeop_p; + vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0; + vfsp.vnfs_marksystem = 0; + vfsp.vnfs_rdev = 0; + vfsp.vnfs_filesize = nvap->nva_size; + vfsp.vnfs_cnp = cnp; + if (dvp && cnp && (flags & NG_MAKEENTRY)) + vfsp.vnfs_flags = 0; + else + vfsp.vnfs_flags = VNFS_NOCACHE; + error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &nvp); + if (error) { + lck_mtx_lock(nfs_node_hash_mutex); + LIST_REMOVE(np, n_hash); + np->n_flag &= ~(NHASHED|NINIT); + if (np->n_flag & NWINIT) { + np->n_flag &= ~NWINIT; + wakeup((caddr_t)np); + } + lck_mtx_unlock(nfs_node_hash_mutex); + if (np->n_fhsize > NFS_SMALLFH) + FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); + FREE_ZONE(np, sizeof *np, M_NFSNODE); + *npp = 0; + return (error); + } + vp = nvp; + np->n_vnode = vp; + vnode_addfsref(vp); + vnode_settag(vp, VT_NFS); // XXX shouldn't this be a vnode_create() parameter? + *npp = np; + /* node is now initialized */ + + /* check if anyone's waiting on this node */ + lck_mtx_lock(nfs_node_hash_mutex); + np->n_flag &= ~NINIT; + if (np->n_flag & NWINIT) { + np->n_flag &= ~NWINIT; + wakeup((caddr_t)np); + } + lck_mtx_unlock(nfs_node_hash_mutex); + + return (error); } + int nfs_inactive(ap) - struct vop_inactive_args /* { - struct vnode *a_vp; - struct proc *a_p; + struct vnop_inactive_args /* { + struct vnodeop_desc *a_desc; + vnode_t a_vp; + vfs_context_t a_context; } */ *ap; { register struct nfsnode *np; register struct sillyrename *sp; - struct proc *p = current_proc(); /* XXX */ - extern int prtactive; - struct ucred *cred; + kauth_cred_t cred; np = VTONFS(ap->a_vp); - if (prtactive && ap->a_vp->v_usecount != 0) - vprint("nfs_inactive: pushing active", ap->a_vp); - if (ap->a_vp->v_type != VDIR) { + if (vnode_vtype(ap->a_vp) != VDIR) { sp = np->n_sillyrename; np->n_sillyrename = (struct sillyrename *)0; } else @@ -238,48 +326,34 @@ nfs_inactive(ap) * Remove the silly file that was rename'd earlier */ #if DIAGNOSTIC - kprintf("nfs_inactive removing %s, dvp=%x, a_vp=%x, ap=%x, np=%x, sp=%x\n", &sp->s_name[0], (unsigned)sp->s_dvp, (unsigned)ap->a_vp, (unsigned)ap, (unsigned)np, (unsigned)sp); + kprintf("nfs_inactive removing %s, dvp=%x, a_vp=%x, ap=%x, np=%x, sp=%x\n", + &sp->s_name[0], (unsigned)sp->s_dvp, (unsigned)ap->a_vp, (unsigned)ap, + (unsigned)np, (unsigned)sp); #endif + nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, vfs_context_proc(ap->a_context), 1); + np->n_size = 0; + ubc_setsize(ap->a_vp, (off_t)0); + nfs_removeit(sp); /* - * We get a reference (vget) to ensure getnewvnode() - * doesn't recycle vp while we're asleep awaiting I/O. - * Note we don't need the reference unless usecount is - * already zero. In the case of a forcible unmount it - * wont be zero and doing a vget would fail because - * vclean holds VXLOCK. + * remove nfsnode from hash now so we can't accidentally find it + * again if another object gets created with the same filehandle + * before this vnode gets reclaimed */ - if (ap->a_vp->v_usecount > 0) { - VREF(ap->a_vp); - } else if (vget(ap->a_vp, 0, ap->a_p)) - panic("nfs_inactive: vget failed"); - (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, p, 1); - ubc_setsize(ap->a_vp, (off_t)0); - - /* We have a problem. The dvp could have gone away on us - * while in the unmount path. Thus it appears as VBAD and we - * cannot use it. If we tried locking the parent (future), for silly - * rename files, it is unclear where we would lock. The unmount - * code just pulls unlocked vnodes as it goes thru its list and - * yanks them. Could unmount be smarter to see if a busy reg vnode has - * a parent, and not yank it yet? Put in more passes at unmount - * time? In the meantime, just check if it went away on us. Could - * have gone away during the nfs_vinvalbuf or ubc_setsize which block. - * Or perhaps even before nfs_inactive got called. - */ - if ((sp->s_dvp)->v_type != VBAD) - nfs_removeit(sp); /* uses the dvp */ + lck_mtx_lock(nfs_node_hash_mutex); + LIST_REMOVE(np, n_hash); + np->n_flag &= ~NHASHED; + lck_mtx_unlock(nfs_node_hash_mutex); cred = sp->s_cred; if (cred != NOCRED) { sp->s_cred = NOCRED; - crfree(cred); + kauth_cred_rele(cred); } - vrele(sp->s_dvp); + vnode_rele(sp->s_dvp); FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ); - vrele(ap->a_vp); + vnode_recycle(ap->a_vp); } - np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED | - NQNFSNONCACHE | NQNFSWRITE); - VOP_UNLOCK(ap->a_vp, 0, ap->a_p); + /* clear all flags other than these */ + np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED); return (0); } @@ -288,32 +362,23 @@ nfs_inactive(ap) */ int nfs_reclaim(ap) - struct vop_reclaim_args /* { - struct vnode *a_vp; + struct vnop_reclaim_args /* { + struct vnodeop_desc *a_desc; + vnode_t a_vp; + vfs_context_t a_context; } */ *ap; { - register struct vnode *vp = ap->a_vp; - register struct nfsnode *np = VTONFS(vp); - register struct nfsmount *nmp = VFSTONFS(vp->v_mount); - register struct nfsdmap *dp, *dp2; - extern int prtactive; - - if (prtactive && vp->v_usecount != 0) - vprint("nfs_reclaim: pushing active", vp); + vnode_t vp = ap->a_vp; + struct nfsnode *np = VTONFS(vp); + struct nfsdmap *dp, *dp2; - LIST_REMOVE(np, n_hash); + vnode_removefsref(vp); - /* - * In case we block during FREE_ZONEs below, get the entry out - * of tbe name cache now so subsequent lookups won't find it. - */ - cache_purge(vp); - - /* - * For nqnfs, take it off the timer queue as required. - */ - if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) { - CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer); + if (np->n_flag & NHASHED) { + lck_mtx_lock(nfs_node_hash_mutex); + LIST_REMOVE(np, n_hash); + np->n_flag &= ~NHASHED; + lck_mtx_unlock(nfs_node_hash_mutex); } /* @@ -321,7 +386,7 @@ nfs_reclaim(ap) * large file handle structures that might be associated with * this nfs node. */ - if (vp->v_type == VDIR) { + if (vnode_vtype(vp) == VDIR) { dp = np->n_cookies.lh_first; while (dp) { dp2 = dp; @@ -331,121 +396,11 @@ nfs_reclaim(ap) } } if (np->n_fhsize > NFS_SMALLFH) { - FREE_ZONE((caddr_t)np->n_fhp, np->n_fhsize, M_NFSBIGFH); - } - - FREE_ZONE(vp->v_data, sizeof (struct nfsnode), M_NFSNODE); - vp->v_data = (void *)0; - return (0); -} - -#if 0 -/* - * Lock an nfsnode - */ -int -nfs_lock(ap) - struct vop_lock_args /* { - struct vnode *a_vp; - } */ *ap; -{ - register struct vnode *vp = ap->a_vp; - - /* - * Ugh, another place where interruptible mounts will get hung. - * If you make this sleep interruptible, then you have to fix all - * the VOP_LOCK() calls to expect interruptibility. - */ - while (vp->v_flag & VXLOCK) { - vp->v_flag |= VXWANT; - (void) tsleep((caddr_t)vp, PINOD, "nfslck", 0); - } - if (vp->v_tag == VT_NON) - return (ENOENT); - -#if 0 - /* - * Only lock regular files. If a server crashed while we were - * holding a directory lock, we could easily end up sleeping - * until the server rebooted while holding a lock on the root. - * Locks are only needed for protecting critical sections in - * VMIO at the moment. - * New vnodes will have type VNON but they should be locked - * since they may become VREG. This is checked in loadattrcache - * and unwanted locks are released there. - */ - if (vp->v_type == VREG || vp->v_type == VNON) { - while (np->n_flag & NLOCKED) { - np->n_flag |= NWANTED; - (void) tsleep((caddr_t) np, PINOD, "nfslck2", 0); - /* - * If the vnode has transmuted into a VDIR while we - * were asleep, then skip the lock. - */ - if (vp->v_type != VREG && vp->v_type != VNON) - return (0); - } - np->n_flag |= NLOCKED; - } -#endif - - return (0); -} - -/* - * Unlock an nfsnode - */ -int -nfs_unlock(ap) - struct vop_unlock_args /* { - struct vnode *a_vp; - } */ *ap; -{ -#if 0 - struct vnode* vp = ap->a_vp; - struct nfsnode* np = VTONFS(vp); - - if (vp->v_type == VREG || vp->v_type == VNON) { - if (!(np->n_flag & NLOCKED)) - panic("nfs_unlock: nfsnode not locked"); - np->n_flag &= ~NLOCKED; - if (np->n_flag & NWANTED) { - np->n_flag &= ~NWANTED; - wakeup((caddr_t) np); - } + FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH); } -#endif + vnode_clearfsnode(vp); + FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE); return (0); } -/* - * Check for a locked nfsnode - */ -int -nfs_islocked(ap) - struct vop_islocked_args /* { - struct vnode *a_vp; - } */ *ap; -{ - return VTONFS(ap->a_vp)->n_flag & NLOCKED ? 1 : 0; -} -#endif - -/* - * Nfs abort op, called after namei() when a CREATE/DELETE isn't actually - * done. Currently nothing to do. - */ -/* ARGSUSED */ -int -nfs_abortop(ap) - struct vop_abortop_args /* { - struct vnode *a_dvp; - struct componentname *a_cnp; - } */ *ap; -{ - - if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) - FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI); - return (0); -}