Commit c58c6610 authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields

nfsd: Protect adding/removing lock owners using client_lock

Once we remove client mutex protection, we'll need to ensure that
stateowner lookup and creation are atomic between concurrent compounds.
Ensure that alloc_init_lock_stateowner checks the hashtable under the
client_lock before adding a new element.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 7ffb5880
...@@ -1000,26 +1000,42 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp) ...@@ -1000,26 +1000,42 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
nfs4_put_stid(&stp->st_stid); nfs4_put_stid(&stp->st_stid);
} }
static void unhash_lockowner(struct nfs4_lockowner *lo) static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
{ {
struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
nfsd_net_id);
lockdep_assert_held(&nn->client_lock);
list_del_init(&lo->lo_owner.so_strhash); list_del_init(&lo->lo_owner.so_strhash);
} }
static void release_lockowner_stateids(struct nfs4_lockowner *lo) static void release_lockowner_stateids(struct nfs4_lockowner *lo)
{ {
struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
nfsd_net_id);
struct nfs4_ol_stateid *stp; struct nfs4_ol_stateid *stp;
lockdep_assert_held(&nn->client_lock);
while (!list_empty(&lo->lo_owner.so_stateids)) { while (!list_empty(&lo->lo_owner.so_stateids)) {
stp = list_first_entry(&lo->lo_owner.so_stateids, stp = list_first_entry(&lo->lo_owner.so_stateids,
struct nfs4_ol_stateid, st_perstateowner); struct nfs4_ol_stateid, st_perstateowner);
spin_unlock(&nn->client_lock);
release_lock_stateid(stp); release_lock_stateid(stp);
spin_lock(&nn->client_lock);
} }
} }
static void release_lockowner(struct nfs4_lockowner *lo) static void release_lockowner(struct nfs4_lockowner *lo)
{ {
unhash_lockowner(lo); struct nfsd_net *nn = net_generic(lo->lo_owner.so_client->net,
nfsd_net_id);
spin_lock(&nn->client_lock);
unhash_lockowner_locked(lo);
release_lockowner_stateids(lo); release_lockowner_stateids(lo);
spin_unlock(&nn->client_lock);
nfs4_put_stateowner(&lo->lo_owner); nfs4_put_stateowner(&lo->lo_owner);
} }
...@@ -4801,7 +4817,7 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) ...@@ -4801,7 +4817,7 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny)
} }
static struct nfs4_lockowner * static struct nfs4_lockowner *
find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner, find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
struct nfsd_net *nn) struct nfsd_net *nn)
{ {
unsigned int strhashval = ownerstr_hashval(clid->cl_id, owner); unsigned int strhashval = ownerstr_hashval(clid->cl_id, owner);
...@@ -4818,9 +4834,25 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner, ...@@ -4818,9 +4834,25 @@ find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
return NULL; return NULL;
} }
static struct nfs4_lockowner *
find_lockowner_str(clientid_t *clid, struct xdr_netobj *owner,
struct nfsd_net *nn)
{
struct nfs4_lockowner *lo;
spin_lock(&nn->client_lock);
lo = find_lockowner_str_locked(clid, owner, nn);
spin_unlock(&nn->client_lock);
return lo;
}
static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop) static void nfs4_unhash_lockowner(struct nfs4_stateowner *sop)
{ {
unhash_lockowner(lockowner(sop)); struct nfsd_net *nn = net_generic(sop->so_client->net, nfsd_net_id);
spin_lock(&nn->client_lock);
unhash_lockowner_locked(lockowner(sop));
spin_unlock(&nn->client_lock);
} }
static void nfs4_free_lockowner(struct nfs4_stateowner *sop) static void nfs4_free_lockowner(struct nfs4_stateowner *sop)
...@@ -4843,9 +4875,12 @@ static const struct nfs4_stateowner_operations lockowner_ops = { ...@@ -4843,9 +4875,12 @@ static const struct nfs4_stateowner_operations lockowner_ops = {
* strhashval = ownerstr_hashval * strhashval = ownerstr_hashval
*/ */
static struct nfs4_lockowner * static struct nfs4_lockowner *
alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp, struct nfsd4_lock *lock) { alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
struct nfs4_lockowner *lo; struct nfs4_ol_stateid *open_stp,
struct nfsd4_lock *lock)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
struct nfs4_lockowner *lo, *ret;
lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp); lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
if (!lo) if (!lo)
...@@ -4854,7 +4889,16 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str ...@@ -4854,7 +4889,16 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
lo->lo_owner.so_is_open_owner = 0; lo->lo_owner.so_is_open_owner = 0;
lo->lo_owner.so_seqid = lock->lk_new_lock_seqid; lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
lo->lo_owner.so_ops = &lockowner_ops; lo->lo_owner.so_ops = &lockowner_ops;
list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); spin_lock(&nn->client_lock);
ret = find_lockowner_str_locked(&clp->cl_clientid,
&lock->lk_new_owner, nn);
if (ret == NULL) {
list_add(&lo->lo_owner.so_strhash,
&nn->ownerstr_hashtbl[strhashval]);
ret = lo;
} else
nfs4_free_lockowner(&lo->lo_owner);
spin_unlock(&nn->client_lock);
return lo; return lo;
} }
...@@ -5395,6 +5439,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, ...@@ -5395,6 +5439,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); unsigned int hashval = ownerstr_hashval(clid->cl_id, owner);
__be32 status; __be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_client *clp;
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id); clid->cl_boot, clid->cl_id);
...@@ -5408,6 +5453,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, ...@@ -5408,6 +5453,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
status = nfserr_locks_held; status = nfserr_locks_held;
/* Find the matching lock stateowner */ /* Find the matching lock stateowner */
spin_lock(&nn->client_lock);
list_for_each_entry(tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) { list_for_each_entry(tmp, &nn->ownerstr_hashtbl[hashval], so_strhash) {
if (tmp->so_is_open_owner) if (tmp->so_is_open_owner)
continue; continue;
...@@ -5417,6 +5463,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, ...@@ -5417,6 +5463,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
break; break;
} }
} }
spin_unlock(&nn->client_lock);
/* No matching owner found, maybe a replay? Just declare victory... */ /* No matching owner found, maybe a replay? Just declare victory... */
if (!sop) { if (!sop) {
...@@ -5426,16 +5473,22 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, ...@@ -5426,16 +5473,22 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
lo = lockowner(sop); lo = lockowner(sop);
/* see if there are still any locks associated with it */ /* see if there are still any locks associated with it */
clp = cstate->clp;
spin_lock(&clp->cl_lock);
list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) { list_for_each_entry(stp, &sop->so_stateids, st_perstateowner) {
if (check_for_locks(stp->st_stid.sc_file, lo)) { if (check_for_locks(stp->st_stid.sc_file, lo)) {
nfs4_put_stateowner(sop); spin_unlock(&clp->cl_lock);
goto out; goto out;
} }
} }
spin_unlock(&clp->cl_lock);
status = nfs_ok; status = nfs_ok;
sop = NULL;
release_lockowner(lo); release_lockowner(lo);
out: out:
if (sop)
nfs4_put_stateowner(sop);
nfs4_unlock_state(); nfs4_unlock_state();
return status; return status;
} }
......
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