diff options
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r-- | drivers/dahdi/dahdi-base.c | 167 |
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(®istration_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(®istration_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(®istration_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(®istration_mutex); + if (pseudo->chan.channo == channo) return &pseudo->chan; - } } - mutex_unlock(®istration_mutex); return NULL; } +static struct dahdi_chan *chan_from_num(unsigned int channo) +{ + struct dahdi_chan *chan; + mutex_lock(®istration_mutex); + chan = _chan_from_num(channo); + mutex_unlock(®istration_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(®istration_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(®istration_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; } /** |