diff options
author | Shaun Ruffell <sruffell@digium.com> | 2009-01-26 07:19:47 +0000 |
---|---|---|
committer | Shaun Ruffell <sruffell@digium.com> | 2009-01-26 07:19:47 +0000 |
commit | c59e4fac75aac14fae9b8e3e69e1f50a02a064de (patch) | |
tree | 34693ba0c28a5adc33831de35202eda235b0085f /drivers/dahdi/dahdi-base.c | |
parent | 3403ce644e15dbfef32e5f267ef9fb09519fe104 (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.c | 56 |
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; |