]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - ti-linux-kernel/ti-linux-kernel-next.git/commitdiff
net: ethernet: ti: am65-cpsw-nuss: use round robin mode for cppi tx queues
authorGrygorii Strashko <grygorii.strashko@ti.com>
Sat, 22 Sep 2018 02:07:16 +0000 (21:07 -0500)
committerSekhar Nori <nsekhar@ti.com>
Tue, 16 Oct 2018 09:39:23 +0000 (15:09 +0530)
By default Linux network stack does not assume the multiqueue network
device works in fixed priority mode. As result, it will try to dispatch
packets between HW queues fairly - basing on skb hash (and XPS map if
enabled). So, by default, MCU CPSW TX statistic looks like below:

     tx_pri0: 14
     tx_pri1: 2
     tx_pri2: 0
     tx_pri3: 12
     tx_pri4: 12
     tx_pri5: 0
     tx_pri6: 306127
     tx_pri7: 0

From another side, MCU CPSW HW processes TX queues in fixed priority mode
(7 - high prio) and this is does not corresponds to what Linux network
stack expects as some packets might be delayed.

To fix this switch MCU CPSW CPPI interface to Round Robin mode
(CPPI_P0_Pri_Ctl.p0_rx_ptype) by default, and implement the same in driver
for TX completion queues processing. Also, introduce netdev private flag
"p0-rx-ptype-rrobin", so switch between Round Robin and Fixed priority
modes:

 # ethtool --show-priv-flags eth0
 Private flags for eth0:
 p0-rx-ptype-rrobin: on

 # ethtool --set-priv-flags eth0 p0-rx-ptype-rrobin off

Fixed priority can be used with multiq, prio and mqprio qdisc's.

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
drivers/net/ethernet/ti/am65-cpsw-ethtool.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h

index 453d457cd4a37ebce71e9895b121af4a97fe0506..37f626e09fe477ffca6ab93b1a17b3939f6aebb3 100644 (file)
@@ -353,6 +353,12 @@ static const struct am65_cpsw_ethtool_stat am65_slave_stats[] = {
        AM65_CPSW_STATS(, tx_pri7_drop_bcnt),
 };
 
+/* Ethtool priv_flags */
+static const char am65_cpsw_ethtool_priv_flags[][ETH_GSTRING_LEN] = {
+#define        AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN       BIT(0)
+       "p0-rx-ptype-rrobin",
+};
+
 static int am65_cpsw_ethtool_op_begin(struct net_device *ndev)
 {
        struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
@@ -597,6 +603,8 @@ static int am65_cpsw_get_sset_count(struct net_device *ndev, int sset)
                        return ARRAY_SIZE(am65_host_stats);
                else
                        return ARRAY_SIZE(am65_slave_stats);
+       case ETH_SS_PRIV_FLAGS:
+               return ARRAY_SIZE(am65_cpsw_ethtool_priv_flags);
        default:
                return -EOPNOTSUPP;
        }
@@ -642,6 +650,15 @@ static void am65_cpsw_get_strings(struct net_device *ndev,
                        }
                }
                break;
+       case ETH_SS_PRIV_FLAGS:
+               num_stats = ARRAY_SIZE(am65_cpsw_ethtool_priv_flags);
+
+               for (i = 0; i < num_stats; i++) {
+                       memcpy(p, am65_cpsw_ethtool_priv_flags[i],
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+               break;
        }
 }
 
@@ -703,6 +720,28 @@ static int am65_cpsw_get_ethtool_ts_info(struct net_device *ndev,
 #define am65_cpsw_get_ethtool_ts_info ethtool_op_get_ts_info
 #endif
 
+static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+       u32 priv_flags = 0;
+
+       if (common->pf_p0_rx_ptype_rrobin)
+               priv_flags |= AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN;
+
+       return priv_flags;
+}
+
+static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+
+       common->pf_p0_rx_ptype_rrobin =
+                       !!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN);
+       am65_cpsw_nuss_set_p0_ptype(common);
+
+       return 0;
+}
+
 const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
        .begin                  = am65_cpsw_ethtool_op_begin,
        .complete               = am65_cpsw_ethtool_op_complete,
@@ -717,6 +756,8 @@ const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
        .get_strings            = am65_cpsw_get_strings,
        .get_ethtool_stats      = am65_cpsw_get_ethtool_stats,
        .get_ts_info            = am65_cpsw_get_ethtool_ts_info,
+       .get_priv_flags         = am65_cpsw_get_ethtool_priv_flags,
+       .set_priv_flags         = am65_cpsw_set_ethtool_priv_flags,
 
        .get_link               = ethtool_op_get_link,
        .get_link_ksettings     = am65_cpsw_get_link_ksettings,
index 31ddb527c7656ddb30268e5d2951c26460bf9a7d..da401d119a18ac18bf91973db69173d7b727de19 100644 (file)
@@ -59,6 +59,9 @@
 
 #define AM65_CPSW_P0_REG_CTL                   0x004
 #define AM65_CPSW_PORT0_REG_FLOW_ID_OFFSET     0x008
+
+#define AM65_CPSW_PORT_REG_PRI_CTL             0x01c
+#define AM65_CPSW_PORT_REG_RX_PRI_MAP          0x020
 #define AM65_CPSW_PORT_REG_RX_MAXLEN           0x024
 
 #define AM65_CPSW_PORTN_REG_SA_L               0x308
@@ -76,6 +79,9 @@
 /* AM65_CPSW_P0_REG_CTL */
 #define AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN    BIT(0)
 
+/* AM65_CPSW_PORT_REG_PRI_CTL */
+#define AM65_CPSW_PORT_REG_PRI_CTL_RX_PTYPE_RROBIN     BIT(8)
+
 /* AM65_CPSW_PN_TS_CTL register fields */
 #define AM65_CPSW_PN_TS_CTL_TX_ANX_F_EN                BIT(4)
 #define AM65_CPSW_PN_TS_CTL_TX_VLAN_LT1_EN     BIT(5)
@@ -315,12 +321,34 @@ static void am65_cpsw_nuss_ndo_slave_set_rx_mode(struct net_device *ndev)
 
 static void am65_cpsw_nuss_ndo_host_tx_timeout(struct net_device *ndev)
 {
-       struct am65_cpsw_port *host_p = am65_ndev_to_port(ndev);
-
-       netdev_err(host_p->ndev, "transmit timed out tx\n");
-
-       netif_trans_update(ndev);
-       netif_tx_wake_all_queues(ndev);
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+       int ch;
+
+       /* process every txq*/
+       for (ch = 0; ch < common->tx_ch_num; ch++) {
+               struct am65_cpsw_tx_chn *tx_chn;
+               struct netdev_queue *netif_txq;
+               unsigned long trans_start;
+
+               netif_txq = netdev_get_tx_queue(ndev, ch);
+               trans_start = netif_txq->trans_start;
+               if (netif_xmit_stopped(netif_txq) &&
+                   time_after(jiffies, (trans_start + ndev->watchdog_timeo))) {
+                       tx_chn = &common->tx_chns[ch];
+                       netdev_err(ndev, "txq:%d DRV_XOFF:%d tmo:%u dql_avail:%d free_desc:%zu\n",
+                                  ch,
+                                  netif_tx_queue_stopped(netif_txq),
+                                  jiffies_to_msecs(jiffies - trans_start),
+                                  dql_avail(&netif_txq->dql),
+                                  k3_knav_pool_avail(tx_chn->desc_pool));
+
+                       if (netif_tx_queue_stopped(netif_txq)) {
+                               /* try recover if stopped by us */
+                               txq_trans_update(netif_txq);
+                               netif_tx_wake_queue(netif_txq);
+                       }
+               }
+       }
 }
 
 static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common,
@@ -358,6 +386,30 @@ static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common,
        return k3_nav_udmax_push_rx_chn(rx_chn->rx_chn, 0, desc_rx, desc_dma);
 }
 
+void am65_cpsw_nuss_set_p0_ptype(struct am65_cpsw_common *common)
+{
+       struct am65_cpsw_port *host_p = am65_common_get_host_port(common);
+       u32 val, pri_map;
+
+       /* P0 set Receive Priority Type */
+       val = readl(host_p->port_base + AM65_CPSW_PORT_REG_PRI_CTL);
+
+       if (common->pf_p0_rx_ptype_rrobin) {
+               val |= AM65_CPSW_PORT_REG_PRI_CTL_RX_PTYPE_RROBIN;
+               /* Enet Ports fifos works in fixed priority mode only, so
+                * reset P0_Rx_Pri_Map so all packet will go in Enet fifo 0
+                */
+               pri_map = 0x0;
+       } else {
+               val &= ~AM65_CPSW_PORT_REG_PRI_CTL_RX_PTYPE_RROBIN;
+               /* restore P0_Rx_Pri_Map */
+               pri_map = 0x76543210;
+       }
+
+       writel(pri_map, host_p->port_base + AM65_CPSW_PORT_REG_RX_PRI_MAP);
+       writel(val, host_p->port_base + AM65_CPSW_PORT_REG_PRI_CTL);
+}
+
 static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common,
                                      netdev_features_t features)
 {
@@ -384,6 +436,8 @@ static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common,
                writel(AM65_CPSW_P0_REG_CTL_RX_CHECKSUM_EN,
                       host_p->port_base + AM65_CPSW_P0_REG_CTL);
 
+       am65_cpsw_nuss_set_p0_ptype(common);
+
        /* enable statistic */
        val = 0;
        for (port_idx = 0; port_idx < common->port_num; port_idx++) {
@@ -930,7 +984,7 @@ static int am65_cpsw_nuss_tx_compl_packets(struct am65_cpsw_common *common,
 
                __netif_tx_unlock(netif_txq);
        }
-       dev_dbg(dev, "%s pkt:%d\n", __func__, num_tx);
+       dev_dbg(dev, "%s:%u pkt:%d\n", __func__, chn, num_tx);
 
        return num_tx;
 }
@@ -942,10 +996,16 @@ static int am65_cpsw_nuss_tx_poll(struct napi_struct *napi_tx, int budget)
 
        /* process every unprocessed channel */
        for (ch = common->tx_ch_num; ch; ch--) {
+               u32 cur_ch = ch;
                cur_budget = budget - num_tx;
 
+               if (common->pf_p0_rx_ptype_rrobin) {
+                       common->cur_txq = (common->cur_txq + 1) %
+                                          common->tx_ch_num;
+                       cur_ch = common->cur_txq + 1;
+               }
                num_tx += am65_cpsw_nuss_tx_compl_packets(
-                               common, ch, cur_budget);
+                               common, cur_ch, cur_budget);
                if (num_tx >= budget)
                        break;
        }
@@ -1901,6 +1961,7 @@ static int am65_cpsw_nuss_init_ndev_2g(struct am65_cpsw_common *common)
        netif_tx_napi_add(port->ndev, &common->napi_rx,
                          am65_cpsw_nuss_rx_poll, NAPI_POLL_WEIGHT);
 
+       common->pf_p0_rx_ptype_rrobin = true;
        ret = register_netdev(port->ndev);
        if (ret)
                dev_err(dev, "error registering slave net device %d\n", ret);
index c8363b72587d0ba87679acd9f91fac2a644276fb..35a6ac856d6e66dedf520cfe630d4f79590600a5 100644 (file)
@@ -98,6 +98,9 @@ struct am65_cpsw_common {
        u32                     bus_freq_mhz;
        struct davinci_mdio_data *mdio;
        struct am65_cpts *cpts;
+
+       bool                    pf_p0_rx_ptype_rrobin;
+       u32                     cur_txq;
 };
 
 struct am65_cpsw_ndev_stats {
@@ -132,6 +135,7 @@ struct am65_cpsw_ndev_priv {
 extern const struct ethtool_ops am65_cpsw_ethtool_ops_slave;
 
 void am65_cpsw_nuss_adjust_link(struct net_device *ndev);
+void am65_cpsw_nuss_set_p0_ptype(struct am65_cpsw_common *common);
 
 #define        am65_ndev_dbg(ndev, arg...) \
        do {                                                          \