Patch "net: mscc: ocelot: fix oversize frame dropping for preemptible TCs" has been added to the 6.4-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    net: mscc: ocelot: fix oversize frame dropping for preemptible TCs

to the 6.4-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     net-mscc-ocelot-fix-oversize-frame-dropping-for-pree.patch
and it can be found in the queue-6.4 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit fea32e0ddf5b2d1794d710488bd9e12dea65d7ea
Author: Vladimir Oltean <vladimir.oltean@xxxxxxx>
Date:   Wed Jul 5 13:44:22 2023 +0300

    net: mscc: ocelot: fix oversize frame dropping for preemptible TCs
    
    [ Upstream commit c6efb4ae387c79bf0d4da286108c810b7b40de3c ]
    
    This switch implements Hold/Release in a strange way, with no control
    from the user as required by IEEE 802.1Q-2018 through Set-And-Hold-MAC
    and Set-And-Release-MAC, but rather, it emits HOLD requests implicitly
    based on the schedule.
    
    Namely, when the gate of a preemptible TC is about to close (actually
    QSYS::PREEMPTION_CFG.HOLD_ADVANCE octet times in advance of this event),
    the QSYS seems to emit a HOLD request pulse towards the MAC which
    preempts the currently transmitted packet, and further packets are held
    back in the queue system.
    
    This allows large frames to be squeezed through small time slots,
    because HOLD requests initiated by the gate events result in the frame
    being segmented in multiple fragments, the bit time of which is equal to
    the size of the time slot.
    
    It has been reported that the vsc9959_tas_guard_bands_update() logic
    breaks this, because it doesn't take preemptible TCs into account, and
    enables oversized frame dropping when the time slot doesn't allow a full
    MTU to be sent, but it does allow 2*minFragSize to be sent (128B).
    Packets larger than 128B are dropped instead of being sent in multiple
    fragments.
    
    Confusingly, the manual says:
    
    | For guard band, SDU calculation of a traffic class of a port, if
    | preemption is enabled (through 'QSYS::PREEMPTION_CFG.P_QUEUES') then
    | QSYS::PREEMPTION_CFG.HOLD_ADVANCE is used, otherwise
    | QSYS::QMAXSDU_CFG_*.QMAXSDU_* is used.
    
    but this only refers to the static guard band durations, and the
    QMAXSDU_CFG_* registers have dual purpose - the other being oversized
    frame dropping, which takes place irrespective of whether frames are
    preemptible or express.
    
    So, to fix the problem, we need to call vsc9959_tas_guard_bands_update()
    from ocelot_port_update_active_preemptible_tcs(), and modify the guard
    band logic to consider a different (lower) oversize limit for
    preemptible traffic classes.
    
    Fixes: 403ffc2c34de ("net: mscc: ocelot: add support for preemptible traffic classes")
    Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
    Message-ID: <20230705104422.49025-4-vladimir.oltean@xxxxxxx>
    Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 219fb672a68d7..bd11f9fb95e54 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -1221,11 +1221,13 @@ static u32 vsc9959_tas_tc_max_sdu(struct tc_taprio_qopt_offload *taprio, int tc)
 static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
 {
 	struct ocelot_port *ocelot_port = ocelot->ports[port];
+	struct ocelot_mm_state *mm = &ocelot->mm[port];
 	struct tc_taprio_qopt_offload *taprio;
 	u64 min_gate_len[OCELOT_NUM_TC];
+	u32 val, maxlen, add_frag_size;
+	u64 needed_min_frag_time_ps;
 	int speed, picos_per_byte;
 	u64 needed_bit_time_ps;
-	u32 val, maxlen;
 	u8 tas_speed;
 	int tc;
 
@@ -1265,9 +1267,18 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
 	 */
 	needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte;
 
+	/* Preemptible TCs don't need to pass a full MTU, the port will
+	 * automatically emit a HOLD request when a preemptible TC gate closes
+	 */
+	val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
+	add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
+	needed_min_frag_time_ps = picos_per_byte *
+		(u64)(24 + 2 * ethtool_mm_frag_size_add_to_min(add_frag_size));
+
 	dev_dbg(ocelot->dev,
-		"port %d: max frame size %d needs %llu ps at speed %d\n",
-		port, maxlen, needed_bit_time_ps, speed);
+		"port %d: max frame size %d needs %llu ps, %llu ps for mPackets at speed %d\n",
+		port, maxlen, needed_bit_time_ps, needed_min_frag_time_ps,
+		speed);
 
 	vsc9959_tas_min_gate_lengths(taprio, min_gate_len);
 
@@ -1281,7 +1292,9 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
 		remaining_gate_len_ps =
 			vsc9959_tas_remaining_gate_len_ps(min_gate_len[tc]);
 
-		if (remaining_gate_len_ps > needed_bit_time_ps) {
+		if ((mm->active_preemptible_tcs & BIT(tc)) ?
+		    remaining_gate_len_ps > needed_min_frag_time_ps :
+		    remaining_gate_len_ps > needed_bit_time_ps) {
 			/* Setting QMAXSDU_CFG to 0 disables oversized frame
 			 * dropping.
 			 */
diff --git a/drivers/net/ethernet/mscc/ocelot_mm.c b/drivers/net/ethernet/mscc/ocelot_mm.c
index fb3145118d686..99b29d1e62449 100644
--- a/drivers/net/ethernet/mscc/ocelot_mm.c
+++ b/drivers/net/ethernet/mscc/ocelot_mm.c
@@ -67,10 +67,13 @@ void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port)
 		val = mm->preemptible_tcs;
 
 	/* Cut through switching doesn't work for preemptible priorities,
-	 * so first make sure it is disabled.
+	 * so first make sure it is disabled. Also, changing the preemptible
+	 * TCs affects the oversized frame dropping logic, so that needs to be
+	 * re-triggered. And since tas_guard_bands_update() also implicitly
+	 * calls cut_through_fwd(), we don't need to explicitly call it.
 	 */
 	mm->active_preemptible_tcs = val;
-	ocelot->ops->cut_through_fwd(ocelot);
+	ocelot->ops->tas_guard_bands_update(ocelot, port);
 
 	dev_dbg(ocelot->dev,
 		"port %d %s/%s, MM TX %s, preemptible TCs 0x%x, active 0x%x\n",



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux