Commit 66f998f6 authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

fs: allow short direct-io reads to be completed via buffered IO

This is similar to what already happens in the write case.  If we have a short
read while doing O_DIRECT, instead of just returning, fallthrough and try to
read the rest via buffered IO.  BTRFS needs this because if we encounter a
compressed or inline extent during DIO, we need to fallback on buffered.  If the
extent is compressed we need to read the entire thing into memory and
de-compress it into the users pages.  I have tested this with fsx and everything
works great.  Thanks,
Signed-off-by: default avatarJosef Bacik <josef@redhat.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 3fd0a558
...@@ -1263,7 +1263,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1263,7 +1263,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
{ {
struct file *filp = iocb->ki_filp; struct file *filp = iocb->ki_filp;
ssize_t retval; ssize_t retval;
unsigned long seg; unsigned long seg = 0;
size_t count; size_t count;
loff_t *ppos = &iocb->ki_pos; loff_t *ppos = &iocb->ki_pos;
...@@ -1290,21 +1290,47 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1290,21 +1290,47 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
retval = mapping->a_ops->direct_IO(READ, iocb, retval = mapping->a_ops->direct_IO(READ, iocb,
iov, pos, nr_segs); iov, pos, nr_segs);
} }
if (retval > 0) if (retval > 0) {
*ppos = pos + retval; *ppos = pos + retval;
if (retval) { count -= retval;
}
/*
* Btrfs can have a short DIO read if we encounter
* compressed extents, so if there was an error, or if
* we've already read everything we wanted to, or if
* there was a short read because we hit EOF, go ahead
* and return. Otherwise fallthrough to buffered io for
* the rest of the read.
*/
if (retval < 0 || !count || *ppos >= size) {
file_accessed(filp); file_accessed(filp);
goto out; goto out;
} }
} }
} }
count = retval;
for (seg = 0; seg < nr_segs; seg++) { for (seg = 0; seg < nr_segs; seg++) {
read_descriptor_t desc; read_descriptor_t desc;
loff_t offset = 0;
/*
* If we did a short DIO read we need to skip the section of the
* iov that we've already read data into.
*/
if (count) {
if (count > iov[seg].iov_len) {
count -= iov[seg].iov_len;
continue;
}
offset = count;
count = 0;
}
desc.written = 0; desc.written = 0;
desc.arg.buf = iov[seg].iov_base; desc.arg.buf = iov[seg].iov_base + offset;
desc.count = iov[seg].iov_len; desc.count = iov[seg].iov_len - offset;
if (desc.count == 0) if (desc.count == 0)
continue; continue;
desc.error = 0; desc.error = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment