|
@@ -1378,6 +1378,25 @@ static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state)
|
|
}
|
|
}
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
|
|
|
+static void nfs_state_log_update_open_stateid(struct nfs4_state *state)
|
|
|
|
+{
|
|
|
|
+ if (test_and_clear_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
|
|
|
|
+ wake_up_all(&state->waitq);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
|
|
|
|
+ const nfs4_stateid *stateid)
|
|
|
|
+{
|
|
|
|
+ u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
|
|
|
|
+ u32 stateid_seqid = be32_to_cpu(stateid->seqid);
|
|
|
|
+
|
|
|
|
+ if (stateid_seqid == state_seqid + 1U ||
|
|
|
|
+ (stateid_seqid == 1U && state_seqid == 0xffffffffU))
|
|
|
|
+ nfs_state_log_update_open_stateid(state);
|
|
|
|
+ else
|
|
|
|
+ set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
|
|
|
+}
|
|
|
|
+
|
|
static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
|
|
static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
|
|
{
|
|
{
|
|
struct nfs_client *clp = state->owner->so_server->nfs_client;
|
|
struct nfs_client *clp = state->owner->so_server->nfs_client;
|
|
@@ -1393,18 +1412,32 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
|
|
nfs4_state_mark_reclaim_nograce(clp, state);
|
|
nfs4_state_mark_reclaim_nograce(clp, state);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Check for whether or not the caller may update the open stateid
|
|
|
|
+ * to the value passed in by stateid.
|
|
|
|
+ *
|
|
|
|
+ * Note: This function relies heavily on the server implementing
|
|
|
|
+ * RFC7530 Section 9.1.4.2, and RFC5661 Section 8.2.2
|
|
|
|
+ * correctly.
|
|
|
|
+ * i.e. The stateid seqids have to be initialised to 1, and
|
|
|
|
+ * are then incremented on every state transition.
|
|
|
|
+ */
|
|
static bool nfs_need_update_open_stateid(struct nfs4_state *state,
|
|
static bool nfs_need_update_open_stateid(struct nfs4_state *state,
|
|
- const nfs4_stateid *stateid, nfs4_stateid *freeme)
|
|
|
|
|
|
+ const nfs4_stateid *stateid)
|
|
{
|
|
{
|
|
- if (test_and_set_bit(NFS_OPEN_STATE, &state->flags) == 0)
|
|
|
|
- return true;
|
|
|
|
- if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
|
|
|
|
- nfs4_stateid_copy(freeme, &state->open_stateid);
|
|
|
|
- nfs_test_and_clear_all_open_stateid(state);
|
|
|
|
|
|
+ if (test_bit(NFS_OPEN_STATE, &state->flags) == 0 ||
|
|
|
|
+ !nfs4_stateid_match_other(stateid, &state->open_stateid)) {
|
|
|
|
+ if (stateid->seqid == cpu_to_be32(1))
|
|
|
|
+ nfs_state_log_update_open_stateid(state);
|
|
|
|
+ else
|
|
|
|
+ set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
- if (nfs4_stateid_is_newer(stateid, &state->open_stateid))
|
|
|
|
|
|
+
|
|
|
|
+ if (nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
|
|
|
|
+ nfs_state_log_out_of_order_open_stateid(state, stateid);
|
|
return true;
|
|
return true;
|
|
|
|
+ }
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1443,11 +1476,13 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
|
|
if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
|
|
if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
|
|
!nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
|
|
!nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
|
|
nfs_resync_open_stateid_locked(state);
|
|
nfs_resync_open_stateid_locked(state);
|
|
- return;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
nfs4_stateid_copy(&state->stateid, stateid);
|
|
nfs4_stateid_copy(&state->stateid, stateid);
|
|
nfs4_stateid_copy(&state->open_stateid, stateid);
|
|
nfs4_stateid_copy(&state->open_stateid, stateid);
|
|
|
|
+out:
|
|
|
|
+ nfs_state_log_update_open_stateid(state);
|
|
}
|
|
}
|
|
|
|
|
|
static void nfs_clear_open_stateid(struct nfs4_state *state,
|
|
static void nfs_clear_open_stateid(struct nfs4_state *state,
|
|
@@ -1464,29 +1499,57 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,
|
|
}
|
|
}
|
|
|
|
|
|
static void nfs_set_open_stateid_locked(struct nfs4_state *state,
|
|
static void nfs_set_open_stateid_locked(struct nfs4_state *state,
|
|
- const nfs4_stateid *stateid, fmode_t fmode,
|
|
|
|
- nfs4_stateid *freeme)
|
|
|
|
|
|
+ const nfs4_stateid *stateid, nfs4_stateid *freeme)
|
|
{
|
|
{
|
|
- switch (fmode) {
|
|
|
|
- case FMODE_READ:
|
|
|
|
- set_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
|
|
+ DEFINE_WAIT(wait);
|
|
|
|
+ int status = 0;
|
|
|
|
+ for (;;) {
|
|
|
|
+
|
|
|
|
+ if (!nfs_need_update_open_stateid(state, stateid))
|
|
|
|
+ return;
|
|
|
|
+ if (!test_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
|
|
break;
|
|
break;
|
|
- case FMODE_WRITE:
|
|
|
|
- set_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
|
|
+ if (status)
|
|
break;
|
|
break;
|
|
- case FMODE_READ|FMODE_WRITE:
|
|
|
|
- set_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
|
|
+ /* Rely on seqids for serialisation with NFSv4.0 */
|
|
|
|
+ if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
|
|
|
|
+ /*
|
|
|
|
+ * Ensure we process the state changes in the same order
|
|
|
|
+ * in which the server processed them by delaying the
|
|
|
|
+ * update of the stateid until we are in sequence.
|
|
|
|
+ */
|
|
|
|
+ write_sequnlock(&state->seqlock);
|
|
|
|
+ spin_unlock(&state->owner->so_lock);
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ if (!signal_pending(current)) {
|
|
|
|
+ if (schedule_timeout(5*HZ) == 0)
|
|
|
|
+ status = -EAGAIN;
|
|
|
|
+ else
|
|
|
|
+ status = 0;
|
|
|
|
+ } else
|
|
|
|
+ status = -EINTR;
|
|
|
|
+ finish_wait(&state->waitq, &wait);
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ spin_lock(&state->owner->so_lock);
|
|
|
|
+ write_seqlock(&state->seqlock);
|
|
}
|
|
}
|
|
- if (!nfs_need_update_open_stateid(state, stateid, freeme))
|
|
|
|
- return;
|
|
|
|
|
|
+
|
|
|
|
+ if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
|
|
|
|
+ nfs4_stateid_copy(freeme, &state->open_stateid);
|
|
|
|
+ nfs_test_and_clear_all_open_stateid(state);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
nfs4_stateid_copy(&state->stateid, stateid);
|
|
nfs4_stateid_copy(&state->stateid, stateid);
|
|
nfs4_stateid_copy(&state->open_stateid, stateid);
|
|
nfs4_stateid_copy(&state->open_stateid, stateid);
|
|
|
|
+ nfs_state_log_update_open_stateid(state);
|
|
}
|
|
}
|
|
|
|
|
|
-static void __update_open_stateid(struct nfs4_state *state,
|
|
|
|
|
|
+static void nfs_state_set_open_stateid(struct nfs4_state *state,
|
|
const nfs4_stateid *open_stateid,
|
|
const nfs4_stateid *open_stateid,
|
|
- const nfs4_stateid *deleg_stateid,
|
|
|
|
fmode_t fmode,
|
|
fmode_t fmode,
|
|
nfs4_stateid *freeme)
|
|
nfs4_stateid *freeme)
|
|
{
|
|
{
|
|
@@ -1494,17 +1557,34 @@ static void __update_open_stateid(struct nfs4_state *state,
|
|
* Protect the call to nfs4_state_set_mode_locked and
|
|
* Protect the call to nfs4_state_set_mode_locked and
|
|
* serialise the stateid update
|
|
* serialise the stateid update
|
|
*/
|
|
*/
|
|
- spin_lock(&state->owner->so_lock);
|
|
|
|
write_seqlock(&state->seqlock);
|
|
write_seqlock(&state->seqlock);
|
|
- if (deleg_stateid != NULL) {
|
|
|
|
- nfs4_stateid_copy(&state->stateid, deleg_stateid);
|
|
|
|
- set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
|
|
+ nfs_set_open_stateid_locked(state, open_stateid, freeme);
|
|
|
|
+ switch (fmode) {
|
|
|
|
+ case FMODE_READ:
|
|
|
|
+ set_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
+ break;
|
|
|
|
+ case FMODE_WRITE:
|
|
|
|
+ set_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
+ break;
|
|
|
|
+ case FMODE_READ|FMODE_WRITE:
|
|
|
|
+ set_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
}
|
|
}
|
|
- if (open_stateid != NULL)
|
|
|
|
- nfs_set_open_stateid_locked(state, open_stateid, fmode, freeme);
|
|
|
|
|
|
+ set_bit(NFS_OPEN_STATE, &state->flags);
|
|
|
|
+ write_sequnlock(&state->seqlock);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void nfs_state_set_delegation(struct nfs4_state *state,
|
|
|
|
+ const nfs4_stateid *deleg_stateid,
|
|
|
|
+ fmode_t fmode)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * Protect the call to nfs4_state_set_mode_locked and
|
|
|
|
+ * serialise the stateid update
|
|
|
|
+ */
|
|
|
|
+ write_seqlock(&state->seqlock);
|
|
|
|
+ nfs4_stateid_copy(&state->stateid, deleg_stateid);
|
|
|
|
+ set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
write_sequnlock(&state->seqlock);
|
|
write_sequnlock(&state->seqlock);
|
|
- update_open_stateflags(state, fmode);
|
|
|
|
- spin_unlock(&state->owner->so_lock);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static int update_open_stateid(struct nfs4_state *state,
|
|
static int update_open_stateid(struct nfs4_state *state,
|
|
@@ -1522,6 +1602,12 @@ static int update_open_stateid(struct nfs4_state *state,
|
|
fmode &= (FMODE_READ|FMODE_WRITE);
|
|
fmode &= (FMODE_READ|FMODE_WRITE);
|
|
|
|
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
|
|
+ spin_lock(&state->owner->so_lock);
|
|
|
|
+ if (open_stateid != NULL) {
|
|
|
|
+ nfs_state_set_open_stateid(state, open_stateid, fmode, &freeme);
|
|
|
|
+ ret = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
deleg_cur = rcu_dereference(nfsi->delegation);
|
|
deleg_cur = rcu_dereference(nfsi->delegation);
|
|
if (deleg_cur == NULL)
|
|
if (deleg_cur == NULL)
|
|
goto no_delegation;
|
|
goto no_delegation;
|
|
@@ -1538,18 +1624,16 @@ static int update_open_stateid(struct nfs4_state *state,
|
|
goto no_delegation_unlock;
|
|
goto no_delegation_unlock;
|
|
|
|
|
|
nfs_mark_delegation_referenced(deleg_cur);
|
|
nfs_mark_delegation_referenced(deleg_cur);
|
|
- __update_open_stateid(state, open_stateid, &deleg_cur->stateid,
|
|
|
|
- fmode, &freeme);
|
|
|
|
|
|
+ nfs_state_set_delegation(state, &deleg_cur->stateid, fmode);
|
|
ret = 1;
|
|
ret = 1;
|
|
no_delegation_unlock:
|
|
no_delegation_unlock:
|
|
spin_unlock(&deleg_cur->lock);
|
|
spin_unlock(&deleg_cur->lock);
|
|
no_delegation:
|
|
no_delegation:
|
|
|
|
+ if (ret)
|
|
|
|
+ update_open_stateflags(state, fmode);
|
|
|
|
+ spin_unlock(&state->owner->so_lock);
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
|
|
|
|
- if (!ret && open_stateid != NULL) {
|
|
|
|
- __update_open_stateid(state, open_stateid, NULL, fmode, &freeme);
|
|
|
|
- ret = 1;
|
|
|
|
- }
|
|
|
|
if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
|
|
if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
|
|
nfs4_schedule_state_manager(clp);
|
|
nfs4_schedule_state_manager(clp);
|
|
if (freeme.type != 0)
|
|
if (freeme.type != 0)
|