Index: channels/chan_dahdi.c =================================================================== --- channels/chan_dahdi.c (revision 291341) +++ channels/chan_dahdi.c (working copy) @@ -2053,6 +2053,13 @@ ast_mutex_unlock(&p->lock); } +static void my_deadlock_avoidance_private(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + DEADLOCK_AVOIDANCE(&p->lock); +} + /* linear_mode = 0 - turn linear mode off, >0 - turn linear mode on * returns the last value of the linear setting */ @@ -3474,6 +3481,7 @@ .all_subchannels_hungup = my_all_subchannels_hungup, .lock_private = my_lock_private, .unlock_private = my_unlock_private, + .deadlock_avoidance_private = my_deadlock_avoidance_private, .handle_dtmfup = my_handle_dtmfup, .wink = my_wink, .new_ast_channel = my_new_analog_ast_channel, @@ -3532,22 +3540,46 @@ return res; } -static void wakeup_sub(struct dahdi_pvt *p, int a) +/*! + * \internal + * \brief Obtain the specified subchannel owner lock if the owner exists. + * + * \param pvt Channel private struct. + * \param sub_idx Subchannel owner to lock. + * + * \note Assumes the pvt->lock is already obtained. + * + * \note + * Because deadlock avoidance may have been necessary, you need to confirm + * the state of things before continuing. + * + * \return Nothing + */ +static void dahdi_lock_sub_owner(struct dahdi_pvt *pvt, int sub_idx) { for (;;) { - if (p->subs[a].owner) { - if (ast_channel_trylock(p->subs[a].owner)) { - DEADLOCK_AVOIDANCE(&p->lock); - } else { - ast_queue_frame(p->subs[a].owner, &ast_null_frame); - ast_channel_unlock(p->subs[a].owner); - break; - } - } else + if (!pvt->subs[sub_idx].owner) { + /* No subchannel owner pointer */ break; + } + if (!ast_channel_trylock(pvt->subs[sub_idx].owner)) { + /* Got subchannel owner lock */ + break; + } + /* We must unlock the private to avoid the possibility of a deadlock */ + DEADLOCK_AVOIDANCE(&pvt->lock); } } +static void wakeup_sub(struct dahdi_pvt *p, int a) +{ + dahdi_lock_sub_owner(p, a); + if (p->subs[a].owner) { + ast_queue_frame(p->subs[a].owner, &ast_null_frame); + ast_channel_unlock(p->subs[a].owner); + } +} + static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f) { for (;;) { @@ -11237,9 +11269,13 @@ if (last) { struct analog_pvt *analog_p = last->sig_pvt; /* Only allow MWI to be initiated on a quiescent fxs port */ - if (!last->mwisendactive && last->sig & __DAHDI_SIG_FXO && - !analog_p->fxsoffhookstate && !last->owner && - !ast_strlen_zero(last->mailbox) && (thispass - analog_p->onhooktime > 3)) { + if (analog_p + && !last->mwisendactive + && (last->sig & __DAHDI_SIG_FXO) + && !analog_p->fxsoffhookstate + && !last->owner + && !ast_strlen_zero(last->mailbox) + && (thispass - analog_p->onhooktime > 3)) { res = has_voicemail(last); if (analog_p->msgstate != res) { /* Set driver resources for signalling VMWI */ Index: channels/sig_analog.c =================================================================== --- channels/sig_analog.c (revision 291341) +++ channels/sig_analog.c (working copy) @@ -508,20 +508,60 @@ } } +#if 0 static void analog_unlock_private(struct analog_pvt *p) { if (p->calls->unlock_private) { p->calls->unlock_private(p->chan_pvt); } } +#endif +#if 0 static void analog_lock_private(struct analog_pvt *p) { if (p->calls->lock_private) { p->calls->lock_private(p->chan_pvt); } } +#endif +/*! + * \internal + * \brief Obtain the specified subchannel owner lock if the owner exists. + * + * \param pvt Analog private struct. + * \param sub_idx Subchannel owner to lock. + * + * \note Assumes the analog_lock_private(pvt->chan_pvt) is already obtained. + * + * \note + * Because deadlock avoidance may have been necessary, you need to confirm + * the state of things before continuing. + * + * \return Nothing + */ +static void analog_lock_sub_owner(struct analog_pvt *pvt, enum analog_sub sub_idx) +{ + for (;;) { + if (!pvt->subs[sub_idx].owner) { + /* No subchannel owner pointer */ + break; + } + if (!ast_channel_trylock(pvt->subs[sub_idx].owner)) { + /* Got subchannel owner lock */ + break; + } + /* We must unlock the private to avoid the possibility of a deadlock */ + if (pvt->calls->deadlock_avoidance_private) { + pvt->calls->deadlock_avoidance_private(pvt->chan_pvt); + } else { + /* Don't use 100% CPU if required callback not present. */ + usleep(1); + } + } +} + static int analog_off_hook(struct analog_pvt *p) { if (p->calls->off_hook) { @@ -605,6 +645,20 @@ return -1; } +/*! + * \internal + * \brief Attempt to transfer 3-way call. + * + * \param p Analog private structure. + * + * \note + * On entry these locks are held: real-call, private, 3-way call. + * + * \retval 1 Transfer successful. 3-way call is unlocked and subchannel is unalloced. + * Swapped real and 3-way subchannel. + * \retval 0 Transfer successful. 3-way call is unlocked and subchannel is unalloced. + * \retval -1 on error. Caller must unlock 3-way call. + */ static int analog_attempt_transfer(struct analog_pvt *p) { /* In order to transfer, we need at least one of the channels to @@ -612,11 +666,18 @@ together (but then, why would we want to?) */ if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { /* The three-way person we're about to transfer to could still be in MOH, so - stop if now if appropriate */ + stop it now if appropriate */ if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_UNHOLD); } if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) { + /* + * This may not be safe. + * We currently hold the locks on the real-call, private, and 3-way call. + * We could possibly avoid this here by using an ast_queue_control() instead. + * However, the following ast_channel_masquerade() is going to be locking + * the bridged channel again anyway. + */ ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner), AST_CONTROL_RINGING); } if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RING) { @@ -636,6 +697,13 @@ } else if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RINGING) { + /* + * This may not be safe. + * We currently hold the locks on the real-call, private, and 3-way call. + * We could possibly avoid this here by using an ast_queue_control() instead. + * However, the following ast_channel_masquerade() is going to be locking + * the bridged channel again anyway. + */ ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), AST_CONTROL_RINGING); } if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RING) { @@ -656,7 +724,6 @@ } else { ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n", p->subs[ANALOG_SUB_REAL].owner->name, p->subs[ANALOG_SUB_THREEWAY].owner->name); - ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); return -1; } return 0; @@ -1196,7 +1263,8 @@ p->subs[index].owner = NULL; p->polarity = POLARITY_IDLE; analog_set_linear_mode(p, index, 0); - if (index == ANALOG_SUB_REAL) { + switch (index) { + case ANALOG_SUB_REAL: if (p->subs[ANALOG_SUB_CALLWAIT].allocd && p->subs[ANALOG_SUB_THREEWAY].allocd) { ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n"); if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { @@ -1215,15 +1283,23 @@ /* This was part of a three way call. Immediately make way for another call */ ast_debug(1, "Call was complete, setting owner to former third call\n"); + p->subs[ANALOG_SUB_REAL].inthreeway = 0; p->owner = p->subs[ANALOG_SUB_REAL].owner; } else { /* This call hasn't been completed yet... Set owner to NULL */ ast_debug(1, "Call was incomplete, setting owner to NULL\n"); p->owner = NULL; } - p->subs[ANALOG_SUB_REAL].inthreeway = 0; } } else if (p->subs[ANALOG_SUB_CALLWAIT].allocd) { + /* Need to hold the lock for real-call, private, and call-waiting call */ + analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT); + if (!p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* The call waiting call dissappeared. */ + p->owner = NULL; + break; + } + /* Move to the call-wait and switch back to them. */ analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL); analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); @@ -1234,6 +1310,8 @@ if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); } + /* Unlock the call-waiting call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); } else if (p->subs[ANALOG_SUB_THREEWAY].allocd) { analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); @@ -1241,17 +1319,21 @@ /* This was part of a three way call. Immediately make way for another call */ ast_debug(1, "Call was complete, setting owner to former third call\n"); + p->subs[ANALOG_SUB_REAL].inthreeway = 0; p->owner = p->subs[ANALOG_SUB_REAL].owner; } else { /* This call hasn't been completed yet... Set owner to NULL */ ast_debug(1, "Call was incomplete, setting owner to NULL\n"); p->owner = NULL; } - p->subs[ANALOG_SUB_REAL].inthreeway = 0; } - } else if (index == ANALOG_SUB_CALLWAIT) { - /* Ditch the holding callwait call, and immediately make it availabe */ + break; + case ANALOG_SUB_CALLWAIT: + /* Ditch the holding callwait call, and immediately make it available */ if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { + /* Need to hold the lock for call-waiting call, private, and 3-way call */ + analog_lock_sub_owner(p, ANALOG_SUB_THREEWAY); + /* This is actually part of a three way, placed on hold. Place the third part on music on hold now */ if (p->subs[ANALOG_SUB_THREEWAY].owner && ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { @@ -1263,27 +1345,42 @@ /* Make it the call wait now */ analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY); analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* Unlock the 3-way call that we swapped to call-waiting call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + } } else { analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT); } - } else if (index == ANALOG_SUB_THREEWAY) { + break; + case ANALOG_SUB_THREEWAY: + /* Need to hold the lock for 3-way call, private, and call-waiting call */ + analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT); if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) { /* The other party of the three way call is currently in a call-wait state. Start music on hold for them, and take the main guy out of the third call */ + p->subs[ANALOG_SUB_CALLWAIT].inthreeway = 0; if (p->subs[ANALOG_SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) { ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, S_OR(p->mohsuggest, NULL), !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); } - p->subs[ANALOG_SUB_CALLWAIT].inthreeway = 0; } + if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + ast_channel_unlock(p->subs[ANALOG_SUB_CALLWAIT].owner); + } p->subs[ANALOG_SUB_REAL].inthreeway = 0; /* If this was part of a three way call index, let us make another three way call */ analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); - } else { - /* This wasn't any sort of call, but how are we an index? */ - ast_log(LOG_WARNING, "Index found but not any type of call?\n"); + break; + default: + /* + * Should never happen. + * This wasn't any sort of call, so how are we an index? + */ + ast_log(LOG_ERROR, "Index found but not any type of call?\n"); + break; } } @@ -1599,9 +1696,8 @@ } ast_verb(3, "Starting simple switch on '%s'\n", chan->name); - index = analog_get_index(chan, p, 1); + index = analog_get_index(chan, p, 0); if (index < 0) { - ast_log(LOG_WARNING, "Huh?\n"); ast_hangup(chan); goto quit; } @@ -2453,10 +2549,18 @@ ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); index = analog_get_index(ast, p, 0); + if (index < 0) { + return &ast_null_frame; + } + if (index != ANALOG_SUB_REAL) { + ast_log(LOG_ERROR, "We got an event on a non real sub. Fix it!\n"); + } + mysig = p->sig; if (p->outsigmod > -1) { mysig = p->outsigmod; } + p->subs[index].f.frametype = AST_FRAME_NULL; p->subs[index].f.subclass.integer = 0; p->subs[index].f.datalen = 0; @@ -2467,14 +2571,6 @@ p->subs[index].f.data.ptr = NULL; f = &p->subs[index].f; - if (index < 0) { - return &p->subs[index].f; - } - - if (index != ANALOG_SUB_REAL) { - ast_log(LOG_ERROR, "We got an event on a non real sub. Fix it!\n"); - } - res = analog_get_event(p); ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", analog_event2str(res), res, p->channel, index); @@ -2583,6 +2679,17 @@ if (index == ANALOG_SUB_REAL) { /* The normal line was hung up */ if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* Need to hold the lock for real-call, private, and call-waiting call */ + analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT); + if (!p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* + * The call waiting call dissappeared. + * This is now a normal hangup. + */ + analog_set_echocanceller(p, 0); + return NULL; + } + /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */ analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL); ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel); @@ -2593,64 +2700,58 @@ if (p->subs[ANALOG_SUB_REAL].owner->_state != AST_STATE_UP) { analog_set_dialing(p, 1); } + /* Unlock the call-waiting call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); analog_ring(p); } else if (p->subs[ANALOG_SUB_THREEWAY].owner) { unsigned int mssinceflash; - /* Here we have to retain the lock on both the main channel, the 3-way channel, and - the private structure -- not especially easy or clean */ - while (p->subs[ANALOG_SUB_THREEWAY].owner && ast_channel_trylock(p->subs[ANALOG_SUB_THREEWAY].owner)) { - /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */ - analog_unlock_private(p); - CHANNEL_DEADLOCK_AVOIDANCE(ast); - /* We can grab ast and p in that order, without worry. We should make sure - nothing seriously bad has happened though like some sort of bizarre double - masquerade! */ - analog_lock_private(p); - if (p->owner != ast) { - ast_log(LOG_WARNING, "This isn't good...\n"); - return NULL; - } - } + + /* Need to hold the lock for real-call, private, and 3-way call */ + analog_lock_sub_owner(p, ANALOG_SUB_THREEWAY); if (!p->subs[ANALOG_SUB_THREEWAY].owner) { ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n"); + /* Just hangup */ return NULL; } + if (p->owner != ast) { + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); + ast_log(LOG_WARNING, "This isn't good...\n"); + /* Just hangup */ + return NULL; + } + mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); ast_debug(1, "Last flash was %d ms ago\n", mssinceflash); if (mssinceflash < MIN_MS_SINCE_FLASH) { /* It hasn't been long enough since the last flashook. This is probably a bounce on hanging up. Hangup both channels now */ - if (p->subs[ANALOG_SUB_THREEWAY].owner) { - ast_queue_hangup_with_cause(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER); - } + ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); + ast_queue_hangup_with_cause(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER); ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) { if (p->transfer) { /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */ if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) { - ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); /* Swap subs and dis-own channel */ analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + /* Unlock the 3-way call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); p->owner = NULL; /* Ring the phone */ analog_ring(p); } else { - if ((res = analog_attempt_transfer(p)) < 0) { + res = analog_attempt_transfer(p); + if (res < 0) { + /* Transfer attempt failed. */ ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - if (p->subs[ANALOG_SUB_THREEWAY].owner) { - ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); - } + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); } else if (res) { /* this isn't a threeway call anymore */ p->subs[ANALOG_SUB_REAL].inthreeway = 0; p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0; /* Don't actually hang up at this point */ - if (p->subs[ANALOG_SUB_THREEWAY].owner) { - ast_channel_unlock(&p->subs[ANALOG_SUB_THREEWAY].owner); - } break; } } @@ -2659,15 +2760,14 @@ p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0; } else { ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); - if (p->subs[ANALOG_SUB_THREEWAY].owner) { - ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); - } + ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); } } else { ast_cel_report_event(ast, AST_CEL_BLINDTRANSFER, NULL, ast->linkedid, NULL); - ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); /* Swap subs and dis-own channel */ analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + /* Unlock the 3-way call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); p->owner = NULL; /* Ring the phone */ analog_ring(p); @@ -2905,32 +3005,43 @@ } if (p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* Need to hold the lock for real-call, private, and call-waiting call */ + analog_lock_sub_owner(p, ANALOG_SUB_CALLWAIT); + if (!p->subs[ANALOG_SUB_CALLWAIT].owner) { + /* + * The call waiting call dissappeared. + * Let's just ignore this flash-hook. + */ + ast_log(LOG_NOTICE, "Whoa, the call-waiting call disappeared.\n"); + goto winkflashdone; + } + /* Swap to call-wait */ - int previous_state = p->subs[ANALOG_SUB_CALLWAIT].owner->_state; - if (p->subs[ANALOG_SUB_CALLWAIT].owner->_state == AST_STATE_RINGING) { - ast_setstate(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_STATE_UP); - } analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT); analog_play_tone(p, ANALOG_SUB_REAL, -1); p->owner = p->subs[ANALOG_SUB_REAL].owner; ast_debug(1, "Making %s the new owner\n", p->owner->name); - if (previous_state == AST_STATE_RINGING) { + if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) { + ast_setstate(p->subs[ANALOG_SUB_REAL].owner, AST_STATE_UP); ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); } analog_stop_callwait(p); + /* Start music on hold if appropriate */ if (!p->subs[ANALOG_SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) { ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, S_OR(p->mohsuggest, NULL), !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); } - ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { ast_queue_control_data(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_HOLD, S_OR(p->mohsuggest, NULL), !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); } ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); + + /* Unlock the call-waiting call that we swapped to real-call. */ + ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); } else if (!p->subs[ANALOG_SUB_THREEWAY].owner) { if (!p->threewaycalling) { /* Just send a flash if no 3-way calling */ @@ -2957,10 +3068,10 @@ /* XXX This section needs much more error checking!!! XXX */ /* Start a 3-way call if feasible */ if (!((ast->pbx) || - (ast->_state == AST_STATE_UP) || - (ast->_state == AST_STATE_RING))) { + (ast->_state == AST_STATE_UP) || + (ast->_state == AST_STATE_RING))) { ast_debug(1, "Flash when call not up or ringing\n"); - goto winkflashdone; + goto winkflashdone; } if (analog_alloc_sub(p, ANALOG_SUB_THREEWAY)) { ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n"); @@ -2968,6 +3079,13 @@ } /* Make new channel */ chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY, NULL); + if (!chan) { + ast_log(LOG_WARNING, + "Cannot allocate new call structure on channel %d\n", + p->channel); + analog_unalloc_sub(p, ANALOG_SUB_THREEWAY); + goto winkflashdone; + } if (p->dahditrcallerid) { if (!p->origcid_num) { p->origcid_num = ast_strdup(p->cid_num); @@ -2989,9 +3107,7 @@ p->ss_astchan = p->owner = chan; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (!chan) { - ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel); - } else if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, p)) { + if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, p)) { ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel); res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); analog_set_echocanceller(p, 1); @@ -3022,6 +3138,20 @@ } } else { /* Already have a 3 way call */ + enum analog_sub orig_3way_sub; + + /* Need to hold the lock for real-call, private, and 3-way call */ + analog_lock_sub_owner(p, ANALOG_SUB_THREEWAY); + if (!p->subs[ANALOG_SUB_THREEWAY].owner) { + /* + * The 3-way call dissappeared. + * Let's just ignore this flash-hook. + */ + ast_log(LOG_NOTICE, "Whoa, the 3-way call disappeared.\n"); + goto winkflashdone; + } + orig_3way_sub = ANALOG_SUB_THREEWAY; + if (p->subs[ANALOG_SUB_THREEWAY].inthreeway) { /* Call is already up, drop the last person */ ast_debug(1, "Got flash with three way call up, dropping last call on %d\n", p->channel); @@ -3030,6 +3160,7 @@ (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_UP)) { /* Swap back -- we're dropping the real 3-way that isn't finished yet*/ analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + orig_3way_sub = ANALOG_SUB_REAL; p->owner = p->subs[ANALOG_SUB_REAL].owner; } /* Drop the last call and stop the conference */ @@ -3041,7 +3172,6 @@ /* Lets see what we're up to */ if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) { - int otherindex = ANALOG_SUB_THREEWAY; struct ast_channel *other = ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner); int way3bridge = 0, cdr3way = 0; @@ -3061,10 +3191,10 @@ p->subs[ANALOG_SUB_REAL].inthreeway = 1; if (ast->_state == AST_STATE_UP) { analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); - otherindex = ANALOG_SUB_REAL; + orig_3way_sub = ANALOG_SUB_REAL; } - if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) { - ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD); + if (ast_bridged_channel(p->subs[orig_3way_sub].owner)) { + ast_queue_control(p->subs[orig_3way_sub].owner, AST_CONTROL_UNHOLD); } p->owner = p->subs[ANALOG_SUB_REAL].owner; if (ast->_state == AST_STATE_RINGING) { @@ -3073,18 +3203,20 @@ analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE); } } else { - ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name); + ast_verb(3, "Dumping incomplete call on %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name); analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL); + orig_3way_sub = ANALOG_SUB_REAL; ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV); p->owner = p->subs[ANALOG_SUB_REAL].owner; - if (p->subs[ANALOG_SUB_REAL].owner && ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { + if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) { ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD); } analog_set_echocanceller(p, 1); } } + ast_channel_unlock(p->subs[orig_3way_sub].owner); } - winkflashdone: +winkflashdone: analog_update_conf(p); break; case ANALOG_SIG_EM: @@ -3275,13 +3407,15 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast) { int res; - int usedindex=-1; int index; struct ast_frame *f; ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); index = analog_get_index(ast, p, 1); + if (index < 0) { + index = ANALOG_SUB_REAL; + } p->subs[index].f.frametype = AST_FRAME_NULL; p->subs[index].f.datalen = 0; @@ -3293,7 +3427,6 @@ p->subs[index].f.src = "dahdi_exception"; p->subs[index].f.data.ptr = NULL; - if (!p->owner) { /* If nobody owns us, absorb the event appropriately, otherwise we loop indefinitely. This occurs when, during call waiting, the @@ -3307,6 +3440,14 @@ (res != ANALOG_EVENT_HOOKCOMPLETE)) { ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res); p->owner = p->subs[ANALOG_SUB_REAL].owner; + if (p->owner && ast != p->owner) { + /* + * Could this even happen? + * Possible deadlock because we do not have the real-call lock. + */ + ast_log(LOG_WARNING, "Event %s on %s is not restored owner %s\n", + analog_event2str(res), ast->name, p->owner->name); + } if (p->owner && ast_bridged_channel(p->owner)) { ast_queue_control(p->owner, AST_CONTROL_UNHOLD); } @@ -3319,7 +3460,8 @@ analog_ring(p); analog_stop_callwait(p); } else { - ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); + ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n", + analog_event2str(res)); } analog_update_conf(p); break; @@ -3327,7 +3469,7 @@ analog_set_echocanceller(p, 1); analog_off_hook(p); if (p->owner && (p->owner->_state == AST_STATE_RINGING)) { - ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER); + ast_queue_control(p->owner, AST_CONTROL_ANSWER); analog_set_dialing(p, 0); } break; @@ -3342,10 +3484,7 @@ ast_verb(3, "Channel %d flashed to other channel %s\n", p->channel, p->owner->name); if (p->owner->_state != AST_STATE_UP) { /* Answer if necessary */ - usedindex = analog_get_index(p->owner, p, 0); - if (usedindex > -1) { - ast_queue_control(p->subs[usedindex].owner, AST_CONTROL_ANSWER); - } + ast_queue_control(p->owner, AST_CONTROL_ANSWER); ast_setstate(p->owner, AST_STATE_UP); } analog_stop_callwait(p); @@ -3353,12 +3492,14 @@ ast_queue_control(p->owner, AST_CONTROL_UNHOLD); } } else { - ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); + ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n", + analog_event2str(res)); } analog_update_conf(p); break; default: ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", analog_event2str(res)); + break; } f = &p->subs[index].f; return f; Index: channels/sig_analog.h =================================================================== --- channels/sig_analog.h (revision 291341) +++ channels/sig_analog.h (working copy) @@ -129,6 +129,9 @@ void (* const unlock_private)(void *pvt); /* Lock the private in the signalling private structure. ... */ void (* const lock_private)(void *pvt); + /* Do deadlock avoidance for the private signaling structure lock. */ + void (* const deadlock_avoidance_private)(void *pvt); + /* Function which is called back to handle any other DTMF up events that are received. Called by analog_handle_event. Why is this * important to use, instead of just directly using events received before they are passed into the library? Because sometimes, * (CWCID) the library absorbs DTMF events received. */