]> git.hungrycats.org Git - linux/commitdiff
[PATCH] direct-io hole fix
authorAndrew Morton <akpm@osdl.org>
Thu, 3 Jun 2004 01:02:02 +0000 (18:02 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 3 Jun 2004 01:02:02 +0000 (18:02 -0700)
From: Chris Mason <mason@suse.com>

When filling holes via DIRECT_IO, we fall back to normal buffered io.  For
this to work properly, the direct io funcs have to return a value of zero to
the file write functions, so the file write functions know where to start
writing.

In some cases, dio->result was getting returned by direct_io_worker, and that
wasn't always zero, causing some data not to be written.

From: <akpm@osdl.org>:

- Simplify things by setting `ret' later on, fix up subsequent damage to the
  dio_complete() args.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/direct-io.c

index 0831f490f8713c468a5e68a3e5267e95b10b7282..3741932a4b935ea46d1edce63ab0af4b20028500 100644 (file)
@@ -987,13 +987,6 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode,
                }
        } /* end iovec loop */
 
-       if (ret == -ENOTBLK && rw == WRITE) {
-               /*
-                * The remaining part of the request will be
-                * be handled by buffered I/O when we return
-                */
-               ret = 0;
-       }
        /*
         * There may be some unwritten disk at the end of a part-written
         * fs-block-sized block.  Go zero that now.
@@ -1060,24 +1053,29 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode,
                        kfree(dio);
                }
        } else {
+               ssize_t transferred = 0;
+
                finished_one_bio(dio);
                ret2 = dio_await_completion(dio);
                if (ret == 0)
                        ret = ret2;
                if (ret == 0)
                        ret = dio->page_errors;
-               if (ret == 0 && dio->result) {
+               if (dio->result) {
                        loff_t i_size = i_size_read(inode);
 
-                       ret = dio->result;
+                       transferred = dio->result;
                        /*
                         * Adjust the return value if the read crossed a
                         * non-block-aligned EOF.
                         */
-                       if (rw == READ && (offset + ret > i_size))
-                               ret = i_size - offset;
+                       if (rw == READ && (offset + transferred > i_size))
+                               transferred = i_size - offset;
                }
-               dio_complete(dio, offset, ret);
+               dio_complete(dio, offset, transferred);
+               if (ret == 0)
+                       ret = transferred;
+
                /* We could have also come here on an AIO file extend */
                if (!is_sync_kiocb(iocb) && rw == WRITE &&
                    ret >= 0 && dio->result == dio->size)
@@ -1088,6 +1086,13 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode,
                        aio_complete(iocb, ret, 0);
                kfree(dio);
        }
+       if (ret == -ENOTBLK && rw == WRITE) {
+               /*
+                * The entire request will be be handled by buffered I/O
+                * when we return
+                */
+               ret = 0;
+       }
        return ret;
 }