Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc05980d authored by Michael S. Tsirkin's avatar Michael S. Tsirkin Committed by Roland Dreier
Browse files

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: default avatarMichael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent bf17c1c7
Loading
Loading
Loading
Loading
+14 −7
Original line number Diff line number Diff line
@@ -2364,8 +2364,12 @@ static void timeout_sends(void *data)
static void ib_mad_thread_completion_handler(struct ib_cq *cq, void *arg)
{
	struct ib_mad_port_private *port_priv = cq->cq_context;
	unsigned long flags;

	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
	if (!list_empty(&port_priv->port_list))
		queue_work(port_priv->wq, &port_priv->work);
	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
}

/*
@@ -2677,18 +2681,23 @@ static int ib_mad_port_open(struct ib_device *device,
	}
	INIT_WORK(&port_priv->work, ib_mad_completion_handler, port_priv);

	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
	list_add_tail(&port_priv->port_list, &ib_mad_port_list);
	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);

	ret = ib_mad_port_start(port_priv);
	if (ret) {
		printk(KERN_ERR PFX "Couldn't start port\n");
		goto error9;
	}

	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
	list_add_tail(&port_priv->port_list, &ib_mad_port_list);
	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
	return 0;

error9:
	spin_lock_irqsave(&ib_mad_port_list_lock, flags);
	list_del_init(&port_priv->port_list);
	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);

	destroy_workqueue(port_priv->wq);
error8:
	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)
		printk(KERN_ERR PFX "Port %d not found\n", port_num);
		return -ENODEV;
	}
	list_del(&port_priv->port_list);
	list_del_init(&port_priv->port_list);
	spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);

	/* Stop processing completions. */
	flush_workqueue(port_priv->wq);
	destroy_workqueue(port_priv->wq);
	destroy_mad_qp(&port_priv->qp_info[1]);
	destroy_mad_qp(&port_priv->qp_info[0]);