mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
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: Sean Hefty <sean.hefty@intel.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
86d710146f
commit
0e442afd92
@ -37,7 +37,8 @@
|
||||
enum rmpp_state {
|
||||
RMPP_STATE_ACTIVE,
|
||||
RMPP_STATE_TIMEOUT,
|
||||
RMPP_STATE_COMPLETE
|
||||
RMPP_STATE_COMPLETE,
|
||||
RMPP_STATE_CANCELING
|
||||
};
|
||||
|
||||
struct mad_rmpp_recv {
|
||||
@ -86,19 +87,23 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent)
|
||||
unsigned long 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) {
|
||||
cancel_delayed_work(&rmpp_recv->timeout_work);
|
||||
cancel_delayed_work(&rmpp_recv->cleanup_work);
|
||||
}
|
||||
spin_unlock_irqrestore(&agent->lock, flags);
|
||||
|
||||
flush_workqueue(agent->qp_info->port_priv->wq);
|
||||
|
||||
list_for_each_entry_safe(rmpp_recv, temp_rmpp_recv,
|
||||
&agent->rmpp_list, 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);
|
||||
}
|
||||
}
|
||||
@ -260,6 +265,10 @@ static void recv_cleanup_handler(struct work_struct *work)
|
||||
unsigned long 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);
|
||||
spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags);
|
||||
destroy_rmpp_recv(rmpp_recv);
|
||||
|
Loading…
Reference in New Issue
Block a user