diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
| -rw-r--r-- | fs/nfs/nfs4proc.c | 862 | 
1 files changed, 628 insertions, 234 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d53d6785cba..4bf3d97cc5a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -105,9 +105,6 @@ nfs4_label_init_security(struct inode *dir, struct dentry *dentry,  	if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)  		return NULL; -	if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2) -		return NULL; -  	err = security_dentry_init_security(dentry, sattr->ia_mode,  				&dentry->d_name, (void **)&label->label, &label->len);  	if (err == 0) @@ -384,6 +381,14 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc  		case -NFS4ERR_STALE_CLIENTID:  			nfs4_schedule_lease_recovery(clp);  			goto wait_on_recovery; +		case -NFS4ERR_MOVED: +			ret = nfs4_schedule_migration_recovery(server); +			if (ret < 0) +				break; +			goto wait_on_recovery; +		case -NFS4ERR_LEASE_MOVED: +			nfs4_schedule_lease_moved_recovery(clp); +			goto wait_on_recovery;  #if defined(CONFIG_NFS_V4_1)  		case -NFS4ERR_BADSESSION:  		case -NFS4ERR_BADSLOT: @@ -431,6 +436,8 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc  	return nfs4_map_errors(ret);  wait_on_recovery:  	ret = nfs4_wait_clnt_recover(clp); +	if (test_bit(NFS_MIG_FAILED, &server->mig_status)) +		return -EIO;  	if (ret == 0)  		exception->retry = 1;  	return ret; @@ -532,7 +539,7 @@ static int nfs40_sequence_done(struct rpc_task *task,  	struct nfs4_slot *slot = res->sr_slot;  	struct nfs4_slot_table *tbl; -	if (!RPC_WAS_SENT(task)) +	if (slot == NULL)  		goto out;  	tbl = slot->table; @@ -552,15 +559,10 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)  {  	struct nfs4_session *session;  	struct nfs4_slot_table *tbl; +	struct nfs4_slot *slot = res->sr_slot;  	bool send_new_highest_used_slotid = false; -	if (!res->sr_slot) { -		/* just wake up the next guy waiting since -		 * we may have not consumed a slot after all */ -		dprintk("%s: No slot\n", __func__); -		return; -	} -	tbl = res->sr_slot->table; +	tbl = slot->table;  	session = tbl->session;  	spin_lock(&tbl->slot_tbl_lock); @@ -570,11 +572,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)  	if (tbl->highest_used_slotid > tbl->target_highest_slotid)  		send_new_highest_used_slotid = true; -	if (nfs41_wake_and_assign_slot(tbl, res->sr_slot)) { +	if (nfs41_wake_and_assign_slot(tbl, slot)) {  		send_new_highest_used_slotid = false;  		goto out_unlock;  	} -	nfs4_free_slot(tbl, res->sr_slot); +	nfs4_free_slot(tbl, slot);  	if (tbl->highest_used_slotid != NFS4_NO_SLOT)  		send_new_highest_used_slotid = false; @@ -585,19 +587,20 @@ out_unlock:  		nfs41_server_notify_highest_slotid_update(session->clp);  } -static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) +int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)  {  	struct nfs4_session *session; -	struct nfs4_slot *slot; +	struct nfs4_slot *slot = res->sr_slot;  	struct nfs_client *clp;  	bool interrupted = false;  	int ret = 1; +	if (slot == NULL) +		goto out_noaction;  	/* don't increment the sequence number if the task wasn't sent */  	if (!RPC_WAS_SENT(task))  		goto out; -	slot = res->sr_slot;  	session = slot->table->session;  	if (slot->interrupted) { @@ -672,6 +675,7 @@ out:  	/* The session may be reset by one of the error handlers. */  	dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);  	nfs41_sequence_free_slot(res); +out_noaction:  	return ret;  retry_nowait:  	if (rpc_restart_call_prepare(task)) { @@ -685,6 +689,7 @@ out_retry:  	rpc_delay(task, NFS4_POLL_RETRY_MAX);  	return 0;  } +EXPORT_SYMBOL_GPL(nfs41_sequence_done);  static int nfs4_sequence_done(struct rpc_task *task,  			       struct nfs4_sequence_res *res) @@ -1063,6 +1068,7 @@ static void nfs4_opendata_free(struct kref *kref)  	dput(p->dentry);  	nfs_sb_deactive(sb);  	nfs_fattr_free_names(&p->f_attr); +	kfree(p->f_attr.mdsthreshold);  	kfree(p);  } @@ -1132,12 +1138,71 @@ static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)  	nfs4_state_set_mode_locked(state, state->state | fmode);  } -static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) +static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state) +{ +	struct nfs_client *clp = state->owner->so_server->nfs_client; +	bool need_recover = false; + +	if (test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags) && state->n_rdonly) +		need_recover = true; +	if (test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags) && state->n_wronly) +		need_recover = true; +	if (test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags) && state->n_rdwr) +		need_recover = true; +	if (need_recover) +		nfs4_state_mark_reclaim_nograce(clp, state); +} + +static bool nfs_need_update_open_stateid(struct nfs4_state *state, +		nfs4_stateid *stateid) +{ +	if (test_and_set_bit(NFS_OPEN_STATE, &state->flags) == 0) +		return true; +	if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) { +		nfs_test_and_clear_all_open_stateid(state); +		return true; +	} +	if (nfs4_stateid_is_newer(stateid, &state->open_stateid)) +		return true; +	return false; +} + +static void nfs_clear_open_stateid_locked(struct nfs4_state *state, +		nfs4_stateid *stateid, fmode_t fmode)  { +	clear_bit(NFS_O_RDWR_STATE, &state->flags); +	switch (fmode & (FMODE_READ|FMODE_WRITE)) { +	case FMODE_WRITE: +		clear_bit(NFS_O_RDONLY_STATE, &state->flags); +		break; +	case FMODE_READ: +		clear_bit(NFS_O_WRONLY_STATE, &state->flags); +		break; +	case 0: +		clear_bit(NFS_O_RDONLY_STATE, &state->flags); +		clear_bit(NFS_O_WRONLY_STATE, &state->flags); +		clear_bit(NFS_OPEN_STATE, &state->flags); +	} +	if (stateid == NULL) +		return; +	if (!nfs_need_update_open_stateid(state, stateid)) +		return;  	if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)  		nfs4_stateid_copy(&state->stateid, stateid);  	nfs4_stateid_copy(&state->open_stateid, stateid); -	set_bit(NFS_OPEN_STATE, &state->flags); +} + +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, 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); +} + +static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) +{  	switch (fmode) {  		case FMODE_READ:  			set_bit(NFS_O_RDONLY_STATE, &state->flags); @@ -1148,13 +1213,11 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *  		case FMODE_READ|FMODE_WRITE:  			set_bit(NFS_O_RDWR_STATE, &state->flags);  	} -} - -static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) -{ -	write_seqlock(&state->seqlock); -	nfs_set_open_stateid_locked(state, stateid, fmode); -	write_sequnlock(&state->seqlock); +	if (!nfs_need_update_open_stateid(state, stateid)) +		return; +	if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) +		nfs4_stateid_copy(&state->stateid, stateid); +	nfs4_stateid_copy(&state->open_stateid, stateid);  }  static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode) @@ -1212,6 +1275,8 @@ no_delegation:  		__update_open_stateid(state, open_stateid, NULL, fmode);  		ret = 1;  	} +	if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) +		nfs4_schedule_state_manager(state->owner->so_server->nfs_client);  	return ret;  } @@ -1318,31 +1383,24 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)  	int ret;  	if (!data->rpc_done) { -		ret = data->rpc_status; -		goto err; +		if (data->rpc_status) { +			ret = data->rpc_status; +			goto err; +		} +		/* cached opens have already been processed */ +		goto update;  	} -	ret = -ESTALE; -	if (!(data->f_attr.valid & NFS_ATTR_FATTR_TYPE) || -	    !(data->f_attr.valid & NFS_ATTR_FATTR_FILEID) || -	    !(data->f_attr.valid & NFS_ATTR_FATTR_CHANGE)) -		goto err; - -	ret = -ENOMEM; -	state = nfs4_get_open_state(inode, data->owner); -	if (state == NULL) -		goto err; -  	ret = nfs_refresh_inode(inode, &data->f_attr);  	if (ret)  		goto err; -	nfs_setsecurity(inode, &data->f_attr, data->f_label); -  	if (data->o_res.delegation_type != 0)  		nfs4_opendata_check_deleg(data, state); +update:  	update_open_stateid(state, &data->o_res.stateid, NULL,  			    data->o_arg.fmode); +	atomic_inc(&state->count);  	return state;  err: @@ -1452,12 +1510,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *  	struct nfs4_state *newstate;  	int ret; +	/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */ +	clear_bit(NFS_O_RDWR_STATE, &state->flags); +	clear_bit(NFS_O_WRONLY_STATE, &state->flags); +	clear_bit(NFS_O_RDONLY_STATE, &state->flags);  	/* memory barrier prior to reading state->n_* */  	clear_bit(NFS_DELEGATED_STATE, &state->flags);  	clear_bit(NFS_OPEN_STATE, &state->flags);  	smp_rmb();  	if (state->n_rdwr != 0) { -		clear_bit(NFS_O_RDWR_STATE, &state->flags);  		ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);  		if (ret != 0)  			return ret; @@ -1465,7 +1526,6 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *  			return -ESTALE;  	}  	if (state->n_wronly != 0) { -		clear_bit(NFS_O_WRONLY_STATE, &state->flags);  		ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);  		if (ret != 0)  			return ret; @@ -1473,7 +1533,6 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *  			return -ESTALE;  	}  	if (state->n_rdonly != 0) { -		clear_bit(NFS_O_RDONLY_STATE, &state->flags);  		ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);  		if (ret != 0)  			return ret; @@ -1575,6 +1634,12 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct  			/* Don't recall a delegation if it was lost */  			nfs4_schedule_lease_recovery(server->nfs_client);  			return -EAGAIN; +		case -NFS4ERR_MOVED: +			nfs4_schedule_migration_recovery(server); +			return -EAGAIN; +		case -NFS4ERR_LEASE_MOVED: +			nfs4_schedule_lease_moved_recovery(server->nfs_client); +			return -EAGAIN;  		case -NFS4ERR_DELEG_REVOKED:  		case -NFS4ERR_ADMIN_REVOKED:  		case -NFS4ERR_BAD_STATEID: @@ -1616,15 +1681,15 @@ static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata)  {  	struct nfs4_opendata *data = calldata; -	nfs40_setup_sequence(data->o_arg.server, &data->o_arg.seq_args, -				&data->o_res.seq_res, task); +	nfs40_setup_sequence(data->o_arg.server, &data->c_arg.seq_args, +				&data->c_res.seq_res, task);  }  static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)  {  	struct nfs4_opendata *data = calldata; -	nfs40_sequence_done(task, &data->o_res.seq_res); +	nfs40_sequence_done(task, &data->c_res.seq_res);  	data->rpc_status = task->tk_status;  	if (data->rpc_status == 0) { @@ -1682,7 +1747,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)  	};  	int status; -	nfs4_init_sequence(&data->o_arg.seq_args, &data->o_res.seq_res, 1); +	nfs4_init_sequence(&data->c_arg.seq_args, &data->c_res.seq_res, 1);  	kref_get(&data->kref);  	data->rpc_done = 0;  	data->rpc_status = 0; @@ -1962,7 +2027,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)  			return status;  	}  	if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) -		_nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label); +		nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label);  	return 0;  } @@ -2240,10 +2305,12 @@ static int _nfs4_do_open(struct inode *dir,  		}  	} -	if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) { -		opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc(); -		if (!opendata->f_attr.mdsthreshold) -			goto err_free_label; +	if (server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) { +		if (!opendata->f_attr.mdsthreshold) { +			opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc(); +			if (!opendata->f_attr.mdsthreshold) +				goto err_free_label; +		}  		opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];  	}  	if (dentry->d_inode != NULL) @@ -2271,11 +2338,10 @@ static int _nfs4_do_open(struct inode *dir,  	if (opendata->file_created)  		*opened |= FILE_CREATED; -	if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server)) +	if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server)) {  		*ctx_th = opendata->f_attr.mdsthreshold; -	else -		kfree(opendata->f_attr.mdsthreshold); -	opendata->f_attr.mdsthreshold = NULL; +		opendata->f_attr.mdsthreshold = NULL; +	}  	nfs4_label_free(olabel); @@ -2285,7 +2351,6 @@ static int _nfs4_do_open(struct inode *dir,  err_free_label:  	nfs4_label_free(olabel);  err_opendata_put: -	kfree(opendata->f_attr.mdsthreshold);  	nfs4_opendata_put(opendata);  err_put_state_owner:  	nfs4_put_state_owner(sp); @@ -2394,13 +2459,16 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,  	if (nfs4_copy_delegation_stateid(&arg.stateid, inode, fmode)) {  		/* Use that stateid */ -	} else if (truncate && state != NULL && nfs4_valid_open_stateid(state)) { +	} else if (truncate && state != NULL) {  		struct nfs_lockowner lockowner = {  			.l_owner = current->files,  			.l_pid = current->tgid,  		}; -		nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE, -				&lockowner); +		if (!nfs4_valid_open_stateid(state)) +			return -EBADF; +		if (nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE, +				&lockowner) == -EIO) +			return -EBADF;  	} else  		nfs4_stateid_copy(&arg.stateid, &zero_stateid); @@ -2472,26 +2540,6 @@ static void nfs4_free_closedata(void *data)  	kfree(calldata);  } -static void nfs4_close_clear_stateid_flags(struct nfs4_state *state, -		fmode_t fmode) -{ -	spin_lock(&state->owner->so_lock); -	clear_bit(NFS_O_RDWR_STATE, &state->flags); -	switch (fmode & (FMODE_READ|FMODE_WRITE)) { -	case FMODE_WRITE: -		clear_bit(NFS_O_RDONLY_STATE, &state->flags); -		break; -	case FMODE_READ: -		clear_bit(NFS_O_WRONLY_STATE, &state->flags); -		break; -	case 0: -		clear_bit(NFS_O_RDONLY_STATE, &state->flags); -		clear_bit(NFS_O_WRONLY_STATE, &state->flags); -		clear_bit(NFS_OPEN_STATE, &state->flags); -	} -	spin_unlock(&state->owner->so_lock); -} -  static void nfs4_close_done(struct rpc_task *task, void *data)  {  	struct nfs4_closedata *calldata = data; @@ -2510,11 +2558,10 @@ static void nfs4_close_done(struct rpc_task *task, void *data)  			if (calldata->roc)  				pnfs_roc_set_barrier(state->inode,  						     calldata->roc_barrier); -			nfs_set_open_stateid(state, &calldata->res.stateid, 0); +			nfs_clear_open_stateid(state, &calldata->res.stateid, 0);  			renew_lease(server, calldata->timestamp); -			nfs4_close_clear_stateid_flags(state, -					calldata->arg.fmode); -			break; +			goto out_release; +		case -NFS4ERR_ADMIN_REVOKED:  		case -NFS4ERR_STALE_STATEID:  		case -NFS4ERR_OLD_STATEID:  		case -NFS4ERR_BAD_STATEID: @@ -2522,9 +2569,13 @@ static void nfs4_close_done(struct rpc_task *task, void *data)  			if (calldata->arg.fmode == 0)  				break;  		default: -			if (nfs4_async_handle_error(task, server, state) == -EAGAIN) +			if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {  				rpc_restart_call_prepare(task); +				goto out_release; +			}  	} +	nfs_clear_open_stateid(state, NULL, calldata->arg.fmode); +out_release:  	nfs_release_seqid(calldata->arg.seqid);  	nfs_refresh_inode(calldata->inode, calldata->res.fattr);  	dprintk("%s: done, ret = %d!\n", __func__, task->tk_status); @@ -2697,6 +2748,10 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)  		nfs4_close_state(ctx->state, ctx->mode);  } +#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL) +#define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL) +#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_SECURITY_LABEL - 1UL) +  static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)  {  	struct nfs4_server_caps_arg args = { @@ -2712,13 +2767,27 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f  	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);  	if (status == 0) { +		/* Sanity check the server answers */ +		switch (server->nfs_client->cl_minorversion) { +		case 0: +			res.attr_bitmask[1] &= FATTR4_WORD1_NFS40_MASK; +			res.attr_bitmask[2] = 0; +			break; +		case 1: +			res.attr_bitmask[2] &= FATTR4_WORD2_NFS41_MASK; +			break; +		case 2: +			res.attr_bitmask[2] &= FATTR4_WORD2_NFS42_MASK; +		}  		memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));  		server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|  				NFS_CAP_SYMLINKS|NFS_CAP_FILEID|  				NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|  				NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME| -				NFS_CAP_CTIME|NFS_CAP_MTIME); -		if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) +				NFS_CAP_CTIME|NFS_CAP_MTIME| +				NFS_CAP_SECURITY_LABEL); +		if (res.attr_bitmask[0] & FATTR4_WORD0_ACL && +				res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)  			server->caps |= NFS_CAP_ACLS;  		if (res.has_links != 0)  			server->caps |= NFS_CAP_HARDLINKS; @@ -2746,14 +2815,12 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f  #endif  		memcpy(server->attr_bitmask_nl, res.attr_bitmask,  				sizeof(server->attr_bitmask)); +		server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL; -		if (server->caps & NFS_CAP_SECURITY_LABEL) { -			server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL; -			res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL; -		}  		memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));  		server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;  		server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; +		server->cache_consistency_bitmask[2] = 0;  		server->acl_bitmask = res.acl_bitmask;  		server->fh_expire_type = res.fh_expire_type;  	} @@ -2864,11 +2931,24 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,  	int status = -EPERM;  	size_t i; -	for (i = 0; i < ARRAY_SIZE(flav_array); i++) { -		status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]); -		if (status == -NFS4ERR_WRONGSEC || status == -EACCES) -			continue; -		break; +	if (server->auth_info.flavor_len > 0) { +		/* try each flavor specified by user */ +		for (i = 0; i < server->auth_info.flavor_len; i++) { +			status = nfs4_lookup_root_sec(server, fhandle, info, +						server->auth_info.flavors[i]); +			if (status == -NFS4ERR_WRONGSEC || status == -EACCES) +				continue; +			break; +		} +	} else { +		/* no flavors specified by user, try default list */ +		for (i = 0; i < ARRAY_SIZE(flav_array); i++) { +			status = nfs4_lookup_root_sec(server, fhandle, info, +						      flav_array[i]); +			if (status == -NFS4ERR_WRONGSEC || status == -EACCES) +				continue; +			break; +		}  	}  	/* @@ -2910,9 +2990,6 @@ int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,  		status = nfs4_lookup_root(server, fhandle, info);  		if (status != -NFS4ERR_WRONGSEC)  			break; -		/* Did user force a 'sec=' mount option? */ -		if (server->flags & NFS_MOUNT_SECFLAVOUR) -			break;  	default:  		status = nfs4_do_find_root_sec(server, fhandle, info);  	} @@ -2981,11 +3058,16 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,  	status = nfs4_proc_fs_locations(client, dir, name, locations, page);  	if (status != 0)  		goto out; -	/* Make sure server returned a different fsid for the referral */ + +	/* +	 * If the fsid didn't change, this is a migration event, not a +	 * referral.  Cause us to drop into the exception handler, which +	 * will kick off migration recovery. +	 */  	if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {  		dprintk("%s: server did not return a different fsid for"  			" a referral at %s\n", __func__, name->name); -		status = -EIO; +		status = -NFS4ERR_MOVED;  		goto out;  	}  	/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */ @@ -3165,10 +3247,7 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,  			err = -EPERM;  			if (client != *clnt)  				goto out; -			/* No security negotiation if the user specified 'sec=' */ -			if (NFS_SERVER(dir)->flags & NFS_MOUNT_SECFLAVOUR) -				goto out; -			client = nfs4_create_sec_client(client, dir, name); +			client = nfs4_negotiate_security(client, dir, name);  			if (IS_ERR(client))  				return PTR_ERR(client); @@ -3469,49 +3548,6 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,  	return 1;  } -static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, -		struct inode *new_dir, struct qstr *new_name) -{ -	struct nfs_server *server = NFS_SERVER(old_dir); -	struct nfs_renameargs arg = { -		.old_dir = NFS_FH(old_dir), -		.new_dir = NFS_FH(new_dir), -		.old_name = old_name, -		.new_name = new_name, -	}; -	struct nfs_renameres res = { -		.server = server, -	}; -	struct rpc_message msg = { -		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME], -		.rpc_argp = &arg, -		.rpc_resp = &res, -	}; -	int status = -ENOMEM; - -	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); -	if (!status) { -		update_changeattr(old_dir, &res.old_cinfo); -		update_changeattr(new_dir, &res.new_cinfo); -	} -	return status; -} - -static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, -		struct inode *new_dir, struct qstr *new_name) -{ -	struct nfs4_exception exception = { }; -	int err; -	do { -		err = _nfs4_proc_rename(old_dir, old_name, -					new_dir, new_name); -		trace_nfs4_rename(old_dir, old_name, new_dir, new_name, err); -		err = nfs4_handle_exception(NFS_SERVER(old_dir), err, -				&exception); -	} while (exception.retry); -	return err; -} -  static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)  {  	struct nfs_server *server = NFS_SERVER(inode); @@ -3738,9 +3774,8 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,  	};  	int			status; -	dprintk("%s: dentry = %s/%s, cookie = %Lu\n", __func__, -			dentry->d_parent->d_name.name, -			dentry->d_name.name, +	dprintk("%s: dentry = %pd2, cookie = %Lu\n", __func__, +			dentry,  			(unsigned long long)cookie);  	nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);  	res.pgbase = args.pgbase; @@ -3977,8 +4012,9 @@ static bool nfs4_stateid_is_current(nfs4_stateid *stateid,  {  	nfs4_stateid current_stateid; -	if (nfs4_set_rw_stateid(¤t_stateid, ctx, l_ctx, fmode)) -		return false; +	/* If the current stateid represents a lost lock, then exit */ +	if (nfs4_set_rw_stateid(¤t_stateid, ctx, l_ctx, fmode) == -EIO) +		return true;  	return nfs4_stateid_match(stateid, ¤t_stateid);  } @@ -3997,12 +4033,12 @@ static bool nfs4_error_stateid_expired(int err)  	return false;  } -void __nfs4_read_done_cb(struct nfs_read_data *data) +void __nfs4_read_done_cb(struct nfs_pgio_data *data)  {  	nfs_invalidate_atime(data->header->inode);  } -static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data) +static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_data *data)  {  	struct nfs_server *server = NFS_SERVER(data->header->inode); @@ -4019,7 +4055,7 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)  }  static bool nfs4_read_stateid_changed(struct rpc_task *task, -		struct nfs_readargs *args) +		struct nfs_pgio_args *args)  {  	if (!nfs4_error_stateid_expired(task->tk_status) || @@ -4032,7 +4068,7 @@ static bool nfs4_read_stateid_changed(struct rpc_task *task,  	return true;  } -static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) +static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_data *data)  {  	dprintk("--> %s\n", __func__); @@ -4041,19 +4077,19 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)  		return -EAGAIN;  	if (nfs4_read_stateid_changed(task, &data->args))  		return -EAGAIN; -	return data->read_done_cb ? data->read_done_cb(task, data) : +	return data->pgio_done_cb ? data->pgio_done_cb(task, data) :  				    nfs4_read_done_cb(task, data);  } -static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg) +static void nfs4_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg)  {  	data->timestamp   = jiffies; -	data->read_done_cb = nfs4_read_done_cb; +	data->pgio_done_cb = nfs4_read_done_cb;  	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];  	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);  } -static int nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data)  {  	if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),  			&data->args.seq_args, @@ -4061,14 +4097,14 @@ static int nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_dat  			task))  		return 0;  	if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, -				data->args.lock_context, FMODE_READ) == -EIO) +				data->args.lock_context, data->header->rw_ops->rw_mode) == -EIO)  		return -EIO;  	if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))  		return -EIO;  	return 0;  } -static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data) +static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_pgio_data *data)  {  	struct inode *inode = data->header->inode; @@ -4085,7 +4121,7 @@ static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data  }  static bool nfs4_write_stateid_changed(struct rpc_task *task, -		struct nfs_writeargs *args) +		struct nfs_pgio_args *args)  {  	if (!nfs4_error_stateid_expired(task->tk_status) || @@ -4098,18 +4134,18 @@ static bool nfs4_write_stateid_changed(struct rpc_task *task,  	return true;  } -static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) +static int nfs4_write_done(struct rpc_task *task, struct nfs_pgio_data *data)  {  	if (!nfs4_sequence_done(task, &data->res.seq_res))  		return -EAGAIN;  	if (nfs4_write_stateid_changed(task, &data->args))  		return -EAGAIN; -	return data->write_done_cb ? data->write_done_cb(task, data) : +	return data->pgio_done_cb ? data->pgio_done_cb(task, data) :  		nfs4_write_done_cb(task, data);  }  static -bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data) +bool nfs4_write_need_cache_consistency_data(const struct nfs_pgio_data *data)  {  	const struct nfs_pgio_header *hdr = data->header; @@ -4122,7 +4158,7 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)  	return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;  } -static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) +static void nfs4_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg)  {  	struct nfs_server *server = NFS_SERVER(data->header->inode); @@ -4132,8 +4168,8 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag  	} else  		data->args.bitmask = server->cache_consistency_bitmask; -	if (!data->write_done_cb) -		data->write_done_cb = nfs4_write_done_cb; +	if (!data->pgio_done_cb) +		data->pgio_done_cb = nfs4_write_done_cb;  	data->res.server = server;  	data->timestamp   = jiffies; @@ -4141,21 +4177,6 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag  	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);  } -static int nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) -{ -	if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), -			&data->args.seq_args, -			&data->res.seq_res, -			task)) -		return 0; -	if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, -				data->args.lock_context, FMODE_WRITE) == -EIO) -		return -EIO; -	if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) -		return -EIO; -	return 0; -} -  static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)  {  	nfs4_setup_sequence(NFS_SERVER(data->inode), @@ -4221,7 +4242,13 @@ static void nfs4_renew_done(struct rpc_task *task, void *calldata)  	unsigned long timestamp = data->timestamp;  	trace_nfs4_renew_async(clp, task->tk_status); -	if (task->tk_status < 0) { +	switch (task->tk_status) { +	case 0: +		break; +	case -NFS4ERR_LEASE_MOVED: +		nfs4_schedule_lease_moved_recovery(clp); +		break; +	default:  		/* Unless we're shutting down, schedule state recovery! */  		if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)  			return; @@ -4280,9 +4307,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)  static inline int nfs4_server_supports_acls(struct nfs_server *server)  { -	return (server->caps & NFS_CAP_ACLS) -		&& (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) -		&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); +	return server->caps & NFS_CAP_ACLS;  }  /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that @@ -4575,7 +4600,7 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,  	struct nfs4_label label = {0, 0, buflen, buf};  	u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL }; -	struct nfs4_getattr_arg args = { +	struct nfs4_getattr_arg arg = {  		.fh		= NFS_FH(inode),  		.bitmask	= bitmask,  	}; @@ -4586,14 +4611,14 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,  	};  	struct rpc_message msg = {  		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_GETATTR], -		.rpc_argp	= &args, +		.rpc_argp	= &arg,  		.rpc_resp	= &res,  	};  	int ret;  	nfs_fattr_init(&fattr); -	ret = rpc_call_sync(server->client, &msg, 0); +	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 0);  	if (ret)  		return ret;  	if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL)) @@ -4630,7 +4655,7 @@ static int _nfs4_do_set_security_label(struct inode *inode,  	struct iattr sattr = {0};  	struct nfs_server *server = NFS_SERVER(inode);  	const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL }; -	struct nfs_setattrargs args = { +	struct nfs_setattrargs arg = {  		.fh             = NFS_FH(inode),  		.iap            = &sattr,  		.server		= server, @@ -4644,14 +4669,14 @@ static int _nfs4_do_set_security_label(struct inode *inode,  	};  	struct rpc_message msg = {  		.rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], -		.rpc_argp       = &args, +		.rpc_argp       = &arg,  		.rpc_resp       = &res,  	};  	int status; -	nfs4_stateid_copy(&args.stateid, &zero_stateid); +	nfs4_stateid_copy(&arg.stateid, &zero_stateid); -	status = rpc_call_sync(server->client, &msg, 0); +	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);  	if (status)  		dprintk("%s failed: %d\n", __func__, status); @@ -4735,17 +4760,24 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,  			if (state == NULL)  				break;  			if (nfs4_schedule_stateid_recovery(server, state) < 0) -				goto stateid_invalid; +				goto recovery_failed;  			goto wait_on_recovery;  		case -NFS4ERR_EXPIRED:  			if (state != NULL) {  				if (nfs4_schedule_stateid_recovery(server, state) < 0) -					goto stateid_invalid; +					goto recovery_failed;  			}  		case -NFS4ERR_STALE_STATEID:  		case -NFS4ERR_STALE_CLIENTID:  			nfs4_schedule_lease_recovery(clp);  			goto wait_on_recovery; +		case -NFS4ERR_MOVED: +			if (nfs4_schedule_migration_recovery(server) < 0) +				goto recovery_failed; +			goto wait_on_recovery; +		case -NFS4ERR_LEASE_MOVED: +			nfs4_schedule_lease_moved_recovery(clp); +			goto wait_on_recovery;  #if defined(CONFIG_NFS_V4_1)  		case -NFS4ERR_BADSESSION:  		case -NFS4ERR_BADSLOT: @@ -4757,29 +4789,28 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,  			dprintk("%s ERROR %d, Reset session\n", __func__,  				task->tk_status);  			nfs4_schedule_session_recovery(clp->cl_session, task->tk_status); -			task->tk_status = 0; -			return -EAGAIN; +			goto wait_on_recovery;  #endif /* CONFIG_NFS_V4_1 */  		case -NFS4ERR_DELAY:  			nfs_inc_server_stats(server, NFSIOS_DELAY);  		case -NFS4ERR_GRACE:  			rpc_delay(task, NFS4_POLL_RETRY_MAX); -			task->tk_status = 0; -			return -EAGAIN;  		case -NFS4ERR_RETRY_UNCACHED_REP:  		case -NFS4ERR_OLD_STATEID: -			task->tk_status = 0; -			return -EAGAIN; +			goto restart_call;  	}  	task->tk_status = nfs4_map_errors(task->tk_status);  	return 0; -stateid_invalid: +recovery_failed:  	task->tk_status = -EIO;  	return 0;  wait_on_recovery:  	rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);  	if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)  		rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task); +	if (test_bit(NFS_MIG_FAILED, &server->mig_status)) +		goto recovery_failed; +restart_call:  	task->tk_status = 0;  	return -EAGAIN;  } @@ -4836,6 +4867,20 @@ nfs4_init_uniform_client_string(const struct nfs_client *clp,  				nodename);  } +/* + * nfs4_callback_up_net() starts only "tcp" and "tcp6" callback + * services.  Advertise one based on the address family of the + * clientaddr. + */ +static unsigned int +nfs4_init_callback_netid(const struct nfs_client *clp, char *buf, size_t len) +{ +	if (strchr(clp->cl_ipaddr, ':') != NULL) +		return scnprintf(buf, len, "tcp6"); +	else +		return scnprintf(buf, len, "tcp"); +} +  /**   * nfs4_proc_setclientid - Negotiate client ID   * @clp: state data structure @@ -4877,12 +4922,10 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,  						setclientid.sc_name,  						sizeof(setclientid.sc_name));  	/* cb_client4 */ -	rcu_read_lock(); -	setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, -				sizeof(setclientid.sc_netid), "%s", -				rpc_peeraddr2str(clp->cl_rpcclient, -							RPC_DISPLAY_NETID)); -	rcu_read_unlock(); +	setclientid.sc_netid_len = +				nfs4_init_callback_netid(clp, +						setclientid.sc_netid, +						sizeof(setclientid.sc_netid));  	setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,  				sizeof(setclientid.sc_uaddr), "%s.%u.%u",  				clp->cl_ipaddr, port >> 8, port & 255); @@ -4943,11 +4986,17 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)  	trace_nfs4_delegreturn_exit(&data->args, &data->res, task->tk_status);  	switch (task->tk_status) { -	case -NFS4ERR_STALE_STATEID: -	case -NFS4ERR_EXPIRED:  	case 0:  		renew_lease(data->res.server, data->timestamp);  		break; +	case -NFS4ERR_ADMIN_REVOKED: +	case -NFS4ERR_DELEG_REVOKED: +	case -NFS4ERR_BAD_STATEID: +	case -NFS4ERR_OLD_STATEID: +	case -NFS4ERR_STALE_STATEID: +	case -NFS4ERR_EXPIRED: +		task->tk_status = 0; +		break;  	default:  		if (nfs4_async_handle_error(task, data->res.server, NULL) ==  				-EAGAIN) { @@ -5106,6 +5155,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock  			status = 0;  	}  	request->fl_ops->fl_release_private(request); +	request->fl_ops = NULL;  out:  	return status;  } @@ -5777,21 +5827,36 @@ struct nfs_release_lockowner_data {  	struct nfs4_lock_state *lsp;  	struct nfs_server *server;  	struct nfs_release_lockowner_args args; -	struct nfs4_sequence_args seq_args; -	struct nfs4_sequence_res seq_res; +	struct nfs_release_lockowner_res res; +	unsigned long timestamp;  };  static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata)  {  	struct nfs_release_lockowner_data *data = calldata;  	nfs40_setup_sequence(data->server, -				&data->seq_args, &data->seq_res, task); +				&data->args.seq_args, &data->res.seq_res, task); +	data->timestamp = jiffies;  }  static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata)  {  	struct nfs_release_lockowner_data *data = calldata; -	nfs40_sequence_done(task, &data->seq_res); +	struct nfs_server *server = data->server; + +	nfs40_sequence_done(task, &data->res.seq_res); + +	switch (task->tk_status) { +	case 0: +		renew_lease(server, data->timestamp); +		break; +	case -NFS4ERR_STALE_CLIENTID: +	case -NFS4ERR_EXPIRED: +	case -NFS4ERR_LEASE_MOVED: +	case -NFS4ERR_DELAY: +		if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) +			rpc_restart_call_prepare(task); +	}  }  static void nfs4_release_lockowner_release(void *calldata) @@ -5820,7 +5885,6 @@ static int nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_st  	data = kmalloc(sizeof(*data), GFP_NOFS);  	if (!data)  		return -ENOMEM; -	nfs4_init_sequence(&data->seq_args, &data->seq_res, 0);  	data->lsp = lsp;  	data->server = server;  	data->args.lock_owner.clientid = server->nfs_client->cl_clientid; @@ -5828,6 +5892,8 @@ static int nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_st  	data->args.lock_owner.s_dev = server->s_dev;  	msg.rpc_argp = &data->args; +	msg.rpc_resp = &data->res; +	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);  	rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);  	return 0;  } @@ -5990,6 +6056,283 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,  	return err;  } +/* + * This operation also signals the server that this client is + * performing migration recovery.  The server can stop returning + * NFS4ERR_LEASE_MOVED to this client.  A RENEW operation is + * appended to this compound to identify the client ID which is + * performing recovery. + */ +static int _nfs40_proc_get_locations(struct inode *inode, +				     struct nfs4_fs_locations *locations, +				     struct page *page, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct rpc_clnt *clnt = server->client; +	u32 bitmask[2] = { +		[0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, +	}; +	struct nfs4_fs_locations_arg args = { +		.clientid	= server->nfs_client->cl_clientid, +		.fh		= NFS_FH(inode), +		.page		= page, +		.bitmask	= bitmask, +		.migration	= 1,		/* skip LOOKUP */ +		.renew		= 1,		/* append RENEW */ +	}; +	struct nfs4_fs_locations_res res = { +		.fs_locations	= locations, +		.migration	= 1, +		.renew		= 1, +	}; +	struct rpc_message msg = { +		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], +		.rpc_argp	= &args, +		.rpc_resp	= &res, +		.rpc_cred	= cred, +	}; +	unsigned long now = jiffies; +	int status; + +	nfs_fattr_init(&locations->fattr); +	locations->server = server; +	locations->nlocations = 0; + +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0); +	nfs4_set_sequence_privileged(&args.seq_args); +	status = nfs4_call_sync_sequence(clnt, server, &msg, +					&args.seq_args, &res.seq_res); +	if (status) +		return status; + +	renew_lease(server, now); +	return 0; +} + +#ifdef CONFIG_NFS_V4_1 + +/* + * This operation also signals the server that this client is + * performing migration recovery.  The server can stop asserting + * SEQ4_STATUS_LEASE_MOVED for this client.  The client ID + * performing this operation is identified in the SEQUENCE + * operation in this compound. + * + * When the client supports GETATTR(fs_locations_info), it can + * be plumbed in here. + */ +static int _nfs41_proc_get_locations(struct inode *inode, +				     struct nfs4_fs_locations *locations, +				     struct page *page, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct rpc_clnt *clnt = server->client; +	u32 bitmask[2] = { +		[0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, +	}; +	struct nfs4_fs_locations_arg args = { +		.fh		= NFS_FH(inode), +		.page		= page, +		.bitmask	= bitmask, +		.migration	= 1,		/* skip LOOKUP */ +	}; +	struct nfs4_fs_locations_res res = { +		.fs_locations	= locations, +		.migration	= 1, +	}; +	struct rpc_message msg = { +		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], +		.rpc_argp	= &args, +		.rpc_resp	= &res, +		.rpc_cred	= cred, +	}; +	int status; + +	nfs_fattr_init(&locations->fattr); +	locations->server = server; +	locations->nlocations = 0; + +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0); +	nfs4_set_sequence_privileged(&args.seq_args); +	status = nfs4_call_sync_sequence(clnt, server, &msg, +					&args.seq_args, &res.seq_res); +	if (status == NFS4_OK && +	    res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED) +		status = -NFS4ERR_LEASE_MOVED; +	return status; +} + +#endif	/* CONFIG_NFS_V4_1 */ + +/** + * nfs4_proc_get_locations - discover locations for a migrated FSID + * @inode: inode on FSID that is migrating + * @locations: result of query + * @page: buffer + * @cred: credential to use for this operation + * + * Returns NFS4_OK on success, a negative NFS4ERR status code if the + * operation failed, or a negative errno if a local error occurred. + * + * On success, "locations" is filled in, but if the server has + * no locations information, NFS_ATTR_FATTR_V4_LOCATIONS is not + * asserted. + * + * -NFS4ERR_LEASE_MOVED is returned if the server still has leases + * from this client that require migration recovery. + */ +int nfs4_proc_get_locations(struct inode *inode, +			    struct nfs4_fs_locations *locations, +			    struct page *page, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct nfs_client *clp = server->nfs_client; +	const struct nfs4_mig_recovery_ops *ops = +					clp->cl_mvops->mig_recovery_ops; +	struct nfs4_exception exception = { }; +	int status; + +	dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__, +		(unsigned long long)server->fsid.major, +		(unsigned long long)server->fsid.minor, +		clp->cl_hostname); +	nfs_display_fhandle(NFS_FH(inode), __func__); + +	do { +		status = ops->get_locations(inode, locations, page, cred); +		if (status != -NFS4ERR_DELAY) +			break; +		nfs4_handle_exception(server, status, &exception); +	} while (exception.retry); +	return status; +} + +/* + * This operation also signals the server that this client is + * performing "lease moved" recovery.  The server can stop + * returning NFS4ERR_LEASE_MOVED to this client.  A RENEW operation + * is appended to this compound to identify the client ID which is + * performing recovery. + */ +static int _nfs40_proc_fsid_present(struct inode *inode, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; +	struct rpc_clnt *clnt = server->client; +	struct nfs4_fsid_present_arg args = { +		.fh		= NFS_FH(inode), +		.clientid	= clp->cl_clientid, +		.renew		= 1,		/* append RENEW */ +	}; +	struct nfs4_fsid_present_res res = { +		.renew		= 1, +	}; +	struct rpc_message msg = { +		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT], +		.rpc_argp	= &args, +		.rpc_resp	= &res, +		.rpc_cred	= cred, +	}; +	unsigned long now = jiffies; +	int status; + +	res.fh = nfs_alloc_fhandle(); +	if (res.fh == NULL) +		return -ENOMEM; + +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0); +	nfs4_set_sequence_privileged(&args.seq_args); +	status = nfs4_call_sync_sequence(clnt, server, &msg, +						&args.seq_args, &res.seq_res); +	nfs_free_fhandle(res.fh); +	if (status) +		return status; + +	do_renew_lease(clp, now); +	return 0; +} + +#ifdef CONFIG_NFS_V4_1 + +/* + * This operation also signals the server that this client is + * performing "lease moved" recovery.  The server can stop asserting + * SEQ4_STATUS_LEASE_MOVED for this client.  The client ID performing + * this operation is identified in the SEQUENCE operation in this + * compound. + */ +static int _nfs41_proc_fsid_present(struct inode *inode, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct rpc_clnt *clnt = server->client; +	struct nfs4_fsid_present_arg args = { +		.fh		= NFS_FH(inode), +	}; +	struct nfs4_fsid_present_res res = { +	}; +	struct rpc_message msg = { +		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT], +		.rpc_argp	= &args, +		.rpc_resp	= &res, +		.rpc_cred	= cred, +	}; +	int status; + +	res.fh = nfs_alloc_fhandle(); +	if (res.fh == NULL) +		return -ENOMEM; + +	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0); +	nfs4_set_sequence_privileged(&args.seq_args); +	status = nfs4_call_sync_sequence(clnt, server, &msg, +						&args.seq_args, &res.seq_res); +	nfs_free_fhandle(res.fh); +	if (status == NFS4_OK && +	    res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED) +		status = -NFS4ERR_LEASE_MOVED; +	return status; +} + +#endif	/* CONFIG_NFS_V4_1 */ + +/** + * nfs4_proc_fsid_present - Is this FSID present or absent on server? + * @inode: inode on FSID to check + * @cred: credential to use for this operation + * + * Server indicates whether the FSID is present, moved, or not + * recognized.  This operation is necessary to clear a LEASE_MOVED + * condition for this client ID. + * + * Returns NFS4_OK if the FSID is present on this server, + * -NFS4ERR_MOVED if the FSID is no longer present, a negative + *  NFS4ERR code if some error occurred on the server, or a + *  negative errno if a local failure occurred. + */ +int nfs4_proc_fsid_present(struct inode *inode, struct rpc_cred *cred) +{ +	struct nfs_server *server = NFS_SERVER(inode); +	struct nfs_client *clp = server->nfs_client; +	const struct nfs4_mig_recovery_ops *ops = +					clp->cl_mvops->mig_recovery_ops; +	struct nfs4_exception exception = { }; +	int status; + +	dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__, +		(unsigned long long)server->fsid.major, +		(unsigned long long)server->fsid.minor, +		clp->cl_hostname); +	nfs_display_fhandle(NFS_FH(inode), __func__); + +	do { +		status = ops->fsid_present(inode, cred); +		if (status != -NFS4ERR_DELAY) +			break; +		nfs4_handle_exception(server, status, &exception); +	} while (exception.retry); +	return status; +} +  /**   * If 'use_integrity' is true and the state managment nfs_client   * cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient @@ -6276,8 +6619,14 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,  	struct nfs41_exchange_id_args args = {  		.verifier = &verifier,  		.client = clp, +#ifdef CONFIG_NFS_V4_1_MIGRATION +		.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER | +			 EXCHGID4_FLAG_BIND_PRINC_STATEID | +			 EXCHGID4_FLAG_SUPP_MOVED_MIGR, +#else  		.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER | -			EXCHGID4_FLAG_BIND_PRINC_STATEID, +			 EXCHGID4_FLAG_BIND_PRINC_STATEID, +#endif  	};  	struct nfs41_exchange_id_res res = {  		0 @@ -7056,9 +7405,9 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)  	struct nfs_server *server = NFS_SERVER(inode);  	struct pnfs_layout_hdr *lo;  	struct nfs4_state *state = NULL; -	unsigned long timeo, giveup; +	unsigned long timeo, now, giveup; -	dprintk("--> %s\n", __func__); +	dprintk("--> %s tk_status => %d\n", __func__, -task->tk_status);  	if (!nfs41_sequence_done(task, &lgp->res.seq_res))  		goto out; @@ -7066,12 +7415,38 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)  	switch (task->tk_status) {  	case 0:  		goto out; +	/* +	 * NFS4ERR_LAYOUTTRYLATER is a conflict with another client +	 * (or clients) writing to the same RAID stripe +	 */  	case -NFS4ERR_LAYOUTTRYLATER: +	/* +	 * NFS4ERR_RECALLCONFLICT is when conflict with self (must recall +	 * existing layout before getting a new one). +	 */  	case -NFS4ERR_RECALLCONFLICT:  		timeo = rpc_get_timeout(task->tk_client);  		giveup = lgp->args.timestamp + timeo; -		if (time_after(giveup, jiffies)) -			task->tk_status = -NFS4ERR_DELAY; +		now = jiffies; +		if (time_after(giveup, now)) { +			unsigned long delay; + +			/* Delay for: +			 * - Not less then NFS4_POLL_RETRY_MIN. +			 * - One last time a jiffie before we give up +			 * - exponential backoff (time_now minus start_attempt) +			 */ +			delay = max_t(unsigned long, NFS4_POLL_RETRY_MIN, +				    min((giveup - now - 1), +					now - lgp->args.timestamp)); + +			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() */ +		}  		break;  	case -NFS4ERR_EXPIRED:  	case -NFS4ERR_BAD_STATEID: @@ -7245,7 +7620,14 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)  		return;  	server = NFS_SERVER(lrp->args.inode); -	if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { +	switch (task->tk_status) { +	default: +		task->tk_status = 0; +	case 0: +		break; +	case -NFS4ERR_DELAY: +		if (nfs4_async_handle_error(task, server, NULL) != -EAGAIN) +			break;  		rpc_restart_call_prepare(task);  		return;  	} @@ -7420,10 +7802,7 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)  	case -NFS4ERR_BADLAYOUT:     /* no layout */  	case -NFS4ERR_GRACE:	    /* loca_recalim always false */  		task->tk_status = 0; -		break;  	case 0: -		nfs_post_op_update_inode_force_wcc(data->args.inode, -						   data->res.fattr);  		break;  	default:  		if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { @@ -7438,6 +7817,8 @@ static void nfs4_layoutcommit_release(void *calldata)  	struct nfs4_layoutcommit_data *data = calldata;  	pnfs_cleanup_layoutcommit(data); +	nfs_post_op_update_inode_force_wcc(data->args.inode, +					   data->res.fattr);  	put_rpccred(data->cred);  	kfree(data);  } @@ -7560,7 +7941,7 @@ nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,  		switch (err) {  		case 0:  		case -NFS4ERR_WRONGSEC: -		case -NFS4ERR_NOTSUPP: +		case -ENOTSUPP:  			goto out;  		default:  			err = nfs4_handle_exception(server, err, &exception); @@ -7594,7 +7975,7 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,  	 * Fall back on "guess and check" method if  	 * the server doesn't support SECINFO_NO_NAME  	 */ -	if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) { +	if (err == -NFS4ERR_WRONGSEC || err == -ENOTSUPP) {  		err = nfs4_find_root_sec(server, fhandle, info);  		goto out_freepage;  	} @@ -7616,6 +7997,9 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,  			break;  		} +		if (!nfs_auth_info_match(&server->auth_info, flavor)) +			flavor = RPC_AUTH_MAXFLAVOR; +  		if (flavor != RPC_AUTH_MAXFLAVOR) {  			err = nfs4_lookup_root_sec(server, fhandle,  						   info, flavor); @@ -7887,6 +8271,18 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {  };  #endif +static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { +	.get_locations = _nfs40_proc_get_locations, +	.fsid_present = _nfs40_proc_fsid_present, +}; + +#if defined(CONFIG_NFS_V4_1) +static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = { +	.get_locations = _nfs41_proc_get_locations, +	.fsid_present = _nfs41_proc_fsid_present, +}; +#endif	/* CONFIG_NFS_V4_1 */ +  static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {  	.minor_version = 0,  	.init_caps = NFS_CAP_READDIRPLUS @@ -7902,6 +8298,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {  	.reboot_recovery_ops = &nfs40_reboot_recovery_ops,  	.nograce_recovery_ops = &nfs40_nograce_recovery_ops,  	.state_renewal_ops = &nfs40_state_renewal_ops, +	.mig_recovery_ops = &nfs40_mig_recovery_ops,  };  #if defined(CONFIG_NFS_V4_1) @@ -7922,6 +8319,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {  	.reboot_recovery_ops = &nfs41_reboot_recovery_ops,  	.nograce_recovery_ops = &nfs41_nograce_recovery_ops,  	.state_renewal_ops = &nfs41_state_renewal_ops, +	.mig_recovery_ops = &nfs41_mig_recovery_ops,  };  #endif @@ -8005,7 +8403,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {  	.unlink_setup	= nfs4_proc_unlink_setup,  	.unlink_rpc_prepare = nfs4_proc_unlink_rpc_prepare,  	.unlink_done	= nfs4_proc_unlink_done, -	.rename		= nfs4_proc_rename,  	.rename_setup	= nfs4_proc_rename_setup,  	.rename_rpc_prepare = nfs4_proc_rename_rpc_prepare,  	.rename_done	= nfs4_proc_rename_done, @@ -8020,13 +8417,10 @@ const struct nfs_rpc_ops nfs_v4_clientops = {  	.pathconf	= nfs4_proc_pathconf,  	.set_capabilities = nfs4_server_capabilities,  	.decode_dirent	= nfs4_decode_dirent, +	.pgio_rpc_prepare = nfs4_proc_pgio_rpc_prepare,  	.read_setup	= nfs4_proc_read_setup, -	.read_pageio_init = pnfs_pageio_init_read, -	.read_rpc_prepare = nfs4_proc_read_rpc_prepare,  	.read_done	= nfs4_read_done,  	.write_setup	= nfs4_proc_write_setup, -	.write_pageio_init = pnfs_pageio_init_write, -	.write_rpc_prepare = nfs4_proc_write_rpc_prepare,  	.write_done	= nfs4_write_done,  	.commit_setup	= nfs4_proc_commit_setup,  	.commit_rpc_prepare = nfs4_proc_commit_rpc_prepare,  | 
