diff options
author | Steve French <smfrench@gmail.com> | 2014-02-07 21:45:12 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2014-02-07 21:47:00 -0500 |
commit | 4a5c80d7b5615be8098f9d5da97d166afc318abc (patch) | |
tree | ff3e40a816442722056805cc0cc1844051a30988 /fs | |
parent | 26c8f0d601f5d4c0d9f4bc8c5151539aae5dc26a (diff) |
[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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 7 | ||||
-rw-r--r-- | fs/cifs/file.c | 31 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 5 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 3 |
6 files changed, 33 insertions, 19 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d6a031ed391b..86dc28c7aa5c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -323,7 +323,8 @@ struct smb_version_operations { | |||
323 | /* async read from the server */ | 323 | /* async read from the server */ |
324 | int (*async_readv)(struct cifs_readdata *); | 324 | int (*async_readv)(struct cifs_readdata *); |
325 | /* async write to the server */ | 325 | /* async write to the server */ |
326 | int (*async_writev)(struct cifs_writedata *); | 326 | int (*async_writev)(struct cifs_writedata *, |
327 | void (*release)(struct kref *)); | ||
327 | /* sync read from the server */ | 328 | /* sync read from the server */ |
328 | int (*sync_read)(const unsigned int, struct cifsFileInfo *, | 329 | int (*sync_read)(const unsigned int, struct cifsFileInfo *, |
329 | struct cifs_io_parms *, unsigned int *, char **, | 330 | struct cifs_io_parms *, unsigned int *, char **, |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 79e6e9a93a8c..d00e09dfc452 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount); | |||
488 | int cifs_async_readv(struct cifs_readdata *rdata); | 488 | int cifs_async_readv(struct cifs_readdata *rdata); |
489 | int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); | 489 | int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); |
490 | 490 | ||
491 | int cifs_async_writev(struct cifs_writedata *wdata); | 491 | int cifs_async_writev(struct cifs_writedata *wdata, |
492 | void (*release)(struct kref *kref)); | ||
492 | void cifs_writev_complete(struct work_struct *work); | 493 | void cifs_writev_complete(struct work_struct *work); |
493 | struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, | 494 | struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, |
494 | work_func_t complete); | 495 | work_func_t complete); |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 0cd742ccb019..f3264bd7a83d 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata) | |||
1910 | 1910 | ||
1911 | do { | 1911 | do { |
1912 | server = tlink_tcon(wdata->cfile->tlink)->ses->server; | 1912 | server = tlink_tcon(wdata->cfile->tlink)->ses->server; |
1913 | rc = server->ops->async_writev(wdata); | 1913 | rc = server->ops->async_writev(wdata, cifs_writedata_release); |
1914 | } while (rc == -EAGAIN); | 1914 | } while (rc == -EAGAIN); |
1915 | 1915 | ||
1916 | for (i = 0; i < wdata->nr_pages; i++) { | 1916 | for (i = 0; i < wdata->nr_pages; i++) { |
@@ -2025,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid) | |||
2025 | 2025 | ||
2026 | /* cifs_async_writev - send an async write, and set up mid to handle result */ | 2026 | /* cifs_async_writev - send an async write, and set up mid to handle result */ |
2027 | int | 2027 | int |
2028 | cifs_async_writev(struct cifs_writedata *wdata) | 2028 | cifs_async_writev(struct cifs_writedata *wdata, |
2029 | void (*release)(struct kref *kref)) | ||
2029 | { | 2030 | { |
2030 | int rc = -EACCES; | 2031 | int rc = -EACCES; |
2031 | WRITE_REQ *smb = NULL; | 2032 | WRITE_REQ *smb = NULL; |
@@ -2099,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata) | |||
2099 | if (rc == 0) | 2100 | if (rc == 0) |
2100 | cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); | 2101 | cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); |
2101 | else | 2102 | else |
2102 | kref_put(&wdata->refcount, cifs_writedata_release); | 2103 | kref_put(&wdata->refcount, release); |
2103 | 2104 | ||
2104 | async_writev_out: | 2105 | async_writev_out: |
2105 | cifs_small_buf_release(smb); | 2106 | cifs_small_buf_release(smb); |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 853d6d1cc822..a301edbdad4a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -2043,7 +2043,8 @@ retry: | |||
2043 | } | 2043 | } |
2044 | wdata->pid = wdata->cfile->pid; | 2044 | wdata->pid = wdata->cfile->pid; |
2045 | server = tlink_tcon(wdata->cfile->tlink)->ses->server; | 2045 | server = tlink_tcon(wdata->cfile->tlink)->ses->server; |
2046 | rc = server->ops->async_writev(wdata); | 2046 | rc = server->ops->async_writev(wdata, |
2047 | cifs_writedata_release); | ||
2047 | } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); | 2048 | } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN); |
2048 | 2049 | ||
2049 | for (i = 0; i < nr_pages; ++i) | 2050 | 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 | } | 2332 | } |
2332 | 2333 | ||
2333 | static void | 2334 | static void |
2334 | cifs_uncached_writev_complete(struct work_struct *work) | 2335 | cifs_uncached_writedata_release(struct kref *refcount) |
2335 | { | 2336 | { |
2336 | int i; | 2337 | int i; |
2338 | struct cifs_writedata *wdata = container_of(refcount, | ||
2339 | struct cifs_writedata, refcount); | ||
2340 | |||
2341 | for (i = 0; i < wdata->nr_pages; i++) | ||
2342 | put_page(wdata->pages[i]); | ||
2343 | cifs_writedata_release(refcount); | ||
2344 | } | ||
2345 | |||
2346 | static void | ||
2347 | cifs_uncached_writev_complete(struct work_struct *work) | ||
2348 | { | ||
2337 | struct cifs_writedata *wdata = container_of(work, | 2349 | struct cifs_writedata *wdata = container_of(work, |
2338 | struct cifs_writedata, work); | 2350 | struct cifs_writedata, work); |
2339 | struct inode *inode = wdata->cfile->dentry->d_inode; | 2351 | struct inode *inode = wdata->cfile->dentry->d_inode; |
@@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work) | |||
2347 | 2359 | ||
2348 | complete(&wdata->done); | 2360 | complete(&wdata->done); |
2349 | 2361 | ||
2350 | if (wdata->result != -EAGAIN) { | 2362 | kref_put(&wdata->refcount, cifs_uncached_writedata_release); |
2351 | for (i = 0; i < wdata->nr_pages; i++) | ||
2352 | put_page(wdata->pages[i]); | ||
2353 | } | ||
2354 | |||
2355 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
2356 | } | 2363 | } |
2357 | 2364 | ||
2358 | /* attempt to send write to server, retry on any -EAGAIN errors */ | 2365 | /* attempt to send write to server, retry on any -EAGAIN errors */ |
@@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata) | |||
2370 | if (rc != 0) | 2377 | if (rc != 0) |
2371 | continue; | 2378 | continue; |
2372 | } | 2379 | } |
2373 | rc = server->ops->async_writev(wdata); | 2380 | rc = server->ops->async_writev(wdata, |
2381 | cifs_uncached_writedata_release); | ||
2374 | } while (rc == -EAGAIN); | 2382 | } while (rc == -EAGAIN); |
2375 | 2383 | ||
2376 | return rc; | 2384 | return rc; |
@@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, | |||
2454 | wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); | 2462 | wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); |
2455 | rc = cifs_uncached_retry_writev(wdata); | 2463 | rc = cifs_uncached_retry_writev(wdata); |
2456 | if (rc) { | 2464 | if (rc) { |
2457 | kref_put(&wdata->refcount, cifs_writedata_release); | 2465 | kref_put(&wdata->refcount, |
2466 | cifs_uncached_writedata_release); | ||
2458 | break; | 2467 | break; |
2459 | } | 2468 | } |
2460 | 2469 | ||
@@ -2496,7 +2505,7 @@ restart_loop: | |||
2496 | } | 2505 | } |
2497 | } | 2506 | } |
2498 | list_del_init(&wdata->list); | 2507 | list_del_init(&wdata->list); |
2499 | kref_put(&wdata->refcount, cifs_writedata_release); | 2508 | kref_put(&wdata->refcount, cifs_uncached_writedata_release); |
2500 | } | 2509 | } |
2501 | 2510 | ||
2502 | if (total_written > 0) | 2511 | if (total_written > 0) |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2013234b73ad..a3f7a9c3cc69 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -1890,7 +1890,8 @@ smb2_writev_callback(struct mid_q_entry *mid) | |||
1890 | 1890 | ||
1891 | /* smb2_async_writev - send an async write, and set up mid to handle result */ | 1891 | /* smb2_async_writev - send an async write, and set up mid to handle result */ |
1892 | int | 1892 | int |
1893 | smb2_async_writev(struct cifs_writedata *wdata) | 1893 | smb2_async_writev(struct cifs_writedata *wdata, |
1894 | void (*release)(struct kref *kref)) | ||
1894 | { | 1895 | { |
1895 | int rc = -EACCES; | 1896 | int rc = -EACCES; |
1896 | struct smb2_write_req *req = NULL; | 1897 | struct smb2_write_req *req = NULL; |
@@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata) | |||
1938 | smb2_writev_callback, wdata, 0); | 1939 | smb2_writev_callback, wdata, 0); |
1939 | 1940 | ||
1940 | if (rc) { | 1941 | if (rc) { |
1941 | kref_put(&wdata->refcount, cifs_writedata_release); | 1942 | kref_put(&wdata->refcount, release); |
1942 | cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); | 1943 | cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); |
1943 | } | 1944 | } |
1944 | 1945 | ||
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 93adc64666f3..0ce48db20a65 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, | |||
123 | extern int smb2_async_readv(struct cifs_readdata *rdata); | 123 | extern int smb2_async_readv(struct cifs_readdata *rdata); |
124 | extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, | 124 | extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, |
125 | unsigned int *nbytes, char **buf, int *buf_type); | 125 | unsigned int *nbytes, char **buf, int *buf_type); |
126 | extern int smb2_async_writev(struct cifs_writedata *wdata); | 126 | extern int smb2_async_writev(struct cifs_writedata *wdata, |
127 | void (*release)(struct kref *kref)); | ||
127 | extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, | 128 | extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, |
128 | unsigned int *nbytes, struct kvec *iov, int n_vec); | 129 | unsigned int *nbytes, struct kvec *iov, int n_vec); |
129 | extern int SMB2_echo(struct TCP_Server_Info *server); | 130 | extern int SMB2_echo(struct TCP_Server_Info *server); |