igc: Add support for CBS offloading
authorAravindhan Gunasekaran <aravindhan.gunasekaran@intel.com>
Mon, 9 Aug 2021 14:53:40 +0000 (20:23 +0530)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Fri, 27 Aug 2021 16:31:09 +0000 (09:31 -0700)
Implement support for Credit-based shaper(CBS) Qdisc hardware
offload mode in the driver. There are two sets of IEEE802.1Qav
(CBS) HW logic in i225 controller and this patch supports
enabling them in the top two priority TX queues.

Driver implemented as recommended by Foxville External
Architecture Specification v0.993. Idleslope and Hi-credit are
the CBS tunable parameters for i225 NIC, programmed in TQAVCC
and TQAVHC registers respectively.

In-order for IEEE802.1Qav (CBS) algorithm to work as intended
and provide BW reservation CBS should be enabled in highest
priority queue first. If we enable CBS on any of low priority
queues, the traffic in high priority queue does not allow low
priority queue to be selected for transmission and bandwidth
reservation is not guaranteed.

Signed-off-by: Aravindhan Gunasekaran <aravindhan.gunasekaran@intel.com>
Signed-off-by: Mallikarjuna Chilakala <mallikarjuna.chilakala@intel.com>
Tested-by: Dvora Fuxbrumer <dvorax.fuxbrumer@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_regs.h
drivers/net/ethernet/intel/igc/igc_tsn.c

index b561beb..3e386c3 100644 (file)
@@ -98,6 +98,13 @@ struct igc_ring {
        u32 start_time;
        u32 end_time;
 
+       /* CBS parameters */
+       bool cbs_enable;                /* indicates if CBS is enabled */
+       s32 idleslope;                  /* idleSlope in kbps */
+       s32 sendslope;                  /* sendSlope in kbps */
+       s32 hicredit;                   /* hiCredit in bytes */
+       s32 locredit;                   /* loCredit in bytes */
+
        /* everything past this point are written often */
        u16 next_to_clean;
        u16 next_to_use;
@@ -290,8 +297,10 @@ extern char igc_driver_name[];
 #define IGC_FLAG_VLAN_PROMISC          BIT(15)
 #define IGC_FLAG_RX_LEGACY             BIT(16)
 #define IGC_FLAG_TSN_QBV_ENABLED       BIT(17)
+#define IGC_FLAG_TSN_QAV_ENABLED       BIT(18)
 
-#define IGC_FLAG_TSN_ANY_ENABLED       IGC_FLAG_TSN_QBV_ENABLED
+#define IGC_FLAG_TSN_ANY_ENABLED \
+       (IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED)
 
 #define IGC_FLAG_RSS_FIELD_IPV4_UDP    BIT(6)
 #define IGC_FLAG_RSS_FIELD_IPV6_UDP    BIT(7)
index c405633..a4bbee7 100644 (file)
 #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT  0x00000001
 #define IGC_TXQCTL_STRICT_CYCLE                0x00000002
 #define IGC_TXQCTL_STRICT_END          0x00000004
+#define IGC_TXQCTL_QAV_SEL_MASK                0x000000C0
+#define IGC_TXQCTL_QAV_SEL_CBS0                0x00000080
+#define IGC_TXQCTL_QAV_SEL_CBS1                0x000000C0
+
+#define IGC_TQAVCC_IDLESLOPE_MASK      0xFFFF
+#define IGC_TQAVCC_KEEP_CREDITS                BIT(30)
+
+#define IGC_MAX_SR_QUEUES              2
 
 /* Receive Checksum Control */
 #define IGC_RXCSUM_CRCOFL      0x00000800   /* CRC32 offload enable */
index 2e5c9b5..b877efa 100644 (file)
@@ -5904,6 +5904,74 @@ static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
        return igc_tsn_offload_apply(adapter);
 }
 
+static int igc_save_cbs_params(struct igc_adapter *adapter, int queue,
+                              bool enable, int idleslope, int sendslope,
+                              int hicredit, int locredit)
+{
+       bool cbs_status[IGC_MAX_SR_QUEUES] = { false };
+       struct net_device *netdev = adapter->netdev;
+       struct igc_ring *ring;
+       int i;
+
+       /* i225 has two sets of credit-based shaper logic.
+        * Supporting it only on the top two priority queues
+        */
+       if (queue < 0 || queue > 1)
+               return -EINVAL;
+
+       ring = adapter->tx_ring[queue];
+
+       for (i = 0; i < IGC_MAX_SR_QUEUES; i++)
+               if (adapter->tx_ring[i])
+                       cbs_status[i] = adapter->tx_ring[i]->cbs_enable;
+
+       /* CBS should be enabled on the highest priority queue first in order
+        * for the CBS algorithm to operate as intended.
+        */
+       if (enable) {
+               if (queue == 1 && !cbs_status[0]) {
+                       netdev_err(netdev,
+                                  "Enabling CBS on queue1 before queue0\n");
+                       return -EINVAL;
+               }
+       } else {
+               if (queue == 0 && cbs_status[1]) {
+                       netdev_err(netdev,
+                                  "Disabling CBS on queue0 before queue1\n");
+                       return -EINVAL;
+               }
+       }
+
+       ring->cbs_enable = enable;
+       ring->idleslope = idleslope;
+       ring->sendslope = sendslope;
+       ring->hicredit = hicredit;
+       ring->locredit = locredit;
+
+       return 0;
+}
+
+static int igc_tsn_enable_cbs(struct igc_adapter *adapter,
+                             struct tc_cbs_qopt_offload *qopt)
+{
+       struct igc_hw *hw = &adapter->hw;
+       int err;
+
+       if (hw->mac.type != igc_i225)
+               return -EOPNOTSUPP;
+
+       if (qopt->queue < 0 || qopt->queue > 1)
+               return -EINVAL;
+
+       err = igc_save_cbs_params(adapter, qopt->queue, qopt->enable,
+                                 qopt->idleslope, qopt->sendslope,
+                                 qopt->hicredit, qopt->locredit);
+       if (err)
+               return err;
+
+       return igc_tsn_offload_apply(adapter);
+}
+
 static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
                        void *type_data)
 {
@@ -5916,6 +5984,9 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
        case TC_SETUP_QDISC_ETF:
                return igc_tsn_enable_launchtime(adapter, type_data);
 
+       case TC_SETUP_QDISC_CBS:
+               return igc_tsn_enable_cbs(adapter, type_data);
+
        default:
                return -EOPNOTSUPP;
        }
index dbba2eb..e197a33 100644 (file)
 #define IGC_ENDQT(_n)          (0x3334 + 0x4 * (_n))
 #define IGC_DTXMXPKTSZ         0x355C
 
+#define IGC_TQAVCC(_n)         (0x3004 + ((_n) * 0x40))
+#define IGC_TQAVHC(_n)         (0x300C + ((_n) * 0x40))
+
 /* System Time Registers */
 #define IGC_SYSTIML    0x0B600  /* System time register Low - RO */
 #define IGC_SYSTIMH    0x0B604  /* System time register High - RO */
index 2935d57..0fce22d 100644 (file)
@@ -18,6 +18,20 @@ static bool is_any_launchtime(struct igc_adapter *adapter)
        return false;
 }
 
+static bool is_cbs_enabled(struct igc_adapter *adapter)
+{
+       int i;
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct igc_ring *ring = adapter->tx_ring[i];
+
+               if (ring->cbs_enable)
+                       return true;
+       }
+
+       return false;
+}
+
 static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
 {
        unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED;
@@ -28,6 +42,9 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
        if (is_any_launchtime(adapter))
                new_flags |= IGC_FLAG_TSN_QBV_ENABLED;
 
+       if (is_cbs_enabled(adapter))
+               new_flags |= IGC_FLAG_TSN_QAV_ENABLED;
+
        return new_flags;
 }
 
@@ -87,6 +104,8 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
        for (i = 0; i < adapter->num_tx_queues; i++) {
                struct igc_ring *ring = adapter->tx_ring[i];
                u32 txqctl = 0;
+               u16 cbs_value;
+               u32 tqavcc;
 
                wr32(IGC_STQT(i), ring->start_time);
                wr32(IGC_ENDQT(i), ring->end_time);
@@ -104,6 +123,90 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
                if (ring->launchtime_enable)
                        txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
 
+               /* Skip configuring CBS for Q2 and Q3 */
+               if (i > 1)
+                       goto skip_cbs;
+
+               if (ring->cbs_enable) {
+                       if (i == 0)
+                               txqctl |= IGC_TXQCTL_QAV_SEL_CBS0;
+                       else
+                               txqctl |= IGC_TXQCTL_QAV_SEL_CBS1;
+
+                       /* According to i225 datasheet section 7.5.2.7, we
+                        * should set the 'idleSlope' field from TQAVCC
+                        * register following the equation:
+                        *
+                        * value = link-speed   0x7736 * BW * 0.2
+                        *         ---------- *  -----------------         (E1)
+                        *          100Mbps            2.5
+                        *
+                        * Note that 'link-speed' is in Mbps.
+                        *
+                        * 'BW' is the percentage bandwidth out of full
+                        * link speed which can be found with the
+                        * following equation. Note that idleSlope here
+                        * is the parameter from this function
+                        * which is in kbps.
+                        *
+                        *     BW =     idleSlope
+                        *          -----------------                      (E2)
+                        *          link-speed * 1000
+                        *
+                        * That said, we can come up with a generic
+                        * equation to calculate the value we should set
+                        * it TQAVCC register by replacing 'BW' in E1 by E2.
+                        * The resulting equation is:
+                        *
+                        * value = link-speed * 0x7736 * idleSlope * 0.2
+                        *         -------------------------------------   (E3)
+                        *             100 * 2.5 * link-speed * 1000
+                        *
+                        * 'link-speed' is present in both sides of the
+                        * fraction so it is canceled out. The final
+                        * equation is the following:
+                        *
+                        *     value = idleSlope * 61036
+                        *             -----------------                   (E4)
+                        *                  2500000
+                        *
+                        * NOTE: For i225, given the above, we can see
+                        *       that idleslope is represented in
+                        *       40.959433 kbps units by the value at
+                        *       the TQAVCC register (2.5Gbps / 61036),
+                        *       which reduces the granularity for
+                        *       idleslope increments.
+                        *
+                        * In i225 controller, the sendSlope and loCredit
+                        * parameters from CBS are not configurable
+                        * by software so we don't do any
+                        * 'controller configuration' in respect to
+                        * these parameters.
+                        */
+                       cbs_value = DIV_ROUND_UP_ULL(ring->idleslope
+                                                    * 61036ULL, 2500000);
+
+                       tqavcc = rd32(IGC_TQAVCC(i));
+                       tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK;
+                       tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS;
+                       wr32(IGC_TQAVCC(i), tqavcc);
+
+                       wr32(IGC_TQAVHC(i),
+                            0x80000000 + ring->hicredit * 0x7735);
+               } else {
+                       /* Disable any CBS for the queue */
+                       txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK);
+
+                       /* Set idleSlope to zero. */
+                       tqavcc = rd32(IGC_TQAVCC(i));
+                       tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK |
+                                   IGC_TQAVCC_KEEP_CREDITS);
+                       wr32(IGC_TQAVCC(i), tqavcc);
+
+                       /* Set hiCredit to zero. */
+                       wr32(IGC_TQAVHC(i), 0);
+               }
+skip_cbs:
                wr32(IGC_TXQCTL(i), txqctl);
        }