Merged revisions 336978 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Tue, 20 Sep 2011 18:20:10 +0000 (18:20 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 20 Sep 2011 18:20:10 +0000 (18:20 +0000)
https://origsvn.digium.com/svn/asterisk/branches/10

................
  r336978 | rmudgett | 2011-09-20 13:14:40 -0500 (Tue, 20 Sep 2011) | 28 lines

  Merged revisions 336977 via svnmerge from
  https://origsvn.digium.com/svn/asterisk/branches/1.8

  ........
    r336977 | rmudgett | 2011-09-20 13:12:17 -0500 (Tue, 20 Sep 2011) | 21 lines

    Fix deadlock from not releasing SS7 linkset lock.

    sig_ss7_hangup() failed to release the SS7 linkset lock if the call had
    the alreadyhungup flag set.

    * Made unlock the SS7 linkset lock in sig_ss7_hangup() if the
    alreadyhungup flag is set.

    * Made ss7_start_call() not hold any locks while creating the channel for
    an incoming call to prevent deadlock.

    * Made ss7_grab() a void function, since it could never fail, to simplify
    calling code.

    * Made obtain the channel lock to do softhangup in some places.

    Patches:
          jira_ast_668_v1.8.patch (license #5621) patch uploaded by rmudgett

    JIRA AST-668
  ........
................

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@336988 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/sig_ss7.c

index 3f7ea2a..915cced 100644 (file)
@@ -454,20 +454,27 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
        }
 
        /*
-        * Release the SS7 lock while we create the channel
-        * so other threads can send messages.
+        * Release the SS7 lock while we create the channel so other
+        * threads can send messages.  We must also release the private
+        * lock to prevent deadlock while creating the channel.
         */
        ast_mutex_unlock(&linkset->lock);
+       sig_ss7_unlock_private(p);
        c = sig_ss7_new_ast_channel(p, AST_STATE_RING, law, 0, p->exten, NULL);
        if (!c) {
                ast_log(LOG_WARNING, "Unable to start PBX on CIC %d\n", p->cic);
                ast_mutex_lock(&linkset->lock);
+               sig_ss7_lock_private(p);
                isup_rel(linkset->ss7, p->ss7call, -1);
                p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
                p->alreadyhungup = 1;
                return;
        }
 
+       /* Hold the channel and private lock while we setup the channel. */
+       ast_channel_lock(c);
+       sig_ss7_lock_private(p);
+
        sig_ss7_set_echocanceller(p, 1);
 
        /*
@@ -549,13 +556,19 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
                p->generic_name[0] = 0;
        }
 
+       sig_ss7_unlock_private(p);
+       ast_channel_unlock(c);
+
        if (ast_pbx_start(c)) {
                ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", c->name, p->cic);
                ast_hangup(c);
        } else {
                ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
        }
+
+       /* Must return with linkset and private lock. */
        ast_mutex_lock(&linkset->lock);
+       sig_ss7_lock_private(p);
 }
 
 static void ss7_apply_plan_to_number(char *buf, size_t size, const struct sig_ss7_linkset *ss7, const char *number, const unsigned nai)
@@ -739,9 +752,12 @@ void *ss7_linkset(void *data)
                                sig_ss7_set_remotelyblocked(p, 0);
                                dpc = p->dpc;
                                isup_set_call_dpc(e->rsc.call, dpc);
+                               sig_ss7_lock_owner(linkset, chanpos);
                                p->ss7call = NULL;
-                               if (p->owner)
-                                       p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                               if (p->owner) {
+                                       ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+                                       ast_channel_unlock(p->owner);
+                               }
                                sig_ss7_unlock_private(p);
                                isup_rlc(ss7, e->rsc.call);
                                break;
@@ -912,9 +928,11 @@ void *ss7_linkset(void *data)
                                }
                                p = linkset->pvts[chanpos];
                                sig_ss7_lock_private(p);
+                               sig_ss7_lock_owner(linkset, chanpos);
                                if (p->owner) {
                                        p->owner->hangupcause = e->rel.cause;
-                                       p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                       ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+                                       ast_channel_unlock(p->owner);
                                } else {
                                        ast_log(LOG_WARNING, "REL on channel (CIC %d) without owner!\n", p->cic);
                                }
@@ -1088,8 +1106,8 @@ void *ss7_linkset(void *data)
                                        else
                                                ast_log(LOG_NOTICE, "Received RLC out and we haven't sent REL.  Ignoring.\n");
                                        sig_ss7_unlock_private(p);
-                                       }
-                                       break;
+                               }
+                               break;
                        case ISUP_EVENT_FAA:
                                chanpos = ss7_find_cic(linkset, e->faa.cic, e->faa.opc);
                                if (chanpos < 0) {
@@ -1122,7 +1140,7 @@ static inline void ss7_rel(struct sig_ss7_linkset *ss7)
        ast_mutex_unlock(&ss7->lock);
 }
 
-static inline int ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
+static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
 {
        int res;
        /* Grab the lock first */
@@ -1135,7 +1153,6 @@ static inline int ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7
        /* Then break the poll */
        if (ss7->master != AST_PTHREADT_NULL)
                pthread_kill(ss7->master, SIGURG);
-       return 0;
 }
 
 /*!
@@ -1302,10 +1319,7 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, char *rdest)
                l = NULL;
        }
 
-       if (ss7_grab(p, p->ss7)) {
-               ast_log(LOG_WARNING, "Failed to grab SS7!\n");
-               return -1;
-       }
+       ss7_grab(p, p->ss7);
 
        p->ss7call = isup_new_call(p->ss7->ss7);
        if (!p->ss7call) {
@@ -1428,24 +1442,22 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast)
        p->exten[0] = '\0';
        /* Perform low level hangup if no owner left */
        if (p->ss7call) {
-               if (!ss7_grab(p, p->ss7)) {
-                       if (!p->alreadyhungup) {
-                               const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
-                               int icause = ast->hangupcause ? ast->hangupcause : -1;
-
-                               if (cause) {
-                                       if (atoi(cause))
-                                               icause = atoi(cause);
+               ss7_grab(p, p->ss7);
+               if (!p->alreadyhungup) {
+                       const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
+                       int icause = ast->hangupcause ? ast->hangupcause : -1;
+
+                       if (cause) {
+                               if (atoi(cause)) {
+                                       icause = atoi(cause);
                                }
-                               isup_rel(p->ss7->ss7, p->ss7call, icause);
-                               ss7_rel(p->ss7);
-                               p->alreadyhungup = 1;
-                       } else
-                               ast_log(LOG_WARNING, "Trying to hangup twice!\n");
+                       }
+                       isup_rel(p->ss7->ss7, p->ss7call, icause);
+                       p->alreadyhungup = 1;
                } else {
-                       ast_log(LOG_WARNING, "Unable to grab SS7 on CIC %d\n", p->cic);
-                       res = -1;
+                       ast_log(LOG_WARNING, "Trying to hangup twice!\n");
                }
+               ss7_rel(p->ss7);
        }
 
        return res;
@@ -1465,16 +1477,12 @@ int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast)
 {
        int res;
 
-       if (!ss7_grab(p, p->ss7)) {
-               if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
-                       p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
-               }
-               res = isup_anm(p->ss7->ss7, p->ss7call);
-               ss7_rel(p->ss7);
-       } else {
-               ast_log(LOG_WARNING, "Unable to grab SS7 on span %d\n", p->ss7->span);
-               res = -1;
+       ss7_grab(p, p->ss7);
+       if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
+               p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
        }
+       res = isup_anm(p->ss7->ss7, p->ss7call);
+       ss7_rel(p->ss7);
        return res;
 }