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-next>] [day] [month] [year] [list]
Message-Id: <1359378668-26536-1-git-send-email-panto@antoniou-consulting.com>
Date:	Mon, 28 Jan 2013 15:11:08 +0200
From:	Pantelis Antoniou <panto@...oniou-consulting.com>
To:	Mugunthan V N <mugunthanvnm@...com>
Cc:	Richard Cochran <richardcochran@...il.com>,
	Matt Porter <mporter@...com>,
	Chase Maupin <chase.maupin@...com>, Jason Kridner <jdk@...com>,
	Tony Lindgren <tony@...mide.com>, linux-omap@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	Pantelis Antoniou <panto@...oniou-consulting.com>
Subject: [PATCH] cpsw: Fix interrupt storm among other things

Fix interrupt storm on bone A4 cause by non-by-the-book interrupt handling.
While at it, added a non-NAPI mode (which is easier to debug), plus
some general fixes.

Signed-off-by: Pantelis Antoniou <panto@...oniou-consulting.com>
---
 Documentation/devicetree/bindings/net/cpsw.txt |   1 +
 drivers/net/ethernet/ti/cpsw.c                 | 222 +++++++++++++++++++++----
 drivers/net/ethernet/ti/davinci_cpdma.c        |   4 +-
 drivers/net/ethernet/ti/davinci_cpdma.h        |   2 +-
 include/linux/platform_data/cpsw.h             |   1 +
 5 files changed, 194 insertions(+), 36 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 6ddd028..d46b293 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -20,6 +20,7 @@ Required properties:
 - cpts_clock_shift	: Denominator to convert input clock ticks into nanoseconds
 - phy_id		: Specifies slave phy id
 - mac-address		: Specifies slave MAC address
+- disable-napi		: Disables driver NAPI
 
 Optional properties:
 - ti,hwmods		: Must be "cpgmac0"
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 40aff68..b6ca4af 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -148,10 +148,37 @@ struct cpsw_wr_regs {
 	u32	soft_reset;
 	u32	control;
 	u32	int_control;
-	u32	rx_thresh_en;
-	u32	rx_en;
-	u32	tx_en;
-	u32	misc_en;
+	u32	c0_rx_thresh_en;
+	u32	c0_rx_en;
+	u32	c0_tx_en;
+	u32	c0_misc_en;
+	u32	c1_rx_thresh_en;
+	u32	c1_rx_en;
+	u32	c1_tx_en;
+	u32	c1_misc_en;
+	u32	c2_rx_thresh_en;
+	u32	c2_rx_en;
+	u32	c2_tx_en;
+	u32	c2_misc_en;
+	u32	c0_rx_thresh_stat;
+	u32	c0_rx_stat;
+	u32	c0_tx_stat;
+	u32	c0_misc_stat;
+	u32	c1_rx_thresh_stat;
+	u32	c1_rx_stat;
+	u32	c1_tx_stat;
+	u32	c1_misc_stat;
+	u32	c2_rx_thresh_stat;
+	u32	c2_rx_stat;
+	u32	c2_tx_stat;
+	u32	c2_misc_stat;
+	u32	c0_rx_imax;
+	u32	c0_tx_imax;
+	u32	c1_rx_imax;
+	u32	c1_tx_imax;
+	u32	c2_rx_imax;
+	u32	c2_tx_imax;
+	u32	rgmii_ctl;
 };
 
 struct cpsw_ss_regs {
@@ -352,8 +379,8 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
 
 static void cpsw_intr_enable(struct cpsw_priv *priv)
 {
-	__raw_writel(0xFF, &priv->wr_regs->tx_en);
-	__raw_writel(0xFF, &priv->wr_regs->rx_en);
+	__raw_writel(0xFF, &priv->wr_regs->c0_tx_en);
+	__raw_writel(0xFF, &priv->wr_regs->c0_rx_en);
 
 	cpdma_ctlr_int_ctrl(priv->dma, true);
 	return;
@@ -361,8 +388,8 @@ static void cpsw_intr_enable(struct cpsw_priv *priv)
 
 static void cpsw_intr_disable(struct cpsw_priv *priv)
 {
-	__raw_writel(0, &priv->wr_regs->tx_en);
-	__raw_writel(0, &priv->wr_regs->rx_en);
+	__raw_writel(0, &priv->wr_regs->c0_tx_en);
+	__raw_writel(0, &priv->wr_regs->c0_rx_en);
 
 	cpdma_ctlr_int_ctrl(priv->dma, false);
 	return;
@@ -399,7 +426,10 @@ void cpsw_rx_handler(void *token, int len, int status)
 		skb_put(skb, len);
 		cpts_rx_timestamp(&priv->cpts, skb);
 		skb->protocol = eth_type_trans(skb, ndev);
-		netif_receive_skb(skb);
+		if (priv->data.disable_napi)
+			netif_rx(skb);
+		else
+			netif_receive_skb(skb);
 		priv->stats.rx_bytes += len;
 		priv->stats.rx_packets++;
 		skb = NULL;
@@ -431,6 +461,7 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
 		cpsw_disable_irq(priv);
 		napi_schedule(&priv->napi);
 	}
+
 	return IRQ_HANDLED;
 }
 
@@ -445,23 +476,104 @@ static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
 static int cpsw_poll(struct napi_struct *napi, int budget)
 {
 	struct cpsw_priv	*priv = napi_to_priv(napi);
-	int			num_tx, num_rx;
+	int			num_tx, num_rx, num_total_tx, num_total_rx;
+	int			budget_left;
+
+	budget_left = budget;
 
-	num_tx = cpdma_chan_process(priv->txch, 128);
-	num_rx = cpdma_chan_process(priv->rxch, budget);
+	/* read status and throw away */
+	(void)__raw_readl(&priv->wr_regs->c0_tx_stat);
+
+	/* handle all transmits */
+	num_total_tx = 0;
+	while (budget_left > 0 &&
+		(num_tx = cpdma_chan_process(priv->txch, 128)) > 0) {
+		budget_left -= num_tx;
+		num_total_tx += num_tx;
+	}
 
-	if (num_rx || num_tx)
-		cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n",
-			 num_rx, num_tx);
+	if (num_total_tx > 0 && budget_left > 0)
+		cpdma_ctlr_eoi(priv->dma, 0x02);
+
+	/* read status and throw away */
+	(void)__raw_readl(&priv->wr_regs->c0_rx_stat);
+
+	/* handle all receives */
+	num_total_rx = 0;
+	while (budget_left > 0 &&
+		(num_rx = cpdma_chan_process(priv->rxch, budget_left)) > 0) {
+		budget_left -= num_rx;
+		num_total_rx += num_rx;
+	}
 
-	if (num_rx < budget) {
+	if (num_total_rx > 0 && budget_left > 0)
+		cpdma_ctlr_eoi(priv->dma, 0x01);
+
+	if ((num_total_rx + num_total_tx) < budget) {
 		napi_complete(napi);
 		cpsw_intr_enable(priv);
-		cpdma_ctlr_eoi(priv->dma);
 		cpsw_enable_irq(priv);
 	}
 
-	return num_rx;
+	return num_total_rx + num_total_rx;
+}
+
+static irqreturn_t cpsw_rx_thresh_pend_irq(int irq, void *dev_id)
+{
+	struct cpsw_priv *priv = dev_id;
+
+	(void)priv;
+
+	/* not handling this interrupt yet */
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_rx_pend_irq(int irq, void *dev_id)
+{
+	struct cpsw_priv *priv = dev_id;
+	int num_rx, total_rx;
+	u32 rx_stat;
+
+	rx_stat = __raw_readl(&priv->wr_regs->c0_rx_stat) & 0xff;
+	if (rx_stat == 0)
+		return IRQ_NONE;
+
+	total_rx = 0;
+	while ((num_rx = cpdma_chan_process(priv->rxch,
+					priv->data.rx_descs)) > 0)
+		total_rx += num_rx;
+
+	cpdma_ctlr_eoi(priv->dma, 0x01);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_tx_pend_irq(int irq, void *dev_id)
+{
+	struct cpsw_priv *priv = dev_id;
+	int num_tx, total_tx;
+	u32 tx_stat;
+
+	tx_stat = __raw_readl(&priv->wr_regs->c0_tx_stat) & 0xff;
+	if (tx_stat == 0)
+		return IRQ_NONE;
+
+	total_tx = 0;
+	while ((num_tx = cpdma_chan_process(priv->txch, 128)) > 0)
+		total_tx += num_tx;
+
+	cpdma_ctlr_eoi(priv->dma, 0x02);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_misc_pend_irq(int irq, void *dev_id)
+{
+	struct cpsw_priv *priv = dev_id;
+
+	(void)priv;
+	/* not handling this interrupt yet */
+	return IRQ_HANDLED;
 }
 
 static inline void soft_reset(const char *module, void __iomem *reg)
@@ -678,8 +790,10 @@ static int cpsw_ndo_open(struct net_device *ndev)
 
 	cpdma_ctlr_start(priv->dma);
 	cpsw_intr_enable(priv);
-	napi_enable(&priv->napi);
-	cpdma_ctlr_eoi(priv->dma);
+	if (!priv->data.disable_napi)
+		napi_enable(&priv->napi);
+	cpdma_ctlr_eoi(priv->dma, 0x01);
+	cpdma_ctlr_eoi(priv->dma, 0x02);
 
 	return 0;
 }
@@ -698,8 +812,10 @@ static int cpsw_ndo_stop(struct net_device *ndev)
 	struct cpsw_priv *priv = netdev_priv(ndev);
 
 	cpsw_info(priv, ifdown, "shutting down cpsw device\n");
+	cpsw_disable_irq(priv);
 	netif_stop_queue(priv->ndev);
-	napi_disable(&priv->napi);
+	if (!priv->data.disable_napi)
+		napi_disable(&priv->napi);
 	netif_carrier_off(priv->ndev);
 	cpsw_intr_disable(priv);
 	cpdma_ctlr_int_ctrl(priv->dma, false);
@@ -901,7 +1017,8 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev)
 	cpdma_chan_start(priv->txch);
 	cpdma_ctlr_int_ctrl(priv->dma, true);
 	cpsw_intr_enable(priv);
-	cpdma_ctlr_eoi(priv->dma);
+	cpdma_ctlr_eoi(priv->dma, 0x01);
+	cpdma_ctlr_eoi(priv->dma, 0x02);
 }
 
 static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
@@ -917,14 +1034,21 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev)
 
 	cpsw_intr_disable(priv);
 	cpdma_ctlr_int_ctrl(priv->dma, false);
-	cpsw_interrupt(ndev->irq, priv);
+	if (!priv->data.disable_napi)
+		cpsw_interrupt(ndev->irq, priv);
+	else {
+		/* bah! */
+		cpsw_rx_pend_irq(ndev->irq, priv);
+		cpsw_tx_pend_irq(ndev->irq, priv);
+	}
 	cpdma_ctlr_int_ctrl(priv->dma, true);
 	cpsw_intr_enable(priv);
-	cpdma_ctlr_eoi(priv->dma);
+	cpdma_ctlr_eoi(priv->dma, 0x01);
+	cpdma_ctlr_eoi(priv->dma, 0x02);
 }
 #endif
 
-static const struct net_device_ops cpsw_netdev_ops = {
+static struct net_device_ops cpsw_netdev_ops = {
 	.ndo_open		= cpsw_ndo_open,
 	.ndo_stop		= cpsw_ndo_stop,
 	.ndo_start_xmit		= cpsw_ndo_start_xmit,
@@ -1079,6 +1203,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 	}
 	data->bd_ram_size = prop;
 
+	if (of_property_read_bool(node, "disable-napi"))
+		data->disable_napi = 1;
+
 	if (of_property_read_u32(node, "rx_descs", &prop)) {
 		pr_err("Missing rx_descs property in the DT.\n");
 		ret = -EINVAL;
@@ -1136,6 +1263,22 @@ error_ret:
 	return ret;
 }
 
+static irq_handler_t cpsw_get_irq_handler(struct cpsw_priv *priv, int irq_idx)
+{
+	static const irq_handler_t non_napi_irq_tab[4] = {
+		cpsw_rx_thresh_pend_irq, cpsw_rx_pend_irq,
+		cpsw_tx_pend_irq, cpsw_misc_pend_irq
+	};
+
+	if ((unsigned int)irq_idx >= 4)
+		return NULL;
+
+	if (!priv->data.disable_napi)
+		return cpsw_interrupt;
+
+	return non_napi_irq_tab[irq_idx];
+}
+
 static int cpsw_probe(struct platform_device *pdev)
 {
 	struct cpsw_platform_data	*data = pdev->dev.platform_data;
@@ -1146,7 +1289,8 @@ static int cpsw_probe(struct platform_device *pdev)
 	void __iomem			*ss_regs, *wr_regs;
 	struct resource			*res;
 	u32 slave_offset, sliver_offset, slave_size;
-	int ret = 0, i, k = 0;
+	irq_handler_t			irqh;
+	int ret = 0, i, j, k = 0;
 
 	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
 	if (!ndev) {
@@ -1333,24 +1477,36 @@ static int cpsw_probe(struct platform_device *pdev)
 		goto clean_ale_ret;
 	}
 
-	while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
-		for (i = res->start; i <= res->end; i++) {
-			if (request_irq(i, cpsw_interrupt, IRQF_DISABLED,
+	dev_info(&pdev->dev, "NAPI %s\n", priv->data.disable_napi ?
+			"disabled" : "enabled");
+
+	/* get interrupts */
+	j = k = 0;
+	while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, j++))) {
+		for (i = res->start; k < 4 && i <= res->end; i++) {
+			irqh = cpsw_get_irq_handler(priv, k);
+			if (irqh == NULL) {
+				dev_err(&pdev->dev, "Unable to get handler "
+						"for #%d (%d)\n", k, i);
+				goto clean_ale_ret;
+			}
+			if (request_irq(i, irqh, IRQF_DISABLED,
 					dev_name(&pdev->dev), priv)) {
 				dev_err(priv->dev, "error attaching irq\n");
 				goto clean_ale_ret;
 			}
-			priv->irqs_table[k] = i;
-			priv->num_irqs = k;
+			priv->irqs_table[k++] = i;
 		}
-		k++;
 	}
+	priv->num_irqs = k;
 
 	ndev->flags |= IFF_ALLMULTI;	/* see cpsw_ndo_change_rx_flags() */
 
 	ndev->netdev_ops = &cpsw_netdev_ops;
 	SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
-	netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
+
+	if (!priv->data.disable_napi)
+		netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
 
 	/* register the network device */
 	SET_NETDEV_DEV(ndev, &pdev->dev);
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 4995673..6effab2 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -474,9 +474,9 @@ int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
 	return 0;
 }
 
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
 {
-	dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+	dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, value);
 }
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index afa19a0..b7c097d 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -86,7 +86,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
 int cpdma_chan_process(struct cpdma_chan *chan, int quota);
 
 int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value);
 int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
 
 enum cpdma_control {
diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h
index 24368a2..be5b27e 100644
--- a/include/linux/platform_data/cpsw.h
+++ b/include/linux/platform_data/cpsw.h
@@ -35,6 +35,7 @@ struct cpsw_platform_data {
 	u32	bd_ram_size;  /*buffer descriptor ram size */
 	u32	rx_descs;	/* Number of Rx Descriptios */
 	u32	mac_control;	/* Mac control register */
+	int	disable_napi;	/* disable NAPI */
 };
 
 #endif /* __CPSW_H__ */
-- 
1.7.12

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ