diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
| -rw-r--r-- | fs/nfs/nfs4proc.c | 38 | 
1 files changed, 26 insertions, 12 deletions
| diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7897826d7c51..241da19b7da4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1451,7 +1451,6 @@ static void nfs_resync_open_stateid_locked(struct nfs4_state *state)  }  static void nfs_clear_open_stateid_locked(struct nfs4_state *state, -		nfs4_stateid *arg_stateid,  		nfs4_stateid *stateid, fmode_t fmode)  {  	clear_bit(NFS_O_RDWR_STATE, &state->flags); @@ -1469,10 +1468,9 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state,  	}  	if (stateid == NULL)  		return; -	/* Handle races with OPEN */ -	if (!nfs4_stateid_match_other(arg_stateid, &state->open_stateid) || -	    (nfs4_stateid_match_other(stateid, &state->open_stateid) && -	    !nfs4_stateid_is_newer(stateid, &state->open_stateid))) { +	/* Handle OPEN+OPEN_DOWNGRADE races */ +	if (nfs4_stateid_match_other(stateid, &state->open_stateid) && +	    !nfs4_stateid_is_newer(stateid, &state->open_stateid)) {  		nfs_resync_open_stateid_locked(state);  		return;  	} @@ -1486,7 +1484,9 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,  	nfs4_stateid *stateid, fmode_t fmode)  {  	write_seqlock(&state->seqlock); -	nfs_clear_open_stateid_locked(state, arg_stateid, stateid, fmode); +	/* Ignore, if the CLOSE argment doesn't match the current stateid */ +	if (nfs4_state_match_open_stateid_other(state, arg_stateid)) +		nfs_clear_open_stateid_locked(state, stateid, fmode);  	write_sequnlock(&state->seqlock);  	if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))  		nfs4_schedule_state_manager(state->owner->so_server->nfs_client); @@ -2564,15 +2564,23 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)  static int nfs41_check_expired_locks(struct nfs4_state *state)  {  	int status, ret = NFS_OK; -	struct nfs4_lock_state *lsp; +	struct nfs4_lock_state *lsp, *prev = NULL;  	struct nfs_server *server = NFS_SERVER(state->inode);  	if (!test_bit(LK_STATE_IN_USE, &state->flags))  		goto out; + +	spin_lock(&state->state_lock);  	list_for_each_entry(lsp, &state->lock_states, ls_locks) {  		if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {  			struct rpc_cred *cred = lsp->ls_state->owner->so_cred; +			atomic_inc(&lsp->ls_count); +			spin_unlock(&state->state_lock); + +			nfs4_put_lock_state(prev); +			prev = lsp; +  			status = nfs41_test_and_free_expired_stateid(server,  					&lsp->ls_stateid,  					cred); @@ -2585,10 +2593,14 @@ static int nfs41_check_expired_locks(struct nfs4_state *state)  					set_bit(NFS_LOCK_LOST, &lsp->ls_flags);  			} else if (status != NFS_OK) {  				ret = status; -				break; +				nfs4_put_lock_state(prev); +				goto out;  			} +			spin_lock(&state->state_lock);  		} -	}; +	} +	spin_unlock(&state->state_lock); +	nfs4_put_lock_state(prev);  out:  	return ret;  } @@ -3122,7 +3134,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)) +	if (!nfs4_valid_open_stateid(state) || +	    test_bit(NFS_OPEN_STATE, &state->flags) == 0)  		call_close = 0;  	spin_unlock(&state->owner->so_lock); @@ -5569,6 +5582,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  	switch (task->tk_status) {  	case 0:  		renew_lease(data->res.server, data->timestamp); +		break;  	case -NFS4ERR_ADMIN_REVOKED:  	case -NFS4ERR_DELEG_REVOKED:  	case -NFS4ERR_EXPIRED: @@ -5579,8 +5593,6 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  	case -NFS4ERR_OLD_STATEID:  	case -NFS4ERR_STALE_STATEID:  		task->tk_status = 0; -		if (data->roc) -			pnfs_roc_set_barrier(data->inode, data->roc_barrier);  		break;  	default:  		if (nfs4_async_handle_error(task, data->res.server, @@ -5590,6 +5602,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  		}  	}  	data->rpc_status = task->tk_status; +	if (data->roc && data->rpc_status == 0) +		pnfs_roc_set_barrier(data->inode, data->roc_barrier);  }  static void nfs4_delegreturn_release(void *calldata) |