--- a/sys/vfs/nfs/nfs_node.c +++ b/sys/vfs/nfs/nfs_node.c @@ -191,13 +191,23 @@ bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) { continue; } + /* + * Connect the fresh (still VNON / v_data == NULL) vnode to + * np before dropping it: vx_put() forces VREF_FINALIZE for + * VNON vnodes (sys/kern/vfs_lock.c), running + * VOP_INACTIVE -> nfs_inactive() and later + * VOP_RECLAIM -> nfs_reclaim(), both of which dereference + * vp->v_data. A bare vx_put() of the unconnected vnode + * NULL-derefs in nfs_inactive (np = VTONFS(vp) == NULL at + * nfs_node.c:393) and panics. Leaving (np, vp) connected + * lets reclaim tear the pair down correctly: nfs_reclaim() + * sees n_hash.le_prev == NULL, skips the LIST_REMOVE, and + * frees np (and a big n_fhp) for us. + */ + np->n_vnode = vp; + vp->v_data = np; vx_put(vp); lockmgr(&nfsnhash_lock, LK_RELEASE); - - if (np->n_fhsize > NFS_SMALLFH) - kfree((caddr_t)np->n_fhp, M_NFSBIGFH); - np->n_fhp = NULL; - kfree_obj(np, nmp->nm_mnode); goto retry; } @@ -270,6 +280,11 @@ } if (NFSTOV(np) != vp) { vput(vp); + /* + * vp is stale after vput(): NULL it or the restart + * double-vputs the same vnode (refcount underflow). + */ + vp = NULL; goto loop; } *npp = np; @@ -333,14 +348,15 @@ bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) { continue; } + /* + * Same discipline as nfs_nget(): never vx_put() a freshly + * created VNON vnode with v_data still NULL (see above). + */ + np->n_vnode = vp; + vp->v_data = np; vx_put(vp); lockmgr(&nfsnhash_lock, LK_RELEASE); - if (np->n_fhsize > NFS_SMALLFH) - kfree((caddr_t)np->n_fhp, M_NFSBIGFH); - np->n_fhp = NULL; - kfree_obj(np, nmp->nm_mnode); - /* * vp state is retained on retry/loop so we must NULL it * out here or fireworks may ensue.