Skip to content

Commit 0f51fa2

Browse files
committed
Merge branch 'dsa-felix-fixes'
Vladimir Oltean says: ==================== Fixes for Felix DSA driver calculation of tc-taprio guard bands This series fixes some bugs which are not quite new, but date from v5.13 when static guard bands were enabled by Michael Walle to prevent tc-taprio overruns. The investigation started when Xiaoliang asked privately what is the expected max SDU for a traffic class when its minimum gate interval is 10 us. The answer, as it turns out, is not an L1 size of 1250 octets, but 1245 octets, since otherwise, the switch will not consider frames for egress scheduling, because the static guard band is exactly as large as the time interval. The switch needs a minimum of 33 ns outside of the guard band to consider a frame for scheduling, and the reduction of the max SDU by 5 provides exactly for that. The fix for that (patch 1/3) is relatively small, but during testing, it became apparent that cut-through forwarding prevents oversized frame dropping from working properly. This is solved through the larger patch 2/3. Finally, patch 3/3 fixes one more tc-taprio locking problem found through code inspection. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents e1091e2 + a4bb481 commit 0f51fa2

File tree

1 file changed

+112
-49
lines changed

1 file changed

+112
-49
lines changed

drivers/net/dsa/ocelot/felix_vsc9959.c

Lines changed: 112 additions & 49 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.
@@ -1539,6 +1557,65 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio,
15391557
min_gate_len[tc] = 0;
15401558
}
15411559

1560+
/* ocelot_write_rix is a macro that concatenates QSYS_MAXSDU_CFG_* with _RSZ,
1561+
* so we need to spell out the register access to each traffic class in helper
1562+
* functions, to simplify callers
1563+
*/
1564+
static void vsc9959_port_qmaxsdu_set(struct ocelot *ocelot, int port, int tc,
1565+
u32 max_sdu)
1566+
{
1567+
switch (tc) {
1568+
case 0:
1569+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_0,
1570+
port);
1571+
break;
1572+
case 1:
1573+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_1,
1574+
port);
1575+
break;
1576+
case 2:
1577+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_2,
1578+
port);
1579+
break;
1580+
case 3:
1581+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_3,
1582+
port);
1583+
break;
1584+
case 4:
1585+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_4,
1586+
port);
1587+
break;
1588+
case 5:
1589+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_5,
1590+
port);
1591+
break;
1592+
case 6:
1593+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_6,
1594+
port);
1595+
break;
1596+
case 7:
1597+
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_7,
1598+
port);
1599+
break;
1600+
}
1601+
}
1602+
1603+
static u32 vsc9959_port_qmaxsdu_get(struct ocelot *ocelot, int port, int tc)
1604+
{
1605+
switch (tc) {
1606+
case 0: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_0, port);
1607+
case 1: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_1, port);
1608+
case 2: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_2, port);
1609+
case 3: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_3, port);
1610+
case 4: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_4, port);
1611+
case 5: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_5, port);
1612+
case 6: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_6, port);
1613+
case 7: return ocelot_read_rix(ocelot, QSYS_QMAXSDU_CFG_7, port);
1614+
default:
1615+
return 0;
1616+
}
1617+
}
1618+
15421619
/* Update QSYS_PORT_MAX_SDU to make sure the static guard bands added by the
15431620
* switch (see the ALWAYS_GUARD_BAND_SCH_Q comment) are correct at all MTU
15441621
* values (the default value is 1518). Also, for traffic class windows smaller
@@ -1595,11 +1672,16 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
15951672

15961673
vsc9959_tas_min_gate_lengths(ocelot_port->taprio, min_gate_len);
15971674

1675+
mutex_lock(&ocelot->fwd_domain_lock);
1676+
15981677
for (tc = 0; tc < OCELOT_NUM_TC; tc++) {
1678+
u64 remaining_gate_len_ps;
15991679
u32 max_sdu;
16001680

1601-
if (min_gate_len[tc] == U64_MAX /* Gate always open */ ||
1602-
min_gate_len[tc] * PSEC_PER_NSEC > needed_bit_time_ps) {
1681+
remaining_gate_len_ps =
1682+
vsc9959_tas_remaining_gate_len_ps(min_gate_len[tc]);
1683+
1684+
if (remaining_gate_len_ps > needed_bit_time_ps) {
16031685
/* Setting QMAXSDU_CFG to 0 disables oversized frame
16041686
* dropping.
16051687
*/
@@ -1612,9 +1694,15 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
16121694
/* If traffic class doesn't support a full MTU sized
16131695
* frame, make sure to enable oversize frame dropping
16141696
* for frames larger than the smallest that would fit.
1697+
*
1698+
* However, the exact same register, QSYS_QMAXSDU_CFG_*,
1699+
* controls not only oversized frame dropping, but also
1700+
* per-tc static guard band lengths, so it reduces the
1701+
* useful gate interval length. Therefore, be careful
1702+
* to calculate a guard band (and therefore max_sdu)
1703+
* that still leaves 33 ns available in the time slot.
16151704
*/
1616-
max_sdu = div_u64(min_gate_len[tc] * PSEC_PER_NSEC,
1617-
picos_per_byte);
1705+
max_sdu = div_u64(remaining_gate_len_ps, picos_per_byte);
16181706
/* A TC gate may be completely closed, which is a
16191707
* special case where all packets are oversized.
16201708
* Any limit smaller than 64 octets accomplishes this
@@ -1637,47 +1725,14 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
16371725
max_sdu);
16381726
}
16391727

1640-
/* ocelot_write_rix is a macro that concatenates
1641-
* QSYS_MAXSDU_CFG_* with _RSZ, so we need to spell out
1642-
* the writes to each traffic class
1643-
*/
1644-
switch (tc) {
1645-
case 0:
1646-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_0,
1647-
port);
1648-
break;
1649-
case 1:
1650-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_1,
1651-
port);
1652-
break;
1653-
case 2:
1654-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_2,
1655-
port);
1656-
break;
1657-
case 3:
1658-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_3,
1659-
port);
1660-
break;
1661-
case 4:
1662-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_4,
1663-
port);
1664-
break;
1665-
case 5:
1666-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_5,
1667-
port);
1668-
break;
1669-
case 6:
1670-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_6,
1671-
port);
1672-
break;
1673-
case 7:
1674-
ocelot_write_rix(ocelot, max_sdu, QSYS_QMAXSDU_CFG_7,
1675-
port);
1676-
break;
1677-
}
1728+
vsc9959_port_qmaxsdu_set(ocelot, port, tc, max_sdu);
16781729
}
16791730

16801731
ocelot_write_rix(ocelot, maxlen, QSYS_PORT_MAX_SDU, port);
1732+
1733+
ocelot->ops->cut_through_fwd(ocelot);
1734+
1735+
mutex_unlock(&ocelot->fwd_domain_lock);
16811736
}
16821737

16831738
static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
@@ -1704,13 +1759,13 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
17041759
break;
17051760
}
17061761

1762+
mutex_lock(&ocelot->tas_lock);
1763+
17071764
ocelot_rmw_rix(ocelot,
17081765
QSYS_TAG_CONFIG_LINK_SPEED(tas_speed),
17091766
QSYS_TAG_CONFIG_LINK_SPEED_M,
17101767
QSYS_TAG_CONFIG, port);
17111768

1712-
mutex_lock(&ocelot->tas_lock);
1713-
17141769
if (ocelot_port->taprio)
17151770
vsc9959_tas_guard_bands_update(ocelot, port);
17161771

@@ -2770,7 +2825,7 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
27702825
{
27712826
struct felix *felix = ocelot_to_felix(ocelot);
27722827
struct dsa_switch *ds = felix->ds;
2773-
int port, other_port;
2828+
int tc, port, other_port;
27742829

27752830
lockdep_assert_held(&ocelot->fwd_domain_lock);
27762831

@@ -2814,19 +2869,27 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
28142869
min_speed = other_ocelot_port->speed;
28152870
}
28162871

2817-
/* Enable cut-through forwarding for all traffic classes. */
2818-
if (ocelot_port->speed == min_speed)
2872+
/* Enable cut-through forwarding for all traffic classes that
2873+
* don't have oversized dropping enabled, since this check is
2874+
* bypassed in cut-through mode.
2875+
*/
2876+
if (ocelot_port->speed == min_speed) {
28192877
val = GENMASK(7, 0);
28202878

2879+
for (tc = 0; tc < OCELOT_NUM_TC; tc++)
2880+
if (vsc9959_port_qmaxsdu_get(ocelot, port, tc))
2881+
val &= ~BIT(tc);
2882+
}
2883+
28212884
set:
28222885
tmp = ocelot_read_rix(ocelot, ANA_CUT_THRU_CFG, port);
28232886
if (tmp == val)
28242887
continue;
28252888

28262889
dev_dbg(ocelot->dev,
2827-
"port %d fwd mask 0x%lx speed %d min_speed %d, %s cut-through forwarding\n",
2890+
"port %d fwd mask 0x%lx speed %d min_speed %d, %s cut-through forwarding on TC mask 0x%x\n",
28282891
port, mask, ocelot_port->speed, min_speed,
2829-
val ? "enabling" : "disabling");
2892+
val ? "enabling" : "disabling", val);
28302893

28312894
ocelot_write_rix(ocelot, val, ANA_CUT_THRU_CFG, port);
28322895
}

0 commit comments

Comments
 (0)