|
@@ -1127,6 +1127,21 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool nfs4_mode_match_open_stateid(struct nfs4_state *state,
|
|
|
|
+ fmode_t fmode)
|
|
|
|
+{
|
|
|
|
+ switch(fmode & (FMODE_READ|FMODE_WRITE)) {
|
|
|
|
+ case FMODE_READ|FMODE_WRITE:
|
|
|
|
+ return state->n_rdwr != 0;
|
|
|
|
+ case FMODE_WRITE:
|
|
|
|
+ return state->n_wronly != 0;
|
|
|
|
+ case FMODE_READ:
|
|
|
|
+ return state->n_rdonly != 0;
|
|
|
|
+ }
|
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
|
|
static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
@@ -1571,17 +1586,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
|
|
return opendata;
|
|
return opendata;
|
|
}
|
|
}
|
|
|
|
|
|
-static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
|
|
|
|
|
|
+static int nfs4_open_recover_helper(struct nfs4_opendata *opendata,
|
|
|
|
+ fmode_t fmode)
|
|
{
|
|
{
|
|
struct nfs4_state *newstate;
|
|
struct nfs4_state *newstate;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- if ((opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR ||
|
|
|
|
- opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEG_CUR_FH) &&
|
|
|
|
- (opendata->o_arg.u.delegation_type & fmode) != fmode)
|
|
|
|
- /* This mode can't have been delegated, so we must have
|
|
|
|
- * a valid open_stateid to cover it - not need to reclaim.
|
|
|
|
- */
|
|
|
|
|
|
+ if (!nfs4_mode_match_open_stateid(opendata->state, fmode))
|
|
return 0;
|
|
return 0;
|
|
opendata->o_arg.open_flags = 0;
|
|
opendata->o_arg.open_flags = 0;
|
|
opendata->o_arg.fmode = fmode;
|
|
opendata->o_arg.fmode = fmode;
|
|
@@ -1597,14 +1608,14 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod
|
|
newstate = nfs4_opendata_to_nfs4_state(opendata);
|
|
newstate = nfs4_opendata_to_nfs4_state(opendata);
|
|
if (IS_ERR(newstate))
|
|
if (IS_ERR(newstate))
|
|
return PTR_ERR(newstate);
|
|
return PTR_ERR(newstate);
|
|
|
|
+ if (newstate != opendata->state)
|
|
|
|
+ ret = -ESTALE;
|
|
nfs4_close_state(newstate, fmode);
|
|
nfs4_close_state(newstate, fmode);
|
|
- *res = newstate;
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
|
|
static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
|
|
{
|
|
{
|
|
- struct nfs4_state *newstate;
|
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */
|
|
/* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */
|
|
@@ -1615,27 +1626,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
clear_bit(NFS_OPEN_STATE, &state->flags);
|
|
clear_bit(NFS_OPEN_STATE, &state->flags);
|
|
smp_rmb();
|
|
smp_rmb();
|
|
- if (state->n_rdwr != 0) {
|
|
|
|
- ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
|
|
|
|
- if (ret != 0)
|
|
|
|
- return ret;
|
|
|
|
- if (newstate != state)
|
|
|
|
- return -ESTALE;
|
|
|
|
- }
|
|
|
|
- if (state->n_wronly != 0) {
|
|
|
|
- ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
|
|
|
|
- if (ret != 0)
|
|
|
|
- return ret;
|
|
|
|
- if (newstate != state)
|
|
|
|
- return -ESTALE;
|
|
|
|
- }
|
|
|
|
- if (state->n_rdonly != 0) {
|
|
|
|
- ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
|
|
|
|
- if (ret != 0)
|
|
|
|
- return ret;
|
|
|
|
- if (newstate != state)
|
|
|
|
- return -ESTALE;
|
|
|
|
- }
|
|
|
|
|
|
+ ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
|
|
|
|
+ if (ret != 0)
|
|
|
|
+ return ret;
|
|
|
|
+ ret = nfs4_open_recover_helper(opendata, FMODE_WRITE);
|
|
|
|
+ if (ret != 0)
|
|
|
|
+ return ret;
|
|
|
|
+ ret = nfs4_open_recover_helper(opendata, FMODE_READ);
|
|
|
|
+ if (ret != 0)
|
|
|
|
+ return ret;
|
|
/*
|
|
/*
|
|
* We may have performed cached opens for all three recoveries.
|
|
* We may have performed cached opens for all three recoveries.
|
|
* Check if we need to update the current stateid.
|
|
* Check if we need to update the current stateid.
|
|
@@ -1759,18 +1758,32 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
|
|
|
|
|
|
+int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
|
|
|
|
+ struct nfs4_state *state, const nfs4_stateid *stateid,
|
|
|
|
+ fmode_t type)
|
|
{
|
|
{
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
struct nfs4_opendata *opendata;
|
|
struct nfs4_opendata *opendata;
|
|
- int err;
|
|
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
|
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
|
NFS4_OPEN_CLAIM_DELEG_CUR_FH);
|
|
NFS4_OPEN_CLAIM_DELEG_CUR_FH);
|
|
if (IS_ERR(opendata))
|
|
if (IS_ERR(opendata))
|
|
return PTR_ERR(opendata);
|
|
return PTR_ERR(opendata);
|
|
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
|
|
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
|
|
- err = nfs4_open_recover(opendata, state);
|
|
|
|
|
|
+ clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
+ switch (type & (FMODE_READ|FMODE_WRITE)) {
|
|
|
|
+ case FMODE_READ|FMODE_WRITE:
|
|
|
|
+ case FMODE_WRITE:
|
|
|
|
+ err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
|
|
|
|
+ if (err)
|
|
|
|
+ break;
|
|
|
|
+ err = nfs4_open_recover_helper(opendata, FMODE_WRITE);
|
|
|
|
+ if (err)
|
|
|
|
+ break;
|
|
|
|
+ case FMODE_READ:
|
|
|
|
+ err = nfs4_open_recover_helper(opendata, FMODE_READ);
|
|
|
|
+ }
|
|
nfs4_opendata_put(opendata);
|
|
nfs4_opendata_put(opendata);
|
|
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
|
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
|
}
|
|
}
|
|
@@ -2645,6 +2658,15 @@ out:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool
|
|
|
|
+nfs4_wait_on_layoutreturn(struct inode *inode, struct rpc_task *task)
|
|
|
|
+{
|
|
|
|
+ if (inode == NULL || !nfs_have_layout(inode))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return pnfs_wait_on_layoutreturn(inode, task);
|
|
|
|
+}
|
|
|
|
+
|
|
struct nfs4_closedata {
|
|
struct nfs4_closedata {
|
|
struct inode *inode;
|
|
struct inode *inode;
|
|
struct nfs4_state *state;
|
|
struct nfs4_state *state;
|
|
@@ -2763,6 +2785,11 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
|
goto out_no_action;
|
|
goto out_no_action;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (nfs4_wait_on_layoutreturn(inode, task)) {
|
|
|
|
+ nfs_release_seqid(calldata->arg.seqid);
|
|
|
|
+ goto out_wait;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (calldata->arg.fmode == 0)
|
|
if (calldata->arg.fmode == 0)
|
|
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
|
|
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
|
|
if (calldata->roc)
|
|
if (calldata->roc)
|
|
@@ -5308,6 +5335,9 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
|
|
|
|
|
|
d_data = (struct nfs4_delegreturndata *)data;
|
|
d_data = (struct nfs4_delegreturndata *)data;
|
|
|
|
|
|
|
|
+ if (nfs4_wait_on_layoutreturn(d_data->inode, task))
|
|
|
|
+ return;
|
|
|
|
+
|
|
if (d_data->roc)
|
|
if (d_data->roc)
|
|
pnfs_roc_get_barrier(d_data->inode, &d_data->roc_barrier);
|
|
pnfs_roc_get_barrier(d_data->inode, &d_data->roc_barrier);
|
|
|
|
|
|
@@ -7800,39 +7830,46 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
|
|
dprintk("%s: NFS4ERR_RECALLCONFLICT waiting %lu\n",
|
|
dprintk("%s: NFS4ERR_RECALLCONFLICT waiting %lu\n",
|
|
__func__, delay);
|
|
__func__, delay);
|
|
rpc_delay(task, delay);
|
|
rpc_delay(task, delay);
|
|
- task->tk_status = 0;
|
|
|
|
- rpc_restart_call_prepare(task);
|
|
|
|
- goto out; /* Do not call nfs4_async_handle_error() */
|
|
|
|
|
|
+ /* Do not call nfs4_async_handle_error() */
|
|
|
|
+ goto out_restart;
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case -NFS4ERR_EXPIRED:
|
|
case -NFS4ERR_EXPIRED:
|
|
case -NFS4ERR_BAD_STATEID:
|
|
case -NFS4ERR_BAD_STATEID:
|
|
spin_lock(&inode->i_lock);
|
|
spin_lock(&inode->i_lock);
|
|
- lo = NFS_I(inode)->layout;
|
|
|
|
- if (!lo || list_empty(&lo->plh_segs)) {
|
|
|
|
|
|
+ if (nfs4_stateid_match(&lgp->args.stateid,
|
|
|
|
+ &lgp->args.ctx->state->stateid)) {
|
|
spin_unlock(&inode->i_lock);
|
|
spin_unlock(&inode->i_lock);
|
|
/* If the open stateid was bad, then recover it. */
|
|
/* If the open stateid was bad, then recover it. */
|
|
state = lgp->args.ctx->state;
|
|
state = lgp->args.ctx->state;
|
|
- } else {
|
|
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ lo = NFS_I(inode)->layout;
|
|
|
|
+ if (lo && nfs4_stateid_match(&lgp->args.stateid,
|
|
|
|
+ &lo->plh_stateid)) {
|
|
LIST_HEAD(head);
|
|
LIST_HEAD(head);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Mark the bad layout state as invalid, then retry
|
|
* Mark the bad layout state as invalid, then retry
|
|
* with the current stateid.
|
|
* with the current stateid.
|
|
*/
|
|
*/
|
|
|
|
+ set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
|
|
pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
|
|
pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
|
|
spin_unlock(&inode->i_lock);
|
|
spin_unlock(&inode->i_lock);
|
|
pnfs_free_lseg_list(&head);
|
|
pnfs_free_lseg_list(&head);
|
|
-
|
|
|
|
- task->tk_status = 0;
|
|
|
|
- rpc_restart_call_prepare(task);
|
|
|
|
- }
|
|
|
|
|
|
+ } else
|
|
|
|
+ spin_unlock(&inode->i_lock);
|
|
|
|
+ goto out_restart;
|
|
}
|
|
}
|
|
if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN)
|
|
if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN)
|
|
- rpc_restart_call_prepare(task);
|
|
|
|
|
|
+ goto out_restart;
|
|
out:
|
|
out:
|
|
dprintk("<-- %s\n", __func__);
|
|
dprintk("<-- %s\n", __func__);
|
|
return;
|
|
return;
|
|
|
|
+out_restart:
|
|
|
|
+ task->tk_status = 0;
|
|
|
|
+ rpc_restart_call_prepare(task);
|
|
|
|
+ return;
|
|
out_overflow:
|
|
out_overflow:
|
|
task->tk_status = -EOVERFLOW;
|
|
task->tk_status = -EOVERFLOW;
|
|
goto out;
|
|
goto out;
|