Message ID | 1391788961-26028-1-git-send-email-jlayton@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
2014-02-07 20:02 GMT+04:00 Jeff Layton <jlayton@redhat.com>: > 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. > > Signed-off-by: Jeff Layton <jlayton@redhat.com> > --- > fs/cifs/cifsglob.h | 2 +- > fs/cifs/cifsproto.h | 2 +- > fs/cifs/cifssmb.c | 6 +++--- > fs/cifs/file.c | 28 +++++++++++++++++----------- > fs/cifs/smb2pdu.c | 4 ++-- > fs/cifs/smb2proto.h | 2 +- > 6 files changed, 25 insertions(+), 19 deletions(-) > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index a245d1809ed8..57d07e704d97 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -323,7 +323,7 @@ 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 **, > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h > index 79e6e9a93a8c..a4f90c0d9950 100644 > --- a/fs/cifs/cifsproto.h > +++ b/fs/cifs/cifsproto.h > @@ -488,7 +488,7 @@ 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); > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > index 4d881c35eeca..0726ab413b8c 100644 > --- a/fs/cifs/cifssmb.c > +++ b/fs/cifs/cifssmb.c > @@ -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++) { > @@ -2031,7 +2031,7 @@ 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; > @@ -2105,7 +2105,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); > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index 853d6d1cc822..03d1f454c713 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -2043,7 +2043,7 @@ 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 +2331,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 +2358,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 +2376,7 @@ 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 +2460,7 @@ 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 +2502,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) > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index 2013234b73ad..8c04bdeda136 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -1890,7 +1890,7 @@ 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 +1938,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); > } > > diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h > index 93adc64666f3..1833724542cd 100644 > --- a/fs/cifs/smb2proto.h > +++ b/fs/cifs/smb2proto.h > @@ -123,7 +123,7 @@ 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); > -- > 1.8.5.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
merged into cifs-2.6.git for-next after minor formatting changes (80 column checkpatch warnings) On Fri, Feb 7, 2014 at 10:26 AM, Pavel Shilovsky <piastryyy@gmail.com> wrote: > 2014-02-07 20:02 GMT+04:00 Jeff Layton <jlayton@redhat.com>: >> 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. >> >> Signed-off-by: Jeff Layton <jlayton@redhat.com> >> --- >> fs/cifs/cifsglob.h | 2 +- >> fs/cifs/cifsproto.h | 2 +- >> fs/cifs/cifssmb.c | 6 +++--- >> fs/cifs/file.c | 28 +++++++++++++++++----------- >> fs/cifs/smb2pdu.c | 4 ++-- >> fs/cifs/smb2proto.h | 2 +- >> 6 files changed, 25 insertions(+), 19 deletions(-) >> >> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h >> index a245d1809ed8..57d07e704d97 100644 >> --- a/fs/cifs/cifsglob.h >> +++ b/fs/cifs/cifsglob.h >> @@ -323,7 +323,7 @@ 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 **, >> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h >> index 79e6e9a93a8c..a4f90c0d9950 100644 >> --- a/fs/cifs/cifsproto.h >> +++ b/fs/cifs/cifsproto.h >> @@ -488,7 +488,7 @@ 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); >> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c >> index 4d881c35eeca..0726ab413b8c 100644 >> --- a/fs/cifs/cifssmb.c >> +++ b/fs/cifs/cifssmb.c >> @@ -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++) { >> @@ -2031,7 +2031,7 @@ 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; >> @@ -2105,7 +2105,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); >> diff --git a/fs/cifs/file.c b/fs/cifs/file.c >> index 853d6d1cc822..03d1f454c713 100644 >> --- a/fs/cifs/file.c >> +++ b/fs/cifs/file.c >> @@ -2043,7 +2043,7 @@ 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 +2331,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 +2358,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 +2376,7 @@ 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 +2460,7 @@ 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 +2502,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) >> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c >> index 2013234b73ad..8c04bdeda136 100644 >> --- a/fs/cifs/smb2pdu.c >> +++ b/fs/cifs/smb2pdu.c >> @@ -1890,7 +1890,7 @@ 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 +1938,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); >> } >> >> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h >> index 93adc64666f3..1833724542cd 100644 >> --- a/fs/cifs/smb2proto.h >> +++ b/fs/cifs/smb2proto.h >> @@ -123,7 +123,7 @@ 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); >> -- >> 1.8.5.3 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru> > > -- > Best regards, > Pavel Shilovsky.
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a245d1809ed8..57d07e704d97 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -323,7 +323,7 @@ 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 **, diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 79e6e9a93a8c..a4f90c0d9950 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -488,7 +488,7 @@ 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); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 4d881c35eeca..0726ab413b8c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -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++) { @@ -2031,7 +2031,7 @@ 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; @@ -2105,7 +2105,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); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 853d6d1cc822..03d1f454c713 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2043,7 +2043,7 @@ 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 +2331,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 +2358,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 +2376,7 @@ 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 +2460,7 @@ 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 +2502,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) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2013234b73ad..8c04bdeda136 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1890,7 +1890,7 @@ 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 +1938,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); } diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 93adc64666f3..1833724542cd 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -123,7 +123,7 @@ 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);
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. Signed-off-by: Jeff Layton <jlayton@redhat.com> --- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 6 +++--- fs/cifs/file.c | 28 +++++++++++++++++----------- fs/cifs/smb2pdu.c | 4 ++-- fs/cifs/smb2proto.h | 2 +- 6 files changed, 25 insertions(+), 19 deletions(-)