Commit 2597641d authored by Alexandros Batsakis's avatar Alexandros Batsakis Committed by Trond Myklebust

nfs41: v2 fix cb_recall bug

in NFSv4.1 the seqid part of a stateid in CB_RECALL must be 0
Signed-off-by: default avatarAlexandros Batsakis <batsakis@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 0629e370
...@@ -106,6 +106,8 @@ struct cb_sequenceres { ...@@ -106,6 +106,8 @@ struct cb_sequenceres {
extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res); struct cb_sequenceres *res);
extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid);
#define RCA4_TYPE_MASK_RDATA_DLG 0 #define RCA4_TYPE_MASK_RDATA_DLG 0
#define RCA4_TYPE_MASK_WDATA_DLG 1 #define RCA4_TYPE_MASK_WDATA_DLG 1
...@@ -125,8 +127,9 @@ extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); ...@@ -125,8 +127,9 @@ extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
extern void nfs_callback_down(int minorversion); extern void nfs_callback_down(int minorversion);
extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
const nfs4_stateid *stateid);
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
/* /*
* nfs41: Callbacks are expected to not cause substantial latency, * nfs41: Callbacks are expected to not cause substantial latency,
* so we limit their concurrency to 1 by setting up the maximum number * so we limit their concurrency to 1 by setting up the maximum number
......
...@@ -61,6 +61,16 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres * ...@@ -61,6 +61,16 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
return res->status; return res->status;
} }
static int (*nfs_validate_delegation_stateid(struct nfs_client *clp))(struct nfs_delegation *, const nfs4_stateid *)
{
#if defined(CONFIG_NFS_V4_1)
if (clp->cl_minorversion > 0)
return nfs41_validate_delegation_stateid;
#endif
return nfs4_validate_delegation_stateid;
}
__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
{ {
struct nfs_client *clp; struct nfs_client *clp;
...@@ -81,7 +91,8 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) ...@@ -81,7 +91,8 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
inode = nfs_delegation_find_inode(clp, &args->fh); inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode != NULL) { if (inode != NULL) {
/* Set up a helper thread to actually return the delegation */ /* Set up a helper thread to actually return the delegation */
switch(nfs_async_inode_return_delegation(inode, &args->stateid)) { switch (nfs_async_inode_return_delegation(inode, &args->stateid,
nfs_validate_delegation_stateid(clp))) {
case 0: case 0:
res = 0; res = 0;
break; break;
...@@ -102,8 +113,31 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) ...@@ -102,8 +113,31 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
return res; return res;
} }
int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid)
{
if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0)
return 0;
return 1;
}
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid)
{
if (delegation == NULL)
return 0;
/* seqid is 4-bytes long */
if (((u32 *) &stateid->data)[0] != 0)
return 0;
if (memcmp(&delegation->stateid.data[4], &stateid->data[4],
sizeof(stateid->data)-4))
return 0;
return 1;
}
/* /*
* Validate the sequenceID sent by the server. * Validate the sequenceID sent by the server.
* Return success if the sequenceID is one more than what we last saw on * Return success if the sequenceID is one more than what we last saw on
...@@ -255,5 +289,4 @@ unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy) ...@@ -255,5 +289,4 @@ unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status; return status;
} }
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
...@@ -454,18 +454,21 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp) ...@@ -454,18 +454,21 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
/* /*
* Asynchronous delegation recall! * Asynchronous delegation recall!
*/ */
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid,
int (*validate_stateid)(struct nfs_delegation *delegation,
const nfs4_stateid *stateid))
{ {
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation); delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0) { if (!validate_stateid(delegation, stateid)) {
rcu_read_unlock(); rcu_read_unlock();
return -ENOENT; return -ENOENT;
} }
nfs_mark_return_delegation(clp, delegation); nfs_mark_return_delegation(clp, delegation);
rcu_read_unlock(); rcu_read_unlock();
nfs_delegation_run_state_manager(clp); nfs_delegation_run_state_manager(clp);
......
...@@ -34,7 +34,9 @@ enum { ...@@ -34,7 +34,9 @@ enum {
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode); int nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid,
int (*validate_stateid)(struct nfs_delegation *delegation,
const nfs4_stateid *stateid));
void nfs_inode_return_delegation_noreclaim(struct inode *inode); void nfs_inode_return_delegation_noreclaim(struct inode *inode);
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
......
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