aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEran Ben Elisha <eranbe@mellanox.com>2015-11-12 12:35:29 -0500
committerDavid S. Miller <davem@davemloft.net>2015-11-15 18:43:41 -0500
commitf5adbfee72282bb1f456d52b04adacd4fe6ac502 (patch)
treeef56854525a2b48771ccb32a81387ccb33931cf5
parentd4e28cbd24c8cb004960ddb8b22124953f6c220c (diff)
net/mlx4_core: Fix sleeping while holding spinlock at rem_slave_counters
When cleaning slave's counter resources, we hold a spinlock that protects the slave's counters list. As part of the clean, we call __mlx4_clear_if_stat which calls mlx4_alloc_cmd_mailbox which is a sleepable function. In order to fix this issue, hold the spinlock, and copy all counter indices into a temporary array, and release the spinlock. Afterwards, iterate over this array and free every counter. Repeat this scenario until the original list is empty (a new counter might have been added while releasing the counters from the temporary array). Fixes: b72ca7e96acf ("net/mlx4_core: Reset counters data when freed") Reported-by: Moni Shoua <monis@mellanox.com> Tested-by: Moni Shoua <monis@mellanox.com> Signed-off-by: Jack Morgenstein <jackm@dev.mellanox.co.il> Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c39
1 files changed, 27 insertions, 12 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 9813d34f3e5b..6fec3e993d02 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -4952,26 +4952,41 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave)
4952 struct res_counter *counter; 4952 struct res_counter *counter;
4953 struct res_counter *tmp; 4953 struct res_counter *tmp;
4954 int err; 4954 int err;
4955 int index; 4955 int *counters_arr = NULL;
4956 int i, j;
4956 4957
4957 err = move_all_busy(dev, slave, RES_COUNTER); 4958 err = move_all_busy(dev, slave, RES_COUNTER);
4958 if (err) 4959 if (err)
4959 mlx4_warn(dev, "rem_slave_counters: Could not move all counters - too busy for slave %d\n", 4960 mlx4_warn(dev, "rem_slave_counters: Could not move all counters - too busy for slave %d\n",
4960 slave); 4961 slave);
4961 4962
4962 spin_lock_irq(mlx4_tlock(dev)); 4963 counters_arr = kmalloc_array(dev->caps.max_counters,
4963 list_for_each_entry_safe(counter, tmp, counter_list, com.list) { 4964 sizeof(*counters_arr), GFP_KERNEL);
4964 if (counter->com.owner == slave) { 4965 if (!counters_arr)
4965 index = counter->com.res_id; 4966 return;
4966 rb_erase(&counter->com.node, 4967
4967 &tracker->res_tree[RES_COUNTER]); 4968 do {
4968 list_del(&counter->com.list); 4969 i = 0;
4969 kfree(counter); 4970 j = 0;
4970 __mlx4_counter_free(dev, index); 4971 spin_lock_irq(mlx4_tlock(dev));
4972 list_for_each_entry_safe(counter, tmp, counter_list, com.list) {
4973 if (counter->com.owner == slave) {
4974 counters_arr[i++] = counter->com.res_id;
4975 rb_erase(&counter->com.node,
4976 &tracker->res_tree[RES_COUNTER]);
4977 list_del(&counter->com.list);
4978 kfree(counter);
4979 }
4980 }
4981 spin_unlock_irq(mlx4_tlock(dev));
4982
4983 while (j < i) {
4984 __mlx4_counter_free(dev, counters_arr[j++]);
4971 mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); 4985 mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0);
4972 } 4986 }
4973 } 4987 } while (i);
4974 spin_unlock_irq(mlx4_tlock(dev)); 4988
4989 kfree(counters_arr);
4975} 4990}
4976 4991
4977static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave) 4992static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave)