[svn-commits] sruffell: linux/trunk r8379 - /linux/trunk/drivers/dahdi/voicebus/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Mar 22 04:21:02 CDT 2010


Author: sruffell
Date: Mon Mar 22 04:20:59 2010
New Revision: 8379

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=8379
Log:
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.

Modified:
    linux/trunk/drivers/dahdi/voicebus/voicebus.c
    linux/trunk/drivers/dahdi/voicebus/voicebus.h

Modified: linux/trunk/drivers/dahdi/voicebus/voicebus.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/voicebus/voicebus.c?view=diff&rev=8379&r1=8378&r2=8379
==============================================================================
--- linux/trunk/drivers/dahdi/voicebus/voicebus.c (original)
+++ linux/trunk/drivers/dahdi/voicebus/voicebus.c Mon Mar 22 04:20:59 2010
@@ -308,7 +308,7 @@
 	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 @@
 	unsigned int i;
 	struct voicebus_descriptor_list *dl = &vb->txd;
 	struct voicebus_descriptor *d;
-
-	spin_lock_bh(&vb->lock);
+	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);
+	}
+
 	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 @@
 	}
 
 	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 @@
 	unsigned int i;
 	struct voicebus_descriptor_list *dl = &vb->rxd;
 	struct voicebus_descriptor *d;
-
-	spin_lock_bh(&vb->lock);
+	struct vbb *vbb;
+
+	tasklet_disable(&vb->tasklet);
 	for (i = 0; i < DRING_SIZE; ++i) {
 		d = vb_descriptor(dl, i);
 		if (d->buffer1) {
@@ -375,7 +384,8 @@
 					 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 @@
 	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_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->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 @@
 	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 @@
 	int i;
 	struct vbb *vbb;
 	LIST_HEAD(buffers);
-	unsigned long flags;
 
 	might_sleep();
 
@@ -633,18 +648,24 @@
 	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 @@
 				list_add_tail(&vbb->entry, &buffers);
 		}
 
+		handle_transmit(vb, &buffers);
+
 		tasklet_disable(&vb->tasklet);
-		handle_transmit(vb, &buffers);
-		tasklet_enable(&vb->tasklet);
-
-		spin_lock_irqsave(&vb->lock, flags);
 		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 @@
 	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 @@
  *
  * \return zero on success, -1 on error.
  */
-int
-voicebus_stop(struct voicebus *vb)
-{
-	if (vb_is_stopped(vb))
-		return 0;
+void voicebus_stop(struct voicebus *vb)
+{
+	static DECLARE_MUTEX(stop);
+
+	down(&stop);
+
+	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);
 
-	if (!vb_is_stopped(vb)) {
-		vb_reset_interface(vb);
-		__vb_set_control_defaults(vb);
-		__vb_set_mac_only_mode(vb);
-		return 0;
-	}
-
-
+	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,12 +999,11 @@
 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)
@@ -996,6 +1011,8 @@
 #else
 	cancel_work_sync(&vb->underrun_work);
 #endif
+	/* quiesce the hardware */
+	voicebus_stop(vb);
 
 	vb_reset_interface(vb);
 
@@ -1105,9 +1122,11 @@
 	 * 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 @@
 	/* 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 @@
 		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 @@
 	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;
-
 tx_error_exit:
 	vb_disable_interrupts(vb);
 	schedule_work(&vb->underrun_work);
@@ -1297,19 +1330,24 @@
 	    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);
-
-	setup_descriptors(vb);
-	start_packet_processing(vb);
+	if (!test_bit(VOICEBUS_SHUTDOWN, &vb->flags)) {
+
+		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 @@
 	    (TX_UNAVAILABLE_INTERRUPT|RX_UNAVAILABLE_INTERRUPT)) &&
 	    !test_bit(VOICEBUS_STOP, &vb->flags) &&
 	    test_bit(VOICEBUS_NORMAL_MODE, &vb->flags))) {
+		__vb_disable_interrupts(vb);
+		__vb_setctl(vb, SR_CSR5, int_status);
 		schedule_work(&vb->underrun_work);
-		__vb_setctl(vb, SR_CSR5, int_status);
 	} else if (likely(int_status &
 		   (TX_COMPLETE_INTERRUPT|RX_COMPLETE_INTERRUPT))) {
 		/* ******************************************************** */
@@ -1430,6 +1469,7 @@
 	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 @@
 					 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 |

Modified: linux/trunk/drivers/dahdi/voicebus/voicebus.h
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/voicebus/voicebus.h?view=diff&rev=8379&r1=8378&r2=8379
==============================================================================
--- linux/trunk/drivers/dahdi/voicebus/voicebus.h (original)
+++ linux/trunk/drivers/dahdi/voicebus/voicebus.h Mon Mar 22 04:20:59 2010
@@ -85,6 +85,7 @@
 };
 
 /* 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 @@
 	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 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);




More information about the svn-commits mailing list