Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a5c80d7 authored by Steve French's avatar Steve French
Browse files

[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: default avatarJeff Layton <jlayton@redhat.com>
Reviewed-by: default avatarPavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent 26c8f0d6
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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 **,
+2 −1
Original line number Diff line number Diff line
@@ -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);
+4 −3
Original line number Diff line number Diff line
@@ -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);
+20 −11
Original line number Diff line number Diff line
@@ -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)
+3 −2
Original line number Diff line number Diff line
@@ -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);
	}

Loading