diff options
| author | Hisashi Hifumi <hifumi.hisashi@oss.ntt.co.jp> | 2008-07-28 18:46:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-28 19:30:21 -0400 |
| commit | 8ab22b9abb5c55413802e4adc9aa6223324547c3 (patch) | |
| tree | cff3319e1275e8a7c083d492889ec6bd0c7712d3 | |
| parent | d84a52f62f6a396ed77aa0052da74ca9e760b28a (diff) | |
vfs: pagecache usage optimization for pagesize!=blocksize
When we read some part of a file through pagecache, if there is a
pagecache of corresponding index but this page is not uptodate, read IO
is issued and this page will be uptodate.
I think this is good for pagesize == blocksize environment but there is
room for improvement on pagesize != blocksize environment. Because in
this case a page can have multiple buffers and even if a page is not
uptodate, some buffers can be uptodate.
So I suggest that when all buffers which correspond to a part of a file
that we want to read are uptodate, use this pagecache and copy data from
this pagecache to user buffer even if a page is not uptodate. This can
reduce read IO and improve system throughput.
I wrote a benchmark program and got result number with this program.
This benchmark do:
1: mount and open a test file.
2: create a 512MB file.
3: close a file and umount.
4: mount and again open a test file.
5: pwrite randomly 300000 times on a test file. offset is aligned
by IO size(1024bytes).
6: measure time of preading randomly 100000 times on a test file.
The result was:
2.6.26
330 sec
2.6.26-patched
226 sec
Arch:i386
Filesystem:ext3
Blocksize:1024 bytes
Memory: 1GB
On ext3/4, a file is written through buffer/block. So random read/write
mixed workloads or random read after random write workloads are optimized
with this patch under pagesize != blocksize environment. This test result
showed this.
The benchmark program is as follows:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#define LEN 1024
#define LOOP 1024*512 /* 512MB */
main(void)
{
unsigned long i, offset, filesize;
int fd;
char buf[LEN];
time_t t1, t2;
if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) {
perror("cannot mount\n");
exit(1);
}
memset(buf, 0, LEN);
fd = open("/root/test1/testfile", O_CREAT|O_RDWR|O_TRUNC);
if (fd < 0) {
perror("cannot open file\n");
exit(1);
}
for (i = 0; i < LOOP; i++)
write(fd, buf, LEN);
close(fd);
if (umount("/root/test1/") < 0) {
perror("cannot umount\n");
exit(1);
}
if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) {
perror("cannot mount\n");
exit(1);
}
fd = open("/root/test1/testfile", O_RDWR);
if (fd < 0) {
perror("cannot open file\n");
exit(1);
}
filesize = LEN * LOOP;
for (i = 0; i < 300000; i++){
offset = (random() % filesize) & (~(LEN - 1));
pwrite(fd, buf, LEN, offset);
}
printf("start test\n");
time(&t1);
for (i = 0; i < 100000; i++){
offset = (random() % filesize) & (~(LEN - 1));
pread(fd, buf, LEN, offset);
}
time(&t2);
printf("%ld sec\n", t2-t1);
close(fd);
if (umount("/root/test1/") < 0) {
perror("cannot umount\n");
exit(1);
}
}
Signed-off-by: Hisashi Hifumi <hifumi.hisashi@oss.ntt.co.jp>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Jan Kara <jack@ucw.cz>
Cc: <linux-ext4@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/buffer.c | 46 | ||||
| -rw-r--r-- | fs/ext2/inode.c | 1 | ||||
| -rw-r--r-- | fs/ext3/inode.c | 67 | ||||
| -rw-r--r-- | fs/ext4/inode.c | 92 | ||||
| -rw-r--r-- | include/linux/buffer_head.h | 2 | ||||
| -rw-r--r-- | include/linux/fs.h | 44 | ||||
| -rw-r--r-- | mm/filemap.c | 14 |
7 files changed, 167 insertions, 99 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index f95805019639..ca12a6bb82b1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
| @@ -2096,6 +2096,52 @@ int generic_write_end(struct file *file, struct address_space *mapping, | |||
| 2096 | EXPORT_SYMBOL(generic_write_end); | 2096 | EXPORT_SYMBOL(generic_write_end); |
| 2097 | 2097 | ||
| 2098 | /* | 2098 | /* |
| 2099 | * block_is_partially_uptodate checks whether buffers within a page are | ||
| 2100 | * uptodate or not. | ||
| 2101 | * | ||
| 2102 | * Returns true if all buffers which correspond to a file portion | ||
| 2103 | * we want to read are uptodate. | ||
| 2104 | */ | ||
| 2105 | int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, | ||
| 2106 | unsigned long from) | ||
| 2107 | { | ||
| 2108 | struct inode *inode = page->mapping->host; | ||
| 2109 | unsigned block_start, block_end, blocksize; | ||
| 2110 | unsigned to; | ||
| 2111 | struct buffer_head *bh, *head; | ||
| 2112 | int ret = 1; | ||
| 2113 | |||
| 2114 | if (!page_has_buffers(page)) | ||
| 2115 | return 0; | ||
| 2116 | |||
| 2117 | blocksize = 1 << inode->i_blkbits; | ||
| 2118 | to = min_t(unsigned, PAGE_CACHE_SIZE - from, desc->count); | ||
| 2119 | to = from + to; | ||
| 2120 | if (from < blocksize && to > PAGE_CACHE_SIZE - blocksize) | ||
| 2121 | return 0; | ||
| 2122 | |||
| 2123 | head = page_buffers(page); | ||
| 2124 | bh = head; | ||
| 2125 | block_start = 0; | ||
| 2126 | do { | ||
| 2127 | block_end = block_start + blocksize; | ||
| 2128 | if (block_end > from && block_start < to) { | ||
| 2129 | if (!buffer_uptodate(bh)) { | ||
| 2130 | ret = 0; | ||
| 2131 | break; | ||
| 2132 | } | ||
| 2133 | if (block_end >= to) | ||
| 2134 | break; | ||
| 2135 | } | ||
| 2136 | block_start = block_end; | ||
| 2137 | bh = bh->b_this_page; | ||
| 2138 | } while (bh != head); | ||
| 2139 | |||
| 2140 | return ret; | ||
| 2141 | } | ||
| 2142 | EXPORT_SYMBOL(block_is_partially_uptodate); | ||
| 2143 | |||
| 2144 | /* | ||
| 2099 | * Generic "read page" function for block devices that have the normal | 2145 | * Generic "read page" function for block devices that have the normal |
| 2100 | * get_block functionality. This is most of the block device filesystems. | 2146 | * get_block functionality. This is most of the block device filesystems. |
| 2101 | * Reads the page asynchronously --- the unlock_buffer() and | 2147 | * Reads the page asynchronously --- the unlock_buffer() and |
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 384fc0d1dd74..991d6dfeb51f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c | |||
| @@ -791,6 +791,7 @@ const struct address_space_operations ext2_aops = { | |||
| 791 | .direct_IO = ext2_direct_IO, | 791 | .direct_IO = ext2_direct_IO, |
| 792 | .writepages = ext2_writepages, | 792 | .writepages = ext2_writepages, |
| 793 | .migratepage = buffer_migrate_page, | 793 | .migratepage = buffer_migrate_page, |
| 794 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 794 | }; | 795 | }; |
| 795 | 796 | ||
| 796 | const struct address_space_operations ext2_aops_xip = { | 797 | const struct address_space_operations ext2_aops_xip = { |
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 3bf07d70b914..507d8689b111 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
| @@ -1767,44 +1767,47 @@ static int ext3_journalled_set_page_dirty(struct page *page) | |||
| 1767 | } | 1767 | } |
| 1768 | 1768 | ||
| 1769 | static const struct address_space_operations ext3_ordered_aops = { | 1769 | static const struct address_space_operations ext3_ordered_aops = { |
| 1770 | .readpage = ext3_readpage, | 1770 | .readpage = ext3_readpage, |
| 1771 | .readpages = ext3_readpages, | 1771 | .readpages = ext3_readpages, |
| 1772 | .writepage = ext3_ordered_writepage, | 1772 | .writepage = ext3_ordered_writepage, |
| 1773 | .sync_page = block_sync_page, | 1773 | .sync_page = block_sync_page, |
| 1774 | .write_begin = ext3_write_begin, | 1774 | .write_begin = ext3_write_begin, |
| 1775 | .write_end = ext3_ordered_write_end, | 1775 | .write_end = ext3_ordered_write_end, |
| 1776 | .bmap = ext3_bmap, | 1776 | .bmap = ext3_bmap, |
| 1777 | .invalidatepage = ext3_invalidatepage, | 1777 | .invalidatepage = ext3_invalidatepage, |
| 1778 | .releasepage = ext3_releasepage, | 1778 | .releasepage = ext3_releasepage, |
| 1779 | .direct_IO = ext3_direct_IO, | 1779 | .direct_IO = ext3_direct_IO, |
| 1780 | .migratepage = buffer_migrate_page, | 1780 | .migratepage = buffer_migrate_page, |
| 1781 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 1781 | }; | 1782 | }; |
| 1782 | 1783 | ||
| 1783 | static const struct address_space_operations ext3_writeback_aops = { | 1784 | static const struct address_space_operations ext3_writeback_aops = { |
| 1784 | .readpage = ext3_readpage, | 1785 | .readpage = ext3_readpage, |
| 1785 | .readpages = ext3_readpages, | 1786 | .readpages = ext3_readpages, |
| 1786 | .writepage = ext3_writeback_writepage, | 1787 | .writepage = ext3_writeback_writepage, |
| 1787 | .sync_page = block_sync_page, | 1788 | .sync_page = block_sync_page, |
| 1788 | .write_begin = ext3_write_begin, | 1789 | .write_begin = ext3_write_begin, |
| 1789 | .write_end = ext3_writeback_write_end, | 1790 | .write_end = ext3_writeback_write_end, |
| 1790 | .bmap = ext3_bmap, | 1791 | .bmap = ext3_bmap, |
| 1791 | .invalidatepage = ext3_invalidatepage, | 1792 | .invalidatepage = ext3_invalidatepage, |
| 1792 | .releasepage = ext3_releasepage, | 1793 | .releasepage = ext3_releasepage, |
| 1793 | .direct_IO = ext3_direct_IO, | 1794 | .direct_IO = ext3_direct_IO, |
| 1794 | .migratepage = buffer_migrate_page, | 1795 | .migratepage = buffer_migrate_page, |
| 1796 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 1795 | }; | 1797 | }; |
| 1796 | 1798 | ||
| 1797 | static const struct address_space_operations ext3_journalled_aops = { | 1799 | static const struct address_space_operations ext3_journalled_aops = { |
| 1798 | .readpage = ext3_readpage, | 1800 | .readpage = ext3_readpage, |
| 1799 | .readpages = ext3_readpages, | 1801 | .readpages = ext3_readpages, |
| 1800 | .writepage = ext3_journalled_writepage, | 1802 | .writepage = ext3_journalled_writepage, |
| 1801 | .sync_page = block_sync_page, | 1803 | .sync_page = block_sync_page, |
| 1802 | .write_begin = ext3_write_begin, | 1804 | .write_begin = ext3_write_begin, |
| 1803 | .write_end = ext3_journalled_write_end, | 1805 | .write_end = ext3_journalled_write_end, |
| 1804 | .set_page_dirty = ext3_journalled_set_page_dirty, | 1806 | .set_page_dirty = ext3_journalled_set_page_dirty, |
| 1805 | .bmap = ext3_bmap, | 1807 | .bmap = ext3_bmap, |
| 1806 | .invalidatepage = ext3_invalidatepage, | 1808 | .invalidatepage = ext3_invalidatepage, |
| 1807 | .releasepage = ext3_releasepage, | 1809 | .releasepage = ext3_releasepage, |
| 1810 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 1808 | }; | 1811 | }; |
| 1809 | 1812 | ||
| 1810 | void ext3_set_aops(struct inode *inode) | 1813 | void ext3_set_aops(struct inode *inode) |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8ca2763df091..9843b046c235 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
| @@ -2806,59 +2806,63 @@ static int ext4_journalled_set_page_dirty(struct page *page) | |||
| 2806 | } | 2806 | } |
| 2807 | 2807 | ||
| 2808 | static const struct address_space_operations ext4_ordered_aops = { | 2808 | static const struct address_space_operations ext4_ordered_aops = { |
| 2809 | .readpage = ext4_readpage, | 2809 | .readpage = ext4_readpage, |
| 2810 | .readpages = ext4_readpages, | 2810 | .readpages = ext4_readpages, |
| 2811 | .writepage = ext4_normal_writepage, | 2811 | .writepage = ext4_normal_writepage, |
| 2812 | .sync_page = block_sync_page, | 2812 | .sync_page = block_sync_page, |
| 2813 | .write_begin = ext4_write_begin, | 2813 | .write_begin = ext4_write_begin, |
| 2814 | .write_end = ext4_ordered_write_end, | 2814 | .write_end = ext4_ordered_write_end, |
| 2815 | .bmap = ext4_bmap, | 2815 | .bmap = ext4_bmap, |
| 2816 | .invalidatepage = ext4_invalidatepage, | 2816 | .invalidatepage = ext4_invalidatepage, |
| 2817 | .releasepage = ext4_releasepage, | 2817 | .releasepage = ext4_releasepage, |
| 2818 | .direct_IO = ext4_direct_IO, | 2818 | .direct_IO = ext4_direct_IO, |
| 2819 | .migratepage = buffer_migrate_page, | 2819 | .migratepage = buffer_migrate_page, |
| 2820 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 2820 | }; | 2821 | }; |
| 2821 | 2822 | ||
| 2822 | static const struct address_space_operations ext4_writeback_aops = { | 2823 | static const struct address_space_operations ext4_writeback_aops = { |
| 2823 | .readpage = ext4_readpage, | 2824 | .readpage = ext4_readpage, |
| 2824 | .readpages = ext4_readpages, | 2825 | .readpages = ext4_readpages, |
| 2825 | .writepage = ext4_normal_writepage, | 2826 | .writepage = ext4_normal_writepage, |
| 2826 | .sync_page = block_sync_page, | 2827 | .sync_page = block_sync_page, |
| 2827 | .write_begin = ext4_write_begin, | 2828 | .write_begin = ext4_write_begin, |
| 2828 | .write_end = ext4_writeback_write_end, | 2829 | .write_end = ext4_writeback_write_end, |
| 2829 | .bmap = ext4_bmap, | 2830 | .bmap = ext4_bmap, |
| 2830 | .invalidatepage = ext4_invalidatepage, | 2831 | .invalidatepage = ext4_invalidatepage, |
| 2831 | .releasepage = ext4_releasepage, | 2832 | .releasepage = ext4_releasepage, |
| 2832 | .direct_IO = ext4_direct_IO, | 2833 | .direct_IO = ext4_direct_IO, |
| 2833 | .migratepage = buffer_migrate_page, | 2834 | .migratepage = buffer_migrate_page, |
| 2835 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 2834 | }; | 2836 | }; |
| 2835 | 2837 | ||
| 2836 | static const struct address_space_operations ext4_journalled_aops = { | 2838 | static const struct address_space_operations ext4_journalled_aops = { |
| 2837 | .readpage = ext4_readpage, | 2839 | .readpage = ext4_readpage, |
| 2838 | .readpages = ext4_readpages, | 2840 | .readpages = ext4_readpages, |
| 2839 | .writepage = ext4_journalled_writepage, | 2841 | .writepage = ext4_journalled_writepage, |
| 2840 | .sync_page = block_sync_page, | 2842 | .sync_page = block_sync_page, |
| 2841 | .write_begin = ext4_write_begin, | 2843 | .write_begin = ext4_write_begin, |
| 2842 | .write_end = ext4_journalled_write_end, | 2844 | .write_end = ext4_journalled_write_end, |
| 2843 | .set_page_dirty = ext4_journalled_set_page_dirty, | 2845 | .set_page_dirty = ext4_journalled_set_page_dirty, |
| 2844 | .bmap = ext4_bmap, | 2846 | .bmap = ext4_bmap, |
| 2845 | .invalidatepage = ext4_invalidatepage, | 2847 | .invalidatepage = ext4_invalidatepage, |
| 2846 | .releasepage = ext4_releasepage, | 2848 | .releasepage = ext4_releasepage, |
| 2849 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 2847 | }; | 2850 | }; |
| 2848 | 2851 | ||
| 2849 | static const struct address_space_operations ext4_da_aops = { | 2852 | static const struct address_space_operations ext4_da_aops = { |
| 2850 | .readpage = ext4_readpage, | 2853 | .readpage = ext4_readpage, |
| 2851 | .readpages = ext4_readpages, | 2854 | .readpages = ext4_readpages, |
| 2852 | .writepage = ext4_da_writepage, | 2855 | .writepage = ext4_da_writepage, |
| 2853 | .writepages = ext4_da_writepages, | 2856 | .writepages = ext4_da_writepages, |
| 2854 | .sync_page = block_sync_page, | 2857 | .sync_page = block_sync_page, |
| 2855 | .write_begin = ext4_da_write_begin, | 2858 | .write_begin = ext4_da_write_begin, |
| 2856 | .write_end = ext4_da_write_end, | 2859 | .write_end = ext4_da_write_end, |
| 2857 | .bmap = ext4_bmap, | 2860 | .bmap = ext4_bmap, |
| 2858 | .invalidatepage = ext4_da_invalidatepage, | 2861 | .invalidatepage = ext4_da_invalidatepage, |
| 2859 | .releasepage = ext4_releasepage, | 2862 | .releasepage = ext4_releasepage, |
| 2860 | .direct_IO = ext4_direct_IO, | 2863 | .direct_IO = ext4_direct_IO, |
| 2861 | .migratepage = buffer_migrate_page, | 2864 | .migratepage = buffer_migrate_page, |
| 2865 | .is_partially_uptodate = block_is_partially_uptodate, | ||
| 2862 | }; | 2866 | }; |
| 2863 | 2867 | ||
| 2864 | void ext4_set_aops(struct inode *inode) | 2868 | void ext4_set_aops(struct inode *inode) |
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 82aa36c53ea7..50cfe8ceb478 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h | |||
| @@ -205,6 +205,8 @@ void block_invalidatepage(struct page *page, unsigned long offset); | |||
| 205 | int block_write_full_page(struct page *page, get_block_t *get_block, | 205 | int block_write_full_page(struct page *page, get_block_t *get_block, |
| 206 | struct writeback_control *wbc); | 206 | struct writeback_control *wbc); |
| 207 | int block_read_full_page(struct page*, get_block_t*); | 207 | int block_read_full_page(struct page*, get_block_t*); |
| 208 | int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, | ||
| 209 | unsigned long from); | ||
| 208 | int block_write_begin(struct file *, struct address_space *, | 210 | int block_write_begin(struct file *, struct address_space *, |
| 209 | loff_t, unsigned, unsigned, | 211 | loff_t, unsigned, unsigned, |
| 210 | struct page **, void **, get_block_t*); | 212 | struct page **, void **, get_block_t*); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 8252b045e624..580b513668fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -443,6 +443,27 @@ static inline size_t iov_iter_count(struct iov_iter *i) | |||
| 443 | return i->count; | 443 | return i->count; |
| 444 | } | 444 | } |
| 445 | 445 | ||
| 446 | /* | ||
| 447 | * "descriptor" for what we're up to with a read. | ||
| 448 | * This allows us to use the same read code yet | ||
| 449 | * have multiple different users of the data that | ||
| 450 | * we read from a file. | ||
| 451 | * | ||
| 452 | * The simplest case just copies the data to user | ||
| 453 | * mode. | ||
| 454 | */ | ||
| 455 | typedef struct { | ||
| 456 | size_t written; | ||
| 457 | size_t count; | ||
| 458 | union { | ||
| 459 | char __user *buf; | ||
| 460 | void *data; | ||
| 461 | } arg; | ||
| 462 | int error; | ||
| 463 | } read_descriptor_t; | ||
| 464 | |||
| 465 | typedef int (*read_actor_t)(read_descriptor_t *, struct page *, | ||
| 466 | unsigned long, unsigned long); | ||
| 446 | 467 | ||
| 447 | struct address_space_operations { | 468 | struct address_space_operations { |
| 448 | int (*writepage)(struct page *page, struct writeback_control *wbc); | 469 | int (*writepage)(struct page *page, struct writeback_control *wbc); |
| @@ -484,6 +505,8 @@ struct address_space_operations { | |||
| 484 | int (*migratepage) (struct address_space *, | 505 | int (*migratepage) (struct address_space *, |
| 485 | struct page *, struct page *); | 506 | struct page *, struct page *); |
| 486 | int (*launder_page) (struct page *); | 507 | int (*launder_page) (struct page *); |
| 508 | int (*is_partially_uptodate) (struct page *, read_descriptor_t *, | ||
| 509 | unsigned long); | ||
| 487 | }; | 510 | }; |
| 488 | 511 | ||
| 489 | /* | 512 | /* |
| @@ -1198,27 +1221,6 @@ struct block_device_operations { | |||
| 1198 | struct module *owner; | 1221 | struct module *owner; |
| 1199 | }; | 1222 | }; |
| 1200 | 1223 | ||
| 1201 | /* | ||
| 1202 | * "descriptor" for what we're up to with a read. | ||
| 1203 | * This allows us to use the same read code yet | ||
| 1204 | * have multiple different users of the data that | ||
| 1205 | * we read from a file. | ||
| 1206 | * | ||
| 1207 | * The simplest case just copies the data to user | ||
| 1208 | * mode. | ||
| 1209 | */ | ||
| 1210 | typedef struct { | ||
| 1211 | size_t written; | ||
| 1212 | size_t count; | ||
| 1213 | union { | ||
| 1214 | char __user * buf; | ||
| 1215 | void *data; | ||
| 1216 | } arg; | ||
| 1217 | int error; | ||
| 1218 | } read_descriptor_t; | ||
| 1219 | |||
| 1220 | typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long); | ||
| 1221 | |||
| 1222 | /* These macros are for out of kernel modules to test that | 1224 | /* These macros are for out of kernel modules to test that |
| 1223 | * the kernel supports the unlocked_ioctl and compat_ioctl | 1225 | * the kernel supports the unlocked_ioctl and compat_ioctl |
| 1224 | * fields in struct file_operations. */ | 1226 | * fields in struct file_operations. */ |
diff --git a/mm/filemap.c b/mm/filemap.c index 5de7633e1dbe..42bbc6909ba4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
| @@ -1023,8 +1023,17 @@ find_page: | |||
| 1023 | ra, filp, page, | 1023 | ra, filp, page, |
| 1024 | index, last_index - index); | 1024 | index, last_index - index); |
| 1025 | } | 1025 | } |
| 1026 | if (!PageUptodate(page)) | 1026 | if (!PageUptodate(page)) { |
| 1027 | goto page_not_up_to_date; | 1027 | if (inode->i_blkbits == PAGE_CACHE_SHIFT || |
| 1028 | !mapping->a_ops->is_partially_uptodate) | ||
| 1029 | goto page_not_up_to_date; | ||
| 1030 | if (TestSetPageLocked(page)) | ||
| 1031 | goto page_not_up_to_date; | ||
| 1032 | if (!mapping->a_ops->is_partially_uptodate(page, | ||
| 1033 | desc, offset)) | ||
| 1034 | goto page_not_up_to_date_locked; | ||
| 1035 | unlock_page(page); | ||
| 1036 | } | ||
| 1028 | page_ok: | 1037 | page_ok: |
| 1029 | /* | 1038 | /* |
| 1030 | * i_size must be checked after we know the page is Uptodate. | 1039 | * i_size must be checked after we know the page is Uptodate. |
| @@ -1094,6 +1103,7 @@ page_not_up_to_date: | |||
| 1094 | if (lock_page_killable(page)) | 1103 | if (lock_page_killable(page)) |
| 1095 | goto readpage_eio; | 1104 | goto readpage_eio; |
| 1096 | 1105 | ||
| 1106 | page_not_up_to_date_locked: | ||
| 1097 | /* Did it get truncated before we got the lock? */ | 1107 | /* Did it get truncated before we got the lock? */ |
| 1098 | if (!page->mapping) { | 1108 | if (!page->mapping) { |
| 1099 | unlock_page(page); | 1109 | unlock_page(page); |
