Date: Mon, 29 Dec 2008 12:07:18 +0000 (UTC) From: Konstantin Belousov <kib@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r186560 - head/sys/fs/pseudofs Message-ID: <200812291207.mBTC7IOg056727@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kib Date: Mon Dec 29 12:07:18 2008 New Revision: 186560 URL: http://svn.freebsd.org/changeset/base/186560 Log: After the pfs_vncache_mutex is dropped, another thread may attempt to do pfs_vncache_alloc() for the same pfs_node and pid. In this case, we could end up with two vnodes for the pair. Recheck the cache under the locked pfs_vncache_mutex after all sleeping operations are done [1]. This case mostly cannot happen now because pseudofs uses exclusive vnode locking for lookup. But it does drop the vnode lock for dotdot lookups, and Marcus' pseudofs_vptocnp implementation is vulnerable too. Do not call free() on the struct pfs_vdata after insmntque() failure, because vp->v_data points to the structure, and pseudofs_reclaim() frees it by the call to pfs_vncache_free(). Tested by: pho [1] Approved by: des MFC after: 2 weeks Modified: head/sys/fs/pseudofs/pseudofs_vncache.c Modified: head/sys/fs/pseudofs/pseudofs_vncache.c ============================================================================== --- head/sys/fs/pseudofs/pseudofs_vncache.c Mon Dec 29 10:26:02 2008 (r186559) +++ head/sys/fs/pseudofs/pseudofs_vncache.c Mon Dec 29 12:07:18 2008 (r186560) @@ -111,7 +111,7 @@ int pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, pid_t pid) { - struct pfs_vdata *pvd; + struct pfs_vdata *pvd, *pvd2; struct vnode *vp; int error; @@ -146,19 +146,11 @@ retry: } } mtx_unlock(&pfs_vncache_mutex); - ++pfs_vncache_misses; /* nope, get a new one */ pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK); - mtx_lock(&pfs_vncache_mutex); - if (++pfs_vncache_entries > pfs_vncache_maxentries) - pfs_vncache_maxentries = pfs_vncache_entries; - mtx_unlock(&pfs_vncache_mutex); error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); if (error) { - mtx_lock(&pfs_vncache_mutex); - --pfs_vncache_entries; - mtx_unlock(&pfs_vncache_mutex); free(pvd, M_PFSVNCACHE); return (error); } @@ -200,14 +192,35 @@ retry: vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); error = insmntque(*vpp, mp); if (error != 0) { - mtx_lock(&pfs_vncache_mutex); - --pfs_vncache_entries; - mtx_unlock(&pfs_vncache_mutex); - free(pvd, M_PFSVNCACHE); *vpp = NULLVP; return (error); } +retry2: mtx_lock(&pfs_vncache_mutex); + /* + * Other thread may race with us, creating the entry we are + * going to insert into the cache. Recheck after + * pfs_vncache_mutex is reacquired. + */ + for (pvd2 = pfs_vncache; pvd2; pvd2 = pvd2->pvd_next) { + if (pvd2->pvd_pn == pn && pvd2->pvd_pid == pid && + pvd2->pvd_vnode->v_mount == mp) { + vp = pvd2->pvd_vnode; + VI_LOCK(vp); + mtx_unlock(&pfs_vncache_mutex); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { + ++pfs_vncache_hits; + vgone(*vpp); + *vpp = vp; + cache_purge(vp); + return (0); + } + goto retry2; + } + } + ++pfs_vncache_misses; + if (++pfs_vncache_entries > pfs_vncache_maxentries) + pfs_vncache_maxentries = pfs_vncache_entries; pvd->pvd_prev = NULL; pvd->pvd_next = pfs_vncache; if (pvd->pvd_next)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812291207.mBTC7IOg056727>