lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1227456274-10388-2-git-send-email-steve.glendinning@smsc.com>
Date:	Sun, 23 Nov 2008 16:04:34 +0000
From:	Steve Glendinning <steve.glendinning@...c.com>
To:	netdev@...r.kernel.org
Cc:	linux-sh@...r.kernel.org, Ian Saturley <ian.saturley@...c.com>,
	Steve Glendinning <steve.glendinning@...c.com>
Subject: [RFC PATCH 2/2] smsc911x: add support for sh3 TX DMA

Following on from the RX DMA patch, this patch also adds TX DMA support 
for the sh architecture.  Tested on SH7709S (sh3), where it works but 
actually *reduces* throughput (by about 30%).

The patch disables TX during the transfer, and uses the DMA completion 
interrupt to re-enable TX.  This causes 1 interrupt per packet, which is 
most likely responsible for the bulk of the performance loss.

This implementation is quite a nasty hack, as the sh DMA subsystem also 
attaches a DMA completion isr (so dma-sh.c must be modified to also
request the isr as shared).  The sh dma subsystem has a wait queue for
this purpose, but I don't think we can sleep in hard_start_xmit?

As with the RX patch, I'm interested to hear any advice on making this 
more generic.

Signed-off-by: Steve Glendinning <steve.glendinning@...c.com>
---
 drivers/net/smsc911x.c   |  139 +++++++++++++++++++++++++++++++++++++++++++++-
 drivers/net/smsc911x.h   |    1 +
 include/linux/smsc911x.h |    1 +
 3 files changed, 140 insertions(+), 1 deletions(-)

diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index b3d9f9e..90ab783 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -52,7 +52,7 @@
 #include <linux/smsc911x.h>
 #include "smsc911x.h"
 
-#ifdef SMSC_USE_SH_RX_DMA
+#if defined(SMSC_USE_SH_TX_DMA) || defined(SMSC_USE_SH_RX_DMA)
 #include <asm/dma.h>
 #include <../arch/sh/drivers/dma/dma-sh.h>
 #endif
@@ -125,6 +125,10 @@ struct smsc911x_data {
 #ifdef SMSC_USE_SH_RX_DMA
 	struct sk_buff *rx_skb;
 #endif
+
+#ifdef SMSC_USE_SH_TX_DMA
+	struct sk_buff *tx_skb;
+#endif
 };
 
 /* The 16-bit access functions are significantly slower, due to the locking
@@ -1253,6 +1257,40 @@ smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
 	smsc911x_mac_write(pdata, ADDRL, mac_low32);
 }
 
+#ifdef SMSC_USE_SH_TX_DMA
+static irqreturn_t smsc911x_tx_dma_irqhandler(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int freespace;
+
+	if (!pdata->tx_skb)
+		return IRQ_NONE;
+
+	/* make sure the channel has finished running */
+	BUG_ON(get_dma_residue(pdata->config.tx_dma_ch));
+
+	dev_kfree_skb_irq(pdata->tx_skb);
+	pdata->tx_skb = NULL;
+
+	freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+	if (freespace < TX_FIFO_LOW_THRESHOLD) {
+		/* free space low, leave tx disabled and enable the
+		 * tx fifo level interrupt */
+		unsigned int temp = smsc911x_reg_read(pdata, FIFO_INT);
+		temp &= 0x00FFFFFF;
+		temp |= 0x32000000;
+		smsc911x_reg_write(pdata, FIFO_INT, temp);
+	} else {
+		/* free space not low, re-enable the tx queue */
+		netif_wake_queue(pdata->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+#endif
+
 static int smsc911x_open(struct net_device *dev)
 {
 	struct smsc911x_data *pdata = netdev_priv(dev);
@@ -1388,7 +1426,30 @@ static int smsc911x_open(struct net_device *dev)
 	printk(KERN_INFO "%s: Rx PIO\n", dev->name);
 #endif
 
+#ifdef SMSC_USE_SH_TX_DMA
+	/* Configure Tx DMA channel for incremented source address, fixed
+	 * destination address and enable the transfer complete interrupt */
+	dma_configure_channel(pdata->config.tx_dma_ch,
+		SM_INC | TS_128 | CHCR_IE | 0x400 | TM_BUR);
+
+	if (request_dma(pdata->config.tx_dma_ch, SMSC_CHIPNAME) < 0) {
+		SMSC_WARNING(DRV, "Error requesting Tx DMA channel %d",
+			pdata->config.tx_dma_ch);
+		goto out_release_rx_dma_1;
+	}
+
+	/* TODO: fix this hardcoded IRQ */
+	if (request_irq(DMTE1_IRQ, smsc911x_tx_dma_irqhandler,
+		IRQF_SHARED | IRQF_DISABLED, SMSC_CHIPNAME " Tx DMA", dev)) {
+		SMSC_WARNING(DRV, "Error requesting Tx DMA irq");
+		goto out_release_tx_dma_2;
+	}
+
+	printk(KERN_INFO "%s: Tx DMA %i\n", dev->name,
+		pdata->config.tx_dma_ch);
+#else
 	printk(KERN_INFO "%s: Tx PIO\n", dev->name);
+#endif
 
 	/* enable NAPI polling before enabling RX interrupts */
 	napi_enable(&pdata->napi);
@@ -1407,6 +1468,16 @@ static int smsc911x_open(struct net_device *dev)
 
 	netif_start_queue(dev);
 	return 0;
+
+#ifdef SMSC_USE_SH_TX_DMA
+out_release_tx_dma_2:
+	free_dma(pdata->config.tx_dma_ch);
+out_release_rx_dma_1:
+#ifdef SMSC_USE_SH_RX_DMA
+	free_dma(pdata->config.rx_dma_ch);
+#endif
+	return -ENODEV;
+#endif
 }
 
 /* Entry point for stopping the interface */
@@ -1434,6 +1505,9 @@ static int smsc911x_stop(struct net_device *dev)
 	phy_stop(pdata->phy_dev);
 
 	/* Free DMA channels */
+#ifdef SMSC_USE_SH_TX_DMA
+	free_dma(pdata->config.tx_dma_ch);
+#endif
 #ifdef SMSC_USE_SH_RX_DMA
 	free_dma(pdata->config.rx_dma_ch);
 #endif
@@ -1449,9 +1523,23 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	unsigned int freespace;
 	unsigned int tx_cmd_a;
 	unsigned int tx_cmd_b;
+#ifdef SMSC_USE_SH_TX_DMA
+	unsigned int dma_cnt;
+	unsigned long physaddrfrom, physaddrto;
+	int cachebytes = dma_get_cache_alignment();
+	unsigned long alignmask = cachebytes - 1;
+	void *dma_buf;
+#else
 	unsigned int temp;
 	u32 wrsz;
 	ulong bufp;
+#endif
+
+#ifdef SMSC_USE_SH_TX_DMA
+	/* make sure the channel is not already running */
+	BUG_ON(pdata->tx_skb);
+	BUG_ON(get_dma_residue(pdata->config.tx_dma_ch));
+#endif
 
 	freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
 
@@ -1459,17 +1547,62 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		SMSC_WARNING(TX_ERR,
 			"Tx data fifo low, space available: %d", freespace);
 
+#ifdef SMSC_USE_SH_TX_DMA
+	/* 16/32 Byte start alignment */
+	tx_cmd_a = (((unsigned int)skb->data) & alignmask) << 16;
+
+	switch (cachebytes) {
+	case 16:
+		tx_cmd_a |= 0x01 << 24; /* 16 byte end alignment */
+		break;
+
+	case 32:
+		tx_cmd_a |= 0x02 << 24; /* 32 byte end alignment */
+		break;
+
+	default:
+		SMSC_WARNING(DRV, "Unknown DMA alignment");
+		break;
+	}
+#else
 	/* Word alignment adjustment */
 	tx_cmd_a = (u32)((ulong)skb->data & 0x03) << 16;
+#endif
+
 	tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
 	tx_cmd_a |= (unsigned int)skb->len;
 
 	tx_cmd_b = ((unsigned int)skb->len) << 16;
 	tx_cmd_b |= (unsigned int)skb->len;
 
+#ifdef SMSC_USE_SH_TX_DMA
+	dma_buf = (void *)(((unsigned long)skb->data) & (~alignmask));
+	dma_cnt = (((u32)skb->len) + alignmask +
+		   (((unsigned long)skb->data) & alignmask)) & (~alignmask);
+#endif
+
 	smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_a);
 	smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_b);
 
+#ifdef SMSC_USE_SH_TX_DMA
+	/* store the skb for the completion isr */
+	pdata->tx_skb = skb;
+
+	/* Calculate the physical transfer addresses */
+	physaddrfrom = virt_to_phys(dma_buf);
+	physaddrto = virt_to_phys(pdata->ioaddr + TX_DATA_FIFO);
+	BUG_ON(physaddrfrom & alignmask);
+	BUG_ON(physaddrto & alignmask);
+
+	/* Flush cache */
+	dma_cache_sync(NULL, dma_buf, dma_cnt, DMA_TO_DEVICE);
+
+	/* stop Tx until the DMA transaction is complete */
+	netif_stop_queue(dev);
+
+	/* Start the DMA transfer */
+	dma_write(pdata->config.tx_dma_ch, physaddrfrom, physaddrto, dma_cnt);
+#else
 	bufp = (ulong)skb->data & (~0x3);
 	wrsz = (u32)skb->len + 3;
 	wrsz += (u32)((ulong)skb->data & 0x3);
@@ -1478,11 +1611,14 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
 	freespace -= (skb->len + 32);
 	dev_kfree_skb(skb);
+#endif
+
 	dev->trans_start = jiffies;
 
 	if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
 		smsc911x_tx_update_txcounters(dev);
 
+#ifndef SMSC_USE_SH_TX_DMA
 	if (freespace < TX_FIFO_LOW_THRESHOLD) {
 		netif_stop_queue(dev);
 		temp = smsc911x_reg_read(pdata, FIFO_INT);
@@ -1490,6 +1626,7 @@ static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		temp |= 0x32000000;
 		smsc911x_reg_write(pdata, FIFO_INT, temp);
 	}
+#endif
 
 	return NETDEV_TX_OK;
 }
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
index 4634dcf..c21b1f9 100644
--- a/drivers/net/smsc911x.h
+++ b/drivers/net/smsc911x.h
@@ -35,6 +35,7 @@
 
 #ifdef CONFIG_SH_DMA
 #define SMSC_USE_SH_RX_DMA
+#define SMSC_USE_SH_TX_DMA
 #endif /* CONFIG_SH_DMA */
 
 #define DPRINTK(nlevel, klevel, fmt, args...) \
diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h
index 0d9408a..d764f01 100644
--- a/include/linux/smsc911x.h
+++ b/include/linux/smsc911x.h
@@ -32,6 +32,7 @@ struct smsc911x_platform_config {
 	phy_interface_t phy_interface;
 #ifdef CONFIG_SH_DMA
 	int rx_dma_ch;
+	int tx_dma_ch;
 #endif
 };
 
-- 
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ