diff options
author | Russell Bryant <russell@russellbryant.com> | 2007-03-29 18:31:35 +0000 |
---|---|---|
committer | Russell Bryant <russell@russellbryant.com> | 2007-03-29 18:31:35 +0000 |
commit | 5d4e6a480ad83291f278cbe5d375133554c26ba0 (patch) | |
tree | c7737b0a21f86542dcb667f554038d7d0c736c9a /channels/chan_iax2.c | |
parent | 921e975d5f19f623b009f1804dbfd010127ff47d (diff) |
Fix an issue with hashing iax2 pvt structures that caused random crashes on
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
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r-- | channels/chan_iax2.c | 45 |
1 files changed, 29 insertions, 16 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 712ecad7f..58ec437af 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -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 */ |