Block devices generally truncate the size of I/O requests which go past EOF.
authorMatthew Dillon <dillon@dragonflybsd.org>
Thu, 4 May 2006 18:32:23 +0000 (18:32 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Thu, 4 May 2006 18:32:23 +0000 (18:32 +0000)
This is exactly what we want when manually reading or writing a block device
such as /dev/ad0s1a, but is not desired when a VFS issues I/O ops on
filesystem buffers.  In such cases, any EOF condition must be considered an
error.

Implement a new filesystem buffer flag B_BNOCLIP, which getblk() and friends
automatically set.  If set, block devices are guarenteed to return an error
if the I/O request is at EOF or would otherwise have to be clipped to EOF.
Block devices further guarentee that b_bcount will not be modified when this
flag is set.

Adjust all block device EOF checks to use the new flag, and clean up the code
while I'm there.  Also, set b_resid in a couple of degenerate cases where
it was not being set.

sys/dev/disk/ccd/ccd.c
sys/dev/disk/vn/vn.c
sys/i386/i386/machdep.c
sys/kern/subr_diskslice.c
sys/kern/vfs_bio.c
sys/kern/vfs_cluster.c
sys/platform/pc32/i386/machdep.c
sys/sys/buf.h
sys/vfs/mfs/mfs_vfsops.c

index f750dc7..ca13ef3 100644 (file)
@@ -1,5 +1,5 @@
 /* $FreeBSD: src/sys/dev/ccd/ccd.c,v 1.73.2.1 2001/09/11 09:49:52 kris Exp $ */
-/* $DragonFly: src/sys/dev/disk/ccd/ccd.c,v 1.29 2006/05/04 08:00:59 y0netan1 Exp $ */
+/* $DragonFly: src/sys/dev/disk/ccd/ccd.c,v 1.30 2006/05/04 18:32:19 dillon Exp $ */
 
 /*     $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $ */
 
@@ -256,6 +256,7 @@ getccdbuf(void)
        BUF_LOCKINIT(&cbp->cb_buf);
        BUF_LOCK(&cbp->cb_buf, LK_EXCLUSIVE);
        BUF_KERNPROC(&cbp->cb_buf);
+       cbp->cb_buf.b_flags = B_PAGING | B_BNOCLIP;
 
        return(cbp);
 }
@@ -760,13 +761,14 @@ ccdstrategy(dev_t dev, struct bio *bio)
 #endif
        if ((cs->sc_flags & CCDF_INITED) == 0) {
                bp->b_error = ENXIO;
-               bp->b_flags |= B_ERROR;
-               goto done;
+               goto error;
        }
 
        /* If it's a nil transfer, wake up the top half now. */
-       if (bp->b_bcount == 0)
+       if (bp->b_bcount == 0) {
+               bp->b_resid = 0;
                goto done;
+       }
 
        lp = &cs->sc_label;
 
@@ -787,25 +789,26 @@ ccdstrategy(dev_t dev, struct bio *bio)
                sz = howmany(bp->b_bcount, cs->sc_geom.ccg_secsize);
 
                /*
-                * If out of bounds return an error. If at the EOF point,
-                * simply read or write less.
-                */
-
-               if (pbn < 0 || pbn >= cs->sc_size) {
-                       bp->b_resid = bp->b_bcount;
-                       if (pbn != cs->sc_size) {
-                               bp->b_error = EINVAL;
-                               bp->b_flags |= B_ERROR | B_INVAL;
-                       }
-                       goto done;
-               }
-
-               /*
-                * If the request crosses EOF, truncate the request.
+                * If out of bounds return an error.  If the request goes
+                * past EOF, clip the request as appropriate.  If exactly
+                * at EOF, return success (don't clip), but with 0 bytes
+                * of I/O.
+                *
+                * Mark EOF B_INVAL (just like bad), indicating that the
+                * contents of the buffer, if any, is invalid.
                 */
+               if (pbn < 0)
+                       goto bad;
                if (pbn + sz > cs->sc_size) {
-                       bp->b_bcount = (cs->sc_size - pbn) * 
-                           cs->sc_geom.ccg_secsize;
+                       if (pbn > cs->sc_size || (bp->b_flags & B_BNOCLIP))
+                               goto bad;
+                       if (pbn == cs->sc_size) {
+                               bp->b_resid = bp->b_bcount;
+                               bp->b_flags |= B_INVAL;
+                               goto done;
+                       }
+                       sz = cs->sc_size - pbn;
+                       bp->b_bcount = sz * cs->sc_geom.ccg_secsize;
                }
                nbio = bio;
        }
@@ -824,6 +827,11 @@ ccdstrategy(dev_t dev, struct bio *bio)
        /*
         * note: bio, not nbio, is valid at the done label.
         */
+bad:
+       bp->b_error = EINVAL;
+error:
+       bp->b_resid = bp->b_bcount;
+       bp->b_flags |= B_ERROR | B_INVAL;
 done:
        biodone(bio);
 }
@@ -1036,7 +1044,7 @@ ccdbuffer(struct ccdbuf **cb, struct ccd_softc *cs, struct bio *bio,
         */
        cbp = getccdbuf();
        cbp->cb_buf.b_cmd = bio->bio_buf->b_cmd;
-       cbp->cb_buf.b_flags = bio->bio_buf->b_flags | B_PAGING;
+       cbp->cb_buf.b_flags |= bio->bio_buf->b_flags;
        cbp->cb_buf.b_data = addr;
        cbp->cb_vp = ci->ci_vp;
        if (cs->sc_ileave == 0)
@@ -1075,7 +1083,7 @@ ccdbuffer(struct ccdbuf **cb, struct ccd_softc *cs, struct bio *bio,
                cbp = getccdbuf();
 
                cbp->cb_buf.b_cmd = bio->bio_buf->b_cmd;
-               cbp->cb_buf.b_flags = bio->bio_buf->b_flags | B_PAGING;
+               cbp->cb_buf.b_flags |= bio->bio_buf->b_flags;
                cbp->cb_buf.b_data = addr;
                cbp->cb_vp = ci2->ci_vp;
                if (cs->sc_ileave == 0)
@@ -1155,15 +1163,6 @@ ccdiodone(struct bio *bio)
                       cbp->cb_buf.b_bcount);
        }
 #endif
-       /*
-        * An early EOF is considered an error
-        */
-       if (cbp->cb_buf.b_bcount != cbp->cb_buf.b_bufsize) {
-               if ((cbp->cb_buf.b_flags & B_ERROR) == 0) {
-                       cbp->cb_buf.b_flags |= B_ERROR;
-                       cbp->cb_buf.b_error = EIO;
-               }
-       }
 
        /*
         * If an error occured, report it.  If this is a mirrored 
index 40728d3..d8a11fe 100644 (file)
@@ -39,7 +39,7 @@
  *
  *     from: @(#)vn.c  8.6 (Berkeley) 4/1/94
  * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $
- * $DragonFly: src/sys/dev/disk/vn/vn.c,v 1.20 2006/05/03 20:44:48 dillon Exp $
+ * $DragonFly: src/sys/dev/disk/vn/vn.c,v 1.21 2006/05/04 18:32:20 dillon Exp $
  */
 
 /*
@@ -314,8 +314,7 @@ vnstrategy(dev_t dev, struct bio *bio)
                if (vn->sc_slices == NULL) {
                        nbio = bio;
                } else if ((nbio = dscheck(dev, bio, vn->sc_slices)) == NULL) {
-                       biodone(bio);
-                       return;
+                       goto done;
                }
        } else {
                int pbn;        /* in sc_secsize chunks */
@@ -327,34 +326,26 @@ vnstrategy(dev_t dev, struct bio *bio)
                 */
                if (bp->b_bcount % vn->sc_secsize != 0 ||
                    bio->bio_offset % vn->sc_secsize != 0) {
-                       bp->b_error = EINVAL;
-                       bp->b_flags |= B_ERROR | B_INVAL;
-                       biodone(bio);
-                       return;
+                       goto bad;
                }
 
                pbn = bio->bio_offset / vn->sc_secsize;
                sz = howmany(bp->b_bcount, vn->sc_secsize);
 
                /*
-                * If out of bounds return an error.  If at the EOF point,
-                * simply read or write less.
-                */
-               if (pbn < 0 || pbn >= vn->sc_size) {
-                       if (pbn != vn->sc_size) {
-                               bp->b_error = EINVAL;
-                               bp->b_flags |= B_ERROR | B_INVAL;
-                       }
-                       biodone(bio);
-                       return;
-               }
-
-               /*
-                * If the request crosses EOF, truncate the request.
+                * Check for an illegal pbn or EOF truncation
                 */
+               if (pbn < 0)
+                       goto bad;
                if (pbn + sz > vn->sc_size) {
+                       if (pbn > vn->sc_size || (bp->b_flags & B_BNOCLIP))
+                               goto bad;
+                       if (pbn == vn->sc_size) {
+                               bp->b_resid = bp->b_bcount;
+                               bp->b_flags |= B_INVAL;
+                               goto done;
+                       }
                        bp->b_bcount = (vn->sc_size - pbn) * vn->sc_secsize;
-                       bp->b_resid = bp->b_bcount;
                }
                nbio = push_bio(bio);
                nbio->bio_offset = pbn * vn->sc_secsize;
@@ -368,7 +359,7 @@ vnstrategy(dev_t dev, struct bio *bio)
                 * Freeblks is not handled for vnode-backed elements yet.
                 */
                bp->b_resid = 0;
-               biodone(nbio);
+               /* operation complete */
        } else if (vn->sc_vp) {
                /*
                 * VNODE I/O
@@ -401,30 +392,44 @@ vnstrategy(dev_t dev, struct bio *bio)
                        error = VOP_WRITE(vn->sc_vp, &auio, IO_NOWDRAIN, vn->sc_cred);
                VOP_UNLOCK(vn->sc_vp, 0, curthread);
                bp->b_resid = auio.uio_resid;
-
                if (error) {
                        bp->b_error = error;
                        bp->b_flags |= B_ERROR;
                }
-               biodone(nbio);
+               /* operation complete */
        } else if (vn->sc_object) {
                /*
-                * OBJT_SWAP I/O
-                *
-                * ( handles read, write, freebuf )
+                * OBJT_SWAP I/O (handles read, write, freebuf)
                 *
-                * Note: freeblks is not supported with pre-reserved swap.
+                * We have nothing to do if freeing  blocks on a reserved
+                * swap area, othrewise execute the op.
                 */
                if (bp->b_cmd == BUF_CMD_FREEBLKS && TESTOPT(vn, VN_RESERVE)) {
-                       biodone(nbio);
+                       bp->b_resid = 0;
+                       /* operation complete */
                } else {
                        vm_pager_strategy(vn->sc_object, nbio);
+                       return;
+                       /* NOT REACHED */
                }
        } else {
-               bp->b_flags |= B_ERROR;
+               bp->b_resid = bp->b_bcount;
+               bp->b_flags |= B_ERROR | B_INVAL;
                bp->b_error = EINVAL;
-               biodone(nbio);
+               /* operation complete */
        }
+       biodone(nbio);
+       return;
+
+       /*
+        * Shortcuts / check failures on the original bio (not nbio).
+        */
+bad:
+       bp->b_error = EINVAL;
+error:
+       bp->b_flags |= B_ERROR | B_INVAL;
+done:
+       biodone(bio);
 }
 
 /* ARGSUSED */
index 619d394..5428a2c 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)machdep.c     7.4 (Berkeley) 6/3/91
  * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $
- * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.90 2006/04/30 17:22:17 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.91 2006/05/04 18:32:21 dillon Exp $
  */
 
 #include "use_apm.h"
@@ -2545,7 +2545,7 @@ bounds_check_with_label(dev_t dev, struct bio *bio,
 #endif
             bp->b_cmd != BUF_CMD_READ && wlabel == 0) {
                 bp->b_error = EROFS;
-                goto bad;
+                goto error;
         }
 
 #if     defined(DOSBBSECTOR) && defined(notyet)
@@ -2553,31 +2553,48 @@ bounds_check_with_label(dev_t dev, struct bio *bio,
         if (blkno + p->p_offset <= DOSBBSECTOR &&
             bp->b_cmd != BUF_CMD_READ && wlabel == 0) {
                 bp->b_error = EROFS;
-                goto bad;
+                goto error;
         }
 #endif
 
-        /* beyond partition? */
-        if (bio->bio_offset < 0 || blkno + sz > maxsz) {
-                /* if exactly at end of disk, return an EOF */
+       /*
+        * Check for out of bounds, EOF, and EOF clipping.
+        */
+       if (bio->bio_offset < 0)
+               goto bad;
+       if (blkno + sz > maxsz) {
+               /*
+                * Past EOF or B_BNOCLIP flag was set, the request is bad.
+                */
+               if (blkno > maxsz || (bp->b_flags & B_BNOCLIP))
+                       goto bad;
+
+               /*
+                * If exactly on EOF just complete the I/O with no bytes
+                * transfered.  B_INVAL must be set to throw away the
+                * contents of the buffer.  Otherwise clip b_bcount.
+                */
                 if (blkno == maxsz) {
                         bp->b_resid = bp->b_bcount;
-                        return(0);
-                }
-                /* or truncate if part of it fits */
-                sz = maxsz - blkno;
-                if (sz <= 0) {
-                        bp->b_error = EINVAL;
-                        goto bad;
+                       bp->b_flags |= B_INVAL;
+                       goto done;
                 }
-                bp->b_bcount = sz << DEV_BSHIFT;
+                bp->b_bcount = (maxsz - blkno) << DEV_BSHIFT;
         }
        nbio = push_bio(bio);
         nbio->bio_offset = bio->bio_offset + ((off_t)p->p_offset << DEV_BSHIFT);
        return (nbio);
 
+       /*
+        * The caller is responsible for calling biodone() on the passed bio
+        * when we return NULL.
+        */
 bad:
-        bp->b_flags |= B_ERROR;
+       bp->b_error = EINVAL;
+error:
+       bp->b_resid = bp->b_bcount;
+        bp->b_flags |= B_ERROR | B_INVAL;
+done:
        return (NULL);
 }
 
index 5806e9e..df0791f 100644 (file)
@@ -44,7 +44,7 @@
  *     from: @(#)ufs_disksubr.c        7.16 (Berkeley) 5/4/91
  *     from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
  * $FreeBSD: src/sys/kern/subr_diskslice.c,v 1.82.2.6 2001/07/24 09:49:41 dd Exp $
- * $DragonFly: src/sys/kern/subr_diskslice.c,v 1.18 2006/05/03 20:44:49 dillon Exp $
+ * $DragonFly: src/sys/kern/subr_diskslice.c,v 1.19 2006/05/04 18:32:22 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -158,7 +158,6 @@ dscheck(dev_t dev, struct bio *bio, struct diskslices *ssp)
        if (bio->bio_offset < 0) {
                printf("dscheck(%s): negative bio_offset %lld\n", 
                    devtoname(dev), bio->bio_offset);
-               bp->b_error = EINVAL;
                goto bad;
        }
        sp = &ssp->dss_slices[dkslice(dev)];
@@ -206,7 +205,7 @@ doshift:
 #endif
            bp->b_cmd != BUF_CMD_READ && sp->ds_wlabel == 0) {
                bp->b_error = EROFS;
-               goto bad;
+               goto error;
        }
 
 #if defined(DOSBBSECTOR) && defined(notyet)
@@ -214,33 +213,42 @@ doshift:
        if (slicerel_secno <= DOSBBSECTOR && bp->b_cmd != BUF_CMD_READ &&
            sp->ds_wlabel == 0) {
                bp->b_error = EROFS;
-               goto bad;
+               goto error;
        }
 #endif
 
-       /* beyond partition? */
+       /*
+        * EOF handling
+        */
        if (secno + nsec > endsecno) {
                /*
-                * If exactly at end of disk, return an EOF.  There's no
-                * point keeping the buffer around so mark it B_INVAL as
-                * well.
+                * Return an error if beyond the end of the disk, or
+                * if B_BNOCLIP is set.  Tell the system that we do not
+                * need to keep the buffer around.
+                */
+               if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
+                       goto bad;
+
+               /*
+                * If exactly at end of disk, return an EOF.  Throw away
+                * the buffer contents, if any, by setting B_INVAL.
                 */
                if (secno == endsecno) {
                        bp->b_resid = bp->b_bcount;
                        bp->b_flags |= B_INVAL;
-                       return (NULL);
+                       goto done;
                }
-               /* or truncate if part of it fits */
+
+               /*
+                * Else truncate
+                */
                nsec = endsecno - secno;
-               if (nsec <= 0) {
-                       bp->b_error = EINVAL;
-                       goto bad;
-               }
                bp->b_bcount = nsec * ssp->dss_secsize;
        }
 
        nbio = push_bio(bio);
-       nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * ssp->dss_secsize;
+       nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * 
+                          ssp->dss_secsize;
 
        /*
         * Snoop on label accesses if the slice offset is nonzero.  Fudge
@@ -277,7 +285,7 @@ doshift:
                                    devtoname(dev), msg);
                                bp->b_error = EROFS;
                                pop_bio(nbio);
-                               goto bad;
+                               goto error;
                        }
                }
        }
@@ -287,23 +295,27 @@ bad_bcount:
        printf(
        "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
            devtoname(dev), bp->b_bcount, ssp->dss_secsize);
-       bp->b_error = EINVAL;
        goto bad;
 
 bad_blkno:
        printf(
        "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
            devtoname(dev), bio->bio_offset, ssp->dss_secsize);
+bad:
        bp->b_error = EINVAL;
        /* fall through */
-
-bad:
+error:
        /*
         * Terminate the I/O with a ranging error.  Since the buffer is
         * either illegal or beyond the file EOF, mark it B_INVAL as well.
         */
        bp->b_resid = bp->b_bcount;
        bp->b_flags |= B_ERROR | B_INVAL;
+done:
+       /*
+        * Caller must biodone() the originally passed bio if NULL is
+        * returned.
+        */
        return (NULL);
 }
 
index ea47b90..2531f72 100644 (file)
@@ -12,7 +12,7 @@
  *             John S. Dyson.
  *
  * $FreeBSD: src/sys/kern/vfs_bio.c,v 1.242.2.20 2003/05/28 18:38:10 alc Exp $
- * $DragonFly: src/sys/kern/vfs_bio.c,v 1.71 2006/05/03 20:44:49 dillon Exp $
+ * $DragonFly: src/sys/kern/vfs_bio.c,v 1.72 2006/05/04 18:32:22 dillon Exp $
  */
 
 /*
@@ -1683,7 +1683,7 @@ restart:
                if (bp->b_bufsize)
                        allocbuf(bp, 0);
 
-               bp->b_flags = 0;
+               bp->b_flags = B_BNOCLIP;
                bp->b_cmd = BUF_CMD_DONE;
                bp->b_vp = NULL;
                bp->b_error = 0;
index 45582cb..0588b25 100644 (file)
@@ -34,7 +34,7 @@
  *
  *     @(#)vfs_cluster.c       8.7 (Berkeley) 2/13/94
  * $FreeBSD: src/sys/kern/vfs_cluster.c,v 1.92.2.9 2001/11/18 07:10:59 dillon Exp $
- * $DragonFly: src/sys/kern/vfs_cluster.c,v 1.24 2006/05/03 20:44:49 dillon Exp $
+ * $DragonFly: src/sys/kern/vfs_cluster.c,v 1.25 2006/05/04 18:32:22 dillon Exp $
  */
 
 #include "opt_debug_cluster.h"
@@ -811,7 +811,7 @@ cluster_wbuild(struct vnode *vp, int size, off_t start_loffset, int bytes)
                bp->b_data = (char *)((vm_offset_t)bp->b_data |
                    ((vm_offset_t)tbp->b_data & PAGE_MASK));
                bp->b_flags &= ~B_ERROR;
-               bp->b_flags |= B_CLUSTER | 
+               bp->b_flags |= B_CLUSTER | B_BNOCLIP |
                        (tbp->b_flags & (B_VMIO | B_NEEDCOMMIT | B_NOWDRAIN));
                bp->b_bio1.bio_done = cluster_callback;
                bp->b_bio1.bio_caller_info1.cluster_head = NULL;
index 6143181..a588ba9 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)machdep.c     7.4 (Berkeley) 6/3/91
  * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $
- * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.90 2006/04/30 17:22:17 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.91 2006/05/04 18:32:21 dillon Exp $
  */
 
 #include "use_apm.h"
@@ -2545,7 +2545,7 @@ bounds_check_with_label(dev_t dev, struct bio *bio,
 #endif
             bp->b_cmd != BUF_CMD_READ && wlabel == 0) {
                 bp->b_error = EROFS;
-                goto bad;
+                goto error;
         }
 
 #if     defined(DOSBBSECTOR) && defined(notyet)
@@ -2553,31 +2553,48 @@ bounds_check_with_label(dev_t dev, struct bio *bio,
         if (blkno + p->p_offset <= DOSBBSECTOR &&
             bp->b_cmd != BUF_CMD_READ && wlabel == 0) {
                 bp->b_error = EROFS;
-                goto bad;
+                goto error;
         }
 #endif
 
-        /* beyond partition? */
-        if (bio->bio_offset < 0 || blkno + sz > maxsz) {
-                /* if exactly at end of disk, return an EOF */
+       /*
+        * Check for out of bounds, EOF, and EOF clipping.
+        */
+       if (bio->bio_offset < 0)
+               goto bad;
+       if (blkno + sz > maxsz) {
+               /*
+                * Past EOF or B_BNOCLIP flag was set, the request is bad.
+                */
+               if (blkno > maxsz || (bp->b_flags & B_BNOCLIP))
+                       goto bad;
+
+               /*
+                * If exactly on EOF just complete the I/O with no bytes
+                * transfered.  B_INVAL must be set to throw away the
+                * contents of the buffer.  Otherwise clip b_bcount.
+                */
                 if (blkno == maxsz) {
                         bp->b_resid = bp->b_bcount;
-                        return(0);
-                }
-                /* or truncate if part of it fits */
-                sz = maxsz - blkno;
-                if (sz <= 0) {
-                        bp->b_error = EINVAL;
-                        goto bad;
+                       bp->b_flags |= B_INVAL;
+                       goto done;
                 }
-                bp->b_bcount = sz << DEV_BSHIFT;
+                bp->b_bcount = (maxsz - blkno) << DEV_BSHIFT;
         }
        nbio = push_bio(bio);
         nbio->bio_offset = bio->bio_offset + ((off_t)p->p_offset << DEV_BSHIFT);
        return (nbio);
 
+       /*
+        * The caller is responsible for calling biodone() on the passed bio
+        * when we return NULL.
+        */
 bad:
-        bp->b_flags |= B_ERROR;
+       bp->b_error = EINVAL;
+error:
+       bp->b_resid = bp->b_bcount;
+        bp->b_flags |= B_ERROR | B_INVAL;
+done:
        return (NULL);
 }
 
index b311395..2f812d8 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)buf.h       8.9 (Berkeley) 3/30/95
  * $FreeBSD: src/sys/sys/buf.h,v 1.88.2.10 2003/01/25 19:02:23 dillon Exp $
- * $DragonFly: src/sys/sys/buf.h,v 1.34 2006/05/03 20:44:49 dillon Exp $
+ * $DragonFly: src/sys/sys/buf.h,v 1.35 2006/05/04 18:32:23 dillon Exp $
  */
 
 #ifndef _SYS_BUF_H_
@@ -116,23 +116,20 @@ typedef enum buf_cmd {
  *     field is NOT USED by lower device layers.  VNode and device
  *     strategy routines WILL NEVER ACCESS THIS FIELD.
  *
- *     b_bcount represents the EOF-clipped request size.  It is typically
- *     set to b_bufsize prior to I/O initiation and may be modified by
- *     the driver chain (for example, to clip upon encountering the end
- *     of the block device).  b_bcount may only be clipped to represent
- *     EOF - for example, it would be clipped to the symlink length when
- *     reading a symlink, or to the file EOF.  It is never clipped due to
- *     an error, nor is it clipped on a zero-fill short read.  For byte
- *     oriented files b_bcount is typically set to b_bufsize to initiate
- *     the read or write to the underlying block device, then clipped to
- *     the file EOF upon completion of the read or write.
+ *     b_bcount represents the I/O request size.  Unless B_NOBCLIP is set,
+ *     the device chain is allowed to clip b_bcount to accomodate the device
+ *     EOF.  Note that this is different from the byte oriented file EOF.
+ *     If B_NOBCLIP is set, the device chain is required to generate an
+ *     error if it would othrewise have to clip the request.  Buffers 
+ *     obtained via getblk() automatically set B_NOBCLIP.  It is important
+ *     to note that EOF clipping via b_bcount is different from EOF clipping
+ *     via returning a b_actual < b_bcount.  B_NOBCLIP only effects block
+ *     oriented EOF clipping (b_bcount modifications).
  *
- *     b_resid.  Number of bytes remaining in I/O.  After an I/O operation
- *     completes, b_resid is usually 0 indicating 100% success.  Note however
- *     that if the device chain encounters an EOF, both b_resid and b_bcount
- *     will be truncated.  So b_resid will also be 0 if a short-read (EOF)
- *     occurs and the caller must check for the EOF condition by comparing
- *     b_bcount against (typically) b_bufsize.
+ *     b_actual represents the number of bytes of I/O that actually occured,
+ *     whether an error occured or not.  b_actual must be initialized to 0
+ *     prior to initiating I/O as the device drivers will assume it to
+ *     start at 0.
  *
  *     b_dirtyoff, b_dirtyend.  Buffers support piecemeal, unaligned
  *     ranges of dirty data that need to be written to backing store.
@@ -255,7 +252,7 @@ struct buf {
 #define        B_CACHE         0x00000020      /* Bread found us in the cache. */
 #define        B_HASHED        0x00000040      /* Indexed via v_rbhash_tree */
 #define        B_DELWRI        0x00000080      /* Delay I/O until buffer reused. */
-#define        B_UNUSED0100    0x00000100
+#define        B_BNOCLIP       0x00000100      /* EOF clipping b_bcount not allowed */
 #define        B_UNUSED0200    0x00000200
 #define        B_EINTR         0x00000400      /* I/O was interrupted */
 #define        B_ERROR         0x00000800      /* I/O error occurred. */
index ba6b69f..07f0d4c 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)mfs_vfsops.c        8.11 (Berkeley) 6/19/95
  * $FreeBSD: src/sys/ufs/mfs/mfs_vfsops.c,v 1.81.2.3 2001/07/04 17:35:21 tegge Exp $
- * $DragonFly: src/sys/vfs/mfs/mfs_vfsops.c,v 1.28 2006/04/02 01:35:34 dillon Exp $
+ * $DragonFly: src/sys/vfs/mfs/mfs_vfsops.c,v 1.29 2006/05/04 18:32:23 dillon Exp $
  */
 
 
@@ -150,34 +150,48 @@ void
 mfsstrategy(dev_t dev, struct bio *bio)
 {
        struct buf *bp = bio->bio_buf;
+       off_t boff = bio->bio_offset;
+       off_t eoff = boff + bp->b_bcount;
        struct mfsnode *mfsp;
 
-       if ((mfsp = dev->si_drv1) != NULL) {
-               off_t boff = bio->bio_offset;
-               off_t eoff = boff + bp->b_bcount;
-
-               if (boff < 0) {
-                       bp->b_error = EINVAL;
-                       biodone(bio);
-               } else if (eoff <= mfsp->mfs_size) {
-                       bioq_insert_tail(&mfsp->bio_queue, bio);
-                       wakeup((caddr_t)mfsp);
-               } else if (boff < mfsp->mfs_size) {
-                       bp->b_bcount = mfsp->mfs_size - boff;
-                       bioq_insert_tail(&mfsp->bio_queue, bio);
-                       wakeup((caddr_t)mfsp);
-               } else if (boff == mfsp->mfs_size) {
+       if ((mfsp = dev->si_drv1) == NULL) {
+               bp->b_error = ENXIO;
+               goto error;
+       }
+       if (boff < 0)
+               goto bad;
+       if (eoff > mfsp->mfs_size) {
+               if (boff > mfsp->mfs_size || (bp->b_flags & B_BNOCLIP))
+                       goto bad;
+               /*
+                * Return EOF by completing the I/O with 0 bytes transfered.
+                * Set B_INVAL to indicate that any data in the buffer is not
+                * valid.
+                */
+               if (boff == mfsp->mfs_size) {
                        bp->b_resid = bp->b_bcount;
-                       biodone(bio);
-               } else {
-                       bp->b_error = EINVAL;
-                       biodone(bio);
+                       bp->b_flags |= B_INVAL;
+                       goto done;
                }
-       } else {
-               bp->b_error = ENXIO;
-               bp->b_flags |= B_ERROR;
-               biodone(bio);
+               bp->b_bcount = mfsp->mfs_size - boff;
        }
+
+       /*
+        * Initiate I/O
+        */
+       bioq_insert_tail(&mfsp->bio_queue, bio);
+       wakeup((caddr_t)mfsp);
+       return;
+
+       /*
+        * Failure conditions on bio
+        */
+bad:
+       bp->b_error = EINVAL;
+error:
+       bp->b_flags |= B_ERROR | B_INVAL;
+done:
+       biodone(bio);
 }
 
 /*