summaryrefslogtreecommitdiffstats
path: root/sys/fs/nwfs/nwfs_node.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nwfs/nwfs_node.c')
-rw-r--r--sys/fs/nwfs/nwfs_node.c130
1 files changed, 73 insertions, 57 deletions
diff --git a/sys/fs/nwfs/nwfs_node.c b/sys/fs/nwfs/nwfs_node.c
index 4232e9b..2d34600 100644
--- a/sys/fs/nwfs/nwfs_node.c
+++ b/sys/fs/nwfs/nwfs_node.c
@@ -61,7 +61,7 @@ extern vop_t **nwfs_vnodeop_p;
static LIST_HEAD(nwnode_hash_head,nwnode) *nwhashtbl;
static u_long nwnodehash;
-static int nwhashlock = 0;
+static struct lock nwhashlock;
MALLOC_DEFINE(M_NWNODE, "NWFS node", "NWFS vnode private part");
MALLOC_DEFINE(M_NWFSHASH, "NWFS hash", "NWFS has table");
@@ -76,20 +76,18 @@ SYSCTL_PROC(_vfs_nwfs, OID_AUTO, vnprint, CTLFLAG_WR|CTLTYPE_OPAQUE,
NULL, 0, nwfs_sysctl_vnprint, "S,vnlist", "vnode hash");
void
-nwfs_hash_init(void)
-{
+nwfs_hash_init(void) {
nwhashtbl = hashinit(desiredvnodes, M_NWFSHASH, &nwnodehash);
+ lockinit(&nwhashlock, PVFS, "nwfshl", 0, 0);
}
void
-nwfs_hash_free(void)
-{
+nwfs_hash_free(void) {
free(nwhashtbl, M_NWFSHASH);
}
int
-nwfs_sysctl_vnprint(SYSCTL_HANDLER_ARGS)
-{
+nwfs_sysctl_vnprint(SYSCTL_HANDLER_ARGS) {
struct nwnode *np;
struct nwnode_hash_head *nhpp;
struct vnode *vp;
@@ -102,6 +100,7 @@ nwfs_sysctl_vnprint(SYSCTL_HANDLER_ARGS)
nhpp = &nwhashtbl[i];
for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
vp = NWTOV(np);
+ vprint(NULL, vp);
printf("%s:%d:%d:%d:%d\n",np->n_name,vp->v_usecount,vp->v_holdcnt,
np->n_fid.f_id, np->n_fid.f_parent);
}
@@ -110,6 +109,27 @@ nwfs_sysctl_vnprint(SYSCTL_HANDLER_ARGS)
}
/*
+ * Search nwnode with given fid.
+ * Hash list should be locked by caller.
+ */
+static int
+nwfs_hashlookup(struct nwmount *nmp, ncpfid fid, struct nwnode **npp)
+{
+ struct nwnode *np;
+ struct nwnode_hash_head *nhpp;
+
+ nhpp = NWNOHASH(fid);
+ LIST_FOREACH(np, nhpp, n_hash) {
+ if (nmp != np->n_mount || !NWCMPF(&fid, &np->n_fid))
+ continue;
+ if (npp)
+ *npp = np;
+ return 0;
+ }
+ return ENOENT;
+}
+
+/*
* Allocate new nwfsnode/vnode from given nwnode.
* Vnode referenced and not locked.
*/
@@ -117,33 +137,26 @@ int
nwfs_allocvp(struct mount *mp, ncpfid fid, struct vnode **vpp)
{
struct proc *p = curproc; /* XXX */
- struct nwnode *np, *np2;
+ struct nwnode *np;
struct nwnode_hash_head *nhpp;
+ struct nwmount *nmp = VFSTONWFS(mp);
struct vnode *vp;
int error;
-retry:
- nhpp = NWNOHASH(fid);
loop:
- for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
+ lockmgr(&nwhashlock, LK_EXCLUSIVE, NULL, p);
+rescan:
+ if (nwfs_hashlookup(nmp, fid, &np) == 0) {
vp = NWTOV(np);
- if (mp != vp->v_mount || !NWCMPF(&fid, &np->n_fid))
- continue;
- if (vget(vp, LK_EXCLUSIVE, p))
+ simple_lock(&vp->v_interlock);
+ lockmgr(&nwhashlock, LK_RELEASE, NULL, p);
+ if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p))
goto loop;
*vpp = vp;
return(0);
}
+ lockmgr(&nwhashlock, LK_RELEASE, NULL, p);
- /* lock list, or waiting in malloc can cause problems */
- if (nwhashlock) {
- while(nwhashlock) {
- nwhashlock = -1;
- tsleep((caddr_t) &nwhashlock, PVM, "nwfsvp", 0);
- }
- goto loop;
- }
- nwhashlock = 1;
/*
* Do the MALLOC before the getnewvnode since doing so afterward
* might cause a bogus v_data pointer to get dereferenced
@@ -152,52 +165,47 @@ loop:
MALLOC(np, struct nwnode *, sizeof *np, M_NWNODE, M_WAITOK);
error = getnewvnode(VT_NWFS, mp, nwfs_vnodeop_p, &vp);
if (error) {
- if (nwhashlock < 0)
- wakeup(&nwhashlock);
- nwhashlock = 0;
- *vpp = 0;
+ *vpp = NULL;
FREE(np, M_NWNODE);
return (error);
}
- *vpp = vp;
- bzero(np,sizeof(*np));
+ bzero(np, sizeof(*np));
vp->v_data = np;
np->n_vnode = vp;
- np->n_mount = VFSTONWFS(mp);
- np->n_fid = fid;
- for (np2 = nhpp->lh_first; np2 != 0; np2 = np->n_hash.le_next) {
- if (mp != NWTOV(np2)->v_mount || !NWCMPF(&fid, &np2->n_fid))
- continue;
+ np->n_mount = nmp;
+ lockmgr(&nwhashlock, LK_EXCLUSIVE, NULL, p);
+ /*
+ * Another process can create vnode while we blocked in malloc() or
+ * getnewvnode(). Rescan list again.
+ */
+ if (nwfs_hashlookup(nmp, fid, NULL) == 0) {
+ vp->v_data = NULL;
+ np->n_vnode = NULL;
vrele(vp);
FREE(np, M_NWNODE);
- if (nwhashlock < 0)
- wakeup(&nwhashlock);
- nwhashlock = 0;
- goto retry;
+ goto rescan;
}
+ *vpp = vp;
+ np->n_fid = fid;
+ np->n_flag |= NNEW;
+ lockinit(&vp->v_lock, PINOD, "nwnode", 0, LK_CANRECURSE);
+ nhpp = NWNOHASH(fid);
LIST_INSERT_HEAD(nhpp, np, n_hash);
- if (nwhashlock < 0)
- wakeup(&nwhashlock);
- nwhashlock = 0;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
- np->n_flag |= NNEW;
- return (error);
+ lockmgr(&nwhashlock, LK_RELEASE, NULL, p);
+ return 0;
}
int
-nwfs_lookupnp(struct nwmount *nmp, ncpfid fid, struct nwnode **npp)
+nwfs_lookupnp(struct nwmount *nmp, ncpfid fid, struct proc *p,
+ struct nwnode **npp)
{
- struct nwnode *np;
- struct nwnode_hash_head *nhpp;
+ int error;
- nhpp = NWNOHASH(fid);
- for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
- if (nmp != np->n_mount || !NWCMPF(&fid, &np->n_fid))
- continue;
- *npp = np;
- return(0);
- }
- return ENOENT;
+ lockmgr(&nwhashlock, LK_EXCLUSIVE, NULL, p);
+ error = nwfs_hashlookup(nmp, fid, npp);
+ lockmgr(&nwhashlock, LK_RELEASE, NULL, p);
+ return error;
}
/*
@@ -206,23 +214,27 @@ nwfs_lookupnp(struct nwmount *nmp, ncpfid fid, struct nwnode **npp)
int
nwfs_reclaim(ap)
struct vop_reclaim_args /* {
- struct vnode *a_vp;
+ struct vnode *a_vp;
+ struct proc *a_p;
} */ *ap;
{
struct vnode *dvp = NULL, *vp = ap->a_vp;
struct nwnode *dnp, *np = VTONW(vp);
- struct nwmount *nmp=VTONWFS(vp);
+ struct nwmount *nmp = VTONWFS(vp);
+ struct proc *p = ap->a_p;
NCPVNDEBUG("%s,%d\n", np->n_name, vp->v_usecount);
if (np->n_refparent) {
np->n_refparent = 0;
- if (nwfs_lookupnp(nmp, np->n_parent, &dnp) == 0) {
+ if (nwfs_lookupnp(nmp, np->n_parent, p, &dnp) == 0) {
dvp = dnp->n_vnode;
} else {
NCPVNDEBUG("%s: has no parent ?\n",np->n_name);
}
}
+ lockmgr(&nwhashlock, LK_EXCLUSIVE, NULL, p);
LIST_REMOVE(np, n_hash);
+ lockmgr(&nwhashlock, LK_RELEASE, NULL, p);
cache_purge(vp);
if (nmp->n_root == np) {
nmp->n_root = NULL;
@@ -255,6 +267,10 @@ nwfs_inactive(ap)
np->opened = 0;
}
VOP_UNLOCK(vp, 0, p);
+ if (np->n_flag & NSHOULDFREE) {
+ cache_purge(vp);
+ vgone(vp);
+ }
return (0);
}
/*
OpenPOWER on IntegriCloud