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

Commit 919e3bd9 authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker
Browse files

NFS: Ensure we commit after writeback is complete



If the page cache is being flushed, then we want to ensure that we
do start a commit once the pages are done being flushed.
If we just wait until all I/O is done to that file, we can end up
livelocking until the balance_dirty_pages() mechanism puts its
foot down and forces I/O to stop.
So instead we do more or less the same thing that O_DIRECT does,
and set up a counter to tell us when the flush is done,

Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent b5973a8c
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -50,6 +50,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
	hdr->cred = hdr->req->wb_context->cred;
	hdr->cred = hdr->req->wb_context->cred;
	hdr->io_start = req_offset(hdr->req);
	hdr->io_start = req_offset(hdr->req);
	hdr->good_bytes = mirror->pg_count;
	hdr->good_bytes = mirror->pg_count;
	hdr->io_completion = desc->pg_io_completion;
	hdr->dreq = desc->pg_dreq;
	hdr->dreq = desc->pg_dreq;
	hdr->release = release;
	hdr->release = release;
	hdr->completion_ops = desc->pg_completion_ops;
	hdr->completion_ops = desc->pg_completion_ops;
@@ -709,6 +710,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
	desc->pg_ioflags = io_flags;
	desc->pg_ioflags = io_flags;
	desc->pg_error = 0;
	desc->pg_error = 0;
	desc->pg_lseg = NULL;
	desc->pg_lseg = NULL;
	desc->pg_io_completion = NULL;
	desc->pg_dreq = NULL;
	desc->pg_dreq = NULL;
	desc->pg_bsize = bsize;
	desc->pg_bsize = bsize;


@@ -1231,6 +1233,7 @@ int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
{
{
	LIST_HEAD(failed);
	LIST_HEAD(failed);


	desc->pg_io_completion = hdr->io_completion;
	desc->pg_dreq = hdr->dreq;
	desc->pg_dreq = hdr->dreq;
	while (!list_empty(&hdr->pages)) {
	while (!list_empty(&hdr->pages)) {
		struct nfs_page *req = nfs_list_entry(hdr->pages.next);
		struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+57 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,12 @@
#define MIN_POOL_WRITE		(32)
#define MIN_POOL_WRITE		(32)
#define MIN_POOL_COMMIT		(4)
#define MIN_POOL_COMMIT		(4)


struct nfs_io_completion {
	void (*complete)(void *data);
	void *data;
	struct kref refcount;
};

/*
/*
 * Local function declarations
 * Local function declarations
 */
 */
@@ -108,6 +114,39 @@ static void nfs_writehdr_free(struct nfs_pgio_header *hdr)
	mempool_free(hdr, nfs_wdata_mempool);
	mempool_free(hdr, nfs_wdata_mempool);
}
}


static struct nfs_io_completion *nfs_io_completion_alloc(gfp_t gfp_flags)
{
	return kmalloc(sizeof(struct nfs_io_completion), gfp_flags);
}

static void nfs_io_completion_init(struct nfs_io_completion *ioc,
		void (*complete)(void *), void *data)
{
	ioc->complete = complete;
	ioc->data = data;
	kref_init(&ioc->refcount);
}

static void nfs_io_completion_release(struct kref *kref)
{
	struct nfs_io_completion *ioc = container_of(kref,
			struct nfs_io_completion, refcount);
	ioc->complete(ioc->data);
	kfree(ioc);
}

static void nfs_io_completion_get(struct nfs_io_completion *ioc)
{
	if (ioc != NULL)
		kref_get(&ioc->refcount);
}

static void nfs_io_completion_put(struct nfs_io_completion *ioc)
{
	if (ioc != NULL)
		kref_put(&ioc->refcount, nfs_io_completion_release);
}

static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
{
{
	ctx->error = error;
	ctx->error = error;
@@ -681,18 +720,29 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *
	return ret;
	return ret;
}
}


static void nfs_io_completion_commit(void *inode)
{
	nfs_commit_inode(inode, 0);
}

int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
{
{
	struct inode *inode = mapping->host;
	struct inode *inode = mapping->host;
	struct nfs_pageio_descriptor pgio;
	struct nfs_pageio_descriptor pgio;
	struct nfs_io_completion *ioc = nfs_io_completion_alloc(GFP_NOFS);
	int err;
	int err;


	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);


	if (ioc)
		nfs_io_completion_init(ioc, nfs_io_completion_commit, inode);

	nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false,
	nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false,
				&nfs_async_write_completion_ops);
				&nfs_async_write_completion_ops);
	pgio.pg_io_completion = ioc;
	err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
	err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
	nfs_pageio_complete(&pgio);
	nfs_pageio_complete(&pgio);
	nfs_io_completion_put(ioc);


	if (err < 0)
	if (err < 0)
		goto out_err;
		goto out_err;
@@ -940,6 +990,11 @@ int nfs_write_need_commit(struct nfs_pgio_header *hdr)
	return hdr->verf.committed != NFS_FILE_SYNC;
	return hdr->verf.committed != NFS_FILE_SYNC;
}
}


static void nfs_async_write_init(struct nfs_pgio_header *hdr)
{
	nfs_io_completion_get(hdr->io_completion);
}

static void nfs_write_completion(struct nfs_pgio_header *hdr)
static void nfs_write_completion(struct nfs_pgio_header *hdr)
{
{
	struct nfs_commit_info cinfo;
	struct nfs_commit_info cinfo;
@@ -973,6 +1028,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
		nfs_release_request(req);
		nfs_release_request(req);
	}
	}
out:
out:
	nfs_io_completion_put(hdr->io_completion);
	hdr->release(hdr);
	hdr->release(hdr);
}
}


@@ -1378,6 +1434,7 @@ static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
}
}


static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
	.init_hdr = nfs_async_write_init,
	.error_cleanup = nfs_async_write_error,
	.error_cleanup = nfs_async_write_error,
	.completion = nfs_write_completion,
	.completion = nfs_write_completion,
	.reschedule_io = nfs_async_write_reschedule_io,
	.reschedule_io = nfs_async_write_reschedule_io,
+1 −0
Original line number Original line Diff line number Diff line
@@ -93,6 +93,7 @@ struct nfs_pageio_descriptor {
	const struct rpc_call_ops *pg_rpc_callops;
	const struct rpc_call_ops *pg_rpc_callops;
	const struct nfs_pgio_completion_ops *pg_completion_ops;
	const struct nfs_pgio_completion_ops *pg_completion_ops;
	struct pnfs_layout_segment *pg_lseg;
	struct pnfs_layout_segment *pg_lseg;
	struct nfs_io_completion *pg_io_completion;
	struct nfs_direct_req	*pg_dreq;
	struct nfs_direct_req	*pg_dreq;
	unsigned int		pg_bsize;	/* default bsize for mirrors */
	unsigned int		pg_bsize;	/* default bsize for mirrors */


+2 −0
Original line number Original line Diff line number Diff line
@@ -1422,6 +1422,7 @@ enum {
	NFS_IOHDR_STAT,
	NFS_IOHDR_STAT,
};
};


struct nfs_io_completion;
struct nfs_pgio_header {
struct nfs_pgio_header {
	struct inode		*inode;
	struct inode		*inode;
	struct rpc_cred		*cred;
	struct rpc_cred		*cred;
@@ -1435,6 +1436,7 @@ struct nfs_pgio_header {
	void (*release) (struct nfs_pgio_header *hdr);
	void (*release) (struct nfs_pgio_header *hdr);
	const struct nfs_pgio_completion_ops *completion_ops;
	const struct nfs_pgio_completion_ops *completion_ops;
	const struct nfs_rw_ops	*rw_ops;
	const struct nfs_rw_ops	*rw_ops;
	struct nfs_io_completion *io_completion;
	struct nfs_direct_req	*dreq;
	struct nfs_direct_req	*dreq;
	spinlock_t		lock;
	spinlock_t		lock;
	/* fields protected by lock */
	/* fields protected by lock */