]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
CIFS: Fix leaking locked VFS cache pages in writeback retry
authorPavel Shilovsky <pshilov@microsoft.com>
Wed, 30 Jan 2019 00:40:28 +0000 (16:40 -0800)
committerKhalid Elmously <khalid.elmously@canonical.com>
Thu, 26 Sep 2019 04:34:52 +0000 (00:34 -0400)
BugLink: https://bugs.launchpad.net/bugs/1844558
[ Upstream commit 165df9a080b6863ae286fa01780c13d87cd81076 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
fs/cifs/cifssmb.c

index a85c3a8dbadbc4b12e92e891a83f99501059b2f2..4491482f091cd625a2ab528846885a6d4c5cdb0c 100644 (file)
@@ -2007,12 +2007,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]);
@@ -2027,6 +2028,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;
                }
 
@@ -2034,6 +2036,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);