recovery: handle state revocation flags in SEQUENCE response
if any of (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED | SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | SEQ4_STATUS_ADMIN_STATE_REVOKED) are set in the session flags returned by SEQUENCE: * enter client recovery mode * determine which state was lost with TEST_STATEID (consider all delegations, opens, locks, and layouts) * send FREE_STATEID for each stateid revoked * recall all layouts and forget devices (required by 12.7.2: Dealing with Lease Expiration on the Client) * call recover_delegation(), recover_open() or recover_locks() to reclaim each lock Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
ed2ec18d2d
commit
78a0bb0ac5
3 changed files with 195 additions and 17 deletions
|
|
@ -190,6 +190,20 @@ retry:
|
||||||
status = nfs41_session_bump_seq(session, args->sa_slotid);
|
status = nfs41_session_bump_seq(session, args->sa_slotid);
|
||||||
if (status)
|
if (status)
|
||||||
goto out_free_slot;
|
goto out_free_slot;
|
||||||
|
|
||||||
|
if (try_recovery) {
|
||||||
|
/* check status flags for state revocation */
|
||||||
|
uint32_t revoked = seq->sr_resok4.sr_status_flags &
|
||||||
|
(SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED
|
||||||
|
| SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED
|
||||||
|
| SEQ4_STATUS_ADMIN_STATE_REVOKED);
|
||||||
|
|
||||||
|
if (revoked && nfs41_recovery_start_or_wait(session->client)) {
|
||||||
|
/* free stateids and attempt to recover them */
|
||||||
|
nfs41_client_state_revoked(session, session->client, revoked);
|
||||||
|
nfs41_recovery_finish(session->client);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,33 @@ out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int recover_delegation(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_delegation_state *deleg,
|
||||||
|
IN OUT bool_t *grace,
|
||||||
|
IN OUT bool_t *want_supported)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* 10.2.1. Delegation Recovery
|
||||||
|
* When a client needs to reclaim a delegation and there is no
|
||||||
|
* associated open, the client may use the CLAIM_PREVIOUS variant
|
||||||
|
* of the WANT_DELEGATION operation. However, since the server is
|
||||||
|
* not required to support this operation, an alternative is to
|
||||||
|
* reclaim via a dummy OPEN together with the delegation using an
|
||||||
|
* OPEN of type CLAIM_PREVIOUS. */
|
||||||
|
if (*want_supported)
|
||||||
|
status = recover_delegation_want(session, deleg, grace);
|
||||||
|
else
|
||||||
|
status = NFS4ERR_NOTSUPP;
|
||||||
|
|
||||||
|
if (status == NFS4ERR_NOTSUPP) {
|
||||||
|
*want_supported = FALSE;
|
||||||
|
status = recover_delegation_open(session, deleg, grace);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
int nfs41_recover_client_state(
|
int nfs41_recover_client_state(
|
||||||
IN nfs41_session *session,
|
IN nfs41_session *session,
|
||||||
IN nfs41_client *client)
|
IN nfs41_client *client)
|
||||||
|
|
@ -426,24 +453,9 @@ int nfs41_recover_client_state(
|
||||||
/* recover delegations that weren't associated with any opens */
|
/* recover delegations that weren't associated with any opens */
|
||||||
list_for_each(entry, &state->delegations) {
|
list_for_each(entry, &state->delegations) {
|
||||||
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||||
|
|
||||||
/* 10.2.1. Delegation Recovery
|
|
||||||
* When a client needs to reclaim a delegation and there is no
|
|
||||||
* associated open, the client may use the CLAIM_PREVIOUS variant
|
|
||||||
* of the WANT_DELEGATION operation. However, since the server is
|
|
||||||
* not required to support this operation, an alternative is to
|
|
||||||
* reclaim via a dummy OPEN together with the delegation using an
|
|
||||||
* OPEN of type CLAIM_PREVIOUS. */
|
|
||||||
if (deleg->revoked) {
|
if (deleg->revoked) {
|
||||||
if (want_supported)
|
status = recover_delegation(session,
|
||||||
status = recover_delegation_want(session, deleg, &grace);
|
deleg, &grace, &want_supported);
|
||||||
else
|
|
||||||
status = NFS4ERR_NOTSUPP;
|
|
||||||
|
|
||||||
if (status == NFS4ERR_NOTSUPP) {
|
|
||||||
want_supported = FALSE;
|
|
||||||
status = recover_delegation_open(session, deleg, &grace);
|
|
||||||
}
|
|
||||||
if (status == NFS4ERR_BADSESSION)
|
if (status == NFS4ERR_BADSESSION)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
@ -467,6 +479,153 @@ unlock:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t stateid_array(
|
||||||
|
IN struct list_entry *delegations,
|
||||||
|
IN struct list_entry *opens,
|
||||||
|
OUT stateid_arg **stateids_out,
|
||||||
|
OUT uint32_t **statuses_out)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
nfs41_open_state *open;
|
||||||
|
nfs41_delegation_state *deleg;
|
||||||
|
stateid_arg *stateids = NULL;
|
||||||
|
uint32_t *statuses = NULL;
|
||||||
|
uint32_t i = 0, count = 0;
|
||||||
|
|
||||||
|
/* count how many stateids the client needs to test */
|
||||||
|
list_for_each(entry, delegations)
|
||||||
|
count++;
|
||||||
|
list_for_each(entry, opens)
|
||||||
|
count += 3; /* open and potentially lock and layout */
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* allocate the stateid and status arrays */
|
||||||
|
stateids = calloc(count, sizeof(stateid_arg));
|
||||||
|
if (stateids == NULL)
|
||||||
|
goto out_err;
|
||||||
|
statuses = calloc(count, sizeof(uint32_t));
|
||||||
|
if (statuses == NULL)
|
||||||
|
goto out_err;
|
||||||
|
memset(statuses, NFS4ERR_BAD_STATEID, count * sizeof(uint32_t));
|
||||||
|
|
||||||
|
/* copy stateids into the array */
|
||||||
|
list_for_each(entry, delegations) {
|
||||||
|
deleg = list_container(entry, nfs41_delegation_state, client_entry);
|
||||||
|
AcquireSRWLockShared(&deleg->lock);
|
||||||
|
/* delegation stateid */
|
||||||
|
memcpy(&stateids[i].stateid, &deleg->state.stateid, sizeof(stateid4));
|
||||||
|
stateids[i].type = STATEID_DELEG_FILE;
|
||||||
|
stateids[i].delegation = deleg;
|
||||||
|
i++;
|
||||||
|
ReleaseSRWLockShared(&deleg->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each(entry, opens) {
|
||||||
|
open = list_container(entry, nfs41_open_state, client_entry);
|
||||||
|
|
||||||
|
AcquireSRWLockShared(&open->lock);
|
||||||
|
/* open stateid */
|
||||||
|
memcpy(&stateids[i].stateid, &open->stateid, sizeof(stateid4));
|
||||||
|
stateids[i].type = STATEID_OPEN;
|
||||||
|
stateids[i].open = open;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (open->locks.stateid.seqid) { /* lock stateid? */
|
||||||
|
memcpy(&stateids[i].stateid, &open->locks.stateid, sizeof(stateid4));
|
||||||
|
stateids[i].type = STATEID_LOCK;
|
||||||
|
stateids[i].open = open;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open->layout) { /* layout stateid? */
|
||||||
|
AcquireSRWLockShared(&open->layout->lock);
|
||||||
|
if (open->layout->status & PNFS_LAYOUT_GRANTED) {
|
||||||
|
memcpy(&stateids[i].stateid, &open->layout->stateid, sizeof(stateid4));
|
||||||
|
stateids[i].type = STATEID_LAYOUT;
|
||||||
|
stateids[i].open = open;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&open->layout->lock);
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&open->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
count = i;
|
||||||
|
*stateids_out = stateids;
|
||||||
|
*statuses_out = statuses;
|
||||||
|
out:
|
||||||
|
return count;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
free(stateids);
|
||||||
|
free(statuses);
|
||||||
|
count = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfs41_client_state_revoked(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_client *client,
|
||||||
|
IN uint32_t revoked)
|
||||||
|
{
|
||||||
|
const struct cb_layoutrecall_args recall = { PNFS_LAYOUTTYPE_FILE,
|
||||||
|
PNFS_IOMODE_ANY, TRUE, { PNFS_RETURN_ALL } };
|
||||||
|
struct client_state *clientstate = &session->client->state;
|
||||||
|
stateid_arg *stateids = NULL;
|
||||||
|
uint32_t *statuses = NULL;
|
||||||
|
uint32_t i, count;
|
||||||
|
bool_t grace = TRUE;
|
||||||
|
bool_t want_supported = TRUE;
|
||||||
|
|
||||||
|
EnterCriticalSection(&clientstate->lock);
|
||||||
|
|
||||||
|
/* get an array of the client's stateids */
|
||||||
|
count = stateid_array(&clientstate->delegations,
|
||||||
|
&clientstate->opens, &stateids, &statuses);
|
||||||
|
if (count == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* determine which stateids were revoked with TEST_STATEID */
|
||||||
|
if ((revoked & SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED) == 0)
|
||||||
|
nfs41_test_stateid(session, stateids, count, statuses);
|
||||||
|
|
||||||
|
/* free all revoked stateids with FREE_STATEID */
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (statuses[i])
|
||||||
|
nfs41_free_stateid(session, &stateids[i].stateid);
|
||||||
|
|
||||||
|
/* revoke all of the client's layouts */
|
||||||
|
pnfs_file_layout_recall(client, &recall);
|
||||||
|
|
||||||
|
/* recover the revoked stateids */
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (statuses[i]) {
|
||||||
|
if (stateids[i].type == STATEID_DELEG_FILE)
|
||||||
|
stateids[i].delegation->revoked = TRUE;
|
||||||
|
else if (stateids[i].type == STATEID_OPEN)
|
||||||
|
recover_open(session, stateids[i].open, &grace);
|
||||||
|
else if (stateids[i].type == STATEID_LOCK)
|
||||||
|
recover_locks(session, stateids[i].open, &grace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
/* delegations that weren't recovered by recover_open() */
|
||||||
|
if (statuses[i] && stateids[i].type == STATEID_DELEG_FILE
|
||||||
|
&& stateids[i].delegation->revoked)
|
||||||
|
recover_delegation(session, stateids[i].delegation,
|
||||||
|
&grace, &want_supported);
|
||||||
|
}
|
||||||
|
|
||||||
|
nfs41_client_delegation_recovery(client);
|
||||||
|
out:
|
||||||
|
LeaveCriticalSection(&clientstate->lock);
|
||||||
|
free(stateids);
|
||||||
|
free(statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool_t recover_stateid_open(
|
static bool_t recover_stateid_open(
|
||||||
IN nfs_argop4 *argop,
|
IN nfs_argop4 *argop,
|
||||||
IN stateid_arg *stateid)
|
IN stateid_arg *stateid)
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,11 @@ int nfs41_recover_client_state(
|
||||||
IN nfs41_session *session,
|
IN nfs41_session *session,
|
||||||
IN nfs41_client *client);
|
IN nfs41_client *client);
|
||||||
|
|
||||||
|
void nfs41_client_state_revoked(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_client *client,
|
||||||
|
IN uint32_t revoked);
|
||||||
|
|
||||||
struct __nfs_argop4;
|
struct __nfs_argop4;
|
||||||
bool_t nfs41_recover_stateid(
|
bool_t nfs41_recover_stateid(
|
||||||
IN nfs41_session *session,
|
IN nfs41_session *session,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue