deleg: handle locks locally on write delegation
new flags for nfs41_lock_state: exclusive, delegated, and id on lock, open_lock_delegate() attempts to register a delegated lock instead of sending a LOCK request to the server on unlock, open_unlock_delegate() removes/frees delegated locks instead of sending a LOCKU request on delegreturn, delegation_flush_locks() sends LOCK requests for all delegated locks before returning a delegation on recovery, recover_locks() avoids lock state recovery for delegated locks Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
284c273999
commit
9e556da92c
4 changed files with 302 additions and 117 deletions
|
|
@ -84,43 +84,8 @@ void nfs41_delegation_deref(
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* delegation return */
|
|
||||||
#define open_entry(pos) list_container(pos, nfs41_open_state, client_entry)
|
#define open_entry(pos) list_container(pos, nfs41_open_state, client_entry)
|
||||||
|
|
||||||
static int open_deleg_cmp(const struct list_entry *entry, const void *value)
|
|
||||||
{
|
|
||||||
nfs41_open_state *open = open_entry(entry);
|
|
||||||
int result = -1;
|
|
||||||
|
|
||||||
/* open must match the delegation and have no open stateid */
|
|
||||||
AcquireSRWLockShared(&open->lock);
|
|
||||||
if (open->delegation.state != value) goto out;
|
|
||||||
if (open->do_close) goto out;
|
|
||||||
result = 0;
|
|
||||||
out:
|
|
||||||
ReleaseSRWLockShared(&open->lock);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find the first open that needs recovery */
|
|
||||||
static nfs41_open_state* deleg_open_find(
|
|
||||||
IN struct client_state *state,
|
|
||||||
IN const nfs41_delegation_state *deleg)
|
|
||||||
{
|
|
||||||
struct list_entry *entry;
|
|
||||||
nfs41_open_state *open = NULL;
|
|
||||||
|
|
||||||
EnterCriticalSection(&state->lock);
|
|
||||||
entry = list_search(&state->opens, deleg, open_deleg_cmp);
|
|
||||||
if (entry) {
|
|
||||||
open = open_entry(entry);
|
|
||||||
nfs41_open_state_ref(open); /* return a reference */
|
|
||||||
}
|
|
||||||
LeaveCriticalSection(&state->lock);
|
|
||||||
return open;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void delegation_remove(
|
static void delegation_remove(
|
||||||
IN nfs41_client *client,
|
IN nfs41_client *client,
|
||||||
IN nfs41_delegation_state *deleg)
|
IN nfs41_delegation_state *deleg)
|
||||||
|
|
@ -154,6 +119,145 @@ static void delegation_remove(
|
||||||
nfs41_delegation_deref(deleg);
|
nfs41_delegation_deref(deleg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* delegation return */
|
||||||
|
#define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
|
||||||
|
|
||||||
|
static bool_t has_delegated_locks(
|
||||||
|
IN nfs41_open_state *open)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
list_for_each(entry, &open->locks.list) {
|
||||||
|
if (lock_entry(entry)->delegated)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_deleg_cmp(const struct list_entry *entry, const void *value)
|
||||||
|
{
|
||||||
|
nfs41_open_state *open = open_entry(entry);
|
||||||
|
int result = -1;
|
||||||
|
|
||||||
|
/* open must match the delegation and have state to reclaim */
|
||||||
|
AcquireSRWLockShared(&open->lock);
|
||||||
|
if (open->delegation.state != value) goto out;
|
||||||
|
if (open->do_close && !has_delegated_locks(open)) goto out;
|
||||||
|
result = 0;
|
||||||
|
out:
|
||||||
|
ReleaseSRWLockShared(&open->lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the first open that needs recovery */
|
||||||
|
static nfs41_open_state* deleg_open_find(
|
||||||
|
IN struct client_state *state,
|
||||||
|
IN const nfs41_delegation_state *deleg)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
nfs41_open_state *open = NULL;
|
||||||
|
|
||||||
|
EnterCriticalSection(&state->lock);
|
||||||
|
entry = list_search(&state->opens, deleg, open_deleg_cmp);
|
||||||
|
if (entry) {
|
||||||
|
open = open_entry(entry);
|
||||||
|
nfs41_open_state_ref(open); /* return a reference */
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&state->lock);
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the first lock that needs recovery */
|
||||||
|
static bool_t deleg_lock_find(
|
||||||
|
IN nfs41_open_state *open,
|
||||||
|
OUT nfs41_lock_state *lock_out)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
bool_t found = FALSE;
|
||||||
|
|
||||||
|
AcquireSRWLockShared(&open->lock);
|
||||||
|
list_for_each(entry, &open->locks.list) {
|
||||||
|
nfs41_lock_state *lock = lock_entry(entry);
|
||||||
|
if (lock->delegated) {
|
||||||
|
/* copy offset, length, type */
|
||||||
|
lock_out->offset = lock->offset;
|
||||||
|
lock_out->length = lock->length;
|
||||||
|
lock_out->exclusive = lock->exclusive;
|
||||||
|
lock_out->id = lock->id;
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&open->lock);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the matching lock by id, and reset lock.delegated */
|
||||||
|
static void deleg_lock_update(
|
||||||
|
IN nfs41_open_state *open,
|
||||||
|
IN const nfs41_lock_state *source)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
|
list_for_each(entry, &open->locks.list) {
|
||||||
|
nfs41_lock_state *lock = lock_entry(entry);
|
||||||
|
if (lock->id == source->id) {
|
||||||
|
lock->delegated = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int delegation_flush_locks(
|
||||||
|
IN nfs41_open_state *open,
|
||||||
|
IN bool_t try_recovery)
|
||||||
|
{
|
||||||
|
stateid_arg stateid;
|
||||||
|
nfs41_lock_state lock;
|
||||||
|
int status = NFS4_OK;
|
||||||
|
|
||||||
|
stateid.open = open;
|
||||||
|
stateid.delegation = NULL;
|
||||||
|
|
||||||
|
/* get the starting open/lock stateid */
|
||||||
|
AcquireSRWLockShared(&open->lock);
|
||||||
|
if (open->locks.stateid.seqid) {
|
||||||
|
memcpy(&stateid.stateid, &open->locks.stateid, sizeof(stateid4));
|
||||||
|
stateid.type = STATEID_LOCK;
|
||||||
|
} else {
|
||||||
|
memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
|
||||||
|
stateid.type = STATEID_OPEN;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&open->lock);
|
||||||
|
|
||||||
|
/* send LOCK requests for each delegated lock range */
|
||||||
|
while (deleg_lock_find(open, &lock)) {
|
||||||
|
status = nfs41_lock(open->session, &open->file,
|
||||||
|
&open->owner, lock.exclusive ? WRITE_LT : READ_LT,
|
||||||
|
lock.offset, lock.length, FALSE, try_recovery, &stateid);
|
||||||
|
if (status)
|
||||||
|
break;
|
||||||
|
deleg_lock_update(open, &lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save the updated lock stateid */
|
||||||
|
if (stateid.type == STATEID_LOCK) {
|
||||||
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
|
if (open->locks.stateid.seqid == 0) {
|
||||||
|
/* if it's a new lock stateid, copy it in */
|
||||||
|
memcpy(&open->locks.stateid, &stateid.stateid, sizeof(stateid4));
|
||||||
|
} else if (stateid.stateid.seqid > open->locks.stateid.seqid) {
|
||||||
|
/* update the seqid if it's more recent */
|
||||||
|
open->locks.stateid.seqid = stateid.stateid.seqid;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma warning (disable : 4706) /* assignment within conditional expression */
|
#pragma warning (disable : 4706) /* assignment within conditional expression */
|
||||||
|
|
||||||
static int delegation_return(
|
static int delegation_return(
|
||||||
|
|
@ -163,16 +267,18 @@ static int delegation_return(
|
||||||
IN bool_t try_recovery)
|
IN bool_t try_recovery)
|
||||||
{
|
{
|
||||||
stateid_arg stateid;
|
stateid_arg stateid;
|
||||||
int status = NFS4_OK;
|
|
||||||
|
|
||||||
/* recover opens associated with the delegation */
|
|
||||||
nfs41_open_state *open;
|
nfs41_open_state *open;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* recover opens and locks associated with the delegation */
|
||||||
while (open = deleg_open_find(&client->state, deleg)) {
|
while (open = deleg_open_find(&client->state, deleg)) {
|
||||||
status = nfs41_delegation_to_open(open, try_recovery);
|
status = nfs41_delegation_to_open(open, try_recovery);
|
||||||
|
if (status == NFS4_OK)
|
||||||
|
status = delegation_flush_locks(open, try_recovery);
|
||||||
nfs41_open_state_deref(open);
|
nfs41_open_state_deref(open);
|
||||||
|
|
||||||
if (status == NFS4ERR_BADSESSION)
|
if (status)
|
||||||
goto out;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return the delegation */
|
/* return the delegation */
|
||||||
|
|
@ -494,7 +600,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_RETURNED)
|
while (deleg->status == DELEGATION_RETURNING)
|
||||||
SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0);
|
SleepConditionVariableSRW(&deleg->cond, &deleg->lock, INFINITE, 0);
|
||||||
status = NFS4ERR_BADHANDLE;
|
status = NFS4ERR_BADHANDLE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
214
daemon/lock.c
214
daemon/lock.c
|
|
@ -41,10 +41,6 @@ static void lock_stateid_arg(
|
||||||
arg->open = state;
|
arg->open = state;
|
||||||
arg->delegation = NULL;
|
arg->delegation = NULL;
|
||||||
|
|
||||||
/* open_to_lock_owner4 requires an open stateid; if we
|
|
||||||
* have a delegation, convert it to an open stateid */
|
|
||||||
nfs41_delegation_to_open(state, TRUE);
|
|
||||||
|
|
||||||
AcquireSRWLockShared(&state->lock);
|
AcquireSRWLockShared(&state->lock);
|
||||||
if (state->locks.stateid.seqid) {
|
if (state->locks.stateid.seqid) {
|
||||||
memcpy(&arg->stateid, &state->locks.stateid, sizeof(stateid4));
|
memcpy(&arg->stateid, &state->locks.stateid, sizeof(stateid4));
|
||||||
|
|
@ -60,7 +56,7 @@ static void lock_stateid_arg(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* expects the caller to hold an exclusive lock on nfs41_open_state.lock */
|
/* expects the caller to hold an exclusive lock on nfs41_open_state.lock */
|
||||||
static void update_lock_state(
|
static void lock_stateid_update(
|
||||||
OUT nfs41_open_state *state,
|
OUT nfs41_open_state *state,
|
||||||
IN const stateid4 *stateid)
|
IN const stateid4 *stateid)
|
||||||
{
|
{
|
||||||
|
|
@ -74,54 +70,118 @@ static void update_lock_state(
|
||||||
}
|
}
|
||||||
|
|
||||||
static int open_lock_add(
|
static int open_lock_add(
|
||||||
IN nfs41_open_state *state,
|
IN nfs41_open_state *open,
|
||||||
IN const stateid4 *stateid,
|
IN const stateid_arg *stateid,
|
||||||
IN uint64_t offset,
|
IN const nfs41_lock_state *input)
|
||||||
IN uint64_t length,
|
|
||||||
IN uint32_t type)
|
|
||||||
{
|
{
|
||||||
nfs41_lock_state *lock;
|
nfs41_lock_state *lock;
|
||||||
int status = NO_ERROR;
|
int status = NO_ERROR;
|
||||||
|
|
||||||
AcquireSRWLockExclusive(&state->lock);
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
update_lock_state(state, stateid);
|
if (stateid->type == STATEID_LOCK)
|
||||||
|
lock_stateid_update(open, &stateid->stateid);
|
||||||
|
|
||||||
lock = malloc(sizeof(nfs41_lock_state));
|
lock = calloc(1, sizeof(nfs41_lock_state));
|
||||||
if (lock == NULL) {
|
if (lock == NULL) {
|
||||||
status = GetLastError();
|
status = GetLastError();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
lock->offset = offset;
|
lock->offset = input->offset;
|
||||||
lock->length = length;
|
lock->length = input->length;
|
||||||
lock->type = type;
|
lock->exclusive = input->exclusive;
|
||||||
|
lock->id = open->locks.counter++;
|
||||||
|
|
||||||
list_add_tail(&state->locks.list, &lock->open_entry);
|
list_add_tail(&open->locks.list, &lock->open_entry);
|
||||||
out:
|
out:
|
||||||
ReleaseSRWLockExclusive(&state->lock);
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void open_lock_remove(
|
static bool_t open_lock_delegate(
|
||||||
IN nfs41_open_state *state,
|
IN nfs41_open_state *open,
|
||||||
IN const stateid4 *stateid,
|
IN const nfs41_lock_state *input)
|
||||||
IN uint64_t offset,
|
{
|
||||||
IN uint64_t length)
|
bool_t delegated = FALSE;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
|
if (open->delegation.state) {
|
||||||
|
nfs41_delegation_state *deleg = open->delegation.state;
|
||||||
|
AcquireSRWLockShared(&deleg->lock);
|
||||||
|
if (deleg->state.type == OPEN_DELEGATE_WRITE
|
||||||
|
&& deleg->status == DELEGATION_GRANTED) {
|
||||||
|
nfs41_lock_state *lock = calloc(1, sizeof(nfs41_lock_state));
|
||||||
|
if (lock) {
|
||||||
|
lock->offset = input->offset;
|
||||||
|
lock->length = input->length;
|
||||||
|
lock->exclusive = input->exclusive;
|
||||||
|
lock->delegated = 1;
|
||||||
|
lock->id = open->locks.counter++;
|
||||||
|
list_add_tail(&open->locks.list, &lock->open_entry);
|
||||||
|
delegated = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&deleg->lock);
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
|
|
||||||
|
return delegated;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
|
||||||
|
|
||||||
|
static int lock_range_cmp(const struct list_entry *entry, const void *value)
|
||||||
|
{
|
||||||
|
const nfs41_lock_state *lhs = lock_entry(entry);
|
||||||
|
const nfs41_lock_state *rhs = (const nfs41_lock_state*)value;
|
||||||
|
if (lhs->offset != rhs->offset) return -1;
|
||||||
|
if (lhs->length != rhs->length) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_unlock_delegate(
|
||||||
|
IN nfs41_open_state *open,
|
||||||
|
IN const nfs41_lock_state *input)
|
||||||
{
|
{
|
||||||
struct list_entry *entry;
|
struct list_entry *entry;
|
||||||
nfs41_lock_state *lock;
|
int status = ERROR_NOT_LOCKED;
|
||||||
|
|
||||||
AcquireSRWLockExclusive(&state->lock);
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
update_lock_state(state, stateid);
|
|
||||||
|
|
||||||
list_for_each(entry, &state->locks.list) {
|
/* find lock state that matches this range */
|
||||||
lock = list_container(entry, nfs41_lock_state, open_entry);
|
entry = list_search(&open->locks.list, input, lock_range_cmp);
|
||||||
if (lock->offset == offset && lock->length == length) {
|
if (entry) {
|
||||||
|
nfs41_lock_state *lock = lock_entry(entry);
|
||||||
|
if (lock->delegated) {
|
||||||
|
/* if the lock was delegated, remove/free it and return success */
|
||||||
list_remove(entry);
|
list_remove(entry);
|
||||||
free(lock);
|
free(lock);
|
||||||
break;
|
status = NO_ERROR;
|
||||||
}
|
} else
|
||||||
|
status = ERROR_LOCKED;
|
||||||
}
|
}
|
||||||
ReleaseSRWLockExclusive(&state->lock);
|
|
||||||
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_unlock_remove(
|
||||||
|
IN nfs41_open_state *open,
|
||||||
|
IN const stateid_arg *stateid,
|
||||||
|
IN const nfs41_lock_state *input)
|
||||||
|
{
|
||||||
|
struct list_entry *entry;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&open->lock);
|
||||||
|
if (stateid->type == STATEID_LOCK)
|
||||||
|
lock_stateid_update(open, &stateid->stateid);
|
||||||
|
|
||||||
|
/* find and remove the unlocked range */
|
||||||
|
entry = list_search(&open->locks.list, input, lock_range_cmp);
|
||||||
|
if (entry) {
|
||||||
|
list_remove(entry);
|
||||||
|
free(lock_entry(entry));
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&open->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -156,13 +216,12 @@ static __inline uint32_t get_lock_type(BOOLEAN exclusive, BOOLEAN blocking)
|
||||||
|
|
||||||
static int handle_lock(nfs41_upcall *upcall)
|
static int handle_lock(nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
|
nfs41_lock_state input;
|
||||||
stateid_arg stateid;
|
stateid_arg stateid;
|
||||||
lock_upcall_args *args = &upcall->args.lock;
|
lock_upcall_args *args = &upcall->args.lock;
|
||||||
nfs41_open_state *state = upcall->state_ref;
|
nfs41_open_state *state = upcall->state_ref;
|
||||||
const uint32_t type = get_lock_type(args->exclusive, args->blocking);
|
const uint32_t type = get_lock_type(args->exclusive, args->blocking);
|
||||||
int status;
|
int status = NO_ERROR;
|
||||||
|
|
||||||
lock_stateid_arg(state, &stateid);
|
|
||||||
|
|
||||||
/* 18.10.3. Operation 12: LOCK - Create Lock
|
/* 18.10.3. Operation 12: LOCK - Create Lock
|
||||||
* "To lock the file from a specific offset through the end-of-file
|
* "To lock the file from a specific offset through the end-of-file
|
||||||
|
|
@ -171,8 +230,29 @@ static int handle_lock(nfs41_upcall *upcall)
|
||||||
if (args->length >= NFS4_UINT64_MAX - args->offset)
|
if (args->length >= NFS4_UINT64_MAX - args->offset)
|
||||||
args->length = NFS4_UINT64_MAX;
|
args->length = NFS4_UINT64_MAX;
|
||||||
|
|
||||||
|
input.offset = args->offset;
|
||||||
|
input.length = args->length;
|
||||||
|
input.exclusive = args->exclusive;
|
||||||
|
|
||||||
|
/* if we hold a write delegation, handle the lock locally */
|
||||||
|
if (open_lock_delegate(state, &input)) {
|
||||||
|
dprintf(LKLVL, "delegated lock { %llu, %llu }\n",
|
||||||
|
input.offset, input.length);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open_to_lock_owner4 requires an open stateid; if we
|
||||||
|
* have a delegation, convert it to an open stateid */
|
||||||
|
status = nfs41_delegation_to_open(state, TRUE);
|
||||||
|
if (status) {
|
||||||
|
status = ERROR_FILE_INVALID;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_stateid_arg(state, &stateid);
|
||||||
|
|
||||||
status = nfs41_lock(state->session, &state->file, &state->owner,
|
status = nfs41_lock(state->session, &state->file, &state->owner,
|
||||||
type, args->offset, args->length, FALSE, TRUE, &stateid);
|
type, input.offset, input.length, FALSE, TRUE, &stateid);
|
||||||
if (status) {
|
if (status) {
|
||||||
dprintf(LKLVL, "nfs41_lock failed with %s\n",
|
dprintf(LKLVL, "nfs41_lock failed with %s\n",
|
||||||
nfs_error_string(status));
|
nfs_error_string(status));
|
||||||
|
|
@ -182,7 +262,7 @@ static int handle_lock(nfs41_upcall *upcall)
|
||||||
|
|
||||||
/* ignore errors from open_lock_add(); they just mean we
|
/* ignore errors from open_lock_add(); they just mean we
|
||||||
* won't be able to recover the lock after reboot */
|
* won't be able to recover the lock after reboot */
|
||||||
open_lock_add(state, &stateid.stateid, args->offset, args->length, type);
|
open_lock_add(state, &stateid, &input);
|
||||||
out:
|
out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +270,7 @@ out:
|
||||||
static void cancel_lock(IN nfs41_upcall *upcall)
|
static void cancel_lock(IN nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
stateid_arg stateid;
|
stateid_arg stateid;
|
||||||
|
nfs41_lock_state input;
|
||||||
lock_upcall_args *args = &upcall->args.lock;
|
lock_upcall_args *args = &upcall->args.lock;
|
||||||
nfs41_open_state *state = upcall->state_ref;
|
nfs41_open_state *state = upcall->state_ref;
|
||||||
int status = NO_ERROR;
|
int status = NO_ERROR;
|
||||||
|
|
@ -199,18 +280,21 @@ static void cancel_lock(IN nfs41_upcall *upcall)
|
||||||
if (upcall->status)
|
if (upcall->status)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
input.offset = args->offset;
|
||||||
|
input.length = args->length;
|
||||||
|
|
||||||
|
/* search for the range to unlock, and remove if delegated */
|
||||||
|
status = open_unlock_delegate(state, &input);
|
||||||
|
if (status != ERROR_LOCKED)
|
||||||
|
goto out;
|
||||||
|
|
||||||
lock_stateid_arg(state, &stateid);
|
lock_stateid_arg(state, &stateid);
|
||||||
|
|
||||||
status = nfs41_unlock(state->session, &state->file,
|
status = nfs41_unlock(state->session, &state->file,
|
||||||
args->offset, args->length, &stateid);
|
args->offset, args->length, &stateid);
|
||||||
if (status) {
|
|
||||||
dprintf(LKLVL, "cancel_lock: nfs41_unlock() failed with %s\n",
|
|
||||||
nfs_error_string(status));
|
|
||||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
open_lock_remove(state, &stateid.stateid, args->offset, args->length);
|
open_unlock_remove(state, &stateid, &input);
|
||||||
|
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||||
out:
|
out:
|
||||||
dprintf(1, "<-- cancel_lock() returning %d\n", status);
|
dprintf(1, "<-- cancel_lock() returning %d\n", status);
|
||||||
}
|
}
|
||||||
|
|
@ -235,48 +319,36 @@ out:
|
||||||
|
|
||||||
static int handle_unlock(nfs41_upcall *upcall)
|
static int handle_unlock(nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
|
nfs41_lock_state input;
|
||||||
stateid_arg stateid;
|
stateid_arg stateid;
|
||||||
unlock_upcall_args *args = &upcall->args.unlock;
|
unlock_upcall_args *args = &upcall->args.unlock;
|
||||||
nfs41_open_state *state = upcall->state_ref;
|
nfs41_open_state *state = upcall->state_ref;
|
||||||
uint32_t i, nsuccess = 0;
|
|
||||||
unsigned char *buf = args->buf;
|
unsigned char *buf = args->buf;
|
||||||
uint32_t buf_len = args->buf_len;
|
uint32_t buf_len = args->buf_len;
|
||||||
uint64_t offset;
|
uint32_t i;
|
||||||
uint64_t length;
|
|
||||||
int status = NO_ERROR;
|
int status = NO_ERROR;
|
||||||
|
|
||||||
lock_stateid_arg(state, &stateid);
|
lock_stateid_arg(state, &stateid);
|
||||||
if (stateid.type != STATEID_LOCK) {
|
|
||||||
eprintf("attempt to unlock a file with no lock state\n");
|
|
||||||
status = ERROR_NOT_LOCKED;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < args->count; i++) {
|
for (i = 0; i < args->count; i++) {
|
||||||
if (safe_read(&buf, &buf_len, &offset, sizeof(LONGLONG))) break;
|
if (safe_read(&buf, &buf_len, &input.offset, sizeof(LONGLONG))) break;
|
||||||
if (safe_read(&buf, &buf_len, &length, sizeof(LONGLONG))) break;
|
if (safe_read(&buf, &buf_len, &input.length, sizeof(LONGLONG))) break;
|
||||||
|
|
||||||
/* do the same translation as LOCK, or the ranges won't match */
|
/* do the same translation as LOCK, or the ranges won't match */
|
||||||
if (length >= NFS4_UINT64_MAX - offset)
|
if (input.length >= NFS4_UINT64_MAX - input.offset)
|
||||||
length = NFS4_UINT64_MAX;
|
input.length = NFS4_UINT64_MAX;
|
||||||
|
|
||||||
status = nfs41_unlock(state->session,
|
/* search for the range to unlock, and remove if delegated */
|
||||||
&state->file, offset, length, &stateid);
|
status = open_unlock_delegate(state, &input);
|
||||||
if (status == NFS4_OK) {
|
if (status != ERROR_LOCKED)
|
||||||
open_lock_remove(state, &stateid.stateid, offset, length);
|
continue;
|
||||||
nsuccess++;
|
|
||||||
} else {
|
|
||||||
dprintf(LKLVL, "nfs41_unlock failed with %s\n",
|
|
||||||
nfs_error_string(status));
|
|
||||||
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nsuccess) {
|
status = nfs41_unlock(state->session, &state->file,
|
||||||
update_lock_state(state, &stateid.stateid);
|
input.offset, input.length, &stateid);
|
||||||
status = NO_ERROR;
|
|
||||||
|
open_unlock_remove(state, &stateid, &input);
|
||||||
|
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,9 @@ typedef struct __nfs41_lock_state {
|
||||||
struct list_entry open_entry; /* entry in nfs41_open_state.locks */
|
struct list_entry open_entry; /* entry in nfs41_open_state.locks */
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
uint64_t length;
|
uint64_t length;
|
||||||
uint32_t type;
|
uint32_t exclusive : 1;
|
||||||
|
uint32_t delegated : 1; /* whether or not there is state on the server */
|
||||||
|
uint32_t id : 30;
|
||||||
} nfs41_lock_state;
|
} nfs41_lock_state;
|
||||||
|
|
||||||
/* nfs41_open_state reference counting:
|
/* nfs41_open_state reference counting:
|
||||||
|
|
@ -140,6 +142,7 @@ typedef struct __nfs41_open_state {
|
||||||
struct { /* list of open lock state for recovery */
|
struct { /* list of open lock state for recovery */
|
||||||
stateid4 stateid;
|
stateid4 stateid;
|
||||||
struct list_entry list;
|
struct list_entry list;
|
||||||
|
uint32_t counter;
|
||||||
} locks;
|
} locks;
|
||||||
} nfs41_open_state;
|
} nfs41_open_state;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -233,18 +233,22 @@ static int recover_locks(
|
||||||
/* recover any locks for this open */
|
/* recover any locks for this open */
|
||||||
list_for_each(entry, &open->locks.list) {
|
list_for_each(entry, &open->locks.list) {
|
||||||
lock = list_container(entry, nfs41_lock_state, open_entry);
|
lock = list_container(entry, nfs41_lock_state, open_entry);
|
||||||
|
if (lock->delegated)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (*grace)
|
if (*grace)
|
||||||
status = nfs41_lock(session, &open->file, &open->owner,
|
status = nfs41_lock(session, &open->file,
|
||||||
lock->type, lock->offset, lock->length, TRUE, FALSE, &stateid);
|
&open->owner, lock->exclusive ? WRITE_LT : READ_LT,
|
||||||
|
lock->offset, lock->length, TRUE, FALSE, &stateid);
|
||||||
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 a normal LOCK */
|
/* attempt out-of-grace recovery with a normal LOCK */
|
||||||
status = nfs41_lock(session, &open->file, &open->owner,
|
status = nfs41_lock(session, &open->file,
|
||||||
lock->type, lock->offset, lock->length, FALSE, FALSE, &stateid);
|
&open->owner, lock->exclusive ? WRITE_LT : READ_LT,
|
||||||
|
lock->offset, lock->length, FALSE, FALSE, &stateid);
|
||||||
}
|
}
|
||||||
if (status == NFS4ERR_BADSESSION)
|
if (status == NFS4ERR_BADSESSION)
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue