int fdesc_allocvp(fdntype ftype, unsigned fd_fd, int ix, struct mount *mp, struct vnode **vpp) { struct fdescmount *fmp; struct fdhashhead *fc; struct fdescnode *fd, *fd2; struct vnode *vp, *vp2; struct thread *td; int error = 0; td = curthread; fc = FD_NHASH(ix); loop: mtx_lock(&fdesc_hashmtx); /* * If a forced unmount is progressing, we need to drop it. The flags are * protected by the hashmtx. */ fmp = (struct fdescmount *)mp->mnt_data; if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) { mtx_unlock(&fdesc_hashmtx); return (-1); } LIST_FOREACH(fd, fc, fd_hash) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { /* Get reference to vnode in case it's being free'd */ vp = fd->fd_vnode; VI_LOCK(vp); mtx_unlock(&fdesc_hashmtx); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) goto loop; *vpp = vp; return (0); } } mtx_unlock(&fdesc_hashmtx); fd = malloc(sizeof(struct fdescnode), M_TEMP, M_WAITOK); error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, &vp); if (error) { free(fd, M_TEMP); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vp->v_data = fd; fd->fd_vnode = vp; fd->fd_type = ftype; fd->fd_fd = fd_fd; fd->fd_ix = ix; error = insmntque1(vp, mp, fdesc_insmntque_dtr, NULL); if (error != 0) { *vpp = NULLVP; return (error); } /* Make sure that someone didn't beat us when inserting the vnode. */ mtx_lock(&fdesc_hashmtx); /* * If a forced unmount is progressing, we need to drop it. The flags are * protected by the hashmtx. */ fmp = (struct fdescmount *)mp->mnt_data; if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) { mtx_unlock(&fdesc_hashmtx); vgone(vp); vput(vp); *vpp = NULLVP; return (-1); } LIST_FOREACH(fd2, fc, fd_hash) { if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) { /* Get reference to vnode in case it's being free'd */ vp2 = fd2->fd_vnode; VI_LOCK(vp2); mtx_unlock(&fdesc_hashmtx); error = vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, td); /* Someone beat us, dec use count and wait for reclaim */ vgone(vp); vput(vp); /* If we didn't get it, return no vnode. */ if (error) vp2 = NULLVP; *vpp = vp2; return (error); } } /* If we came here, we can insert it safely. */ LIST_INSERT_HEAD(fc, fd, fd_hash); mtx_unlock(&fdesc_hashmtx); *vpp = vp; return (0); }
/* * Make a new or get existing pefs node. * vp is the alias vnode * lvp is the lower vnode * ldvp is the lower directory vnode, used if no key specified * * The lvp assumed to be locked and having "spare" reference. This routine * vrele lvp if pefs node was taken from hash. Otherwise it "transfers" the * caller's "spare" reference to created pefs vnode. */ static int pefs_node_get(struct mount *mp, struct vnode *lvp, struct vnode **vpp, pefs_node_init_fn *init_fn, void *context) { struct pefs_node *pn; struct vnode *vp; int error; ASSERT_VOP_LOCKED(lvp, "pefs_node_get"); /* Lookup the hash firstly */ *vpp = pefs_nodehash_get(mp, lvp); if (*vpp != NULL) { vrele(lvp); return (0); } /* * We do not serialize vnode creation, instead we will check for * duplicates later, when adding new vnode to hash. * * Note that duplicate can only appear in hash if the lvp is * locked LK_SHARED. */ /* * 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. */ pn = uma_zalloc(pefs_node_zone, M_WAITOK | M_ZERO); pn->pn_lowervp = lvp; /* pn->pn_lowervp should be initialized before calling init_fn. */ error = init_fn(mp, pn, context); MPASS(!(((pn->pn_flags & PN_HASKEY) == 0) ^ (pn->pn_tkey.ptk_key == NULL))); if (error != 0) { uma_zfree(pefs_node_zone, pn); return (error); } error = getnewvnode("pefs", mp, &pefs_vnodeops, &vp); if (error != 0) { pefs_key_release(pn->pn_tkey.ptk_key); uma_zfree(pefs_node_zone, pn); return (error); } if (pn->pn_tkey.ptk_key == NULL) PEFSDEBUG("pefs_node_get: creating node without key: %p\n", pn); pn->pn_vnode = vp; vp->v_type = lvp->v_type; vp->v_data = pn; vp->v_vnlock = lvp->v_vnlock; if (vp->v_vnlock == NULL) panic("pefs_node_get: Passed a NULL vnlock.\n"); error = insmntque1(vp, mp, pefs_insmntque_dtr, pn); if (error != 0) return (error); /* * Atomically insert our new node into the hash or vget existing * if someone else has beaten us to it. */ *vpp = pefs_nodehash_insert(mp, pn); if (*vpp != NULL) { vrele(lvp); vp->v_vnlock = &vp->v_lock; pn->pn_lowervp = NULL; vrele(vp); MPASS(PEFS_LOWERVP(*vpp) == lvp); ASSERT_VOP_LOCKED(*vpp, "pefs_node_get: duplicate"); return (0); } if (vp->v_type == VDIR) pn->pn_dircache = pefs_dircache_get(); *vpp = vp; MPASS(PEFS_LOWERVP(*vpp) == lvp); ASSERT_VOP_LOCKED(*vpp, "pefs_node_get"); return (0); }