summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi-base.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-01-26 07:19:47 +0000
committerShaun Ruffell <sruffell@digium.com>2009-01-26 07:19:47 +0000
commitc59e4fac75aac14fae9b8e3e69e1f50a02a064de (patch)
tree34693ba0c28a5adc33831de35202eda235b0085f /drivers/dahdi/dahdi-base.c
parent3403ce644e15dbfef32e5f267ef9fb09519fe104 (diff)
Ensure the channel is in a good state before placing it on the chans arrays.
Also ensure that dahdi_receive holds the chan_lock while iterating over the chans array to prevent channels from entering or leaving the array while the interrupt handler is running. Related to issue #14183 . git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@5811 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi-base.c')
-rw-r--r--drivers/dahdi/dahdi-base.c56
1 files changed, 33 insertions, 23 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index fefe98a..276558a 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -1161,6 +1161,14 @@ static void release_echocan(const struct dahdi_echocan *ec)
#endif
}
+/**
+ * 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.chano member which is not set yet.
+ */
static void close_channel(struct dahdi_chan *chan)
{
unsigned long flags;
@@ -1478,43 +1486,43 @@ static void dahdi_set_law(struct dahdi_chan *chan, int law)
static int dahdi_chan_reg(struct dahdi_chan *chan)
{
int x;
- int res = 0;
unsigned long flags;
might_sleep();
+ spin_lock_init(&chan->lock);
+ if (!chan->master)
+ chan->master = chan;
+ if (!chan->readchunk)
+ chan->readchunk = chan->sreadchunk;
+ if (!chan->writechunk)
+ chan->writechunk = chan->swritechunk;
+ dahdi_set_law(chan, 0);
+ close_channel(chan);
+
write_lock_irqsave(&chan_lock, flags);
for (x = 1; x < DAHDI_MAX_CHANNELS; x++) {
if (chans[x])
continue;
- spin_lock_init(&chan->lock);
chans[x] = chan;
if (maxchans < x + 1)
maxchans = x + 1;
chan->channo = x;
- if (!chan->master)
- chan->master = chan;
- if (!chan->readchunk)
- chan->readchunk = chan->sreadchunk;
- if (!chan->writechunk)
- chan->writechunk = chan->swritechunk;
write_unlock_irqrestore(&chan_lock, flags);
- dahdi_set_law(chan, 0);
- close_channel(chan);
/* set this AFTER running close_channel() so that
HDLC channels wont cause hangage */
chan->flags |= DAHDI_FLAG_REGISTERED;
- res = 0;
break;
}
if (DAHDI_MAX_CHANNELS == x) {
write_unlock_irqrestore(&chan_lock, flags);
module_printk(KERN_ERR, "No more channels available\n");
+ return -ENOMEM;
}
- return res;
+ return 0;
}
char *dahdi_lboname(int x)
@@ -7659,7 +7667,7 @@ int dahdi_transmit(struct dahdi_span *span)
int dahdi_receive(struct dahdi_span *span)
{
int x,y,z;
- unsigned long flags, flagso;
+ unsigned long flags;
#if 1
#ifdef CONFIG_DAHDI_WATCHDOG
@@ -7744,7 +7752,8 @@ int dahdi_receive(struct dahdi_span *span)
if (span == master) {
/* Hold the big zap lock for the duration of major
activities which touch all sorts of channels */
- spin_lock_irqsave(&bigzaplock, flagso);
+ spin_lock_irqsave(&bigzaplock, flags);
+ read_lock(&chan_lock);
/* Process any timers */
process_timers();
/* If we have dynamic stuff, call the ioctl with 0,0 parameters to
@@ -7754,12 +7763,12 @@ int dahdi_receive(struct dahdi_span *span)
for (x=1;x<maxchans;x++) {
if (chans[x] && chans[x]->confmode && !(chans[x]->flags & DAHDI_FLAG_PSEUDO)) {
u_char *data;
- spin_lock_irqsave(&chans[x]->lock, flags);
+ spin_lock(&chans[x]->lock);
data = __buf_peek(&chans[x]->confin);
__dahdi_receive_chunk(chans[x], data);
if (data)
__buf_pull(&chans[x]->confin, NULL,chans[x], "confreceive");
- spin_unlock_irqrestore(&chans[x]->lock, flags);
+ spin_unlock(&chans[x]->lock);
}
}
/* This is the master channel, so make things switch over */
@@ -7767,9 +7776,9 @@ int dahdi_receive(struct dahdi_span *span)
/* do all the pseudo and/or conferenced channel receives (getbuf's) */
for (x=1;x<maxchans;x++) {
if (chans[x] && (chans[x]->flags & DAHDI_FLAG_PSEUDO)) {
- spin_lock_irqsave(&chans[x]->lock, flags);
+ spin_lock(&chans[x]->lock);
__dahdi_transmit_chunk(chans[x], NULL);
- spin_unlock_irqrestore(&chans[x]->lock, flags);
+ spin_unlock(&chans[x]->lock);
}
}
if (maxlinks) {
@@ -7792,21 +7801,21 @@ int dahdi_receive(struct dahdi_span *span)
for (x=1;x<maxchans;x++) {
if (chans[x] && (chans[x]->flags & DAHDI_FLAG_PSEUDO)) {
unsigned char tmp[DAHDI_CHUNKSIZE];
- spin_lock_irqsave(&chans[x]->lock, flags);
+ spin_lock(&chans[x]->lock);
__dahdi_getempty(chans[x], tmp);
__dahdi_receive_chunk(chans[x], tmp);
- spin_unlock_irqrestore(&chans[x]->lock, flags);
+ spin_unlock(&chans[x]->lock);
}
}
for (x=1;x<maxchans;x++) {
if (chans[x] && chans[x]->confmode && !(chans[x]->flags & DAHDI_FLAG_PSEUDO)) {
u_char *data;
- spin_lock_irqsave(&chans[x]->lock, flags);
+ spin_lock(&chans[x]->lock);
data = __buf_pushpeek(&chans[x]->confout);
__dahdi_transmit_chunk(chans[x], data);
if (data)
__buf_push(&chans[x]->confout, NULL, "conftransmit");
- spin_unlock_irqrestore(&chans[x]->lock, flags);
+ spin_unlock(&chans[x]->lock);
}
}
#ifdef DAHDI_SYNC_TICK
@@ -7817,7 +7826,8 @@ int dahdi_receive(struct dahdi_span *span)
s->sync_tick(s, s == master);
}
#endif
- spin_unlock_irqrestore(&bigzaplock, flagso);
+ read_unlock(&chan_lock);
+ spin_unlock_irqrestore(&bigzaplock, flags);
}
#endif
return 0;