Cleanup ffs fsync and make devices on wapbl enabled file systems work here:

- Replace the ugly sync loop in ffs_full_fsync() and ffs_vfs_fsync() with
  vflushbuf().  This loop is a relic of softdeps and not needed anymore.

- Add ffs_spec_fsync() for device nodes on ffs file systems that calls
  spec_fsync() like all other file systems do and then updates the ctime.

Discussed on tech-kern.

Should fix PRs:
PR #41192 wapbl diagnostic panic during cgdconfig
PR #41977 kernel diagnostic assertion "rw_lock_held(&wl->wl_rwlock)" failed
PR #42149 wapbl locking panic if watching DVD
PR #42551 Lockdebug assert in wapbl when running zpool
This commit is contained in:
hannken 2011-04-27 07:24:52 +00:00
parent 00d08a9058
commit bb3ca01e60
3 changed files with 77 additions and 177 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ffs_extern.h,v 1.76 2011/03/06 17:08:38 bouyer Exp $ */
/* $NetBSD: ffs_extern.h,v 1.77 2011/04/27 07:24:52 hannken Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
@ -125,6 +125,7 @@ int ffs_cgupdate(struct ufsmount *, int);
int ffs_read(void *);
int ffs_write(void *);
int ffs_fsync(void *);
int ffs_spec_fsync(void *);
int ffs_reclaim(void *);
int ffs_getpages(void *);
void ffs_gop_size(struct vnode *, off_t, off_t *, int);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ffs_vfsops.c,v 1.265 2011/03/27 08:04:50 mlelstv Exp $ */
/* $NetBSD: ffs_vfsops.c,v 1.266 2011/04/27 07:24:53 hannken Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.265 2011/03/27 08:04:50 mlelstv Exp $");
__KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c,v 1.266 2011/04/27 07:24:53 hannken Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@ -2071,14 +2071,12 @@ ffs_suspendctl(struct mount *mp, int cmd)
}
/*
* Synch vnode for a mounted file system. This is called for foreign
* vnodes, i.e. non-ffs.
* Synch vnode for a mounted file system.
*/
static int
ffs_vfs_fsync(vnode_t *vp, int flags)
{
int error, passes, skipmeta, i, pflags;
buf_t *bp, *nbp;
int error, i, pflags;
#ifdef WAPBL
struct mount *mp;
#endif
@ -2130,80 +2128,9 @@ ffs_vfs_fsync(vnode_t *vp, int flags)
}
#endif /* WAPBL */
/*
* Write out metadata for non-logging file systems. XXX This block
* should be simplified now that softdep is gone.
*/
passes = NIADDR + 1;
skipmeta = 0;
if (flags & FSYNC_WAIT)
skipmeta = 1;
loop:
mutex_enter(&bufcache_lock);
LIST_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) {
bp->b_cflags &= ~BC_SCANNED;
}
for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
nbp = LIST_NEXT(bp, b_vnbufs);
if (bp->b_cflags & (BC_BUSY | BC_SCANNED))
continue;
if ((bp->b_oflags & BO_DELWRI) == 0)
panic("ffs_fsync: not dirty");
if (skipmeta && bp->b_lblkno < 0)
continue;
bp->b_cflags |= BC_BUSY | BC_VFLUSH | BC_SCANNED;
mutex_exit(&bufcache_lock);
/*
* On our final pass through, do all I/O synchronously
* so that we can find out if our flush is failing
* because of write errors.
*/
if (passes > 0 || !(flags & FSYNC_WAIT))
(void) bawrite(bp);
else if ((error = bwrite(bp)) != 0)
return (error);
/*
* Since we unlocked during the I/O, we need
* to start from a known point.
*/
mutex_enter(&bufcache_lock);
nbp = LIST_FIRST(&vp->v_dirtyblkhd);
}
mutex_exit(&bufcache_lock);
if (skipmeta) {
skipmeta = 0;
goto loop;
}
if ((flags & FSYNC_WAIT) != 0) {
mutex_enter(&vp->v_interlock);
while (vp->v_numoutput) {
cv_wait(&vp->v_cv, &vp->v_interlock);
}
mutex_exit(&vp->v_interlock);
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
/*
* Block devices associated with filesystems may
* have new I/O requests posted for them even if
* the vnode is locked, so no amount of trying will
* get them clean. Thus we give block devices a
* good effort, then just give up. For all other file
* types, go around and try again until it is clean.
*/
if (passes > 0) {
passes--;
goto loop;
}
#ifdef DIAGNOSTIC
if (vp->v_type != VBLK)
vprint("ffs_fsync: dirty", vp);
#endif
}
}
error = vflushbuf(vp, (flags & FSYNC_WAIT) != 0);
if (error == 0 && (flags & FSYNC_CACHE) != 0) {
i = 1;
(void)VOP_IOCTL(vp, DIOCCACHESYNC, &i, FWRITE,
kauth_cred_get());
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: ffs_vnops.c,v 1.117 2011/04/15 15:54:11 hannken Exp $ */
/* $NetBSD: ffs_vnops.c,v 1.118 2011/04/27 07:24:53 hannken Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.117 2011/04/15 15:54:11 hannken Exp $");
__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.118 2011/04/27 07:24:53 hannken Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@ -175,7 +175,7 @@ const struct vnodeopv_entry_desc ffs_specop_entries[] = {
{ &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */
{ &vop_revoke_desc, spec_revoke }, /* revoke */
{ &vop_mmap_desc, spec_mmap }, /* mmap */
{ &vop_fsync_desc, ffs_fsync }, /* fsync */
{ &vop_fsync_desc, ffs_spec_fsync }, /* fsync */
{ &vop_seek_desc, spec_seek }, /* seek */
{ &vop_remove_desc, spec_remove }, /* remove */
{ &vop_link_desc, spec_link }, /* link */
@ -265,6 +265,61 @@ const struct vnodeopv_desc ffs_fifoop_opv_desc =
#include <ufs/ufs/ufs_readwrite.c>
int
ffs_spec_fsync(void *v)
{
struct vop_fsync_args /* {
struct vnode *a_vp;
kauth_cred_t a_cred;
int a_flags;
off_t a_offlo;
off_t a_offhi;
struct lwp *a_l;
} */ *ap = v;
int error, flags, uflags;
struct vnode *vp;
struct mount *mp;
flags = ap->a_flags;
uflags = UPDATE_CLOSE | ((flags & FSYNC_WAIT) ? UPDATE_WAIT : 0);
vp = ap->a_vp;
mp = vp->v_mount;
fstrans_start(mp, FSTRANS_LAZY);
error = spec_fsync(v);
if (error)
goto out;
#ifdef WAPBL
if (mp && mp->mnt_wapbl) {
/*
* Don't bother writing out metadata if the syncer is
* making the request. We will let the sync vnode
* write it out in a single burst through a call to
* VFS_SYNC().
*/
if ((flags & (FSYNC_DATAONLY | FSYNC_LAZY)) != 0)
goto out;
if ((VTOI(vp)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE
| IN_MODIFY | IN_MODIFIED | IN_ACCESSED)) != 0) {
error = UFS_WAPBL_BEGIN(mp);
if (error != 0)
goto out;
error = ffs_update(vp, NULL, NULL, uflags);
UFS_WAPBL_END(mp);
}
goto out;
}
#endif /* WAPBL */
error = ffs_update(vp, NULL, NULL, uflags);
out:
fstrans_done(mp);
return error;
}
int
ffs_fsync(void *v)
{
@ -399,32 +454,27 @@ out:
int
ffs_full_fsync(struct vnode *vp, int flags)
{
struct buf *bp, *nbp;
int error, passes, skipmeta, waitfor, i;
int error, i, uflags;
struct mount *mp;
KASSERT(VTOI(vp) != NULL);
KASSERT(vp->v_tag == VT_UFS);
KASSERT(VTOI(vp) != NULL);
KASSERT(vp->v_type != VCHR && vp->v_type != VBLK);
error = 0;
uflags = UPDATE_CLOSE | ((flags & FSYNC_WAIT) ? UPDATE_WAIT : 0);
mp = vp->v_mount;
if (vp->v_type == VBLK && vp->v_specmountpoint != NULL) {
mp = vp->v_specmountpoint;
} else {
mp = vp->v_mount;
}
/*
* Flush all dirty data associated with the vnode.
*/
if (vp->v_type == VREG || vp->v_type == VBLK) {
if (vp->v_type == VREG) {
int pflags = PGO_ALLPAGES | PGO_CLEANIT;
if ((flags & FSYNC_WAIT))
pflags |= PGO_SYNCIO;
if (vp->v_type == VREG &&
fstrans_getstate(mp) == FSTRANS_SUSPENDING)
if (fstrans_getstate(mp) == FSTRANS_SUSPENDING)
pflags |= PGO_FREE;
mutex_enter(&vp->v_interlock);
error = VOP_PUTPAGES(vp, 0, 0, pflags);
@ -433,7 +483,6 @@ ffs_full_fsync(struct vnode *vp, int flags)
}
#ifdef WAPBL
mp = wapbl_vptomp(vp);
if (mp && mp->mnt_wapbl) {
/*
* Don't bother writing out metadata if the syncer is
@ -449,8 +498,7 @@ ffs_full_fsync(struct vnode *vp, int flags)
error = UFS_WAPBL_BEGIN(mp);
if (error)
return error;
error = ffs_update(vp, NULL, NULL, UPDATE_CLOSE |
((flags & FSYNC_WAIT) ? UPDATE_WAIT : 0));
error = ffs_update(vp, NULL, NULL, uflags);
UFS_WAPBL_END(mp);
}
if (error || (flags & FSYNC_NOLOG) != 0)
@ -477,87 +525,11 @@ ffs_full_fsync(struct vnode *vp, int flags)
}
#endif /* WAPBL */
/*
* Write out metadata for non-logging file systems. XXX This block
* should be simplified now that softdep is gone.
*/
passes = NIADDR + 1;
skipmeta = 0;
if (flags & FSYNC_WAIT)
skipmeta = 1;
loop:
mutex_enter(&bufcache_lock);
LIST_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) {
bp->b_cflags &= ~BC_SCANNED;
}
for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
nbp = LIST_NEXT(bp, b_vnbufs);
if (bp->b_cflags & (BC_BUSY | BC_SCANNED))
continue;
if ((bp->b_oflags & BO_DELWRI) == 0)
panic("ffs_fsync: not dirty");
if (skipmeta && bp->b_lblkno < 0)
continue;
bp->b_cflags |= BC_BUSY | BC_VFLUSH | BC_SCANNED;
mutex_exit(&bufcache_lock);
/*
* On our final pass through, do all I/O synchronously
* so that we can find out if our flush is failing
* because of write errors.
*/
if (passes > 0 || !(flags & FSYNC_WAIT))
(void) bawrite(bp);
else if ((error = bwrite(bp)) != 0)
return (error);
/*
* Since we unlocked during the I/O, we need
* to start from a known point.
*/
mutex_enter(&bufcache_lock);
nbp = LIST_FIRST(&vp->v_dirtyblkhd);
}
mutex_exit(&bufcache_lock);
if (skipmeta) {
skipmeta = 0;
goto loop;
}
if ((flags & FSYNC_WAIT) != 0) {
mutex_enter(&vp->v_interlock);
while (vp->v_numoutput) {
cv_wait(&vp->v_cv, &vp->v_interlock);
}
mutex_exit(&vp->v_interlock);
/*
* Ensure that any filesystem metadata associated
* with the vnode has been written.
*/
if (!LIST_EMPTY(&vp->v_dirtyblkhd)) {
/*
* Block devices associated with filesystems may
* have new I/O requests posted for them even if
* the vnode is locked, so no amount of trying will
* get them clean. Thus we give block devices a
* good effort, then just give up. For all other file
* types, go around and try again until it is clean.
*/
if (passes > 0) {
passes--;
goto loop;
}
#ifdef DIAGNOSTIC
if (vp->v_type != VBLK)
vprint("ffs_fsync: dirty", vp);
#endif
}
}
waitfor = (flags & FSYNC_WAIT) ? UPDATE_WAIT : 0;
error = ffs_update(vp, NULL, NULL, UPDATE_CLOSE | waitfor);
error = vflushbuf(vp, (flags & FSYNC_WAIT) != 0);
if (error == 0)
error = ffs_update(vp, NULL, NULL, uflags);
if (error == 0 && (flags & FSYNC_CACHE) != 0) {
i = 1;
(void)VOP_IOCTL(VTOI(vp)->i_devvp, DIOCCACHESYNC, &i, FWRITE,
kauth_cred_get());
}