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;
