diff options
Diffstat (limited to 'fs/nfs/nfs4state.c')
| -rw-r--r-- | fs/nfs/nfs4state.c | 266 | 
1 files changed, 246 insertions, 20 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index cc14cbb78b73..74a7e12e10df 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -239,14 +239,12 @@ static void nfs4_end_drain_session(struct nfs_client *clp)  	}  } -#if defined(CONFIG_NFS_V4_1) -  static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)  {  	set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);  	spin_lock(&tbl->slot_tbl_lock);  	if (tbl->highest_used_slotid != NFS4_NO_SLOT) { -		INIT_COMPLETION(tbl->complete); +		reinit_completion(&tbl->complete);  		spin_unlock(&tbl->slot_tbl_lock);  		return wait_for_completion_interruptible(&tbl->complete);  	} @@ -270,6 +268,8 @@ static int nfs4_begin_drain_session(struct nfs_client *clp)  	return nfs4_drain_slot_tbl(&ses->fc_slot_table);  } +#if defined(CONFIG_NFS_V4_1) +  static int nfs41_setup_state_renewal(struct nfs_client *clp)  {  	int status; @@ -1197,20 +1197,74 @@ void nfs4_schedule_lease_recovery(struct nfs_client *clp)  }  EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery); +/** + * nfs4_schedule_migration_recovery - trigger migration recovery + * + * @server: FSID that is migrating + * + * Returns zero if recovery has started, otherwise a negative NFS4ERR + * value is returned. + */ +int nfs4_schedule_migration_recovery(const struct nfs_server *server) +{ +	struct nfs_client *clp = server->nfs_client; + +	if (server->fh_expire_type != NFS4_FH_PERSISTENT) { +		pr_err("NFS: volatile file handles not supported (server %s)\n", +				clp->cl_hostname); +		return -NFS4ERR_IO; +	} + +	if (test_bit(NFS_MIG_FAILED, &server->mig_status)) +		return -NFS4ERR_IO; + +	dprintk("%s: scheduling migration recovery for (%llx:%llx) on %s\n", +			__func__, +			(unsigned long long)server->fsid.major, +			(unsigned long long)server->fsid.minor, +			clp->cl_hostname); + +	set_bit(NFS_MIG_IN_TRANSITION, +			&((struct nfs_server *)server)->mig_status); +	set_bit(NFS4CLNT_MOVED, &clp->cl_state); + +	nfs4_schedule_state_manager(clp); +	return 0; +} +EXPORT_SYMBOL_GPL(nfs4_schedule_migration_recovery); + +/** + * nfs4_schedule_lease_moved_recovery - start lease-moved recovery + * + * @clp: server to check for moved leases + * + */ +void nfs4_schedule_lease_moved_recovery(struct nfs_client *clp) +{ +	dprintk("%s: scheduling lease-moved recovery for client ID %llx on %s\n", +		__func__, clp->cl_clientid, clp->cl_hostname); + +	set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state); +	nfs4_schedule_state_manager(clp); +} +EXPORT_SYMBOL_GPL(nfs4_schedule_lease_moved_recovery); +  int nfs4_wait_clnt_recover(struct nfs_client *clp)  {  	int res;  	might_sleep(); +	atomic_inc(&clp->cl_count);  	res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,  			nfs_wait_bit_killable, TASK_KILLABLE);  	if (res) -		return res; - +		goto out;  	if (clp->cl_cons_state < 0) -		return clp->cl_cons_state; -	return 0; +		res = clp->cl_cons_state; +out: +	nfs_put_client(clp); +	return res;  }  int nfs4_client_recover_expired_lease(struct nfs_client *clp) @@ -1375,8 +1429,8 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_  			case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:  				goto out;  			default: -				printk(KERN_ERR "NFS: %s: unhandled error %d. " -					"Zeroing state\n", __func__, status); +				printk(KERN_ERR "NFS: %s: unhandled error %d\n", +					 __func__, status);  			case -ENOMEM:  			case -NFS4ERR_DENIED:  			case -NFS4ERR_RECLAIM_BAD: @@ -1422,7 +1476,7 @@ restart:  		if (status >= 0) {  			status = nfs4_reclaim_locks(state, ops);  			if (status >= 0) { -				if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) { +				if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) {  					spin_lock(&state->state_lock);  					list_for_each_entry(lock, &state->lock_states, ls_locks) {  						if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) @@ -1439,15 +1493,12 @@ restart:  		}  		switch (status) {  			default: -				printk(KERN_ERR "NFS: %s: unhandled error %d. " -					"Zeroing state\n", __func__, status); +				printk(KERN_ERR "NFS: %s: unhandled error %d\n", +					__func__, status);  			case -ENOENT:  			case -ENOMEM:  			case -ESTALE: -				/* -				 * Open state on this file cannot be recovered -				 * All we can do is revert to using the zero stateid. -				 */ +				/* Open state on this file cannot be recovered */  				nfs4_state_mark_recovery_failed(state, status);  				break;  			case -EAGAIN: @@ -1628,7 +1679,6 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)  			nfs4_state_end_reclaim_reboot(clp);  			break;  		case -NFS4ERR_STALE_CLIENTID: -		case -NFS4ERR_LEASE_MOVED:  			set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);  			nfs4_state_clear_reclaim_reboot(clp);  			nfs4_state_start_reclaim_reboot(clp); @@ -1829,6 +1879,168 @@ static int nfs4_purge_lease(struct nfs_client *clp)  	return 0;  } +/* + * Try remote migration of one FSID from a source server to a + * destination server.  The source server provides a list of + * potential destinations. + * + * Returns zero or a negative NFS4ERR status code. + */ +static int nfs4_try_migration(struct nfs_server *server, struct rpc_cred *cred) +{ +	struct nfs_client *clp = server->nfs_client; +	struct nfs4_fs_locations *locations = NULL; +	struct inode *inode; +	struct page *page; +	int status, result; + +	dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__, +			(unsigned long long)server->fsid.major, +			(unsigned long long)server->fsid.minor, +			clp->cl_hostname); + +	result = 0; +	page = alloc_page(GFP_KERNEL); +	locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); +	if (page == NULL || locations == NULL) { +		dprintk("<-- %s: no memory\n", __func__); +		goto out; +	} + +	inode = server->super->s_root->d_inode; +	result = nfs4_proc_get_locations(inode, locations, page, cred); +	if (result) { +		dprintk("<-- %s: failed to retrieve fs_locations: %d\n", +			__func__, result); +		goto out; +	} + +	result = -NFS4ERR_NXIO; +	if (!(locations->fattr.valid & NFS_ATTR_FATTR_V4_LOCATIONS)) { +		dprintk("<-- %s: No fs_locations data, migration skipped\n", +			__func__); +		goto out; +	} + +	nfs4_begin_drain_session(clp); + +	status = nfs4_replace_transport(server, locations); +	if (status != 0) { +		dprintk("<-- %s: failed to replace transport: %d\n", +			__func__, status); +		goto out; +	} + +	result = 0; +	dprintk("<-- %s: migration succeeded\n", __func__); + +out: +	if (page != NULL) +		__free_page(page); +	kfree(locations); +	if (result) { +		pr_err("NFS: migration recovery failed (server %s)\n", +				clp->cl_hostname); +		set_bit(NFS_MIG_FAILED, &server->mig_status); +	} +	return result; +} + +/* + * Returns zero or a negative NFS4ERR status code. + */ +static int nfs4_handle_migration(struct nfs_client *clp) +{ +	const struct nfs4_state_maintenance_ops *ops = +				clp->cl_mvops->state_renewal_ops; +	struct nfs_server *server; +	struct rpc_cred *cred; + +	dprintk("%s: migration reported on \"%s\"\n", __func__, +			clp->cl_hostname); + +	spin_lock(&clp->cl_lock); +	cred = ops->get_state_renewal_cred_locked(clp); +	spin_unlock(&clp->cl_lock); +	if (cred == NULL) +		return -NFS4ERR_NOENT; + +	clp->cl_mig_gen++; +restart: +	rcu_read_lock(); +	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { +		int status; + +		if (server->mig_gen == clp->cl_mig_gen) +			continue; +		server->mig_gen = clp->cl_mig_gen; + +		if (!test_and_clear_bit(NFS_MIG_IN_TRANSITION, +						&server->mig_status)) +			continue; + +		rcu_read_unlock(); +		status = nfs4_try_migration(server, cred); +		if (status < 0) { +			put_rpccred(cred); +			return status; +		} +		goto restart; +	} +	rcu_read_unlock(); +	put_rpccred(cred); +	return 0; +} + +/* + * Test each nfs_server on the clp's cl_superblocks list to see + * if it's moved to another server.  Stop when the server no longer + * returns NFS4ERR_LEASE_MOVED. + */ +static int nfs4_handle_lease_moved(struct nfs_client *clp) +{ +	const struct nfs4_state_maintenance_ops *ops = +				clp->cl_mvops->state_renewal_ops; +	struct nfs_server *server; +	struct rpc_cred *cred; + +	dprintk("%s: lease moved reported on \"%s\"\n", __func__, +			clp->cl_hostname); + +	spin_lock(&clp->cl_lock); +	cred = ops->get_state_renewal_cred_locked(clp); +	spin_unlock(&clp->cl_lock); +	if (cred == NULL) +		return -NFS4ERR_NOENT; + +	clp->cl_mig_gen++; +restart: +	rcu_read_lock(); +	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { +		struct inode *inode; +		int status; + +		if (server->mig_gen == clp->cl_mig_gen) +			continue; +		server->mig_gen = clp->cl_mig_gen; + +		rcu_read_unlock(); + +		inode = server->super->s_root->d_inode; +		status = nfs4_proc_fsid_present(inode, cred); +		if (status != -NFS4ERR_MOVED) +			goto restart;	/* wasn't this one */ +		if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED) +			goto restart;	/* there are more */ +		goto out; +	} +	rcu_read_unlock(); + +out: +	put_rpccred(cred); +	return 0; +} +  /**   * nfs4_discover_server_trunking - Detect server IP address trunking   * @@ -2017,9 +2229,10 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)  		nfs41_handle_server_reboot(clp);  	if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |  			    SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | -			    SEQ4_STATUS_ADMIN_STATE_REVOKED | -			    SEQ4_STATUS_LEASE_MOVED)) +			    SEQ4_STATUS_ADMIN_STATE_REVOKED))  		nfs41_handle_state_revoked(clp); +	if (flags & SEQ4_STATUS_LEASE_MOVED) +		nfs4_schedule_lease_moved_recovery(clp);  	if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)  		nfs41_handle_recallable_state_revoked(clp);  	if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT) @@ -2157,7 +2370,20 @@ static void nfs4_state_manager(struct nfs_client *clp)  			status = nfs4_check_lease(clp);  			if (status < 0)  				goto out_error; -			continue; +		} + +		if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state)) { +			section = "migration"; +			status = nfs4_handle_migration(clp); +			if (status < 0) +				goto out_error; +		} + +		if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state)) { +			section = "lease moved"; +			status = nfs4_handle_lease_moved(clp); +			if (status < 0) +				goto out_error;  		}  		/* First recover reboot state... */  |