aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-11 00:29:17 +0000
committerrmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-11 00:29:17 +0000
commit8058eaa1e77f3aa77d538f5adba06847cf6265cb (patch)
tree3287afadcaa7e8ea60ace51e1084090dd2dae8a0
parentcd97ddc2daee31781b9b4612d981abf1eac5dcf4 (diff)
Reentrancy problem if outgoing call gets different B channel than requested.
The chan_dahdi pri_fixup_principle() routine needs to protect the Asterisk channel with the channel lock when it changes the technology private pointer to a new private structure. * Added lock protection while pri_fixup_principle() moves a call from one private structure to another. * Made some pri_fixup_principle() messages more meaningful. Partial backport from v1.8 -r300714. git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@307623 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_dahdi.c95
1 files changed, 90 insertions, 5 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index a1869dafd..e701b3925 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -8996,7 +8996,7 @@ next:
}
-#ifdef HAVE_PRI
+#if defined(HAVE_PRI)
static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv)
{
struct dahdi_pvt *p;
@@ -9008,8 +9008,54 @@ static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv)
}
return NULL;
}
+#endif /* defined(HAVE_PRI) */
+#if defined(HAVE_PRI)
+/*!
+ * \internal
+ * \brief Obtain the DAHDI owner channel lock if the owner exists.
+ * \since 1.8
+ *
+ * \param pri DAHDI PRI control structure.
+ * \param chanpos Channel position in the span.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the pri->pvts[chanpos]->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_lock_owner(struct dahdi_pri *pri, int chanpos)
+{
+ for (;;) {
+ if (!pri->pvts[chanpos]->owner) {
+ /* There is no owner lock to get. */
+ break;
+ }
+ if (!ast_channel_trylock(pri->pvts[chanpos]->owner)) {
+ /* We got the lock */
+ break;
+ }
+ /* We must unlock the PRI to avoid the possibility of a deadlock */
+ ast_mutex_unlock(&pri->lock);
+ DEADLOCK_AVOIDANCE(&pri->pvts[chanpos]->lock);
+ ast_mutex_lock(&pri->lock);
+ }
+}
+#endif /* defined(HAVE_PRI) */
+#if defined(HAVE_PRI)
+/*!
+ * \internal
+ * \brief Find the private structure for the libpri call.
+ *
+ * \param pri Span controller structure.
+ * \param channel LibPRI encoded channel ID.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval array-index into private pointer array on success.
+ * \retval -1 on error.
+ */
static int pri_find_principle(struct dahdi_pri *pri, int channel)
{
int x;
@@ -9037,7 +9083,22 @@ static int pri_find_principle(struct dahdi_pri *pri, int channel)
return principle;
}
+#endif /* defined(HAVE_PRI) */
+#if defined(HAVE_PRI)
+/*!
+ * \internal
+ * \brief Fixup the private structure associated with the libpri call.
+ *
+ * \param pri Span controller structure.
+ * \param principle Array-index into private array to move call to if not already there.
+ * \param c LibPRI opaque call pointer to find if need to move call.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \retval principle on success.
+ * \retval -1 on error.
+ */
static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *c)
{
int x;
@@ -9061,14 +9122,30 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *
if (principle != x) {
struct dahdi_pvt *new = pri->pvts[principle], *old = pri->pvts[x];
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Moving call from channel %d to channel %d\n",
+ /* Get locks to safely move to the new private structure. */
+ ast_mutex_lock(&old->lock);
+ sig_pri_lock_owner(pri, x);
+ ast_mutex_lock(&new->lock);
+
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3
+ "Moving call (%s) from channel %d to %d.\n",
+ old->owner ? old->owner->name : "",
old->channel, new->channel);
+ }
if (new->owner) {
- ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n",
- old->channel, new->channel, new->channel);
+ ast_log(LOG_WARNING,
+ "Can't move call (%s) from channel %d to %d. It is already in use.\n",
+ old->owner ? old->owner->name : "",
+ old->channel, new->channel);
+ ast_mutex_unlock(&new->lock);
+ if (old->owner) {
+ ast_mutex_unlock(&old->owner->lock);
+ }
+ ast_mutex_unlock(&old->lock);
return -1;
}
+
/* Fix it all up now */
new->owner = old->owner;
old->owner = NULL;
@@ -9104,6 +9181,12 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *
/* More stuff to transfer to the new channel. */
new->call_level = old->call_level;
old->call_level = DAHDI_CALL_LEVEL_IDLE;
+
+ ast_mutex_unlock(&old->lock);
+ if (new->owner) {
+ ast_mutex_unlock(&new->owner->lock);
+ }
+ ast_mutex_unlock(&new->lock);
}
return principle;
}
@@ -9135,7 +9218,9 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *
ast_log(LOG_WARNING, "Call specified, but not found?\n");
return -1;
}
+#endif /* defined(HAVE_PRI) */
+#if defined(HAVE_PRI)
static void *do_idle_thread(void *vchan)
{
struct ast_channel *chan = vchan;