igc: Lift TAPRIO schedule restriction
authorKurt Kanzenbach <kurt@linutronix.de>
Mon, 6 Jun 2022 09:27:47 +0000 (11:27 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 31 Dec 2022 12:14:38 +0000 (13:14 +0100)
[ Upstream commit a5fd39464a4081ce11c801d7e20c4551ba7cb983 ]

Add support for Qbv schedules where one queue stays open
in consecutive entries. Currently that's not supported.

Example schedule:

|tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 3 \
|   map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \
|   queues 1@0 1@1 2@2 \
|   base-time ${BASETIME} \
|   sched-entry S 0x01 300000 \ # Stream High/Low
|   sched-entry S 0x06 500000 \ # Management and Best Effort
|   sched-entry S 0x04 200000 \ # Best Effort
|   flags 0x02

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Tested-by: Naama Meir <naamax.meir@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Stable-dep-of: 72abeedd8398 ("igc: Set Qbv start_time and end_time to end_time if not being configured in GCL")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/intel/igc/igc_main.c

index df78aa4..791f59d 100644 (file)
@@ -5919,9 +5919,10 @@ static bool validate_schedule(struct igc_adapter *adapter,
                return false;
 
        for (n = 0; n < qopt->num_entries; n++) {
-               const struct tc_taprio_sched_entry *e;
+               const struct tc_taprio_sched_entry *e, *prev;
                int i;
 
+               prev = n ? &qopt->entries[n - 1] : NULL;
                e = &qopt->entries[n];
 
                /* i225 only supports "global" frame preemption
@@ -5934,7 +5935,12 @@ static bool validate_schedule(struct igc_adapter *adapter,
                        if (e->gate_mask & BIT(i))
                                queue_uses[i]++;
 
-                       if (queue_uses[i] > 1)
+                       /* There are limitations: A single queue cannot be
+                        * opened and closed multiple times per cycle unless the
+                        * gate stays open. Check for it.
+                        */
+                       if (queue_uses[i] > 1 &&
+                           !(prev->gate_mask & BIT(i)))
                                return false;
                }
        }
@@ -5978,6 +5984,7 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter)
 static int igc_save_qbv_schedule(struct igc_adapter *adapter,
                                 struct tc_taprio_qopt_offload *qopt)
 {
+       bool queue_configured[IGC_MAX_TX_QUEUES] = { };
        u32 start_time = 0, end_time = 0;
        size_t n;
 
@@ -5998,9 +6005,6 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,
        adapter->cycle_time = qopt->cycle_time;
        adapter->base_time = qopt->base_time;
 
-       /* FIXME: be a little smarter about cases when the gate for a
-        * queue stays open for more than one entry.
-        */
        for (n = 0; n < qopt->num_entries; n++) {
                struct tc_taprio_sched_entry *e = &qopt->entries[n];
                int i;
@@ -6028,8 +6032,15 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter,
                        if (!(e->gate_mask & BIT(i)))
                                continue;
 
-                       ring->start_time = start_time;
+                       /* Check whether a queue stays open for more than one
+                        * entry. If so, keep the start and advance the end
+                        * time.
+                        */
+                       if (!queue_configured[i])
+                               ring->start_time = start_time;
                        ring->end_time = end_time;
+
+                       queue_configured[i] = true;
                }
 
                start_time += e->interval;