[asterisk-commits] rmudgett: branch 1.6.2 r291655 - in /branches/1.6.2: ./ channels/chan_dahdi.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Oct 13 18:36:56 CDT 2010


Author: rmudgett
Date: Wed Oct 13 18:36:50 2010
New Revision: 291655

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=291655
Log:
Merged revisions 291643 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r291643 | rmudgett | 2010-10-13 18:29:58 -0500 (Wed, 13 Oct 2010) | 20 lines
  
  Deadlock between dahdi_exception() and dahdi_indicate().
  
  There is a deadlock between dahdi_exception() and dahdi_indicate() for
  analog ports.  The call-waiting and three-way-calling feature can
  experience deadlock if these features are trying to do something and an
  event from the bridged channel happens at the same time.
  
  Deadlock avoidance code added to obtain necessary channel locks before
  attemting an operation with call-waiting and three-way-calling.
  
  (closes issue #16847)
  Reported by: shin-shoryuken
  Patches:
        issue_16847_v1.4.patch uploaded by rmudgett (license 664)
        issue_16847_v1.6.2.patch uploaded by rmudgett (license 664)
        issue_16847_v1.8_v2.patch uploaded by rmudgett (license 664)
  Tested by: alecdavis, rmudgett
  
  Review: https://reviewboard.asterisk.org/r/971/
........

Modified:
    branches/1.6.2/   (props changed)
    branches/1.6.2/channels/chan_dahdi.c

Propchange: branches/1.6.2/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: branches/1.6.2/channels/chan_dahdi.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.6.2/channels/chan_dahdi.c?view=diff&rev=291655&r1=291654&r2=291655
==============================================================================
--- branches/1.6.2/channels/chan_dahdi.c (original)
+++ branches/1.6.2/channels/chan_dahdi.c Wed Oct 13 18:36:50 2010
@@ -1519,23 +1519,47 @@
 	return res;
 }
 
+/*!
+ * \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 (!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, struct dahdi_pri *pri)
 {
 #ifdef HAVE_PRI
 	if (pri)
 		ast_mutex_unlock(&pri->lock);
 #endif
-	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
-			break;
+	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);
 	}
 #ifdef HAVE_PRI
 	if (pri)
@@ -4391,7 +4415,8 @@
 		p->subs[idx].needcallerid = 0;
 		p->polarity = POLARITY_IDLE;
 		dahdi_setlinear(p->subs[idx].dfd, 0);
-		if (idx == SUB_REAL) {
+		switch (idx) {
+		case SUB_REAL:
 			if ((p->subs[SUB_CALLWAIT].dfd > -1) && (p->subs[SUB_THREEWAY].dfd > -1)) {
 				ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n");
 				if (p->subs[SUB_CALLWAIT].inthreeway) {
@@ -4410,15 +4435,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[SUB_REAL].inthreeway = 0;
 						p->owner = p->subs[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[SUB_REAL].inthreeway = 0;
 				}
 			} else if (p->subs[SUB_CALLWAIT].dfd > -1) {
+				/* Need to hold the lock for real-call, private, and call-waiting call */
+				dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+				if (!p->subs[SUB_CALLWAIT].owner) {
+					/* The call waiting call dissappeared. */
+					p->owner = NULL;
+					break;
+				}
+
 				/* Move to the call-wait and switch back to them. */
 				swap_subs(p, SUB_CALLWAIT, SUB_REAL);
 				unalloc_sub(p, SUB_CALLWAIT);
@@ -4427,6 +4460,8 @@
 					p->subs[SUB_REAL].needanswer = 1;
 				if (ast_bridged_channel(p->subs[SUB_REAL].owner))
 					ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+				/* Unlock the call-waiting call that we swapped to real-call. */
+				ast_channel_unlock(p->subs[SUB_REAL].owner);
 			} else if (p->subs[SUB_THREEWAY].dfd > -1) {
 				swap_subs(p, SUB_THREEWAY, SUB_REAL);
 				unalloc_sub(p, SUB_THREEWAY);
@@ -4434,17 +4469,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[SUB_REAL].inthreeway = 0;
 					p->owner = p->subs[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[SUB_REAL].inthreeway = 0;
-			}
-		} else if (idx == SUB_CALLWAIT) {
+			}
+			break;
+		case SUB_CALLWAIT:
 			/* Ditch the holding callwait call, and immediately make it availabe */
 			if (p->subs[SUB_CALLWAIT].inthreeway) {
+				/* Need to hold the lock for call-waiting call, private, and 3-way call */
+				dahdi_lock_sub_owner(p, 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[SUB_THREEWAY].owner && ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
@@ -4456,26 +4495,41 @@
 				/* Make it the call wait now */
 				swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
 				unalloc_sub(p, SUB_THREEWAY);
+				if (p->subs[SUB_CALLWAIT].owner) {
+					/* Unlock the 3-way call that we swapped to call-waiting call. */
+					ast_channel_unlock(p->subs[SUB_CALLWAIT].owner);
+				}
 			} else
 				unalloc_sub(p, SUB_CALLWAIT);
-		} else if (idx == SUB_THREEWAY) {
+			break;
+		case SUB_THREEWAY:
+			/* Need to hold the lock for 3-way call, private, and call-waiting call */
+			dahdi_lock_sub_owner(p, SUB_CALLWAIT);
 			if (p->subs[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[SUB_CALLWAIT].inthreeway = 0;
 				if (p->subs[SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
 					ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
 						S_OR(p->mohsuggest, NULL),
 						!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
 				}
-				p->subs[SUB_CALLWAIT].inthreeway = 0;
+			}
+			if (p->subs[SUB_CALLWAIT].owner) {
+				ast_channel_unlock(p->subs[SUB_CALLWAIT].owner);
 			}
 			p->subs[SUB_REAL].inthreeway = 0;
 			/* If this was part of a three way call index, let us make
 			   another three way call */
 			unalloc_sub(p, 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;
 		}
 	}
 
@@ -5584,6 +5638,20 @@
 
 static void *ss_thread(void *data);
 
+/*!
+ * \internal
+ * \brief Attempt to transfer 3-way call.
+ *
+ * \param p 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 attempt_transfer(struct dahdi_pvt *p)
 {
 	/* In order to transfer, we need at least one of the channels to
@@ -5591,10 +5659,17 @@
 	   together (but then, why would we want to?) */
 	if (ast_bridged_channel(p->subs[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[SUB_THREEWAY].owner))
 			ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
 		if (p->subs[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[SUB_REAL].owner), AST_CONTROL_RINGING);
 		}
 		if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RING) {
@@ -5611,6 +5686,13 @@
 	} else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
 		ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
 		if (p->subs[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[SUB_THREEWAY].owner), AST_CONTROL_RINGING);
 		}
 		if (p->subs[SUB_REAL].owner->_state == AST_STATE_RING) {
@@ -5630,7 +5712,6 @@
 	} else {
 		ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
 			p->subs[SUB_REAL].owner->name, p->subs[SUB_THREEWAY].owner->name);
-		p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
 		return -1;
 	}
 	return 0;
@@ -5792,9 +5873,17 @@
 	struct ast_frame *f;
 
 	idx = dahdi_get_index(ast, p, 0);
+	if (idx < 0) {
+		return &ast_null_frame;
+	}
+	if (idx != 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[idx].f.frametype = AST_FRAME_NULL;
 	p->subs[idx].f.subclass = 0;
 	p->subs[idx].f.datalen = 0;
@@ -5805,8 +5894,6 @@
 	p->subs[idx].f.data.ptr = NULL;
 	f = &p->subs[idx].f;
 
-	if (idx < 0)
-		return &p->subs[idx].f;
 	if (p->fake_event) {
 		res = p->fake_event;
 		p->fake_event = 0;
@@ -5999,6 +6086,17 @@
 				if (idx == SUB_REAL) {
 					/* The normal line was hung up */
 					if (p->subs[SUB_CALLWAIT].owner) {
+						/* Need to hold the lock for real-call, private, and call-waiting call */
+						dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+						if (!p->subs[SUB_CALLWAIT].owner) {
+							/*
+							 * The call waiting call dissappeared.
+							 * This is now a normal hangup.
+							 */
+							dahdi_disable_ec(p);
+							return NULL;
+						}
+
 						/* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
 						swap_subs(p, SUB_CALLWAIT, SUB_REAL);
 						ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
@@ -6013,37 +6111,34 @@
 						/* Don't start streaming audio yet if the incoming call isn't up yet */
 						if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP)
 							p->dialing = 1;
+						/* Unlock the call-waiting call that we swapped to real-call. */
+						ast_channel_unlock(p->subs[SUB_REAL].owner);
 						dahdi_ring_phone(p);
 					} else if (p->subs[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[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) {
-							/* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
-							DLA_UNLOCK(&p->lock);
-							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! */
-							DLA_LOCK(&p->lock);
-							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 */
+						dahdi_lock_sub_owner(p, SUB_THREEWAY);
 						if (!p->subs[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[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[SUB_THREEWAY].owner)
-								ast_queue_hangup_with_cause(p->subs[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[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
 							p->subs[SUB_THREEWAY].owner->_softhangup |= 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[SUB_THREEWAY].owner);
 						} else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) {
 							if (p->transfer) {
@@ -6052,33 +6147,33 @@
 								p->subs[SUB_THREEWAY].inthreeway = 0;
 								/* 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[SUB_THREEWAY].owner);
 									/* Swap subs and dis-own channel */
 									swap_subs(p, SUB_THREEWAY, SUB_REAL);
+									/* Unlock the 3-way call that we swapped to real-call. */
+									ast_channel_unlock(p->subs[SUB_REAL].owner);
 									p->owner = NULL;
 									/* Ring the phone */
 									dahdi_ring_phone(p);
 								} else {
-									if ((res = attempt_transfer(p)) < 0) {
+									res = attempt_transfer(p);
+									if (res < 0) {
+										/* Transfer attempt failed. */
 										p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-										if (p->subs[SUB_THREEWAY].owner)
-											ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+										ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
 									} else if (res) {
 										/* Don't actually hang up at this point */
-										if (p->subs[SUB_THREEWAY].owner)
-											ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
 										break;
 									}
 								}
 							} else {
 								p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-								if (p->subs[SUB_THREEWAY].owner)
-									ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+								ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
 							}
 						} else {
-							ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
 							/* Swap subs and dis-own channel */
 							swap_subs(p, SUB_THREEWAY, SUB_REAL);
+							/* Unlock the 3-way call that we swapped to real-call. */
+							ast_channel_unlock(p->subs[SUB_REAL].owner);
 							p->owner = NULL;
 							/* Ring the phone */
 							dahdi_ring_phone(p);
@@ -6339,6 +6434,17 @@
 				}
 
 				if (p->subs[SUB_CALLWAIT].owner) {
+					/* Need to hold the lock for real-call, private, and call-waiting call */
+					dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+					if (!p->subs[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 */
 					swap_subs(p, SUB_REAL, SUB_CALLWAIT);
 					tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
@@ -6350,6 +6456,7 @@
 					}
 					p->callwaitingrepeat = 0;
 					p->cidcwexpire = 0;
+
 					/* Start music on hold if appropriate */
 					if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
 						ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
@@ -6363,6 +6470,9 @@
 							!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
 					}
 					p->subs[SUB_REAL].needunhold = 1;
+
+					/* Unlock the call-waiting call that we swapped to real-call. */
+					ast_channel_unlock(p->subs[SUB_REAL].owner);
 				} else if (!p->subs[SUB_THREEWAY].owner) {
 					if (!p->threewaycalling) {
 						/* Just send a flash if no 3-way calling */
@@ -6394,6 +6504,13 @@
 						}
 						/* Make new channel */
 						chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
+						if (!chan) {
+							ast_log(LOG_WARNING,
+								"Cannot allocate new call structure on channel %d\n",
+								p->channel);
+							unalloc_sub(p, SUB_THREEWAY);
+							goto winkflashdone;
+						}
 						if (p->dahditrcallerid) {
 							if (!p->origcid_num)
 								p->origcid_num = ast_strdup(p->cid_num);
@@ -6410,9 +6527,7 @@
 						if (res)
 							ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
 						p->owner = chan;
-						if (!chan) {
-							ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
-						} else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+						if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
 							ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
 							res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
 							dahdi_enable_ec(p);
@@ -6442,6 +6557,20 @@
 					}
 				} else {
 					/* Already have a 3 way call */
+					int orig_3way_sub;
+
+					/* Need to hold the lock for real-call, private, and 3-way call */
+					dahdi_lock_sub_owner(p, SUB_THREEWAY);
+					if (!p->subs[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 = SUB_THREEWAY;
+
 					if (p->subs[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);
@@ -6449,6 +6578,7 @@
 						if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) {
 							/* Swap back -- we're dropping the real 3-way that isn't finished yet*/
 							swap_subs(p, SUB_THREEWAY, SUB_REAL);
+							orig_3way_sub = SUB_REAL;
 							p->owner = p->subs[SUB_REAL].owner;
 						}
 						/* Drop the last call and stop the conference */
@@ -6460,7 +6590,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 = SUB_THREEWAY;
 							struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
 							int way3bridge = 0, cdr3way = 0;
 
@@ -6478,11 +6607,12 @@
 							p->subs[SUB_REAL].inthreeway = 1;
 							if (ast->_state == AST_STATE_UP) {
 								swap_subs(p, SUB_THREEWAY, SUB_REAL);
-								otherindex = SUB_REAL;
+								orig_3way_sub = SUB_REAL;
 							}
-							if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner))
-								ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
-							p->subs[otherindex].needunhold = 1;
+							if (ast_bridged_channel(p->subs[orig_3way_sub].owner)) {
+								ast_queue_control(p->subs[orig_3way_sub].owner, AST_CONTROL_UNHOLD);
+							}
+							p->subs[orig_3way_sub].needunhold = 1;
 							p->owner = p->subs[SUB_REAL].owner;
 							if (ast->_state == AST_STATE_RINGING) {
 								ast_debug(1, "Enabling ringtone on real and threeway\n");
@@ -6490,16 +6620,19 @@
 								res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
 							}
 						} else {
-							ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name);
+							ast_verb(3, "Dumping incomplete call on %s\n", p->subs[SUB_THREEWAY].owner->name);
 							swap_subs(p, SUB_THREEWAY, SUB_REAL);
+							orig_3way_sub = SUB_REAL;
 							p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
 							p->owner = p->subs[SUB_REAL].owner;
-							if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner))
+							if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
 								ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+							}
 							p->subs[SUB_REAL].needunhold = 1;
 							dahdi_enable_ec(p);
 						}
 					}
+					ast_channel_unlock(p->subs[orig_3way_sub].owner);
 				}
 winkflashdone:
 				update_conf(p);
@@ -6688,12 +6821,14 @@
 {
 	struct dahdi_pvt *p = ast->tech_pvt;
 	int res;
-	int usedindex=-1;
 	int idx;
 	struct ast_frame *f;
 
 
 	idx = dahdi_get_index(ast, p, 1);
+	if (idx < 0) {
+		idx = SUB_REAL;
+	}
 
 	p->subs[idx].f.frametype = AST_FRAME_NULL;
 	p->subs[idx].f.datalen = 0;
@@ -6704,7 +6839,6 @@
 	p->subs[idx].f.delivery = ast_tv(0,0);
 	p->subs[idx].f.src = "dahdi_exception";
 	p->subs[idx].f.data.ptr = NULL;
-
 
 	if ((!p->owner) && (!(p->radio || (p->oprmode < 0)))) {
 		/* If nobody owns us, absorb the event appropriately, otherwise
@@ -6722,6 +6856,14 @@
 			(res != DAHDI_EVENT_HOOKCOMPLETE)) {
 			ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res);
 			p->owner = p->subs[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",
+					event2str(res), ast->name, p->owner->name);
+			}
 			if (p->owner && ast_bridged_channel(p->owner))
 				ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
 			p->subs[SUB_REAL].needunhold = 1;
@@ -6734,8 +6876,10 @@
 				dahdi_ring_phone(p);
 				p->callwaitingrepeat = 0;
 				p->cidcwexpire = 0;
-			} else
-				ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+			} else {
+				ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n",
+					event2str(res));
+			}
 			update_conf(p);
 			break;
 		case DAHDI_EVENT_RINGOFFHOOK:
@@ -6757,10 +6901,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 = dahdi_get_index(p->owner, p, 0);
-					if (usedindex > -1) {
-						p->subs[usedindex].needanswer = 1;
-					}
+					p->subs[SUB_REAL].needanswer = 1;
 					ast_setstate(p->owner, AST_STATE_UP);
 				}
 				p->callwaitingrepeat = 0;
@@ -6768,12 +6909,15 @@
 				if (ast_bridged_channel(p->owner))
 					ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
 				p->subs[SUB_REAL].needunhold = 1;
-			} else
-				ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+			} else {
+				ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n",
+					event2str(res));
+			}
 			update_conf(p);
 			break;
 		default:
 			ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
+			break;
 		}
 		f = &p->subs[idx].f;
 		return f;
@@ -7797,9 +7941,8 @@
 		goto quit;
 	}
 	ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
-	idx = dahdi_get_index(chan, p, 1);
+	idx = dahdi_get_index(chan, p, 0);
 	if (idx < 0) {
-		ast_log(LOG_WARNING, "Huh?\n");
 		ast_hangup(chan);
 		goto quit;
 	}
@@ -9708,9 +9851,12 @@
 					last = i;
 					if (last) {
 						/* Only allow MWI to be initiated on a quiescent fxs port */
-						if (!last->mwisendactive &&	last->sig & __DAHDI_SIG_FXO &&
-								!last->fxsoffhookstate && !last->owner &&
-								!ast_strlen_zero(last->mailbox) && (thispass - last->onhooktime > 3)) {
+						if (!last->mwisendactive
+							&& (last->sig & __DAHDI_SIG_FXO)
+							&& !last->fxsoffhookstate
+							&& !last->owner
+							&& !ast_strlen_zero(last->mailbox)
+							&& (thispass - last->onhooktime > 3)) {
 							res = has_voicemail(last);
 							if (last->msgstate != res) {
 								/* Set driver resources for signalling VMWI */




More information about the asterisk-commits mailing list