summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi-base.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2011-01-03 18:28:15 +0000
committerShaun Ruffell <sruffell@digium.com>2011-01-03 18:28:15 +0000
commit785624d473684d4b88de16d2f5a898c0dc826051 (patch)
tree8ae395a82589a7e4bd6a53cec7556f9b3aea50ff /drivers/dahdi/dahdi-base.c
parent9128a66f9a6ee5169be0ef9fe0f96531ee25bb2c (diff)
dahdi: Remove 'chans' array from reg/unreg. Now completely removed.
Remove the remaining locations the 'chans' array was referenced but keep the observable behavior the same. Namely, channels are numbered in registration order. Only now it's possible to renumber channels easily since their number is not also their implied location in the 'chans' array. Signed-off-by: Shaun Ruffell <sruffell@digium.com> Acked-by: Kinsey Moore <kmoore@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9609 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r--drivers/dahdi/dahdi-base.c167
1 files changed, 90 insertions, 77 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index a03f4a9..60e2d72 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -442,6 +442,9 @@ static DEFINE_SPINLOCK(zone_lock);
/* The first zone on the list is the default zone. */
static LIST_HEAD(tone_zones);
+/* Protects the span_list and pseudo_chans lists from concurrent access in
+ * process context. The spin_lock is needed to synchronize with the interrupt
+ * handler. */
static DEFINE_SPINLOCK(chan_lock);
struct pseudo_chan {
@@ -454,11 +457,11 @@ static inline struct pseudo_chan *chan_to_pseudo(struct dahdi_chan *chan)
return container_of(chan, struct pseudo_chan, chan);
}
-/* This is protected by the chan_lock. */
+/* This list is protected by the chan_lock. */
static LIST_HEAD(pseudo_chans);
/**
- * is_pseudo_chan() - By definition psuedo channels are not on a span.
+ * is_pseudo_chan() - By definition pseudo channels are not on a span.
*/
static inline bool is_pseudo_chan(const struct dahdi_chan *chan)
{
@@ -467,15 +470,12 @@ static inline bool is_pseudo_chan(const struct dahdi_chan *chan)
static bool valid_channo(const int channo)
{
- return ((channo >= DAHDI_MAX_CHANNELS) || (channo < 1)) ?
- false : true;
+ return (channo < 1) ? false : true;
}
static DEFINE_MUTEX(registration_mutex);
static LIST_HEAD(span_list);
-static struct dahdi_chan *chans[DAHDI_MAX_CHANNELS];
-
static unsigned long
__for_each_channel(unsigned long (*func)(struct dahdi_chan *chan,
unsigned long data),
@@ -503,7 +503,13 @@ __for_each_channel(unsigned long (*func)(struct dahdi_chan *chan,
return 0;
}
-static struct dahdi_chan *chan_from_num(unsigned int channo)
+/**
+ * _chan_from_num - Lookup a channel
+ *
+ * Must be called with the registration_mutex held.
+ *
+ */
+static struct dahdi_chan *_chan_from_num(unsigned int channo)
{
struct dahdi_span *s;
struct pseudo_chan *pseudo;
@@ -511,8 +517,6 @@ static struct dahdi_chan *chan_from_num(unsigned int channo)
if (!unlikely(valid_channo(channo)))
return NULL;
- mutex_lock(&registration_mutex);
-
/* When searching for the channel amongst the spans, we can use the
* fact that channels on a span must be numbered consecutively to skip
* checking each individual channel. */
@@ -527,32 +531,36 @@ static struct dahdi_chan *chan_from_num(unsigned int channo)
if (channo >= (basechan + s->channels))
continue;
- if (unlikely(channo < basechan)) {
- /* Looks like they are asking for a channel that has
- * already been removed. */
- mutex_unlock(&registration_mutex);
+ /* Since all the spans should be on the list in sorted order,
+ * if channo is less than base chan, the caller must be
+ * looking for a channel that has already been removed. */
+ if (unlikely(channo < basechan))
return NULL;
- }
chan = s->chans[channo - basechan];
WARN_ON(chan->channo != channo);
- mutex_unlock(&registration_mutex);
return chan;
}
/* If we didn't find the channel on the list of real channels, then
* it's most likely a pseudo channel. */
list_for_each_entry(pseudo, &pseudo_chans, node) {
- if (pseudo->chan.channo == channo) {
- mutex_unlock(&registration_mutex);
+ if (pseudo->chan.channo == channo)
return &pseudo->chan;
- }
}
- mutex_unlock(&registration_mutex);
return NULL;
}
+static struct dahdi_chan *chan_from_num(unsigned int channo)
+{
+ struct dahdi_chan *chan;
+ mutex_lock(&registration_mutex);
+ chan = _chan_from_num(channo);
+ mutex_unlock(&registration_mutex);
+ return chan;
+}
+
static inline struct dahdi_chan *chan_from_file(struct file *file)
{
return (file->private_data) ?
@@ -617,8 +625,7 @@ static inline bool can_provide_timing(const struct dahdi_span *const s)
return !s->cannot_provide_timing;
}
-static int maxchans = 0;
-static int maxconfs = 0;
+static int maxconfs;
short __dahdi_mulaw[256];
short __dahdi_alaw[256];
@@ -1431,9 +1438,10 @@ static inline bool is_gain_allocated(const struct dahdi_chan *chan)
* close_channel - close the channel, resetting any channel variables
* @chan: the dahdi_chan to close
*
- * This function might be called before the channel is placed on the global
- * array of channels, (chans), and therefore, neither this function nor it's
- * children should depend on the dahdi_chan.channo member which is not set yet.
+ * This function is called before either the parent span is linked into the
+ * span list, or for pseudos, place on the psuedo_list. Therefore, this
+ * function nor it's callers should depend on the channel being findable
+ * via those methods.
*/
static void close_channel(struct dahdi_chan *chan)
{
@@ -1739,11 +1747,8 @@ static void dahdi_set_law(struct dahdi_chan *chan, int law)
}
}
-static int dahdi_chan_reg(struct dahdi_chan *chan)
+static void dahdi_chan_reg(struct dahdi_chan *chan)
{
- int x;
- unsigned long flags;
-
might_sleep();
spin_lock_init(&chan->lock);
@@ -1760,27 +1765,9 @@ static int dahdi_chan_reg(struct dahdi_chan *chan)
dahdi_set_law(chan, DAHDI_LAW_DEFAULT);
close_channel(chan);
- spin_lock_irqsave(&chan_lock, flags);
- for (x = 1; x < DAHDI_MAX_CHANNELS; x++) {
- if (chans[x])
- continue;
-
- chans[x] = chan;
- if (maxchans < x + 1)
- maxchans = x + 1;
- chan->channo = x;
- /* set this AFTER running close_channel() so that
- HDLC channels wont cause hangage */
- set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
- break;
- }
- spin_unlock_irqrestore(&chan_lock, flags);
- if (DAHDI_MAX_CHANNELS == x) {
- module_printk(KERN_ERR, "No more channels available\n");
- return -ENOMEM;
- }
-
- return 0;
+ /* set this AFTER running close_channel() so that
+ HDLC channels wont cause hangage */
+ set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
}
/**
@@ -2133,9 +2120,6 @@ static unsigned long _chan_cleanup(struct dahdi_chan *pos, unsigned long data)
{
unsigned long flags;
struct dahdi_chan *const chan = (struct dahdi_chan *)data;
-
- ++maxchans;
-
/* Remove anyone pointing to us as master
and make them their own thing */
if (pos->master == chan)
@@ -2187,16 +2171,14 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan)
chan->hdlcnetdev = NULL;
}
#endif
- spin_lock_irqsave(&chan_lock, flags);
- if (test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags)) {
- chans[chan->channo] = NULL;
- clear_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
- }
+ clear_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
+
#ifdef CONFIG_DAHDI_PPP
if (chan->ppp) {
module_printk(KERN_NOTICE, "HUH??? PPP still attached??\n");
}
#endif
+ spin_lock_irqsave(&chan_lock, flags);
__for_each_channel(_chan_cleanup, (unsigned long)chan);
spin_unlock_irqrestore(&chan_lock, flags);
@@ -3025,34 +3007,66 @@ static struct dahdi_chan *dahdi_alloc_pseudo(void)
{
struct pseudo_chan *pseudo;
unsigned long flags;
+ unsigned int channo;
+ struct list_head *pos = &pseudo_chans;
+ static unsigned int first_pseudo_channo;
/* Don't allow /dev/dahdi/pseudo to open if there is not a timing
* source. */
if (!can_open_timer())
return NULL;
- if (!(pseudo = kzalloc(sizeof(*pseudo), GFP_KERNEL)))
+ pseudo = kzalloc(sizeof(*pseudo), GFP_KERNEL);
+ if (NULL == pseudo)
return NULL;
- INIT_LIST_HEAD(&pseudo->node);
-
pseudo->chan.sig = DAHDI_SIG_CLEAR;
pseudo->chan.sigcap = DAHDI_SIG_CLEAR;
pseudo->chan.flags = DAHDI_FLAG_AUDIO;
pseudo->chan.span = NULL; /* No span == psuedo channel */
- if (dahdi_chan_reg(&pseudo->chan)) {
- kfree(pseudo);
- return NULL;
+ mutex_lock(&registration_mutex);
+
+ if (list_empty(&pseudo_chans)) {
+ /* If there aren't any pseudo channels allocated, we need to
+ * take the next channel number after all the 'real' channels
+ * on the spans. */
+ struct dahdi_span *span;
+ channo = 1;
+ list_for_each_entry_reverse(span, &span_list, node) {
+ if (unlikely(!span->channels))
+ continue;
+ channo = span->chans[0]->channo + span->channels;
+ break;
+ }
+ first_pseudo_channo = channo;
+ } else {
+ struct pseudo_chan *p;
+ /* Otherwise, we'll find the first available channo in the list
+ * of pseudo chans. */
+ channo = first_pseudo_channo;
+ list_for_each_entry(p, &pseudo_chans, node) {
+ if (channo != p->chan.channo)
+ break;
+ pos = &p->node;
+ ++channo;
+ }
}
- spin_lock_irqsave(&chan_lock, flags);
- list_add_tail(&pseudo->node, &pseudo_chans);
- spin_unlock_irqrestore(&chan_lock, flags);
+ pseudo->chan.channo = channo;
+ dahdi_chan_reg(&pseudo->chan);
snprintf(pseudo->chan.name, sizeof(pseudo->chan.name)-1,
"Pseudo/%d", pseudo->chan.channo);
+ /* Once we place the pseudo chan on the list...it's registered and
+ * live. */
+ spin_lock_irqsave(&chan_lock, flags);
+ list_add(&pseudo->node, pos);
+ spin_unlock_irqrestore(&chan_lock, flags);
+
+ mutex_unlock(&registration_mutex);
+
return &pseudo->chan;
}
@@ -6357,9 +6371,9 @@ static int _dahdi_register(struct dahdi_span *span, int prefmaster)
{
unsigned int spanno;
unsigned int x;
- int res = 0;
struct list_head *loc = &span_list;
unsigned long flags;
+ unsigned int next_channo = 1;
if (!span || !span->ops || !span->ops->owner)
return -EINVAL;
@@ -6376,13 +6390,19 @@ static int _dahdi_register(struct dahdi_span *span, int prefmaster)
spanno = 1;
/* Look through the span list to find the first available span number.
- * The spans are kept on this list in sorted order. */
+ * The spans are kept on this list in sorted order. We'll also save
+ * off the next available channel number to use. */
+
if (!list_empty(&span_list)) {
struct dahdi_span *pos;
list_for_each_entry(pos, &span_list, node) {
loc = &pos->node;
if (pos->spanno == spanno) {
++spanno;
+ if (pos->channels) {
+ next_channo = pos->chans[0]->channo +
+ pos->channels;
+ }
continue;
}
break;
@@ -6392,12 +6412,8 @@ static int _dahdi_register(struct dahdi_span *span, int prefmaster)
span->spanno = spanno;
for (x = 0; x < span->channels; x++) {
span->chans[x]->span = span;
- res = dahdi_chan_reg(span->chans[x]);
- if (res) {
- for (x--; x >= 0; x--)
- dahdi_chan_unreg(span->chans[x]);
- goto unreg_channels;
- }
+ span->chans[x]->channo = next_channo + x;
+ dahdi_chan_reg(span->chans[x]);
}
#ifdef CONFIG_PROC_FS
@@ -6439,9 +6455,6 @@ static int _dahdi_register(struct dahdi_span *span, int prefmaster)
set_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags);
return 0;
-
-unreg_channels:
- return res;
}
/**