diff options
author | Jeff Layton <jlayton@primarydata.com> | 2014-07-29 21:34:13 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-07-31 14:20:07 -0400 |
commit | 356a95ece7aab38ae464e1041da26dcc1dff7ad2 (patch) | |
tree | fad6d6096f39fe4d9aa2df803b6f2e7d21877916 /fs/nfsd/nfs4state.c | |
parent | 1c755dc1ada95adc9aa41102baada73659397b80 (diff) |
nfsd: clean up races in lock stateid searching and creation
Preparation for removal of the client_mutex.
Currently, no lock aside from the client_mutex is held when calling
find_lock_state. Ensure that the cl_lock is held by adding a lockdep
assertion.
Once we remove the client_mutex, it'll be possible for another thread to
race in and insert a lock state for the same file after we search but
before we insert a new one. Ensure that doesn't happen by redoing the
search after allocating a new stid that we plan to insert. If one is
found just put the one that was allocated.
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 71 |
1 files changed, 49 insertions, 22 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3ac6e2fdabe5..59d44873b68b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -4719,20 +4719,15 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str | |||
4719 | return lo; | 4719 | return lo; |
4720 | } | 4720 | } |
4721 | 4721 | ||
4722 | static struct nfs4_ol_stateid * | 4722 | static void |
4723 | alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, | 4723 | init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo, |
4724 | struct inode *inode, | 4724 | struct nfs4_file *fp, struct inode *inode, |
4725 | struct nfs4_ol_stateid *open_stp) | 4725 | struct nfs4_ol_stateid *open_stp) |
4726 | { | 4726 | { |
4727 | struct nfs4_stid *s; | ||
4728 | struct nfs4_openowner *oo = openowner(open_stp->st_stateowner); | ||
4729 | struct nfs4_ol_stateid *stp; | ||
4730 | struct nfs4_client *clp = lo->lo_owner.so_client; | 4727 | struct nfs4_client *clp = lo->lo_owner.so_client; |
4731 | 4728 | ||
4732 | s = nfs4_alloc_stid(clp, stateid_slab); | 4729 | lockdep_assert_held(&clp->cl_lock); |
4733 | if (s == NULL) | 4730 | |
4734 | return NULL; | ||
4735 | stp = openlockstateid(s); | ||
4736 | stp->st_stid.sc_type = NFS4_LOCK_STID; | 4731 | stp->st_stid.sc_type = NFS4_LOCK_STID; |
4737 | stp->st_stateowner = &lo->lo_owner; | 4732 | stp->st_stateowner = &lo->lo_owner; |
4738 | get_nfs4_file(fp); | 4733 | get_nfs4_file(fp); |
@@ -4741,20 +4736,20 @@ alloc_init_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp, | |||
4741 | stp->st_access_bmap = 0; | 4736 | stp->st_access_bmap = 0; |
4742 | stp->st_deny_bmap = open_stp->st_deny_bmap; | 4737 | stp->st_deny_bmap = open_stp->st_deny_bmap; |
4743 | stp->st_openstp = open_stp; | 4738 | stp->st_openstp = open_stp; |
4744 | spin_lock(&oo->oo_owner.so_client->cl_lock); | ||
4745 | list_add(&stp->st_locks, &open_stp->st_locks); | 4739 | list_add(&stp->st_locks, &open_stp->st_locks); |
4746 | list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); | 4740 | list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); |
4747 | spin_lock(&fp->fi_lock); | 4741 | spin_lock(&fp->fi_lock); |
4748 | list_add(&stp->st_perfile, &fp->fi_stateids); | 4742 | list_add(&stp->st_perfile, &fp->fi_stateids); |
4749 | spin_unlock(&fp->fi_lock); | 4743 | spin_unlock(&fp->fi_lock); |
4750 | spin_unlock(&oo->oo_owner.so_client->cl_lock); | ||
4751 | return stp; | ||
4752 | } | 4744 | } |
4753 | 4745 | ||
4754 | static struct nfs4_ol_stateid * | 4746 | static struct nfs4_ol_stateid * |
4755 | find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp) | 4747 | find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp) |
4756 | { | 4748 | { |
4757 | struct nfs4_ol_stateid *lst; | 4749 | struct nfs4_ol_stateid *lst; |
4750 | struct nfs4_client *clp = lo->lo_owner.so_client; | ||
4751 | |||
4752 | lockdep_assert_held(&clp->cl_lock); | ||
4758 | 4753 | ||
4759 | list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) { | 4754 | list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) { |
4760 | if (lst->st_stid.sc_file == fp) | 4755 | if (lst->st_stid.sc_file == fp) |
@@ -4763,6 +4758,38 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp) | |||
4763 | return NULL; | 4758 | return NULL; |
4764 | } | 4759 | } |
4765 | 4760 | ||
4761 | static struct nfs4_ol_stateid * | ||
4762 | find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi, | ||
4763 | struct inode *inode, struct nfs4_ol_stateid *ost, | ||
4764 | bool *new) | ||
4765 | { | ||
4766 | struct nfs4_stid *ns = NULL; | ||
4767 | struct nfs4_ol_stateid *lst; | ||
4768 | struct nfs4_openowner *oo = openowner(ost->st_stateowner); | ||
4769 | struct nfs4_client *clp = oo->oo_owner.so_client; | ||
4770 | |||
4771 | spin_lock(&clp->cl_lock); | ||
4772 | lst = find_lock_stateid(lo, fi); | ||
4773 | if (lst == NULL) { | ||
4774 | spin_unlock(&clp->cl_lock); | ||
4775 | ns = nfs4_alloc_stid(clp, stateid_slab); | ||
4776 | if (ns == NULL) | ||
4777 | return NULL; | ||
4778 | |||
4779 | spin_lock(&clp->cl_lock); | ||
4780 | lst = find_lock_stateid(lo, fi); | ||
4781 | if (likely(!lst)) { | ||
4782 | lst = openlockstateid(ns); | ||
4783 | init_lock_stateid(lst, lo, fi, inode, ost); | ||
4784 | ns = NULL; | ||
4785 | *new = true; | ||
4786 | } | ||
4787 | } | ||
4788 | spin_unlock(&clp->cl_lock); | ||
4789 | if (ns) | ||
4790 | nfs4_put_stid(ns); | ||
4791 | return lst; | ||
4792 | } | ||
4766 | 4793 | ||
4767 | static int | 4794 | static int |
4768 | check_lock_length(u64 offset, u64 length) | 4795 | check_lock_length(u64 offset, u64 length) |
@@ -4783,7 +4810,11 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access) | |||
4783 | set_access(access, lock_stp); | 4810 | set_access(access, lock_stp); |
4784 | } | 4811 | } |
4785 | 4812 | ||
4786 | static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new) | 4813 | static __be32 |
4814 | lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, | ||
4815 | struct nfs4_ol_stateid *ost, | ||
4816 | struct nfsd4_lock *lock, | ||
4817 | struct nfs4_ol_stateid **lst, bool *new) | ||
4787 | { | 4818 | { |
4788 | struct nfs4_file *fi = ost->st_stid.sc_file; | 4819 | struct nfs4_file *fi = ost->st_stid.sc_file; |
4789 | struct nfs4_openowner *oo = openowner(ost->st_stateowner); | 4820 | struct nfs4_openowner *oo = openowner(ost->st_stateowner); |
@@ -4807,14 +4838,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s | |||
4807 | return nfserr_bad_seqid; | 4838 | return nfserr_bad_seqid; |
4808 | } | 4839 | } |
4809 | 4840 | ||
4810 | *lst = find_lock_stateid(lo, fi); | 4841 | *lst = find_or_create_lock_stateid(lo, fi, inode, ost, new); |
4811 | if (*lst == NULL) { | 4842 | if (*lst == NULL) { |
4812 | *lst = alloc_init_lock_stateid(lo, fi, inode, ost); | 4843 | release_lockowner_if_empty(lo); |
4813 | if (*lst == NULL) { | 4844 | return nfserr_jukebox; |
4814 | release_lockowner_if_empty(lo); | ||
4815 | return nfserr_jukebox; | ||
4816 | } | ||
4817 | *new = true; | ||
4818 | } | 4845 | } |
4819 | return nfs_ok; | 4846 | return nfs_ok; |
4820 | } | 4847 | } |