recovery: reboot recovery of delegation stateids

recover_client_state() flags all delegations as revoked before starting open state recovery.  if recover_open() finds that its delegation is revoked, it attempts to recover it using CLAIM_PREVIOUS.  if its delegation has already been reclaimed by another open, it can skip reclaiming the open stateid (provided it has no byte-range locks to reclaim).  after all opens have been reclaimed, any delegations still marked 'revoked' are passed to recover_delegation().  recover_delegation() also uses CLAIM_PREVIOUS (or CLAIM_NULL outside of the grace period) to reclaim the delegation, but has to throw away the open stateid with CLOSE

added a try_recovery argument to nfs41_delegreturn() and nfs41_delegation_granted(), so it can be called by recover_open() if OPEN grants an unexpected open

Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2011-07-19 11:04:53 -04:00 committed by unknown
parent 3734527757
commit 4f47ae9a37
7 changed files with 228 additions and 32 deletions

View file

@ -32,6 +32,7 @@
/* allocation and reference counting */ /* allocation and reference counting */
static int delegation_create( static int delegation_create(
IN const nfs41_path_fh *parent,
IN const nfs41_path_fh *file, IN const nfs41_path_fh *file,
IN const open_delegation4 *delegation, IN const open_delegation4 *delegation,
OUT nfs41_delegation_state **deleg_out) OUT nfs41_delegation_state **deleg_out)
@ -46,9 +47,15 @@ static int delegation_create(
} }
memcpy(&state->state, delegation, sizeof(open_delegation4)); memcpy(&state->state, delegation, sizeof(open_delegation4));
abs_path_copy(&state->path, file->path); abs_path_copy(&state->path, file->path);
path_fh_init(&state->file, &state->path); path_fh_init(&state->file, &state->path);
fh_copy(&state->file.fh, &file->fh); fh_copy(&state->file.fh, &file->fh);
path_fh_init(&state->parent, &state->path);
last_component(state->path.path, state->file.name.name,
&state->parent.name);
fh_copy(&state->parent.fh, &parent->fh);
list_init(&state->client_entry); list_init(&state->client_entry);
state->status = DELEGATION_GRANTED; state->status = DELEGATION_GRANTED;
InitializeSRWLock(&state->lock); InitializeSRWLock(&state->lock);
@ -132,7 +139,8 @@ static void delegation_return(
/* TODO: flush data and metadata before returning delegation */ /* TODO: flush data and metadata before returning delegation */
nfs41_delegreturn(client->session, &deleg->file, &deleg->state.stateid); nfs41_delegreturn(client->session, &deleg->file,
&deleg->state.stateid, TRUE);
/* remove from the client's list */ /* remove from the client's list */
EnterCriticalSection(&client->state.lock); EnterCriticalSection(&client->state.lock);
@ -164,21 +172,25 @@ static void delegation_return(
/* open delegation */ /* open delegation */
int nfs41_delegation_granted( int nfs41_delegation_granted(
IN nfs41_session *session, IN nfs41_session *session,
IN nfs41_path_fh *parent,
IN nfs41_path_fh *file, IN nfs41_path_fh *file,
IN open_delegation4 *delegation, IN open_delegation4 *delegation,
IN bool_t try_recovery,
OUT nfs41_delegation_state **deleg_out) OUT nfs41_delegation_state **deleg_out)
{ {
nfs41_client *client = session->client; nfs41_client *client = session->client;
nfs41_delegation_state *state; nfs41_delegation_state *state;
int status = NO_ERROR; int status = NO_ERROR;
if (delegation->recalled || if (delegation->type != OPEN_DELEGATE_READ &&
delegation->type == OPEN_DELEGATE_NONE || delegation->type != OPEN_DELEGATE_WRITE)
delegation->type == OPEN_DELEGATE_NONE_EXT)
goto out; goto out;
if (delegation->recalled)
goto out_return;
/* allocate the delegation state */ /* allocate the delegation state */
status = delegation_create(file, delegation, &state); status = delegation_create(parent, file, delegation, &state);
if (status) if (status)
goto out_return; goto out_return;
@ -194,7 +206,7 @@ out:
return status; return status;
out_return: /* return the delegation on failure */ out_return: /* return the delegation on failure */
nfs41_delegreturn(session, file, &delegation->stateid); nfs41_delegreturn(session, file, &delegation->stateid, try_recovery);
goto out; goto out;
} }
@ -439,7 +451,7 @@ int nfs41_delegation_return(
} }
} else { } else {
/* the delegation is being returned, wait for it to finish */ /* the delegation is being returned, wait for it to finish */
while (deleg->status != DELEGATION_RETURNING) while (deleg->status != DELEGATION_RETURNED)
SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0); SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0);
status = NFS4ERR_BADHANDLE; status = NFS4ERR_BADHANDLE;
} }

View file

@ -45,8 +45,10 @@ void nfs41_client_delegation_free(
/* open delegation */ /* open delegation */
int nfs41_delegation_granted( int nfs41_delegation_granted(
IN nfs41_session *session, IN nfs41_session *session,
IN nfs41_path_fh *parent,
IN nfs41_path_fh *file, IN nfs41_path_fh *file,
IN open_delegation4 *delegation, IN open_delegation4 *delegation,
IN bool_t try_recovery,
OUT nfs41_delegation_state **deleg_out); OUT nfs41_delegation_state **deleg_out);
int nfs41_delegate_open( int nfs41_delegate_open(

View file

@ -89,6 +89,7 @@ enum delegation_status {
typedef struct __nfs41_delegation_state { typedef struct __nfs41_delegation_state {
open_delegation4 state; open_delegation4 state;
nfs41_abs_path path; nfs41_abs_path path;
nfs41_path_fh parent;
nfs41_path_fh file; nfs41_path_fh file;
struct list_entry client_entry; /* entry in nfs41_client.delegations */ struct list_entry client_entry; /* entry in nfs41_client.delegations */
LONG ref_count; LONG ref_count;
@ -96,6 +97,8 @@ typedef struct __nfs41_delegation_state {
enum delegation_status status; enum delegation_status status;
SRWLOCK lock; SRWLOCK lock;
CONDITION_VARIABLE cond; CONDITION_VARIABLE cond;
bool_t revoked; /* for recovery, accessed under client.state.lock */
} nfs41_delegation_state; } nfs41_delegation_state;
typedef struct __nfs41_lock_state { typedef struct __nfs41_lock_state {

View file

@ -1530,7 +1530,8 @@ out:
int nfs41_delegreturn( int nfs41_delegreturn(
IN nfs41_session *session, IN nfs41_session *session,
IN nfs41_path_fh *file, IN nfs41_path_fh *file,
IN stateid4 *stateid) IN stateid4 *stateid,
IN bool_t try_recovery)
{ {
int status; int status;
nfs41_compound compound; nfs41_compound compound;
@ -1557,7 +1558,7 @@ int nfs41_delegreturn(
compound_add_op(&compound, OP_DELEGRETURN, &dr_args, &dr_res); compound_add_op(&compound, OP_DELEGRETURN, &dr_args, &dr_res);
dr_args.stateid = stateid; dr_args.stateid = stateid;
status = compound_encode_send_decode(session, &compound, TRUE); status = compound_encode_send_decode(session, &compound, try_recovery);
if (status) if (status)
goto out; goto out;

View file

@ -1154,7 +1154,8 @@ int nfs41_access(
int nfs41_delegreturn( int nfs41_delegreturn(
IN nfs41_session *session, IN nfs41_session *session,
IN nfs41_path_fh *file, IN nfs41_path_fh *file,
IN stateid4 *stateid); IN stateid4 *stateid,
IN bool_t try_recovery);
enum nfsstat4 nfs41_fs_locations( enum nfsstat4 nfs41_fs_locations(
IN nfs41_session *session, IN nfs41_session *session,

View file

@ -209,8 +209,8 @@ static int do_open(
goto out; goto out;
/* allocate delegation state and register it with the client */ /* allocate delegation state and register it with the client */
nfs41_delegation_granted(state->session, nfs41_delegation_granted(state->session, &state->parent,
&state->file, &delegation, &deleg_state); &state->file, &delegation, TRUE, &deleg_state);
AcquireSRWLockExclusive(&state->lock); AcquireSRWLockExclusive(&state->lock);
/* update the stateid */ /* update the stateid */

View file

@ -21,7 +21,10 @@
* such damages. * such damages.
*/ */
#include <time.h>
#include "recovery.h" #include "recovery.h"
#include "delegation.h"
#include "nfs41_callback.h" #include "nfs41_callback.h"
#include "nfs41_compound.h" #include "nfs41_compound.h"
#include "nfs41_ops.h" #include "nfs41_ops.h"
@ -63,44 +66,131 @@ void nfs41_recovery_finish(
LeaveCriticalSection(&client->recovery.lock); LeaveCriticalSection(&client->recovery.lock);
} }
/* client state recovery for server reboot or lease expiration */
static int recover_open_grace(
IN nfs41_session *session,
IN nfs41_path_fh *parent,
IN nfs41_path_fh *file,
IN state_owner4 *owner,
IN uint32_t access,
IN uint32_t deny,
IN enum open_delegation_type4 delegate_type,
OUT stateid4 *stateid,
OUT open_delegation4 *delegation)
{
/* reclaim the open stateid with CLAIM_PREVIOUS */
open_claim4 claim;
claim.claim = CLAIM_PREVIOUS;
claim.u.prev.delegate_type = delegate_type;
return nfs41_open(session, parent, file, owner,
&claim, access, deny, OPEN4_NOCREATE, 0, 0, FALSE,
stateid, delegation, NULL);
}
static int recover_open_no_grace(
IN nfs41_session *session,
IN nfs41_path_fh *parent,
IN nfs41_path_fh *file,
IN state_owner4 *owner,
IN uint32_t access,
IN uint32_t deny,
IN enum open_delegation_type4 delegate_type,
OUT stateid4 *stateid,
OUT open_delegation4 *delegation)
{
open_claim4 claim;
/* TODO: try CLAIM_DELEGATE_PREV / CLAIM_DELEG_PREV_FH first */
/* attempt out-of-grace recovery with CLAIM_NULL */
claim.claim = CLAIM_NULL;
claim.u.null.filename = &file->name;
/* ask nicely for the delegation we had */
if (delegate_type == OPEN_DELEGATE_READ)
access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
else if (delegate_type == OPEN_DELEGATE_WRITE)
access |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
return nfs41_open(session, parent, file, owner,
&claim, access, deny, OPEN4_NOCREATE, 0, 0, FALSE,
stateid, delegation, NULL);
}
static int recover_open( static int recover_open(
IN nfs41_session *session, IN nfs41_session *session,
IN nfs41_open_state *open, IN nfs41_open_state *open,
IN OUT bool_t *grace) IN OUT bool_t *grace)
{ {
open_claim4 claim; open_delegation4 delegation = { 0 };
open_delegation4 delegation;
stateid4 stateid; stateid4 stateid;
int status; enum open_delegation_type4 delegate_type = OPEN_DELEGATE_NONE;
int status = NFS4ERR_BADHANDLE;
/* reclaim the open stateid */ /* check for an associated delegation */
claim.claim = CLAIM_PREVIOUS; AcquireSRWLockExclusive(&open->lock);
claim.u.prev.delegate_type = OPEN_DELEGATE_NONE; if (open->delegation.state) {
nfs41_delegation_state *deleg = open->delegation.state;
if (deleg->revoked) {
/* reclaim the delegation along with the open */
AcquireSRWLockShared(&deleg->lock);
delegate_type = deleg->state.type;
ReleaseSRWLockShared(&deleg->lock);
} else if (deleg->state.recalled) {
/* we'll need an open stateid regardless */
} else if (list_empty(&open->locks.list)) {
/* if there are locks, we need an open stateid to
* reclaim them; otherwise, the open can be delegated */
open->do_close = FALSE;
status = NFS4_OK;
}
}
ReleaseSRWLockExclusive(&open->lock);
if (status == NFS4_OK) /* use existing delegation */
goto out;
if (*grace) if (*grace)
status = nfs41_open(session, &open->parent, &open->file, status = recover_open_grace(session, &open->parent, &open->file,
&open->owner, &claim, open->share_access, open->share_deny, &open->owner, open->share_access, open->share_deny,
OPEN4_NOCREATE, 0, 0, FALSE, &stateid, &delegation, NULL); delegate_type, &stateid, &delegation);
else else
status = NFS4ERR_NO_GRACE; status = NFS4ERR_NO_GRACE;
if (status == NFS4ERR_NO_GRACE) { if (status == NFS4ERR_NO_GRACE) {
*grace = FALSE; *grace = FALSE;
/* attempt out-of-grace recovery with CLAIM_NULL */ status = recover_open_no_grace(session, &open->parent, &open->file,
claim.claim = CLAIM_NULL; &open->owner, open->share_access, open->share_deny,
claim.u.null.filename = &open->file.name; delegate_type, &stateid, &delegation);
status = nfs41_open(session, &open->parent, &open->file,
&open->owner, &claim, open->share_access, open->share_deny,
OPEN4_NOCREATE, 0, 0, FALSE, &stateid, &delegation, NULL);
} }
if (status) if (status)
goto out; goto out;
/* update the open stateid on success */
AcquireSRWLockExclusive(&open->lock); AcquireSRWLockExclusive(&open->lock);
/* update the open stateid */
memcpy(&open->stateid, &stateid, sizeof(stateid4)); memcpy(&open->stateid, &stateid, sizeof(stateid4));
open->layout = NULL; open->do_close = TRUE;
if (open->delegation.state) {
nfs41_delegation_state *deleg = open->delegation.state;
if (deleg->revoked) {
/* update delegation state */
AcquireSRWLockExclusive(&deleg->lock);
if (delegation.type != OPEN_DELEGATE_READ &&
delegation.type != OPEN_DELEGATE_WRITE) {
eprintf("recover_open() got delegation type %u, "
"expected %u\n", delegation.type, deleg->state.type);
} else {
memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
deleg->revoked = FALSE;
}
ReleaseSRWLockExclusive(&deleg->lock);
}
} else /* granted a new delegation? */
nfs41_delegation_granted(session, &open->parent, &open->file,
&delegation, FALSE, &open->delegation.state);
ReleaseSRWLockExclusive(&open->lock); ReleaseSRWLockExclusive(&open->lock);
out: out:
return status; return status;
@ -155,6 +245,69 @@ static int recover_locks(
return status; return status;
} }
/* delegation recovery via OPEN (requires corresponding CLOSE) */
static int recover_delegation_open(
IN nfs41_session *session,
IN nfs41_delegation_state *deleg,
IN OUT bool_t *grace)
{
state_owner4 owner;
open_delegation4 delegation = { 0 };
stateid_arg stateid;
uint32_t access = OPEN4_SHARE_ACCESS_READ;
uint32_t deny = OPEN4_SHARE_DENY_NONE;
enum open_delegation_type4 delegate_type = OPEN_DELEGATE_NONE;
int status = NFS4_OK;
/* choose the desired access mode based on delegation type */
AcquireSRWLockShared(&deleg->lock);
delegate_type = deleg->state.type;
if (delegate_type == OPEN_DELEGATE_WRITE)
access |= OPEN4_SHARE_ACCESS_WRITE | OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
else
access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
ReleaseSRWLockShared(&deleg->lock);
/* construct a temporary open owner by concatenating the time
* in seconds with the delegation pointer */
time((time_t*)owner.owner);
memcpy(owner.owner + sizeof(time_t), deleg, sizeof(deleg));
owner.owner_len = sizeof(time_t) + sizeof(deleg);
if (*grace)
status = recover_open_grace(session, &deleg->parent, &deleg->file,
&owner, access, deny, delegate_type, &stateid.stateid, &delegation);
else
status = NFS4ERR_NO_GRACE;
if (status == NFS4ERR_NO_GRACE) {
*grace = FALSE;
status = recover_open_no_grace(session, &deleg->parent, &deleg->file,
&owner, access, deny, delegate_type, &stateid.stateid, &delegation);
}
if (status)
goto out;
/* update delegation state */
AcquireSRWLockExclusive(&deleg->lock);
if (delegation.type != OPEN_DELEGATE_READ &&
delegation.type != OPEN_DELEGATE_WRITE) {
eprintf("recover_delegation_open() got delegation type %u, "
"expected %u\n", delegation.type, deleg->state.type);
} else {
memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
deleg->revoked = FALSE;
}
ReleaseSRWLockExclusive(&deleg->lock);
/* send CLOSE to free the open stateid */
stateid.open = NULL;
stateid.type = STATEID_OPEN;
nfs41_close(session, &deleg->file, &stateid);
out:
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)
@ -164,19 +317,43 @@ int nfs41_recover_client_state(
struct client_state *state = &session->client->state; struct client_state *state = &session->client->state;
struct list_entry *entry; struct list_entry *entry;
nfs41_open_state *open; nfs41_open_state *open;
nfs41_delegation_state *deleg;
bool_t grace = TRUE; bool_t grace = TRUE;
int status = NFS4_OK; int status = NFS4_OK;
/* recover each of the client's opens */
EnterCriticalSection(&state->lock); EnterCriticalSection(&state->lock);
/* flag all delegations as revoked until successful recovery;
* recover_open() and recover_delegation_open() will only ask
* for delegations when revoked = TRUE */
list_for_each(entry, &state->delegations) {
deleg = list_container(entry, nfs41_delegation_state, client_entry);
deleg->revoked = TRUE;
}
/* recover each of the client's opens and associated delegations */
list_for_each(entry, &state->opens) { list_for_each(entry, &state->opens) {
open = list_container(entry, nfs41_open_state, client_entry); open = list_container(entry, nfs41_open_state, client_entry);
status = recover_open(session, open, &grace); status = recover_open(session, open, &grace);
if (status == NFS4_OK) if (status == NFS4_OK)
status = recover_locks(session, open, &grace); status = recover_locks(session, open, &grace);
if (status == NFS4ERR_BADSESSION) if (status == NFS4ERR_BADSESSION)
break; goto unlock;
} }
/* recover delegations that weren't associated with any opens */
list_for_each(entry, &state->delegations) {
deleg = list_container(entry, nfs41_delegation_state, client_entry);
if (deleg->revoked) {
/* TODO: try WANT_DELEGATION (server support is optional) */
status = recover_delegation_open(session, deleg, &grace);
if (status == NFS4ERR_BADSESSION)
goto unlock;
}
}
/* TODO: return any delegations that were reclaimed as 'recalled' */
unlock:
LeaveCriticalSection(&state->lock); LeaveCriticalSection(&state->lock);
/* revoke all of the client's layouts */ /* revoke all of the client's layouts */