Skip to content

Commit 3e1f941

Browse files
idryomovaxboe
authored andcommitted
block: fix DIO handling regressions in blkdev_read_iter()
Commit ceaa762 ("block: move direct_IO into our own read_iter handler") introduced several regressions for bdev DIO: 1. read spanning EOF always returns 0 instead of the number of bytes read. This is because "count" is assigned early and isn't updated when the iterator is truncated: $ lsblk -o name,size /dev/vdb NAME SIZE vdb 1G $ xfs_io -d -c 'pread -b 4M 1021M 4M' /dev/vdb read 0/4194304 bytes at offset 1070596096 0.000000 bytes, 0 ops; 0.0007 sec (0.000000 bytes/sec and 0.0000 ops/sec) instead of $ xfs_io -d -c 'pread -b 4M 1021M 4M' /dev/vdb read 3145728/4194304 bytes at offset 1070596096 3 MiB, 1 ops; 0.0007 sec (3.865 GiB/sec and 1319.2612 ops/sec) 2. truncated iterator isn't reexpanded 3. iterator isn't reverted on blkdev_direct_IO() error 4. zero size read no longer skips atime update Fixes: ceaa762 ("block: move direct_IO into our own read_iter handler") Signed-off-by: Ilya Dryomov <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent b879f91 commit 3e1f941

File tree

1 file changed

+19
-14
lines changed

1 file changed

+19
-14
lines changed

block/fops.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -566,34 +566,37 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
566566
{
567567
struct block_device *bdev = iocb->ki_filp->private_data;
568568
loff_t size = bdev_nr_bytes(bdev);
569-
size_t count = iov_iter_count(to);
570569
loff_t pos = iocb->ki_pos;
571570
size_t shorted = 0;
572571
ssize_t ret = 0;
572+
size_t count;
573573

574-
if (unlikely(pos + count > size)) {
574+
if (unlikely(pos + iov_iter_count(to) > size)) {
575575
if (pos >= size)
576576
return 0;
577577
size -= pos;
578-
if (count > size) {
579-
shorted = count - size;
580-
iov_iter_truncate(to, size);
581-
}
578+
shorted = iov_iter_count(to) - size;
579+
iov_iter_truncate(to, size);
582580
}
583581

582+
count = iov_iter_count(to);
583+
if (!count)
584+
goto reexpand; /* skip atime */
585+
584586
if (iocb->ki_flags & IOCB_DIRECT) {
585587
struct address_space *mapping = iocb->ki_filp->f_mapping;
586588

587589
if (iocb->ki_flags & IOCB_NOWAIT) {
588-
if (filemap_range_needs_writeback(mapping, iocb->ki_pos,
589-
iocb->ki_pos + count - 1))
590-
return -EAGAIN;
590+
if (filemap_range_needs_writeback(mapping, pos,
591+
pos + count - 1)) {
592+
ret = -EAGAIN;
593+
goto reexpand;
594+
}
591595
} else {
592-
ret = filemap_write_and_wait_range(mapping,
593-
iocb->ki_pos,
594-
iocb->ki_pos + count - 1);
596+
ret = filemap_write_and_wait_range(mapping, pos,
597+
pos + count - 1);
595598
if (ret < 0)
596-
return ret;
599+
goto reexpand;
597600
}
598601

599602
file_accessed(iocb->ki_filp);
@@ -603,12 +606,14 @@ static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
603606
iocb->ki_pos += ret;
604607
count -= ret;
605608
}
609+
iov_iter_revert(to, count - iov_iter_count(to));
606610
if (ret < 0 || !count)
607-
return ret;
611+
goto reexpand;
608612
}
609613

610614
ret = filemap_read(iocb, to, ret);
611615

616+
reexpand:
612617
if (unlikely(shorted))
613618
iov_iter_reexpand(to, iov_iter_count(to) + shorted);
614619
return ret;

0 commit comments

Comments
 (0)