From a236af613705dd786f1ddaf66e70a544bd4fa3e3 Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Tue, 5 Jun 2007 14:45:48 +0000 Subject: Merged revisions 67270 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r67270 | kpfleming | 2007-06-05 09:35:52 -0500 (Tue, 05 Jun 2007) | 3 lines ensure that a burst of full frames (AST_FRAME_DTMF being the prime example) will not be processed out of order... this is a brute force fix, but seems to be the safest fix for now (thanks to the Digium PQ department for finding this bug) ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@67271 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_iax2.c | 90 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index e32983b43..ed87de531 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -726,6 +726,12 @@ struct iax2_thread { time_t checktime; ast_mutex_t lock; ast_cond_t cond; + unsigned short ffcallno; /* if this thread is processing a full frame, the + callno for that frame will be here, so we can + avoid dispatching any more full frames for that + callno to other threads */ + struct sockaddr_in ffsin; /* remember the peer IP/port number for a full frame + in process */ }; /* Thread lists */ @@ -966,6 +972,11 @@ static struct iax2_thread *find_idle_thread(void) thread = NULL; } + /* this thread is not processing a full frame (since it is idle), + so ensure that the field for the full frame call number is empty */ + thread->ffcallno = 0; + memset(&thread->ffsin, 0, sizeof(thread->ffsin)); + return thread; } @@ -6471,37 +6482,68 @@ static int socket_read(int *id, int fd, short events, void *cbdata) struct iax2_thread *thread; socklen_t len; time_t t; - static time_t last_errtime=0; + static time_t last_errtime = 0; + struct ast_iax2_full_hdr *fh; - thread = find_idle_thread(); - if (thread) { - len = sizeof(thread->iosin); - thread->iofd = fd; - thread->iores = recvfrom(fd, thread->buf, sizeof(thread->buf), 0,(struct sockaddr *) &thread->iosin, &len); - if (thread->iores < 0) { - if (errno != ECONNREFUSED && errno != EAGAIN) - ast_log(LOG_WARNING, "Error: %s\n", strerror(errno)); - handle_error(); - insert_idle_thread(thread); - return 1; - } - if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */ - insert_idle_thread(thread); - return 1; - } - /* Mark as ready and send on its way */ - thread->iostate = IAX_IOSTATE_READY; -#ifdef DEBUG_SCHED_MULTITHREAD - ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc)); -#endif - signal_condition(&thread->lock, &thread->cond); - } else { + if (!(thread = find_idle_thread())) { time(&t); if (t != last_errtime) ast_log(LOG_NOTICE, "Out of idle IAX2 threads for I/O, pausing!\n"); last_errtime = t; usleep(1); + return 1; + } + + len = sizeof(thread->iosin); + thread->iofd = fd; + thread->iores = recvfrom(fd, thread->buf, sizeof(thread->buf), 0, (struct sockaddr *) &thread->iosin, &len); + if (thread->iores < 0) { + if (errno != ECONNREFUSED && errno != EAGAIN) + ast_log(LOG_WARNING, "Error: %s\n", strerror(errno)); + handle_error(); + insert_idle_thread(thread); + return 1; } + if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */ + insert_idle_thread(thread); + return 1; + } + + /* Determine if this frame is a full frame; if so, and any thread is currently + processing a full frame for the same callno from this peer, then drop this + frame (and the peer will retransmit it) */ + fh = (struct ast_iax2_full_hdr *) thread->buf; + if (ntohs(fh->scallno) & IAX_FLAG_FULL) { + struct iax2_thread *cur = NULL; + + AST_LIST_LOCK(&active_list); + AST_LIST_TRAVERSE(&active_list, cur, list) { + if ((cur->ffcallno == ntohs(fh->scallno)) && + !memcmp(&cur->ffsin, &thread->iosin, sizeof(cur->ffsin))) + break; + } + AST_LIST_UNLOCK(&active_list); + if (cur) { + /* we found another thread processing a full frame for this call, + so we can't accept this frame */ + ast_log(LOG_WARNING, "Dropping full frame from %s (callno %d) received too rapidly\n", + ast_inet_ntoa(thread->iosin.sin_addr), cur->ffcallno); + insert_idle_thread(thread); + return 1; + } else { + /* this thread is going to process this frame, so mark it */ + thread->ffcallno = ntohs(fh->scallno); + memcpy(&thread->ffsin, &thread->iosin, sizeof(thread->ffsin)); + } + } + + /* Mark as ready and send on its way */ + thread->iostate = IAX_IOSTATE_READY; +#ifdef DEBUG_SCHED_MULTITHREAD + ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc)); +#endif + signal_condition(&thread->lock, &thread->cond); + return 1; } -- cgit v1.2.3