aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/ulp
diff options
context:
space:
mode:
authorShlomo Pongratz <shlomop@mellanox.com>2012-08-29 11:14:34 -0400
committerRoland Dreier <roland@purestorage.com>2012-09-12 12:21:45 -0400
commitb5120a6e11e90d98d8a752545ac60bfa1ea95f1a (patch)
treef376dd84bf679fb2b3ed5ae18af967455a75a958 /drivers/infiniband/ulp
parent66172c09938bfc4efdcf9b5e0246a85b9b76dd54 (diff)
IPoIB: Fix AB-BA deadlock when deleting neighbours
Lockdep points out a circular locking dependency betwwen the ipoib device priv spinlock (priv->lock) and the neighbour table rwlock (ntbl->rwlock). In the normal path, ie neigbour garbage collection task, the neigh table rwlock is taken first and then if the neighbour needs to be deleted, priv->lock is taken. However in some error paths, such as in ipoib_cm_handle_tx_wc(), priv->lock is taken first and then ipoib_neigh_free routine is called which in turn takes the neighbour table ntbl->rwlock. The solution is to get rid the neigh table rwlock completely and use only priv->lock. Signed-off-by: Shlomo Pongratz <shlomop@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband/ulp')
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c70
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c2
3 files changed, 27 insertions, 46 deletions
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index e6bbeae1c30..0af216d21f8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -274,7 +274,6 @@ struct ipoib_neigh_hash {
274 274
275struct ipoib_neigh_table { 275struct ipoib_neigh_table {
276 struct ipoib_neigh_hash __rcu *htbl; 276 struct ipoib_neigh_hash __rcu *htbl;
277 rwlock_t rwlock;
278 atomic_t entries; 277 atomic_t entries;
279 struct completion flushed; 278 struct completion flushed;
280 struct completion deleted; 279 struct completion deleted;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 72c1fc28f07..1e19b5ae7c4 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -546,15 +546,15 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
546 struct ipoib_neigh *neigh; 546 struct ipoib_neigh *neigh;
547 unsigned long flags; 547 unsigned long flags;
548 548
549 spin_lock_irqsave(&priv->lock, flags);
549 neigh = ipoib_neigh_alloc(daddr, dev); 550 neigh = ipoib_neigh_alloc(daddr, dev);
550 if (!neigh) { 551 if (!neigh) {
552 spin_unlock_irqrestore(&priv->lock, flags);
551 ++dev->stats.tx_dropped; 553 ++dev->stats.tx_dropped;
552 dev_kfree_skb_any(skb); 554 dev_kfree_skb_any(skb);
553 return; 555 return;
554 } 556 }
555 557
556 spin_lock_irqsave(&priv->lock, flags);
557
558 path = __path_find(dev, daddr + 4); 558 path = __path_find(dev, daddr + 4);
559 if (!path) { 559 if (!path) {
560 path = path_rec_create(dev, daddr + 4); 560 path = path_rec_create(dev, daddr + 4);
@@ -863,10 +863,10 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
863 if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) 863 if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
864 return; 864 return;
865 865
866 write_lock_bh(&ntbl->rwlock); 866 spin_lock_irqsave(&priv->lock, flags);
867 867
868 htbl = rcu_dereference_protected(ntbl->htbl, 868 htbl = rcu_dereference_protected(ntbl->htbl,
869 lockdep_is_held(&ntbl->rwlock)); 869 lockdep_is_held(&priv->lock));
870 870
871 if (!htbl) 871 if (!htbl)
872 goto out_unlock; 872 goto out_unlock;
@@ -883,16 +883,14 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
883 struct ipoib_neigh __rcu **np = &htbl->buckets[i]; 883 struct ipoib_neigh __rcu **np = &htbl->buckets[i];
884 884
885 while ((neigh = rcu_dereference_protected(*np, 885 while ((neigh = rcu_dereference_protected(*np,
886 lockdep_is_held(&ntbl->rwlock))) != NULL) { 886 lockdep_is_held(&priv->lock))) != NULL) {
887 /* was the neigh idle for two GC periods */ 887 /* was the neigh idle for two GC periods */
888 if (time_after(neigh_obsolete, neigh->alive)) { 888 if (time_after(neigh_obsolete, neigh->alive)) {
889 rcu_assign_pointer(*np, 889 rcu_assign_pointer(*np,
890 rcu_dereference_protected(neigh->hnext, 890 rcu_dereference_protected(neigh->hnext,
891 lockdep_is_held(&ntbl->rwlock))); 891 lockdep_is_held(&priv->lock)));
892 /* remove from path/mc list */ 892 /* remove from path/mc list */
893 spin_lock_irqsave(&priv->lock, flags);
894 list_del(&neigh->list); 893 list_del(&neigh->list);
895 spin_unlock_irqrestore(&priv->lock, flags);
896 call_rcu(&neigh->rcu, ipoib_neigh_reclaim); 894 call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
897 } else { 895 } else {
898 np = &neigh->hnext; 896 np = &neigh->hnext;
@@ -902,7 +900,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
902 } 900 }
903 901
904out_unlock: 902out_unlock:
905 write_unlock_bh(&ntbl->rwlock); 903 spin_unlock_irqrestore(&priv->lock, flags);
906} 904}
907 905
908static void ipoib_reap_neigh(struct work_struct *work) 906static void ipoib_reap_neigh(struct work_struct *work)
@@ -947,10 +945,8 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
947 struct ipoib_neigh *neigh; 945 struct ipoib_neigh *neigh;
948 u32 hash_val; 946 u32 hash_val;
949 947
950 write_lock_bh(&ntbl->rwlock);
951
952 htbl = rcu_dereference_protected(ntbl->htbl, 948 htbl = rcu_dereference_protected(ntbl->htbl,
953 lockdep_is_held(&ntbl->rwlock)); 949 lockdep_is_held(&priv->lock));
954 if (!htbl) { 950 if (!htbl) {
955 neigh = NULL; 951 neigh = NULL;
956 goto out_unlock; 952 goto out_unlock;
@@ -961,10 +957,10 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
961 */ 957 */
962 hash_val = ipoib_addr_hash(htbl, daddr); 958 hash_val = ipoib_addr_hash(htbl, daddr);
963 for (neigh = rcu_dereference_protected(htbl->buckets[hash_val], 959 for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
964 lockdep_is_held(&ntbl->rwlock)); 960 lockdep_is_held(&priv->lock));
965 neigh != NULL; 961 neigh != NULL;
966 neigh = rcu_dereference_protected(neigh->hnext, 962 neigh = rcu_dereference_protected(neigh->hnext,
967 lockdep_is_held(&ntbl->rwlock))) { 963 lockdep_is_held(&priv->lock))) {
968 if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) { 964 if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
969 /* found, take one ref on behalf of the caller */ 965 /* found, take one ref on behalf of the caller */
970 if (!atomic_inc_not_zero(&neigh->refcnt)) { 966 if (!atomic_inc_not_zero(&neigh->refcnt)) {
@@ -987,12 +983,11 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
987 /* put in hash */ 983 /* put in hash */
988 rcu_assign_pointer(neigh->hnext, 984 rcu_assign_pointer(neigh->hnext,
989 rcu_dereference_protected(htbl->buckets[hash_val], 985 rcu_dereference_protected(htbl->buckets[hash_val],
990 lockdep_is_held(&ntbl->rwlock))); 986 lockdep_is_held(&priv->lock)));
991 rcu_assign_pointer(htbl->buckets[hash_val], neigh); 987 rcu_assign_pointer(htbl->buckets[hash_val], neigh);
992 atomic_inc(&ntbl->entries); 988 atomic_inc(&ntbl->entries);
993 989
994out_unlock: 990out_unlock:
995 write_unlock_bh(&ntbl->rwlock);
996 991
997 return neigh; 992 return neigh;
998} 993}
@@ -1040,35 +1035,29 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
1040 struct ipoib_neigh *n; 1035 struct ipoib_neigh *n;
1041 u32 hash_val; 1036 u32 hash_val;
1042 1037
1043 write_lock_bh(&ntbl->rwlock);
1044
1045 htbl = rcu_dereference_protected(ntbl->htbl, 1038 htbl = rcu_dereference_protected(ntbl->htbl,
1046 lockdep_is_held(&ntbl->rwlock)); 1039 lockdep_is_held(&priv->lock));
1047 if (!htbl) 1040 if (!htbl)
1048 goto out_unlock; 1041 return;
1049 1042
1050 hash_val = ipoib_addr_hash(htbl, neigh->daddr); 1043 hash_val = ipoib_addr_hash(htbl, neigh->daddr);
1051 np = &htbl->buckets[hash_val]; 1044 np = &htbl->buckets[hash_val];
1052 for (n = rcu_dereference_protected(*np, 1045 for (n = rcu_dereference_protected(*np,
1053 lockdep_is_held(&ntbl->rwlock)); 1046 lockdep_is_held(&priv->lock));
1054 n != NULL; 1047 n != NULL;
1055 n = rcu_dereference_protected(*np, 1048 n = rcu_dereference_protected(*np,
1056 lockdep_is_held(&ntbl->rwlock))) { 1049 lockdep_is_held(&priv->lock))) {
1057 if (n == neigh) { 1050 if (n == neigh) {
1058 /* found */ 1051 /* found */
1059 rcu_assign_pointer(*np, 1052 rcu_assign_pointer(*np,
1060 rcu_dereference_protected(neigh->hnext, 1053 rcu_dereference_protected(neigh->hnext,
1061 lockdep_is_held(&ntbl->rwlock))); 1054 lockdep_is_held(&priv->lock)));
1062 call_rcu(&neigh->rcu, ipoib_neigh_reclaim); 1055 call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
1063 goto out_unlock; 1056 return;
1064 } else { 1057 } else {
1065 np = &n->hnext; 1058 np = &n->hnext;
1066 } 1059 }
1067 } 1060 }
1068
1069out_unlock:
1070 write_unlock_bh(&ntbl->rwlock);
1071
1072} 1061}
1073 1062
1074static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) 1063static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
@@ -1080,7 +1069,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
1080 1069
1081 clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags); 1070 clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
1082 ntbl->htbl = NULL; 1071 ntbl->htbl = NULL;
1083 rwlock_init(&ntbl->rwlock);
1084 htbl = kzalloc(sizeof(*htbl), GFP_KERNEL); 1072 htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
1085 if (!htbl) 1073 if (!htbl)
1086 return -ENOMEM; 1074 return -ENOMEM;
@@ -1128,10 +1116,10 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
1128 int i; 1116 int i;
1129 1117
1130 /* remove all neigh connected to a given path or mcast */ 1118 /* remove all neigh connected to a given path or mcast */
1131 write_lock_bh(&ntbl->rwlock); 1119 spin_lock_irqsave(&priv->lock, flags);
1132 1120
1133 htbl = rcu_dereference_protected(ntbl->htbl, 1121 htbl = rcu_dereference_protected(ntbl->htbl,
1134 lockdep_is_held(&ntbl->rwlock)); 1122 lockdep_is_held(&priv->lock));
1135 1123
1136 if (!htbl) 1124 if (!htbl)
1137 goto out_unlock; 1125 goto out_unlock;
@@ -1141,16 +1129,14 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
1141 struct ipoib_neigh __rcu **np = &htbl->buckets[i]; 1129 struct ipoib_neigh __rcu **np = &htbl->buckets[i];
1142 1130
1143 while ((neigh = rcu_dereference_protected(*np, 1131 while ((neigh = rcu_dereference_protected(*np,
1144 lockdep_is_held(&ntbl->rwlock))) != NULL) { 1132 lockdep_is_held(&priv->lock))) != NULL) {
1145 /* delete neighs belong to this parent */ 1133 /* delete neighs belong to this parent */
1146 if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) { 1134 if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
1147 rcu_assign_pointer(*np, 1135 rcu_assign_pointer(*np,
1148 rcu_dereference_protected(neigh->hnext, 1136 rcu_dereference_protected(neigh->hnext,
1149 lockdep_is_held(&ntbl->rwlock))); 1137 lockdep_is_held(&priv->lock)));
1150 /* remove from parent list */ 1138 /* remove from parent list */
1151 spin_lock_irqsave(&priv->lock, flags);
1152 list_del(&neigh->list); 1139 list_del(&neigh->list);
1153 spin_unlock_irqrestore(&priv->lock, flags);
1154 call_rcu(&neigh->rcu, ipoib_neigh_reclaim); 1140 call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
1155 } else { 1141 } else {
1156 np = &neigh->hnext; 1142 np = &neigh->hnext;
@@ -1159,7 +1145,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
1159 } 1145 }
1160 } 1146 }
1161out_unlock: 1147out_unlock:
1162 write_unlock_bh(&ntbl->rwlock); 1148 spin_unlock_irqrestore(&priv->lock, flags);
1163} 1149}
1164 1150
1165static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) 1151static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
@@ -1171,10 +1157,10 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
1171 1157
1172 init_completion(&priv->ntbl.flushed); 1158 init_completion(&priv->ntbl.flushed);
1173 1159
1174 write_lock_bh(&ntbl->rwlock); 1160 spin_lock_irqsave(&priv->lock, flags);
1175 1161
1176 htbl = rcu_dereference_protected(ntbl->htbl, 1162 htbl = rcu_dereference_protected(ntbl->htbl,
1177 lockdep_is_held(&ntbl->rwlock)); 1163 lockdep_is_held(&priv->lock));
1178 if (!htbl) 1164 if (!htbl)
1179 goto out_unlock; 1165 goto out_unlock;
1180 1166
@@ -1187,14 +1173,12 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
1187 struct ipoib_neigh __rcu **np = &htbl->buckets[i]; 1173 struct ipoib_neigh __rcu **np = &htbl->buckets[i];
1188 1174
1189 while ((neigh = rcu_dereference_protected(*np, 1175 while ((neigh = rcu_dereference_protected(*np,
1190 lockdep_is_held(&ntbl->rwlock))) != NULL) { 1176 lockdep_is_held(&priv->lock))) != NULL) {
1191 rcu_assign_pointer(*np, 1177 rcu_assign_pointer(*np,
1192 rcu_dereference_protected(neigh->hnext, 1178 rcu_dereference_protected(neigh->hnext,
1193 lockdep_is_held(&ntbl->rwlock))); 1179 lockdep_is_held(&priv->lock)));
1194 /* remove from path/mc list */ 1180 /* remove from path/mc list */
1195 spin_lock_irqsave(&priv->lock, flags);
1196 list_del(&neigh->list); 1181 list_del(&neigh->list);
1197 spin_unlock_irqrestore(&priv->lock, flags);
1198 call_rcu(&neigh->rcu, ipoib_neigh_reclaim); 1182 call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
1199 } 1183 }
1200 } 1184 }
@@ -1204,7 +1188,7 @@ free_htbl:
1204 call_rcu(&htbl->rcu, neigh_hash_free_rcu); 1188 call_rcu(&htbl->rcu, neigh_hash_free_rcu);
1205 1189
1206out_unlock: 1190out_unlock:
1207 write_unlock_bh(&ntbl->rwlock); 1191 spin_unlock_irqrestore(&priv->lock, flags);
1208 if (wait_flushed) 1192 if (wait_flushed)
1209 wait_for_completion(&priv->ntbl.flushed); 1193 wait_for_completion(&priv->ntbl.flushed);
1210} 1194}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 13f4aa7593c..75367249f44 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -707,9 +707,7 @@ out:
707 neigh = ipoib_neigh_get(dev, daddr); 707 neigh = ipoib_neigh_get(dev, daddr);
708 spin_lock_irqsave(&priv->lock, flags); 708 spin_lock_irqsave(&priv->lock, flags);
709 if (!neigh) { 709 if (!neigh) {
710 spin_unlock_irqrestore(&priv->lock, flags);
711 neigh = ipoib_neigh_alloc(daddr, dev); 710 neigh = ipoib_neigh_alloc(daddr, dev);
712 spin_lock_irqsave(&priv->lock, flags);
713 if (neigh) { 711 if (neigh) {
714 kref_get(&mcast->ah->ref); 712 kref_get(&mcast->ah->ref);
715 neigh->ah = mcast->ah; 713 neigh->ah = mcast->ah;