When mnt_vnode_next_active iterator cannot lock the next vnode and

yields, specify the user priority for the yield.  Otherwise, a
higher-priority (kernel) thread could fall into the priority-inversion
with the thread owning the mutex lock.

On single-processor machines or UP kernels, do not loop adaptively
when the next vnode cannot be locked, instead yield unconditionally.

Restructure the iteration initializer and the iterator to remove code
duplication.  Put the code to fetch and lock a vnode next to the
current marker, into the mnt_vnode_next_active() function, and use it
instead of repeating the loop.

Reported by:	hrs, rmacklem
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	3 days
This commit is contained in:
Konstantin Belousov 2012-12-15 02:04:46 +00:00
parent 9f37ee804a
commit 14df601e47
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=244240
2 changed files with 54 additions and 63 deletions

View file

@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/reboot.h>
#include <sys/sched.h>
#include <sys/sleepqueue.h>
#include <sys/smp.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
@ -4710,32 +4711,54 @@ __mnt_vnode_markerfree_all(struct vnode **mvp, struct mount *mp)
* These are helper functions for filesystems to traverse their
* active vnodes. See MNT_VNODE_FOREACH_ACTIVE() in sys/mount.h
*/
struct vnode *
__mnt_vnode_next_active(struct vnode **mvp, struct mount *mp)
static void
mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *mp)
{
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
MNT_ILOCK(mp);
MNT_REL(mp);
MNT_IUNLOCK(mp);
free(*mvp, M_VNODE_MARKER);
*mvp = NULL;
}
#ifdef SMP
#define ALWAYS_YIELD (mp_ncpus == 1)
#else
#define ALWAYS_YIELD 1
#endif
static struct vnode *
mnt_vnode_next_active(struct vnode **mvp, struct mount *mp)
{
struct vnode *vp, *nvp;
if (should_yield())
kern_yield(PRI_UNCHANGED);
mtx_lock(&vnode_free_list_mtx);
restart:
mtx_assert(&vnode_free_list_mtx, MA_OWNED);
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
restart:
vp = TAILQ_NEXT(*mvp, v_actfreelist);
TAILQ_REMOVE(&mp->mnt_activevnodelist, *mvp, v_actfreelist);
while (vp != NULL) {
if (vp->v_type == VMARKER) {
vp = TAILQ_NEXT(vp, v_actfreelist);
continue;
}
if (!VI_TRYLOCK(vp)) {
if (should_yield()) {
if (ALWAYS_YIELD || should_yield()) {
TAILQ_INSERT_BEFORE(vp, *mvp, v_actfreelist);
mtx_unlock(&vnode_free_list_mtx);
kern_yield(PRI_UNCHANGED);
kern_yield(PRI_USER);
mtx_lock(&vnode_free_list_mtx);
goto restart;
}
goto restart;
continue;
}
if (vp->v_mount == mp && vp->v_type != VMARKER &&
(vp->v_iflag & VI_DOOMED) == 0)
KASSERT(vp->v_type != VMARKER, ("locked marker %p", vp));
KASSERT(vp->v_mount == mp || vp->v_mount == NULL,
("alien vnode on the active list %p %p", vp, mp));
if (vp->v_mount == mp && (vp->v_iflag & VI_DOOMED) == 0)
break;
nvp = TAILQ_NEXT(vp, v_actfreelist);
VI_UNLOCK(vp);
@ -4745,22 +4768,31 @@ __mnt_vnode_next_active(struct vnode **mvp, struct mount *mp)
/* Check if we are done */
if (vp == NULL) {
mtx_unlock(&vnode_free_list_mtx);
__mnt_vnode_markerfree_active(mvp, mp);
mtx_assert(MNT_MTX(mp), MA_NOTOWNED);
mnt_vnode_markerfree_active(mvp, mp);
return (NULL);
}
TAILQ_REMOVE(&mp->mnt_activevnodelist, *mvp, v_actfreelist);
TAILQ_INSERT_AFTER(&mp->mnt_activevnodelist, vp, *mvp, v_actfreelist);
mtx_unlock(&vnode_free_list_mtx);
ASSERT_VI_LOCKED(vp, "active iter");
KASSERT((vp->v_iflag & VI_ACTIVE) != 0, ("Non-active vp %p", vp));
return (vp);
}
#undef ALWAYS_YIELD
struct vnode *
__mnt_vnode_next_active(struct vnode **mvp, struct mount *mp)
{
if (should_yield())
kern_yield(PRI_UNCHANGED);
mtx_lock(&vnode_free_list_mtx);
return (mnt_vnode_next_active(mvp, mp));
}
struct vnode *
__mnt_vnode_first_active(struct vnode **mvp, struct mount *mp)
{
struct vnode *vp, *nvp;
struct vnode *vp;
*mvp = malloc(sizeof(struct vnode), M_VNODE_MARKER, M_WAITOK | M_ZERO);
MNT_ILOCK(mp);
@ -4770,44 +4802,14 @@ __mnt_vnode_first_active(struct vnode **mvp, struct mount *mp)
(*mvp)->v_mount = mp;
mtx_lock(&vnode_free_list_mtx);
restart:
vp = TAILQ_FIRST(&mp->mnt_activevnodelist);
while (vp != NULL) {
if (vp->v_type == VMARKER) {
vp = TAILQ_NEXT(vp, v_actfreelist);
continue;
}
if (!VI_TRYLOCK(vp)) {
if (should_yield()) {
mtx_unlock(&vnode_free_list_mtx);
kern_yield(PRI_UNCHANGED);
mtx_lock(&vnode_free_list_mtx);
}
goto restart;
}
if (vp->v_mount == mp && vp->v_type != VMARKER &&
(vp->v_iflag & VI_DOOMED) == 0)
break;
nvp = TAILQ_NEXT(vp, v_actfreelist);
VI_UNLOCK(vp);
vp = nvp;
}
/* Check if we are done */
if (vp == NULL) {
mtx_unlock(&vnode_free_list_mtx);
MNT_ILOCK(mp);
MNT_REL(mp);
MNT_IUNLOCK(mp);
free(*mvp, M_VNODE_MARKER);
*mvp = NULL;
mnt_vnode_markerfree_active(mvp, mp);
return (NULL);
}
TAILQ_INSERT_AFTER(&mp->mnt_activevnodelist, vp, *mvp, v_actfreelist);
mtx_unlock(&vnode_free_list_mtx);
ASSERT_VI_LOCKED(vp, "active iter first");
KASSERT((vp->v_iflag & VI_ACTIVE) != 0, ("Non-active vp %p", vp));
return (vp);
TAILQ_INSERT_BEFORE(vp, *mvp, v_actfreelist);
return (mnt_vnode_next_active(mvp, mp));
}
void
@ -4817,14 +4819,8 @@ __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *mp)
if (*mvp == NULL)
return;
KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
mtx_lock(&vnode_free_list_mtx);
TAILQ_REMOVE(&mp->mnt_activevnodelist, *mvp, v_actfreelist);
mtx_unlock(&vnode_free_list_mtx);
MNT_ILOCK(mp);
MNT_REL(mp);
MNT_IUNLOCK(mp);
free(*mvp, M_VNODE_MARKER);
*mvp = NULL;
mnt_vnode_markerfree_active(mvp, mp);
}

View file

@ -218,17 +218,12 @@ struct vnode *__mnt_vnode_next_active(struct vnode **mvp, struct mount *mp);
struct vnode *__mnt_vnode_first_active(struct vnode **mvp, struct mount *mp);
void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
#define MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) \
for (vp = __mnt_vnode_first_active(&(mvp), (mp)); \
#define MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) \
for (vp = __mnt_vnode_first_active(&(mvp), (mp)); \
(vp) != NULL; vp = __mnt_vnode_next_active(&(mvp), (mp)))
#define MNT_VNODE_FOREACH_ACTIVE_ABORT(mp, mvp) \
do { \
MNT_ILOCK(mp); \
__mnt_vnode_markerfree_active(&(mvp), (mp)); \
/* MNT_IUNLOCK(mp); -- done in above function */ \
mtx_assert(MNT_MTX(mp), MA_NOTOWNED); \
} while (0)
__mnt_vnode_markerfree_active(&(mvp), (mp))
/*
* Definitions for MNT_VNODE_FOREACH.