summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2010-03-22 09:20:59 +0000
committerShaun Ruffell <sruffell@digium.com>2010-03-22 09:20:59 +0000
commit50eaaa0d038aa6cc5e6d30c0547cafad13a390e7 (patch)
treed84e772902fa48fe628ef5d55542dbc2ec0bf268
parentd010a28168a6883ef74e1f7c55c6576cf19bba4f (diff)
wctdm24xxp, wcte12xp: Fix several problems with buffer processing.
* If the receive packet isn't the correct size, we can't just drop it, we have to resubmit it to the receive queue immediately so it will get cleaned up properly. * It isn't safe to leave the buffers on any lists while calling the function to resubmit it to the descriptor ring, since it may be freed which potentially results in a corrupted list. * If the card is held up in a state where it is waiting for receive data, there is a chance that when the transmit process is started up again it could DMA data into a buffer that has already been freed. By using our own freelist, there isn't a chance that the board will DMA data into memory that has been recycled for another purpose. * Disable the tasklet when processing hard underruns in order to not corrupt the descriptor rings. * Make sure voicebus_stop and voicebus_release aren't running at the same time as the hard underrun handler. This too can result in corrupted descriptor lists. DAHDI-560. git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8379 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/voicebus/voicebus.c153
-rw-r--r--drivers/dahdi/voicebus/voicebus.h4
2 files changed, 100 insertions, 57 deletions
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c
index 68dd9f5..11e1b23 100644
--- a/drivers/dahdi/voicebus/voicebus.c
+++ b/drivers/dahdi/voicebus/voicebus.c
@@ -308,7 +308,7 @@ __vb_is_stopped(struct voicebus *vb)
u32 reg;
reg = __vb_getctl(vb, SR_CSR5);
reg = (reg >> 17) & 0x3f;
- return (0 == reg) ? 1 : 0;
+ return ((0 == reg) || (3 == reg)) ? 1 : 0;
}
/*!
* \brief Returns whether or not the interface is running.
@@ -334,8 +334,16 @@ vb_cleanup_tx_descriptors(struct voicebus *vb)
unsigned int i;
struct voicebus_descriptor_list *dl = &vb->txd;
struct voicebus_descriptor *d;
+ struct vbb *vbb;
+
+ tasklet_disable(&vb->tasklet);
+
+ while (!list_empty(&vb->tx_complete)) {
+ vbb = list_entry(vb->tx_complete.next, struct vbb, entry);
+ list_del(&vbb->entry);
+ kmem_cache_free(voicebus_vbb_cache, vbb);
+ }
- spin_lock_bh(&vb->lock);
for (i = 0; i < DRING_SIZE; ++i) {
d = vb_descriptor(dl, i);
if (d->buffer1 && (d->buffer1 != vb->idle_vbb_dma_addr)) {
@@ -357,8 +365,8 @@ vb_cleanup_tx_descriptors(struct voicebus *vb)
}
dl->head = dl->tail = 0;
- spin_unlock_bh(&vb->lock);
atomic_set(&dl->count, 0);
+ tasklet_enable(&vb->tasklet);
}
static void vb_cleanup_rx_descriptors(struct voicebus *vb)
@@ -366,8 +374,9 @@ static void vb_cleanup_rx_descriptors(struct voicebus *vb)
unsigned int i;
struct voicebus_descriptor_list *dl = &vb->rxd;
struct voicebus_descriptor *d;
+ struct vbb *vbb;
- spin_lock_bh(&vb->lock);
+ tasklet_disable(&vb->tasklet);
for (i = 0; i < DRING_SIZE; ++i) {
d = vb_descriptor(dl, i);
if (d->buffer1) {
@@ -375,7 +384,8 @@ static void vb_cleanup_rx_descriptors(struct voicebus *vb)
VOICEBUS_SFRAME_SIZE, DMA_FROM_DEVICE);
d->buffer1 = 0;
BUG_ON(!dl->pending[i]);
- kmem_cache_free(voicebus_vbb_cache, dl->pending[i]);
+ vbb = dl->pending[i];
+ list_add_tail(&vbb->entry, &vb->free_rx);
dl->pending[i] = NULL;
}
d->des0 &= ~OWN_BIT;
@@ -383,7 +393,7 @@ static void vb_cleanup_rx_descriptors(struct voicebus *vb)
dl->head = 0;
dl->tail = 0;
atomic_set(&dl->count, 0);
- spin_unlock_bh(&vb->lock);
+ tasklet_enable(&vb->tasklet);
}
static void vb_cleanup_descriptors(struct voicebus *vb,
@@ -398,6 +408,7 @@ static void vb_cleanup_descriptors(struct voicebus *vb,
static void
vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
{
+ struct vbb *vbb;
if (NULL == dl->desc) {
WARN_ON(1);
return;
@@ -407,6 +418,11 @@ vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
vb->pdev,
(sizeof(struct voicebus_descriptor)+dl->padding)*DRING_SIZE,
dl->desc, dl->desc_dma);
+ while (!list_empty(&vb->free_rx)) {
+ vbb = list_entry(vb->free_rx.next, struct vbb, entry);
+ list_del(&vbb->entry);
+ kmem_cache_free(voicebus_vbb_cache, vbb);
+ }
}
/*!
@@ -560,7 +576,7 @@ vb_submit_rxb(struct voicebus *vb, struct vbb *vbb)
if (unlikely(d->buffer1)) {
/* Do not overwrite a buffer that is still in progress. */
WARN_ON(1);
- kmem_cache_free(voicebus_vbb_cache, vbb);
+ list_add_tail(&vbb->entry, &vb->free_rx);
return -EBUSY;
}
@@ -621,7 +637,6 @@ static void setup_descriptors(struct voicebus *vb)
int i;
struct vbb *vbb;
LIST_HEAD(buffers);
- unsigned long flags;
might_sleep();
@@ -633,18 +648,24 @@ static void setup_descriptors(struct voicebus *vb)
vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma);
for (i = 0; i < DRING_SIZE; ++i) {
- vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL);
+ if (list_empty(&vb->free_rx)) {
+ vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL);
+ } else {
+ vbb = list_entry(vb->free_rx.next, struct vbb, entry);
+ list_del(&vbb->entry);
+ }
if (unlikely(NULL == vbb))
BUG_ON(1);
list_add_tail(&vbb->entry, &buffers);
}
- spin_lock_irqsave(&vb->lock, flags);
- list_for_each_entry(vbb, &buffers, entry)
+ tasklet_disable(&vb->tasklet);
+ while (!list_empty(&buffers)) {
+ vbb = list_entry(buffers.next, struct vbb, entry);
+ list_del(&vbb->entry);
vb_submit_rxb(vb, vbb);
- spin_unlock_irqrestore(&vb->lock, flags);
-
- INIT_LIST_HEAD(&buffers);
+ }
+ tasklet_enable(&vb->tasklet);
if (test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) {
for (i = 0; i < vb->min_tx_buffer_count; ++i) {
@@ -655,14 +676,12 @@ static void setup_descriptors(struct voicebus *vb)
list_add_tail(&vbb->entry, &buffers);
}
- tasklet_disable(&vb->tasklet);
handle_transmit(vb, &buffers);
- tasklet_enable(&vb->tasklet);
- spin_lock_irqsave(&vb->lock, flags);
+ tasklet_disable(&vb->tasklet);
list_for_each_entry(vbb, &buffers, entry)
voicebus_transmit(vb, vbb);
- spin_unlock_irqrestore(&vb->lock, flags);
+ tasklet_enable(&vb->tasklet);
}
}
@@ -927,9 +946,9 @@ static void vb_stop_txrx_processors(struct voicebus *vb)
spin_unlock_irqrestore(&vb->lock, flags);
barrier();
- i = 500;
+ i = 150;
while (--i && (__vb_getctl(vb, SR_CSR5) & (0x007e0000)))
- udelay(10);
+ udelay(100);
}
/*!
@@ -944,30 +963,27 @@ static void vb_stop_txrx_processors(struct voicebus *vb)
*
* \return zero on success, -1 on error.
*/
-int
-voicebus_stop(struct voicebus *vb)
+void voicebus_stop(struct voicebus *vb)
{
- if (vb_is_stopped(vb))
- return 0;
+ static DECLARE_MUTEX(stop);
- set_bit(VOICEBUS_STOP, &vb->flags);
- vb_stop_txrx_processors(vb);
+ down(&stop);
- if (!vb_is_stopped(vb)) {
- vb_reset_interface(vb);
- __vb_set_control_defaults(vb);
- __vb_set_mac_only_mode(vb);
- return 0;
+ if (test_bit(VOICEBUS_STOP, &vb->flags) || vb_is_stopped(vb)) {
+ up(&stop);
+ return;
}
+ set_bit(VOICEBUS_STOP, &vb->flags);
+ vb_stop_txrx_processors(vb);
+ WARN_ON(!vb_is_stopped(vb));
set_bit(VOICEBUS_STOPPED, &vb->flags);
#if defined(CONFIG_VOICEBUS_TIMER)
del_timer_sync(&vb->timer);
#endif
-
- return 0;
+ up(&stop);
}
EXPORT_SYMBOL(voicebus_stop);
@@ -983,19 +999,20 @@ EXPORT_SYMBOL(voicebus_stop);
void
voicebus_release(struct voicebus *vb)
{
+ set_bit(VOICEBUS_SHUTDOWN, &vb->flags);
+
#ifdef VOICEBUS_NET_DEBUG
vb_net_unregister(vb);
#endif
- /* quiesce the hardware */
- voicebus_stop(vb);
-
/* Make sure the underrun_work isn't running or going to run. */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
flush_scheduled_work();
#else
cancel_work_sync(&vb->underrun_work);
#endif
+ /* quiesce the hardware */
+ voicebus_stop(vb);
vb_reset_interface(vb);
@@ -1105,9 +1122,11 @@ static void vb_tasklet_relaxed(unsigned long data)
* of them. */
handle_transmit(vb, &buffers);
- list_for_each_entry(vbb, &buffers, entry)
+ while (!list_empty(&buffers)) {
+ vbb = list_entry(buffers.next, struct vbb, entry);
+ list_del(&vbb->entry);
voicebus_transmit(vb, vbb);
- INIT_LIST_HEAD(&buffers);
+ }
/* If there may still be buffers in the descriptor rings, reschedule
* ourself to run again. We essentially yield here to allow any other
@@ -1118,13 +1137,19 @@ static void vb_tasklet_relaxed(unsigned long data)
/* And finally, pass up any receive buffers. */
count = DEFAULT_COUNT;
while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) {
- if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE)
+ if (likely((des0 & (0x7fff << 16)) ==
+ (VOICEBUS_SFRAME_SIZE << 16)))
list_add_tail(&vbb->entry, &buffers);
+ else
+ vb_submit_rxb(vb, vbb);
}
handle_receive(vb, &buffers);
- list_for_each_entry(vbb, &buffers, entry)
+ while (!list_empty(&buffers)) {
+ vbb = list_entry(buffers.next, struct vbb, entry);
+ list_del(&vbb->entry);
vb_submit_rxb(vb, vbb);
+ }
return;
}
@@ -1222,9 +1247,11 @@ static void vb_tasklet(unsigned long data)
goto tx_error_exit;
/* Now we can send all our buffers together in a group. */
- list_for_each_entry(vbb, &buffers, entry)
+ while (!list_empty(&buffers)) {
+ vbb = list_entry(buffers.next, struct vbb, entry);
+ list_del(&vbb->entry);
voicebus_transmit(vb, vbb);
- INIT_LIST_HEAD(&buffers);
+ }
/* Print any messages about soft latency bumps after we fix the transmit
* descriptor ring. Otherwise it's possible to take so much time
@@ -1260,13 +1287,19 @@ static void vb_tasklet(unsigned long data)
while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) {
if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE)
list_add_tail(&vbb->entry, &buffers);
+ else
+ vb_submit_rxb(vb, vbb);
}
handle_receive(vb, &buffers);
- list_for_each_entry(vbb, &buffers, entry)
+
+ while (!list_empty(&buffers)) {
+ vbb = list_entry(buffers.next, struct vbb, entry);
+ list_del(&vbb->entry);
vb_submit_rxb(vb, vbb);
- return;
+ }
+ return;
tx_error_exit:
vb_disable_interrupts(vb);
schedule_work(&vb->underrun_work);
@@ -1297,19 +1330,24 @@ static void handle_hardunderrun(struct work_struct *work)
test_bit(VOICEBUS_STOPPED, &vb->flags))
return;
- if (printk_ratelimit()) {
- dev_info(&vb->pdev->dev, "Host failed to service "
- "card interrupt within %d ms which is a "
- "hardunderun.\n", DRING_SIZE);
- }
-
voicebus_stop(vb);
- if (vb->ops->handle_error)
- vb->ops->handle_error(vb);
+ if (!test_bit(VOICEBUS_SHUTDOWN, &vb->flags)) {
- setup_descriptors(vb);
- start_packet_processing(vb);
+ if (printk_ratelimit()) {
+ dev_info(&vb->pdev->dev, "Host failed to service "
+ "card interrupt within %d ms which is a "
+ "hardunderun.\n", DRING_SIZE);
+ }
+
+ if (vb->ops->handle_error)
+ vb->ops->handle_error(vb);
+
+ tasklet_disable(&vb->tasklet);
+ setup_descriptors(vb);
+ start_packet_processing(vb);
+ tasklet_enable(&vb->tasklet);
+ }
}
/*!
@@ -1343,8 +1381,9 @@ vb_isr(int irq, void *dev_id)
(TX_UNAVAILABLE_INTERRUPT|RX_UNAVAILABLE_INTERRUPT)) &&
!test_bit(VOICEBUS_STOP, &vb->flags) &&
test_bit(VOICEBUS_NORMAL_MODE, &vb->flags))) {
- schedule_work(&vb->underrun_work);
+ __vb_disable_interrupts(vb);
__vb_setctl(vb, SR_CSR5, int_status);
+ schedule_work(&vb->underrun_work);
} else if (likely(int_status &
(TX_COMPLETE_INTERRUPT|RX_COMPLETE_INTERRUPT))) {
/* ******************************************************** */
@@ -1430,6 +1469,7 @@ __voicebus_init(struct voicebus *vb, const char *board_name, int normal_mode)
vb->min_tx_buffer_count = VOICEBUS_DEFAULT_LATENCY;
INIT_LIST_HEAD(&vb->tx_complete);
+ INIT_LIST_HEAD(&vb->free_rx);
#if defined(CONFIG_VOICEBUS_TIMER)
init_timer(&vb->timer);
@@ -1652,7 +1692,8 @@ static int __init voicebus_module_init(void)
NULL);
#endif
#else
-#if (defined(DEBUG) && defined(CONFIG_SLAB_DEBUG))
+#if (defined(CONFIG_SLAB) && defined(CONFIG_SLAB_DEBUG)) || \
+ (defined(CONFIG_SLUB) && defined(CONFIG_SLUB_DEBUG))
voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name,
sizeof(struct vbb), 0,
SLAB_HWCACHE_ALIGN | SLAB_STORE_USER |
diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h
index de565d9..5dea5b6 100644
--- a/drivers/dahdi/voicebus/voicebus.h
+++ b/drivers/dahdi/voicebus/voicebus.h
@@ -85,6 +85,7 @@ struct voicebus_descriptor_list {
};
/* Bit definitions for struct voicebus.flags */
+#define VOICEBUS_SHUTDOWN 0
#define VOICEBUS_STOP 1
#define VOICEBUS_STOPPED 2
#define VOICEBUS_LATENCY_LOCKED 3
@@ -118,6 +119,7 @@ struct voicebus {
unsigned int min_tx_buffer_count;
unsigned int max_latency;
struct list_head tx_complete;
+ struct list_head free_rx;
#ifdef VOICEBUS_NET_DEBUG
struct sk_buff_head captured_packets;
@@ -141,7 +143,7 @@ int __voicebus_init(struct voicebus *vb, const char *board_name,
int normal_mode);
void voicebus_release(struct voicebus *vb);
int voicebus_start(struct voicebus *vb);
-int voicebus_stop(struct voicebus *vb);
+void voicebus_stop(struct voicebus *vb);
int voicebus_transmit(struct voicebus *vb, struct vbb *vbb);
int voicebus_set_minlatency(struct voicebus *vb, unsigned int milliseconds);
int voicebus_current_latency(struct voicebus *vb);