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: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
b5973a8c1c
commit
919e3bd9a8
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue