Commit 515dcdcd authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFS: nfsiod should not block forever in mempool_alloc()



The concern is that since nfsiod is sometimes required to kick off a
commit, it can get locked up waiting forever in mempool_alloc() instead
of failing gracefully and leaving the commit until later.

Try to allocate from the slab first, with GFP_KERNEL | __GFP_NORETRY,
then fall back to a non-blocking attempt to allocate from the memory
pool.

Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent b2648015
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -587,6 +587,13 @@ nfs_write_match_verf(const struct nfs_writeverf *verf,
		!nfs_write_verifier_cmp(&req->wb_verf, &verf->verifier);
}

static inline gfp_t nfs_io_gfp_mask(void)
{
	if (current->flags & PF_WQ_WORKER)
		return GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
	return GFP_KERNEL;
}

/* unlink.c */
extern struct rpc_task *
nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
+6 −2
Original line number Diff line number Diff line
@@ -419,7 +419,7 @@ static struct nfs_commit_data *
pnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket *bucket,
			     struct nfs_commit_info *cinfo)
{
	struct nfs_commit_data *data = nfs_commitdata_alloc(false);
	struct nfs_commit_data *data = nfs_commitdata_alloc();

	if (!data)
		return NULL;
@@ -515,7 +515,11 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
	unsigned int nreq = 0;

	if (!list_empty(mds_pages)) {
		data = nfs_commitdata_alloc(true);
		data = nfs_commitdata_alloc();
		if (!data) {
			nfs_retry_commit(mds_pages, NULL, cinfo, -1);
			return -ENOMEM;
		}
		data->ds_commit_index = -1;
		list_splice_init(mds_pages, &data->pages);
		list_add_tail(&data->list, &list);
+9 −15
Original line number Diff line number Diff line
@@ -70,27 +70,17 @@ static mempool_t *nfs_wdata_mempool;
static struct kmem_cache *nfs_cdata_cachep;
static mempool_t *nfs_commit_mempool;

struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail)
struct nfs_commit_data *nfs_commitdata_alloc(void)
{
	struct nfs_commit_data *p;

	if (never_fail)
		p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
	else {
		/* It is OK to do some reclaim, not no safe to wait
		 * for anything to be returned to the pool.
		 * mempool_alloc() cannot handle that particular combination,
		 * so we need two separate attempts.
		 */
	p = kmem_cache_zalloc(nfs_cdata_cachep, nfs_io_gfp_mask());
	if (!p) {
		p = mempool_alloc(nfs_commit_mempool, GFP_NOWAIT);
		if (!p)
			p = kmem_cache_alloc(nfs_cdata_cachep, GFP_NOIO |
					     __GFP_NOWARN | __GFP_NORETRY);
		if (!p)
			return NULL;
	}

		memset(p, 0, sizeof(*p));
	}
	INIT_LIST_HEAD(&p->pages);
	return p;
}
@@ -1826,7 +1816,11 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
	if (list_empty(head))
		return 0;

	data = nfs_commitdata_alloc(true);
	data = nfs_commitdata_alloc();
	if (!data) {
		nfs_retry_commit(head, NULL, cinfo, -1);
		return -ENOMEM;
	}

	/* Set up the argument struct */
	nfs_init_commit(data, head, NULL, cinfo);
+1 −1
Original line number Diff line number Diff line
@@ -580,7 +580,7 @@ extern int nfs_wb_all(struct inode *inode);
extern int nfs_wb_page(struct inode *inode, struct page *page);
extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
extern int  nfs_commit_inode(struct inode *, int);
extern struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail);
extern struct nfs_commit_data *nfs_commitdata_alloc(void);
extern void nfs_commit_free(struct nfs_commit_data *data);
bool nfs_commit_end(struct nfs_mds_commit_info *cinfo);