提交 4a5c80d7 编写于 作者: S Steve French

[CIFS] clean up page array when uncached write send fails

In the event that a send fails in an uncached write, or we end up
needing to reissue it (-EAGAIN case), we'll kfree the wdata but
the pages currently leak.

Fix this by adding a new kref release routine for uncached writedata
that releases the pages, and have the uncached codepaths use that.

[original patch by Jeff modified to fix minor formatting problems]
Signed-off-by: NJeff Layton <jlayton@redhat.com>
Reviewed-by: NPavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: NSteve French <smfrench@gmail.com>
上级 26c8f0d6
...@@ -323,7 +323,8 @@ struct smb_version_operations { ...@@ -323,7 +323,8 @@ struct smb_version_operations {
/* async read from the server */ /* async read from the server */
int (*async_readv)(struct cifs_readdata *); int (*async_readv)(struct cifs_readdata *);
/* async write to the server */ /* async write to the server */
int (*async_writev)(struct cifs_writedata *); int (*async_writev)(struct cifs_writedata *,
void (*release)(struct kref *));
/* sync read from the server */ /* sync read from the server */
int (*sync_read)(const unsigned int, struct cifsFileInfo *, int (*sync_read)(const unsigned int, struct cifsFileInfo *,
struct cifs_io_parms *, unsigned int *, char **, struct cifs_io_parms *, unsigned int *, char **,
......
...@@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount); ...@@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount);
int cifs_async_readv(struct cifs_readdata *rdata); int cifs_async_readv(struct cifs_readdata *rdata);
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
int cifs_async_writev(struct cifs_writedata *wdata); int cifs_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref));
void cifs_writev_complete(struct work_struct *work); void cifs_writev_complete(struct work_struct *work);
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
work_func_t complete); work_func_t complete);
......
...@@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata) ...@@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
do { do {
server = tlink_tcon(wdata->cfile->tlink)->ses->server; server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata); rc = server->ops->async_writev(wdata, cifs_writedata_release);
} while (rc == -EAGAIN); } while (rc == -EAGAIN);
for (i = 0; i < wdata->nr_pages; i++) { for (i = 0; i < wdata->nr_pages; i++) {
...@@ -2025,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid) ...@@ -2025,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid)
/* cifs_async_writev - send an async write, and set up mid to handle result */ /* cifs_async_writev - send an async write, and set up mid to handle result */
int int
cifs_async_writev(struct cifs_writedata *wdata) cifs_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref))
{ {
int rc = -EACCES; int rc = -EACCES;
WRITE_REQ *smb = NULL; WRITE_REQ *smb = NULL;
...@@ -2099,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata) ...@@ -2099,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
if (rc == 0) if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
else else
kref_put(&wdata->refcount, cifs_writedata_release); kref_put(&wdata->refcount, release);
async_writev_out: async_writev_out:
cifs_small_buf_release(smb); cifs_small_buf_release(smb);
......
...@@ -2043,7 +2043,8 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -2043,7 +2043,8 @@ static int cifs_writepages(struct address_space *mapping,
} }
wdata->pid = wdata->cfile->pid; wdata->pid = wdata->cfile->pid;
server = tlink_tcon(wdata->cfile->tlink)->ses->server; server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata); rc = server->ops->async_writev(wdata,
cifs_writedata_release);
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
for (i = 0; i < nr_pages; ++i) for (i = 0; i < nr_pages; ++i)
...@@ -2331,9 +2332,20 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) ...@@ -2331,9 +2332,20 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
} }
static void static void
cifs_uncached_writev_complete(struct work_struct *work) cifs_uncached_writedata_release(struct kref *refcount)
{ {
int i; int i;
struct cifs_writedata *wdata = container_of(refcount,
struct cifs_writedata, refcount);
for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
cifs_writedata_release(refcount);
}
static void
cifs_uncached_writev_complete(struct work_struct *work)
{
struct cifs_writedata *wdata = container_of(work, struct cifs_writedata *wdata = container_of(work,
struct cifs_writedata, work); struct cifs_writedata, work);
struct inode *inode = wdata->cfile->dentry->d_inode; struct inode *inode = wdata->cfile->dentry->d_inode;
...@@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work) ...@@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work)
complete(&wdata->done); complete(&wdata->done);
if (wdata->result != -EAGAIN) { kref_put(&wdata->refcount, cifs_uncached_writedata_release);
for (i = 0; i < wdata->nr_pages; i++)
put_page(wdata->pages[i]);
}
kref_put(&wdata->refcount, cifs_writedata_release);
} }
/* attempt to send write to server, retry on any -EAGAIN errors */ /* attempt to send write to server, retry on any -EAGAIN errors */
...@@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata) ...@@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
if (rc != 0) if (rc != 0)
continue; continue;
} }
rc = server->ops->async_writev(wdata); rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
} while (rc == -EAGAIN); } while (rc == -EAGAIN);
return rc; return rc;
...@@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, ...@@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
rc = cifs_uncached_retry_writev(wdata); rc = cifs_uncached_retry_writev(wdata);
if (rc) { if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release); kref_put(&wdata->refcount,
cifs_uncached_writedata_release);
break; break;
} }
...@@ -2496,7 +2505,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, ...@@ -2496,7 +2505,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
} }
} }
list_del_init(&wdata->list); list_del_init(&wdata->list);
kref_put(&wdata->refcount, cifs_writedata_release); kref_put(&wdata->refcount, cifs_uncached_writedata_release);
} }
if (total_written > 0) if (total_written > 0)
......
...@@ -1890,7 +1890,8 @@ smb2_writev_callback(struct mid_q_entry *mid) ...@@ -1890,7 +1890,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
/* smb2_async_writev - send an async write, and set up mid to handle result */ /* smb2_async_writev - send an async write, and set up mid to handle result */
int int
smb2_async_writev(struct cifs_writedata *wdata) smb2_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref))
{ {
int rc = -EACCES; int rc = -EACCES;
struct smb2_write_req *req = NULL; struct smb2_write_req *req = NULL;
...@@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata) ...@@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata)
smb2_writev_callback, wdata, 0); smb2_writev_callback, wdata, 0);
if (rc) { if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release); kref_put(&wdata->refcount, release);
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
} }
......
...@@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
extern int smb2_async_readv(struct cifs_readdata *rdata); extern int smb2_async_readv(struct cifs_readdata *rdata);
extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, char **buf, int *buf_type); unsigned int *nbytes, char **buf, int *buf_type);
extern int smb2_async_writev(struct cifs_writedata *wdata); extern int smb2_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref));
extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, struct kvec *iov, int n_vec); unsigned int *nbytes, struct kvec *iov, int n_vec);
extern int SMB2_echo(struct TCP_Server_Info *server); extern int SMB2_echo(struct TCP_Server_Info *server);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册