提交 0e442afd 编写于 作者: R Roland Dreier 提交者: Roland Dreier

IB/mad: Fix lock-lock-timer deadlock in RMPP code

Holding agent->lock across cancel_delayed_work() (which does
del_timer_sync()) in ib_cancel_rmpp_recvs() leads to lockdep reports of
possible lock-timer deadlocks if a consumer ever does something that
connects agent->lock to a lock taken in IRQ context (cf
http://marc.info/?l=linux-rdma&m=125243699026045).

Fix this by changing the list items to a new state "CANCELING" while
holding the lock, and then canceling the delayed work without holding
the lock.  If the delayed work runs after the lock is dropped, it will
see the state is CANCELING and return immediately, so the list will
stay stable while we traverse it with the lock not held.
Reviewed-by: NSean Hefty <sean.hefty@intel.com>
Signed-off-by: NRoland Dreier <rolandd@cisco.com>
上级 86d71014
...@@ -37,7 +37,8 @@ ...@@ -37,7 +37,8 @@
enum rmpp_state { enum rmpp_state {
RMPP_STATE_ACTIVE, RMPP_STATE_ACTIVE,
RMPP_STATE_TIMEOUT, RMPP_STATE_TIMEOUT,
RMPP_STATE_COMPLETE RMPP_STATE_COMPLETE,
RMPP_STATE_CANCELING
}; };
struct mad_rmpp_recv { struct mad_rmpp_recv {
...@@ -86,19 +87,23 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent) ...@@ -86,19 +87,23 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&agent->lock, flags); spin_lock_irqsave(&agent->lock, flags);
list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) {
if (rmpp_recv->state != RMPP_STATE_COMPLETE)
ib_free_recv_mad(rmpp_recv->rmpp_wc);
rmpp_recv->state = RMPP_STATE_CANCELING;
}
spin_unlock_irqrestore(&agent->lock, flags);
list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) {
cancel_delayed_work(&rmpp_recv->timeout_work); cancel_delayed_work(&rmpp_recv->timeout_work);
cancel_delayed_work(&rmpp_recv->cleanup_work); cancel_delayed_work(&rmpp_recv->cleanup_work);
} }
spin_unlock_irqrestore(&agent->lock, flags);
flush_workqueue(agent->qp_info->port_priv->wq); flush_workqueue(agent->qp_info->port_priv->wq);
list_for_each_entry_safe(rmpp_recv, temp_rmpp_recv, list_for_each_entry_safe(rmpp_recv, temp_rmpp_recv,
&agent->rmpp_list, list) { &agent->rmpp_list, list) {
list_del(&rmpp_recv->list); list_del(&rmpp_recv->list);
if (rmpp_recv->state != RMPP_STATE_COMPLETE)
ib_free_recv_mad(rmpp_recv->rmpp_wc);
destroy_rmpp_recv(rmpp_recv); destroy_rmpp_recv(rmpp_recv);
} }
} }
...@@ -260,6 +265,10 @@ static void recv_cleanup_handler(struct work_struct *work) ...@@ -260,6 +265,10 @@ static void recv_cleanup_handler(struct work_struct *work)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&rmpp_recv->agent->lock, flags); spin_lock_irqsave(&rmpp_recv->agent->lock, flags);
if (rmpp_recv->state == RMPP_STATE_CANCELING) {
spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags);
return;
}
list_del(&rmpp_recv->list); list_del(&rmpp_recv->list);
spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags);
destroy_rmpp_recv(rmpp_recv); destroy_rmpp_recv(rmpp_recv);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册