Start playing around with vnode locks. For now, do the very easy

thing and release locks before the userspace wait for operations
which release the lock before exit from the method in any case.
However, releasing the lock after inserting the request on the
operation queue gives us proper ordering possibilities in userspace
(at least if that bit were implemented, but I don't think there
any file system in userspace that depends on kernel locking and
probably there never should be one).

inspired by a conversation with Nacho Navarro
This commit is contained in:
pooka 2007-11-17 21:30:48 +00:00
parent 2632ba6df4
commit 63e9f065b3
1 changed files with 124 additions and 41 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: puffs_vnops.c,v 1.115 2007/11/17 18:09:04 pooka Exp $ */
/* $NetBSD: puffs_vnops.c,v 1.116 2007/11/17 21:30:48 pooka Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.115 2007/11/17 18:09:04 pooka Exp $");
__KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.116 2007/11/17 21:30:48 pooka Exp $");
#include <sys/param.h>
#include <sys/fstrans.h>
@ -437,6 +437,20 @@ puffs_abortbutton(struct puffs_mount *pmp, int what,
puffs_callreclaim(pmp, cookie, cnp->cn_lwp);
}
/*
* Begin vnode operations.
*
* A word from the keymaster about locks: generally we don't want
* to use the vnode locks at all: it creates an ugly dependency between
* the userlandia file server and the kernel. But we'll play along with
* the kernel vnode locks for now. However, even currently we attempt
* to release locks as early as possible. This is possible for some
* operations which a) don't need a locked vnode after the userspace op
* and b) return with the vnode unlocked. Theoretically we could
* unlock-do op-lock for others and order the graph in userspace, but I
* don't want to think of the consequences for the time being.
*/
int
puffs_lookup(void *v)
{
@ -581,6 +595,27 @@ puffs_lookup(void *v)
return error;
}
#define REFPN_AND_UNLOCKVP(a, b) \
do { \
mutex_enter(&b->pn_mtx); \
puffs_referencenode(b); \
mutex_exit(&b->pn_mtx); \
VOP_UNLOCK(a, 0); \
} while (/*CONSTCOND*/0)
#define REFPN(b) \
do { \
mutex_enter(&b->pn_mtx); \
puffs_referencenode(b); \
mutex_exit(&b->pn_mtx); \
} while (/*CONSTCOND*/0)
#define RELEPN_AND_VP(a, b) \
do { \
puffs_releasenode(b); \
vrele(a); \
} while (/*CONSTCOND*/0)
int
puffs_create(void *v)
{
@ -593,8 +628,10 @@ puffs_create(void *v)
} */ *ap = v;
PUFFS_MSG_VARS(vn, create);
struct vnode *dvp = ap->a_dvp;
struct puffs_node *dpn = VPTOPP(dvp);
struct componentname *cnp = ap->a_cnp;
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct mount *mp = dvp->v_mount;
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int error;
DPRINTF(("puffs_create: dvp %p, cnp: %s\n",
@ -607,22 +644,31 @@ puffs_create(void *v)
puffs_msg_setinfo(park_create, PUFFSOP_VN,
PUFFS_VN_CREATE, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_create, dvp->v_data, NULL, error);
/*
* Do the dance:
* + insert into queue ("interlock")
* + unlock vnode
* + wait for response
*/
puffs_msg_enqueue(pmp, park_create);
REFPN_AND_UNLOCKVP(dvp, dpn);
error = puffs_msg_wait2(pmp, park_create, dpn, NULL);
error = checkerr(pmp, error, __func__);
if (error)
goto out;
error = puffs_newnode(dvp->v_mount, dvp, ap->a_vpp,
error = puffs_newnode(mp, dvp, ap->a_vpp,
create_msg->pvnr_newnode, cnp, ap->a_vap->va_type, 0);
if (error)
puffs_abortbutton(pmp, PUFFS_ABORT_CREATE, VPTOPNC(dvp),
puffs_abortbutton(pmp, PUFFS_ABORT_CREATE, dpn->pn_cookie,
create_msg->pvnr_newnode, cnp);
out:
if (error || (cnp->cn_flags & SAVESTART) == 0)
PNBUF_PUT(cnp->cn_pnbuf);
vput(dvp);
RELEPN_AND_VP(dvp, dpn);
DPRINTF(("puffs_create: return %d\n", error));
PUFFS_MSG_RELEASE(create);
return error;
@ -640,8 +686,10 @@ puffs_mknod(void *v)
} */ *ap = v;
PUFFS_MSG_VARS(vn, mknod);
struct vnode *dvp = ap->a_dvp;
struct puffs_node *dpn = VPTOPP(dvp);
struct componentname *cnp = ap->a_cnp;
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct mount *mp = dvp->v_mount;
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int error;
PUFFS_MSG_ALLOC(vn, mknod);
@ -651,23 +699,26 @@ puffs_mknod(void *v)
puffs_msg_setinfo(park_mknod, PUFFSOP_VN,
PUFFS_VN_MKNOD, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error);
puffs_msg_enqueue(pmp, park_mknod);
REFPN_AND_UNLOCKVP(dvp, dpn);
error = puffs_msg_wait2(pmp, park_mknod, dpn, NULL);
error = checkerr(pmp, error, __func__);
if (error)
goto out;
error = puffs_newnode(dvp->v_mount, dvp, ap->a_vpp,
error = puffs_newnode(mp, dvp, ap->a_vpp,
mknod_msg->pvnr_newnode, cnp, ap->a_vap->va_type,
ap->a_vap->va_rdev);
if (error)
puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, VPTOPNC(dvp),
puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie,
mknod_msg->pvnr_newnode, cnp);
out:
PUFFS_MSG_RELEASE(mknod);
if (error || (cnp->cn_flags & SAVESTART) == 0)
PNBUF_PUT(cnp->cn_pnbuf);
vput(dvp);
RELEPN_AND_VP(dvp, dpn);
return error;
}
@ -1376,7 +1427,7 @@ puffs_callremove(struct puffs_mount *pmp, void *dcookie, void *cookie,
/*
* XXX: can't use callremove now because can't catch setbacks with
* it due to lack of a vnode argument.
* it due to lack of a pnode argument.
*/
int
puffs_remove(void *v)
@ -1390,8 +1441,11 @@ puffs_remove(void *v)
PUFFS_MSG_VARS(vn, remove);
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct puffs_node *dpn = VPTOPP(dvp);
struct puffs_node *pn = VPTOPP(vp);
struct componentname *cnp = ap->a_cnp;
struct mount *mp = dvp->v_mount;
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int error;
PUFFS_MSG_ALLOC(vn, remove);
@ -1401,17 +1455,20 @@ puffs_remove(void *v)
puffs_msg_setinfo(park_remove, PUFFSOP_VN,
PUFFS_VN_REMOVE, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_remove,
dvp->v_data, vp->v_data, error);
puffs_msg_enqueue(pmp, park_remove);
REFPN_AND_UNLOCKVP(dvp, dpn);
if (dvp == vp)
REFPN(pn);
else
REFPN_AND_UNLOCKVP(vp, pn);
error = puffs_msg_wait2(pmp, park_remove, dpn, pn);
PUFFS_MSG_RELEASE(remove);
error = checkerr(pmp, error, __func__);
vput(vp);
if (dvp == vp)
vrele(dvp);
else
vput(dvp);
RELEPN_AND_VP(dvp, dpn);
RELEPN_AND_VP(vp, pn);
error = checkerr(pmp, error, __func__);
return error;
}
@ -1427,8 +1484,10 @@ puffs_mkdir(void *v)
} */ *ap = v;
PUFFS_MSG_VARS(vn, mkdir);
struct vnode *dvp = ap->a_dvp;
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct puffs_node *dpn = VPTOPP(dvp);
struct componentname *cnp = ap->a_cnp;
struct mount *mp = dvp->v_mount;
struct puffs_mount *pmp = MPTOPUFFSMP(mp);
int error;
PUFFS_MSG_ALLOC(vn, mkdir);
@ -1438,22 +1497,25 @@ puffs_mkdir(void *v)
puffs_msg_setinfo(park_mkdir, PUFFSOP_VN,
PUFFS_VN_MKDIR, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mkdir, dvp->v_data, NULL, error);
puffs_msg_enqueue(pmp, park_mkdir);
REFPN_AND_UNLOCKVP(dvp, dpn);
error = puffs_msg_wait2(pmp, park_mkdir, dpn, NULL);
error = checkerr(pmp, error, __func__);
if (error)
goto out;
error = puffs_newnode(dvp->v_mount, dvp, ap->a_vpp,
error = puffs_newnode(mp, dvp, ap->a_vpp,
mkdir_msg->pvnr_newnode, cnp, VDIR, 0);
if (error)
puffs_abortbutton(pmp, PUFFS_ABORT_MKDIR, VPTOPNC(ap->a_dvp),
puffs_abortbutton(pmp, PUFFS_ABORT_MKDIR, dpn->pn_cookie,
mkdir_msg->pvnr_newnode, cnp);
out:
PUFFS_MSG_RELEASE(mkdir);
if (error || (cnp->cn_flags & SAVESTART) == 0)
PNBUF_PUT(cnp->cn_pnbuf);
vput(ap->a_dvp);
RELEPN_AND_VP(dvp, dpn);
return error;
}
@ -1488,6 +1550,8 @@ puffs_rmdir(void *v)
PUFFS_MSG_VARS(vn, rmdir);
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
struct puffs_node *dpn = VPTOPP(dvp);
struct puffs_node *pn = VPTOPP(vp);
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
int error;
@ -1499,12 +1563,16 @@ puffs_rmdir(void *v)
puffs_msg_setinfo(park_rmdir, PUFFSOP_VN,
PUFFS_VN_RMDIR, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rmdir, dvp->v_data, vp->v_data, error);
puffs_msg_enqueue(pmp, park_rmdir);
REFPN_AND_UNLOCKVP(dvp, dpn);
REFPN_AND_UNLOCKVP(vp, pn);
error = puffs_msg_wait2(pmp, park_rmdir, dpn, pn);
PUFFS_MSG_RELEASE(rmdir);
/* XXX: some call cache_purge() *for both vnodes* here, investigate */
vput(dvp);
vput(vp);
RELEPN_AND_VP(dvp, dpn);
RELEPN_AND_VP(vp, pn);
return error;
}
@ -1521,6 +1589,8 @@ puffs_link(void *v)
PUFFS_MSG_VARS(vn, link);
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
struct puffs_node *dpn = VPTOPP(dvp);
struct puffs_node *pn = VPTOPP(vp);
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
int error;
@ -1532,7 +1602,11 @@ puffs_link(void *v)
puffs_msg_setinfo(park_link, PUFFSOP_VN,
PUFFS_VN_LINK, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_link, dvp->v_data, NULL, error);
puffs_msg_enqueue(pmp, park_link);
REFPN_AND_UNLOCKVP(dvp, dpn);
REFPN(pn);
error = puffs_msg_wait2(pmp, park_link, dpn, pn);
PUFFS_MSG_RELEASE(link);
error = checkerr(pmp, error, __func__);
@ -1540,12 +1614,15 @@ puffs_link(void *v)
/*
* XXX: stay in touch with the cache. I don't like this, but
* don't have a better solution either. See also puffs_rename().
*
* XXX2: can't use vp safely here
*/
if (error == 0)
puffs_updatenode(vp, PUFFS_UPDATECTIME);
PNBUF_PUT(cnp->cn_pnbuf);
vput(dvp);
RELEPN_AND_VP(dvp, dpn);
puffs_releasenode(pn);
return error;
}
@ -1563,36 +1640,42 @@ puffs_symlink(void *v)
} */ *ap = v;
PUFFS_MSG_VARS(vn, symlink);
struct vnode *dvp = ap->a_dvp;
struct puffs_node *dpn = VPTOPP(dvp);
struct mount *mp = dvp->v_mount;
struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount);
struct componentname *cnp = ap->a_cnp;
int error;
*ap->a_vpp = NULL;
PUFFS_MSG_ALLOC(vn, symlink);
puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred,
&symlink_msg->pvnr_cn_cid, ap->a_cnp, PUFFS_USE_FULLPNBUF(pmp));
&symlink_msg->pvnr_cn_cid, cnp, PUFFS_USE_FULLPNBUF(pmp));
symlink_msg->pvnr_va = *ap->a_vap;
(void)strlcpy(symlink_msg->pvnr_link, ap->a_target,
sizeof(symlink_msg->pvnr_link));
puffs_msg_setinfo(park_symlink, PUFFSOP_VN,
PUFFS_VN_SYMLINK, VPTOPNC(dvp));
PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error);
puffs_msg_enqueue(pmp, park_symlink);
REFPN_AND_UNLOCKVP(dvp, dpn);
error = puffs_msg_wait2(pmp, park_symlink, dpn, NULL);
error = checkerr(pmp, error, __func__);
if (error)
goto out;
error = puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
symlink_msg->pvnr_newnode, ap->a_cnp, VLNK, 0);
error = puffs_newnode(mp, dvp, ap->a_vpp,
symlink_msg->pvnr_newnode, cnp, VLNK, 0);
if (error)
puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, VPTOPNC(ap->a_dvp),
symlink_msg->pvnr_newnode, ap->a_cnp);
puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie,
symlink_msg->pvnr_newnode, cnp);
out:
PUFFS_MSG_RELEASE(symlink);
if (error || (ap->a_cnp->cn_flags & SAVESTART) == 0)
PNBUF_PUT(ap->a_cnp->cn_pnbuf);
vput(ap->a_dvp);
if (error || (cnp->cn_flags & SAVESTART) == 0)
PNBUF_PUT(cnp->cn_pnbuf);
RELEPN_AND_VP(dvp, dpn);
return error;
}