[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: Jeff Layton <jlayton@redhat.com> Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
Родитель
26c8f0d601
Коммит
4a5c80d7b5
|
@ -323,7 +323,8 @@ struct smb_version_operations {
|
|||
/* async read from the server */
|
||||
int (*async_readv)(struct cifs_readdata *);
|
||||
/* 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 */
|
||||
int (*sync_read)(const unsigned int, struct cifsFileInfo *,
|
||||
struct cifs_io_parms *, unsigned int *, char **,
|
||||
|
|
|
@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount);
|
|||
int cifs_async_readv(struct cifs_readdata *rdata);
|
||||
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);
|
||||
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
|
||||
work_func_t complete);
|
||||
|
|
|
@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
|
|||
|
||||
do {
|
||||
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);
|
||||
|
||||
for (i = 0; i < wdata->nr_pages; i++) {
|
||||
|
@ -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 */
|
||||
int
|
||||
cifs_async_writev(struct cifs_writedata *wdata)
|
||||
cifs_async_writev(struct cifs_writedata *wdata,
|
||||
void (*release)(struct kref *kref))
|
||||
{
|
||||
int rc = -EACCES;
|
||||
WRITE_REQ *smb = NULL;
|
||||
|
@ -2099,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
|||
if (rc == 0)
|
||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
||||
else
|
||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||
kref_put(&wdata->refcount, release);
|
||||
|
||||
async_writev_out:
|
||||
cifs_small_buf_release(smb);
|
||||
|
|
|
@ -2043,7 +2043,8 @@ retry:
|
|||
}
|
||||
wdata->pid = wdata->cfile->pid;
|
||||
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);
|
||||
|
||||
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)
|
|||
}
|
||||
|
||||
static void
|
||||
cifs_uncached_writev_complete(struct work_struct *work)
|
||||
cifs_uncached_writedata_release(struct kref *refcount)
|
||||
{
|
||||
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, work);
|
||||
struct inode *inode = wdata->cfile->dentry->d_inode;
|
||||
|
@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work)
|
|||
|
||||
complete(&wdata->done);
|
||||
|
||||
if (wdata->result != -EAGAIN) {
|
||||
for (i = 0; i < wdata->nr_pages; i++)
|
||||
put_page(wdata->pages[i]);
|
||||
}
|
||||
|
||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||
}
|
||||
|
||||
/* attempt to send write to server, retry on any -EAGAIN errors */
|
||||
|
@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
|
|||
if (rc != 0)
|
||||
continue;
|
||||
}
|
||||
rc = server->ops->async_writev(wdata);
|
||||
rc = server->ops->async_writev(wdata,
|
||||
cifs_uncached_writedata_release);
|
||||
} while (rc == -EAGAIN);
|
||||
|
||||
return rc;
|
||||
|
@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
|
|||
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
|
||||
rc = cifs_uncached_retry_writev(wdata);
|
||||
if (rc) {
|
||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||
kref_put(&wdata->refcount,
|
||||
cifs_uncached_writedata_release);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2496,7 +2505,7 @@ restart_loop:
|
|||
}
|
||||
}
|
||||
list_del_init(&wdata->list);
|
||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||
}
|
||||
|
||||
if (total_written > 0)
|
||||
|
|
|
@ -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 */
|
||||
int
|
||||
smb2_async_writev(struct cifs_writedata *wdata)
|
||||
smb2_async_writev(struct cifs_writedata *wdata,
|
||||
void (*release)(struct kref *kref))
|
||||
{
|
||||
int rc = -EACCES;
|
||||
struct smb2_write_req *req = NULL;
|
||||
|
@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata)
|
|||
smb2_writev_callback, wdata, 0);
|
||||
|
||||
if (rc) {
|
||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||
kref_put(&wdata->refcount, release);
|
||||
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,
|
|||
extern int smb2_async_readv(struct cifs_readdata *rdata);
|
||||
extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||
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,
|
||||
unsigned int *nbytes, struct kvec *iov, int n_vec);
|
||||
extern int SMB2_echo(struct TCP_Server_Info *server);
|
||||
|
|
Загрузка…
Ссылка в новой задаче