aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Shilovsky2019-01-29 16:40:28 -0800
committerSteve French2019-03-04 20:05:34 -0600
commit165df9a080b6863ae286fa01780c13d87cd81076 (patch)
treee8e6ac557b3f08c4e8f49cd82788bdbbf6a1d2ae
parent1c163f4c7b3f621efff9b28a47abb36f7378d783 (diff)
CIFS: Fix leaking locked VFS cache pages in writeback retry
If we don't find a writable file handle when retrying writepages we break of the loop and do not unlock and put pages neither from wdata2 nor from the original wdata. Fix this by walking through all the remaining pages and cleanup them properly. Cc: <stable@vger.kernel.org> Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/cifs/cifssmb.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index bb54ccf8481c..551924beb86f 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -2125,12 +2125,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
wdata2->cfile = find_writable_file(CIFS_I(inode), false);
if (!wdata2->cfile) {
- cifs_dbg(VFS, "No writable handles for inode\n");
+ cifs_dbg(VFS, "No writable handle to retry writepages\n");
rc = -EBADF;
- break;
+ } else {
+ wdata2->pid = wdata2->cfile->pid;
+ rc = server->ops->async_writev(wdata2,
+ cifs_writedata_release);
}
- wdata2->pid = wdata2->cfile->pid;
- rc = server->ops->async_writev(wdata2, cifs_writedata_release);
for (j = 0; j < nr_pages; j++) {
unlock_page(wdata2->pages[j]);
@@ -2145,6 +2146,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
kref_put(&wdata2->refcount, cifs_writedata_release);
if (is_retryable_error(rc))
continue;
+ i += nr_pages;
break;
}
@@ -2152,6 +2154,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
i += nr_pages;
} while (i < wdata->nr_pages);
+ /* cleanup remaining pages from the original wdata */
+ for (; i < wdata->nr_pages; i++) {
+ SetPageError(wdata->pages[i]);
+ end_page_writeback(wdata->pages[i]);
+ put_page(wdata->pages[i]);
+ }
+
if (rc != 0 && !is_retryable_error(rc))
mapping_set_error(inode->i_mapping, rc);
kref_put(&wdata->refcount, cifs_writedata_release);