diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
| -rw-r--r-- | fs/nfs/nfs4proc.c | 142 | 
1 files changed, 95 insertions, 47 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 693b903b48bd..5133bb18830e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1127,6 +1127,21 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)  	return ret;  } +static bool nfs4_mode_match_open_stateid(struct nfs4_state *state, +		fmode_t fmode) +{ +	switch(fmode & (FMODE_READ|FMODE_WRITE)) { +	case FMODE_READ|FMODE_WRITE: +		return state->n_rdwr != 0; +	case FMODE_WRITE: +		return state->n_wronly != 0; +	case FMODE_READ: +		return state->n_rdonly != 0; +	} +	WARN_ON_ONCE(1); +	return false; +} +  static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)  {  	int ret = 0; @@ -1443,12 +1458,18 @@ nfs4_opendata_check_deleg(struct nfs4_opendata *data, struct nfs4_state *state)  	if (delegation)  		delegation_flags = delegation->flags;  	rcu_read_unlock(); -	if (data->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR) { +	switch (data->o_arg.claim) { +	default: +		break; +	case NFS4_OPEN_CLAIM_DELEGATE_CUR: +	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:  		pr_err_ratelimited("NFS: Broken NFSv4 server %s is "  				   "returning a delegation for "  				   "OPEN(CLAIM_DELEGATE_CUR)\n",  				   clp->cl_hostname); -	} else if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0) +		return; +	} +	if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)  		nfs_inode_set_delegation(state->inode,  					 data->owner->so_cred,  					 &data->o_res); @@ -1571,17 +1592,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context  	return opendata;  } -static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res) +static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, +		fmode_t fmode)  {  	struct nfs4_state *newstate;  	int ret; -	if ((opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR || -	     opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEG_CUR_FH) && -	    (opendata->o_arg.u.delegation_type & fmode) != fmode) -		/* This mode can't have been delegated, so we must have -		 * a valid open_stateid to cover it - not need to reclaim. -		 */ +	if (!nfs4_mode_match_open_stateid(opendata->state, fmode))  		return 0;  	opendata->o_arg.open_flags = 0;  	opendata->o_arg.fmode = fmode; @@ -1597,14 +1614,14 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod  	newstate = nfs4_opendata_to_nfs4_state(opendata);  	if (IS_ERR(newstate))  		return PTR_ERR(newstate); +	if (newstate != opendata->state) +		ret = -ESTALE;  	nfs4_close_state(newstate, fmode); -	*res = newstate; -	return 0; +	return ret;  }  static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)  { -	struct nfs4_state *newstate;  	int ret;  	/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */ @@ -1615,27 +1632,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *  	clear_bit(NFS_DELEGATED_STATE, &state->flags);  	clear_bit(NFS_OPEN_STATE, &state->flags);  	smp_rmb(); -	if (state->n_rdwr != 0) { -		ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); -		if (ret != 0) -			return ret; -		if (newstate != state) -			return -ESTALE; -	} -	if (state->n_wronly != 0) { -		ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); -		if (ret != 0) -			return ret; -		if (newstate != state) -			return -ESTALE; -	} -	if (state->n_rdonly != 0) { -		ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); -		if (ret != 0) -			return ret; -		if (newstate != state) -			return -ESTALE; -	} +	ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE); +	if (ret != 0) +		return ret; +	ret = nfs4_open_recover_helper(opendata, FMODE_WRITE); +	if (ret != 0) +		return ret; +	ret = nfs4_open_recover_helper(opendata, FMODE_READ); +	if (ret != 0) +		return ret;  	/*  	 * We may have performed cached opens for all three recoveries.  	 * Check if we need to update the current stateid. @@ -1759,18 +1764,35 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct  	return err;  } -int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) +int nfs4_open_delegation_recall(struct nfs_open_context *ctx, +		struct nfs4_state *state, const nfs4_stateid *stateid, +		fmode_t type)  {  	struct nfs_server *server = NFS_SERVER(state->inode);  	struct nfs4_opendata *opendata; -	int err; +	int err = 0;  	opendata = nfs4_open_recoverdata_alloc(ctx, state,  			NFS4_OPEN_CLAIM_DELEG_CUR_FH);  	if (IS_ERR(opendata))  		return PTR_ERR(opendata);  	nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid); -	err = nfs4_open_recover(opendata, state); +	write_seqlock(&state->seqlock); +	nfs4_stateid_copy(&state->stateid, &state->open_stateid); +	write_sequnlock(&state->seqlock); +	clear_bit(NFS_DELEGATED_STATE, &state->flags); +	switch (type & (FMODE_READ|FMODE_WRITE)) { +	case FMODE_READ|FMODE_WRITE: +	case FMODE_WRITE: +		err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE); +		if (err) +			break; +		err = nfs4_open_recover_helper(opendata, FMODE_WRITE); +		if (err) +			break; +	case FMODE_READ: +		err = nfs4_open_recover_helper(opendata, FMODE_READ); +	}  	nfs4_opendata_put(opendata);  	return nfs4_handle_delegation_recall_error(server, state, stateid, err);  } @@ -1850,6 +1872,8 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)  	data->rpc_done = 0;  	data->rpc_status = 0;  	data->timestamp = jiffies; +	if (data->is_recover) +		nfs4_set_sequence_privileged(&data->c_arg.seq_args);  	task = rpc_run_task(&task_setup_data);  	if (IS_ERR(task))  		return PTR_ERR(task); @@ -2645,6 +2669,15 @@ out:  	return err;  } +static bool +nfs4_wait_on_layoutreturn(struct inode *inode, struct rpc_task *task) +{ +	if (inode == NULL || !nfs_have_layout(inode)) +		return false; + +	return pnfs_wait_on_layoutreturn(inode, task); +} +  struct nfs4_closedata {  	struct inode *inode;  	struct nfs4_state *state; @@ -2763,6 +2796,11 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)  		goto out_no_action;  	} +	if (nfs4_wait_on_layoutreturn(inode, task)) { +		nfs_release_seqid(calldata->arg.seqid); +		goto out_wait; +	} +  	if (calldata->arg.fmode == 0)  		task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];  	if (calldata->roc) @@ -5308,6 +5346,9 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)  	d_data = (struct nfs4_delegreturndata *)data; +	if (nfs4_wait_on_layoutreturn(d_data->inode, task)) +		return; +  	if (d_data->roc)  		pnfs_roc_get_barrier(d_data->inode, &d_data->roc_barrier); @@ -7800,39 +7841,46 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)  			dprintk("%s: NFS4ERR_RECALLCONFLICT waiting %lu\n",  				__func__, delay);  			rpc_delay(task, delay); -			task->tk_status = 0; -			rpc_restart_call_prepare(task); -			goto out; /* Do not call nfs4_async_handle_error() */ +			/* Do not call nfs4_async_handle_error() */ +			goto out_restart;  		}  		break;  	case -NFS4ERR_EXPIRED:  	case -NFS4ERR_BAD_STATEID:  		spin_lock(&inode->i_lock); -		lo = NFS_I(inode)->layout; -		if (!lo || list_empty(&lo->plh_segs)) { +		if (nfs4_stateid_match(&lgp->args.stateid, +					&lgp->args.ctx->state->stateid)) {  			spin_unlock(&inode->i_lock);  			/* If the open stateid was bad, then recover it. */  			state = lgp->args.ctx->state; -		} else { +			break; +		} +		lo = NFS_I(inode)->layout; +		if (lo && nfs4_stateid_match(&lgp->args.stateid, +					&lo->plh_stateid)) {  			LIST_HEAD(head);  			/*  			 * Mark the bad layout state as invalid, then retry  			 * with the current stateid.  			 */ +			set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);  			pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);  			spin_unlock(&inode->i_lock);  			pnfs_free_lseg_list(&head); -	 -			task->tk_status = 0; -			rpc_restart_call_prepare(task); -		} +		} else +			spin_unlock(&inode->i_lock); +		goto out_restart;  	}  	if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN) -		rpc_restart_call_prepare(task); +		goto out_restart;  out:  	dprintk("<-- %s\n", __func__);  	return; +out_restart: +	task->tk_status = 0; +	rpc_restart_call_prepare(task); +	return;  out_overflow:  	task->tk_status = -EOVERFLOW;  	goto out;  |