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

Commit 75f26df6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable fixes:
   - Fix a regression in the SunRPC socket polling code
   - Fix the attribute cache revalidation code
   - Fix race in __update_open_stateid()
   - Fix an lo->plh_block_lgets imbalance in layoutreturn
   - Fix an Oopsable typo in ff_mirror_match_fh()

  Features:
   - pNFS layout recall performance improvements.
   - pNFS/flexfiles: Support server-supplied layoutstats sampling period

  Bugfixes + cleanups:
   - NFSv4: Don't perform cached access checks before we've OPENed the
     file
   - Fix starvation issues with background flushes
   - Reclaim writes should be flushed as unstable writes if there are
     already entries in the commit lists
   - Various bugfixes from Chuck to fix NFS/RDMA send queue ordering
     problems
   - Ensure that we propagate fatal layoutget errors back to the
     application
   - Fixes for sundry flexfiles layoutstats bugs
   - Fix files/flexfiles to not cache invalidated layouts in the DS
     commit buckets"

* tag 'nfs-for-4.5-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (68 commits)
  NFS: Fix a compile warning about unused variable in nfs_generic_pg_pgios()
  NFSv4: Fix a compile warning about no prototype for nfs4_ioctl()
  NFS: Use wait_on_atomic_t() for unlock after readahead
  SUNRPC: Fixup socket wait for memory
  NFSv4.1/pNFS: Cleanup constify struct pnfs_layout_range arguments
  NFSv4.1/pnfs: Cleanup copying of pnfs_layout_range structures
  NFSv4.1/pNFS: Cleanup pnfs_mark_matching_lsegs_invalid()
  NFSv4.1/pNFS: Fix a race in initiate_file_draining()
  NFSv4.1/pNFS: pnfs_error_mark_layout_for_return() must always return layout
  NFSv4.1/pNFS: pnfs_mark_matching_lsegs_return() should set the iomode
  NFSv4.1/pNFS: Use nfs4_stateid_copy for copying stateids
  NFSv4.1/pNFS: Don't pass stateids by value to pnfs_send_layoutreturn()
  NFS: Relax requirements in nfs_flush_incompatible
  NFSv4.1/pNFS: Don't queue up a new commit if the layout segment is invalid
  NFS: Allow multiple commit requests in flight per file
  NFS/pNFS: Fix up pNFS write reschedule layering violations and bugs
  SUNRPC: Fix a missing break in rpc_anyaddr()
  pNFS/flexfiles: Fix an Oopsable typo in ff_mirror_match_fh()
  NFS: Fix attribute cache revalidation
  NFS: Ensure we revalidate attributes before using execute_ok()
  ...
parents 63f729cb 44aab3e0
Loading
Loading
Loading
Loading
+45 −7
Original line number Original line Diff line number Diff line
@@ -83,8 +83,11 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,


	res = htonl(NFS4ERR_BADHANDLE);
	res = htonl(NFS4ERR_BADHANDLE);
	inode = nfs_delegation_find_inode(cps->clp, &args->fh);
	inode = nfs_delegation_find_inode(cps->clp, &args->fh);
	if (inode == NULL)
	if (inode == NULL) {
		trace_nfs4_cb_recall(cps->clp, &args->fh, NULL,
				&args->stateid, -ntohl(res));
		goto out;
		goto out;
	}
	/* Set up a helper thread to actually return the delegation */
	/* Set up a helper thread to actually return the delegation */
	switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
	switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
	case 0:
	case 0:
@@ -96,7 +99,8 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
	default:
	default:
		res = htonl(NFS4ERR_RESOURCE);
		res = htonl(NFS4ERR_RESOURCE);
	}
	}
	trace_nfs4_recall_delegation(inode, -ntohl(res));
	trace_nfs4_cb_recall(cps->clp, &args->fh, inode,
			&args->stateid, -ntohl(res));
	iput(inode);
	iput(inode);
out:
out:
	dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
	dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
@@ -160,6 +164,22 @@ static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
	return lo;
	return lo;
}
}


/*
 * Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing)
 */
static bool pnfs_check_stateid_sequence(struct pnfs_layout_hdr *lo,
					const nfs4_stateid *new)
{
	u32 oldseq, newseq;

	oldseq = be32_to_cpu(lo->plh_stateid.seqid);
	newseq = be32_to_cpu(new->seqid);

	if (newseq > oldseq + 1)
		return false;
	return true;
}

static u32 initiate_file_draining(struct nfs_client *clp,
static u32 initiate_file_draining(struct nfs_client *clp,
				  struct cb_layoutrecallargs *args)
				  struct cb_layoutrecallargs *args)
{
{
@@ -169,34 +189,52 @@ static u32 initiate_file_draining(struct nfs_client *clp,
	LIST_HEAD(free_me_list);
	LIST_HEAD(free_me_list);


	lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid);
	lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid);
	if (!lo)
	if (!lo) {
		trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
				&args->cbl_stateid, -rv);
		goto out;
		goto out;
	}


	ino = lo->plh_inode;
	ino = lo->plh_inode;


	spin_lock(&ino->i_lock);
	spin_lock(&ino->i_lock);
	if (!pnfs_check_stateid_sequence(lo, &args->cbl_stateid)) {
		rv = NFS4ERR_DELAY;
		goto unlock;
	}
	pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
	pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
	spin_unlock(&ino->i_lock);
	spin_unlock(&ino->i_lock);


	pnfs_layoutcommit_inode(ino, false);
	pnfs_layoutcommit_inode(ino, false);


	spin_lock(&ino->i_lock);
	spin_lock(&ino->i_lock);
	if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
	/*
	    pnfs_mark_matching_lsegs_invalid(lo, &free_me_list,
	 * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
					&args->cbl_range)) {
	 */
	if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
		rv = NFS4ERR_DELAY;
		rv = NFS4ERR_DELAY;
		goto unlock;
		goto unlock;
	}
	}


	if (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
					&args->cbl_range)) {
		rv = NFS4_OK;
		goto unlock;
	}

	if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
	if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
		NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
		NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
			&args->cbl_range);
			&args->cbl_range);
	}
	}
	pnfs_mark_layout_returned_if_empty(lo);
unlock:
unlock:
	spin_unlock(&ino->i_lock);
	spin_unlock(&ino->i_lock);
	pnfs_free_lseg_list(&free_me_list);
	pnfs_free_lseg_list(&free_me_list);
	/* Free all lsegs that are attached to commit buckets */
	nfs_commit_inode(ino, 0);
	pnfs_put_layout_hdr(lo);
	pnfs_put_layout_hdr(lo);
	trace_nfs4_cb_layoutrecall_inode(clp, &args->cbl_fh, ino, -rv);
	trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
			&args->cbl_stateid, -rv);
	iput(ino);
	iput(ino);
out:
out:
	return rv;
	return rv;
+19 −2
Original line number Original line Diff line number Diff line
@@ -2431,6 +2431,20 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
}
}
EXPORT_SYMBOL_GPL(nfs_may_open);
EXPORT_SYMBOL_GPL(nfs_may_open);


static int nfs_execute_ok(struct inode *inode, int mask)
{
	struct nfs_server *server = NFS_SERVER(inode);
	int ret;

	if (mask & MAY_NOT_BLOCK)
		ret = nfs_revalidate_inode_rcu(server, inode);
	else
		ret = nfs_revalidate_inode(server, inode);
	if (ret == 0 && !execute_ok(inode))
		ret = -EACCES;
	return ret;
}

int nfs_permission(struct inode *inode, int mask)
int nfs_permission(struct inode *inode, int mask)
{
{
	struct rpc_cred *cred;
	struct rpc_cred *cred;
@@ -2448,6 +2462,9 @@ int nfs_permission(struct inode *inode, int mask)
		case S_IFLNK:
		case S_IFLNK:
			goto out;
			goto out;
		case S_IFREG:
		case S_IFREG:
			if ((mask & MAY_OPEN) &&
			   nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN))
				return 0;
			break;
			break;
		case S_IFDIR:
		case S_IFDIR:
			/*
			/*
@@ -2480,8 +2497,8 @@ int nfs_permission(struct inode *inode, int mask)
			res = PTR_ERR(cred);
			res = PTR_ERR(cred);
	}
	}
out:
out:
	if (!res && (mask & MAY_EXEC) && !execute_ok(inode))
	if (!res && (mask & MAY_EXEC))
		res = -EACCES;
		res = nfs_execute_ok(inode, mask);


	dfprintk(VFS, "NFS: permission(%s/%lu), mask=0x%x, res=%d\n",
	dfprintk(VFS, "NFS: permission(%s/%lu), mask=0x%x, res=%d\n",
		inode->i_sb->s_id, inode->i_ino, mask, res);
		inode->i_sb->s_id, inode->i_ino, mask, res);
+38 −10
Original line number Original line Diff line number Diff line
@@ -117,12 +117,6 @@ static inline int put_dreq(struct nfs_direct_req *dreq)
	return atomic_dec_and_test(&dreq->io_count);
	return atomic_dec_and_test(&dreq->io_count);
}
}


void nfs_direct_set_resched_writes(struct nfs_direct_req *dreq)
{
	dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
}
EXPORT_SYMBOL_GPL(nfs_direct_set_resched_writes);

static void
static void
nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr)
nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr)
{
{
@@ -670,6 +664,10 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)


	req = nfs_list_entry(reqs.next);
	req = nfs_list_entry(reqs.next);
	nfs_direct_setup_mirroring(dreq, &desc, req);
	nfs_direct_setup_mirroring(dreq, &desc, req);
	if (desc.pg_error < 0) {
		list_splice_init(&reqs, &failed);
		goto out_failed;
	}


	list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
	list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
		if (!nfs_pageio_add_request(&desc, req)) {
		if (!nfs_pageio_add_request(&desc, req)) {
@@ -677,6 +675,9 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
			nfs_list_add_request(req, &failed);
			nfs_list_add_request(req, &failed);
			spin_lock(cinfo.lock);
			spin_lock(cinfo.lock);
			dreq->flags = 0;
			dreq->flags = 0;
			if (desc.pg_error < 0)
				dreq->error = desc.pg_error;
			else
				dreq->error = -EIO;
				dreq->error = -EIO;
			spin_unlock(cinfo.lock);
			spin_unlock(cinfo.lock);
		}
		}
@@ -684,6 +685,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
	}
	}
	nfs_pageio_complete(&desc);
	nfs_pageio_complete(&desc);


out_failed:
	while (!list_empty(&failed)) {
	while (!list_empty(&failed)) {
		req = nfs_list_entry(failed.next);
		req = nfs_list_entry(failed.next);
		nfs_list_remove_request(req);
		nfs_list_remove_request(req);
@@ -727,14 +729,20 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
		nfs_direct_write_complete(dreq, data->inode);
		nfs_direct_write_complete(dreq, data->inode);
}
}


static void nfs_direct_error_cleanup(struct nfs_inode *nfsi)
static void nfs_direct_resched_write(struct nfs_commit_info *cinfo,
		struct nfs_page *req)
{
{
	/* There is no lock to clear */
	struct nfs_direct_req *dreq = cinfo->dreq;

	spin_lock(&dreq->lock);
	dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
	spin_unlock(&dreq->lock);
	nfs_mark_request_commit(req, NULL, cinfo, 0);
}
}


static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops = {
static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops = {
	.completion = nfs_direct_commit_complete,
	.completion = nfs_direct_commit_complete,
	.error_cleanup = nfs_direct_error_cleanup,
	.resched_write = nfs_direct_resched_write,
};
};


static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
@@ -839,10 +847,25 @@ static void nfs_write_sync_pgio_error(struct list_head *head)
	}
	}
}
}


static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr)
{
	struct nfs_direct_req *dreq = hdr->dreq;

	spin_lock(&dreq->lock);
	if (dreq->error == 0) {
		dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
		/* fake unstable write to let common nfs resend pages */
		hdr->verf.committed = NFS_UNSTABLE;
		hdr->good_bytes = hdr->args.count;
	}
	spin_unlock(&dreq->lock);
}

static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
	.error_cleanup = nfs_write_sync_pgio_error,
	.error_cleanup = nfs_write_sync_pgio_error,
	.init_hdr = nfs_direct_pgio_init,
	.init_hdr = nfs_direct_pgio_init,
	.completion = nfs_direct_write_completion,
	.completion = nfs_direct_write_completion,
	.reschedule_io = nfs_direct_write_reschedule_io,
};
};




@@ -900,6 +923,11 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
			}
			}


			nfs_direct_setup_mirroring(dreq, &desc, req);
			nfs_direct_setup_mirroring(dreq, &desc, req);
			if (desc.pg_error < 0) {
				nfs_free_request(req);
				result = desc.pg_error;
				break;
			}


			nfs_lock_request(req);
			nfs_lock_request(req);
			req->wb_index = pos >> PAGE_SHIFT;
			req->wb_index = pos >> PAGE_SHIFT;
+3 −3
Original line number Original line Diff line number Diff line
@@ -514,7 +514,7 @@ static void nfs_check_dirty_writeback(struct page *page,
	 * so it will not block due to pages that will shortly be freeable.
	 * so it will not block due to pages that will shortly be freeable.
	 */
	 */
	nfsi = NFS_I(mapping->host);
	nfsi = NFS_I(mapping->host);
	if (test_bit(NFS_INO_COMMIT, &nfsi->flags)) {
	if (atomic_read(&nfsi->commit_info.rpcs_out)) {
		*writeback = true;
		*writeback = true;
		return;
		return;
	}
	}
@@ -545,7 +545,7 @@ static int nfs_launder_page(struct page *page)
		inode->i_ino, (long long)page_offset(page));
		inode->i_ino, (long long)page_offset(page));


	nfs_fscache_wait_on_page_write(nfsi, page);
	nfs_fscache_wait_on_page_write(nfsi, page);
	return nfs_wb_page(inode, page);
	return nfs_wb_launder_page(inode, page);
}
}


static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
@@ -756,7 +756,7 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)


	l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
	l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
	if (!IS_ERR(l_ctx)) {
	if (!IS_ERR(l_ctx)) {
		status = nfs_iocounter_wait(&l_ctx->io_count);
		status = nfs_iocounter_wait(l_ctx);
		nfs_put_lock_context(l_ctx);
		nfs_put_lock_context(l_ctx);
		if (status < 0)
		if (status < 0)
			return status;
			return status;
+16 −2
Original line number Original line Diff line number Diff line
@@ -202,6 +202,7 @@ static int filelayout_async_handle_error(struct rpc_task *task,
			task->tk_status);
			task->tk_status);
		nfs4_mark_deviceid_unavailable(devid);
		nfs4_mark_deviceid_unavailable(devid);
		pnfs_error_mark_layout_for_return(inode, lseg);
		pnfs_error_mark_layout_for_return(inode, lseg);
		pnfs_set_lo_fail(lseg);
		rpc_wake_up(&tbl->slot_tbl_waitq);
		rpc_wake_up(&tbl->slot_tbl_waitq);
		/* fall through */
		/* fall through */
	default:
	default:
@@ -883,13 +884,19 @@ static void
filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,
filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,
			struct nfs_page *req)
			struct nfs_page *req)
{
{
	if (!pgio->pg_lseg)
	if (!pgio->pg_lseg) {
		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
					   req->wb_context,
					   req->wb_context,
					   0,
					   0,
					   NFS4_MAX_UINT64,
					   NFS4_MAX_UINT64,
					   IOMODE_READ,
					   IOMODE_READ,
					   GFP_KERNEL);
					   GFP_KERNEL);
		if (IS_ERR(pgio->pg_lseg)) {
			pgio->pg_error = PTR_ERR(pgio->pg_lseg);
			pgio->pg_lseg = NULL;
			return;
		}
	}
	/* If no lseg, fall back to read through mds */
	/* If no lseg, fall back to read through mds */
	if (pgio->pg_lseg == NULL)
	if (pgio->pg_lseg == NULL)
		nfs_pageio_reset_read_mds(pgio);
		nfs_pageio_reset_read_mds(pgio);
@@ -902,13 +909,20 @@ filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,
	struct nfs_commit_info cinfo;
	struct nfs_commit_info cinfo;
	int status;
	int status;


	if (!pgio->pg_lseg)
	if (!pgio->pg_lseg) {
		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
					   req->wb_context,
					   req->wb_context,
					   0,
					   0,
					   NFS4_MAX_UINT64,
					   NFS4_MAX_UINT64,
					   IOMODE_RW,
					   IOMODE_RW,
					   GFP_NOFS);
					   GFP_NOFS);
		if (IS_ERR(pgio->pg_lseg)) {
			pgio->pg_error = PTR_ERR(pgio->pg_lseg);
			pgio->pg_lseg = NULL;
			return;
		}
	}

	/* If no lseg, fall back to write through mds */
	/* If no lseg, fall back to write through mds */
	if (pgio->pg_lseg == NULL)
	if (pgio->pg_lseg == NULL)
		goto out_mds;
		goto out_mds;
Loading