aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@poochiereds.net>2015-08-24 12:41:47 -0400
committerJ. Bruce Fields <bfields@redhat.com>2015-08-31 16:32:15 -0400
commite85687393f3ee0a77ccca016f903d1558bb69258 (patch)
tree6683eba3ed1b59e299f085ed56d98d2fc67530f6 /fs/nfsd
parent051ac3848a94f21cfdec899cc9c65ce7f9f116fa (diff)
nfsd: ensure that the ol stateid hash reference is only put once
When an open or lock stateid is hashed, we take an extra reference to it. When we unhash it, we drop that reference. The code however does not properly account for the case where we have two callers concurrently trying to unhash the stateid. This can lead to list corruption and the hash reference being put more than once. Fix this by having unhash_ol_stateid use list_del_init on the st_perfile list_head, and then testing to see if that list_head is empty before releasing the hash reference. This means that some of the unhashing wrappers now become bool return functions so we can test to see whether the stateid was unhashed before we put the reference. Reported-by: Andrew W Elble <aweits@rit.edu> Tested-by: Andrew W Elble <aweits@rit.edu> Reported-by: Anna Schumaker <Anna.Schumaker@netapp.com> Tested-by: Anna Schumaker <Anna.Schumaker@netapp.com> Signed-off-by: Jeff Layton <jeff.layton@primarydata.com> Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c58
1 files changed, 36 insertions, 22 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index af88d1d7ccae..f318e706cb35 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1009,16 +1009,20 @@ static void nfs4_put_stateowner(struct nfs4_stateowner *sop)
1009 nfs4_free_stateowner(sop); 1009 nfs4_free_stateowner(sop);
1010} 1010}
1011 1011
1012static void unhash_ol_stateid(struct nfs4_ol_stateid *stp) 1012static bool unhash_ol_stateid(struct nfs4_ol_stateid *stp)
1013{ 1013{
1014 struct nfs4_file *fp = stp->st_stid.sc_file; 1014 struct nfs4_file *fp = stp->st_stid.sc_file;
1015 1015
1016 lockdep_assert_held(&stp->st_stateowner->so_client->cl_lock); 1016 lockdep_assert_held(&stp->st_stateowner->so_client->cl_lock);
1017 1017
1018 if (list_empty(&stp->st_perfile))
1019 return false;
1020
1018 spin_lock(&fp->fi_lock); 1021 spin_lock(&fp->fi_lock);
1019 list_del(&stp->st_perfile); 1022 list_del_init(&stp->st_perfile);
1020 spin_unlock(&fp->fi_lock); 1023 spin_unlock(&fp->fi_lock);
1021 list_del(&stp->st_perstateowner); 1024 list_del(&stp->st_perstateowner);
1025 return true;
1022} 1026}
1023 1027
1024static void nfs4_free_ol_stateid(struct nfs4_stid *stid) 1028static void nfs4_free_ol_stateid(struct nfs4_stid *stid)
@@ -1068,25 +1072,27 @@ static void put_ol_stateid_locked(struct nfs4_ol_stateid *stp,
1068 list_add(&stp->st_locks, reaplist); 1072 list_add(&stp->st_locks, reaplist);
1069} 1073}
1070 1074
1071static void unhash_lock_stateid(struct nfs4_ol_stateid *stp) 1075static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp)
1072{ 1076{
1073 struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); 1077 struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
1074 1078
1075 lockdep_assert_held(&oo->oo_owner.so_client->cl_lock); 1079 lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
1076 1080
1077 list_del_init(&stp->st_locks); 1081 list_del_init(&stp->st_locks);
1078 unhash_ol_stateid(stp);
1079 nfs4_unhash_stid(&stp->st_stid); 1082 nfs4_unhash_stid(&stp->st_stid);
1083 return unhash_ol_stateid(stp);
1080} 1084}
1081 1085
1082static void release_lock_stateid(struct nfs4_ol_stateid *stp) 1086static void release_lock_stateid(struct nfs4_ol_stateid *stp)
1083{ 1087{
1084 struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); 1088 struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
1089 bool unhashed;
1085 1090
1086 spin_lock(&oo->oo_owner.so_client->cl_lock); 1091 spin_lock(&oo->oo_owner.so_client->cl_lock);
1087 unhash_lock_stateid(stp); 1092 unhashed = unhash_lock_stateid(stp);
1088 spin_unlock(&oo->oo_owner.so_client->cl_lock); 1093 spin_unlock(&oo->oo_owner.so_client->cl_lock);
1089 nfs4_put_stid(&stp->st_stid); 1094 if (unhashed)
1095 nfs4_put_stid(&stp->st_stid);
1090} 1096}
1091 1097
1092static void unhash_lockowner_locked(struct nfs4_lockowner *lo) 1098static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
@@ -1134,7 +1140,7 @@ static void release_lockowner(struct nfs4_lockowner *lo)
1134 while (!list_empty(&lo->lo_owner.so_stateids)) { 1140 while (!list_empty(&lo->lo_owner.so_stateids)) {
1135 stp = list_first_entry(&lo->lo_owner.so_stateids, 1141 stp = list_first_entry(&lo->lo_owner.so_stateids,
1136 struct nfs4_ol_stateid, st_perstateowner); 1142 struct nfs4_ol_stateid, st_perstateowner);
1137 unhash_lock_stateid(stp); 1143 WARN_ON(!unhash_lock_stateid(stp));
1138 put_ol_stateid_locked(stp, &reaplist); 1144 put_ol_stateid_locked(stp, &reaplist);
1139 } 1145 }
1140 spin_unlock(&clp->cl_lock); 1146 spin_unlock(&clp->cl_lock);
@@ -1147,21 +1153,26 @@ static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp,
1147{ 1153{
1148 struct nfs4_ol_stateid *stp; 1154 struct nfs4_ol_stateid *stp;
1149 1155
1156 lockdep_assert_held(&open_stp->st_stid.sc_client->cl_lock);
1157
1150 while (!list_empty(&open_stp->st_locks)) { 1158 while (!list_empty(&open_stp->st_locks)) {
1151 stp = list_entry(open_stp->st_locks.next, 1159 stp = list_entry(open_stp->st_locks.next,
1152 struct nfs4_ol_stateid, st_locks); 1160 struct nfs4_ol_stateid, st_locks);
1153 unhash_lock_stateid(stp); 1161 WARN_ON(!unhash_lock_stateid(stp));
1154 put_ol_stateid_locked(stp, reaplist); 1162 put_ol_stateid_locked(stp, reaplist);
1155 } 1163 }
1156} 1164}
1157 1165
1158static void unhash_open_stateid(struct nfs4_ol_stateid *stp, 1166static bool unhash_open_stateid(struct nfs4_ol_stateid *stp,
1159 struct list_head *reaplist) 1167 struct list_head *reaplist)
1160{ 1168{
1169 bool unhashed;
1170
1161 lockdep_assert_held(&stp->st_stid.sc_client->cl_lock); 1171 lockdep_assert_held(&stp->st_stid.sc_client->cl_lock);
1162 1172
1163 unhash_ol_stateid(stp); 1173 unhashed = unhash_ol_stateid(stp);
1164 release_open_stateid_locks(stp, reaplist); 1174 release_open_stateid_locks(stp, reaplist);
1175 return unhashed;
1165} 1176}
1166 1177
1167static void release_open_stateid(struct nfs4_ol_stateid *stp) 1178static void release_open_stateid(struct nfs4_ol_stateid *stp)
@@ -1169,8 +1180,8 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
1169 LIST_HEAD(reaplist); 1180 LIST_HEAD(reaplist);
1170 1181
1171 spin_lock(&stp->st_stid.sc_client->cl_lock); 1182 spin_lock(&stp->st_stid.sc_client->cl_lock);
1172 unhash_open_stateid(stp, &reaplist); 1183 if (unhash_open_stateid(stp, &reaplist))
1173 put_ol_stateid_locked(stp, &reaplist); 1184 put_ol_stateid_locked(stp, &reaplist);
1174 spin_unlock(&stp->st_stid.sc_client->cl_lock); 1185 spin_unlock(&stp->st_stid.sc_client->cl_lock);
1175 free_ol_stateid_reaplist(&reaplist); 1186 free_ol_stateid_reaplist(&reaplist);
1176} 1187}
@@ -1215,8 +1226,8 @@ static void release_openowner(struct nfs4_openowner *oo)
1215 while (!list_empty(&oo->oo_owner.so_stateids)) { 1226 while (!list_empty(&oo->oo_owner.so_stateids)) {
1216 stp = list_first_entry(&oo->oo_owner.so_stateids, 1227 stp = list_first_entry(&oo->oo_owner.so_stateids,
1217 struct nfs4_ol_stateid, st_perstateowner); 1228 struct nfs4_ol_stateid, st_perstateowner);
1218 unhash_open_stateid(stp, &reaplist); 1229 if (unhash_open_stateid(stp, &reaplist))
1219 put_ol_stateid_locked(stp, &reaplist); 1230 put_ol_stateid_locked(stp, &reaplist);
1220 } 1231 }
1221 spin_unlock(&clp->cl_lock); 1232 spin_unlock(&clp->cl_lock);
1222 free_ol_stateid_reaplist(&reaplist); 1233 free_ol_stateid_reaplist(&reaplist);
@@ -4752,7 +4763,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
4752 if (check_for_locks(stp->st_stid.sc_file, 4763 if (check_for_locks(stp->st_stid.sc_file,
4753 lockowner(stp->st_stateowner))) 4764 lockowner(stp->st_stateowner)))
4754 break; 4765 break;
4755 unhash_lock_stateid(stp); 4766 WARN_ON(!unhash_lock_stateid(stp));
4756 spin_unlock(&cl->cl_lock); 4767 spin_unlock(&cl->cl_lock);
4757 nfs4_put_stid(s); 4768 nfs4_put_stid(s);
4758 ret = nfs_ok; 4769 ret = nfs_ok;
@@ -4968,20 +4979,23 @@ out:
4968static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) 4979static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
4969{ 4980{
4970 struct nfs4_client *clp = s->st_stid.sc_client; 4981 struct nfs4_client *clp = s->st_stid.sc_client;
4982 bool unhashed;
4971 LIST_HEAD(reaplist); 4983 LIST_HEAD(reaplist);
4972 4984
4973 s->st_stid.sc_type = NFS4_CLOSED_STID; 4985 s->st_stid.sc_type = NFS4_CLOSED_STID;
4974 spin_lock(&clp->cl_lock); 4986 spin_lock(&clp->cl_lock);
4975 unhash_open_stateid(s, &reaplist); 4987 unhashed = unhash_open_stateid(s, &reaplist);
4976 4988
4977 if (clp->cl_minorversion) { 4989 if (clp->cl_minorversion) {
4978 put_ol_stateid_locked(s, &reaplist); 4990 if (unhashed)
4991 put_ol_stateid_locked(s, &reaplist);
4979 spin_unlock(&clp->cl_lock); 4992 spin_unlock(&clp->cl_lock);
4980 free_ol_stateid_reaplist(&reaplist); 4993 free_ol_stateid_reaplist(&reaplist);
4981 } else { 4994 } else {
4982 spin_unlock(&clp->cl_lock); 4995 spin_unlock(&clp->cl_lock);
4983 free_ol_stateid_reaplist(&reaplist); 4996 free_ol_stateid_reaplist(&reaplist);
4984 move_to_close_lru(s, clp->net); 4997 if (unhashed)
4998 move_to_close_lru(s, clp->net);
4985 } 4999 }
4986} 5000}
4987 5001
@@ -6014,7 +6028,7 @@ nfsd_inject_add_lock_to_list(struct nfs4_ol_stateid *lst,
6014 6028
6015static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, 6029static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
6016 struct list_head *collect, 6030 struct list_head *collect,
6017 void (*func)(struct nfs4_ol_stateid *)) 6031 bool (*func)(struct nfs4_ol_stateid *))
6018{ 6032{
6019 struct nfs4_openowner *oop; 6033 struct nfs4_openowner *oop;
6020 struct nfs4_ol_stateid *stp, *st_next; 6034 struct nfs4_ol_stateid *stp, *st_next;
@@ -6028,9 +6042,9 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
6028 list_for_each_entry_safe(lst, lst_next, 6042 list_for_each_entry_safe(lst, lst_next,
6029 &stp->st_locks, st_locks) { 6043 &stp->st_locks, st_locks) {
6030 if (func) { 6044 if (func) {
6031 func(lst); 6045 if (func(lst))
6032 nfsd_inject_add_lock_to_list(lst, 6046 nfsd_inject_add_lock_to_list(lst,
6033 collect); 6047 collect);
6034 } 6048 }
6035 ++count; 6049 ++count;
6036 /* 6050 /*