diff options
author | dillon <dillon@FreeBSD.org> | 2001-11-05 18:48:54 +0000 |
---|---|---|
committer | dillon <dillon@FreeBSD.org> | 2001-11-05 18:48:54 +0000 |
commit | 1147eaf58a59e2d2aa15fb9395dc939c237e6269 (patch) | |
tree | de03e156782fad9ffef4684a7cf3f1908967e835 | |
parent | 815c903d62ca6edef2915e089d29443365138986 (diff) | |
download | FreeBSD-src-1147eaf58a59e2d2aa15fb9395dc939c237e6269.zip FreeBSD-src-1147eaf58a59e2d2aa15fb9395dc939c237e6269.tar.gz |
Implement IO_NOWDRAIN and B_NOWDRAIN - prevents the buffer cache from blocking
in wdrain during a write. This flag needs to be used in devices whos
strategy routines turn-around and issue another high level I/O, such as
when MD turns around and issues a VOP_WRITE to vnode backing store, in order
to avoid deadlocking the dirty buffer draining code.
Remove a vprintf() warning from MD when the backing vnode is found to be
in-use. The syncer of buf_daemon could be flushing the backing vnode at
the time of an MD operation so the warning is not correct.
MFC after: 1 week
-rw-r--r-- | sys/dev/md/md.c | 11 | ||||
-rw-r--r-- | sys/kern/vfs_bio.c | 13 | ||||
-rw-r--r-- | sys/kern/vfs_cluster.c | 2 | ||||
-rw-r--r-- | sys/nfsclient/nfs_bio.c | 6 | ||||
-rw-r--r-- | sys/sys/buf.h | 7 | ||||
-rw-r--r-- | sys/sys/vnode.h | 1 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_readwrite.c | 2 |
7 files changed, 32 insertions, 10 deletions
diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 0a69769..bb4b7f0 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -386,15 +386,18 @@ mdstart_vnode(struct md_s *sc) auio.uio_rw = UIO_WRITE; auio.uio_resid = bp->bio_bcount; auio.uio_td = curthread; - if (VOP_ISLOCKED(sc->vnode, NULL)) - vprint("unexpected md driver lock", sc->vnode); + /* + * When reading set IO_DIRECT to try to avoid double-caching + * the data. When writing IO_DIRECT is not optimal, but we + * must set IO_NOWDRAIN to avoid a wdrain deadlock. + */ if (bp->bio_cmd == BIO_READ) { vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY, curthread); - error = VOP_READ(sc->vnode, &auio, 0, sc->cred); + error = VOP_READ(sc->vnode, &auio, IO_DIRECT, sc->cred); } else { (void) vn_start_write(sc->vnode, &mp, V_WAIT); vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY, curthread); - error = VOP_WRITE(sc->vnode, &auio, 0, sc->cred); + error = VOP_WRITE(sc->vnode, &auio, IO_NOWDRAIN, sc->cred); vn_finished_write(mp); } VOP_UNLOCK(sc->vnode, 0, curthread); diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 635e394..ba55013 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -758,11 +758,15 @@ bwrite(struct buf * bp) int rtval = bufwait(bp); brelse(bp); return (rtval); - } else { + } else if ((oldflags & B_NOWDRAIN) == 0) { /* * don't allow the async write to saturate the I/O - * system. There is no chance of deadlock here because - * we are blocking on I/O that is already in-progress. + * system. Deadlocks can occur only if a device strategy + * routine (like in MD) turns around and issues another + * high-level write, in which case B_NOWDRAIN is expected + * to be set. Otherwise we will not deadlock here because + * we are blocking waiting for I/O that is already in-progress + * to complete. */ waitrunningbufspace(); } @@ -1286,7 +1290,8 @@ brelse(struct buf * bp) /* unlock */ BUF_UNLOCK(bp); - bp->b_flags &= ~(B_ASYNC | B_NOCACHE | B_AGE | B_RELBUF | B_DIRECT); + bp->b_flags &= ~(B_ASYNC | B_NOCACHE | B_AGE | B_RELBUF | + B_DIRECT | B_NOWDRAIN); bp->b_ioflags &= ~BIO_ORDERED; if ((bp->b_flags & B_DELWRI) == 0 && (bp->b_xflags & BX_VNDIRTY)) panic("brelse: not dirty"); diff --git a/sys/kern/vfs_cluster.c b/sys/kern/vfs_cluster.c index 0af9fed..ff1176a 100644 --- a/sys/kern/vfs_cluster.c +++ b/sys/kern/vfs_cluster.c @@ -836,7 +836,7 @@ cluster_wbuild(vp, size, start_lbn, len) bp->b_data = (char *)((vm_offset_t)bp->b_data | ((vm_offset_t)tbp->b_data & PAGE_MASK)); bp->b_flags |= B_CLUSTER | - (tbp->b_flags & (B_VMIO | B_NEEDCOMMIT)); + (tbp->b_flags & (B_VMIO | B_NEEDCOMMIT | B_NOWDRAIN)); bp->b_iodone = cluster_callback; pbgetvp(vp, bp); /* diff --git a/sys/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c index fae952d..754d561 100644 --- a/sys/nfsclient/nfs_bio.c +++ b/sys/nfsclient/nfs_bio.c @@ -961,6 +961,12 @@ again: } vfs_bio_set_validclean(bp, on, n); } + /* + * If IO_NOWDRAIN then set B_NOWDRAIN (nfs-backed MD + * filesystem) + */ + if (ioflag & IO_NOWDRAIN) + bp->b_flags |= B_NOWDRAIN; /* * If IO_SYNC do bwrite(). diff --git a/sys/sys/buf.h b/sys/sys/buf.h index 28c22ae..dbac38b 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -192,6 +192,11 @@ struct buf { * the pages underlying the buffer. B_DIRECT is * sticky until the buffer is released and typically * only has an effect when B_RELBUF is also set. + * + * B_NOWDRAIN This flag should be set when a device (like MD) + * does a turn-around VOP_WRITE from its strategy + * routine. This flag prevents bwrite() from blocking + * in wdrain, avoiding a deadlock situation. */ #define B_AGE 0x00000001 /* Move to age queue when I/O done. */ @@ -204,7 +209,7 @@ struct buf { #define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */ #define B_DONE 0x00000200 /* I/O completed. */ #define B_EINTR 0x00000400 /* I/O was interrupted */ -#define B_00000800 0x00000800 /* Available flag. */ +#define B_NOWDRAIN 0x00000800 /* Avoid wdrain deadlock */ #define B_SCANNED 0x00001000 /* VOP_FSYNC funcs mark written bufs */ #define B_INVAL 0x00002000 /* Does not contain valid info. */ #define B_LOCKED 0x00004000 /* Locked in core (not reusable). */ diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 8c3a24b..9861f8e 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -222,6 +222,7 @@ struct vattr { #define IO_INVAL 0x40 /* invalidate after I/O */ #define IO_ASYNC 0x80 /* bawrite rather then bdwrite */ #define IO_DIRECT 0x100 /* attempt to bypass buffer cache */ +#define IO_NOWDRAIN 0x200 /* do not block on wdrain */ /* * Modes. Some values same as Ixxx entries from inode.h for now. diff --git a/sys/ufs/ufs/ufs_readwrite.c b/sys/ufs/ufs/ufs_readwrite.c index 83673eb..bc19b98 100644 --- a/sys/ufs/ufs/ufs_readwrite.c +++ b/sys/ufs/ufs/ufs_readwrite.c @@ -511,6 +511,8 @@ WRITE(ap) break; if (ioflag & IO_DIRECT) bp->b_flags |= B_DIRECT; + if (ioflag & IO_NOWDRAIN) + bp->b_flags |= B_NOWDRAIN; if (uio->uio_offset + xfersize > ip->i_size) { ip->i_size = uio->uio_offset + xfersize; |