aboutsummaryrefslogtreecommitdiff
path: root/sys/nfsclient/nfs_bio.c
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2007-07-03 18:30:55 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2007-07-03 18:30:55 +0000
commit03e557fd5ad6194d12287bb3af2af4c2c27f4a1d (patch)
tree8869bd5d3dd765504610ed7c731db937ff7a746b /sys/nfsclient/nfs_bio.c
parent9d53363bc845c42c897acd4995b226cbf9ff045a (diff)
downloadsrc-03e557fd5ad6194d12287bb3af2af4c2c27f4a1d.tar.gz
src-03e557fd5ad6194d12287bb3af2af4c2c27f4a1d.zip
Fix up NFS client write error handling. Errors are split into
recoverable and unrecoverable. For the former, we redirty the buffer and hang onto it for future retries. For the latter (eg. ESTALE), we discard the buffer and return the error back to the user on the next syscall. This fixes a number of vfs panics and fixes having a large number of dirty buffers (that cannot be written out and reclaimed) from hanging around. Thanks to ups@ for discussions on this issue. Reported by: kris, Kai, others Approved by: re (kensmith)
Notes
Notes: svn path=/head/; revision=171189
Diffstat (limited to 'sys/nfsclient/nfs_bio.c')
-rw-r--r--sys/nfsclient/nfs_bio.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/sys/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c
index 147f1905e222..e1a74536c498 100644
--- a/sys/nfsclient/nfs_bio.c
+++ b/sys/nfsclient/nfs_bio.c
@@ -1714,6 +1714,19 @@ nfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td)
* the vp's paging queues so we cannot call bdirty(). The
* bp in this case is not an NFS cache block so we should
* be safe. XXX
+ *
+ * The logic below breaks up errors into recoverable and
+ * unrecoverable. For the former, we clear B_INVAL|B_NOCACHE
+ * and keep the buffer around for potential write retries.
+ * For the latter (eg ESTALE), we toss the buffer away (B_INVAL)
+ * and save the error in the nfsnode. This is less than ideal
+ * but necessary. Keeping such buffers around could potentially
+ * cause buffer exhaustion eventually (they can never be written
+ * out, so will get constantly be re-dirtied). It also causes
+ * all sorts of vfs panics. For non-recoverable write errors,
+ * also invalidate the attrcache, so we'll be forced to go over
+ * the wire for this object, returning an error to user on next
+ * call (most of the time).
*/
if (error == EINTR || error == EIO || error == ETIMEDOUT
|| (!error && (bp->b_flags & B_NEEDCOMMIT))) {
@@ -1731,9 +1744,11 @@ nfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td)
} else {
if (error) {
bp->b_ioflags |= BIO_ERROR;
+ bp->b_flags |= B_INVAL;
bp->b_error = np->n_error = error;
mtx_lock(&np->n_mtx);
np->n_flag |= NWRITEERR;
+ np->n_attrstamp = 0;
mtx_unlock(&np->n_mtx);
}
bp->b_dirtyoff = bp->b_dirtyend = 0;