Fix an issue with hashing iax2 pvt structures that caused random crashes on
authorRussell Bryant <russell@russellbryant.com>
Thu, 29 Mar 2007 18:31:35 +0000 (18:31 +0000)
committerRussell Bryant <russell@russellbryant.com>
Thu, 29 Mar 2007 18:31:35 +0000 (18:31 +0000)
systems under heavy load such as IAXtel.
(possibly related to issue #9403)

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

channels/chan_iax2.c

index 712ecad..58ec437 100644 (file)
@@ -474,6 +474,8 @@ struct iax_rr {
        int ooo;
 };
 
+struct iax2_pvt_ref;
+
 struct chan_iax2_pvt {
        /*! Socket to send/receive on for this call */
        int sockfd;
@@ -632,14 +634,19 @@ struct chan_iax2_pvt {
        int frames_dropped;
        /*! received frame count: (just for stats) */
        int frames_received;
-
-       AST_LIST_ENTRY(chan_iax2_pvt) entry;
+       struct iax2_pvt_ref *hash_ref;
        unsigned short hash;
 };
 
+struct iax2_pvt_ref {
+       struct chan_iax2_pvt *pvt;
+       unsigned int callno;
+       AST_LIST_ENTRY(iax2_pvt_ref) entry;
+};
+
 /* Somewhat arbitrary prime number */
-#define PVT_HASH_SIZE 563
-static AST_RWLIST_HEAD(pvt_list, chan_iax2_pvt) pvt_hash_tbl[PVT_HASH_SIZE];
+#define PVT_HASH_SIZE 3373
+static AST_RWLIST_HEAD(pvt_list, iax2_pvt_ref) pvt_hash_tbl[PVT_HASH_SIZE];
 
 static AST_LIST_HEAD_STATIC(queue, iax_frame);
 
@@ -1274,16 +1281,21 @@ static inline unsigned short peer_hash_val(const struct sockaddr_in *sin, unsign
 
 static inline void hash_on_peer(struct chan_iax2_pvt *pvt)
 {
-       if (pvt->hash) {
+       if (pvt->hash_ref) {
                AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]);
-               AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt, entry);
+               AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt->hash_ref, entry);
                AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]);    
+       } else {
+               if (!(pvt->hash_ref = ast_calloc(1, sizeof(pvt->hash_ref))))
+                       return;
+               pvt->hash_ref->pvt = pvt;
        }
 
        pvt->hash = peer_hash_val(&pvt->addr, pvt->peercallno);
+       pvt->hash_ref->callno = pvt->callno;
 
        AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]);
-       AST_RWLIST_INSERT_HEAD(&pvt_hash_tbl[pvt->hash], pvt, entry);
+       AST_RWLIST_INSERT_HEAD(&pvt_hash_tbl[pvt->hash], pvt->hash_ref, entry);
        AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]);
 }
 
@@ -1295,13 +1307,13 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
        char host[80];
        if (new <= NEW_ALLOW) {
                unsigned short hash = peer_hash_val(sin, callno);
-               const struct chan_iax2_pvt *pvt;
-               AST_RWLIST_RDLOCK(&pvt_hash_tbl[hash]);
-               AST_RWLIST_TRAVERSE(&pvt_hash_tbl[hash], pvt, entry) {
-                       ast_mutex_lock(&iaxsl[pvt->callno]);
-                       if (match(sin, callno, dcallno, pvt))
-                               res = pvt->callno;
-                       ast_mutex_unlock(&iaxsl[pvt->callno]);
+               struct iax2_pvt_ref *pvt_ref;
+               AST_RWLIST_WRLOCK(&pvt_hash_tbl[hash]);
+               AST_RWLIST_TRAVERSE(&pvt_hash_tbl[hash], pvt_ref, entry) {
+                       ast_mutex_lock(&iaxsl[pvt_ref->callno]);
+                       if (match(sin, callno, dcallno, pvt_ref->pvt))
+                               res = pvt_ref->callno;
+                       ast_mutex_unlock(&iaxsl[pvt_ref->callno]);
                        if (res > 0)
                                break;
                }
@@ -1844,10 +1856,11 @@ retry:
                        pvt->owner = NULL;
                iax2_destroy_helper(pvt);
 
-               if (pvt->hash) {
+               if (pvt->hash_ref) {
                        AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]);
-                       AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt, entry);
+                       AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt->hash_ref, entry);
                        AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]);
+                       free(pvt->hash_ref);
                }
 
                /* Already gone */