Ted Unangst wrote:
> > Keeping a buf with an error in the delayed write list would probably have 
> > some
> > serious consequences. When would we ever remove it?
> 
> Thought about this some more. The best approach may be to set a flag in
> the vnode that there was an IO error, and return that for any following fsync.
> 
> Userland can retry by closing the file and opening again, which will clear the
> flag. Annoying, but I don't what else to do. The kernel can't know what the
> application wants to do. I believe that will be posix compliant as well.

I think this works, but I don't have an easy way to test it.

FreeBSD redirties the buf in this case, but I don't want to go down that path.
There's a lot of code that seems to assume a buf that makes it here will be
clean. Trying to fix up the buf to be dirty again seems very likely to triger
some assertion or panic elsewhere.

Instead, we note that the write failed and mark a flag in the vnode. Future
calls to fsync will then return EIO when this flag is set. We clear the flag
when the vnode is released.


Index: kern/vfs_bio.c
===================================================================
RCS file: /home/cvs/src/sys/kern/vfs_bio.c,v
retrieving revision 1.187
diff -u -p -r1.187 vfs_bio.c
--- kern/vfs_bio.c      21 Nov 2018 16:14:43 -0000      1.187
+++ kern/vfs_bio.c      31 Jan 2019 21:19:36 -0000
@@ -867,6 +867,11 @@ brelse(struct buf *bp)
        /* If it's not cacheable, or an error, mark it invalid. */
        if (ISSET(bp->b_flags, (B_NOCACHE|B_ERROR)))
                SET(bp->b_flags, B_INVAL);
+       /* If it's a write error, also mark the vnode as damaged. */
+       if (ISSET(bp->b_flags, B_ERROR) && !ISSET(bp->b_flags, B_READ)) {
+               if (bp->b_vp && bp->b_vp->v_type == VREG)
+                       SET(bp->b_vp->v_bioflag, VBIOERROR);
+       }
 
        if (ISSET(bp->b_flags, B_INVAL)) {
                /*
Index: kern/vfs_subr.c
===================================================================
RCS file: /home/cvs/src/sys/kern/vfs_subr.c,v
retrieving revision 1.285
diff -u -p -r1.285 vfs_subr.c
--- kern/vfs_subr.c     21 Jan 2019 18:09:21 -0000      1.285
+++ kern/vfs_subr.c     31 Jan 2019 21:09:22 -0000
@@ -712,6 +712,7 @@ vputonfreelist(struct vnode *vp)
 #endif
 
        vp->v_bioflag |= VBIOONFREELIST;
+       vp->v_bioflag &= ~VBIOERROR;
 
        if (vp->v_holdcnt > 0)
                lst = &vnode_hold_list;
Index: kern/vfs_vops.c
===================================================================
RCS file: /home/cvs/src/sys/kern/vfs_vops.c,v
retrieving revision 1.19
diff -u -p -r1.19 vfs_vops.c
--- kern/vfs_vops.c     21 Jun 2018 14:17:23 -0000      1.19
+++ kern/vfs_vops.c     31 Jan 2019 21:04:43 -0000
@@ -336,7 +336,7 @@ int
 VOP_FSYNC(struct vnode *vp, struct ucred *cred, int waitfor, 
     struct proc *p)
 {
-       int r;
+       int r, s;
        struct vop_fsync_args a;
        a.a_vp = vp;
        a.a_cred = cred;
@@ -351,6 +351,10 @@ VOP_FSYNC(struct vnode *vp, struct ucred
        vp->v_inflight++;
        r = (vp->v_op->vop_fsync)(&a);
        vp->v_inflight--;
+       s = splbio();
+       if (r == 0 && vp->v_bioflag & VBIOERROR)
+               r = EIO;
+       splx(s);
        return r;
 }
 
Index: sys/vnode.h
===================================================================
RCS file: /home/cvs/src/sys/sys/vnode.h,v
retrieving revision 1.149
diff -u -p -r1.149 vnode.h
--- sys/vnode.h 23 Dec 2018 10:46:51 -0000      1.149
+++ sys/vnode.h 31 Jan 2019 20:52:37 -0000
@@ -149,6 +149,7 @@ struct vnode {
 #define        VBIOWAIT        0x0001  /* waiting for output to complete */
 #define VBIOONSYNCLIST 0x0002  /* Vnode is on syncer worklist */
 #define VBIOONFREELIST  0x0004  /* Vnode is on a free list */
+#define VBIOERROR      0x0008  /* A write failed */
 
 /*
  * Vnode attributes.  A field value of VNOVAL represents a field whose value

Reply via email to