Commit 6ee41268 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Don't call OPEN if we already have an open stateid for a file

If we already have a stateid with the correct open mode for a given file,
then we can reuse that stateid instead of re-issuing an OPEN call without
violating the close-to-open caching semantics.
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent aac00a8d
...@@ -324,6 +324,24 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) ...@@ -324,6 +324,24 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
return ret; return ret;
} }
static int can_open_cached(struct nfs4_state *state, int mode)
{
int ret = 0;
switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
case FMODE_READ:
ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
break;
case FMODE_WRITE:
ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0;
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
break;
case FMODE_READ|FMODE_WRITE:
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
}
return ret;
}
static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags) static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
{ {
if ((delegation->type & open_flags) != open_flags) if ((delegation->type & open_flags) != open_flags)
...@@ -407,7 +425,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open ...@@ -407,7 +425,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
} }
static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata) static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
{ {
struct nfs4_state *state = opendata->state; struct nfs4_state *state = opendata->state;
struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_inode *nfsi = NFS_I(state->inode);
...@@ -418,9 +436,19 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata ...@@ -418,9 +436,19 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation); delegation = rcu_dereference(nfsi->delegation);
if (delegation == NULL)
goto out_unlock;
for (;;) { for (;;) {
if (can_open_cached(state, open_mode)) {
spin_lock(&state->owner->so_lock);
if (can_open_cached(state, open_mode)) {
update_open_stateflags(state, open_mode);
spin_unlock(&state->owner->so_lock);
rcu_read_unlock();
goto out_return_state;
}
spin_unlock(&state->owner->so_lock);
}
if (delegation == NULL)
break;
if (!can_open_delegated(delegation, open_mode)) if (!can_open_delegated(delegation, open_mode))
break; break;
/* Save the delegation */ /* Save the delegation */
...@@ -434,8 +462,9 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata ...@@ -434,8 +462,9 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata
ret = -EAGAIN; ret = -EAGAIN;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation); delegation = rcu_dereference(nfsi->delegation);
/* If no delegation, try a cached open */
if (delegation == NULL) if (delegation == NULL)
break; continue;
/* Is the delegation still valid? */ /* Is the delegation still valid? */
if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0) if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0)
continue; continue;
...@@ -443,7 +472,6 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata ...@@ -443,7 +472,6 @@ static struct nfs4_state *nfs4_try_open_delegated(struct nfs4_opendata *opendata
update_open_stateid(state, NULL, &stateid, open_mode); update_open_stateid(state, NULL, &stateid, open_mode);
goto out_return_state; goto out_return_state;
} }
out_unlock:
rcu_read_unlock(); rcu_read_unlock();
out: out:
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -461,7 +489,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data ...@@ -461,7 +489,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
int ret; int ret;
if (!data->rpc_done) { if (!data->rpc_done) {
state = nfs4_try_open_delegated(data); state = nfs4_try_open_cached(data);
goto out; goto out;
} }
...@@ -775,13 +803,14 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -775,13 +803,14 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
if (data->state != NULL) { if (data->state != NULL) {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL)))
goto out_no_action;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation); delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
if (delegation != NULL && if (delegation != NULL &&
(delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) { (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) {
rcu_read_unlock(); rcu_read_unlock();
task->tk_action = NULL; goto out_no_action;
return;
} }
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -792,6 +821,10 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) ...@@ -792,6 +821,10 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
data->timestamp = jiffies; data->timestamp = jiffies;
rpc_call_setup(task, &msg, 0); rpc_call_setup(task, &msg, 0);
return;
out_no_action:
task->tk_action = NULL;
} }
static void nfs4_open_done(struct rpc_task *task, void *calldata) static void nfs4_open_done(struct rpc_task *task, void *calldata)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment