diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
| -rw-r--r-- | fs/nfs/nfs4proc.c | 315 | 
1 files changed, 200 insertions, 115 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1406858bae6c..11eafcfc490b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1073,14 +1073,26 @@ static const struct rpc_call_ops nfs40_call_sync_ops = {  	.rpc_call_done = nfs40_call_sync_done,  }; +static int nfs4_call_sync_custom(struct rpc_task_setup *task_setup) +{ +	int ret; +	struct rpc_task *task; + +	task = rpc_run_task(task_setup); +	if (IS_ERR(task)) +		return PTR_ERR(task); + +	ret = task->tk_status; +	rpc_put_task(task); +	return ret; +} +  static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,  				   struct nfs_server *server,  				   struct rpc_message *msg,  				   struct nfs4_sequence_args *args,  				   struct nfs4_sequence_res *res)  { -	int ret; -	struct rpc_task *task;  	struct nfs_client *clp = server->nfs_client;  	struct nfs4_call_sync_data data = {  		.seq_server = server, @@ -1094,14 +1106,7 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,  		.callback_data = &data  	}; -	task = rpc_run_task(&task_setup); -	if (IS_ERR(task)) -		ret = PTR_ERR(task); -	else { -		ret = task->tk_status; -		rpc_put_task(task); -	} -	return ret; +	return nfs4_call_sync_custom(&task_setup);  }  int nfs4_call_sync(struct rpc_clnt *clnt, @@ -3308,6 +3313,75 @@ nfs4_wait_on_layoutreturn(struct inode *inode, struct rpc_task *task)  	return pnfs_wait_on_layoutreturn(inode, task);  } +/* + * Update the seqid of an open stateid + */ +static void nfs4_sync_open_stateid(nfs4_stateid *dst, +		struct nfs4_state *state) +{ +	__be32 seqid_open; +	u32 dst_seqid; +	int seq; + +	for (;;) { +		if (!nfs4_valid_open_stateid(state)) +			break; +		seq = read_seqbegin(&state->seqlock); +		if (!nfs4_state_match_open_stateid_other(state, dst)) { +			nfs4_stateid_copy(dst, &state->open_stateid); +			if (read_seqretry(&state->seqlock, seq)) +				continue; +			break; +		} +		seqid_open = state->open_stateid.seqid; +		if (read_seqretry(&state->seqlock, seq)) +			continue; + +		dst_seqid = be32_to_cpu(dst->seqid); +		if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) < 0) +			dst->seqid = seqid_open; +		break; +	} +} + +/* + * Update the seqid of an open stateid after receiving + * NFS4ERR_OLD_STATEID + */ +static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst, +		struct nfs4_state *state) +{ +	__be32 seqid_open; +	u32 dst_seqid; +	bool ret; +	int seq; + +	for (;;) { +		ret = false; +		if (!nfs4_valid_open_stateid(state)) +			break; +		seq = read_seqbegin(&state->seqlock); +		if (!nfs4_state_match_open_stateid_other(state, dst)) { +			if (read_seqretry(&state->seqlock, seq)) +				continue; +			break; +		} +		seqid_open = state->open_stateid.seqid; +		if (read_seqretry(&state->seqlock, seq)) +			continue; + +		dst_seqid = be32_to_cpu(dst->seqid); +		if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) >= 0) +			dst->seqid = cpu_to_be32(dst_seqid + 1); +		else +			dst->seqid = seqid_open; +		ret = true; +		break; +	} + +	return ret; +} +  struct nfs4_closedata {  	struct inode *inode;  	struct nfs4_state *state; @@ -3358,32 +3432,11 @@ static void nfs4_close_done(struct rpc_task *task, void *data)  	trace_nfs4_close(state, &calldata->arg, &calldata->res, task->tk_status);  	/* Handle Layoutreturn errors */ -	if (calldata->arg.lr_args && task->tk_status != 0) { -		switch (calldata->res.lr_ret) { -		default: -			calldata->res.lr_ret = -NFS4ERR_NOMATCHING_LAYOUT; -			break; -		case 0: -			calldata->arg.lr_args = NULL; -			calldata->res.lr_res = NULL; -			break; -		case -NFS4ERR_OLD_STATEID: -			if (nfs4_layoutreturn_refresh_stateid(&calldata->arg.lr_args->stateid, -						&calldata->arg.lr_args->range, -						calldata->inode)) -				goto lr_restart; -			/* Fallthrough */ -		case -NFS4ERR_ADMIN_REVOKED: -		case -NFS4ERR_DELEG_REVOKED: -		case -NFS4ERR_EXPIRED: -		case -NFS4ERR_BAD_STATEID: -		case -NFS4ERR_UNKNOWN_LAYOUTTYPE: -		case -NFS4ERR_WRONG_CRED: -			calldata->arg.lr_args = NULL; -			calldata->res.lr_res = NULL; -			goto lr_restart; -		} -	} +	if (pnfs_roc_done(task, calldata->inode, +				&calldata->arg.lr_args, +				&calldata->res.lr_res, +				&calldata->res.lr_ret) == -EAGAIN) +		goto out_restart;  	/* hmm. we are done with the inode, and in the process of freeing  	 * the state_owner. we keep this around to process errors @@ -3403,7 +3456,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)  			break;  		case -NFS4ERR_OLD_STATEID:  			/* Did we race with OPEN? */ -			if (nfs4_refresh_open_stateid(&calldata->arg.stateid, +			if (nfs4_refresh_open_old_stateid(&calldata->arg.stateid,  						state))  				goto out_restart;  			goto out_release; @@ -3415,7 +3468,9 @@ static void nfs4_close_done(struct rpc_task *task, void *data)  					task->tk_msg.rpc_cred);  			/* Fallthrough */  		case -NFS4ERR_BAD_STATEID: -			break; +			if (calldata->arg.fmode == 0) +				break; +			/* Fallthrough */  		default:  			task->tk_status = nfs4_async_handle_exception(task,  					server, task->tk_status, &exception); @@ -3430,8 +3485,6 @@ out_release:  	nfs_refresh_inode(calldata->inode, &calldata->fattr);  	dprintk("%s: done, ret = %d!\n", __func__, task->tk_status);  	return; -lr_restart: -	calldata->res.lr_ret = 0;  out_restart:  	task->tk_status = 0;  	rpc_restart_call_prepare(task); @@ -3472,8 +3525,8 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)  	} else if (is_rdwr)  		calldata->arg.fmode |= FMODE_READ|FMODE_WRITE; -	if (!nfs4_valid_open_stateid(state) || -	    !nfs4_refresh_open_stateid(&calldata->arg.stateid, state)) +	nfs4_sync_open_stateid(&calldata->arg.stateid, state); +	if (!nfs4_valid_open_stateid(state))  		call_close = 0;  	spin_unlock(&state->owner->so_lock); @@ -6018,7 +6071,6 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,  		.rpc_resp = res,  		.rpc_cred = cred,  	}; -	struct rpc_task *task;  	struct rpc_task_setup task_setup_data = {  		.rpc_client = clp->cl_rpcclient,  		.rpc_message = &msg, @@ -6051,17 +6103,12 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,  	dprintk("NFS call  setclientid auth=%s, '%s'\n",  		clp->cl_rpcclient->cl_auth->au_ops->au_name,  		clp->cl_owner_id); -	task = rpc_run_task(&task_setup_data); -	if (IS_ERR(task)) { -		status = PTR_ERR(task); -		goto out; -	} -	status = task->tk_status; + +	status = nfs4_call_sync_custom(&task_setup_data);  	if (setclientid.sc_cred) {  		clp->cl_acceptor = rpcauth_stringify_acceptor(setclientid.sc_cred);  		put_rpccred(setclientid.sc_cred);  	} -	rpc_put_task(task);  out:  	trace_nfs4_setclientid(clp, status);  	dprintk("NFS reply setclientid: %d\n", status); @@ -6129,32 +6176,11 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  	trace_nfs4_delegreturn_exit(&data->args, &data->res, task->tk_status);  	/* Handle Layoutreturn errors */ -	if (data->args.lr_args && task->tk_status != 0) { -		switch(data->res.lr_ret) { -		default: -			data->res.lr_ret = -NFS4ERR_NOMATCHING_LAYOUT; -			break; -		case 0: -			data->args.lr_args = NULL; -			data->res.lr_res = NULL; -			break; -		case -NFS4ERR_OLD_STATEID: -			if (nfs4_layoutreturn_refresh_stateid(&data->args.lr_args->stateid, -						&data->args.lr_args->range, -						data->inode)) -				goto lr_restart; -			/* Fallthrough */ -		case -NFS4ERR_ADMIN_REVOKED: -		case -NFS4ERR_DELEG_REVOKED: -		case -NFS4ERR_EXPIRED: -		case -NFS4ERR_BAD_STATEID: -		case -NFS4ERR_UNKNOWN_LAYOUTTYPE: -		case -NFS4ERR_WRONG_CRED: -			data->args.lr_args = NULL; -			data->res.lr_res = NULL; -			goto lr_restart; -		} -	} +	if (pnfs_roc_done(task, data->inode, +				&data->args.lr_args, +				&data->res.lr_res, +				&data->res.lr_ret) == -EAGAIN) +		goto out_restart;  	switch (task->tk_status) {  	case 0: @@ -6192,8 +6218,6 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  	}  	data->rpc_status = task->tk_status;  	return; -lr_restart: -	data->res.lr_ret = 0;  out_restart:  	task->tk_status = 0;  	rpc_restart_call_prepare(task); @@ -6386,6 +6410,42 @@ static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *  	return err;  } +/* + * Update the seqid of a lock stateid after receiving + * NFS4ERR_OLD_STATEID + */ +static bool nfs4_refresh_lock_old_stateid(nfs4_stateid *dst, +		struct nfs4_lock_state *lsp) +{ +	struct nfs4_state *state = lsp->ls_state; +	bool ret = false; + +	spin_lock(&state->state_lock); +	if (!nfs4_stateid_match_other(dst, &lsp->ls_stateid)) +		goto out; +	if (!nfs4_stateid_is_newer(&lsp->ls_stateid, dst)) +		nfs4_stateid_seqid_inc(dst); +	else +		dst->seqid = lsp->ls_stateid.seqid; +	ret = true; +out: +	spin_unlock(&state->state_lock); +	return ret; +} + +static bool nfs4_sync_lock_stateid(nfs4_stateid *dst, +		struct nfs4_lock_state *lsp) +{ +	struct nfs4_state *state = lsp->ls_state; +	bool ret; + +	spin_lock(&state->state_lock); +	ret = !nfs4_stateid_match_other(dst, &lsp->ls_stateid); +	nfs4_stateid_copy(dst, &lsp->ls_stateid); +	spin_unlock(&state->state_lock); +	return ret; +} +  struct nfs4_unlockdata {  	struct nfs_locku_args arg;  	struct nfs_locku_res res; @@ -6403,7 +6463,8 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,  		struct nfs_seqid *seqid)  {  	struct nfs4_unlockdata *p; -	struct inode *inode = lsp->ls_state->inode; +	struct nfs4_state *state = lsp->ls_state; +	struct inode *inode = state->inode;  	p = kzalloc(sizeof(*p), GFP_NOFS);  	if (p == NULL) @@ -6419,6 +6480,9 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,  	locks_init_lock(&p->fl);  	locks_copy_lock(&p->fl, fl);  	p->server = NFS_SERVER(inode); +	spin_lock(&state->state_lock); +	nfs4_stateid_copy(&p->arg.stateid, &lsp->ls_stateid); +	spin_unlock(&state->state_lock);  	return p;  } @@ -6457,10 +6521,14 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)  					task->tk_msg.rpc_cred);  			/* Fall through */  		case -NFS4ERR_BAD_STATEID: -		case -NFS4ERR_OLD_STATEID:  		case -NFS4ERR_STALE_STATEID: -			if (!nfs4_stateid_match(&calldata->arg.stateid, -						&calldata->lsp->ls_stateid)) +			if (nfs4_sync_lock_stateid(&calldata->arg.stateid, +						calldata->lsp)) +				rpc_restart_call_prepare(task); +			break; +		case -NFS4ERR_OLD_STATEID: +			if (nfs4_refresh_lock_old_stateid(&calldata->arg.stateid, +						calldata->lsp))  				rpc_restart_call_prepare(task);  			break;  		default: @@ -6483,7 +6551,6 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)  	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)  		goto out_wait; -	nfs4_stateid_copy(&calldata->arg.stateid, &calldata->lsp->ls_stateid);  	if (test_bit(NFS_LOCK_INITIALIZED, &calldata->lsp->ls_flags) == 0) {  		/* Note: exit _without_ running nfs4_locku_done */  		goto out_no_action; @@ -7645,6 +7712,8 @@ int nfs4_proc_fsid_present(struct inode *inode, const struct cred *cred)  static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors, bool use_integrity)  {  	int status; +	struct rpc_clnt *clnt = NFS_SERVER(dir)->client; +	struct nfs_client *clp = NFS_SERVER(dir)->nfs_client;  	struct nfs4_secinfo_arg args = {  		.dir_fh = NFS_FH(dir),  		.name   = name, @@ -7657,26 +7726,37 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct  		.rpc_argp = &args,  		.rpc_resp = &res,  	}; -	struct rpc_clnt *clnt = NFS_SERVER(dir)->client; +	struct nfs4_call_sync_data data = { +		.seq_server = NFS_SERVER(dir), +		.seq_args = &args.seq_args, +		.seq_res = &res.seq_res, +	}; +	struct rpc_task_setup task_setup = { +		.rpc_client = clnt, +		.rpc_message = &msg, +		.callback_ops = clp->cl_mvops->call_sync_ops, +		.callback_data = &data, +		.flags = RPC_TASK_NO_ROUND_ROBIN, +	};  	const struct cred *cred = NULL;  	if (use_integrity) { -		clnt = NFS_SERVER(dir)->nfs_client->cl_rpcclient; -		cred = nfs4_get_clid_cred(NFS_SERVER(dir)->nfs_client); +		clnt = clp->cl_rpcclient; +		task_setup.rpc_client = clnt; + +		cred = nfs4_get_clid_cred(clp);  		msg.rpc_cred = cred;  	}  	dprintk("NFS call  secinfo %s\n", name->name); -	nfs4_state_protect(NFS_SERVER(dir)->nfs_client, -		NFS_SP4_MACH_CRED_SECINFO, &clnt, &msg); +	nfs4_state_protect(clp, NFS_SP4_MACH_CRED_SECINFO, &clnt, &msg); +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); +	status = nfs4_call_sync_custom(&task_setup); -	status = nfs4_call_sync(clnt, NFS_SERVER(dir), &msg, &args.seq_args, -				&res.seq_res, RPC_TASK_NO_ROUND_ROBIN);  	dprintk("NFS reply  secinfo: %d\n", status);  	put_cred(cred); -  	return status;  } @@ -8344,7 +8424,6 @@ static const struct rpc_call_ops nfs4_get_lease_time_ops = {  int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)  { -	struct rpc_task *task;  	struct nfs4_get_lease_time_args args;  	struct nfs4_get_lease_time_res res = {  		.lr_fsinfo = fsinfo, @@ -8366,17 +8445,9 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)  		.callback_data = &data,  		.flags = RPC_TASK_TIMEOUT,  	}; -	int status;  	nfs4_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0, 1); -	task = rpc_run_task(&task_setup); - -	if (IS_ERR(task)) -		return PTR_ERR(task); - -	status = task->tk_status; -	rpc_put_task(task); -	return status; +	return nfs4_call_sync_custom(&task_setup);  }  #ifdef CONFIG_NFS_V4_1 @@ -8845,7 +8916,6 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp,  		const struct cred *cred)  {  	struct nfs4_reclaim_complete_data *calldata; -	struct rpc_task *task;  	struct rpc_message msg = {  		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],  		.rpc_cred = cred, @@ -8854,7 +8924,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp,  		.rpc_client = clp->cl_rpcclient,  		.rpc_message = &msg,  		.callback_ops = &nfs4_reclaim_complete_call_ops, -		.flags = RPC_TASK_ASYNC | RPC_TASK_NO_ROUND_ROBIN, +		.flags = RPC_TASK_NO_ROUND_ROBIN,  	};  	int status = -ENOMEM; @@ -8869,15 +8939,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp,  	msg.rpc_argp = &calldata->arg;  	msg.rpc_resp = &calldata->res;  	task_setup_data.callback_data = calldata; -	task = rpc_run_task(&task_setup_data); -	if (IS_ERR(task)) { -		status = PTR_ERR(task); -		goto out; -	} -	status = rpc_wait_for_completion_task(task); -	if (status == 0) -		status = task->tk_status; -	rpc_put_task(task); +	status = nfs4_call_sync_custom(&task_setup_data);  out:  	dprintk("<-- %s status=%d\n", __func__, status);  	return status; @@ -9103,10 +9165,19 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)  	if (!nfs41_sequence_process(task, &lrp->res.seq_res))  		return; +	/* +	 * Was there an RPC level error? Assume the call succeeded, +	 * and that we need to release the layout +	 */ +	if (task->tk_rpc_status != 0 && RPC_WAS_SENT(task)) { +		lrp->res.lrs_present = 0; +		return; +	} +  	server = NFS_SERVER(lrp->args.inode);  	switch (task->tk_status) {  	case -NFS4ERR_OLD_STATEID: -		if (nfs4_layoutreturn_refresh_stateid(&lrp->args.stateid, +		if (nfs4_layout_refresh_old_stateid(&lrp->args.stateid,  					&lrp->args.range,  					lrp->args.inode))  			goto out_restart; @@ -9362,18 +9433,32 @@ _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,  		.rpc_resp = &res,  	};  	struct rpc_clnt *clnt = server->client; +	struct nfs4_call_sync_data data = { +		.seq_server = server, +		.seq_args = &args.seq_args, +		.seq_res = &res.seq_res, +	}; +	struct rpc_task_setup task_setup = { +		.rpc_client = server->client, +		.rpc_message = &msg, +		.callback_ops = server->nfs_client->cl_mvops->call_sync_ops, +		.callback_data = &data, +		.flags = RPC_TASK_NO_ROUND_ROBIN, +	};  	const struct cred *cred = NULL;  	int status;  	if (use_integrity) {  		clnt = server->nfs_client->cl_rpcclient; +		task_setup.rpc_client = clnt; +  		cred = nfs4_get_clid_cred(server->nfs_client);  		msg.rpc_cred = cred;  	}  	dprintk("--> %s\n", __func__); -	status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, -				&res.seq_res, RPC_TASK_NO_ROUND_ROBIN); +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0); +	status = nfs4_call_sync_custom(&task_setup);  	dprintk("<-- %s status=%d\n", __func__, status);  	put_cred(cred);  |