linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4]  net: ethernet: ti: cpsw: add multi-queue support
@ 2016-06-30 19:04 Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels Ivan Khoronzhuk
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-06-30 19:04 UTC (permalink / raw)
  To: davem, netdev, mugunthanvnm
  Cc: grygorii.strashko, linux-kernel, linux-omap, nsekhar, Ivan Khoronzhuk

This series is intended to allow cpsw driver to use its ability of h/w
shaper to send/receive data with up to 8 tx and rx queues. This series
doesn't contain interface to configure h/w shaper itself, it contains
only multi queue support part and ability to configure number of tx/rx
queues with ethtool. Default shaper mode - priority mode. The h/w
shaper configuration will be added with separate patch series.
This series doesn't affect on net throughput.

Tested on:
am572x-idk, 1Gbps link
am335-boneblack, 100Mbps link.

Based on:
git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git

A simple example for splitting traffic on queues:

#check how many queues are supported and active:
$ ethtool -l eth0

#increase number of active rx and tx queues,
#by default 1 rx and 1 tx queue
#can be set any combination of 0 < rx <= 8 and 0 < tx <= 8
$ ethtool -L eth0 rx 8 tx 8

#set multi-queue-aware queuing discipline
$ tc qdisc add dev eth0 root handle 1: multiq

#send packets with ip 172.22.39.12 to queue #5 which can be
#prioritized or throughput limited by h/w shaper.
$ tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \
    match ip dst 172.22.39.12 \
    action skbedit queue_mapping 5

#get statistic for active channels:
ethtool -S eth0

Ivan Khoronzhuk (4):
  net: ethernet: ti: davinci_cpdma: split descs num between all channels
  net: ethernet: ti: cpsw: add multi queue support
  net: ethernet: ti: davinci_cpdma: move cpdma channel struct macroses
    to internals
  net: ethernet: ti: cpsw: add ethtool channels support

 drivers/net/ethernet/ti/cpsw.c          | 533 +++++++++++++++++++++++++-------
 drivers/net/ethernet/ti/davinci_cpdma.c |  79 ++++-
 drivers/net/ethernet/ti/davinci_cpdma.h |  13 +-
 drivers/net/ethernet/ti/davinci_emac.c  |   8 +-
 4 files changed, 505 insertions(+), 128 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels
  2016-06-30 19:04 [PATCH 0/4] net: ethernet: ti: cpsw: add multi-queue support Ivan Khoronzhuk
@ 2016-06-30 19:04 ` Ivan Khoronzhuk
  2016-07-01 20:35   ` David Miller
  2016-06-30 19:04 ` [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support Ivan Khoronzhuk
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-06-30 19:04 UTC (permalink / raw)
  To: davem, netdev, mugunthanvnm
  Cc: grygorii.strashko, linux-kernel, linux-omap, nsekhar, Ivan Khoronzhuk

Currently the tx channels are using the same pool of descriptors.
Thus one channel can block another if pool is emptied by one.
But, the shaper should decide which channel is allowed to send
packets. To avoid such impact of one channel on another let every
channel to have its own peace of pool.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c          | 59 +++++++++++++++++++++------------
 drivers/net/ethernet/ti/davinci_cpdma.c | 54 ++++++++++++++++++++++++++++--
 drivers/net/ethernet/ti/davinci_cpdma.h |  2 +-
 3 files changed, 89 insertions(+), 26 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 1a93a1f..a713336 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1230,6 +1230,39 @@ static void cpsw_init_host_port(struct cpsw_priv *priv)
 	}
 }
 
+static int cpsw_fill_rx_channels(struct net_device *ndev)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	struct sk_buff *skb;
+	int ch_buf_num;
+	int i, ret;
+
+	ch_buf_num = cpdma_chan_get_rx_buf_num(priv->rxch);
+	for (i = 0; i < ch_buf_num; i++) {
+		skb = __netdev_alloc_skb_ip_align(ndev,
+						  priv->rx_packet_max,
+						  GFP_KERNEL);
+		if (!skb) {
+			dev_err(priv->dev, "cannot allocate skb\n");
+			return -ENOMEM;
+		}
+
+		ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
+					skb_tailroom(skb), 0);
+		if (ret < 0) {
+			dev_err(priv->dev,
+				"cannot submit skb to rx channel, error %d\n",
+				ret);
+			kfree_skb(skb);
+			return ret;
+		}
+	}
+
+	cpsw_info(priv, ifup, "submitted %d rx descriptors\n", ch_buf_num);
+
+	return ch_buf_num;
+}
+
 static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
 {
 	u32 slave_port;
@@ -1249,7 +1282,7 @@ static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
 static int cpsw_ndo_open(struct net_device *ndev)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
-	int i, ret;
+	int ret;
 	u32 reg;
 
 	ret = pm_runtime_get_sync(&priv->pdev->dev);
@@ -1282,7 +1315,6 @@ static int cpsw_ndo_open(struct net_device *ndev)
 
 	if (!cpsw_common_res_usage_state(priv)) {
 		struct cpsw_priv *priv_sl0 = cpsw_get_slave_priv(priv, 0);
-		int buf_num;
 
 		/* setup tx dma to fixed prio and zero offset */
 		cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1);
@@ -1310,26 +1342,9 @@ static int cpsw_ndo_open(struct net_device *ndev)
 			enable_irq(priv->irqs_table[0]);
 		}
 
-		buf_num = cpdma_chan_get_rx_buf_num(priv->dma);
-		for (i = 0; i < buf_num; i++) {
-			struct sk_buff *skb;
-
-			ret = -ENOMEM;
-			skb = __netdev_alloc_skb_ip_align(priv->ndev,
-					priv->rx_packet_max, GFP_KERNEL);
-			if (!skb)
-				goto err_cleanup;
-			ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
-					skb_tailroom(skb), 0);
-			if (ret < 0) {
-				kfree_skb(skb);
-				goto err_cleanup;
-			}
-		}
-		/* continue even if we didn't manage to submit all
-		 * receive descs
-		 */
-		cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
+		ret = cpsw_fill_rx_channels(ndev);
+		if (ret < 0)
+			goto err_cleanup;
 
 		if (cpts_register(&priv->pdev->dev, priv->cpts,
 				  priv->data.cpts_clock_mult,
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 1c653ca..2f4b571 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -106,6 +106,7 @@ struct cpdma_ctlr {
 	struct cpdma_desc_pool	*pool;
 	spinlock_t		lock;
 	struct cpdma_chan	*channels[2 * CPDMA_MAX_CHANNELS];
+	int chan_num;
 };
 
 struct cpdma_chan {
@@ -262,6 +263,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
 	ctlr->state = CPDMA_STATE_IDLE;
 	ctlr->params = *params;
 	ctlr->dev = params->dev;
+	ctlr->chan_num = 0;
 	spin_lock_init(&ctlr->lock);
 
 	ctlr->pool = cpdma_desc_pool_create(ctlr->dev,
@@ -479,6 +481,32 @@ void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
 }
 EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
 
+/**
+ * cpdma_chan_split_pool - Splits ctrl pool between all channels.
+ * Has to be called under ctlr lock
+ *
+ */
+static void cpdma_chan_split_pool(struct cpdma_ctlr *ctlr)
+{
+	struct cpdma_desc_pool *pool = ctlr->pool;
+	struct cpdma_chan *chan;
+	int ch_desc_num;
+	int i;
+
+	if (!ctlr->chan_num)
+		return;
+
+	/* calculate average size of pool slice */
+	ch_desc_num = pool->num_desc / ctlr->chan_num;
+
+	/* split ctlr pool */
+	for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+		chan = ctlr->channels[i];
+		if (chan)
+			chan->desc_num = ch_desc_num;
+	}
+}
+
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
 				     cpdma_handler_fn handler)
 {
@@ -527,14 +555,25 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
 	spin_lock_init(&chan->lock);
 
 	ctlr->channels[chan_num] = chan;
+	ctlr->chan_num++;
+
+	cpdma_chan_split_pool(ctlr);
+
 	spin_unlock_irqrestore(&ctlr->lock, flags);
 	return chan;
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_create);
 
-int cpdma_chan_get_rx_buf_num(struct cpdma_ctlr *ctlr)
+int cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan)
 {
-	return ctlr->pool->num_desc / 2;
+	unsigned long flags;
+	int desc_num;
+
+	spin_lock_irqsave(&chan->lock, flags);
+	desc_num = chan->desc_num;
+	spin_unlock_irqrestore(&chan->lock, flags);
+
+	return desc_num;
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_get_rx_buf_num);
 
@@ -551,6 +590,10 @@ int cpdma_chan_destroy(struct cpdma_chan *chan)
 	if (chan->state != CPDMA_STATE_IDLE)
 		cpdma_chan_stop(chan);
 	ctlr->channels[chan->chan_num] = NULL;
+	ctlr->chan_num--;
+
+	cpdma_chan_split_pool(ctlr);
+
 	spin_unlock_irqrestore(&ctlr->lock, flags);
 	return 0;
 }
@@ -720,7 +763,7 @@ unlock_ret:
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_submit);
 
-bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
+inline bool cpdma_check_free_desc(struct cpdma_chan *chan)
 {
 	struct cpdma_ctlr	*ctlr = chan->ctlr;
 	struct cpdma_desc_pool	*pool = ctlr->pool;
@@ -733,6 +776,11 @@ bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
 	spin_unlock_irqrestore(&chan->lock, flags);
 	return free_tx_desc;
 }
+
+bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
+{
+	return cpdma_check_free_desc(chan);
+}
 EXPORT_SYMBOL_GPL(cpdma_check_free_tx_desc);
 
 static void __cpdma_chan_free(struct cpdma_chan *chan,
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index 80c015c..0308b67 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -81,7 +81,7 @@ int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr);
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
 				     cpdma_handler_fn handler);
-int cpdma_chan_get_rx_buf_num(struct cpdma_ctlr *ctlr);
+int cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan);
 int cpdma_chan_destroy(struct cpdma_chan *chan);
 int cpdma_chan_start(struct cpdma_chan *chan);
 int cpdma_chan_stop(struct cpdma_chan *chan);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support
  2016-06-30 19:04 [PATCH 0/4] net: ethernet: ti: cpsw: add multi-queue support Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels Ivan Khoronzhuk
@ 2016-06-30 19:04 ` Ivan Khoronzhuk
  2016-07-08 13:12   ` Grygorii Strashko
  2016-06-30 19:04 ` [PATCH 3/4] net: ethernet: ti: davinci_cpdma: move cpdma channel struct macroses to internals Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support Ivan Khoronzhuk
  3 siblings, 1 reply; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-06-30 19:04 UTC (permalink / raw)
  To: davem, netdev, mugunthanvnm
  Cc: grygorii.strashko, linux-kernel, linux-omap, nsekhar, Ivan Khoronzhuk

The cpsw h/w supports up to 8 tx and 8 rx channels.This patch adds
multi-queue support to the driver. An ability to configure h/w
shaper will be added with separate patch. Default shaper mode, as
before, priority mode.

The poll function handles all unprocessed channels, till all of
them are free, beginning from hi priority channel.

The statistic for every channel can be read with:
ethtool -S ethX

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c          | 334 +++++++++++++++++++++-----------
 drivers/net/ethernet/ti/davinci_cpdma.c |  12 ++
 drivers/net/ethernet/ti/davinci_cpdma.h |   2 +
 3 files changed, 237 insertions(+), 111 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index a713336..14d53eb 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -140,6 +140,8 @@ do {								\
 #define CPSW_CMINTMAX_INTVL	(1000 / CPSW_CMINTMIN_CNT)
 #define CPSW_CMINTMIN_INTVL	((1000 / CPSW_CMINTMAX_CNT) + 1)
 
+#define CPSW_MAX_QUEUES		8
+
 #define cpsw_slave_index(priv)				\
 		((priv->data.dual_emac) ? priv->emac_port :	\
 		priv->data.active_slave)
@@ -383,7 +385,8 @@ struct cpsw_priv {
 	u8				mac_addr[ETH_ALEN];
 	struct cpsw_slave		*slaves;
 	struct cpdma_ctlr		*dma;
-	struct cpdma_chan		*txch, *rxch;
+	struct cpdma_chan		*txch[CPSW_MAX_QUEUES];
+	struct cpdma_chan		*rxch[CPSW_MAX_QUEUES];
 	struct cpsw_ale			*ale;
 	bool				rx_pause;
 	bool				tx_pause;
@@ -395,6 +398,7 @@ struct cpsw_priv {
 	u32 num_irqs;
 	struct cpts *cpts;
 	u32 emac_port;
+	int rx_ch_num, tx_ch_num;
 };
 
 struct cpsw_stats {
@@ -455,35 +459,26 @@ static const struct cpsw_stats cpsw_gstrings_stats[] = {
 	{ "Rx Start of Frame Overruns", CPSW_STAT(rxsofoverruns) },
 	{ "Rx Middle of Frame Overruns", CPSW_STAT(rxmofoverruns) },
 	{ "Rx DMA Overruns", CPSW_STAT(rxdmaoverruns) },
-	{ "Rx DMA chan: head_enqueue", CPDMA_RX_STAT(head_enqueue) },
-	{ "Rx DMA chan: tail_enqueue", CPDMA_RX_STAT(tail_enqueue) },
-	{ "Rx DMA chan: pad_enqueue", CPDMA_RX_STAT(pad_enqueue) },
-	{ "Rx DMA chan: misqueued", CPDMA_RX_STAT(misqueued) },
-	{ "Rx DMA chan: desc_alloc_fail", CPDMA_RX_STAT(desc_alloc_fail) },
-	{ "Rx DMA chan: pad_alloc_fail", CPDMA_RX_STAT(pad_alloc_fail) },
-	{ "Rx DMA chan: runt_receive_buf", CPDMA_RX_STAT(runt_receive_buff) },
-	{ "Rx DMA chan: runt_transmit_buf", CPDMA_RX_STAT(runt_transmit_buff) },
-	{ "Rx DMA chan: empty_dequeue", CPDMA_RX_STAT(empty_dequeue) },
-	{ "Rx DMA chan: busy_dequeue", CPDMA_RX_STAT(busy_dequeue) },
-	{ "Rx DMA chan: good_dequeue", CPDMA_RX_STAT(good_dequeue) },
-	{ "Rx DMA chan: requeue", CPDMA_RX_STAT(requeue) },
-	{ "Rx DMA chan: teardown_dequeue", CPDMA_RX_STAT(teardown_dequeue) },
-	{ "Tx DMA chan: head_enqueue", CPDMA_TX_STAT(head_enqueue) },
-	{ "Tx DMA chan: tail_enqueue", CPDMA_TX_STAT(tail_enqueue) },
-	{ "Tx DMA chan: pad_enqueue", CPDMA_TX_STAT(pad_enqueue) },
-	{ "Tx DMA chan: misqueued", CPDMA_TX_STAT(misqueued) },
-	{ "Tx DMA chan: desc_alloc_fail", CPDMA_TX_STAT(desc_alloc_fail) },
-	{ "Tx DMA chan: pad_alloc_fail", CPDMA_TX_STAT(pad_alloc_fail) },
-	{ "Tx DMA chan: runt_receive_buf", CPDMA_TX_STAT(runt_receive_buff) },
-	{ "Tx DMA chan: runt_transmit_buf", CPDMA_TX_STAT(runt_transmit_buff) },
-	{ "Tx DMA chan: empty_dequeue", CPDMA_TX_STAT(empty_dequeue) },
-	{ "Tx DMA chan: busy_dequeue", CPDMA_TX_STAT(busy_dequeue) },
-	{ "Tx DMA chan: good_dequeue", CPDMA_TX_STAT(good_dequeue) },
-	{ "Tx DMA chan: requeue", CPDMA_TX_STAT(requeue) },
-	{ "Tx DMA chan: teardown_dequeue", CPDMA_TX_STAT(teardown_dequeue) },
 };
 
-#define CPSW_STATS_LEN	ARRAY_SIZE(cpsw_gstrings_stats)
+static const struct cpsw_stats cpsw_gstrings_ch_stats[] = {
+	{ "head_enqueue", CPDMA_RX_STAT(head_enqueue) },
+	{ "tail_enqueue", CPDMA_RX_STAT(tail_enqueue) },
+	{ "pad_enqueue", CPDMA_RX_STAT(pad_enqueue) },
+	{ "misqueued", CPDMA_RX_STAT(misqueued) },
+	{ "desc_alloc_fail", CPDMA_RX_STAT(desc_alloc_fail) },
+	{ "pad_alloc_fail", CPDMA_RX_STAT(pad_alloc_fail) },
+	{ "runt_receive_buf", CPDMA_RX_STAT(runt_receive_buff) },
+	{ "runt_transmit_buf", CPDMA_RX_STAT(runt_transmit_buff) },
+	{ "empty_dequeue", CPDMA_RX_STAT(empty_dequeue) },
+	{ "busy_dequeue", CPDMA_RX_STAT(busy_dequeue) },
+	{ "good_dequeue", CPDMA_RX_STAT(good_dequeue) },
+	{ "requeue", CPDMA_RX_STAT(requeue) },
+	{ "teardown_dequeue", CPDMA_RX_STAT(teardown_dequeue) },
+};
+
+#define CPSW_STATS_COMMON_LEN	ARRAY_SIZE(cpsw_gstrings_stats)
+#define CPSW_STATS_CH_LEN	ARRAY_SIZE(cpsw_gstrings_ch_stats)
 
 #define napi_to_priv(napi)	container_of(napi, struct cpsw_priv, napi)
 #define for_each_slave(priv, func, arg...)				\
@@ -676,12 +671,15 @@ static void cpsw_tx_handler(void *token, int len, int status)
 	struct sk_buff		*skb = token;
 	struct net_device	*ndev = skb->dev;
 	struct cpsw_priv	*priv = netdev_priv(ndev);
+	struct netdev_queue	*txq;
 
 	/* Check whether the queue is stopped due to stalled tx dma, if the
 	 * queue is stopped then start the queue as we have free desc for tx
 	 */
-	if (unlikely(netif_queue_stopped(ndev)))
-		netif_wake_queue(ndev);
+	txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb));
+	if (unlikely(netif_tx_queue_stopped(txq)))
+		netif_tx_wake_queue(txq);
+
 	cpts_tx_timestamp(priv->cpts, skb);
 	ndev->stats.tx_packets++;
 	ndev->stats.tx_bytes += len;
@@ -695,6 +693,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
 	struct net_device	*ndev = skb->dev;
 	struct cpsw_priv	*priv = netdev_priv(ndev);
 	int			ret = 0;
+	struct cpdma_chan	*ch;
 
 	cpsw_dual_emac_src_port_detect(status, priv, ndev, skb);
 
@@ -728,6 +727,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
 
 	new_skb = netdev_alloc_skb_ip_align(ndev, priv->rx_packet_max);
 	if (new_skb) {
+		skb_copy_queue_mapping(new_skb, skb);
 		skb_put(skb, len);
 		cpts_rx_timestamp(priv->cpts, skb);
 		skb->protocol = eth_type_trans(skb, ndev);
@@ -740,8 +740,9 @@ static void cpsw_rx_handler(void *token, int len, int status)
 	}
 
 requeue:
-	ret = cpdma_chan_submit(priv->rxch, new_skb, new_skb->data,
-			skb_tailroom(new_skb), 0);
+	ch = priv->rxch[skb_get_queue_mapping(new_skb)];
+	ret = cpdma_chan_submit(ch, new_skb, new_skb->data,
+				skb_tailroom(new_skb), 0);
 	if (WARN_ON(ret < 0))
 		dev_kfree_skb_any(new_skb);
 }
@@ -781,9 +782,26 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
 static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
 {
 	struct cpsw_priv	*priv = napi_to_priv(napi_tx);
-	int			num_tx;
+	int			num_tx, ch;
+	u32			ch_map;
+
+	/* process every unprocessed channel */
+	ch_map = cpdma_ctrl_txchs_state(priv->dma);
+	for (ch = 0, num_tx = 0; num_tx < budget; ch_map >>= 1, ch++) {
+		if (!ch_map) {
+			ch_map = cpdma_ctrl_txchs_state(priv->dma);
+			if (!ch_map)
+				break;
+
+			ch = 0;
+		}
+
+		if (!(ch_map & 0x01))
+			continue;
+
+		num_tx += cpdma_chan_process(priv->txch[ch], budget - num_tx);
+	}
 
-	num_tx = cpdma_chan_process(priv->txch, budget);
 	if (num_tx < budget) {
 		napi_complete(napi_tx);
 		writel(0xff, &priv->wr_regs->tx_en);
@@ -802,9 +820,26 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
 static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
 {
 	struct cpsw_priv	*priv = napi_to_priv(napi_rx);
-	int			num_rx;
+	int			num_rx, ch;
+	u32			ch_map;
+
+	/* process every unprocessed channel */
+	ch_map = cpdma_ctrl_rxchs_state(priv->dma);
+	for (ch = 0, num_rx = 0; num_rx < budget; ch_map >>= 1, ch++) {
+		if (!ch_map) {
+			ch_map = cpdma_ctrl_rxchs_state(priv->dma);
+			if (!ch_map)
+				break;
+
+			ch = 0;
+		}
+
+		if (!(ch_map & 0x01))
+			continue;
+
+		num_rx += cpdma_chan_process(priv->rxch[ch], budget - num_rx);
+	}
 
-	num_rx = cpdma_chan_process(priv->rxch, budget);
 	if (num_rx < budget) {
 		napi_complete(napi_rx);
 		writel(0xff, &priv->wr_regs->rx_en);
@@ -905,10 +940,10 @@ static void cpsw_adjust_link(struct net_device *ndev)
 	if (link) {
 		netif_carrier_on(ndev);
 		if (netif_running(ndev))
-			netif_wake_queue(ndev);
+			netif_tx_wake_all_queues(ndev);
 	} else {
 		netif_carrier_off(ndev);
-		netif_stop_queue(ndev);
+		netif_tx_stop_all_queues(ndev);
 	}
 }
 
@@ -989,26 +1024,50 @@ update_return:
 
 static int cpsw_get_sset_count(struct net_device *ndev, int sset)
 {
+	struct cpsw_priv *priv = netdev_priv(ndev);
+
 	switch (sset) {
 	case ETH_SS_STATS:
-		return CPSW_STATS_LEN;
+		return (CPSW_STATS_COMMON_LEN +
+		       (priv->rx_ch_num + priv->tx_ch_num) *
+		       CPSW_STATS_CH_LEN);
 	default:
 		return -EOPNOTSUPP;
 	}
 }
 
+static void cpsw_add_ch_strings(u8 **p, int ch_num, int rx_dir)
+{
+	int ch_stats_len;
+	int line;
+	int i;
+
+	ch_stats_len = CPSW_STATS_CH_LEN * ch_num;
+	for (i = 0; i < ch_stats_len; i++) {
+		line = i % CPSW_STATS_CH_LEN;
+		sprintf(*p, "%s DMA chan %d: %s", rx_dir ? "Rx" : "Tx",
+			i / CPSW_STATS_CH_LEN,
+			cpsw_gstrings_ch_stats[line].stat_string);
+		*p += ETH_GSTRING_LEN;
+	}
+}
+
 static void cpsw_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 {
+	struct cpsw_priv *priv = netdev_priv(ndev);
 	u8 *p = data;
 	int i;
 
 	switch (stringset) {
 	case ETH_SS_STATS:
-		for (i = 0; i < CPSW_STATS_LEN; i++) {
+		for (i = 0; i < CPSW_STATS_COMMON_LEN; i++) {
 			memcpy(p, cpsw_gstrings_stats[i].stat_string,
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
+
+		cpsw_add_ch_strings(&p, priv->rx_ch_num, 1);
+		cpsw_add_ch_strings(&p, priv->tx_ch_num, 0);
 		break;
 	}
 }
@@ -1017,35 +1076,38 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev,
 				    struct ethtool_stats *stats, u64 *data)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
-	struct cpdma_chan_stats rx_stats;
-	struct cpdma_chan_stats tx_stats;
-	u32 val;
+	struct cpdma_chan_stats ch_stats;
+	int i, l, ch, ret;
 	u8 *p;
-	int i;
+
+	ret = pm_runtime_get_sync(&priv->pdev->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(&priv->pdev->dev);
+		return;
+	}
 
 	/* Collect Davinci CPDMA stats for Rx and Tx Channel */
-	cpdma_chan_get_stats(priv->rxch, &rx_stats);
-	cpdma_chan_get_stats(priv->txch, &tx_stats);
-
-	for (i = 0; i < CPSW_STATS_LEN; i++) {
-		switch (cpsw_gstrings_stats[i].type) {
-		case CPSW_STATS:
-			val = readl(priv->hw_stats +
-				    cpsw_gstrings_stats[i].stat_offset);
-			data[i] = val;
-			break;
+	for (l = 0; l < CPSW_STATS_COMMON_LEN; l++)
+		data[l] = readl(priv->hw_stats +
+				cpsw_gstrings_stats[l].stat_offset);
 
-		case CPDMA_RX_STATS:
-			p = (u8 *)&rx_stats +
-				cpsw_gstrings_stats[i].stat_offset;
-			data[i] = *(u32 *)p;
-			break;
+	pm_runtime_put(&priv->pdev->dev);
 
-		case CPDMA_TX_STATS:
-			p = (u8 *)&tx_stats +
-				cpsw_gstrings_stats[i].stat_offset;
-			data[i] = *(u32 *)p;
-			break;
+	for (ch = 0; ch < priv->rx_ch_num; ch++) {
+		cpdma_chan_get_stats(priv->rxch[ch], &ch_stats);
+		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
+			p = (u8 *)&ch_stats +
+				cpsw_gstrings_ch_stats[i].stat_offset;
+			data[l] = *(u32 *)p;
+		}
+	}
+
+	for (ch = 0; ch < priv->tx_ch_num; ch++) {
+		cpdma_chan_get_stats(priv->txch[ch], &ch_stats);
+		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
+			p = (u8 *)&ch_stats +
+				cpsw_gstrings_ch_stats[i].stat_offset;
+			data[l] = *(u32 *)p;
 		}
 	}
 }
@@ -1065,19 +1127,29 @@ static int cpsw_common_res_usage_state(struct cpsw_priv *priv)
 	return usage_count;
 }
 
+static inline struct cpdma_chan *
+cpsw_tx_queue_mapping(struct cpsw_priv *priv, struct sk_buff *skb)
+{
+	unsigned int q_idx = skb_get_queue_mapping(skb);
+
+	if (q_idx >= priv->tx_ch_num)
+		q_idx = q_idx % priv->tx_ch_num;
+
+	return priv->txch[q_idx];
+}
+
 static inline int cpsw_tx_packet_submit(struct net_device *ndev,
-			struct cpsw_priv *priv, struct sk_buff *skb)
+					struct cpsw_priv *priv,
+					struct sk_buff *skb,
+					struct cpdma_chan *txch)
 {
 	if (!priv->data.dual_emac)
-		return cpdma_chan_submit(priv->txch, skb, skb->data,
-				  skb->len, 0);
+		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 0);
 
 	if (ndev == cpsw_get_slave_ndev(priv, 0))
-		return cpdma_chan_submit(priv->txch, skb, skb->data,
-				  skb->len, 1);
+		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 1);
 	else
-		return cpdma_chan_submit(priv->txch, skb, skb->data,
-				  skb->len, 2);
+		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 2);
 }
 
 static inline void cpsw_add_dual_emac_def_ale_entries(
@@ -1234,33 +1306,38 @@ static int cpsw_fill_rx_channels(struct net_device *ndev)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
 	struct sk_buff *skb;
-	int ch_buf_num;
-	int i, ret;
-
-	ch_buf_num = cpdma_chan_get_rx_buf_num(priv->rxch);
-	for (i = 0; i < ch_buf_num; i++) {
-		skb = __netdev_alloc_skb_ip_align(ndev,
-						  priv->rx_packet_max,
-						  GFP_KERNEL);
-		if (!skb) {
-			dev_err(priv->dev, "cannot allocate skb\n");
-			return -ENOMEM;
-		}
+	int ch, i, ret;
+
+	for (ch = 0; ch < priv->rx_ch_num; ch++) {
+		int ch_buf_num;
+
+		ch_buf_num = cpdma_chan_get_rx_buf_num(priv->rxch[ch]);
+		for (i = 0; i < ch_buf_num; i++) {
+			skb = __netdev_alloc_skb_ip_align(ndev,
+							  priv->rx_packet_max,
+							  GFP_KERNEL);
+			if (!skb) {
+				dev_err(priv->dev, "cannot allocate skb\n");
+				return -ENOMEM;
+			}
 
-		ret = cpdma_chan_submit(priv->rxch, skb, skb->data,
-					skb_tailroom(skb), 0);
-		if (ret < 0) {
-			dev_err(priv->dev,
-				"cannot submit skb to rx channel, error %d\n",
-				ret);
-			kfree_skb(skb);
-			return ret;
+			skb_set_queue_mapping(skb, ch);
+			ret = cpdma_chan_submit(priv->rxch[ch], skb, skb->data,
+						skb_tailroom(skb), 0);
+			if (ret < 0) {
+				dev_err(priv->dev,
+					"cannot submit skb to channel %d rx, error %d\n",
+					ch, ret);
+				kfree_skb(skb);
+				return ret;
+			}
 		}
-	}
 
-	cpsw_info(priv, ifup, "submitted %d rx descriptors\n", ch_buf_num);
+		cpsw_info(priv, ifup, "ch %d rx, submitted %d descriptors\n",
+			  ch, ch_buf_num);
+	}
 
-	return ch_buf_num;
+	return 0;
 }
 
 static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv)
@@ -1295,6 +1372,19 @@ static int cpsw_ndo_open(struct net_device *ndev)
 		cpsw_intr_disable(priv);
 	netif_carrier_off(ndev);
 
+	/* Notify the stack of the actual queue counts. */
+	ret = netif_set_real_num_tx_queues(ndev, priv->tx_ch_num);
+	if (ret) {
+		dev_err(priv->dev, "cannot set real number of tx queues\n");
+		goto err_cleanup;
+	}
+
+	ret = netif_set_real_num_rx_queues(ndev, priv->rx_ch_num);
+	if (ret) {
+		dev_err(priv->dev, "cannot set real number of rx queues\n");
+		goto err_cleanup;
+	}
+
 	reg = priv->version;
 
 	dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n",
@@ -1366,6 +1456,9 @@ static int cpsw_ndo_open(struct net_device *ndev)
 
 	if (priv->data.dual_emac)
 		priv->slaves[priv->emac_port].open_stat = true;
+
+	netif_tx_start_all_queues(ndev);
+
 	return 0;
 
 err_cleanup:
@@ -1381,7 +1474,7 @@ 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");
-	netif_stop_queue(priv->ndev);
+	netif_tx_stop_all_queues(priv->ndev);
 	netif_carrier_off(priv->ndev);
 
 	if (cpsw_common_res_usage_state(priv) <= 1) {
@@ -1405,6 +1498,8 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
 				       struct net_device *ndev)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
+	struct netdev_queue *txq;
+	struct cpdma_chan *txch;
 	int ret;
 
 	netif_trans_update(ndev);
@@ -1421,7 +1516,8 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
 
 	skb_tx_timestamp(skb);
 
-	ret = cpsw_tx_packet_submit(ndev, priv, skb);
+	txch = cpsw_tx_queue_mapping(priv, skb);
+	ret = cpsw_tx_packet_submit(ndev, priv, skb, txch);
 	if (unlikely(ret != 0)) {
 		cpsw_err(priv, tx_err, "desc submit failed\n");
 		goto fail;
@@ -1430,13 +1526,16 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
 	/* If there is no more tx desc left free then we need to
 	 * tell the kernel to stop sending us tx frames.
 	 */
-	if (unlikely(!cpdma_check_free_tx_desc(priv->txch)))
-		netif_stop_queue(ndev);
+	if (unlikely(!cpdma_check_free_tx_desc(txch))) {
+		txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb));
+		netif_tx_stop_queue(txq);
+	}
 
 	return NETDEV_TX_OK;
 fail:
 	ndev->stats.tx_dropped++;
-	netif_stop_queue(ndev);
+	txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb));
+	netif_tx_stop_queue(txq);
 	return NETDEV_TX_BUSY;
 }
 
@@ -1614,12 +1713,16 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
 static void cpsw_ndo_tx_timeout(struct net_device *ndev)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
+	int ch;
 
 	cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
 	ndev->stats.tx_errors++;
 	cpsw_intr_disable(priv);
-	cpdma_chan_stop(priv->txch);
-	cpdma_chan_start(priv->txch);
+	for (ch = 0; ch < priv->tx_ch_num; ch++) {
+		cpdma_chan_stop(priv->txch[ch]);
+		cpdma_chan_start(priv->txch[ch]);
+	}
+
 	cpsw_intr_enable(priv);
 }
 
@@ -1833,7 +1936,7 @@ static void cpsw_get_drvinfo(struct net_device *ndev,
 	struct cpsw_priv *priv = netdev_priv(ndev);
 
 	strlcpy(info->driver, "cpsw", sizeof(info->driver));
-	strlcpy(info->version, "1.0", sizeof(info->version));
+	strlcpy(info->version, "1.1", sizeof(info->version));
 	strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info));
 }
 
@@ -2181,7 +2284,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
 	struct cpsw_priv		*priv_sl2;
 	int ret = 0, i;
 
-	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+	ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES);
 	if (!ndev) {
 		dev_err(&pdev->dev, "cpsw: error allocating net_device\n");
 		return -ENOMEM;
@@ -2216,8 +2319,15 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
 	priv_sl2->wr_regs = priv->wr_regs;
 	priv_sl2->hw_stats = priv->hw_stats;
 	priv_sl2->dma = priv->dma;
-	priv_sl2->txch = priv->txch;
-	priv_sl2->rxch = priv->rxch;
+	priv_sl2->rx_ch_num = priv->rx_ch_num;
+	priv_sl2->tx_ch_num = priv->tx_ch_num;
+
+	for (i = 0; i < priv->tx_ch_num; i++)
+		priv_sl2->txch[i] = priv->txch[i];
+
+	for (i = 0; i < priv->rx_ch_num; i++)
+		priv_sl2->rxch[i] = priv->rxch[i];
+
 	priv_sl2->ale = priv->ale;
 	priv_sl2->emac_port = 1;
 	priv->slaves[1].ndev = ndev;
@@ -2298,7 +2408,7 @@ static int cpsw_probe(struct platform_device *pdev)
 	int ret = 0, i;
 	int irq;
 
-	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
+	ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES);
 	if (!ndev) {
 		dev_err(&pdev->dev, "error allocating net_device\n");
 		return -ENOMEM;
@@ -2339,6 +2449,8 @@ static int cpsw_probe(struct platform_device *pdev)
 		goto clean_runtime_disable_ret;
 	}
 	data = &priv->data;
+	priv->rx_ch_num = 1;
+	priv->tx_ch_num = 1;
 
 	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
 		memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
@@ -2463,12 +2575,12 @@ static int cpsw_probe(struct platform_device *pdev)
 		goto clean_runtime_disable_ret;
 	}
 
-	priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0),
-				       cpsw_tx_handler);
-	priv->rxch = cpdma_chan_create(priv->dma, rx_chan_num(0),
-				       cpsw_rx_handler);
+	priv->txch[0] = cpdma_chan_create(priv->dma, tx_chan_num(0),
+					  cpsw_tx_handler);
+	priv->rxch[0] = cpdma_chan_create(priv->dma, rx_chan_num(0),
+					  cpsw_rx_handler);
 
-	if (WARN_ON(!priv->txch || !priv->rxch)) {
+	if (WARN_ON(!priv->rxch[0] || !priv->txch[0])) {
 		dev_err(priv->dev, "error initializing dma channels\n");
 		ret = -ENOMEM;
 		goto clean_dma_ret;
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 2f4b571..a4b299d 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -481,6 +481,18 @@ void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
 }
 EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
 
+u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr)
+{
+	return dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED);
+}
+EXPORT_SYMBOL_GPL(cpdma_ctrl_rxchs_state);
+
+u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr)
+{
+	return dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED);
+}
+EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state);
+
 /**
  * cpdma_chan_split_pool - Splits ctrl pool between all channels.
  * Has to be called under ctlr lock
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index 0308b67..3ce91a1 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -96,6 +96,8 @@ 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, u32 value);
 int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
+u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr);
+u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr);
 bool cpdma_check_free_tx_desc(struct cpdma_chan *chan);
 
 enum cpdma_control {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/4] net: ethernet: ti: davinci_cpdma: move cpdma channel struct macroses to internals
  2016-06-30 19:04 [PATCH 0/4] net: ethernet: ti: cpsw: add multi-queue support Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support Ivan Khoronzhuk
@ 2016-06-30 19:04 ` Ivan Khoronzhuk
  2016-06-30 19:04 ` [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support Ivan Khoronzhuk
  3 siblings, 0 replies; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-06-30 19:04 UTC (permalink / raw)
  To: davem, netdev, mugunthanvnm
  Cc: grygorii.strashko, linux-kernel, linux-omap, nsekhar, Ivan Khoronzhuk

Better to move functions that works with channel internals to C file.
Currently it's not required for drivers to know rx or tx a channel
is, except create function. So correct "channel create" function, and
use all channel struct macroses only for internal use.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c          |  6 ++----
 drivers/net/ethernet/ti/davinci_cpdma.c | 13 +++++++++++--
 drivers/net/ethernet/ti/davinci_cpdma.h |  9 +--------
 drivers/net/ethernet/ti/davinci_emac.c  |  8 ++++----
 4 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 14d53eb..595ed56 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -2575,10 +2575,8 @@ static int cpsw_probe(struct platform_device *pdev)
 		goto clean_runtime_disable_ret;
 	}
 
-	priv->txch[0] = cpdma_chan_create(priv->dma, tx_chan_num(0),
-					  cpsw_tx_handler);
-	priv->rxch[0] = cpdma_chan_create(priv->dma, rx_chan_num(0),
-					  cpsw_rx_handler);
+	priv->txch[0] = cpdma_chan_create(priv->dma, 0, cpsw_tx_handler, 0);
+	priv->rxch[0] = cpdma_chan_create(priv->dma, 0, cpsw_rx_handler, 1);
 
 	if (WARN_ON(!priv->rxch[0] || !priv->txch[0])) {
 		dev_err(priv->dev, "error initializing dma channels\n");
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index a4b299d..d6c4967 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -126,6 +126,13 @@ struct cpdma_chan {
 	int	int_set, int_clear, td;
 };
 
+#define tx_chan_num(chan)	(chan)
+#define rx_chan_num(chan)	((chan) + CPDMA_MAX_CHANNELS)
+#define is_rx_chan(chan)	((chan)->chan_num >= CPDMA_MAX_CHANNELS)
+#define is_tx_chan(chan)	(!is_rx_chan(chan))
+#define __chan_linear(chan_num)	((chan_num) & (CPDMA_MAX_CHANNELS - 1))
+#define chan_linear(chan)	__chan_linear((chan)->chan_num)
+
 /* The following make access to common cpdma_ctlr params more readable */
 #define dmaregs		params.dmaregs
 #define num_chan	params.num_chan
@@ -520,12 +527,14 @@ static void cpdma_chan_split_pool(struct cpdma_ctlr *ctlr)
 }
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
-				     cpdma_handler_fn handler)
+				     cpdma_handler_fn handler, int rx_type)
 {
+	int offset = chan_num * 4;
 	struct cpdma_chan *chan;
-	int offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
 	unsigned long flags;
 
+	chan_num = rx_type ? rx_chan_num(chan_num) : tx_chan_num(chan_num);
+
 	if (__chan_linear(chan_num) >= ctlr->num_chan)
 		return NULL;
 
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index 3ce91a1..52db03a 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -17,13 +17,6 @@
 
 #define CPDMA_MAX_CHANNELS	BITS_PER_LONG
 
-#define tx_chan_num(chan)	(chan)
-#define rx_chan_num(chan)	((chan) + CPDMA_MAX_CHANNELS)
-#define is_rx_chan(chan)	((chan)->chan_num >= CPDMA_MAX_CHANNELS)
-#define is_tx_chan(chan)	(!is_rx_chan(chan))
-#define __chan_linear(chan_num)	((chan_num) & (CPDMA_MAX_CHANNELS - 1))
-#define chan_linear(chan)	__chan_linear((chan)->chan_num)
-
 #define CPDMA_RX_SOURCE_PORT(__status__)	((__status__ >> 16) & 0x7)
 
 #define CPDMA_EOI_RX_THRESH	0x0
@@ -80,7 +73,7 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr);
 int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr);
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
-				     cpdma_handler_fn handler);
+				     cpdma_handler_fn handler, int rx_type);
 int cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan);
 int cpdma_chan_destroy(struct cpdma_chan *chan);
 int cpdma_chan_start(struct cpdma_chan *chan);
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index f56d66e..1df0c89 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -2008,10 +2008,10 @@ static int davinci_emac_probe(struct platform_device *pdev)
 		goto no_pdata;
 	}
 
-	priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH),
-				       emac_tx_handler);
-	priv->rxchan = cpdma_chan_create(priv->dma, rx_chan_num(EMAC_DEF_RX_CH),
-				       emac_rx_handler);
+	priv->txchan = cpdma_chan_create(priv->dma, EMAC_DEF_TX_CH,
+					 emac_tx_handler, 0);
+	priv->rxchan = cpdma_chan_create(priv->dma, EMAC_DEF_RX_CH,
+					 emac_rx_handler, 1);
 	if (WARN_ON(!priv->txchan || !priv->rxchan)) {
 		rc = -ENOMEM;
 		goto no_cpdma_chan;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support
  2016-06-30 19:04 [PATCH 0/4] net: ethernet: ti: cpsw: add multi-queue support Ivan Khoronzhuk
                   ` (2 preceding siblings ...)
  2016-06-30 19:04 ` [PATCH 3/4] net: ethernet: ti: davinci_cpdma: move cpdma channel struct macroses to internals Ivan Khoronzhuk
@ 2016-06-30 19:04 ` Ivan Khoronzhuk
  2016-07-08 13:33   ` Grygorii Strashko
  3 siblings, 1 reply; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-06-30 19:04 UTC (permalink / raw)
  To: davem, netdev, mugunthanvnm
  Cc: grygorii.strashko, linux-kernel, linux-omap, nsekhar, Ivan Khoronzhuk

These ops allow to control number of channels driver is allowed to
work with. The maximum number of channels is 8 for rx and 8 for tx.
After this patch the following commands are possible:

$ ethtool -l eth0
$ ethtool -L eth0 rx 6 tx 6

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c | 188 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 188 insertions(+)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 595ed56..729b8be 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -740,6 +740,11 @@ static void cpsw_rx_handler(void *token, int len, int status)
 	}
 
 requeue:
+	if (netif_dormant(ndev)) {
+		dev_kfree_skb_any(new_skb);
+		return;
+	}
+
 	ch = priv->rxch[skb_get_queue_mapping(new_skb)];
 	ret = cpdma_chan_submit(ch, new_skb, new_skb->data,
 				skb_tailroom(new_skb), 0);
@@ -2077,6 +2082,187 @@ static void cpsw_ethtool_op_complete(struct net_device *ndev)
 		cpsw_err(priv, drv, "ethtool complete failed %d\n", ret);
 }
 
+static void cpsw_get_channels(struct net_device *dev,
+			      struct ethtool_channels *ch)
+{
+	struct cpsw_priv *priv = netdev_priv(dev);
+
+	ch->max_combined = 0;
+	ch->max_rx = CPSW_MAX_QUEUES;
+	ch->max_tx = CPSW_MAX_QUEUES;
+	ch->max_other = 0;
+	ch->other_count = 0;
+	ch->rx_count = priv->rx_ch_num;
+	ch->tx_count = priv->tx_ch_num;
+	ch->combined_count = 0;
+}
+
+static int cpsw_check_ch_settings(struct cpsw_priv *priv,
+				  struct ethtool_channels *ch)
+{
+	if (ch->combined_count)
+		return -EINVAL;
+
+	/* verify we have at least one channel in each direction */
+	if (!ch->rx_count || !ch->tx_count)
+		return -EINVAL;
+
+	if (ch->rx_count > priv->data.channels ||
+	    ch->tx_count > priv->data.channels)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void cpsw_sync_dual_ch_list(struct net_device *sdev,
+				   struct net_device *ddev)
+{
+	struct cpsw_priv *priv_s, *priv_d;
+	int i;
+
+	priv_s = netdev_priv(sdev);
+	priv_d = netdev_priv(ddev);
+
+	priv_d->rx_ch_num = priv_s->rx_ch_num;
+	priv_d->tx_ch_num = priv_s->tx_ch_num;
+
+	for (i = 0; i < priv_d->tx_ch_num; i++)
+		priv_d->txch[i] = priv_s->txch[i];
+	for (i = 0; i < priv_d->rx_ch_num; i++)
+		priv_d->rxch[i] = priv_s->rxch[i];
+}
+
+static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx)
+{
+	int (*poll)(struct napi_struct *, int);
+	void (*handler)(void *, int, int);
+	struct cpdma_chan **chan;
+	int *ch;
+	int ret;
+
+	if (rx) {
+		ch = &priv->rx_ch_num;
+		chan = priv->rxch;
+		handler = cpsw_rx_handler;
+		poll = cpsw_rx_poll;
+	} else {
+		ch = &priv->tx_ch_num;
+		chan = priv->txch;
+		handler = cpsw_tx_handler;
+		poll = cpsw_tx_poll;
+	}
+
+	while (*ch < ch_num) {
+		chan[*ch] = cpdma_chan_create(priv->dma, *ch, handler, rx);
+
+		if (IS_ERR(chan[*ch]))
+			return PTR_ERR(chan[*ch]);
+
+		if (!chan[*ch])
+			return -EINVAL;
+
+		dev_info(priv->dev, "created new %d %s channel\n", *ch,
+			 (rx ? "rx" : "tx"));
+		(*ch)++;
+	}
+
+	while (*ch > ch_num) {
+		int tch = *ch - 1;
+
+		ret = cpdma_chan_destroy(chan[tch]);
+		if (ret)
+			return ret;
+
+		dev_info(priv->dev, "destroyed %d %s channel\n", tch,
+			 (rx ? "rx" : "tx"));
+		(*ch)--;
+	}
+
+	return 0;
+}
+
+static int cpsw_update_channels(struct net_device *dev,
+				struct ethtool_channels *ch)
+{
+	struct cpsw_priv *priv;
+	int ret;
+
+	priv = netdev_priv(dev);
+
+	ret = cpsw_update_channels_res(priv, ch->rx_count, 1);
+	if (ret)
+		return ret;
+
+	ret = cpsw_update_channels_res(priv, ch->tx_count, 0);
+	if (ret)
+		return ret;
+
+	if (priv->data.dual_emac) {
+		int i;
+		/* mirror channels for another SL */
+		for (i = 0; i < priv->data.slaves; i++) {
+			if (priv->slaves[i].ndev == dev)
+				continue;
+
+			cpsw_sync_dual_ch_list(dev, priv->slaves[i].ndev);
+		}
+	}
+
+	return 0;
+}
+
+static int cpsw_set_channels(struct net_device *ndev,
+			     struct ethtool_channels *chs)
+{
+	struct cpsw_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	ret = cpsw_check_ch_settings(priv, chs);
+	if (ret < 0)
+		return ret;
+
+	if (netif_running(ndev)) {
+		netif_tx_stop_all_queues(ndev);
+		cpsw_intr_disable(priv);
+		netif_dormant_on(ndev);
+		cpdma_ctlr_stop(priv->dma);
+	}
+
+	ret = cpsw_update_channels(ndev, chs);
+	if (ret)
+		goto err;
+
+	if (netif_running(ndev)) {
+		/* inform stach about new count of queues */
+		ret = netif_set_real_num_tx_queues(ndev, priv->tx_ch_num);
+		if (ret) {
+			dev_err(priv->dev, "cannot set real number of tx queues\n");
+			goto err;
+		}
+
+		ret = netif_set_real_num_rx_queues(ndev, priv->rx_ch_num);
+		if (ret) {
+			dev_err(priv->dev, "cannot set real number of rx queues\n");
+			goto err;
+		}
+
+		netif_dormant_off(ndev);
+
+		if (cpsw_fill_rx_channels(ndev))
+			goto err;
+
+		cpdma_ctlr_start(priv->dma);
+		cpsw_intr_enable(priv);
+		netif_tx_start_all_queues(ndev);
+	}
+
+	return 0;
+err:
+	dev_err(priv->dev, "cannot update channels number, closing device\n");
+	dev_close(ndev);
+	return ret;
+}
+
 static const struct ethtool_ops cpsw_ethtool_ops = {
 	.get_drvinfo	= cpsw_get_drvinfo,
 	.get_msglevel	= cpsw_get_msglevel,
@@ -2098,6 +2284,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = {
 	.get_regs	= cpsw_get_regs,
 	.begin		= cpsw_ethtool_op_begin,
 	.complete	= cpsw_ethtool_op_complete,
+	.get_channels	= cpsw_get_channels,
+	.set_channels	= cpsw_set_channels,
 };
 
 static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels
  2016-06-30 19:04 ` [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels Ivan Khoronzhuk
@ 2016-07-01 20:35   ` David Miller
  2016-07-03  8:57     ` Ivan Khoronzhuk
  0 siblings, 1 reply; 11+ messages in thread
From: David Miller @ 2016-07-01 20:35 UTC (permalink / raw)
  To: ivan.khoronzhuk
  Cc: netdev, mugunthanvnm, grygorii.strashko, linux-kernel,
	linux-omap, nsekhar

From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
Date: Thu, 30 Jun 2016 22:04:35 +0300

> @@ -720,7 +763,7 @@ unlock_ret:
>  }
>  EXPORT_SYMBOL_GPL(cpdma_chan_submit);
>  
> -bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
> +inline bool cpdma_check_free_desc(struct cpdma_chan *chan)
>  {

This needs to be marked static.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels
  2016-07-01 20:35   ` David Miller
@ 2016-07-03  8:57     ` Ivan Khoronzhuk
  0 siblings, 0 replies; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-07-03  8:57 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, mugunthanvnm, grygorii.strashko, linux-kernel,
	linux-omap, nsekhar


On 01.07.16 23:35, David Miller wrote:
> From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
> Date: Thu, 30 Jun 2016 22:04:35 +0300
>
>> @@ -720,7 +763,7 @@ unlock_ret:
>>   }
>>   EXPORT_SYMBOL_GPL(cpdma_chan_submit);
>>
>> -bool cpdma_check_free_tx_desc(struct cpdma_chan *chan)
>> +inline bool cpdma_check_free_desc(struct cpdma_chan *chan)
>>   {
>
> This needs to be marked static.
>
Yes. Will correct it.

-- 
Regards,
Ivan Khoronzhuk

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support
  2016-06-30 19:04 ` [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support Ivan Khoronzhuk
@ 2016-07-08 13:12   ` Grygorii Strashko
  2016-07-19 13:24     ` Ivan Khoronzhuk
  0 siblings, 1 reply; 11+ messages in thread
From: Grygorii Strashko @ 2016-07-08 13:12 UTC (permalink / raw)
  To: Ivan Khoronzhuk, davem, netdev, mugunthanvnm
  Cc: linux-kernel, linux-omap, nsekhar

On 06/30/2016 10:04 PM, Ivan Khoronzhuk wrote:
> The cpsw h/w supports up to 8 tx and 8 rx channels.This patch adds
> multi-queue support to the driver. An ability to configure h/w
> shaper will be added with separate patch. Default shaper mode, as
> before, priority mode.
> 
> The poll function handles all unprocessed channels, till all of
> them are free, beginning from hi priority channel.
> 
> The statistic for every channel can be read with:
> ethtool -S ethX
> 
> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
> ---
>   drivers/net/ethernet/ti/cpsw.c          | 334 +++++++++++++++++++++-----------
>   drivers/net/ethernet/ti/davinci_cpdma.c |  12 ++
>   drivers/net/ethernet/ti/davinci_cpdma.h |   2 +
>   3 files changed, 237 insertions(+), 111 deletions(-)
> 
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index a713336..14d53eb 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
> @@ -140,6 +140,8 @@ do {								\
>   #define CPSW_CMINTMAX_INTVL	(1000 / CPSW_CMINTMIN_CNT)
>   #define CPSW_CMINTMIN_INTVL	((1000 / CPSW_CMINTMAX_CNT) + 1)
>   
> +#define CPSW_MAX_QUEUES		8
> +
>   #define cpsw_slave_index(priv)				\
>   		((priv->data.dual_emac) ? priv->emac_port :	\
>   		priv->data.active_slave)
> @@ -383,7 +385,8 @@ struct cpsw_priv {
>   	u8				mac_addr[ETH_ALEN];
>   	struct cpsw_slave		*slaves;
>   	struct cpdma_ctlr		*dma;
> -	struct cpdma_chan		*txch, *rxch;
> +	struct cpdma_chan		*txch[CPSW_MAX_QUEUES];
> +	struct cpdma_chan		*rxch[CPSW_MAX_QUEUES];
>   	struct cpsw_ale			*ale;
>   	bool				rx_pause;
>   	bool				tx_pause;
> @@ -395,6 +398,7 @@ struct cpsw_priv {
>   	u32 num_irqs;
>   	struct cpts *cpts;
>   	u32 emac_port;
> +	int rx_ch_num, tx_ch_num;
>   };
>   

[...]

>   
> @@ -989,26 +1024,50 @@ update_return:
>   
>   static int cpsw_get_sset_count(struct net_device *ndev, int sset)
>   {
> +	struct cpsw_priv *priv = netdev_priv(ndev);
> +
>   	switch (sset) {
>   	case ETH_SS_STATS:
> -		return CPSW_STATS_LEN;
> +		return (CPSW_STATS_COMMON_LEN +
> +		       (priv->rx_ch_num + priv->tx_ch_num) *
> +		       CPSW_STATS_CH_LEN);
>   	default:
>   		return -EOPNOTSUPP;
>   	}
>   }
>   
> +static void cpsw_add_ch_strings(u8 **p, int ch_num, int rx_dir)
> +{
> +	int ch_stats_len;
> +	int line;
> +	int i;
> +
> +	ch_stats_len = CPSW_STATS_CH_LEN * ch_num;
> +	for (i = 0; i < ch_stats_len; i++) {
> +		line = i % CPSW_STATS_CH_LEN;
> +		sprintf(*p, "%s DMA chan %d: %s", rx_dir ? "Rx" : "Tx",
> +			i / CPSW_STATS_CH_LEN,

snprintf(,ETH_GSTRING_LEN,) ?

> +			cpsw_gstrings_ch_stats[line].stat_string);
> +		*p += ETH_GSTRING_LEN;
> +	}
> +}
> +
>   static void cpsw_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
>   {
> +	struct cpsw_priv *priv = netdev_priv(ndev);
>   	u8 *p = data;
>   	int i;
>   
>   	switch (stringset) {
>   	case ETH_SS_STATS:
> -		for (i = 0; i < CPSW_STATS_LEN; i++) {
> +		for (i = 0; i < CPSW_STATS_COMMON_LEN; i++) {
>   			memcpy(p, cpsw_gstrings_stats[i].stat_string,
>   			       ETH_GSTRING_LEN);
>   			p += ETH_GSTRING_LEN;
>   		}
> +
> +		cpsw_add_ch_strings(&p, priv->rx_ch_num, 1);
> +		cpsw_add_ch_strings(&p, priv->tx_ch_num, 0);
>   		break;
>   	}
>   }
> @@ -1017,35 +1076,38 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev,
>   				    struct ethtool_stats *stats, u64 *data)
>   {
>   	struct cpsw_priv *priv = netdev_priv(ndev);
> -	struct cpdma_chan_stats rx_stats;
> -	struct cpdma_chan_stats tx_stats;
> -	u32 val;
> +	struct cpdma_chan_stats ch_stats;
> +	int i, l, ch, ret;
>   	u8 *p;
> -	int i;
> +
> +	ret = pm_runtime_get_sync(&priv->pdev->dev);
> +	if (ret < 0) {
> +		pm_runtime_put_noidle(&priv->pdev->dev);
> +		return;
> +	}

You probably need to base you work on top of net-next.git

>   
>   	/* Collect Davinci CPDMA stats for Rx and Tx Channel */
> -	cpdma_chan_get_stats(priv->rxch, &rx_stats);
> -	cpdma_chan_get_stats(priv->txch, &tx_stats);
> -
> -	for (i = 0; i < CPSW_STATS_LEN; i++) {
> -		switch (cpsw_gstrings_stats[i].type) {
> -		case CPSW_STATS:
> -			val = readl(priv->hw_stats +
> -				    cpsw_gstrings_stats[i].stat_offset);
> -			data[i] = val;
> -			break;
> +	for (l = 0; l < CPSW_STATS_COMMON_LEN; l++)
> +		data[l] = readl(priv->hw_stats +
> +				cpsw_gstrings_stats[l].stat_offset);
>   
> -		case CPDMA_RX_STATS:
> -			p = (u8 *)&rx_stats +
> -				cpsw_gstrings_stats[i].stat_offset;
> -			data[i] = *(u32 *)p;
> -			break;
> +	pm_runtime_put(&priv->pdev->dev);
>   
> -		case CPDMA_TX_STATS:
> -			p = (u8 *)&tx_stats +
> -				cpsw_gstrings_stats[i].stat_offset;
> -			data[i] = *(u32 *)p;
> -			break;
> +	for (ch = 0; ch < priv->rx_ch_num; ch++) {
> +		cpdma_chan_get_stats(priv->rxch[ch], &ch_stats);
> +		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
> +			p = (u8 *)&ch_stats +
> +				cpsw_gstrings_ch_stats[i].stat_offset;
> +			data[l] = *(u32 *)p;
> +		}
> +	}
> +
> +	for (ch = 0; ch < priv->tx_ch_num; ch++) {
> +		cpdma_chan_get_stats(priv->txch[ch], &ch_stats);
> +		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
> +			p = (u8 *)&ch_stats +
> +				cpsw_gstrings_ch_stats[i].stat_offset;
> +			data[l] = *(u32 *)p;
>   		}
>   	}

I think, it's better to do pm_runtime_put() here even if now cpdma does'n access
HW from cpdma_chan_get_stats it may change in fufture.
And it's not critical from PM point of view

>   }
> @@ -1065,19 +1127,29 @@ static int cpsw_common_res_usage_state(struct cpsw_priv *priv)
>   	return usage_count;
>   }
>   
> +static inline struct cpdma_chan *
> +cpsw_tx_queue_mapping(struct cpsw_priv *priv, struct sk_buff *skb)
> +{
> +	unsigned int q_idx = skb_get_queue_mapping(skb);
> +
> +	if (q_idx >= priv->tx_ch_num)
> +		q_idx = q_idx % priv->tx_ch_num;
> +
> +	return priv->txch[q_idx];
> +}
> +
>   static inline int cpsw_tx_packet_submit(struct net_device *ndev,
> -			struct cpsw_priv *priv, struct sk_buff *skb)
> +					struct cpsw_priv *priv,
> +					struct sk_buff *skb,
> +					struct cpdma_chan *txch)
>   {
>   	if (!priv->data.dual_emac)
> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
> -				  skb->len, 0);
> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 0);
>   
>   	if (ndev == cpsw_get_slave_ndev(priv, 0))
> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
> -				  skb->len, 1);
> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 1);
>   	else
> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
> -				  skb->len, 2);
> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 2);
>   }
>   
>   static inline void cpsw_add_dual_emac_def_ale_entries(

[...]

>   
> @@ -1614,12 +1713,16 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
>   static void cpsw_ndo_tx_timeout(struct net_device *ndev)
>   {
>   	struct cpsw_priv *priv = netdev_priv(ndev);
> +	int ch;
>   
>   	cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
>   	ndev->stats.tx_errors++;
>   	cpsw_intr_disable(priv);
> -	cpdma_chan_stop(priv->txch);
> -	cpdma_chan_start(priv->txch);
> +	for (ch = 0; ch < priv->tx_ch_num; ch++) {
> +		cpdma_chan_stop(priv->txch[ch]);
> +		cpdma_chan_start(priv->txch[ch]);
> +	}
> +
>   	cpsw_intr_enable(priv);
>   }
>   
> @@ -1833,7 +1936,7 @@ static void cpsw_get_drvinfo(struct net_device *ndev,
>   	struct cpsw_priv *priv = netdev_priv(ndev);
>   
>   	strlcpy(info->driver, "cpsw", sizeof(info->driver));
> -	strlcpy(info->version, "1.0", sizeof(info->version));
> +	strlcpy(info->version, "1.1", sizeof(info->version));

Not sure about this change, at least not as part of this patch.

>   	strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info));
>   }
>   
> @@ -2181,7 +2284,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
>   	struct cpsw_priv		*priv_sl2;
>   	int ret = 0, i;
>   
> -	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
> +	ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES);
>   	if (!ndev) {
>   		dev_err(&pdev->dev, "cpsw: error allocating net_device\n");
>   		return -ENOMEM;
> @@ -2216,8 +2319,15 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
>   	priv_sl2->wr_regs = priv->wr_regs;
>   	priv_sl2->hw_stats = priv->hw_stats;
>   	priv_sl2->dma = priv->dma;
> -	priv_sl2->txch = priv->txch;
> -	priv_sl2->rxch = priv->rxch;

[...]

>   
> -	if (WARN_ON(!priv->txch || !priv->rxch)) {
> +	if (WARN_ON(!priv->rxch[0] || !priv->txch[0])) {
>   		dev_err(priv->dev, "error initializing dma channels\n");
>   		ret = -ENOMEM;
>   		goto clean_dma_ret;
> diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
> index 2f4b571..a4b299d 100644
> --- a/drivers/net/ethernet/ti/davinci_cpdma.c
> +++ b/drivers/net/ethernet/ti/davinci_cpdma.c
> @@ -481,6 +481,18 @@ void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
>   }
>   EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
>   
> +u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr)
> +{
> +	return dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED);
> +}
> +EXPORT_SYMBOL_GPL(cpdma_ctrl_rxchs_state);
> +
> +u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr)
> +{
> +	return dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED);

TRM: CPDMA_INT TX INTERRUPT STATUS REGISTER (MASKED VALUE)

> +}
> +EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state);

This is interrupt status, so may be cpdma_ctrl_tx[rx]chs_intr_status() name
will be more appropriate?

> +
>   /**
>    * cpdma_chan_split_pool - Splits ctrl pool between all channels.
>    * Has to be called under ctlr lock
> diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
> index 0308b67..3ce91a1 100644
> --- a/drivers/net/ethernet/ti/davinci_cpdma.h
> +++ b/drivers/net/ethernet/ti/davinci_cpdma.h
> @@ -96,6 +96,8 @@ 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, u32 value);
>   int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
> +u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr);
> +u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr);
>   bool cpdma_check_free_tx_desc(struct cpdma_chan *chan);
>   
>   enum cpdma_control {
> 


-- 
regards,
-grygorii

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support
  2016-06-30 19:04 ` [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support Ivan Khoronzhuk
@ 2016-07-08 13:33   ` Grygorii Strashko
  2016-07-19 13:39     ` Ivan Khoronzhuk
  0 siblings, 1 reply; 11+ messages in thread
From: Grygorii Strashko @ 2016-07-08 13:33 UTC (permalink / raw)
  To: Ivan Khoronzhuk, davem, netdev, mugunthanvnm
  Cc: linux-kernel, linux-omap, nsekhar

On 06/30/2016 10:04 PM, Ivan Khoronzhuk wrote:
> These ops allow to control number of channels driver is allowed to
> work with. The maximum number of channels is 8 for rx and 8 for tx.
> After this patch the following commands are possible:
> 
> $ ethtool -l eth0
> $ ethtool -L eth0 rx 6 tx 6


Could you add some description here about switch vs dual_mac behavior? 

> 
> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
> ---
>   drivers/net/ethernet/ti/cpsw.c | 188 +++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 188 insertions(+)
> 
> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
> index 595ed56..729b8be 100644
> --- a/drivers/net/ethernet/ti/cpsw.c
> +++ b/drivers/net/ethernet/ti/cpsw.c
> @@ -740,6 +740,11 @@ static void cpsw_rx_handler(void *token, int len, int status)
>   	}
>   
>   requeue:
> +	if (netif_dormant(ndev)) {
> +		dev_kfree_skb_any(new_skb);
> +		return;
> +	}
> +
>   	ch = priv->rxch[skb_get_queue_mapping(new_skb)];
>   	ret = cpdma_chan_submit(ch, new_skb, new_skb->data,
>   				skb_tailroom(new_skb), 0);
> @@ -2077,6 +2082,187 @@ static void cpsw_ethtool_op_complete(struct net_device *ndev)
>   		cpsw_err(priv, drv, "ethtool complete failed %d\n", ret);
>   }
>   
> +static void cpsw_get_channels(struct net_device *dev,
> +			      struct ethtool_channels *ch)
> +{
> +	struct cpsw_priv *priv = netdev_priv(dev);
> +
> +	ch->max_combined = 0;
> +	ch->max_rx = CPSW_MAX_QUEUES;
> +	ch->max_tx = CPSW_MAX_QUEUES;
> +	ch->max_other = 0;
> +	ch->other_count = 0;
> +	ch->rx_count = priv->rx_ch_num;
> +	ch->tx_count = priv->tx_ch_num;
> +	ch->combined_count = 0;
> +}
> +
> +static int cpsw_check_ch_settings(struct cpsw_priv *priv,
> +				  struct ethtool_channels *ch)
> +{
> +	if (ch->combined_count)
> +		return -EINVAL;
> +
> +	/* verify we have at least one channel in each direction */
> +	if (!ch->rx_count || !ch->tx_count)
> +		return -EINVAL;
> +
> +	if (ch->rx_count > priv->data.channels ||
> +	    ch->tx_count > priv->data.channels)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void cpsw_sync_dual_ch_list(struct net_device *sdev,
> +				   struct net_device *ddev)
> +{
> +	struct cpsw_priv *priv_s, *priv_d;
> +	int i;
> +
> +	priv_s = netdev_priv(sdev);
> +	priv_d = netdev_priv(ddev);
> +
> +	priv_d->rx_ch_num = priv_s->rx_ch_num;
> +	priv_d->tx_ch_num = priv_s->tx_ch_num;
> +
> +	for (i = 0; i < priv_d->tx_ch_num; i++)
> +		priv_d->txch[i] = priv_s->txch[i];
> +	for (i = 0; i < priv_d->rx_ch_num; i++)
> +		priv_d->rxch[i] = priv_s->rxch[i];
> +}
> +
> +static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx)
> +{
> +	int (*poll)(struct napi_struct *, int);
> +	void (*handler)(void *, int, int);
> +	struct cpdma_chan **chan;
> +	int *ch;
> +	int ret;
> +
> +	if (rx) {
> +		ch = &priv->rx_ch_num;
> +		chan = priv->rxch;
> +		handler = cpsw_rx_handler;
> +		poll = cpsw_rx_poll;
> +	} else {
> +		ch = &priv->tx_ch_num;
> +		chan = priv->txch;
> +		handler = cpsw_tx_handler;
> +		poll = cpsw_tx_poll;
> +	}
> +
> +	while (*ch < ch_num) {
> +		chan[*ch] = cpdma_chan_create(priv->dma, *ch, handler, rx);
> +
> +		if (IS_ERR(chan[*ch]))
> +			return PTR_ERR(chan[*ch]);
> +
> +		if (!chan[*ch])
> +			return -EINVAL;
> +
> +		dev_info(priv->dev, "created new %d %s channel\n", *ch,
> +			 (rx ? "rx" : "tx"));
> +		(*ch)++;
> +	}
> +
> +	while (*ch > ch_num) {
> +		int tch = *ch - 1;

why tch? can you use more informative names

> +
> +		ret = cpdma_chan_destroy(chan[tch]);
> +		if (ret)
> +			return ret;
> +
> +		dev_info(priv->dev, "destroyed %d %s channel\n", tch,
> +			 (rx ? "rx" : "tx"));
> +		(*ch)--;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cpsw_update_channels(struct net_device *dev,
> +				struct ethtool_channels *ch)
> +{
> +	struct cpsw_priv *priv;
> +	int ret;
> +
> +	priv = netdev_priv(dev);
> +
> +	ret = cpsw_update_channels_res(priv, ch->rx_count, 1);
> +	if (ret)
> +		return ret;
> +
> +	ret = cpsw_update_channels_res(priv, ch->tx_count, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->data.dual_emac) {
> +		int i;
> +		/* mirror channels for another SL */
> +		for (i = 0; i < priv->data.slaves; i++) {
> +			if (priv->slaves[i].ndev == dev)
> +				continue;
> +
> +			cpsw_sync_dual_ch_list(dev, priv->slaves[i].ndev);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cpsw_set_channels(struct net_device *ndev,
> +			     struct ethtool_channels *chs)
> +{
> +	struct cpsw_priv *priv = netdev_priv(ndev);
> +	int ret;
> +
> +	ret = cpsw_check_ch_settings(priv, chs);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (netif_running(ndev)) {
> +		netif_tx_stop_all_queues(ndev);
> +		cpsw_intr_disable(priv);
> +		netif_dormant_on(ndev);
> +		cpdma_ctlr_stop(priv->dma);
> +	}
> +
> +	ret = cpsw_update_channels(ndev, chs);
> +	if (ret)
> +		goto err;
> +
> +	if (netif_running(ndev)) {
> +		/* inform stach about new count of queues */
> +		ret = netif_set_real_num_tx_queues(ndev, priv->tx_ch_num);
> +		if (ret) {
> +			dev_err(priv->dev, "cannot set real number of tx queues\n");
> +			goto err;
> +		}
> +
> +		ret = netif_set_real_num_rx_queues(ndev, priv->rx_ch_num);
> +		if (ret) {
> +			dev_err(priv->dev, "cannot set real number of rx queues\n");
> +			goto err;
> +		}

You're synchronizing slaves channels for dual_mac mode above, but updating netif real_num_XY_queues()
only for current netdev. Is it correct?

> +
> +		netif_dormant_off(ndev);
> +
> +		if (cpsw_fill_rx_channels(ndev))
> +			goto err;
> +
> +		cpdma_ctlr_start(priv->dma);
> +		cpsw_intr_enable(priv);
> +		netif_tx_start_all_queues(ndev);
> +	}
> +
> +	return 0;
> +err:
> +	dev_err(priv->dev, "cannot update channels number, closing device\n");
> +	dev_close(ndev);
> +	return ret;
> +}
> +
>   static const struct ethtool_ops cpsw_ethtool_ops = {
>   	.get_drvinfo	= cpsw_get_drvinfo,
>   	.get_msglevel	= cpsw_get_msglevel,
> @@ -2098,6 +2284,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = {
>   	.get_regs	= cpsw_get_regs,
>   	.begin		= cpsw_ethtool_op_begin,
>   	.complete	= cpsw_ethtool_op_complete,
> +	.get_channels	= cpsw_get_channels,
> +	.set_channels	= cpsw_set_channels,
>   };
>   
>   static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
> 


-- 
regards,
-grygorii

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support
  2016-07-08 13:12   ` Grygorii Strashko
@ 2016-07-19 13:24     ` Ivan Khoronzhuk
  0 siblings, 0 replies; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-07-19 13:24 UTC (permalink / raw)
  To: Grygorii Strashko, davem, netdev, mugunthanvnm
  Cc: linux-kernel, linux-omap, nsekhar



On 08.07.16 16:12, Grygorii Strashko wrote:
> On 06/30/2016 10:04 PM, Ivan Khoronzhuk wrote:
>> The cpsw h/w supports up to 8 tx and 8 rx channels.This patch adds
>> multi-queue support to the driver. An ability to configure h/w
>> shaper will be added with separate patch. Default shaper mode, as
>> before, priority mode.
>>
>> The poll function handles all unprocessed channels, till all of
>> them are free, beginning from hi priority channel.
>>
>> The statistic for every channel can be read with:
>> ethtool -S ethX
>>
>> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
>> ---
>>    drivers/net/ethernet/ti/cpsw.c          | 334 +++++++++++++++++++++-----------
>>    drivers/net/ethernet/ti/davinci_cpdma.c |  12 ++
>>    drivers/net/ethernet/ti/davinci_cpdma.h |   2 +
>>    3 files changed, 237 insertions(+), 111 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
>> index a713336..14d53eb 100644
>> --- a/drivers/net/ethernet/ti/cpsw.c
>> +++ b/drivers/net/ethernet/ti/cpsw.c
>> @@ -140,6 +140,8 @@ do {								\
>>    #define CPSW_CMINTMAX_INTVL	(1000 / CPSW_CMINTMIN_CNT)
>>    #define CPSW_CMINTMIN_INTVL	((1000 / CPSW_CMINTMAX_CNT) + 1)
>>
>> +#define CPSW_MAX_QUEUES		8
>> +
>>    #define cpsw_slave_index(priv)				\
>>    		((priv->data.dual_emac) ? priv->emac_port :	\
>>    		priv->data.active_slave)
>> @@ -383,7 +385,8 @@ struct cpsw_priv {
>>    	u8				mac_addr[ETH_ALEN];
>>    	struct cpsw_slave		*slaves;
>>    	struct cpdma_ctlr		*dma;
>> -	struct cpdma_chan		*txch, *rxch;
>> +	struct cpdma_chan		*txch[CPSW_MAX_QUEUES];
>> +	struct cpdma_chan		*rxch[CPSW_MAX_QUEUES];
>>    	struct cpsw_ale			*ale;
>>    	bool				rx_pause;
>>    	bool				tx_pause;
>> @@ -395,6 +398,7 @@ struct cpsw_priv {
>>    	u32 num_irqs;
>>    	struct cpts *cpts;
>>    	u32 emac_port;
>> +	int rx_ch_num, tx_ch_num;
>>    };
>>
>
> [...]
>
>>
>> @@ -989,26 +1024,50 @@ update_return:
>>
>>    static int cpsw_get_sset_count(struct net_device *ndev, int sset)
>>    {
>> +	struct cpsw_priv *priv = netdev_priv(ndev);
>> +
>>    	switch (sset) {
>>    	case ETH_SS_STATS:
>> -		return CPSW_STATS_LEN;
>> +		return (CPSW_STATS_COMMON_LEN +
>> +		       (priv->rx_ch_num + priv->tx_ch_num) *
>> +		       CPSW_STATS_CH_LEN);
>>    	default:
>>    		return -EOPNOTSUPP;
>>    	}
>>    }
>>
>> +static void cpsw_add_ch_strings(u8 **p, int ch_num, int rx_dir)
>> +{
>> +	int ch_stats_len;
>> +	int line;
>> +	int i;
>> +
>> +	ch_stats_len = CPSW_STATS_CH_LEN * ch_num;
>> +	for (i = 0; i < ch_stats_len; i++) {
>> +		line = i % CPSW_STATS_CH_LEN;
>> +		sprintf(*p, "%s DMA chan %d: %s", rx_dir ? "Rx" : "Tx",
>> +			i / CPSW_STATS_CH_LEN,
>
> snprintf(,ETH_GSTRING_LEN,) ?
It's number of channel.

>
>> +			cpsw_gstrings_ch_stats[line].stat_string);
>> +		*p += ETH_GSTRING_LEN;
>> +	}
>> +}
>> +
>>    static void cpsw_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
>>    {
>> +	struct cpsw_priv *priv = netdev_priv(ndev);
>>    	u8 *p = data;
>>    	int i;
>>
>>    	switch (stringset) {
>>    	case ETH_SS_STATS:
>> -		for (i = 0; i < CPSW_STATS_LEN; i++) {
>> +		for (i = 0; i < CPSW_STATS_COMMON_LEN; i++) {
>>    			memcpy(p, cpsw_gstrings_stats[i].stat_string,
>>    			       ETH_GSTRING_LEN);
>>    			p += ETH_GSTRING_LEN;
>>    		}
>> +
>> +		cpsw_add_ch_strings(&p, priv->rx_ch_num, 1);
>> +		cpsw_add_ch_strings(&p, priv->tx_ch_num, 0);
>>    		break;
>>    	}
>>    }
>> @@ -1017,35 +1076,38 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev,
>>    				    struct ethtool_stats *stats, u64 *data)
>>    {
>>    	struct cpsw_priv *priv = netdev_priv(ndev);
>> -	struct cpdma_chan_stats rx_stats;
>> -	struct cpdma_chan_stats tx_stats;
>> -	u32 val;
>> +	struct cpdma_chan_stats ch_stats;
>> +	int i, l, ch, ret;
>>    	u8 *p;
>> -	int i;
>> +
>> +	ret = pm_runtime_get_sync(&priv->pdev->dev);
>> +	if (ret < 0) {
>> +		pm_runtime_put_noidle(&priv->pdev->dev);
>> +		return;
>> +	}
>
> You probably need to base you work on top of net-next.git
Yep. Will correct.

>
>>
>>    	/* Collect Davinci CPDMA stats for Rx and Tx Channel */
>> -	cpdma_chan_get_stats(priv->rxch, &rx_stats);
>> -	cpdma_chan_get_stats(priv->txch, &tx_stats);
>> -
>> -	for (i = 0; i < CPSW_STATS_LEN; i++) {
>> -		switch (cpsw_gstrings_stats[i].type) {
>> -		case CPSW_STATS:
>> -			val = readl(priv->hw_stats +
>> -				    cpsw_gstrings_stats[i].stat_offset);
>> -			data[i] = val;
>> -			break;
>> +	for (l = 0; l < CPSW_STATS_COMMON_LEN; l++)
>> +		data[l] = readl(priv->hw_stats +
>> +				cpsw_gstrings_stats[l].stat_offset);
>>
>> -		case CPDMA_RX_STATS:
>> -			p = (u8 *)&rx_stats +
>> -				cpsw_gstrings_stats[i].stat_offset;
>> -			data[i] = *(u32 *)p;
>> -			break;
>> +	pm_runtime_put(&priv->pdev->dev);
>>
>> -		case CPDMA_TX_STATS:
>> -			p = (u8 *)&tx_stats +
>> -				cpsw_gstrings_stats[i].stat_offset;
>> -			data[i] = *(u32 *)p;
>> -			break;
>> +	for (ch = 0; ch < priv->rx_ch_num; ch++) {
>> +		cpdma_chan_get_stats(priv->rxch[ch], &ch_stats);
>> +		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
>> +			p = (u8 *)&ch_stats +
>> +				cpsw_gstrings_ch_stats[i].stat_offset;
>> +			data[l] = *(u32 *)p;
>> +		}
>> +	}
>> +
>> +	for (ch = 0; ch < priv->tx_ch_num; ch++) {
>> +		cpdma_chan_get_stats(priv->txch[ch], &ch_stats);
>> +		for (i = 0; i < CPSW_STATS_CH_LEN; i++, l++) {
>> +			p = (u8 *)&ch_stats +
>> +				cpsw_gstrings_ch_stats[i].stat_offset;
>> +			data[l] = *(u32 *)p;
>>    		}
>>    	}
>
> I think, it's better to do pm_runtime_put() here even if now cpdma does'n access
> HW from cpdma_chan_get_stats it may change in fufture.
> And it's not critical from PM point of view
This part is not going to access h/w.
The function is ethtool op, and after adding pm_runtime in begin/end ops no need in it.
Will correct it in next version.

>
>>    }
>> @@ -1065,19 +1127,29 @@ static int cpsw_common_res_usage_state(struct cpsw_priv *priv)
>>    	return usage_count;
>>    }
>>
>> +static inline struct cpdma_chan *
>> +cpsw_tx_queue_mapping(struct cpsw_priv *priv, struct sk_buff *skb)
>> +{
>> +	unsigned int q_idx = skb_get_queue_mapping(skb);
>> +
>> +	if (q_idx >= priv->tx_ch_num)
>> +		q_idx = q_idx % priv->tx_ch_num;
>> +
>> +	return priv->txch[q_idx];
>> +}
>> +
>>    static inline int cpsw_tx_packet_submit(struct net_device *ndev,
>> -			struct cpsw_priv *priv, struct sk_buff *skb)
>> +					struct cpsw_priv *priv,
>> +					struct sk_buff *skb,
>> +					struct cpdma_chan *txch)
>>    {
>>    	if (!priv->data.dual_emac)
>> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
>> -				  skb->len, 0);
>> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 0);
>>
>>    	if (ndev == cpsw_get_slave_ndev(priv, 0))
>> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
>> -				  skb->len, 1);
>> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 1);
>>    	else
>> -		return cpdma_chan_submit(priv->txch, skb, skb->data,
>> -				  skb->len, 2);
>> +		return cpdma_chan_submit(txch, skb, skb->data, skb->len, 2);
>>    }
>>
>>    static inline void cpsw_add_dual_emac_def_ale_entries(
>
> [...]
>
>>
>> @@ -1614,12 +1713,16 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
>>    static void cpsw_ndo_tx_timeout(struct net_device *ndev)
>>    {
>>    	struct cpsw_priv *priv = netdev_priv(ndev);
>> +	int ch;
>>
>>    	cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
>>    	ndev->stats.tx_errors++;
>>    	cpsw_intr_disable(priv);
>> -	cpdma_chan_stop(priv->txch);
>> -	cpdma_chan_start(priv->txch);
>> +	for (ch = 0; ch < priv->tx_ch_num; ch++) {
>> +		cpdma_chan_stop(priv->txch[ch]);
>> +		cpdma_chan_start(priv->txch[ch]);
>> +	}
>> +
>>    	cpsw_intr_enable(priv);
>>    }
>>
>> @@ -1833,7 +1936,7 @@ static void cpsw_get_drvinfo(struct net_device *ndev,
>>    	struct cpsw_priv *priv = netdev_priv(ndev);
>>
>>    	strlcpy(info->driver, "cpsw", sizeof(info->driver));
>> -	strlcpy(info->version, "1.0", sizeof(info->version));
>> +	strlcpy(info->version, "1.1", sizeof(info->version));
>
> Not sure about this change, at least not as part of this patch.
The possibilities of the driver are changed. Now it's multichannel.
If you think it's not needed I can drop it otherwise will send it with separate patch.

>
>>    	strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info));
>>    }
>>
>> @@ -2181,7 +2284,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
>>    	struct cpsw_priv		*priv_sl2;
>>    	int ret = 0, i;
>>
>> -	ndev = alloc_etherdev(sizeof(struct cpsw_priv));
>> +	ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES);
>>    	if (!ndev) {
>>    		dev_err(&pdev->dev, "cpsw: error allocating net_device\n");
>>    		return -ENOMEM;
>> @@ -2216,8 +2319,15 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
>>    	priv_sl2->wr_regs = priv->wr_regs;
>>    	priv_sl2->hw_stats = priv->hw_stats;
>>    	priv_sl2->dma = priv->dma;
>> -	priv_sl2->txch = priv->txch;
>> -	priv_sl2->rxch = priv->rxch;
>
> [...]
>
>>
>> -	if (WARN_ON(!priv->txch || !priv->rxch)) {
>> +	if (WARN_ON(!priv->rxch[0] || !priv->txch[0])) {
>>    		dev_err(priv->dev, "error initializing dma channels\n");
>>    		ret = -ENOMEM;
>>    		goto clean_dma_ret;
>> diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
>> index 2f4b571..a4b299d 100644
>> --- a/drivers/net/ethernet/ti/davinci_cpdma.c
>> +++ b/drivers/net/ethernet/ti/davinci_cpdma.c
>> @@ -481,6 +481,18 @@ void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
>>    }
>>    EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
>>
>> +u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr)
>> +{
>> +	return dma_reg_read(ctlr, CPDMA_RXINTSTATMASKED);
>> +}
>> +EXPORT_SYMBOL_GPL(cpdma_ctrl_rxchs_state);
>> +
>> +u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr)
>> +{
>> +	return dma_reg_read(ctlr, CPDMA_TXINTSTATMASKED);
>
> TRM: CPDMA_INT TX INTERRUPT STATUS REGISTER (MASKED VALUE)
>
>> +}
>> +EXPORT_SYMBOL_GPL(cpdma_ctrl_txchs_state);
>
> This is interrupt status, so may be cpdma_ctrl_tx[rx]chs_intr_status() name
> will be more appropriate?
Not sure. It's not exacly interrupt status as can give status of channel even if the intertupt was disabled.
And it can be used w/o interrupt at all, just polling. The interrupt status can be read with another register.
This one continue to mirror descriptors presence for channels till they are all correctly handled.

>
>> +
>>    /**
>>     * cpdma_chan_split_pool - Splits ctrl pool between all channels.
>>     * Has to be called under ctlr lock
>> diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
>> index 0308b67..3ce91a1 100644
>> --- a/drivers/net/ethernet/ti/davinci_cpdma.h
>> +++ b/drivers/net/ethernet/ti/davinci_cpdma.h
>> @@ -96,6 +96,8 @@ 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, u32 value);
>>    int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
>> +u32 cpdma_ctrl_rxchs_state(struct cpdma_ctlr *ctlr);
>> +u32 cpdma_ctrl_txchs_state(struct cpdma_ctlr *ctlr);
>>    bool cpdma_check_free_tx_desc(struct cpdma_chan *chan);
>>
>>    enum cpdma_control {
>>
>
>

-- 
Regards,
Ivan Khoronzhuk

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support
  2016-07-08 13:33   ` Grygorii Strashko
@ 2016-07-19 13:39     ` Ivan Khoronzhuk
  0 siblings, 0 replies; 11+ messages in thread
From: Ivan Khoronzhuk @ 2016-07-19 13:39 UTC (permalink / raw)
  To: Grygorii Strashko, davem, netdev, mugunthanvnm
  Cc: linux-kernel, linux-omap, nsekhar



On 08.07.16 16:33, Grygorii Strashko wrote:
> On 06/30/2016 10:04 PM, Ivan Khoronzhuk wrote:
>> These ops allow to control number of channels driver is allowed to
>> work with. The maximum number of channels is 8 for rx and 8 for tx.
>> After this patch the following commands are possible:
>>
>> $ ethtool -l eth0
>> $ ethtool -L eth0 rx 6 tx 6
>
>
> Could you add some description here about switch vs dual_mac behavior?
I will add, but, maybe here should be some changes + info about how to share channels
between two interfaces. Would you like me to add some concrete description?

>
>>
>> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
>> ---
>>    drivers/net/ethernet/ti/cpsw.c | 188 +++++++++++++++++++++++++++++++++++++++++
>>    1 file changed, 188 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
>> index 595ed56..729b8be 100644
>> --- a/drivers/net/ethernet/ti/cpsw.c
>> +++ b/drivers/net/ethernet/ti/cpsw.c
>> @@ -740,6 +740,11 @@ static void cpsw_rx_handler(void *token, int len, int status)
>>    	}
>>
>>    requeue:
>> +	if (netif_dormant(ndev)) {
>> +		dev_kfree_skb_any(new_skb);
>> +		return;
>> +	}
>> +
>>    	ch = priv->rxch[skb_get_queue_mapping(new_skb)];
>>    	ret = cpdma_chan_submit(ch, new_skb, new_skb->data,
>>    				skb_tailroom(new_skb), 0);
>> @@ -2077,6 +2082,187 @@ static void cpsw_ethtool_op_complete(struct net_device *ndev)
>>    		cpsw_err(priv, drv, "ethtool complete failed %d\n", ret);
>>    }
>>
>> +static void cpsw_get_channels(struct net_device *dev,
>> +			      struct ethtool_channels *ch)
>> +{
>> +	struct cpsw_priv *priv = netdev_priv(dev);
>> +
>> +	ch->max_combined = 0;
>> +	ch->max_rx = CPSW_MAX_QUEUES;
>> +	ch->max_tx = CPSW_MAX_QUEUES;
>> +	ch->max_other = 0;
>> +	ch->other_count = 0;
>> +	ch->rx_count = priv->rx_ch_num;
>> +	ch->tx_count = priv->tx_ch_num;
>> +	ch->combined_count = 0;
>> +}
>> +
>> +static int cpsw_check_ch_settings(struct cpsw_priv *priv,
>> +				  struct ethtool_channels *ch)
>> +{
>> +	if (ch->combined_count)
>> +		return -EINVAL;
>> +
>> +	/* verify we have at least one channel in each direction */
>> +	if (!ch->rx_count || !ch->tx_count)
>> +		return -EINVAL;
>> +
>> +	if (ch->rx_count > priv->data.channels ||
>> +	    ch->tx_count > priv->data.channels)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static void cpsw_sync_dual_ch_list(struct net_device *sdev,
>> +				   struct net_device *ddev)
>> +{
>> +	struct cpsw_priv *priv_s, *priv_d;
>> +	int i;
>> +
>> +	priv_s = netdev_priv(sdev);
>> +	priv_d = netdev_priv(ddev);
>> +
>> +	priv_d->rx_ch_num = priv_s->rx_ch_num;
>> +	priv_d->tx_ch_num = priv_s->tx_ch_num;
>> +
>> +	for (i = 0; i < priv_d->tx_ch_num; i++)
>> +		priv_d->txch[i] = priv_s->txch[i];
>> +	for (i = 0; i < priv_d->rx_ch_num; i++)
>> +		priv_d->rxch[i] = priv_s->rxch[i];
>> +}
>> +
>> +static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx)
>> +{
>> +	int (*poll)(struct napi_struct *, int);
>> +	void (*handler)(void *, int, int);
>> +	struct cpdma_chan **chan;
>> +	int *ch;
>> +	int ret;
>> +
>> +	if (rx) {
>> +		ch = &priv->rx_ch_num;
>> +		chan = priv->rxch;
>> +		handler = cpsw_rx_handler;
>> +		poll = cpsw_rx_poll;
>> +	} else {
>> +		ch = &priv->tx_ch_num;
>> +		chan = priv->txch;
>> +		handler = cpsw_tx_handler;
>> +		poll = cpsw_tx_poll;
>> +	}
>> +
>> +	while (*ch < ch_num) {
>> +		chan[*ch] = cpdma_chan_create(priv->dma, *ch, handler, rx);
>> +
>> +		if (IS_ERR(chan[*ch]))
>> +			return PTR_ERR(chan[*ch]);
>> +
>> +		if (!chan[*ch])
>> +			return -EINVAL;
>> +
>> +		dev_info(priv->dev, "created new %d %s channel\n", *ch,
>> +			 (rx ? "rx" : "tx"));
>> +		(*ch)++;
>> +	}
>> +
>> +	while (*ch > ch_num) {
>> +		int tch = *ch - 1;
>
> why tch? can you use more informative names
Yep. Will correct.

>
>> +
>> +		ret = cpdma_chan_destroy(chan[tch]);
>> +		if (ret)
>> +			return ret;
>> +
>> +		dev_info(priv->dev, "destroyed %d %s channel\n", tch,
>> +			 (rx ? "rx" : "tx"));
>> +		(*ch)--;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cpsw_update_channels(struct net_device *dev,
>> +				struct ethtool_channels *ch)
>> +{
>> +	struct cpsw_priv *priv;
>> +	int ret;
>> +
>> +	priv = netdev_priv(dev);
>> +
>> +	ret = cpsw_update_channels_res(priv, ch->rx_count, 1);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = cpsw_update_channels_res(priv, ch->tx_count, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (priv->data.dual_emac) {
>> +		int i;
>> +		/* mirror channels for another SL */
>> +		for (i = 0; i < priv->data.slaves; i++) {
>> +			if (priv->slaves[i].ndev == dev)
>> +				continue;
>> +
>> +			cpsw_sync_dual_ch_list(dev, priv->slaves[i].ndev);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cpsw_set_channels(struct net_device *ndev,
>> +			     struct ethtool_channels *chs)
>> +{
>> +	struct cpsw_priv *priv = netdev_priv(ndev);
>> +	int ret;
>> +
>> +	ret = cpsw_check_ch_settings(priv, chs);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (netif_running(ndev)) {
>> +		netif_tx_stop_all_queues(ndev);
>> +		cpsw_intr_disable(priv);
>> +		netif_dormant_on(ndev);
>> +		cpdma_ctlr_stop(priv->dma);
>> +	}
>> +
>> +	ret = cpsw_update_channels(ndev, chs);
>> +	if (ret)
>> +		goto err;
>> +
>> +	if (netif_running(ndev)) {
>> +		/* inform stach about new count of queues */
>> +		ret = netif_set_real_num_tx_queues(ndev, priv->tx_ch_num);
>> +		if (ret) {
>> +			dev_err(priv->dev, "cannot set real number of tx queues\n");
>> +			goto err;
>> +		}
>> +
>> +		ret = netif_set_real_num_rx_queues(ndev, priv->rx_ch_num);
>> +		if (ret) {
>> +			dev_err(priv->dev, "cannot set real number of rx queues\n");
>> +			goto err;
>> +		}
>
> You're synchronizing slaves channels for dual_mac mode above, but updating netif real_num_XY_queues()
> only for current netdev. Is it correct?
It seems to be an issue. I'm going to change behavior for dual mode a little, and will keep it in mind.

>
>> +
>> +		netif_dormant_off(ndev);
>> +
>> +		if (cpsw_fill_rx_channels(ndev))
>> +			goto err;
>> +
>> +		cpdma_ctlr_start(priv->dma);
>> +		cpsw_intr_enable(priv);
>> +		netif_tx_start_all_queues(ndev);
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	dev_err(priv->dev, "cannot update channels number, closing device\n");
>> +	dev_close(ndev);
>> +	return ret;
>> +}
>> +
>>    static const struct ethtool_ops cpsw_ethtool_ops = {
>>    	.get_drvinfo	= cpsw_get_drvinfo,
>>    	.get_msglevel	= cpsw_get_msglevel,
>> @@ -2098,6 +2284,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = {
>>    	.get_regs	= cpsw_get_regs,
>>    	.begin		= cpsw_ethtool_op_begin,
>>    	.complete	= cpsw_ethtool_op_complete,
>> +	.get_channels	= cpsw_get_channels,
>> +	.set_channels	= cpsw_set_channels,
>>    };
>>
>>    static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
>>
>
>

-- 
Regards,
Ivan Khoronzhuk

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-07-19 13:39 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-30 19:04 [PATCH 0/4] net: ethernet: ti: cpsw: add multi-queue support Ivan Khoronzhuk
2016-06-30 19:04 ` [PATCH 1/4] net: ethernet: ti: davinci_cpdma: split descs num between all channels Ivan Khoronzhuk
2016-07-01 20:35   ` David Miller
2016-07-03  8:57     ` Ivan Khoronzhuk
2016-06-30 19:04 ` [PATCH 2/4] net: ethernet: ti: cpsw: add multi queue support Ivan Khoronzhuk
2016-07-08 13:12   ` Grygorii Strashko
2016-07-19 13:24     ` Ivan Khoronzhuk
2016-06-30 19:04 ` [PATCH 3/4] net: ethernet: ti: davinci_cpdma: move cpdma channel struct macroses to internals Ivan Khoronzhuk
2016-06-30 19:04 ` [PATCH 4/4] net: ethernet: ti: cpsw: add ethtool channels support Ivan Khoronzhuk
2016-07-08 13:33   ` Grygorii Strashko
2016-07-19 13:39     ` Ivan Khoronzhuk

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).