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
This commit is contained in:
commit
8e36a5d6ad
1 changed files with 56 additions and 21 deletions
|
@ -1475,7 +1475,11 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
|
|||
cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
|
||||
page, pos, copied));
|
||||
|
||||
if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
|
||||
if (PageChecked(page)) {
|
||||
if (copied == len)
|
||||
SetPageUptodate(page);
|
||||
ClearPageChecked(page);
|
||||
} else if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
|
||||
SetPageUptodate(page);
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
|
@ -2062,39 +2066,70 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
|
|||
{
|
||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
||||
loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||
loff_t page_start = pos & PAGE_MASK;
|
||||
loff_t i_size;
|
||||
struct page *page;
|
||||
int rc = 0;
|
||||
|
||||
cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
|
||||
|
||||
*pagep = __grab_cache_page(mapping, index);
|
||||
if (!*pagep)
|
||||
return -ENOMEM;
|
||||
page = __grab_cache_page(mapping, index);
|
||||
if (!page) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (PageUptodate(*pagep))
|
||||
return 0;
|
||||
if (PageUptodate(page))
|
||||
goto out;
|
||||
|
||||
/* If we are writing a full page it will be up to date,
|
||||
no need to read from the server */
|
||||
if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
|
||||
return 0;
|
||||
/*
|
||||
* If we write a full page it will be up to date, no need to read from
|
||||
* the server. If the write is short, we'll end up doing a sync write
|
||||
* instead.
|
||||
*/
|
||||
if (len == PAGE_CACHE_SIZE)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* optimize away the read when we have an oplock, and we're not
|
||||
* expecting to use any of the data we'd be reading in. That
|
||||
* is, when the page lies beyond the EOF, or straddles the EOF
|
||||
* and the write will cover all of the existing data.
|
||||
*/
|
||||
if (CIFS_I(mapping->host)->clientCanCacheRead) {
|
||||
i_size = i_size_read(mapping->host);
|
||||
if (page_start >= i_size ||
|
||||
(offset == 0 && (pos + len) >= i_size)) {
|
||||
zero_user_segments(page, 0, offset,
|
||||
offset + len,
|
||||
PAGE_CACHE_SIZE);
|
||||
/*
|
||||
* PageChecked means that the parts of the page
|
||||
* to which we're not writing are considered up
|
||||
* to date. Once the data is copied to the
|
||||
* page, it can be set uptodate.
|
||||
*/
|
||||
SetPageChecked(page);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
|
||||
int rc;
|
||||
|
||||
/* might as well read a page, it is fast enough */
|
||||
rc = cifs_readpage_worker(file, *pagep, &offset);
|
||||
|
||||
/* we do not need to pass errors back
|
||||
e.g. if we do not have read access to the file
|
||||
because cifs_write_end will attempt synchronous writes
|
||||
-- shaggy */
|
||||
/*
|
||||
* might as well read a page, it is fast enough. If we get
|
||||
* an error, we don't need to return it. cifs_write_end will
|
||||
* do a sync write instead since PG_uptodate isn't set.
|
||||
*/
|
||||
cifs_readpage_worker(file, page, &page_start);
|
||||
} else {
|
||||
/* we could try using another file handle if there is one -
|
||||
but how would we lock it to prevent close of that handle
|
||||
racing with this read? In any case
|
||||
this will be written out by write_end so is fine */
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
*pagep = page;
|
||||
return rc;
|
||||
}
|
||||
|
||||
const struct address_space_operations cifs_addr_ops = {
|
||||
|
|
Loading…
Reference in a new issue