diff options
author | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2007-02-23 21:20:33 +0000 |
---|---|---|
committer | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2007-02-23 21:20:33 +0000 |
commit | d9ccedef174fbf66181dca94b04062d8cebe5d61 (patch) | |
tree | afc48767aa3da427b8993025365755981775afa8 /channels | |
parent | 3b1fc742d8aae69817e991740cf8de9ad58a6f66 (diff) |
Merge team/russell/iax2_performance.
There is not a large amount of code here and the changes are not very invasive.
However, they should significantly improve performance of chan_iax2 under load.
IAX2 media frames only carry the *source* call number. So, when one arrives,
the correct session that it is a part of has to be matched on IP address, port
number, and call number, instead of just a call number. Had these frames
carried the *destination* call number, this would not be an issue, because that
would be a unique identifier that would make it easy to immediately identify
the correct session.
The way that chan_iax2 did this matching was extremely inefficient. It starts
at the first available call number and traverses each call number sequentially,
locking and unlocking a mutex for each one, to try to match against it. It
would do this regardless of whether the call number was in use or not. So,
for a call with a local call number of 25000, every single incoming media
frame would require a traversal that required 25000 mutex lock and unlock
operations. (Note that the max call number is about 32k).
I have introduced a hash table of active IAX2 calls to improve this lookup
process. The hash is done on the IP address, port number, and call number.
So, for the previous example, a few lock/unlock operations may be done versus
25000 for each frame.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@56447 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_iax2.c | 81 |
1 files changed, 65 insertions, 16 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index a85abeeae..958a95aba 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -631,8 +631,15 @@ struct chan_iax2_pvt { int frames_dropped; /*! received frame count: (just for stats) */ int frames_received; + + AST_LIST_ENTRY(chan_iax2_pvt) entry; + unsigned int hash; }; +/* Somewhat arbitrary prime number */ +#define PVT_HASH_SIZE 563 +static AST_RWLIST_HEAD(pvt_list, chan_iax2_pvt) pvt_hash_tbl[PVT_HASH_SIZE]; + static AST_LIST_HEAD_STATIC(queue, iax_frame); static AST_LIST_HEAD_STATIC(users, iax2_user); @@ -1258,20 +1265,32 @@ static int make_trunk(unsigned short callno, int locked) return res; } -/*! - * \todo XXX Note that this function contains a very expensive operation that - * happens for *every* incoming media frame. It iterates through every - * possible call number, locking and unlocking each one, to try to match the - * incoming frame to an active call. Call numbers can be up to 2^15, 32768. - * So, for an call with a local call number of 20000, every incoming audio - * frame would require 20000 mutex lock and unlock operations. Ouch. - * - * It's a shame that IAX2 media frames carry the source call number instead of - * the destination call number. If they did, this lookup wouldn't be needed. - * However, it's too late to change that now. Instead, we need to come up with - * a better way of indexing active calls so that these frequent lookups are not - * so expensive. - */ +static inline unsigned int peer_hash_val(const struct sockaddr_in *sin, unsigned short callno) +{ + return ( (sin->sin_addr.s_addr & 0xFF000000) ^ + (sin->sin_addr.s_addr & 0x00FF0000) ^ + (sin->sin_addr.s_addr & 0x0000FF00) ^ + (sin->sin_addr.s_addr & 0x000000FF) ^ + (sin->sin_port & 0xFF00) ^ (sin->sin_port ^ 0x00FF) ^ + (callno & 0xFF00) ^ (callno & 0x00FF) ) + % PVT_HASH_SIZE; +} + +static inline void hash_on_peer(struct chan_iax2_pvt *pvt) +{ + if (pvt->hash) { + AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]); + AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt, entry); + AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]); + } + + pvt->hash = peer_hash_val(&pvt->addr, pvt->peercallno); + + AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]); + AST_RWLIST_INSERT_HEAD(&pvt_hash_tbl[pvt->hash], pvt, entry); + AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]); +} + static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd) { int res = 0; @@ -1279,7 +1298,19 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc struct timeval now; char host[80]; if (new <= NEW_ALLOW) { - /* Look for an existing connection first */ + unsigned int 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, iaxs[pvt->callno])) + res = pvt->callno; + ast_mutex_unlock(&iaxsl[pvt->callno]); + if (res > 0) + break; + } + AST_RWLIST_UNLOCK(&pvt_hash_tbl[hash]); + /* Not hashed yet, Look for an existing connection */ for (x=1;(res < 1) && (x<maxnontrunkcall);x++) { ast_mutex_lock(&iaxsl[x]); if (iaxs[x]) { @@ -1326,6 +1357,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc iaxs[x]->addr.sin_family = sin->sin_family; iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr; iaxs[x]->peercallno = callno; + hash_on_peer(iaxs[x]); iaxs[x]->callno = x; iaxs[x]->pingtime = DEFAULT_RETRY_TIME; iaxs[x]->expiry = min_reg_expire; @@ -1810,6 +1842,12 @@ retry: pvt->owner = NULL; iax2_destroy_helper(pvt); + if (pvt->hash) { + AST_RWLIST_WRLOCK(&pvt_hash_tbl[pvt->hash]); + AST_RWLIST_REMOVE(&pvt_hash_tbl[pvt->hash], pvt, entry); + AST_RWLIST_UNLOCK(&pvt_hash_tbl[pvt->hash]); + } + /* Already gone */ ast_set_flag(pvt, IAX_ALREADYGONE); @@ -5406,6 +5444,7 @@ static int complete_transfer(int callno, struct iax_ies *ies) pvt->iseqno = 0; pvt->aseqno = 0; pvt->peercallno = peercallno; + hash_on_peer(pvt); pvt->transferring = TRANSFER_NONE; pvt->svoiceformat = -1; pvt->voiceformat = 0; @@ -6616,8 +6655,12 @@ static int socket_process(struct iax2_thread *thread) if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid && f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */ - f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */ + f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */ iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL); + ast_mutex_unlock(&iaxsl[fr->callno]); + hash_on_peer(iaxs[fr->callno]); + ast_mutex_lock(&iaxsl[fr->callno]); + } if (ntohs(mh->callno) & IAX_FLAG_FULL) { if (option_debug && iaxdebug) ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass); @@ -10103,6 +10146,9 @@ static int __unload_module(void) for (x = 0; x < IAX_MAX_CALLS; x++) ast_mutex_destroy(&iaxsl[x]); + for (x = 0; x < PVT_HASH_SIZE; x++) + AST_RWLIST_HEAD_DESTROY(&pvt_hash_tbl[x]); + return 0; } @@ -10144,6 +10190,9 @@ static int load_module(void) for (x=0;x<IAX_MAX_CALLS;x++) ast_mutex_init(&iaxsl[x]); + for (x = 0; x < PVT_HASH_SIZE; x++) + AST_RWLIST_HEAD_INIT(&pvt_hash_tbl[x]); + ast_cond_init(&sched_cond, NULL); if (!(sched = sched_context_create())) { |