mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
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: https://origsvn.digium.com/svn/asterisk/trunk@56447 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -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())) {
|
||||
|
Reference in New Issue
Block a user