[prev in list] [next in list] [prev in thread] [next in thread] 

List:       netbsd-tech-kern
Subject:    Re: Add operation vcache_rekey
From:       "J. Hannken-Illjes" <hannken () eis ! cs ! tu-bs ! de>
Date:       2014-06-30 9:25:52
Message-ID: 29C56CF3-5492-4799-84E8-D9C413B0182E () eis ! cs ! tu-bs ! de
[Download RAW message or body]

On 26 Jun 2014, at 05:33, David Holland <dholland-tech@netbsd.org> wrote:

> On Wed, Jun 25, 2014 at 09:46:16AM +0000, Taylor R Campbell wrote:
>> Also, I wonder whether any file systems will ever change the length of
>> the key for a vnode.  Probably not.
> 
> As I recall from when I was looking at this a couple weeks ago (feels
> like a minor eternity ago, but it can't be much longer than that)
> ... yes, they might.

David, they will.  nfs/nfs_vnops.c::nfs_lookitup() will change the
file handle in the "*npp != NULL" case.  Here both the key and its
length may change.  I overlooked this one as it didn't update the
rbtree.  To make it worse the memory holding the file handle may
get reallocated so it is impossible to do the rekey with one call.

The attached diff adds

int
vcache_rekey_enter(struct mount *mp, struct vnode *vp,
    const void *old_key, size_t old_key_len,
    const void *new_key, size_t new_key_len)

to prepare the key change.  It will allocate a cache node for the new
key and lock both the old and the new cache node.  It is an error if
the new node already exists.

void
vcache_rekey_exit(struct mount *mp, struct vnode *vp,
    const void *old_key, size_t old_key_len,
    const void *new_key, size_t new_key_len)

to finish the key change.  It will remove the old cache node and setup
and unlock the new cache node.

Comments, suggestions or objections?

--
J. Hannken-Illjes - hannken@eis.cs.tu-bs.de - TU Braunschweig (Germany)

["vcache.diff" (vcache.diff)]

Index: sys/sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.248
diff -p -u -4 -r1.248 vnode.h
--- sys/sys/vnode.h	25 May 2014 13:51:26 -0000	1.248
+++ sys/sys/vnode.h	30 Jun 2014 09:03:41 -0000
@@ -556,8 +556,12 @@ struct vnode *
 	vnalloc(struct mount *);
 void	vnfree(struct vnode *);
 void	vremfree(struct vnode *);
 int	vcache_get(struct mount *, const void *, size_t, struct vnode **);
+int	vcache_rekey_enter(struct mount *, struct vnode *,
+	    const void *, size_t, const void *, size_t);
+void	vcache_rekey_exit(struct mount *, struct vnode *,
+	    const void *, size_t, const void *, size_t);
 void	vcache_remove(struct mount *, const void *, size_t);
 
 /* see vnsubr(9) */
 int	vn_bwrite(void *);
Index: sys/kern/vfs_vnode.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_vnode.c,v
retrieving revision 1.36
diff -p -u -4 -r1.36 vfs_vnode.c
--- sys/kern/vfs_vnode.c	8 May 2014 08:21:53 -0000	1.36
+++ sys/kern/vfs_vnode.c	30 Jun 2014 09:03:37 -0000
@@ -1323,8 +1323,89 @@ again:
 	return 0;
 }
 
 /*
+ * Prepare key change: lock old and new cache node.
+ * Return an error if the new node already exists.
+ */
+int
+vcache_rekey_enter(struct mount *mp, struct vnode *vp,
+    const void *old_key, size_t old_key_len,
+    const void *new_key, size_t new_key_len)
+{
+	uint32_t old_hash, new_hash;
+	struct vcache_key old_vcache_key, new_vcache_key;
+	struct vcache_node *node, *new_node;
+
+	old_vcache_key.vk_mount = mp;
+	old_vcache_key.vk_key = old_key;
+	old_vcache_key.vk_key_len = old_key_len;
+	old_hash = vcache_hash(&old_vcache_key);
+
+	new_vcache_key.vk_mount = mp;
+	new_vcache_key.vk_key = new_key;
+	new_vcache_key.vk_key_len = new_key_len;
+	new_hash = vcache_hash(&new_vcache_key);
+
+	new_node = pool_cache_get(vcache.pool, PR_WAITOK);
+	new_node->vn_vnode = NULL;
+	new_node->vn_key = new_vcache_key;
+
+	mutex_enter(&vcache.lock);
+	node = vcache_hash_lookup(&new_vcache_key, new_hash);
+	if (node != NULL) {
+		mutex_exit(&vcache.lock);
+		pool_cache_put(vcache.pool, new_node);
+		return EEXIST;
+	}
+	SLIST_INSERT_HEAD(&vcache.hashtab[new_hash & vcache.hashmask],
+	    new_node, vn_hash);
+	node = vcache_hash_lookup(&old_vcache_key, old_hash);
+	KASSERT(node != NULL);
+	KASSERT(node->vn_vnode == vp);
+	node->vn_vnode = NULL;
+	node->vn_key = old_vcache_key;
+	mutex_exit(&vcache.lock);
+	return 0;
+}
+
+/*
+ * Key change complete: remove old node and unlock new node.
+ */
+void
+vcache_rekey_exit(struct mount *mp, struct vnode *vp,
+    const void *old_key, size_t old_key_len,
+    const void *new_key, size_t new_key_len)
+{
+	uint32_t old_hash, new_hash;
+	struct vcache_key old_vcache_key, new_vcache_key;
+	struct vcache_node *node;
+
+	old_vcache_key.vk_mount = mp;
+	old_vcache_key.vk_key = old_key;
+	old_vcache_key.vk_key_len = old_key_len;
+	old_hash = vcache_hash(&old_vcache_key);
+
+	new_vcache_key.vk_mount = mp;
+	new_vcache_key.vk_key = new_key;
+	new_vcache_key.vk_key_len = new_key_len;
+	new_hash = vcache_hash(&new_vcache_key);
+
+	mutex_enter(&vcache.lock);
+	node = vcache_hash_lookup(&new_vcache_key, new_hash);
+	KASSERT(node != NULL && node->vn_vnode == NULL);
+	node->vn_vnode = vp;
+	node->vn_key = new_vcache_key;
+	node = vcache_hash_lookup(&old_vcache_key, old_hash);
+	KASSERT(node != NULL);
+	KASSERT(node->vn_vnode == NULL);
+	SLIST_REMOVE(&vcache.hashtab[old_hash & vcache.hashmask],
+	    node, vcache_node, vn_hash);
+	mutex_exit(&vcache.lock);
+	pool_cache_put(vcache.pool, node);
+}
+
+/*
  * Remove a vnode / fs node pair from the cache.
  */
 void
 vcache_remove(struct mount *mp, const void *key, size_t key_len)
Index: sys/nfs/nfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/nfs/nfs_vnops.c,v
retrieving revision 1.304
diff -p -u -4 -r1.304 nfs_vnops.c
--- sys/nfs/nfs_vnops.c	7 Feb 2014 15:29:22 -0000	1.304
+++ sys/nfs/nfs_vnops.c	30 Jun 2014 09:03:40 -0000
@@ -2942,14 +2942,14 @@ nfs_lookitup(struct vnode *dvp, const ch
 	int32_t t1, t2;
 	struct vnode *newvp = (struct vnode *)0;
 	struct nfsnode *np, *dnp = VTONFS(dvp);
 	char *bpos, *dpos, *cp2;
-	int error = 0, fhlen;
+	int error = 0, ofhlen, fhlen;
 #ifndef NFS_V2_ONLY
 	int attrflag;
 #endif
 	struct mbuf *mreq, *mrep, *md, *mb;
-	nfsfh_t *nfhp;
+	nfsfh_t *ofhp, *nfhp;
 	const int v3 = NFS_ISV3(dvp);
 
 	nfsstats.rpccnt[NFSPROC_LOOKUP]++;
 	nfsm_reqhead(dnp, NFSPROC_LOOKUP,
@@ -2960,8 +2960,19 @@ nfs_lookitup(struct vnode *dvp, const ch
 	if (npp && !error) {
 		nfsm_getfh(nfhp, fhlen, v3);
 		if (*npp) {
 		    np = *npp;
+		    newvp = NFSTOV(np);
+		    ofhlen = np->n_fhsize;
+		    ofhp = kmem_alloc(ofhlen, KM_SLEEP);
+		    memcpy(ofhp, np->n_fhp, ofhlen);
+		    error = vcache_rekey_enter(newvp->v_mount, newvp,
+			ofhp, ofhlen, nfhp, fhlen);
+		    if (error) {
+			kmem_free(ofhp, ofhlen);
+			m_freem(mrep);
+			return error;
+		    }
 		    if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) {
 			kmem_free(np->n_fhp, np->n_fhsize);
 			np->n_fhp = &np->n_fh;
 		    }
@@ -2970,9 +2981,11 @@ nfs_lookitup(struct vnode *dvp, const ch
 			np->n_fhp = kmem_alloc(fhlen, KM_SLEEP);
 #endif
 		    memcpy(np->n_fhp, nfhp, fhlen);
 		    np->n_fhsize = fhlen;
-		    newvp = NFSTOV(np);
+		    vcache_rekey_exit(newvp->v_mount, newvp,
+			ofhp, ofhlen, np->n_fhp, fhlen);
+		    kmem_free(ofhp, ofhlen);
 		} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
 		    vref(dvp);
 		    newvp = dvp;
 		    np = dnp;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic