diff options
| author | Erez Shitrit <erezsh@mellanox.com> | 2017-06-07 05:14:24 -0400 |
|---|---|---|
| committer | Saeed Mahameed <saeedm@mellanox.com> | 2017-08-07 03:47:07 -0400 |
| commit | 97834eba7c194659a72c5bb0f8c19c7055bb69ea (patch) | |
| tree | 2ca3e322e59cb9a9cf43708faa02304679cc0ade | |
| parent | e80541ecabd57b69726232b89242e28d8123cccc (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.c | 73 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/main.c | 3 | ||||
| -rw-r--r-- | include/linux/mlx5/driver.h | 3 |
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 | ||
| 48 | struct 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 | |||
| 48 | enum { | 55 | enum { |
| 49 | MLX5_INTERFACE_ADDED, | 56 | MLX5_INTERFACE_ADDED, |
| 50 | MLX5_INTERFACE_ATTACHED, | 57 | MLX5_INTERFACE_ATTACHED, |
| 51 | }; | 58 | }; |
| 52 | 59 | ||
| 60 | static 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 | |||
| 80 | static 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 | |||
| 98 | static 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 | |||
| 53 | void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) | 112 | void 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; |
