Commit 67435d2d authored by Mike Snitzer's avatar Mike Snitzer Committed by Anna Schumaker
Browse files

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages



LOCALIO is an NFS loopback mount optimization that avoids using the
network for READ, WRITE and COMMIT if the NFS client and server are
determined to be on the same system. But because LOCALIO is still
fundamentally "just NFS loopback mount" it is susceptible to recursion
deadlock via direct reclaim, e.g.: NFS LOCALIO down to XFS and then
back into NFS via nfs_writepages.

Fix LOCALIO's potential for direct reclaim deadlock by ensuring that
all its page cache allocations are done from GFP_NOFS context.

Thanks to Ben Coddington for pointing out commit ad22c7a0 ("xfs:
prevent stack overflows from page cache allocation").

Reported-by: default avatarJohn Cagle <john.cagle@hammerspace.com>
Tested-by: default avatarAllen Lu <allen.lu@hammerspace.com>
Suggested-by: default avatarBenjamin Coddington <bcodding@hammerspace.com>
Fixes: 70ba381e ("nfs: add LOCALIO support")
Signed-off-by: default avatarMike Snitzer <snitzer@hammerspace.com>
Signed-off-by: default avatarAnna Schumaker <anna.schumaker@oracle.com>
parent 5fcd9583
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
}
EXPORT_SYMBOL_GPL(nfs_local_open_fh);

/*
 * Ensure all page cache allocations are done from GFP_NOFS context to
 * prevent direct reclaim recursion back into NFS via nfs_writepages.
 */
static void
nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
{
	gfp_t gfp_mask = mapping_gfp_mask(m);

	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
}

static void
nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
{
@@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
		return NULL;
	}

	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
	init_sync_kiocb(&iocb->kiocb, file);

	iocb->hdr = hdr;
@@ -1000,6 +1013,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
			end = LLONG_MAX;
	}

	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);

	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
	return vfs_fsync_range(filp, start, end, 0);
}