Allow vfs_write_suspend() to wait if the file system is already

suspending.

Move vfs_write_suspend() and vfs_write_resume() from kern/vfs_vnops.c
to kern/vfs_subr.c.

Change vnode write gating in ufs/ffs/ffs_softdep.c (from FreeBSD).

When vnodes are throttled in softdep_trackbufs() check for
file system suspension every 10 msecs to avoid a deadlock.
This commit is contained in:
hannken 2004-01-10 17:16:38 +00:00
parent 6297e36a60
commit ed68c4e34c
6 changed files with 108 additions and 74 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: vfssubr.9,v 1.7 2003/12/04 23:57:44 wiz Exp $
.\" $NetBSD: vfssubr.9,v 1.8 2004/01/10 17:16:38 hannken Exp $
.\"
.\" Copyright (c) 2003 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -34,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd December 5, 2003
.Dd January 10, 2004
.Dt VFSSUBR 9
.Os
.Sh NAME
@ -103,7 +103,7 @@
.Ft struct vfsops *
.Fn vfs_getopsbyname "const char *name"
.Ft int
.Fn vfs_write_suspend "struct mount *mp"
.Fn vfs_write_suspend "struct mount *mp" "int slpflag" "int slptimeo"
.Ft void
.Fn vfs_write_resume "struct mount *mp"
.Sh DESCRIPTION
@ -189,11 +189,25 @@ look up the vfs operations for that file system (see
or return
.Dv NULL
if file system isn't present in the kernel.
.It Fn vfs_write_suspend "mp"
.It Fn vfs_write_suspend "mp" "slpflag" "slptimeo"
Request a mounted file system to suspend write operations.
All new write operations to the file system are stopped.
After all write operations in progress have completed, the
file system is synced to disk and the function returns.
If the file system is currently suspended the
.Xr sleep 9
flag and timeout are specified by the arguments
.Fa slpflag
and
.Fa slptimeo
respectively.
If
.Fa slptimeo
is less than zero
.Er EWOULDBLOCK
is returned.
If the operation is successful zero is returned, otherwise an
appropriate error code is returned.
.It Fn vfs_write_resume "mp"
Request a mounted file system to resume write operations.
.El

View File

@ -1,4 +1,4 @@
/* $NetBSD: fss.c,v 1.2 2003/12/13 18:59:29 hannken Exp $ */
/* $NetBSD: fss.c,v 1.3 2004/01/10 17:16:38 hannken Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
@ -43,7 +43,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: fss.c,v 1.2 2003/12/13 18:59:29 hannken Exp $");
__KERNEL_RCSID(0, "$NetBSD: fss.c,v 1.3 2004/01/10 17:16:38 hannken Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -632,7 +632,7 @@ fss_create_snapshot(struct fss_softc *sc, struct fss_set *fss, struct proc *p)
* Activate the snapshot.
*/
if ((error = vfs_write_suspend(sc->sc_mount)) != 0)
if ((error = vfs_write_suspend(sc->sc_mount, PUSER|PCATCH, 0)) != 0)
goto bad;
microtime(&sc->sc_time);

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_subr.c,v 1.213 2003/12/30 12:33:24 pk Exp $ */
/* $NetBSD: vfs_subr.c,v 1.214 2004/01/10 17:16:38 hannken Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@ -78,7 +78,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_subr.c,v 1.213 2003/12/30 12:33:24 pk Exp $");
__KERNEL_RCSID(0, "$NetBSD: vfs_subr.c,v 1.214 2004/01/10 17:16:38 hannken Exp $");
#include "opt_inet.h"
#include "opt_ddb.h"
@ -2828,6 +2828,54 @@ vfs_reinit(void)
}
}
/*
* Request a filesystem to suspend write operations.
*/
int
vfs_write_suspend(struct mount *mp, int slpflag, int slptimeo)
{
struct proc *p = curproc; /* XXX */
int error;
while ((mp->mnt_iflag & IMNT_SUSPEND)) {
if (slptimeo < 0)
return EWOULDBLOCK;
error = tsleep(&mp->mnt_flag, slpflag, "suspwt1", slptimeo);
if (error)
return error;
}
mp->mnt_iflag |= IMNT_SUSPEND;
if (mp->mnt_writeopcountupper > 0)
tsleep(&mp->mnt_writeopcountupper, PUSER - 1, "suspwt", 0);
error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p);
if (error) {
vfs_write_resume(mp);
return error;
}
mp->mnt_iflag |= IMNT_SUSPENDLOW;
if (mp->mnt_writeopcountlower > 0)
tsleep(&mp->mnt_writeopcountlower, PUSER - 1, "suspwt", 0);
mp->mnt_iflag |= IMNT_SUSPENDED;
return 0;
}
/*
* Request a filesystem to resume write operations.
*/
void
vfs_write_resume(struct mount *mp)
{
if ((mp->mnt_iflag & IMNT_SUSPEND) == 0)
return;
mp->mnt_iflag &= ~(IMNT_SUSPEND | IMNT_SUSPENDLOW | IMNT_SUSPENDED);
wakeup(&mp->mnt_flag);
}
void
copy_statfs_info(struct statfs *sbp, const struct mount *mp)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_vnops.c,v 1.75 2003/10/15 11:29:01 hannken Exp $ */
/* $NetBSD: vfs_vnops.c,v 1.76 2004/01/10 17:16:38 hannken Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.75 2003/10/15 11:29:01 hannken Exp $");
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.76 2004/01/10 17:16:38 hannken Exp $");
#include "fs_union.h"
@ -825,41 +825,3 @@ vn_restorerecurse(vp, flags)
lkp->lk_flags &= ~LK_CANRECURSE;
lkp->lk_flags |= flags;
}
/*
* Request a filesystem to suspend write operations.
*/
int
vfs_write_suspend(struct mount *mp)
{
struct proc *p = curproc; /* XXX */
int error;
if (mp->mnt_iflag & IMNT_SUSPEND)
return (0);
mp->mnt_iflag |= IMNT_SUSPEND;
if (mp->mnt_writeopcountupper > 0)
tsleep(&mp->mnt_writeopcountupper, PUSER - 1, "suspwt", 0);
if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) != 0) {
vfs_write_resume(mp);
return (error);
}
mp->mnt_iflag |= IMNT_SUSPENDLOW;
if (mp->mnt_writeopcountlower > 0)
tsleep(&mp->mnt_writeopcountlower, PUSER - 1, "suspwt", 0);
mp->mnt_iflag |= IMNT_SUSPENDED;
return (0);
}
/*
* Request a filesystem to resume write operations.
*/
void
vfs_write_resume(struct mount *mp)
{
if ((mp->mnt_iflag & IMNT_SUSPEND) == 0)
return;
mp->mnt_iflag &= ~(IMNT_SUSPEND | IMNT_SUSPENDLOW | IMNT_SUSPENDED);
wakeup(&mp->mnt_flag);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: vnode.h,v 1.118 2003/11/18 18:26:18 dbj Exp $ */
/* $NetBSD: vnode.h,v 1.119 2004/01/10 17:16:38 hannken Exp $ */
/*
* Copyright (c) 1989, 1993
@ -678,12 +678,12 @@ int getvnode(struct filedesc *fdp, int fd, struct file **fpp);
/* see vfssubr(9) */
void vfs_getnewfsid(struct mount *);
int vfs_drainvnodes(long target, struct proc *);
void vfs_write_resume(struct mount *);
int vfs_write_suspend(struct mount *, int, int);
#ifdef DDB
void vfs_vnode_print(struct vnode *, int, void (*)(const char *, ...));
void vfs_mount_print(struct mount *, int, void (*)(const char *, ...));
#endif /* DDB */
void vfs_write_resume(struct mount *);
int vfs_write_suspend(struct mount *);
#endif /* _KERNEL */
#endif /* !_SYS_VNODE_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: ffs_softdep.c,v 1.54 2004/01/10 16:23:36 hannken Exp $ */
/* $NetBSD: ffs_softdep.c,v 1.55 2004/01/10 17:16:38 hannken Exp $ */
/*
* Copyright 1998 Marshall Kirk McKusick. All Rights Reserved.
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ffs_softdep.c,v 1.54 2004/01/10 16:23:36 hannken Exp $");
__KERNEL_RCSID(0, "$NetBSD: ffs_softdep.c,v 1.55 2004/01/10 17:16:38 hannken Exp $");
#include <sys/param.h>
#include <sys/buf.h>
@ -193,7 +193,7 @@ void softdep_pageiodone1 __P((struct buf *));
#endif
void softdep_pageiodone __P((struct buf *));
void softdep_flush_vnode __P((struct vnode *, daddr_t));
static void softdep_trackbufs(int, boolean_t);
static void softdep_trackbufs(struct inode *, int, boolean_t);
/*
* Exported softdep operations.
@ -692,9 +692,9 @@ softdep_process_worklist(matchmnt)
mp = WK_DIRREM(wk)->dm_mnt;
if (mp == matchmnt)
matchcnt += 1;
vn_start_write(NULL, &mp, V_WAIT);
vn_start_write(NULL, &mp, V_WAIT|V_LOWER);
handle_workitem_remove(WK_DIRREM(wk));
vn_finished_write(mp, 0);
vn_finished_write(mp, V_LOWER);
break;
case D_FREEBLKS:
@ -702,9 +702,9 @@ softdep_process_worklist(matchmnt)
mp = WK_FREEBLKS(wk)->fb_ump->um_mountp;
if (mp == matchmnt)
matchcnt += 1;
vn_start_write(NULL, &mp, V_WAIT);
vn_start_write(NULL, &mp, V_WAIT|V_LOWER);
handle_workitem_freeblocks(WK_FREEBLKS(wk));
vn_finished_write(mp, 0);
vn_finished_write(mp, V_LOWER);
break;
case D_FREEFRAG:
@ -712,9 +712,9 @@ softdep_process_worklist(matchmnt)
mp = WK_FREEFRAG(wk)->ff_mnt;
if (mp == matchmnt)
matchcnt += 1;
vn_start_write(NULL, &mp, V_WAIT);
vn_start_write(NULL, &mp, V_WAIT|V_LOWER);
handle_workitem_freefrag(WK_FREEFRAG(wk));
vn_finished_write(mp, 0);
vn_finished_write(mp, V_LOWER);
break;
case D_FREEFILE:
@ -722,9 +722,9 @@ softdep_process_worklist(matchmnt)
mp = WK_FREEFILE(wk)->fx_mnt;
if (mp == matchmnt)
matchcnt += 1;
vn_start_write(NULL, &mp, V_WAIT);
vn_start_write(NULL, &mp, V_WAIT|V_LOWER);
handle_workitem_freefile(WK_FREEFILE(wk));
vn_finished_write(mp, 0);
vn_finished_write(mp, V_LOWER);
break;
default:
@ -1852,7 +1852,7 @@ setup_allocindir_phase2(bp, ip, aip)
if (newindirdep) {
if (indirdep->ir_savebp != NULL) {
brelse(newindirdep->ir_savebp);
softdep_trackbufs(-1, FALSE);
softdep_trackbufs(ip, -1, FALSE);
}
WORKITEM_FREE(newindirdep, D_INDIRDEP);
}
@ -1869,7 +1869,7 @@ setup_allocindir_phase2(bp, ip, aip)
VOP_BMAP(bp->b_vp, bp->b_lblkno, NULL, &bp->b_blkno,
NULL);
}
softdep_trackbufs(1, TRUE);
softdep_trackbufs(ip, 1, TRUE);
newindirdep->ir_savebp =
getblk(ip->i_devvp, bp->b_blkno, bp->b_bcount, 0, 0);
newindirdep->ir_savebp->b_flags |= B_ASYNC;
@ -2523,7 +2523,7 @@ indir_trunc(ip, dbn, level, lbn, countp)
FREE_LOCK(&lk);
} else {
FREE_LOCK(&lk);
softdep_trackbufs(1, FALSE);
softdep_trackbufs(ip, 1, FALSE);
error = bread(ip->i_devvp, dbn, (int)fs->fs_bsize, NOCRED, &bp);
if (error)
return (error);
@ -2559,7 +2559,7 @@ indir_trunc(ip, dbn, level, lbn, countp)
}
bp->b_flags |= B_INVAL | B_NOCACHE;
brelse(bp);
softdep_trackbufs(-1, FALSE);
softdep_trackbufs(ip, -1, FALSE);
return (allerror);
}
@ -3400,7 +3400,7 @@ softdep_disk_io_initiation(bp)
if (LIST_FIRST(&indirdep->ir_deplisthd) == NULL) {
indirdep->ir_savebp->b_flags |= B_INVAL | B_NOCACHE;
brelse(indirdep->ir_savebp);
softdep_trackbufs(-1, FALSE);
softdep_trackbufs(NULL, -1, FALSE);
/* inline expand WORKLIST_REMOVE(wk); */
wk->wk_state &= ~ONWORKLIST;
@ -5428,9 +5428,9 @@ clear_remove(p)
continue;
mp = pagedep->pd_mnt;
ino = pagedep->pd_ino;
if (vn_start_write(NULL, &mp, V_NOWAIT) != 0)
continue;
FREE_LOCK(&lk);
if (vn_start_write(NULL, &mp, V_WAIT | V_PCATCH) != 0)
return;
if ((error = VFS_VGET(mp, ino, &vp)) != 0) {
softdep_error("clear_remove: vget", error);
vn_finished_write(mp, 0);
@ -5501,9 +5501,9 @@ clear_inodedeps(p)
for (ino = firstino; ino <= lastino; ino++) {
if (inodedep_lookup(fs, ino, 0, &inodedep) == 0)
continue;
if (vn_start_write(NULL, &mp, V_NOWAIT) != 0)
continue;
FREE_LOCK(&lk);
if (vn_start_write(NULL, &mp, V_WAIT | V_PCATCH) != 0)
return;
if ((error = VFS_VGET(mp, ino, &vp)) != 0) {
softdep_error("clear_inodedeps: vget", error);
vn_finished_write(mp, 0);
@ -5797,8 +5797,9 @@ softdep_lookupvp(fs, ino)
}
static void
softdep_trackbufs(int delta, boolean_t throttle)
softdep_trackbufs(struct inode *ip, int delta, boolean_t throttle)
{
struct proc *p = curproc;
if (delta < 0) {
if (softdep_lockedbufs < nbuf >> 2) {
@ -5809,9 +5810,18 @@ softdep_trackbufs(int delta, boolean_t throttle)
return;
}
KASSERT(ip != NULL);
/*
* Kernel threads never get blocked.
* User processes check for file system suspension every 10 msecs.
*/
while (throttle && softdep_lockedbufs >= nbuf >> 2) {
speedup_syncer();
tsleep(&softdep_lockedbufs, PRIBIO, "softdbufs", 0);
if (p && (p->p_flag & P_SYSTEM))
break;
tsleep(&softdep_lockedbufs, PRIBIO, "softdbufs", mstohz(10));
if ((ITOV(ip)->v_mount->mnt_iflag & IMNT_SUSPEND))
break;
}
softdep_lockedbufs += delta;
}