diff options
author | Michael S. Tsirkin <mst@mellanox.co.il> | 2006-03-20 13:08:25 -0500 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2006-03-20 13:08:25 -0500 |
commit | dc05980dd736bfbe5b2524b463e5f098e67a64e9 (patch) | |
tree | 87f212e24ee4362a5e670418b32bd0c959d3f1c6 /drivers/infiniband | |
parent | bf17c1c7cc9250d7c3c01b0ae898aefa1853535a (diff) |
IB/mad: Fix oopsable race on device removal
Fix an oopsable race debugged by Eli Cohen <eli@mellanox.co.il>:
After removing the port from port_list, ib_mad_port_close flushes
port_priv->wq before destroying the special QPs. This means that a
completion event could arrive, and queue a new work in this work queue
after flush.
This patch also removes an unnecessary flush_workqueue():
destroy_workqueue() already includes a flush.
Signed-off-by: Michael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/mad.c | 21 |
1 files changed, 14 insertions, 7 deletions
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 16549add8e8f..f7854b65fd55 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c | |||
@@ -2364,8 +2364,12 @@ static void timeout_sends(void *data) | |||
2364 | static void ib_mad_thread_completion_handler(struct ib_cq *cq, void *arg) | 2364 | static void ib_mad_thread_completion_handler(struct ib_cq *cq, void *arg) |
2365 | { | 2365 | { |
2366 | struct ib_mad_port_private *port_priv = cq->cq_context; | 2366 | struct ib_mad_port_private *port_priv = cq->cq_context; |
2367 | unsigned long flags; | ||
2367 | 2368 | ||
2368 | queue_work(port_priv->wq, &port_priv->work); | 2369 | spin_lock_irqsave(&ib_mad_port_list_lock, flags); |
2370 | if (!list_empty(&port_priv->port_list)) | ||
2371 | queue_work(port_priv->wq, &port_priv->work); | ||
2372 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); | ||
2369 | } | 2373 | } |
2370 | 2374 | ||
2371 | /* | 2375 | /* |
@@ -2677,18 +2681,23 @@ static int ib_mad_port_open(struct ib_device *device, | |||
2677 | } | 2681 | } |
2678 | INIT_WORK(&port_priv->work, ib_mad_completion_handler, port_priv); | 2682 | INIT_WORK(&port_priv->work, ib_mad_completion_handler, port_priv); |
2679 | 2683 | ||
2684 | spin_lock_irqsave(&ib_mad_port_list_lock, flags); | ||
2685 | list_add_tail(&port_priv->port_list, &ib_mad_port_list); | ||
2686 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); | ||
2687 | |||
2680 | ret = ib_mad_port_start(port_priv); | 2688 | ret = ib_mad_port_start(port_priv); |
2681 | if (ret) { | 2689 | if (ret) { |
2682 | printk(KERN_ERR PFX "Couldn't start port\n"); | 2690 | printk(KERN_ERR PFX "Couldn't start port\n"); |
2683 | goto error9; | 2691 | goto error9; |
2684 | } | 2692 | } |
2685 | 2693 | ||
2686 | spin_lock_irqsave(&ib_mad_port_list_lock, flags); | ||
2687 | list_add_tail(&port_priv->port_list, &ib_mad_port_list); | ||
2688 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); | ||
2689 | return 0; | 2694 | return 0; |
2690 | 2695 | ||
2691 | error9: | 2696 | error9: |
2697 | spin_lock_irqsave(&ib_mad_port_list_lock, flags); | ||
2698 | list_del_init(&port_priv->port_list); | ||
2699 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); | ||
2700 | |||
2692 | destroy_workqueue(port_priv->wq); | 2701 | destroy_workqueue(port_priv->wq); |
2693 | error8: | 2702 | error8: |
2694 | destroy_mad_qp(&port_priv->qp_info[1]); | 2703 | destroy_mad_qp(&port_priv->qp_info[1]); |
@@ -2725,11 +2734,9 @@ static int ib_mad_port_close(struct ib_device *device, int port_num) | |||
2725 | printk(KERN_ERR PFX "Port %d not found\n", port_num); | 2734 | printk(KERN_ERR PFX "Port %d not found\n", port_num); |
2726 | return -ENODEV; | 2735 | return -ENODEV; |
2727 | } | 2736 | } |
2728 | list_del(&port_priv->port_list); | 2737 | list_del_init(&port_priv->port_list); |
2729 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); | 2738 | spin_unlock_irqrestore(&ib_mad_port_list_lock, flags); |
2730 | 2739 | ||
2731 | /* Stop processing completions. */ | ||
2732 | flush_workqueue(port_priv->wq); | ||
2733 | destroy_workqueue(port_priv->wq); | 2740 | destroy_workqueue(port_priv->wq); |
2734 | destroy_mad_qp(&port_priv->qp_info[1]); | 2741 | destroy_mad_qp(&port_priv->qp_info[1]); |
2735 | destroy_mad_qp(&port_priv->qp_info[0]); | 2742 | destroy_mad_qp(&port_priv->qp_info[0]); |