From CAPI thread locks are taken in the wrong order. The right ordr is ast_channel_lock followed by i->lock. But from CAPI thread

i->lock was taken firsst and ast_channel_lock after by call to one of asterisk functions (get variable, set variable).
As result CAPI thread was blocked in one deadlock condition and stopped the processing of CAPI messages.
The resulting behavior is reported by: http://www.ipphoneforum.eu/showthread.php?t=220051.
Timeouts for CONNECT_CONF and DISC with 'incompatible destination' for opposite side.

But this is still problem: this is delayed channel work which is done without ast_channel_lock held (or channel reference count increassed in
asterisk 1.8(). As result this iss still possible that channel is freed (by asterisk) before the delayed work is executed.
This should be addressed in the next time.
This commit is contained in:
MelwareDE 2010-09-12 08:52:18 +00:00
parent a6f24499b8
commit e590876769
4 changed files with 75 additions and 17 deletions

View File

@ -221,6 +221,7 @@ static int pbx_capi_indicate(struct ast_channel *c, int condition);
static struct capi_pvt* get_active_plci (struct ast_channel *c);
static _cstruct diva_get_b1_conf (struct capi_pvt *i);
static void clear_channel_fax_loop (struct ast_channel *c, struct capi_pvt *i);
static struct ast_channel* capidev_acquire_locks_from_thread_context (struct capi_pvt *i);
/*
* B protocol settings
@ -4839,7 +4840,11 @@ static void capidev_handle_disconnect_indication(_cmsg *CMSG, unsigned int PLCI,
/*
* CAPI CONNECT_IND
*/
static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **interface)
static void capidev_handle_connect_indication(_cmsg *CMSG,
unsigned int PLCI,
unsigned int NCCI,
struct capi_pvt **interface,
struct ast_channel** interface_owner)
{
struct capi_pvt *i;
char *DNID;
@ -4999,7 +5004,7 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
*interface = i;
cc_mutex_unlock(&iflock);
cc_mutex_lock(&i->lock);
*interface_owner = capidev_acquire_locks_from_thread_context (i);
pbx_builtin_setvar_helper(i->owner, "TRANSFERCAPABILITY", transfercapability2str(i->transfercapability));
pbx_builtin_setvar_helper(i->owner, "BCHANNELINFO", bchannelinfo);
@ -5057,14 +5062,15 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
/*
* CAPI FACILITY_CONF
*/
static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i)
static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i,
struct ast_channel** interface_owner)
{
int selector;
selector = FACILITY_CONF_FACILITYSELECTOR(CMSG);
if (selector == FACILITYSELECTOR_SUPPLEMENTARY) {
handle_facility_confirmation_supplementary(CMSG, PLCI, NCCI, i);
handle_facility_confirmation_supplementary(CMSG, PLCI, NCCI, i, interface_owner);
return;
}
@ -5217,7 +5223,7 @@ static void capidev_post_handling(struct capi_pvt *i, _cmsg *CMSG)
* handle CONNECT_CONF or FACILITY_CONF(CCBS call)
*/
void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
unsigned short wInfo, unsigned short wMsgNum)
unsigned short wInfo, unsigned short wMsgNum, struct ast_channel** interface_owner)
{
struct capi_pvt *ii;
struct ast_frame fr = { AST_FRAME_CONTROL, };
@ -5236,7 +5242,9 @@ void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
}
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: received CONNECT_CONF PLCI = %#x\n",
ii->vname, PLCI);
cc_mutex_lock(&ii->lock);
*interface_owner = capidev_acquire_locks_from_thread_context (ii);
if (wInfo == 0) {
ii->PLCI = PLCI;
} else {
@ -5248,6 +5256,47 @@ void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
}
}
/*! \brief acquire locks in the correct order
*/
static struct ast_channel* capidev_acquire_locks_from_thread_context (struct capi_pvt *i)
{
struct ast_channel *owner = 0;
if (unlikely(i == 0))
return (0);
#ifdef CC_AST_HAS_VERSION_1_8
cc_mutex_lock(&i->lock);
owner = i->owner;
if (likely(owner != 0)) {
ast_channel_ref (owner);
cc_mutex_unlock(&i->lock);
ast_channel_lock(owner);
ast_channel_unref (owner);
cc_mutex_lock(&i->lock);
if (unlikely(i->owner == 0)) {
cc_mutex_unlock (&i->lock);
ast_channel_unlock (owner);
cc_mutex_lock (&i->lock);
owner = 0;
}
}
#else
for (;;) {
cc_mutex_lock(&i->lock);
owner = i->owner;
if (unlikely(owner == 0))
break;
if (likely(ast_channel_trylock(owner) == 0))
break;
cc_mutex_unlock(&i->lock);
usleep (100);
}
#endif
return (owner);
}
/*
* handle CAPI msg
*/
@ -5259,6 +5308,7 @@ static void capidev_handle_msg(_cmsg *CMSG)
unsigned short wMsgNum = HEADER_MSGNUM(CMSG);
unsigned short wInfo = 0xffff;
struct capi_pvt *i = capi_find_interface_by_plci(PLCI);
struct ast_channel* owner;
if ((wCmd == CAPI_P_IND(DATA_B3)) ||
(wCmd == CAPI_P_CONF(DATA_B3))) {
@ -5271,8 +5321,7 @@ static void capidev_handle_msg(_cmsg *CMSG)
cc_verbose(4, 1, "%s\n", capi_cmsg2str(CMSG));
}
if (i != NULL)
cc_mutex_lock(&i->lock);
owner = capidev_acquire_locks_from_thread_context (i);
/* main switch table */
@ -5282,7 +5331,7 @@ static void capidev_handle_msg(_cmsg *CMSG)
* CAPI indications
*/
case CAPI_P_IND(CONNECT):
capidev_handle_connect_indication(CMSG, PLCI, NCCI, &i);
capidev_handle_connect_indication(CMSG, PLCI, NCCI, &i, &owner);
break;
case CAPI_P_IND(DATA_B3):
capidev_handle_data_b3_indication(CMSG, PLCI, NCCI, i, 0, 0);
@ -5318,11 +5367,11 @@ static void capidev_handle_msg(_cmsg *CMSG)
case CAPI_P_CONF(FACILITY):
wInfo = FACILITY_CONF_INFO(CMSG);
capidev_handle_facility_confirmation(CMSG, PLCI, NCCI, &i);
capidev_handle_facility_confirmation(CMSG, PLCI, NCCI, &i, &owner);
break;
case CAPI_P_CONF(CONNECT):
wInfo = CONNECT_CONF_INFO(CMSG);
capidev_handle_connection_conf(&i, PLCI, wInfo, wMsgNum);
capidev_handle_connection_conf(&i, PLCI, wInfo, wMsgNum, &owner);
break;
case CAPI_P_CONF(CONNECT_B3):
wInfo = CONNECT_B3_CONF_INFO(CMSG);
@ -5400,7 +5449,7 @@ static void capidev_handle_msg(_cmsg *CMSG)
break;
case _DI_ASSIGN_PLCI:
wInfo = (unsigned short)(CMSG->Class >> 16);
capidev_handle_connection_conf(&i, PLCI, wInfo, wMsgNum);
capidev_handle_connection_conf(&i, PLCI, wInfo, wMsgNum, &owner);
break;
#ifdef DIVA_STREAMING
case _DI_STREAM_CTRL:
@ -5408,16 +5457,20 @@ static void capidev_handle_msg(_cmsg *CMSG)
if (wInfo != 0) {
int do_lock = (i == 0);
struct capi_pvt *ii = (i != 0) ? i : capi_find_interface_by_msgnum(wMsgNum);
struct ast_channel* streaming_interface_owner = 0;
cc_log(LOG_ERROR, "stream error %04x for %s=%#x\n", wInfo, PLCI != 0 ? "PLCI" : "MsgNr", PLCI != 0 ? PLCI : wMsgNum);
if (ii != 0) {
if (do_lock) {
cc_mutex_lock(&ii->lock);
streaming_interface_owner = capidev_acquire_locks_from_thread_context (ii);
}
capi_DivaStreamingRemove(ii);
if (do_lock) {
cc_mutex_unlock(&ii->lock);
if (streaming_interface_owner != 0) {
ast_channel_unlock (streaming_interface_owner);
}
}
} else {
cc_log(LOG_ERROR, "stream error %04x for MsgNr %04x, unexpected", wInfo, wMsgNum);
@ -5456,6 +5509,10 @@ static void capidev_handle_msg(_cmsg *CMSG)
cc_mutex_unlock(&i->lock);
}
if (owner != 0) {
ast_channel_unlock (owner);
}
return;
}

View File

@ -712,7 +712,7 @@ extern void cc_start_b3(struct capi_pvt *i);
extern unsigned char capi_tcap_is_digital(unsigned short tcap);
extern void capi_queue_cause_control(struct capi_pvt *i, int control);
extern void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
unsigned short wInfo, unsigned short wMsgNum);
unsigned short wInfo, unsigned short wMsgNum, struct ast_channel** interface_owner);
extern void capi_wait_for_answered(struct capi_pvt *i);
extern int capi_wait_for_b3_up(struct capi_pvt *i);
extern void capi_activehangup(struct capi_pvt *i, int state);

View File

@ -664,7 +664,7 @@ int handle_facility_indication_supplementary(
* CAPI FACILITY_CONF supplementary
*/
void handle_facility_confirmation_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i)
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i, struct ast_channel** interface_owner)
{
_cword function;
_cword serviceinfo;
@ -709,7 +709,7 @@ void handle_facility_confirmation_supplementary(
case 0x0012: /* CCBS call */
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: CCBS call confirmation (0x%04x) (PLCI=%#x)\n",
name, serviceinfo, PLCI);
capidev_handle_connection_conf(i, PLCI, FACILITY_CONF_INFO(CMSG), HEADER_MSGNUM(CMSG));
capidev_handle_connection_conf(i, PLCI, FACILITY_CONF_INFO(CMSG), HEADER_MSGNUM(CMSG), interface_owner);
break;
default:
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: unhandled FACILITY_CONF supplementary function %04x\n",

View File

@ -19,7 +19,8 @@ extern void ListenOnSupplementary(unsigned controller);
extern int handle_facility_indication_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
extern void handle_facility_confirmation_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i);
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i,
struct ast_channel** interface_owner);
extern int pbx_capi_ccbs(struct ast_channel *c, char *data);
extern int pbx_capi_ccbsstop(struct ast_channel *c, char *data);
extern int pbx_capi_ccpartybusy(struct ast_channel *c, char *data);