diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-30 17:04:02 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-30 17:04:02 -0500 |
commit | 8e36a5d6ad587d906f0ff677974e5edb0335db30 (patch) | |
tree | ff005c92b621a0f7da159bfaf45bc0fcc4af4b28 /fs/cifs | |
parent | 9bd062d9eaf9e790330f37d9f4518e1b95131f6c (diff) | |
parent | a98ee8c1c707fe3210b00ef9f806ba8e2bf35504 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
[CIFS] fix regression in cifs_write_begin/cifs_write_end
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/file.c | 77 |
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 | 2130 | out: | |
2097 | return 0; | 2131 | *pagep = page; |
2132 | return rc; | ||
2098 | } | 2133 | } |
2099 | 2134 | ||
2100 | const struct address_space_operations cifs_addr_ops = { | 2135 | const struct address_space_operations cifs_addr_ops = { |