Skip to content

Commit 4a5c80d

Browse files
committed
[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 <[email protected]> Reviewed-by: Pavel Shilovsky <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 26c8f0d commit 4a5c80d

File tree

6 files changed

+33
-19
lines changed

6 files changed

+33
-19
lines changed

fs/cifs/cifsglob.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ struct smb_version_operations {
323323
/* async read from the server */
324324
int (*async_readv)(struct cifs_readdata *);
325325
/* async write to the server */
326-
int (*async_writev)(struct cifs_writedata *);
326+
int (*async_writev)(struct cifs_writedata *,
327+
void (*release)(struct kref *));
327328
/* sync read from the server */
328329
int (*sync_read)(const unsigned int, struct cifsFileInfo *,
329330
struct cifs_io_parms *, unsigned int *, char **,

fs/cifs/cifsproto.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,8 @@ void cifs_readdata_release(struct kref *refcount);
488488
int cifs_async_readv(struct cifs_readdata *rdata);
489489
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
490490

491-
int cifs_async_writev(struct cifs_writedata *wdata);
491+
int cifs_async_writev(struct cifs_writedata *wdata,
492+
void (*release)(struct kref *kref));
492493
void cifs_writev_complete(struct work_struct *work);
493494
struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
494495
work_func_t complete);

fs/cifs/cifssmb.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
19101910

19111911
do {
19121912
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);
19141914
} while (rc == -EAGAIN);
19151915

19161916
for (i = 0; i < wdata->nr_pages; i++) {
@@ -2025,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid)
20252025

20262026
/* cifs_async_writev - send an async write, and set up mid to handle result */
20272027
int
2028-
cifs_async_writev(struct cifs_writedata *wdata)
2028+
cifs_async_writev(struct cifs_writedata *wdata,
2029+
void (*release)(struct kref *kref))
20292030
{
20302031
int rc = -EACCES;
20312032
WRITE_REQ *smb = NULL;
@@ -2099,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)
20992100
if (rc == 0)
21002101
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
21012102
else
2102-
kref_put(&wdata->refcount, cifs_writedata_release);
2103+
kref_put(&wdata->refcount, release);
21032104

21042105
async_writev_out:
21052106
cifs_small_buf_release(smb);

fs/cifs/file.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,7 +2043,8 @@ static int cifs_writepages(struct address_space *mapping,
20432043
}
20442044
wdata->pid = wdata->cfile->pid;
20452045
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);
20472048
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
20482049

20492050
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)
23312332
}
23322333

23332334
static void
2334-
cifs_uncached_writev_complete(struct work_struct *work)
2335+
cifs_uncached_writedata_release(struct kref *refcount)
23352336
{
23362337
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+
{
23372349
struct cifs_writedata *wdata = container_of(work,
23382350
struct cifs_writedata, work);
23392351
struct inode *inode = wdata->cfile->dentry->d_inode;
@@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work)
23472359

23482360
complete(&wdata->done);
23492361

2350-
if (wdata->result != -EAGAIN) {
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);
2362+
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
23562363
}
23572364

23582365
/* attempt to send write to server, retry on any -EAGAIN errors */
@@ -2370,7 +2377,8 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
23702377
if (rc != 0)
23712378
continue;
23722379
}
2373-
rc = server->ops->async_writev(wdata);
2380+
rc = server->ops->async_writev(wdata,
2381+
cifs_uncached_writedata_release);
23742382
} while (rc == -EAGAIN);
23752383

23762384
return rc;
@@ -2454,7 +2462,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
24542462
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
24552463
rc = cifs_uncached_retry_writev(wdata);
24562464
if (rc) {
2457-
kref_put(&wdata->refcount, cifs_writedata_release);
2465+
kref_put(&wdata->refcount,
2466+
cifs_uncached_writedata_release);
24582467
break;
24592468
}
24602469

@@ -2496,7 +2505,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
24962505
}
24972506
}
24982507
list_del_init(&wdata->list);
2499-
kref_put(&wdata->refcount, cifs_writedata_release);
2508+
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
25002509
}
25012510

25022511
if (total_written > 0)

fs/cifs/smb2pdu.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
18901890

18911891
/* smb2_async_writev - send an async write, and set up mid to handle result */
18921892
int
1893-
smb2_async_writev(struct cifs_writedata *wdata)
1893+
smb2_async_writev(struct cifs_writedata *wdata,
1894+
void (*release)(struct kref *kref))
18941895
{
18951896
int rc = -EACCES;
18961897
struct smb2_write_req *req = NULL;
@@ -1938,7 +1939,7 @@ smb2_async_writev(struct cifs_writedata *wdata)
19381939
smb2_writev_callback, wdata, 0);
19391940

19401941
if (rc) {
1941-
kref_put(&wdata->refcount, cifs_writedata_release);
1942+
kref_put(&wdata->refcount, release);
19421943
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
19431944
}
19441945

fs/cifs/smb2proto.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
123123
extern int smb2_async_readv(struct cifs_readdata *rdata);
124124
extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
125125
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));
127128
extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
128129
unsigned int *nbytes, struct kvec *iov, int n_vec);
129130
extern int SMB2_echo(struct TCP_Server_Info *server);

0 commit comments

Comments
 (0)