Skip to content

Commit 11afdc6

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: felix: tc-taprio intervals smaller than MTU should send at least one packet
The blamed commit broke tc-taprio schedules such as this one: tc qdisc replace dev $swp1 root taprio \ num_tc 8 \ map 0 1 2 3 4 5 6 7 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \ base-time 0 \ sched-entry S 0x7f 990000 \ sched-entry S 0x80 10000 \ flags 0x2 because the gate entry for TC 7 (S 0x80 10000 ns) now has a static guard band added earlier than its 'gate close' event, such that packet overruns won't occur in the worst case of the largest packet possible. Since guard bands are statically determined based on the per-tc QSYS_QMAXSDU_CFG_* with a fallback on the port-based QSYS_PORT_MAX_SDU, we need to discuss what happens with TC 7 depending on kernel version, since the driver, prior to commit 55a515b ("net: dsa: felix: drop oversized frames with tc-taprio instead of hanging the port"), did not touch QSYS_QMAXSDU_CFG_*, and therefore relied on QSYS_PORT_MAX_SDU. 1 (before vsc9959_tas_guard_bands_update): QSYS_PORT_MAX_SDU defaults to 1518, and at gigabit this introduces a static guard band (independent of packet sizes) of 12144 ns, plus QSYS::HSCH_MISC_CFG.FRM_ADJ (bit time of 20 octets => 160 ns). But this is larger than the time window itself, of 10000 ns. So, the queue system never considers a frame with TC 7 as eligible for transmission, since the gate practically never opens, and these frames are forever stuck in the TX queues and hang the port. 2 (after vsc9959_tas_guard_bands_update): Under the sole goal of enabling oversized frame dropping, we make an effort to set QSYS_QMAXSDU_CFG_7 to 1230 bytes. But QSYS_QMAXSDU_CFG_7 plays one more role, which we did not take into account: per-tc static guard band, expressed in L2 byte time (auto-adjusted for FCS and L1 overhead). There is a discrepancy between what the driver thinks (that there is no guard band, and 100% of min_gate_len[tc] is available for egress scheduling) and what the hardware actually does (crops the equivalent of QSYS_QMAXSDU_CFG_7 ns out of min_gate_len[tc]). In practice, this means that the hardware thinks it has exactly 0 ns for scheduling tc 7. In both cases, even minimum sized Ethernet frames are stuck on egress rather than being considered for scheduling on TC 7, even if they would fit given a proper configuration. Considering the current situation, with vsc9959_tas_guard_bands_update(), frames between 60 octets and 1230 octets in size are not eligible for oversized dropping (because they are smaller than QSYS_QMAXSDU_CFG_7), but won't be considered as eligible for scheduling either, because the min_gate_len[7] (10000 ns) minus the guard band determined by QSYS_QMAXSDU_CFG_7 (1230 octets * 8 ns per octet == 9840 ns) minus the guard band auto-added for L1 overhead by QSYS::HSCH_MISC_CFG.FRM_ADJ (20 octets * 8 ns per octet == 160 octets) leaves 0 ns for scheduling in the queue system proper. Investigating the hardware behavior, it becomes apparent that the queue system needs precisely 33 ns of 'gate open' time in order to consider a frame as eligible for scheduling to a tc. So the solution to this problem is to amend vsc9959_tas_guard_bands_update(), by giving the per-tc guard bands less space by exactly 33 ns, just enough for one frame to be scheduled in that interval. This allows the queue system to make forward progress for that port-tc, and prevents it from hanging. Fixes: 297c4de ("net: dsa: felix: re-enable TAS guard band mode") Reported-by: Xiaoliang Yang <xiaoliang.yang_1@nxp.com> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent e1091e2 commit 11afdc6

File tree

1 file changed

+31
-4
lines changed

1 file changed

+31
-4
lines changed

drivers/net/dsa/ocelot/felix_vsc9959.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define VSC9959_NUM_PORTS 6
2323

2424
#define VSC9959_TAS_GCL_ENTRY_MAX 63
25+
#define VSC9959_TAS_MIN_GATE_LEN_NS 33
2526
#define VSC9959_VCAP_POLICER_BASE 63
2627
#define VSC9959_VCAP_POLICER_MAX 383
2728
#define VSC9959_SWITCH_PCI_BAR 4
@@ -1478,6 +1479,23 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
14781479
mdiobus_free(felix->imdio);
14791480
}
14801481

1482+
/* The switch considers any frame (regardless of size) as eligible for
1483+
* transmission if the traffic class gate is open for at least 33 ns.
1484+
* Overruns are prevented by cropping an interval at the end of the gate time
1485+
* slot for which egress scheduling is blocked, but we need to still keep 33 ns
1486+
* available for one packet to be transmitted, otherwise the port tc will hang.
1487+
* This function returns the size of a gate interval that remains available for
1488+
* setting the guard band, after reserving the space for one egress frame.
1489+
*/
1490+
static u64 vsc9959_tas_remaining_gate_len_ps(u64 gate_len_ns)
1491+
{
1492+
/* Gate always open */
1493+
if (gate_len_ns == U64_MAX)
1494+
return U64_MAX;
1495+
1496+
return (gate_len_ns - VSC9959_TAS_MIN_GATE_LEN_NS) * PSEC_PER_NSEC;
1497+
}
1498+
14811499
/* Extract shortest continuous gate open intervals in ns for each traffic class
14821500
* of a cyclic tc-taprio schedule. If a gate is always open, the duration is
14831501
* considered U64_MAX. If the gate is always closed, it is considered 0.
@@ -1596,10 +1614,13 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
15961614
vsc9959_tas_min_gate_lengths(ocelot_port->taprio, min_gate_len);
15971615

15981616
for (tc = 0; tc < OCELOT_NUM_TC; tc++) {
1617+
u64 remaining_gate_len_ps;
15991618
u32 max_sdu;
16001619

1601-
if (min_gate_len[tc] == U64_MAX /* Gate always open */ ||
1602-
min_gate_len[tc] * PSEC_PER_NSEC > needed_bit_time_ps) {
1620+
remaining_gate_len_ps =
1621+
vsc9959_tas_remaining_gate_len_ps(min_gate_len[tc]);
1622+
1623+
if (remaining_gate_len_ps > needed_bit_time_ps) {
16031624
/* Setting QMAXSDU_CFG to 0 disables oversized frame
16041625
* dropping.
16051626
*/
@@ -1612,9 +1633,15 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
16121633
/* If traffic class doesn't support a full MTU sized
16131634
* frame, make sure to enable oversize frame dropping
16141635
* for frames larger than the smallest that would fit.
1636+
*
1637+
* However, the exact same register, QSYS_QMAXSDU_CFG_*,
1638+
* controls not only oversized frame dropping, but also
1639+
* per-tc static guard band lengths, so it reduces the
1640+
* useful gate interval length. Therefore, be careful
1641+
* to calculate a guard band (and therefore max_sdu)
1642+
* that still leaves 33 ns available in the time slot.
16151643
*/
1616-
max_sdu = div_u64(min_gate_len[tc] * PSEC_PER_NSEC,
1617-
picos_per_byte);
1644+
max_sdu = div_u64(remaining_gate_len_ps, picos_per_byte);
16181645
/* A TC gate may be completely closed, which is a
16191646
* special case where all packets are oversized.
16201647
* Any limit smaller than 64 octets accomplishes this

0 commit comments

Comments
 (0)