aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-11-26 14:32:33 -0500
committerSteve French <sfrench@us.ibm.com>2008-11-26 14:32:33 -0500
commita98ee8c1c707fe3210b00ef9f806ba8e2bf35504 (patch)
treeed8557a1755d4e924643cbaf75bb04511f69b3a6
parented313489badef16d700f5a3be50e8fd8f8294bc8 (diff)
[CIFS] fix regression in cifs_write_begin/cifs_write_end
The conversion to write_begin/write_end interfaces had a bug where we were passing a bad parameter to cifs_readpage_worker. Rather than passing the page offset of the start of the write, we needed to pass the offset of the beginning of the page. This was reliably showing up as data corruption in the fsx-linux test from LTP. It also became evident that this code was occasionally doing unnecessary read calls. Optimize those away by using the PG_checked flag to indicate that the unwritten part of the page has been initialized. CC: Nick Piggin <npiggin@suse.de> Acked-by: Dave Kleikamp <shaggy@us.ibm.com> Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/file.c77
1 files changed, 56 insertions, 21 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b691b893a848..f0a81e631ae6 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -1475,7 +1475,11 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
1475 cFYI(1, ("write_end for page %p from pos %lld with %d bytes", 1475 cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
1476 page, pos, copied)); 1476 page, pos, copied));
1477 1477
1478 if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) 1478 if (PageChecked(page)) {
1479 if (copied == len)
1480 SetPageUptodate(page);
1481 ClearPageChecked(page);
1482 } else if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
1479 SetPageUptodate(page); 1483 SetPageUptodate(page);
1480 1484
1481 if (!PageUptodate(page)) { 1485 if (!PageUptodate(page)) {
@@ -2062,39 +2066,70 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
2062{ 2066{
2063 pgoff_t index = pos >> PAGE_CACHE_SHIFT; 2067 pgoff_t index = pos >> PAGE_CACHE_SHIFT;
2064 loff_t offset = pos & (PAGE_CACHE_SIZE - 1); 2068 loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
2069 loff_t page_start = pos & PAGE_MASK;
2070 loff_t i_size;
2071 struct page *page;
2072 int rc = 0;
2065 2073
2066 cFYI(1, ("write_begin from %lld len %d", (long long)pos, len)); 2074 cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
2067 2075
2068 *pagep = __grab_cache_page(mapping, index); 2076 page = __grab_cache_page(mapping, index);
2069 if (!*pagep) 2077 if (!page) {
2070 return -ENOMEM; 2078 rc = -ENOMEM;
2071 2079 goto out;
2072 if (PageUptodate(*pagep)) 2080 }
2073 return 0;
2074 2081
2075 /* If we are writing a full page it will be up to date, 2082 if (PageUptodate(page))
2076 no need to read from the server */ 2083 goto out;
2077 if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
2078 return 0;
2079 2084
2080 if ((file->f_flags & O_ACCMODE) != O_WRONLY) { 2085 /*
2081 int rc; 2086 * If we write a full page it will be up to date, no need to read from
2087 * the server. If the write is short, we'll end up doing a sync write
2088 * instead.
2089 */
2090 if (len == PAGE_CACHE_SIZE)
2091 goto out;
2082 2092
2083 /* might as well read a page, it is fast enough */ 2093 /*
2084 rc = cifs_readpage_worker(file, *pagep, &offset); 2094 * optimize away the read when we have an oplock, and we're not
2095 * expecting to use any of the data we'd be reading in. That
2096 * is, when the page lies beyond the EOF, or straddles the EOF
2097 * and the write will cover all of the existing data.
2098 */
2099 if (CIFS_I(mapping->host)->clientCanCacheRead) {
2100 i_size = i_size_read(mapping->host);
2101 if (page_start >= i_size ||
2102 (offset == 0 && (pos + len) >= i_size)) {
2103 zero_user_segments(page, 0, offset,
2104 offset + len,
2105 PAGE_CACHE_SIZE);
2106 /*
2107 * PageChecked means that the parts of the page
2108 * to which we're not writing are considered up
2109 * to date. Once the data is copied to the
2110 * page, it can be set uptodate.
2111 */
2112 SetPageChecked(page);
2113 goto out;
2114 }
2115 }
2085 2116
2086 /* we do not need to pass errors back 2117 if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
2087 e.g. if we do not have read access to the file 2118 /*
2088 because cifs_write_end will attempt synchronous writes 2119 * might as well read a page, it is fast enough. If we get
2089 -- shaggy */ 2120 * an error, we don't need to return it. cifs_write_end will
2121 * do a sync write instead since PG_uptodate isn't set.
2122 */
2123 cifs_readpage_worker(file, page, &page_start);
2090 } else { 2124 } else {
2091 /* we could try using another file handle if there is one - 2125 /* we could try using another file handle if there is one -
2092 but how would we lock it to prevent close of that handle 2126 but how would we lock it to prevent close of that handle
2093 racing with this read? In any case 2127 racing with this read? In any case
2094 this will be written out by write_end so is fine */ 2128 this will be written out by write_end so is fine */
2095 } 2129 }
2096 2130out:
2097 return 0; 2131 *pagep = page;
2132 return rc;
2098} 2133}
2099 2134
2100const struct address_space_operations cifs_addr_ops = { 2135const struct address_space_operations cifs_addr_ops = {