aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErez Shitrit <erezsh@mellanox.com>2017-06-07 05:14:24 -0400
committerSaeed Mahameed <saeedm@mellanox.com>2017-08-07 03:47:07 -0400
commit97834eba7c194659a72c5bb0f8c19c7055bb69ea (patch)
tree2ca3e322e59cb9a9cf43708faa02304679cc0ade
parente80541ecabd57b69726232b89242e28d8123cccc (diff)
net/mlx5: Delay events till ib registration ends
When mlx5_ib registers itself to mlx5_core as an interface, it will call mlx5_add_device which will call mlx5_ib interface add callback, in case the latter successfully returns, only then mlx5_core will add it to the interface list and async events will be forwarded to mlx5_ib. Between mlx5_ib interface add callback and mlx5_core adding the mlx5_ib interface to its devices list, arriving mlx5_core events can be missed by the new mlx5_ib registering interface. In other words: thread 1: mlx5_ib: mlx5_register_interface(dev) thread 1: mlx5_core: mlx5_add_device(dev) thread 1: mlx5_core: ctx = dev->add => (mlx5_ib)->mlx5_ib_add thread 2: mlx5_core_event: **new event arrives, forward to dev_list thread 1: mlx5_core: add_ctx_to_dev_list(ctx) /* previous event was missed by the new interface.*/ It is ok to miss events before dev->add (mlx5_ib)->mlx5_ib_add_device but not after. We fix this race by accumulating the events that come between the ib_register_device (inside mlx5_add_device->(dev->add)) till the adding to the list completes and fire them to the new registering interface after that. Fixes: f1ee87fe55c8 ("net/mlx5: Organize device list API in one place") Signed-off-by: Erez Shitrit <erezsh@mellanox.com> Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dev.c73
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c3
-rw-r--r--include/linux/mlx5/driver.h3
3 files changed, 79 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
index a62f4b6a21a5..ff60cf7342ca 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -45,11 +45,70 @@ struct mlx5_device_context {
45 unsigned long state; 45 unsigned long state;
46}; 46};
47 47
48struct mlx5_delayed_event {
49 struct list_head list;
50 struct mlx5_core_dev *dev;
51 enum mlx5_dev_event event;
52 unsigned long param;
53};
54
48enum { 55enum {
49 MLX5_INTERFACE_ADDED, 56 MLX5_INTERFACE_ADDED,
50 MLX5_INTERFACE_ATTACHED, 57 MLX5_INTERFACE_ATTACHED,
51}; 58};
52 59
60static void add_delayed_event(struct mlx5_priv *priv,
61 struct mlx5_core_dev *dev,
62 enum mlx5_dev_event event,
63 unsigned long param)
64{
65 struct mlx5_delayed_event *delayed_event;
66
67 delayed_event = kzalloc(sizeof(*delayed_event), GFP_ATOMIC);
68 if (!delayed_event) {
69 mlx5_core_err(dev, "event %d is missed\n", event);
70 return;
71 }
72
73 mlx5_core_dbg(dev, "Accumulating event %d\n", event);
74 delayed_event->dev = dev;
75 delayed_event->event = event;
76 delayed_event->param = param;
77 list_add_tail(&delayed_event->list, &priv->waiting_events_list);
78}
79
80static void fire_delayed_event_locked(struct mlx5_device_context *dev_ctx,
81 struct mlx5_core_dev *dev,
82 struct mlx5_priv *priv)
83{
84 struct mlx5_delayed_event *de;
85 struct mlx5_delayed_event *n;
86
87 /* stop delaying events */
88 priv->is_accum_events = false;
89
90 /* fire all accumulated events before new event comes */
91 list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) {
92 dev_ctx->intf->event(dev, dev_ctx->context, de->event, de->param);
93 list_del(&de->list);
94 kfree(de);
95 }
96}
97
98static void cleanup_delayed_evets(struct mlx5_priv *priv)
99{
100 struct mlx5_delayed_event *de;
101 struct mlx5_delayed_event *n;
102
103 spin_lock_irq(&priv->ctx_lock);
104 priv->is_accum_events = false;
105 list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) {
106 list_del(&de->list);
107 kfree(de);
108 }
109 spin_unlock_irq(&priv->ctx_lock);
110}
111
53void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) 112void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
54{ 113{
55 struct mlx5_device_context *dev_ctx; 114 struct mlx5_device_context *dev_ctx;
@@ -63,6 +122,12 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
63 return; 122 return;
64 123
65 dev_ctx->intf = intf; 124 dev_ctx->intf = intf;
125 /* accumulating events that can come after mlx5_ib calls to
126 * ib_register_device, till adding that interface to the events list.
127 */
128
129 priv->is_accum_events = true;
130
66 dev_ctx->context = intf->add(dev); 131 dev_ctx->context = intf->add(dev);
67 set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); 132 set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state);
68 if (intf->attach) 133 if (intf->attach)
@@ -71,6 +136,9 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
71 if (dev_ctx->context) { 136 if (dev_ctx->context) {
72 spin_lock_irq(&priv->ctx_lock); 137 spin_lock_irq(&priv->ctx_lock);
73 list_add_tail(&dev_ctx->list, &priv->ctx_list); 138 list_add_tail(&dev_ctx->list, &priv->ctx_list);
139
140 fire_delayed_event_locked(dev_ctx, dev, priv);
141
74#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING 142#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
75 if (dev_ctx->intf->pfault) { 143 if (dev_ctx->intf->pfault) {
76 if (priv->pfault) { 144 if (priv->pfault) {
@@ -84,6 +152,8 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv)
84 spin_unlock_irq(&priv->ctx_lock); 152 spin_unlock_irq(&priv->ctx_lock);
85 } else { 153 } else {
86 kfree(dev_ctx); 154 kfree(dev_ctx);
155 /* delete all accumulated events */
156 cleanup_delayed_evets(priv);
87 } 157 }
88} 158}
89 159
@@ -341,6 +411,9 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
341 411
342 spin_lock_irqsave(&priv->ctx_lock, flags); 412 spin_lock_irqsave(&priv->ctx_lock, flags);
343 413
414 if (priv->is_accum_events)
415 add_delayed_event(priv, dev, event, param);
416
344 list_for_each_entry(dev_ctx, &priv->ctx_list, list) 417 list_for_each_entry(dev_ctx, &priv->ctx_list, list)
345 if (dev_ctx->intf->event) 418 if (dev_ctx->intf->event)
346 dev_ctx->intf->event(dev, dev_ctx->context, event, param); 419 dev_ctx->intf->event(dev, dev_ctx->context, event, param);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 124c7c3c3a00..6dbd637b4e66 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1343,6 +1343,9 @@ static int init_one(struct pci_dev *pdev,
1343 mutex_init(&dev->pci_status_mutex); 1343 mutex_init(&dev->pci_status_mutex);
1344 mutex_init(&dev->intf_state_mutex); 1344 mutex_init(&dev->intf_state_mutex);
1345 1345
1346 INIT_LIST_HEAD(&priv->waiting_events_list);
1347 priv->is_accum_events = false;
1348
1346#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING 1349#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
1347 err = init_srcu_struct(&priv->pfault_srcu); 1350 err = init_srcu_struct(&priv->pfault_srcu);
1348 if (err) { 1351 if (err) {
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 88d6eb5b3a76..d26f18b39c4a 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -647,6 +647,9 @@ struct mlx5_priv {
647 struct list_head ctx_list; 647 struct list_head ctx_list;
648 spinlock_t ctx_lock; 648 spinlock_t ctx_lock;
649 649
650 struct list_head waiting_events_list;
651 bool is_accum_events;
652
650 struct mlx5_flow_steering *steering; 653 struct mlx5_flow_steering *steering;
651 struct mlx5_mpfs *mpfs; 654 struct mlx5_mpfs *mpfs;
652 struct mlx5_eswitch *eswitch; 655 struct mlx5_eswitch *eswitch;