From 45c3ac0436427a6c730837ad2d38ab2cb2189e8c Mon Sep 17 00:00:00 2001 From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu> Date: Tue, 21 Sep 2021 11:13:16 +0200 Subject: [PATCH] Avoid deadlock during R4 call transfer There was a possibility that during attended call transfer, deadlock happened in locking bridge_channel and channel. bridge_channel_queue_deferred_frames() was waiting for brcm_indicate() to unlock the channel, while brcm_indicate() which called ast_bridge_channel_queue_frame() was waiting for bridge_channel_queue_deferred_frames() to unlock the bridge. Creating new macro to lock both channel and bridge_channel at the same time and sched_yield() -> retry if failed and use it in bridge_channel_queue_deferred_frames() avoiding the deadlock. --- include/asterisk/bridge_channel.h | 13 +++++++++++++ main/bridge_channel.c | 3 +-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/asterisk/bridge_channel.h b/include/asterisk/bridge_channel.h index fb2fd4f6f3..dc2735f308 100644 --- a/include/asterisk/bridge_channel.h +++ b/include/asterisk/bridge_channel.h @@ -247,6 +247,19 @@ static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_ __ao2_unlock(bridge_channel, file, function, line, var); } +/*! \brief Lock bridge_channel and channel at the same time. */ +#define ast_bridge_lock_channel_and_bridge_channel(bridge_channel, channel) \ + do { \ + for (;;) { \ + ast_bridge_channel_lock(bridge_channel); \ + if (!ast_channel_trylock(channel)) { \ + break; \ + } \ + ast_bridge_channel_unlock(bridge_channel); \ + sched_yield(); \ + } \ + } while (0) + /*! * \brief Lock the bridge associated with the bridge channel. * \since 12.0.0 diff --git a/main/bridge_channel.c b/main/bridge_channel.c index d39e3e16ee..ffacbed1dc 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -832,8 +832,7 @@ void bridge_channel_queue_deferred_frames(struct ast_bridge_channel *bridge_chan { struct ast_frame *frame; - ast_bridge_channel_lock(bridge_channel); - ast_channel_lock(bridge_channel->chan); + ast_bridge_lock_channel_and_bridge_channel(bridge_channel, bridge_channel->chan); while ((frame = AST_LIST_REMOVE_HEAD(&bridge_channel->deferred_queue, frame_list))) { ast_queue_frame_head(bridge_channel->chan, frame); ast_frfree(frame); -- GitLab