recovery: support for WANT_DELEGATION
for delegations without an associated open, try the optional WANT_DELEGATION operation with CLAIM_PREVIOUS before falling back to OPEN/CLAIM_PREVIOUS. the advantage of WANT_DELEGATION is that it doesn't generate an open stateid, so we don't need the corresponding CLOSE as in recover_delegation_open(). if WANT_DELEGATION fails with NFS4ERR_NOTSUPP, don't try it again during that round of state recovery Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
b9b3b00e76
commit
8e5ab92b2b
4 changed files with 238 additions and 42 deletions
|
|
@ -1527,6 +1527,50 @@ out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum nfsstat4 nfs41_want_delegation(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_path_fh *file,
|
||||||
|
IN deleg_claim4 *claim,
|
||||||
|
IN uint32_t want,
|
||||||
|
IN bool_t try_recovery,
|
||||||
|
OUT open_delegation4 *delegation)
|
||||||
|
{
|
||||||
|
enum nfsstat4 status;
|
||||||
|
nfs41_compound compound;
|
||||||
|
nfs_argop4 argops[3];
|
||||||
|
nfs_resop4 resops[3];
|
||||||
|
nfs41_sequence_args sequence_args;
|
||||||
|
nfs41_sequence_res sequence_res;
|
||||||
|
nfs41_putfh_args putfh_args;
|
||||||
|
nfs41_putfh_res putfh_res;
|
||||||
|
nfs41_want_delegation_args wd_args;
|
||||||
|
nfs41_want_delegation_res wd_res;
|
||||||
|
|
||||||
|
compound_init(&compound, argops, resops, "want_delegation");
|
||||||
|
|
||||||
|
compound_add_op(&compound, OP_SEQUENCE, &sequence_args, &sequence_res);
|
||||||
|
status = nfs41_session_sequence(&sequence_args, session, 0);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
|
||||||
|
putfh_args.file = file;
|
||||||
|
putfh_args.in_recovery = 0;
|
||||||
|
|
||||||
|
compound_add_op(&compound, OP_WANT_DELEGATION, &wd_args, &wd_res);
|
||||||
|
wd_args.claim = claim;
|
||||||
|
wd_args.want = want;
|
||||||
|
wd_res.delegation = delegation;
|
||||||
|
|
||||||
|
status = compound_encode_send_decode(session, &compound, try_recovery);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
compound_error(status = compound.res.status);
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
int nfs41_delegpurge(
|
int nfs41_delegpurge(
|
||||||
IN nfs41_session *session)
|
IN nfs41_session *session)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -667,8 +667,6 @@ typedef struct __nfs41_op_open_res_ok {
|
||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
bitmap4 attrset;
|
bitmap4 attrset;
|
||||||
open_delegation4 *delegation;
|
open_delegation4 *delegation;
|
||||||
uint32_t why_no_deleg;
|
|
||||||
uint32_t why_none_flag;
|
|
||||||
} nfs41_op_open_res_ok;
|
} nfs41_op_open_res_ok;
|
||||||
|
|
||||||
typedef struct __nfs41_op_open_res {
|
typedef struct __nfs41_op_open_res {
|
||||||
|
|
@ -798,6 +796,25 @@ typedef struct __nfs41_setattr_res {
|
||||||
} nfs41_setattr_res;
|
} nfs41_setattr_res;
|
||||||
|
|
||||||
|
|
||||||
|
/* OP_WANT_DELEGATION */
|
||||||
|
typedef struct __deleg_claim4 {
|
||||||
|
uint32_t claim;
|
||||||
|
/* case CLAIM_PREVIOUS: */
|
||||||
|
uint32_t prev_delegate_type;
|
||||||
|
} deleg_claim4;
|
||||||
|
|
||||||
|
typedef struct __nfs41_want_delegation_args {
|
||||||
|
uint32_t want;
|
||||||
|
deleg_claim4 *claim;
|
||||||
|
} nfs41_want_delegation_args;
|
||||||
|
|
||||||
|
typedef struct __nfs41_want_delegation_res {
|
||||||
|
uint32_t status;
|
||||||
|
/* case NFS4_OK: */
|
||||||
|
open_delegation4 *delegation;
|
||||||
|
} nfs41_want_delegation_res;
|
||||||
|
|
||||||
|
|
||||||
/* OP_WRITE */
|
/* OP_WRITE */
|
||||||
enum stable_how4 {
|
enum stable_how4 {
|
||||||
UNSTABLE4 = 0,
|
UNSTABLE4 = 0,
|
||||||
|
|
@ -1158,6 +1175,14 @@ int nfs41_access(
|
||||||
OUT uint32_t *supported OPTIONAL,
|
OUT uint32_t *supported OPTIONAL,
|
||||||
OUT uint32_t *access OPTIONAL);
|
OUT uint32_t *access OPTIONAL);
|
||||||
|
|
||||||
|
enum nfsstat4 nfs41_want_delegation(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_path_fh *file,
|
||||||
|
IN deleg_claim4 *claim,
|
||||||
|
IN uint32_t want,
|
||||||
|
IN bool_t try_recovery,
|
||||||
|
OUT open_delegation4 *delegation);
|
||||||
|
|
||||||
int nfs41_delegpurge(
|
int nfs41_delegpurge(
|
||||||
IN nfs41_session *session);
|
IN nfs41_session *session);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2005,22 +2005,22 @@ static bool_t encode_op_open(
|
||||||
|
|
||||||
static bool_t decode_open_none_delegation4(
|
static bool_t decode_open_none_delegation4(
|
||||||
XDR *xdr,
|
XDR *xdr,
|
||||||
nfs41_op_open_res_ok *res)
|
open_delegation4 *delegation)
|
||||||
{
|
{
|
||||||
bool_t result = TRUE;
|
enum_t why_no_deleg;
|
||||||
|
bool_t will_signal;
|
||||||
|
|
||||||
if (!xdr_u_int32_t(xdr, &res->why_no_deleg))
|
if (!xdr_enum(xdr, (enum_t*)&why_no_deleg))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
switch (res->why_no_deleg)
|
|
||||||
|
switch (why_no_deleg)
|
||||||
{
|
{
|
||||||
case WND4_CONTENTION:
|
case WND4_CONTENTION:
|
||||||
case WND4_RESOURCE:
|
case WND4_RESOURCE:
|
||||||
result = xdr_u_int32_t(xdr, &res->why_none_flag);
|
return xdr_bool(xdr, &will_signal);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool_t decode_open_read_delegation4(
|
static bool_t decode_open_read_delegation4(
|
||||||
|
|
@ -2037,14 +2037,19 @@ static bool_t decode_open_read_delegation4(
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool_t decode_modified_limit4(
|
static bool_t decode_modified_limit4(
|
||||||
XDR *xdr)
|
XDR *xdr,
|
||||||
|
uint64_t *filesize)
|
||||||
{
|
{
|
||||||
uint32_t tmp;
|
uint32_t blocks, bytes_per_block;
|
||||||
|
|
||||||
if (!xdr_u_int32_t(xdr, &tmp))
|
if (!xdr_u_int32_t(xdr, &blocks))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return xdr_u_int32_t(xdr, &tmp);
|
if (!xdr_u_int32_t(xdr, &bytes_per_block))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*filesize = blocks * bytes_per_block;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum limit_by4 {
|
enum limit_by4 {
|
||||||
|
|
@ -2053,27 +2058,23 @@ enum limit_by4 {
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool_t decode_space_limit4(
|
static bool_t decode_space_limit4(
|
||||||
XDR *xdr)
|
XDR *xdr,
|
||||||
|
uint64_t *filesize)
|
||||||
{
|
{
|
||||||
uint32_t tmp_limitby;
|
uint32_t limitby;
|
||||||
uint64_t tmp_filesize;
|
|
||||||
|
|
||||||
if (!xdr_u_int32_t(xdr, &tmp_limitby))
|
if (!xdr_u_int32_t(xdr, &limitby))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
switch (tmp_limitby)
|
switch (limitby)
|
||||||
{
|
{
|
||||||
case NFS_LIMIT_SIZE:
|
case NFS_LIMIT_SIZE:
|
||||||
return xdr_u_hyper(xdr, &tmp_filesize);
|
return xdr_u_hyper(xdr, filesize);
|
||||||
break;
|
|
||||||
case NFS_LIMIT_BLOCKS:
|
case NFS_LIMIT_BLOCKS:
|
||||||
return decode_modified_limit4(xdr);
|
return decode_modified_limit4(xdr, filesize);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
eprintf("decode_space_limit4: limitby %d invalid\n",
|
eprintf("decode_space_limit4: limitby %d invalid\n", limitby);
|
||||||
tmp_limitby);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2081,13 +2082,15 @@ static bool_t decode_open_write_delegation4(
|
||||||
XDR *xdr,
|
XDR *xdr,
|
||||||
open_delegation4 *delegation)
|
open_delegation4 *delegation)
|
||||||
{
|
{
|
||||||
|
uint64_t size_limit;
|
||||||
|
|
||||||
if (!xdr_stateid4(xdr, &delegation->stateid))
|
if (!xdr_stateid4(xdr, &delegation->stateid))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!xdr_bool(xdr, &delegation->recalled))
|
if (!xdr_bool(xdr, &delegation->recalled))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!decode_space_limit4(xdr))
|
if (!decode_space_limit4(xdr, &size_limit))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return xdr_nfsace4(xdr, &delegation->permissions);
|
return xdr_nfsace4(xdr, &delegation->permissions);
|
||||||
|
|
@ -2097,8 +2100,6 @@ static bool_t decode_open_res_ok(
|
||||||
XDR *xdr,
|
XDR *xdr,
|
||||||
nfs41_op_open_res_ok *res)
|
nfs41_op_open_res_ok *res)
|
||||||
{
|
{
|
||||||
bool_t result = TRUE;
|
|
||||||
|
|
||||||
if (!xdr_stateid4(xdr, res->stateid))
|
if (!xdr_stateid4(xdr, res->stateid))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
|
@ -2117,23 +2118,18 @@ static bool_t decode_open_res_ok(
|
||||||
switch (res->delegation->type)
|
switch (res->delegation->type)
|
||||||
{
|
{
|
||||||
case OPEN_DELEGATE_NONE:
|
case OPEN_DELEGATE_NONE:
|
||||||
break;
|
return TRUE;
|
||||||
case OPEN_DELEGATE_NONE_EXT:
|
case OPEN_DELEGATE_NONE_EXT:
|
||||||
result = decode_open_none_delegation4(xdr, res);
|
return decode_open_none_delegation4(xdr, res->delegation);
|
||||||
break;
|
|
||||||
case OPEN_DELEGATE_READ:
|
case OPEN_DELEGATE_READ:
|
||||||
result = decode_open_read_delegation4(xdr, res->delegation);
|
return decode_open_read_delegation4(xdr, res->delegation);
|
||||||
break;
|
|
||||||
case OPEN_DELEGATE_WRITE:
|
case OPEN_DELEGATE_WRITE:
|
||||||
result = decode_open_write_delegation4(xdr, res->delegation);
|
return decode_open_write_delegation4(xdr, res->delegation);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
eprintf("decode_open_res_ok: delegation type %d not "
|
eprintf("decode_open_res_ok: delegation type %d not "
|
||||||
"supported.\n", res->delegation->type);
|
"supported.\n", res->delegation->type);
|
||||||
result = FALSE;
|
return FALSE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool_t decode_op_open(
|
static bool_t decode_op_open(
|
||||||
|
|
@ -2681,6 +2677,64 @@ static bool_t decode_op_setattr(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OP_WANT_DELEGATION
|
||||||
|
*/
|
||||||
|
static bool_t encode_op_want_delegation(
|
||||||
|
XDR *xdr,
|
||||||
|
nfs_argop4 *argop)
|
||||||
|
{
|
||||||
|
nfs41_want_delegation_args *args = (nfs41_want_delegation_args*)argop->arg;
|
||||||
|
|
||||||
|
if (unexpected_op(argop->op, OP_WANT_DELEGATION))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!xdr_u_int32_t(xdr, &args->want))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!xdr_u_int32_t(xdr, &args->claim->claim))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return args->claim->claim != CLAIM_PREVIOUS ||
|
||||||
|
xdr_u_int32_t(xdr, &args->claim->prev_delegate_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool_t decode_op_want_delegation(
|
||||||
|
XDR *xdr,
|
||||||
|
nfs_resop4 *resop)
|
||||||
|
{
|
||||||
|
nfs41_want_delegation_res *res = (nfs41_want_delegation_res*)resop->res;
|
||||||
|
|
||||||
|
if (unexpected_op(resop->op, OP_WANT_DELEGATION))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!xdr_u_int32_t(xdr, &res->status))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (res->status)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!xdr_enum(xdr, (enum_t*)&res->delegation->type))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
switch (res->delegation->type)
|
||||||
|
{
|
||||||
|
case OPEN_DELEGATE_NONE:
|
||||||
|
return TRUE;
|
||||||
|
case OPEN_DELEGATE_NONE_EXT:
|
||||||
|
return decode_open_none_delegation4(xdr, res->delegation);
|
||||||
|
case OPEN_DELEGATE_READ:
|
||||||
|
return decode_open_read_delegation4(xdr, res->delegation);
|
||||||
|
case OPEN_DELEGATE_WRITE:
|
||||||
|
return decode_open_write_delegation4(xdr, res->delegation);
|
||||||
|
default:
|
||||||
|
eprintf("decode_open_res_ok: delegation type %d not "
|
||||||
|
"supported.\n", res->delegation->type);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OP_WRITE
|
* OP_WRITE
|
||||||
*/
|
*/
|
||||||
|
|
@ -3426,7 +3480,7 @@ static const op_table_entry g_op_table[] = {
|
||||||
{ encode_op_sequence, decode_op_sequence }, /* OP_SEQUENCE = 53 */
|
{ encode_op_sequence, decode_op_sequence }, /* OP_SEQUENCE = 53 */
|
||||||
{ NULL, NULL }, /* OP_SET_SSV = 54 */
|
{ NULL, NULL }, /* OP_SET_SSV = 54 */
|
||||||
{ NULL, NULL }, /* OP_TEST_STATEID = 55 */
|
{ NULL, NULL }, /* OP_TEST_STATEID = 55 */
|
||||||
{ NULL, NULL }, /* OP_WANT_DELEGATION = 56 */
|
{ encode_op_want_delegation, decode_op_want_delegation }, /* OP_WANT_DELEGATION = 56 */
|
||||||
{ encode_op_destroy_clientid, decode_op_destroy_clientid }, /* OP_DESTROY_CLIENTID = 57 */
|
{ encode_op_destroy_clientid, decode_op_destroy_clientid }, /* OP_DESTROY_CLIENTID = 57 */
|
||||||
{ encode_op_reclaim_complete, decode_op_reclaim_complete }, /* OP_RECLAIM_COMPLETE = 58 */
|
{ encode_op_reclaim_complete, decode_op_reclaim_complete }, /* OP_RECLAIM_COMPLETE = 58 */
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,63 @@ static int recover_locks(
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* delegation recovery via WANT_DELEGATION */
|
||||||
|
static int recover_delegation_want(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_delegation_state *deleg,
|
||||||
|
IN OUT bool_t *grace)
|
||||||
|
{
|
||||||
|
deleg_claim4 claim;
|
||||||
|
open_delegation4 delegation = { 0 };
|
||||||
|
enum open_delegation_type4 delegate_type;
|
||||||
|
uint32_t want_flags = 0;
|
||||||
|
int status = NFS4_OK;
|
||||||
|
|
||||||
|
AcquireSRWLockShared(&deleg->lock);
|
||||||
|
delegate_type = deleg->state.type;
|
||||||
|
ReleaseSRWLockShared(&deleg->lock);
|
||||||
|
|
||||||
|
if (delegate_type == OPEN_DELEGATE_READ)
|
||||||
|
want_flags |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
|
||||||
|
else
|
||||||
|
want_flags |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
|
||||||
|
|
||||||
|
if (*grace) {
|
||||||
|
/* recover the delegation with WANT_DELEGATION/CLAIM_PREVIOUS */
|
||||||
|
claim.claim = CLAIM_PREVIOUS;
|
||||||
|
claim.prev_delegate_type = delegate_type;
|
||||||
|
|
||||||
|
status = nfs41_want_delegation(session, &deleg->file,
|
||||||
|
&claim, want_flags, FALSE, &delegation);
|
||||||
|
} else
|
||||||
|
status = NFS4ERR_NO_GRACE;
|
||||||
|
|
||||||
|
if (status == NFS4ERR_NO_GRACE) {
|
||||||
|
*grace = FALSE;
|
||||||
|
/* attempt out-of-grace recovery with with CLAIM_DELEG_PREV_FH */
|
||||||
|
claim.claim = CLAIM_DELEG_PREV_FH;
|
||||||
|
|
||||||
|
status = nfs41_want_delegation(session, &deleg->file,
|
||||||
|
&claim, want_flags, FALSE, &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_want() 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);
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* delegation recovery via OPEN (requires corresponding CLOSE) */
|
/* delegation recovery via OPEN (requires corresponding CLOSE) */
|
||||||
static int recover_delegation_open(
|
static int recover_delegation_open(
|
||||||
IN nfs41_session *session,
|
IN nfs41_session *session,
|
||||||
|
|
@ -337,6 +394,7 @@ int nfs41_recover_client_state(
|
||||||
nfs41_open_state *open;
|
nfs41_open_state *open;
|
||||||
nfs41_delegation_state *deleg;
|
nfs41_delegation_state *deleg;
|
||||||
bool_t grace = TRUE;
|
bool_t grace = TRUE;
|
||||||
|
bool_t want_supported = TRUE;
|
||||||
int status = NFS4_OK;
|
int status = NFS4_OK;
|
||||||
|
|
||||||
EnterCriticalSection(&state->lock);
|
EnterCriticalSection(&state->lock);
|
||||||
|
|
@ -362,9 +420,24 @@ 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) {
|
||||||
/* TODO: try WANT_DELEGATION (server support is optional) */
|
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);
|
status = recover_delegation_open(session, deleg, &grace);
|
||||||
|
}
|
||||||
if (status == NFS4ERR_BADSESSION)
|
if (status == NFS4ERR_BADSESSION)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue