From 3451d6a72d0499a83d82ce03d00d2156a1258c8b Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Mon, 23 Jun 2014 07:44:19 +0000 Subject: [PATCH] suspended destructions of pri spans on events If a DAHDI span disappears, we wish for its representation in Asterisk to be destroyed as well. The information about the span's removal may come from several paths: 1. DAHDI sends DAHDI_EVENT_REMOVE on every channel. 2. An extra DAHDI_EVENT_REMOVED is sent on every subsequent call to DAHDI_GET_EVENT. 3. Every read (including the internal one by libpri on the D-channel) returns -ENODEV. Asterisk responsds to DAHDI_EVENT_REMOVE on a channel by destroying it. Destroying a channel requires holding the channel list lock (iflock). Destroying a channel that is part of a span requires holding the span's lock. Destroying a channel from a context that holds the span lock, while at the same time another channel is destroyed directly, leads to a deadlock. Solution: don't destroy span while holding the channels list lock. Thus changes in this patch: * Deferring removal of PRI spans in response to events: doomed spans are collected on a list. * Doomed spans are removed periodically by the monitor thread. * ENODEV reads from the D-channel will warant the same deferred removal. Review: https://reviewboard.asterisk.org/r/3548/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@417059 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_dahdi.c | 78 +++++++++++++++++++++++++++++++++++++++++-- channels/sig_pri.c | 29 ++++++++++++++++ channels/sig_pri.h | 2 ++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index adc025ae06..0a0b330f26 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -780,6 +780,14 @@ static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */ static struct dahdi_pvt *ifend = NULL; /*!< Main interface list end */ #if defined(HAVE_PRI) +struct doomed_pri { + struct sig_pri_span *pri; + AST_LIST_ENTRY(doomed_pri) list; +}; +static AST_LIST_HEAD_STATIC(doomed_pris, doomed_pri); + +static void pri_destroy_span(struct sig_pri_span *pri); + static struct dahdi_parms_pseudo { int buf_no; /*!< Number of buffers */ int buf_policy; /*!< Buffer policy */ @@ -1112,6 +1120,69 @@ static int analogsub_to_dahdisub(enum analog_sub analogsub) return index; } +/*! + * \internal + * \brief release all members on the doomed pris list + * \since 13.0 + * + * Called priodically by the monitor threads to release spans marked for + * removal. + */ +static void release_doomed_pris(void) +{ +#ifdef HAVE_PRI + struct doomed_pri *entry; + + AST_LIST_LOCK(&doomed_pris); + while ((entry = AST_LIST_REMOVE_HEAD(&doomed_pris, list))) { + /* The span destruction must be done with this lock not held */ + AST_LIST_UNLOCK(&doomed_pris); + ast_debug(4, "Destroying span %d from doomed queue.\n", + entry->pri->span); + pri_destroy_span(entry->pri); + ast_free(entry); + AST_LIST_LOCK(&doomed_pris); + } + AST_LIST_UNLOCK(&doomed_pris); +#endif +} + +#ifdef HAVE_PRI +/*! + * \brief Queue a span for destruction + * \since 13.0 + * + * \param pri the span to destroy + * + * Add a span to the list of spans to be destroyed later on + * by the monitor thread. Allows destroying a span while holding its + * lock. + */ +static void pri_queue_for_destruction(struct sig_pri_span *pri) +{ + struct doomed_pri *entry; + + AST_LIST_LOCK(&doomed_pris); + AST_LIST_TRAVERSE(&doomed_pris, entry, list) { + if (entry->pri == pri) { + AST_LIST_UNLOCK(&doomed_pris); + return; + } + } + entry = ast_calloc(sizeof(struct doomed_pri), 1); + if (!entry) { + /* Nothing useful to do here. Panic? */ + ast_log(LOG_WARNING, "Failed allocating memory for a doomed_pri.\n"); + AST_LIST_UNLOCK(&doomed_pris); + return; + } + entry->pri = pri; + ast_debug(4, "Queue span %d for destruction.\n", pri->span); + AST_LIST_INSERT_TAIL(&doomed_pris, entry, list); + AST_LIST_UNLOCK(&doomed_pris); +} +#endif + /*! * \internal * \brief Send a dial string to DAHDI. @@ -2654,8 +2725,6 @@ static int sig_pri_tone_to_dahditone(enum sig_pri_tone tone) #endif /* defined(HAVE_PRI) */ #if defined(HAVE_PRI) -static void pri_destroy_span(struct sig_pri_span *pri); - static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) { int x; @@ -2684,7 +2753,7 @@ static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) pri_event_noalarm(pri, index, 0); break; case DAHDI_EVENT_REMOVED: - pri_destroy_span(pri); + pri_queue_for_destruction(pri); break; default: break; @@ -2967,6 +3036,7 @@ struct sig_pri_callback sig_pri_callbacks = .dial_digits = my_pri_dial_digits, .open_media = my_pri_ss7_open_media, .ami_channel_event = my_ami_channel_event, + .destroy_later = pri_queue_for_destruction, }; #endif /* defined(HAVE_PRI) */ @@ -11469,6 +11539,7 @@ static void *do_monitor(void *data) } } ast_mutex_unlock(&iflock); + release_doomed_pris(); } /* Never reached */ pthread_cleanup_pop(1); @@ -14217,6 +14288,7 @@ static void pri_destroy_span(struct sig_pri_span *pri) for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) { ast_debug(4, "closing pri_fd %d\n", i); dahdi_close_pri_fd(dahdi_pri, i); + dahdi_pri->dchannels[i] = 0; } sig_pri_init_pri(pri); ast_debug(1, "PRI span %d destroyed\n", pri->span); diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 57c518c886..4c1f170f11 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1450,6 +1450,27 @@ static int pri_find_principle_by_call(struct sig_pri_span *pri, q931_call *call) return -1; } +/*! + * \internal + * \brief Queue the span for destruction + * \since 13.0 + * + * \param pri PRI span control structure. + * + * Asks the channel driver to queue the span for destruction at a + * possibly later time, if (e.g.) locking considerations don't allow + * destroying it right now. + * + * \return Nothing + */ +static void pri_destroy_later(struct sig_pri_span *pri) +{ + if (!sig_pri_callbacks.destroy_later) { + return; + } + sig_pri_callbacks.destroy_later(pri); +} + /*! * \internal * \brief Kill the call. @@ -6427,6 +6448,14 @@ static void *pri_dchannel(void *vpri) } if (e) break; + + if ((errno != 0) && (errno != EINTR)) { + ast_log(LOG_NOTICE, "pri_check_event returned error %d (%s)\n", + errno, strerror(errno)); + } + if (errno == ENODEV) { + pri_destroy_later(pri); + } } } else if (errno != EINTR) ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno)); diff --git a/channels/sig_pri.h b/channels/sig_pri.h index ad6c4c5483..c8498faf1e 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -231,6 +231,8 @@ struct sig_pri_callback { void (*module_ref)(void); /*! Unreference the parent module. */ void (*module_unref)(void); + /*! Mark the span for destruction. */ + void (*destroy_later)(struct sig_pri_span *pri); }; /*! Global sig_pri callbacks to the upper layer. */