Skip to content

Commit

Permalink
NFS: Add basic migration support to state manager thread
Browse files Browse the repository at this point in the history
Migration recovery and state recovery must be serialized, so handle
both in the state manager thread.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
  • Loading branch information
Chuck Lever authored and Trond Myklebust committed Oct 28, 2013
1 parent ce6cda1 commit c9fdeb2
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 3 deletions.
2 changes: 2 additions & 0 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum nfs4_client_state {
NFS4CLNT_SERVER_SCOPE_MISMATCH,
NFS4CLNT_PURGE_STATE,
NFS4CLNT_BIND_CONN_TO_SESSION,
NFS4CLNT_MOVED,
};

#define NFS4_RENEW_TIMEOUT 0x01
Expand Down Expand Up @@ -421,6 +422,7 @@ extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
extern void nfs4_schedule_state_manager(struct nfs_client *);
extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
extern int nfs4_schedule_migration_recovery(const struct nfs_server *);
extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
extern void nfs41_handle_server_scope(struct nfs_client *,
struct nfs41_server_scope **);
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/nfs4client.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion;
clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
clp->cl_mig_gen = 1;
return clp;

error:
Expand Down
161 changes: 158 additions & 3 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ 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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1197,6 +1197,42 @@ 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);

int nfs4_wait_clnt_recover(struct nfs_client *clp)
{
int res;
Expand Down Expand Up @@ -1826,6 +1862,119 @@ 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;
}

/**
* nfs4_discover_server_trunking - Detect server IP address trunking
*
Expand Down Expand Up @@ -2154,7 +2303,13 @@ 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;
}

/* First recover reboot state... */
Expand Down
7 changes: 7 additions & 0 deletions include/linux/nfs_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct nfs_client {
char cl_ipaddr[48];
u32 cl_cb_ident; /* v4.0 callback identifier */
const struct nfs4_minor_version_ops *cl_mvops;
unsigned long cl_mig_gen;

/* NFSv4.0 transport blocking */
struct nfs4_slot_table *cl_slot_tbl;
Expand Down Expand Up @@ -189,6 +190,12 @@ struct nfs_server {
struct list_head state_owners_lru;
struct list_head layouts;
struct list_head delegations;

unsigned long mig_gen;
unsigned long mig_status;
#define NFS_MIG_IN_TRANSITION (1)
#define NFS_MIG_FAILED (2)

void (*destroy)(struct nfs_server *);

atomic_t active; /* Keep trace of any activity to this server */
Expand Down

0 comments on commit c9fdeb2

Please sign in to comment.