Commit 9a66396f authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

CIFS: Fix error paths in writeback code



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: default avatarPavel Shilovsky <pshilov@microsoft.com>
Acked-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent ee258d79
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -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
+4 −3
Original line number Diff line number Diff line
@@ -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]);
@@ -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;
		}
@@ -2141,6 +2141,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
		i += nr_pages;
	} while (i < wdata->nr_pages);

	if (rc != 0 && !is_retryable_error(rc))
		mapping_set_error(inode->i_mapping, rc);
	kref_put(&wdata->refcount, cifs_writedata_release);
}
+23 −6
Original line number Diff line number Diff line
@@ -733,6 +733,7 @@ reopen_success:

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

		if (tcon->unix_ext)
@@ -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;

	/*
@@ -2146,8 +2148,10 @@ retry:

		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;

@@ -2155,6 +2159,7 @@ retry:
						  &found_pages);
		if (!wdata) {
			rc = -ENOMEM;
			done = true;
			add_credits_and_wake_if(server, credits, 0);
			break;
		}
@@ -2183,7 +2188,7 @@ retry:
		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
@@ -2191,7 +2196,7 @@ retry:
				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);
@@ -2201,6 +2206,15 @@ retry:
			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;
@@ -2218,6 +2232,9 @@ retry:
		goto retry;
	}

	if (saved_rc != 0)
		rc = saved_rc;

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

@@ -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) {
+10 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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;