1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-11-23 07:31:31 +00:00

vfs: fully lockless v_writecount adjustment

Reviewed by:	kib
Differential Revision:	https://reviews.freebsd.org/D33128
This commit is contained in:
Mateusz Guzik 2021-11-26 13:33:28 +01:00
parent 4dcdf3987c
commit e511bd1406

View File

@ -1293,91 +1293,86 @@ static int
vop_stdis_text(struct vop_is_text_args *ap) vop_stdis_text(struct vop_is_text_args *ap)
{ {
return (ap->a_vp->v_writecount < 0); return (atomic_load_int(&ap->a_vp->v_writecount) < 0);
} }
int int
vop_stdset_text(struct vop_set_text_args *ap) vop_stdset_text(struct vop_set_text_args *ap)
{ {
struct vnode *vp; struct vnode *vp;
int error, n; int n;
bool gotref;
vp = ap->a_vp; vp = ap->a_vp;
/*
* Avoid the interlock if execs are already present.
*/
n = atomic_load_int(&vp->v_writecount); n = atomic_load_int(&vp->v_writecount);
for (;;) { for (;;) {
if (n > -1) { if (__predict_false(n > 0)) {
break; return (ETXTBSY);
} }
/*
* Transition point, we may need to grab a reference on the vnode.
*
* Take the ref early As a safety measure against bogus calls
* to vop_stdunset_text.
*/
if (n == 0) {
gotref = false;
if ((vn_irflag_read(vp) & VIRF_TEXT_REF) != 0) {
vref(vp);
gotref = true;
}
if (atomic_fcmpset_int(&vp->v_writecount, &n, -1)) {
return (0);
}
if (gotref) {
vunref(vp);
}
continue;
}
MPASS(n < 0);
if (atomic_fcmpset_int(&vp->v_writecount, &n, n - 1)) { if (atomic_fcmpset_int(&vp->v_writecount, &n, n - 1)) {
return (0); return (0);
} }
} }
__assert_unreachable();
VI_LOCK(vp);
if (vp->v_writecount > 0) {
error = ETXTBSY;
} else {
/*
* If requested by fs, keep a use reference to the
* vnode until the last text reference is released.
*/
if ((vn_irflag_read(vp) & VIRF_TEXT_REF) != 0) {
if (vp->v_writecount == 0) {
vrefl(vp);
}
}
atomic_subtract_int(&vp->v_writecount, 1);
error = 0;
}
VI_UNLOCK(vp);
return (error);
} }
static int static int
vop_stdunset_text(struct vop_unset_text_args *ap) vop_stdunset_text(struct vop_unset_text_args *ap)
{ {
struct vnode *vp; struct vnode *vp;
int error, n; int n;
bool last;
vp = ap->a_vp; vp = ap->a_vp;
/*
* Avoid the interlock if this is not the last exec.
*/
n = atomic_load_int(&vp->v_writecount); n = atomic_load_int(&vp->v_writecount);
for (;;) { for (;;) {
if (n >= -1) { if (__predict_false(n >= 0)) {
break; return (EINVAL);
} }
/*
* Transition point, we may need to release a reference on the vnode.
*/
if (n == -1) {
if (atomic_fcmpset_int(&vp->v_writecount, &n, 0)) {
if ((vn_irflag_read(vp) & VIRF_TEXT_REF) != 0) {
vunref(vp);
}
return (0);
}
continue;
}
MPASS(n < -1);
if (atomic_fcmpset_int(&vp->v_writecount, &n, n + 1)) { if (atomic_fcmpset_int(&vp->v_writecount, &n, n + 1)) {
return (0); return (0);
} }
} }
__assert_unreachable();
last = false;
VI_LOCK(vp);
if (vp->v_writecount < 0) {
if ((vn_irflag_read(vp) & VIRF_TEXT_REF) != 0) {
if (vp->v_writecount == -1) {
last = true;
}
}
atomic_add_int(&vp->v_writecount, 1);
error = 0;
} else {
error = EINVAL;
}
VI_UNLOCK(vp);
if (last)
vunref(vp);
return (error);
} }
static int __always_inline static int __always_inline
@ -1385,7 +1380,7 @@ vop_stdadd_writecount_impl(struct vop_add_writecount_args *ap, bool handle_msync
{ {
struct vnode *vp; struct vnode *vp;
struct mount *mp __diagused; struct mount *mp __diagused;
int error; int n;
vp = ap->a_vp; vp = ap->a_vp;
@ -1400,20 +1395,26 @@ vop_stdadd_writecount_impl(struct vop_add_writecount_args *ap, bool handle_msync
} }
#endif #endif
VI_LOCK_FLAGS(vp, MTX_DUPOK); n = atomic_load_int(&vp->v_writecount);
if (__predict_false(vp->v_writecount < 0)) { for (;;) {
error = ETXTBSY; if (__predict_false(n < 0)) {
} else { return (ETXTBSY);
VNASSERT(vp->v_writecount + ap->a_inc >= 0, vp, }
("neg writecount increment %d", ap->a_inc));
if (handle_msync && vp->v_writecount == 0) { VNASSERT(n + ap->a_inc >= 0, vp,
vlazy(vp); ("neg writecount increment %d + %d = %d", n, ap->a_inc,
n + ap->a_inc));
if (n == 0) {
if (handle_msync) {
vlazy(vp);
}
}
if (atomic_fcmpset_int(&vp->v_writecount, &n, n + ap->a_inc)) {
return (0);
} }
vp->v_writecount += ap->a_inc;
error = 0;
} }
VI_UNLOCK(vp); __assert_unreachable();
return (error);
} }
int int