dpaa2-eth: Add mqprio support
authorIoana Radulescu <ruxandra.radulescu@nxp.com>
Tue, 11 Jun 2019 11:50:03 +0000 (14:50 +0300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 12 Jun 2019 18:22:10 +0000 (11:22 -0700)
Implement mqprio qdisc support by mapping traffic classes to
different hardware enqueue priorities. The maximum number of
supported traffic classes is an attribute of each DPNI object.

The traffic classes map to hardware priorities from highest (0)
to lowest (highest prio number). The skb priority information
received from the stack is used to select the hardware Tx queue
on which to enqueue the frame.

Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h

index 7007e8d0e8b90f44ff72fd64978746d5a7b06888..0acb11557ed1ce120b11e6bb9c361076936f6d1b 100644 (file)
@@ -757,6 +757,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
        u16 queue_mapping;
        unsigned int needed_headroom;
        u32 fd_len;
+       u8 prio = 0;
        int err, i;
 
        percpu_stats = this_cpu_ptr(priv->percpu_stats);
@@ -814,6 +815,18 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
         * a queue affined to the same core that processed the Rx frame
         */
        queue_mapping = skb_get_queue_mapping(skb);
+
+       if (net_dev->num_tc) {
+               prio = netdev_txq_to_tc(net_dev, queue_mapping);
+               /* Hardware interprets priority level 0 as being the highest,
+                * so we need to do a reverse mapping to the netdev tc index
+                */
+               prio = net_dev->num_tc - prio - 1;
+               /* We have only one FQ array entry for all Tx hardware queues
+                * with the same flow id (but different priority levels)
+                */
+               queue_mapping %= dpaa2_eth_queue_count(priv);
+       }
        fq = &priv->fq[queue_mapping];
 
        fd_len = dpaa2_fd_get_len(&fd);
@@ -824,7 +837,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
         * the Tx confirmation callback for this frame
         */
        for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = priv->enqueue(priv, fq, &fd, 0);
+               err = priv->enqueue(priv, fq, &fd, prio);
                if (err != -EBUSY)
                        break;
        }
@@ -1868,16 +1881,17 @@ static int update_xps(struct dpaa2_eth_priv *priv)
        struct net_device *net_dev = priv->net_dev;
        struct cpumask xps_mask;
        struct dpaa2_eth_fq *fq;
-       int i, num_queues;
+       int i, num_queues, netdev_queues;
        int err = 0;
 
        num_queues = dpaa2_eth_queue_count(priv);
+       netdev_queues = (net_dev->num_tc ? : 1) * num_queues;
 
        /* The first <num_queues> entries in priv->fq array are Tx/Tx conf
         * queues, so only process those
         */
-       for (i = 0; i < num_queues; i++) {
-               fq = &priv->fq[i];
+       for (i = 0; i < netdev_queues; i++) {
+               fq = &priv->fq[i % num_queues];
 
                cpumask_clear(&xps_mask);
                cpumask_set_cpu(fq->target_cpu, &xps_mask);
@@ -1892,6 +1906,48 @@ static int update_xps(struct dpaa2_eth_priv *priv)
        return err;
 }
 
+static int dpaa2_eth_setup_tc(struct net_device *net_dev,
+                             enum tc_setup_type type, void *type_data)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       struct tc_mqprio_qopt *mqprio = type_data;
+       u8 num_tc, num_queues;
+       int i;
+
+       if (type != TC_SETUP_QDISC_MQPRIO)
+               return -EINVAL;
+
+       mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+       num_queues = dpaa2_eth_queue_count(priv);
+       num_tc = mqprio->num_tc;
+
+       if (num_tc == net_dev->num_tc)
+               return 0;
+
+       if (num_tc  > dpaa2_eth_tc_count(priv)) {
+               netdev_err(net_dev, "Max %d traffic classes supported\n",
+                          dpaa2_eth_tc_count(priv));
+               return -EINVAL;
+       }
+
+       if (!num_tc) {
+               netdev_reset_tc(net_dev);
+               netif_set_real_num_tx_queues(net_dev, num_queues);
+               goto out;
+       }
+
+       netdev_set_num_tc(net_dev, num_tc);
+       netif_set_real_num_tx_queues(net_dev, num_tc * num_queues);
+
+       for (i = 0; i < num_tc; i++)
+               netdev_set_tc_queue(net_dev, i, num_queues, i * num_queues);
+
+out:
+       update_xps(priv);
+
+       return 0;
+}
+
 static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_open = dpaa2_eth_open,
        .ndo_start_xmit = dpaa2_eth_tx,
@@ -1904,6 +1960,7 @@ static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_change_mtu = dpaa2_eth_change_mtu,
        .ndo_bpf = dpaa2_eth_xdp,
        .ndo_xdp_xmit = dpaa2_eth_xdp_xmit,
+       .ndo_setup_tc = dpaa2_eth_setup_tc,
 };
 
 static void cdan_cb(struct dpaa2_io_notification_ctx *ctx)
@@ -3244,7 +3301,7 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
        dev = &dpni_dev->dev;
 
        /* Net device */
-       net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_TX_QUEUES);
+       net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_NETDEV_QUEUES);
        if (!net_dev) {
                dev_err(dev, "alloc_etherdev_mq() failed\n");
                return -ENOMEM;
index 73756e6a3a7815dc19809376fdc3d77992628c34..9af18c24221f714d605604d604b5fcce9158efea 100644 (file)
@@ -287,6 +287,8 @@ struct dpaa2_eth_ch_stats {
 #define DPAA2_ETH_MAX_TX_QUEUES                16
 #define DPAA2_ETH_MAX_QUEUES           (DPAA2_ETH_MAX_RX_QUEUES + \
                                        DPAA2_ETH_MAX_TX_QUEUES)
+#define DPAA2_ETH_MAX_NETDEV_QUEUES    \
+       (DPAA2_ETH_MAX_TX_QUEUES * DPAA2_ETH_MAX_TCS)
 
 #define DPAA2_ETH_MAX_DPCONS           16