Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35dc8161 authored by Jeff Moyer's avatar Jeff Moyer Committed by Linus Torvalds
Browse files

[PATCH] fix O_DIRECT read of last block in a sparse file



Currently, if you open a file O_DIRECT, truncate it to a size that is not a
multiple of the disk block size, and then try to read the last block in the
file, the read will return 0.  The problem is in do_direct_IO, here:

        /* Handle holes */
        if (!buffer_mapped(map_bh)) {
                char *kaddr;

		...

                if (dio->block_in_file >=
                        i_size_read(dio->inode)>>blkbits) {
                        /* We hit eof */
                        page_cache_release(page);
                        goto out;
                }

We shift off any remaining bytes in the final block of the I/O, resulting
in a 0-sized read.  I've attached a patch that fixes this.  I'm not happy
about how ugly the math is getting, so suggestions are more than welcome.

I've tested this with a simple program that performs the steps outlined for
reproducing the problem above.  Without the patch, we get a 0-sized result
from read.  With the patch, we get the correct return value from the short
read.

Signed-off-by: default avatarJeff Moyer <jmoyer@redhat.com>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Suparna Bhattacharya <suparna@in.ibm.com>
Cc: Mingming Cao <cmm@us.ibm.com>
Cc: Joel Becker <Joel.Becker@oracle.com>
Cc: "Chen, Kenneth W" <kenneth.w.chen@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 83541796
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -857,6 +857,7 @@ static int do_direct_IO(struct dio *dio)
			/* Handle holes */
			if (!buffer_mapped(map_bh)) {
				char *kaddr;
				loff_t i_size_aligned;

				/* AKPM: eargh, -ENOTBLK is a hack */
				if (dio->rw == WRITE) {
@@ -864,8 +865,14 @@ static int do_direct_IO(struct dio *dio)
					return -ENOTBLK;
				}

				/*
				 * Be sure to account for a partial block as the
				 * last block in the file
				 */
				i_size_aligned = ALIGN(i_size_read(dio->inode),
							1 << blkbits);
				if (dio->block_in_file >=
					i_size_read(dio->inode)>>blkbits) {
						i_size_aligned >> blkbits) {
					/* We hit eof */
					page_cache_release(page);
					goto out;