Skip to content

Commit

Permalink
CIFS: Fix error paths in writeback code
Browse files Browse the repository at this point in the history
This patch aims to address writeback code problems related to error
paths. In particular it respects EINTR and related error codes and
stores and returns the first error occurred during writeback.

Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Acked-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
piastry authored and Steve French committed Jan 11, 2019
1 parent ee258d7 commit 9a66396
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 9 deletions.
19 changes: 19 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,25 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
kfree(param);
}

static inline bool is_interrupt_error(int error)
{
switch (error) {
case -EINTR:
case -ERESTARTSYS:
case -ERESTARTNOHAND:
case -ERESTARTNOINTR:
return true;
}
return false;
}

static inline bool is_retryable_error(int error)
{
if (is_interrupt_error(error) || error == -EAGAIN)
return true;
return false;
}

#define MID_FREE 0
#define MID_REQUEST_ALLOCATED 1
#define MID_REQUEST_SUBMITTED 2
Expand Down
7 changes: 4 additions & 3 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2123,7 +2123,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)

for (j = 0; j < nr_pages; j++) {
unlock_page(wdata2->pages[j]);
if (rc != 0 && rc != -EAGAIN) {
if (rc != 0 && !is_retryable_error(rc)) {
SetPageError(wdata2->pages[j]);
end_page_writeback(wdata2->pages[j]);
put_page(wdata2->pages[j]);
Expand All @@ -2132,7 +2132,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)

if (rc) {
kref_put(&wdata2->refcount, cifs_writedata_release);
if (rc == -EAGAIN)
if (is_retryable_error(rc))
continue;
break;
}
Expand All @@ -2141,7 +2141,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
i += nr_pages;
} while (i < wdata->nr_pages);

mapping_set_error(inode->i_mapping, rc);
if (rc != 0 && !is_retryable_error(rc))
mapping_set_error(inode->i_mapping, rc);
kref_put(&wdata->refcount, cifs_writedata_release);
}

Expand Down
29 changes: 23 additions & 6 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)

if (can_flush) {
rc = filemap_write_and_wait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
if (!is_interrupt_error(rc))
mapping_set_error(inode->i_mapping, rc);

if (tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, full_path,
Expand Down Expand Up @@ -2118,6 +2119,7 @@ static int cifs_writepages(struct address_space *mapping,
pgoff_t end, index;
struct cifs_writedata *wdata;
int rc = 0;
int saved_rc = 0;
unsigned int xid;

/*
Expand Down Expand Up @@ -2146,15 +2148,18 @@ static int cifs_writepages(struct address_space *mapping,

rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits);
if (rc)
if (rc != 0) {
done = true;
break;
}

tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1;

wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index,
&found_pages);
if (!wdata) {
rc = -ENOMEM;
done = true;
add_credits_and_wake_if(server, credits, 0);
break;
}
Expand Down Expand Up @@ -2183,15 +2188,15 @@ static int cifs_writepages(struct address_space *mapping,
if (rc != 0) {
add_credits_and_wake_if(server, wdata->credits, 0);
for (i = 0; i < nr_pages; ++i) {
if (rc == -EAGAIN)
if (is_retryable_error(rc))
redirty_page_for_writepage(wbc,
wdata->pages[i]);
else
SetPageError(wdata->pages[i]);
end_page_writeback(wdata->pages[i]);
put_page(wdata->pages[i]);
}
if (rc != -EAGAIN)
if (!is_retryable_error(rc))
mapping_set_error(mapping, rc);
}
kref_put(&wdata->refcount, cifs_writedata_release);
Expand All @@ -2201,6 +2206,15 @@ static int cifs_writepages(struct address_space *mapping,
continue;
}

/* Return immediately if we received a signal during writing */
if (is_interrupt_error(rc)) {
done = true;
break;
}

if (rc != 0 && saved_rc == 0)
saved_rc = rc;

wbc->nr_to_write -= nr_pages;
if (wbc->nr_to_write <= 0)
done = true;
Expand All @@ -2218,6 +2232,9 @@ static int cifs_writepages(struct address_space *mapping,
goto retry;
}

if (saved_rc != 0)
rc = saved_rc;

if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
mapping->writeback_index = index;

Expand Down Expand Up @@ -2250,8 +2267,8 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
set_page_writeback(page);
retry_write:
rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
if (rc == -EAGAIN) {
if (wbc->sync_mode == WB_SYNC_ALL)
if (is_retryable_error(rc)) {
if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN)
goto retry_write;
redirty_page_for_writepage(wbc, page);
} else if (rc != 0) {
Expand Down
10 changes: 10 additions & 0 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,11 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
* the flush returns error?
*/
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS;
goto out;
}

mapping_set_error(inode->i_mapping, rc);
rc = 0;

Expand Down Expand Up @@ -2400,6 +2405,11 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
* the flush returns error?
*/
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS;
goto cifs_setattr_exit;
}

mapping_set_error(inode->i_mapping, rc);
rc = 0;

Expand Down

0 comments on commit 9a66396

Please sign in to comment.