Switch the out-of-range diagnostic in ieee8021q_tt_to_tc() from %d to %u. The traffic class count num_queues is an unsigned int and the traffic type is an enumeration, so printing them as unsigned matches their types. The print sits on the range-check error path, which still rejects a negative traffic type; %u only changes how an out-of-range value is rendered, not which values are rejected. Preparation for the inverse helper added next, which follows the same convention. Signed-off-by: Oleksij Rempel --- net/core/ieee8021q_helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c index 669b357b73b2..bbacdfff9b4c 100644 --- a/net/core/ieee8021q_helpers.c +++ b/net/core/ieee8021q_helpers.c @@ -99,7 +99,7 @@ static const u8 ieee8021q_1queue_tt_tc_map[] = { int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues) { if (tt < 0 || tt >= IEEE8021Q_TT_MAX) { - pr_err("Requested Traffic Type (%d) is out of range (%d)\n", tt, + pr_err("Requested Traffic Type (%u) is out of range (%u)\n", tt, IEEE8021Q_TT_MAX); return -EINVAL; } @@ -131,7 +131,7 @@ int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues) return ieee8021q_1queue_tt_tc_map[tt]; } - pr_err("Invalid number of queues %d\n", num_queues); + pr_err("Invalid number of queues %u\n", num_queues); return -EINVAL; } -- 2.47.3 Add a helper that maps an IEEE 802.1Q Priority Code Point (PCP) to the corresponding 802.1Q traffic type, complementing the existing ietf_dscp_to_ieee8021q_tt(). It is very nearly an identity map, except that PCP 0 (Best Effort) and PCP 1 (Background) are swapped: per 802.1Q-2022 (Annex I, Table I-2) Background is a lower priority than Best Effort even though it carries the higher PCP value. PCP 2..7 map straight through to the matching traffic type, and values above 7 are rejected with -EINVAL. This decodes the 8P0D encoding (Table I-7), where every PCP value is a priority and none carries drop eligibility. Drivers that classify ingress traffic by PCP need this to map into a priority-ordered internal space without misordering Best Effort and Background. The first consumer, the realtek rtl8365mb DSA driver, lands later in this series. Signed-off-by: Oleksij Rempel --- include/net/ieee8021q.h | 10 +++++++-- net/core/ieee8021q_helpers.c | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h index 8bfe903dd3d0..d24e4036577b 100644 --- a/include/net/ieee8021q.h +++ b/include/net/ieee8021q.h @@ -10,8 +10,8 @@ * enum ieee8021q_traffic_type - 802.1Q traffic type priority values (802.1Q-2022) * * @IEEE8021Q_TT_BK: Background - * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022, BE is 0 - * but has higher priority than BK which is 1. + * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022 (Annex I, + * Table I-2), BE is 0 but has higher priority than BK which is 1. * @IEEE8021Q_TT_EE: Excellent Effort * @IEEE8021Q_TT_CA: Critical Applications * @IEEE8021Q_TT_VI: Video, < 100 ms latency and jitter @@ -38,6 +38,7 @@ enum ieee8021q_traffic_type { #if IS_ENABLED(CONFIG_NET_IEEE8021Q_HELPERS) int ietf_dscp_to_ieee8021q_tt(u8 dscp); +int ieee8021q_pcp_to_tt(u8 pcp); int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues); #else @@ -47,6 +48,11 @@ static inline int ietf_dscp_to_ieee8021q_tt(u8 dscp) return -EOPNOTSUPP; } +static inline int ieee8021q_pcp_to_tt(u8 pcp) +{ + return -EOPNOTSUPP; +} + static inline int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues) { diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c index bbacdfff9b4c..a5bd5e039b47 100644 --- a/net/core/ieee8021q_helpers.c +++ b/net/core/ieee8021q_helpers.c @@ -222,3 +222,44 @@ int ietf_dscp_to_ieee8021q_tt(u8 dscp) return SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp); } EXPORT_SYMBOL_GPL(ietf_dscp_to_ieee8021q_tt); + +/** + * ieee8021q_pcp_to_tt - Map an IEEE 802.1Q PCP to a traffic type + * @pcp: IEEE 802.1Q Priority Code Point value + * + * Decode @pcp to its IEEE 802.1Q traffic type per Table I-7 ("Priority Code + * Point decoding") for the 8P0D encoding, where all eight PCP values are + * priorities and none carries drop eligibility. It is nearly the identity, + * except PCP 0 (Best Effort) and PCP 1 (Background) map to swapped traffic + * types: Background is lower priority than Best Effort despite the higher PCP + * value (Table I-2: BE = priority 0, BK = priority 1). + * + * The 7P1D/6P2D/5P3D encodings, whose lower PCP values carry drop eligibility, + * are the other rows of Table I-7 and are not decoded here. + * + * Return: the traffic type, or a negative value on error. + */ +int ieee8021q_pcp_to_tt(u8 pcp) +{ + switch (pcp) { + case 0: + return IEEE8021Q_TT_BE; + case 1: + return IEEE8021Q_TT_BK; + case 2: + return IEEE8021Q_TT_EE; + case 3: + return IEEE8021Q_TT_CA; + case 4: + return IEEE8021Q_TT_VI; + case 5: + return IEEE8021Q_TT_VO; + case 6: + return IEEE8021Q_TT_IC; + case 7: + return IEEE8021Q_TT_NC; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ieee8021q_pcp_to_tt); -- 2.47.3 Document that the tt_to_tc() traffic-class numbers are the pre-computed composition of Table I-2 (traffic type -> priority) with Table 8-5 (priority -> traffic class). Note that Table 8-5 is the mapping for implementations without the credit-based shaper, so the helper's scope is clear; credit-based-shaper implementations use Table 34-1/34-2 instead. The existing kdoc cited Annex I Table I-1 as the mapping basis, but Table I-1 only partitions traffic types into classes; it does not assign the class numbers. Signed-off-by: Oleksij Rempel --- net/core/ieee8021q_helpers.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c index a5bd5e039b47..520d86d592eb 100644 --- a/net/core/ieee8021q_helpers.c +++ b/net/core/ieee8021q_helpers.c @@ -12,10 +12,10 @@ compiletime_assert(ARRAY_SIZE(tbl) == IEEE8021Q_TT_MAX, \ #tbl " size mismatch") -/* The following arrays map Traffic Types (TT) to traffic classes (TC) for - * different number of queues as shown in the example provided by - * IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic class mapping" and - * Table I-1 "Traffic type to traffic class mapping". +/* The following arrays map a traffic type (TT) to a traffic class (TC) for + * each supported number of queues. The mapping is the traffic-type -> priority + * -> traffic-class composition documented in the ieee8021q_tt_to_tc() kdoc + * below, reduced for the given number of queues. */ static const u8 ieee8021q_8queue_tt_tc_map[] = { [IEEE8021Q_TT_BK] = 0, @@ -84,17 +84,21 @@ static const u8 ieee8021q_1queue_tt_tc_map[] = { }; /** - * ieee8021q_tt_to_tc - Map IEEE 802.1Q Traffic Type to Traffic Class - * @tt: IEEE 802.1Q Traffic Type - * @num_queues: Number of queues + * ieee8021q_tt_to_tc - Map an IEEE 802.1Q traffic type to a traffic class + * @tt: IEEE 802.1Q traffic type + * @num_queues: number of traffic classes (queues) available, 1..8 * - * This function maps an IEEE 802.1Q Traffic Type to a Traffic Class (TC) based - * on the number of queues configured on the NIC. The mapping is based on the - * example provided by IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic - * class mapping" and Table I-1 "Traffic type to traffic class mapping". + * Return the traffic-class number for @tt. It is the pre-computed composition + * of two IEEE 802.1Q-2022 mappings: * - * Return: Traffic Class corresponding to the given Traffic Type or negative - * value in case of error. + * traffic type --[Table I-2]--> priority --[Table 8-5]--> traffic class + * + * Table 8-5 is the priority-to-traffic-class mapping for implementations that + * do NOT support the credit-based shaper (8.6.8.2). Credit-based-shaper + * implementations use a different mapping (Table 34-1 for SR classes A and B, + * or Table 34-2 for SR class B only), which this helper does not implement. + * + * Return: the traffic class, or a negative value on error. */ int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues) { -- 2.47.3 Add ieee8021q_tt_to_pcp(), the inverse of ieee8021q_pcp_to_tt(): it encodes an 802.1Q traffic type back to its Priority Code Point, and completes the pcp/tt helper family. A driver that reseeds its PCP classifier through ieee8021q_pcp_to_tt() stores traffic types in the hardware tables, while the dcbnl priority ops exchange 802.1p priorities across the DSA boundary. Reading such a table back therefore needs the reverse mapping to restore the 802.1p value, which no existing helper provides. Because pcp_to_tt only transposes Best Effort and Background and is the identity elsewhere the two directions are numerically equal; provide tt_to_pcp as its own named function so the read path does not decode a traffic type as if it were a PCP. The first user is the Realtek rtl8365mb DSA driver, whose dcbnl get_default_prio and get_dscp_prio paths read back traffic types and must return 802.1p priorities. Signed-off-by: Oleksij Rempel --- include/net/ieee8021q.h | 6 ++++++ net/core/ieee8021q_helpers.c | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h index d24e4036577b..b09572da0289 100644 --- a/include/net/ieee8021q.h +++ b/include/net/ieee8021q.h @@ -39,6 +39,7 @@ enum ieee8021q_traffic_type { int ietf_dscp_to_ieee8021q_tt(u8 dscp); int ieee8021q_pcp_to_tt(u8 pcp); +int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt); int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues); #else @@ -53,6 +54,11 @@ static inline int ieee8021q_pcp_to_tt(u8 pcp) return -EOPNOTSUPP; } +static inline int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt) +{ + return -EOPNOTSUPP; +} + static inline int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues) { diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c index 520d86d592eb..205c11793473 100644 --- a/net/core/ieee8021q_helpers.c +++ b/net/core/ieee8021q_helpers.c @@ -267,3 +267,43 @@ int ieee8021q_pcp_to_tt(u8 pcp) return -EINVAL; } EXPORT_SYMBOL_GPL(ieee8021q_pcp_to_tt); + +/** + * ieee8021q_tt_to_pcp - Map an IEEE 802.1Q traffic type to a PCP + * @tt: IEEE 802.1Q traffic type + * + * Encode @tt back to its IEEE 802.1Q Priority Code Point per Table I-7 + * ("Priority Code Point decoding") for the 8P0D encoding, the inverse of + * ieee8021q_pcp_to_tt(). It is nearly the identity, except traffic types Best + * Effort and Background map to swapped PCP values: Best Effort is the default + * (PCP 0) despite being higher priority than Background (PCP 1) (Table I-2: + * BE = priority 0, BK = priority 1). + * + * Return: the PCP value, or a negative value on error. + */ +int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt) +{ + switch (tt) { + case IEEE8021Q_TT_BK: + return 1; + case IEEE8021Q_TT_BE: + return 0; + case IEEE8021Q_TT_EE: + return 2; + case IEEE8021Q_TT_CA: + return 3; + case IEEE8021Q_TT_VI: + return 4; + case IEEE8021Q_TT_VO: + return 5; + case IEEE8021Q_TT_IC: + return 6; + case IEEE8021Q_TT_NC: + return 7; + case IEEE8021Q_TT_MAX: + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ieee8021q_tt_to_pcp); -- 2.47.3 Store the number of egress queues in the per-chip info structure and advertise it through ds->num_tx_queues, following the ksz driver (struct ksz_chip_data.num_tx_queues). The queue count is a per-chip hardware property, and this driver supports several chips of the rtl8367c family. All currently-supported chips expose eight queues, but keeping the count in chip_info lets the QoS code that follows program the priority-to-queue map via the ieee8021q helpers for whatever queue count a chip carries, so the 802.1Q collapse is correct rather than hard-coded to eight. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/realtek/rtl8365mb_main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 87ec924a0611..0924d31790e0 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -708,6 +708,7 @@ struct rtl8365mb_extint { * @extints: available external interfaces * @jam_table: chip-specific initialization jam table * @jam_size: size of the chip's jam table + * @num_tx_queues: number of egress queues exposed by the chip * * These data are specific to a given chip in the family of switches supported * by this driver. When adding support for another chip in the family, a new @@ -720,6 +721,7 @@ struct rtl8365mb_chip_info { const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS]; const struct rtl8365mb_jam_tbl_entry *jam_table; size_t jam_size; + unsigned int num_tx_queues; }; /* Chip info for each supported switch in the family */ @@ -735,6 +737,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { }, .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + .num_tx_queues = 8, }, { .name = "RTL8367S", @@ -747,6 +750,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { }, .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + .num_tx_queues = 8, }, { .name = "RTL8367SB", @@ -761,6 +765,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { }, .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + .num_tx_queues = 8, }, { .name = "RTL8367RB-VB", @@ -774,6 +779,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { }, .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + .num_tx_queues = 8, }, }; @@ -3262,6 +3268,12 @@ static int rtl8365mb_setup(struct dsa_switch *ds) goto out_teardown_irq; } + /* The rtl8367c family this driver supports exposes a fixed number of + * egress queues per chip; advertise that count to DSA so the QoS code + * programs the priority-to-queue map for the right number of queues. + */ + ds->num_tx_queues = mb->chip_info->num_tx_queues; + /* Start statistics counter polling */ ret = rtl8365mb_stats_setup(priv); if (ret) { -- 2.47.3 Add rtl8365mb_dcb.c to establish a defined QoS baseline and offload the DCB default priority. The switch's internal priority is an opaque 3-bit value; give it the meaning of an IEEE 802.1Q traffic type. The switch classifies from several domains, each numbered on its own scale: the port default, PCP and DSCP. A single traffic-type meaning lets each domain map onto the internal priority with consistent ordering, so Best Effort outranks Background rather than following the raw PCP number. Program the baseline explicitly rather than trusting the chip reset state: set the priority-to-queue map for the chip's queue count, remap PCP to the internal priority, and give every one of the eight priority-decision sources a defined weight. Only the port default is trusted; PCP and DSCP stay untrusted until the admin opts in (apptrust, added later), and every port defaults to Best Effort, so all traffic lands on a single egress queue and the baseline has no user-visible effect on its own. Select DCB and the 802.1Q helpers following the microchip ksz driver, so the DCB code needs no CONFIG_DCB guards. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/realtek/Kconfig | 2 + drivers/net/dsa/realtek/Makefile | 1 + drivers/net/dsa/realtek/rtl8365mb_dcb.c | 287 +++++++++++++++++++++++ drivers/net/dsa/realtek/rtl8365mb_dcb.h | 20 ++ drivers/net/dsa/realtek/rtl8365mb_main.c | 15 ++ 5 files changed, 325 insertions(+) create mode 100644 drivers/net/dsa/realtek/rtl8365mb_dcb.c create mode 100644 drivers/net/dsa/realtek/rtl8365mb_dcb.h diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig index d6eb6713e5f6..934833adde58 100644 --- a/drivers/net/dsa/realtek/Kconfig +++ b/drivers/net/dsa/realtek/Kconfig @@ -33,6 +33,8 @@ config NET_DSA_REALTEK_RTL8365MB tristate "Realtek RTL8365MB switch driver" depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO select NET_DSA_TAG_RTL8_4 + select NET_IEEE8021Q_HELPERS + select DCB help Select to enable support for Realtek RTL8365MB-VC and RTL8367S. diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 72bb42e80c95..052a924519e8 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -20,4 +20,5 @@ rtl8365mb-objs := rtl8365mb_main.o \ rtl8365mb_table.o \ rtl8365mb_vlan.o \ rtl8365mb_l2.o \ + rtl8365mb_dcb.o \ # end of rtl8365mb-objs diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.c b/drivers/net/dsa/realtek/rtl8365mb_dcb.c new file mode 100644 index 000000000000..f48e95ef3947 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* QoS and DCB configuration for the rtl8365mb switch family + * + * The internal priority is the common rail: every classifier writes onto it, + * and it is the 802.1Q traffic type. + * + * A queue is a traffic class. Priority is not the same as traffic class: the + * mapping is policy (Table 8-5 for non-CBS) and collapses 8 priorities onto nq + * queues when nq < 8; they only line up here because this is a non-CBS switch + * at 8 queues. The code therefore always maps through ieee8021q_tt_to_tc(), never + * assuming priority == queue. + */ + +#include +#include +#include +#include +#include + +#include "realtek.h" +#include "rtl8365mb_dcb.h" + +/* The chip's eight internal priorities carry 802.1Q traffic types, so the + * two axes must have the same cardinality. + */ +static_assert(RTL8365MB_NUM_IPMS == IEEE8021Q_TT_MAX); + +/* Per-port output-queue mapping index (selects the queue count). Four ports + * per register, 3-bit field. An N-queue configuration selects index N, except + * the full eight-queue configuration, which is encoded as index 0 (the field + * wraps modulo eight). + */ +#define RTL8365MB_QOS_PORT_QUEUE_NUMBER_REG(_p) (0x0900 + ((_p) >> 2)) +#define RTL8365MB_QOS_PORT_QUEUE_NUMBER_OFFSET(_p) (((_p) & 0x3) << 2) +#define RTL8365MB_QOS_QMAP_IDX(_nq) ((_nq) & 0x7) + +/* Internal priority -> queue-id map. Eight tables (one per queue count), + * indexed [table][prio]; four priorities per register, 3-bit qid. An N-queue + * configuration uses table N-1 (1Q is table 0 at 0x0904, 8Q is table 7). + */ +#define RTL8365MB_QOS_PRI_TO_QID_TABLE(_nq) ((_nq) - 1) +#define RTL8365MB_QOS_PRI_TO_QID_REG(_t, _pri) \ + (0x0904 + ((_t) << 1) + ((_pri) >> 2)) +#define RTL8365MB_QOS_PRI_TO_QID_OFFSET(_pri) (((_pri) & 0x3) << 2) + +/* 802.1p (PCP) -> internal priority remap. Four priorities per register. */ +#define RTL8365MB_QOS_1Q_REMAP_REG(_pri) (0x0865 + ((_pri) >> 2)) +#define RTL8365MB_QOS_1Q_REMAP_OFFSET(_pri) (((_pri) & 0x3) << 2) + +/* Port-based (default) priority. Four ports per register, 3-bit. */ +#define RTL8365MB_QOS_PORT_PRI_REG(_p) (0x0877 + ((_p) >> 2)) +#define RTL8365MB_QOS_PORT_PRI_OFFSET(_p) (((_p) & 0x3) << 2) + +/* Default internal priority for unmarked traffic. Best Effort, to match what + * an untagged (PCP 0) frame resolves to via ieee8021q_pcp_to_tt(). + */ +#define RTL8365MB_QOS_DEFAULT_PRIO IEEE8021Q_TT_BE + +/* Priority-decision weight tables. Two tables (each port selects one), eight + * sources, one 8-bit weight each, two sources per register. Higher weight + * wins; a weight of zero disables the source. + */ +#define RTL8365MB_QOS_PRIDEC_TBL0_REG(_s) (0x087B + ((_s) >> 1)) +#define RTL8365MB_QOS_PRIDEC_TBL1_REG(_s) (0x0885 + ((_s) >> 1)) +#define RTL8365MB_QOS_PRIDEC_OFFSET(_s) (((_s) & 0x1) << 3) + +/* Each port selects one of the two decision tables; one bit per port. */ +#define RTL8365MB_QOS_PRIDEC_IDX_REG 0x0889 + +/* Priority-decision sources. The hardware numbers eight sources; this driver + * programs the three it uses by name and explicitly disables the rest. The + * hardware assigns these source ordinals: PORT=0, ACL=1 (unused), DSCP=2, + * 1Q=3, SVLAN=4 (unused), CVLAN=5 (unused), DA=6 (unused), SA=7 (unused). + */ +#define RTL8365MB_QOS_PRIDEC_PORT 0 +#define RTL8365MB_QOS_PRIDEC_DSCP 2 +#define RTL8365MB_QOS_PRIDEC_1Q 3 +#define RTL8365MB_QOS_PRIDEC_NUM_SRC 8 + +/* Priority-decision weights. The baseline trusts only the port-based + * priority; every other source is disabled (weight 0). apptrust raises a + * trusted source's weight above the port default so that it wins. + */ +#define RTL8365MB_QOS_WEIGHT_UNTRUSTED 0 +#define RTL8365MB_QOS_WEIGHT_PORT 1 + +/* The QoS priority and queue selectors are 3-bit register fields; derive a + * field's mask from its bit offset. + */ +static inline u32 rtl8365mb_qos_sel_field_mask(unsigned int off) +{ + return GENMASK(off + 2, off); +} + +/* The priority-decision weight is an 8-bit register field; derive its mask + * from the field's bit offset, mirroring rtl8365mb_qos_sel_field_mask(). + */ +static inline u32 rtl8365mb_qos_weight_field_mask(unsigned int off) +{ + return GENMASK(off + 7, off); +} + +static int rtl8365mb_set_field(struct realtek_priv *priv, u32 reg, u32 mask, + u32 val) +{ + return regmap_update_bits(priv->map, reg, mask, + (val << __ffs(mask)) & mask); +} + +static int rtl8365mb_get_field(struct realtek_priv *priv, u32 reg, u32 mask, + u32 *val) +{ + int ret; + + ret = regmap_read(priv->map, reg, val); + if (ret) + return ret; + + *val = (*val & mask) >> __ffs(mask); + return 0; +} + +static u32 rtl8365mb_qos_pridec_reg(int table, int src) +{ + return table ? RTL8365MB_QOS_PRIDEC_TBL1_REG(src) : + RTL8365MB_QOS_PRIDEC_TBL0_REG(src); +} + +static int rtl8365mb_qos_set_pridec(struct realtek_priv *priv, int table, + int src, u8 weight) +{ + int off = RTL8365MB_QOS_PRIDEC_OFFSET(src); + + return rtl8365mb_set_field(priv, rtl8365mb_qos_pridec_reg(table, src), + rtl8365mb_qos_weight_field_mask(off), weight); +} + +static int rtl8365mb_qos_setup_queues(struct realtek_priv *priv, + unsigned int nq) +{ + int table = RTL8365MB_QOS_PRI_TO_QID_TABLE(nq); + struct dsa_switch *ds = &priv->ds; + struct dsa_port *dp; + int tt, ret; + + dsa_switch_for_each_port(dp, ds) { + u32 reg = RTL8365MB_QOS_PORT_QUEUE_NUMBER_REG(dp->index); + int off = RTL8365MB_QOS_PORT_QUEUE_NUMBER_OFFSET(dp->index); + + ret = rtl8365mb_set_field(priv, reg, + rtl8365mb_qos_sel_field_mask(off), + RTL8365MB_QOS_QMAP_IDX(nq)); + if (ret) + return ret; + } + + for (tt = 0; tt < IEEE8021Q_TT_MAX; tt++) { + u32 reg = RTL8365MB_QOS_PRI_TO_QID_REG(table, tt); + int off = RTL8365MB_QOS_PRI_TO_QID_OFFSET(tt); + int tc = ieee8021q_tt_to_tc(tt, nq); + + if (tc < 0) + return tc; + + /* QID field holds the traffic class */ + ret = rtl8365mb_set_field(priv, reg, + rtl8365mb_qos_sel_field_mask(off), tc); + if (ret) + return ret; + } + + return 0; +} + +/* Map ingress PCP to the internal priority via its 802.1Q traffic type, so + * Best Effort (PCP 0) correctly outranks Background (PCP 1). + */ +static int rtl8365mb_qos_setup_pcp(struct realtek_priv *priv) +{ + int pcp, ret; + + for (pcp = 0; pcp < IEEE_8021Q_MAX_PRIORITIES; pcp++) { + u32 reg = RTL8365MB_QOS_1Q_REMAP_REG(pcp); + int off = RTL8365MB_QOS_1Q_REMAP_OFFSET(pcp); + int tt = ieee8021q_pcp_to_tt(pcp); + + if (tt < 0) + return tt; + + ret = rtl8365mb_set_field(priv, reg, + rtl8365mb_qos_sel_field_mask(off), tt); + if (ret) + return ret; + } + + return 0; +} + +int rtl8365mb_dcb_init(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + int table, ret; + + /* The priority->queue table is a single switch-wide resource, shared by + * all ports (not per-port). + */ + ret = rtl8365mb_qos_setup_queues(priv, ds->num_tx_queues); + if (ret) + return ret; + + ret = rtl8365mb_qos_setup_pcp(priv); + if (ret) + return ret; + + /* Program every decision source in both tables rather than relying on + * the reset state: only the port default carries weight, the other + * seven sources are disabled. apptrust later raises the weights in the + * trusted table and steers ports to it on demand. + */ + for (table = 0; table <= 1; table++) { + int src; + + for (src = 0; src < RTL8365MB_QOS_PRIDEC_NUM_SRC; src++) { + u8 weight = src == RTL8365MB_QOS_PRIDEC_PORT ? + RTL8365MB_QOS_WEIGHT_PORT : + RTL8365MB_QOS_WEIGHT_UNTRUSTED; + + ret = rtl8365mb_qos_set_pridec(priv, table, src, weight); + if (ret) + return ret; + } + } + + return regmap_write(priv->map, RTL8365MB_QOS_PRIDEC_IDX_REG, 0); +} + +int rtl8365mb_dcb_init_port(struct dsa_switch *ds, int port) +{ + int off = RTL8365MB_QOS_PORT_PRI_OFFSET(port); + struct realtek_priv *priv = ds->priv; + + /* All ports default to Best Effort: with no source trusted, every port + * treats its traffic as unmarked, matching the default PCP result + * so classification stays consistent once the admin opts a source in. + */ + return rtl8365mb_set_field(priv, RTL8365MB_QOS_PORT_PRI_REG(port), + rtl8365mb_qos_sel_field_mask(off), + RTL8365MB_QOS_DEFAULT_PRIO); +} + +int rtl8365mb_port_get_default_prio(struct dsa_switch *ds, int port) +{ + int off = RTL8365MB_QOS_PORT_PRI_OFFSET(port); + struct realtek_priv *priv = ds->priv; + u32 val; + int ret; + + ret = rtl8365mb_get_field(priv, RTL8365MB_QOS_PORT_PRI_REG(port), + rtl8365mb_qos_sel_field_mask(off), &val); + if (ret) + return ret; + + /* The register holds the internal priority (an 802.1Q traffic type); + * dcbnl expects an 802.1p priority. + */ + return ieee8021q_tt_to_pcp(val); +} + +int rtl8365mb_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) +{ + int off = RTL8365MB_QOS_PORT_PRI_OFFSET(port); + struct realtek_priv *priv = ds->priv; + int tt; + + if (prio >= IEEE_8021Q_MAX_PRIORITIES) + return -ERANGE; + + /* dcbnl passes an 802.1p priority; the register holds the internal + * priority (an 802.1Q traffic type). + */ + tt = ieee8021q_pcp_to_tt(prio); + if (tt < 0) + return tt; + + return rtl8365mb_set_field(priv, RTL8365MB_QOS_PORT_PRI_REG(port), + rtl8365mb_qos_sel_field_mask(off), tt); +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.h b/drivers/net/dsa/realtek/rtl8365mb_dcb.h new file mode 100644 index 000000000000..4f8068dd3522 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* QoS and DCB configuration for the rtl8365mb switch family */ + +#ifndef _REALTEK_RTL8365MB_DCB_H +#define _REALTEK_RTL8365MB_DCB_H + +#include +#include + +/* The switch has eight internal priorities (0..7). The egress queue count is + * a per-chip property; see struct rtl8365mb_chip_info::num_tx_queues. + */ +#define RTL8365MB_NUM_IPMS 8 + +int rtl8365mb_dcb_init(struct dsa_switch *ds); +int rtl8365mb_dcb_init_port(struct dsa_switch *ds, int port); +int rtl8365mb_port_get_default_prio(struct dsa_switch *ds, int port); +int rtl8365mb_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio); + +#endif /* _REALTEK_RTL8365MB_DCB_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 0924d31790e0..5a3f4328354e 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -108,6 +108,7 @@ #include "realtek-smi.h" #include "realtek-mdio.h" #include "rtl83xx.h" +#include "rtl8365mb_dcb.h" #include "rtl8365mb_l2.h" #include "rtl8365mb_vlan.h" @@ -3187,6 +3188,11 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; + /* Default the port QoS (default priority = Best Effort) */ + ret = rtl8365mb_dcb_init_port(ds, dp->index); + if (ret) + goto out_teardown_irq; + /* Set up per-port private data */ p->priv = priv; p->index = dp->index; @@ -3274,6 +3280,13 @@ static int rtl8365mb_setup(struct dsa_switch *ds) */ ds->num_tx_queues = mb->chip_info->num_tx_queues; + /* Establish a defined QoS baseline: program the priority-to-queue map + * for the chip's queue count and trust only the port default priority. + */ + ret = rtl8365mb_dcb_init(ds); + if (ret) + goto out_teardown_irq; + /* Start statistics counter polling */ ret = rtl8365mb_stats_setup(priv); if (ret) { @@ -3435,6 +3448,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .port_bridge_leave = rtl83xx_port_bridge_leave, .port_pre_bridge_flags = rtl8365mb_port_pre_bridge_flags, .port_bridge_flags = rtl83xx_port_bridge_flags, + .port_get_default_prio = rtl8365mb_port_get_default_prio, + .port_set_default_prio = rtl8365mb_port_set_default_prio, .port_stp_state_set = rtl8365mb_port_stp_state_set, .port_fast_age = rtl83xx_port_fast_age, .port_fdb_add = rtl83xx_port_fdb_add, -- 2.47.3 Wire up port_get_apptrust/port_set_apptrust so the admin can choose, through dcbnl, which ingress classification sources a port trusts for its priority: 802.1p (PCP) and/or DSCP. Nothing but the port default is trusted until this opts in. The hardware exposes two priority-decision weight tables and a per-port bit selecting between them. rtl8365mb_dcb_init() leaves both tables trusting only the port priority; apptrust programs the second ("trusted") table with the requested PCP/DSCP weights and steers the port to it, or back to the untrusted table when the selector list is empty. This driver fixes the precedence to PCP > DSCP via the decision weights, so the selector list is validated to be in that order. Unlike switches with independent per-port trust bits, the trusted table is a single switch-wide resource: all ports that trust something must trust the same selectors. A request that conflicts with the selectors another port is already using is refused with -EBUSY. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/realtek/rtl8365mb_dcb.c | 193 +++++++++++++++++++++++ drivers/net/dsa/realtek/rtl8365mb_dcb.h | 4 + drivers/net/dsa/realtek/rtl8365mb_main.c | 2 + 3 files changed, 199 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.c b/drivers/net/dsa/realtek/rtl8365mb_dcb.c index f48e95ef3947..4afcae6e85a1 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_dcb.c +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.c @@ -13,7 +13,9 @@ #include #include +#include #include +#include #include #include @@ -66,6 +68,8 @@ static_assert(RTL8365MB_NUM_IPMS == IEEE8021Q_TT_MAX); /* Each port selects one of the two decision tables; one bit per port. */ #define RTL8365MB_QOS_PRIDEC_IDX_REG 0x0889 +#define RTL8365MB_QOS_PRIDEC_TABLE_UNTRUSTED 0 +#define RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED 1 /* Priority-decision sources. The hardware numbers eight sources; this driver * programs the three it uses by name and explicitly disables the rest. The @@ -84,6 +88,24 @@ static_assert(RTL8365MB_NUM_IPMS == IEEE8021Q_TT_MAX); #define RTL8365MB_QOS_WEIGHT_UNTRUSTED 0 #define RTL8365MB_QOS_WEIGHT_PORT 1 +/* apptrust selectors this driver supports, in descending precedence. Each + * entry binds a dcbnl selector to the priority-decision source it enables, so + * this ordered table is the one place the fixed precedence lives. + */ +static const struct rtl8365mb_apptrust_map { + u8 sel; /* dcbnl apptrust selector */ + u8 src; /* priority-decision source it enables */ +} rtl8365mb_apptrust_map[] = { + { DCB_APP_SEL_PCP, RTL8365MB_QOS_PRIDEC_1Q }, + { IEEE_8021QAZ_APP_SEL_DSCP, RTL8365MB_QOS_PRIDEC_DSCP }, +}; + +/* rtl8365mb_apptrust_weight() gives the first entry the top weight, + * RTL8365MB_QOS_WEIGHT_PORT + ARRAY_SIZE(rtl8365mb_apptrust_map); fail the + * build if adding a selector would raise it past the decision weight range. + */ +static_assert(RTL8365MB_QOS_WEIGHT_PORT + ARRAY_SIZE(rtl8365mb_apptrust_map) <= 7); + /* The QoS priority and queue selectors are 3-bit register fields; derive a * field's mask from its bit offset. */ @@ -135,6 +157,22 @@ static int rtl8365mb_qos_set_pridec(struct realtek_priv *priv, int table, rtl8365mb_qos_weight_field_mask(off), weight); } +static int rtl8365mb_qos_get_pridec(struct realtek_priv *priv, int table, + int src, u8 *weight) +{ + int off = RTL8365MB_QOS_PRIDEC_OFFSET(src); + u32 val; + int ret; + + ret = rtl8365mb_get_field(priv, rtl8365mb_qos_pridec_reg(table, src), + rtl8365mb_qos_weight_field_mask(off), &val); + if (ret) + return ret; + + *weight = val; + return 0; +} + static int rtl8365mb_qos_setup_queues(struct realtek_priv *priv, unsigned int nq) { @@ -285,3 +323,158 @@ int rtl8365mb_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) return rtl8365mb_set_field(priv, RTL8365MB_QOS_PORT_PRI_REG(port), rtl8365mb_qos_sel_field_mask(off), tt); } + +/* Read which sources a decision table trusts (weight != 0), indexed like + * rtl8365mb_apptrust_map[]. + */ +static int rtl8365mb_apptrust_read(struct realtek_priv *priv, int table, + bool *trust) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_apptrust_map); i++) { + u8 weight; + + ret = rtl8365mb_qos_get_pridec(priv, table, + rtl8365mb_apptrust_map[i].src, + &weight); + if (ret) + return ret; + + trust[i] = weight != RTL8365MB_QOS_WEIGHT_UNTRUSTED; + } + + return 0; +} + +/* Validate the selector list and mark which table entries it trusts. This + * driver fixes the precedence via the decision weights, so the list must be in + * rtl8365mb_apptrust_map[] order. + */ +static int rtl8365mb_apptrust_parse(struct realtek_priv *priv, const u8 *sel, + int nsel, bool *trust) +{ + int i, prev = -1; + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_apptrust_map); i++) + trust[i] = false; + + for (i = 0; i < nsel; i++) { + int idx; + + for (idx = 0; idx < ARRAY_SIZE(rtl8365mb_apptrust_map); idx++) + if (sel[i] == rtl8365mb_apptrust_map[idx].sel) + break; + + if (idx == ARRAY_SIZE(rtl8365mb_apptrust_map) || idx <= prev) { + dev_err(priv->dev, + "unsupported apptrust selector, or not in the driver's fixed precedence order\n"); + return -EINVAL; + } + prev = idx; + trust[idx] = true; + } + + return 0; +} + +/* Trusted sources outrank the port default, and earlier entries in + * rtl8365mb_apptrust_map[] outrank later ones. Deriving the weight from the + * entry's position makes the table order the sole expression of precedence, + * so the order and the hardware weights cannot drift apart. + */ +static u8 rtl8365mb_apptrust_weight(unsigned int entry) +{ + return RTL8365MB_QOS_WEIGHT_PORT + + ARRAY_SIZE(rtl8365mb_apptrust_map) - entry; +} + +int rtl8365mb_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, + int *nsel) +{ + bool trust[ARRAY_SIZE(rtl8365mb_apptrust_map)]; + struct realtek_priv *priv = ds->priv; + int ret, i; + u32 idx; + + *nsel = 0; + + ret = regmap_read(priv->map, RTL8365MB_QOS_PRIDEC_IDX_REG, &idx); + if (ret) + return ret; + + /* On the untrusted table nothing but the port default is trusted. */ + if (!(idx & BIT(port))) + return 0; + + ret = rtl8365mb_apptrust_read(priv, RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED, + trust); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_apptrust_map); i++) + if (trust[i]) + sel[(*nsel)++] = rtl8365mb_apptrust_map[i].sel; + + return 0; +} + +int rtl8365mb_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, + int nsel) +{ + bool trust[ARRAY_SIZE(rtl8365mb_apptrust_map)]; + struct realtek_priv *priv = ds->priv; + bool any = false; + int ret, i; + u32 idx; + + ret = rtl8365mb_apptrust_parse(priv, sel, nsel, trust); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_apptrust_map); i++) + any |= trust[i]; + + ret = regmap_read(priv->map, RTL8365MB_QOS_PRIDEC_IDX_REG, &idx); + if (ret) + return ret; + + /* Nothing trusted: point the port at the untrusted table. */ + if (!any) + return rtl8365mb_set_field(priv, RTL8365MB_QOS_PRIDEC_IDX_REG, + BIT(port), 0); + + /* The trusted table is a single switch-wide resource. If another port + * already uses it, this request must trust the same selectors. + */ + if (idx & ~BIT(port)) { + bool other[ARRAY_SIZE(rtl8365mb_apptrust_map)]; + + ret = rtl8365mb_apptrust_read(priv, + RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED, + other); + if (ret) + return ret; + + if (memcmp(trust, other, sizeof(trust))) { + dev_err(priv->dev, + "trust profile is switch-wide; another port already trusts different sources\n"); + return -EBUSY; + } + } + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_apptrust_map); i++) { + u8 weight = trust[i] ? rtl8365mb_apptrust_weight(i) : + RTL8365MB_QOS_WEIGHT_UNTRUSTED; + + ret = rtl8365mb_qos_set_pridec(priv, + RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED, + rtl8365mb_apptrust_map[i].src, + weight); + if (ret) + return ret; + } + + return rtl8365mb_set_field(priv, RTL8365MB_QOS_PRIDEC_IDX_REG, + BIT(port), 1); +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.h b/drivers/net/dsa/realtek/rtl8365mb_dcb.h index 4f8068dd3522..156fa6e1c4fd 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_dcb.h +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.h @@ -16,5 +16,9 @@ int rtl8365mb_dcb_init(struct dsa_switch *ds); int rtl8365mb_dcb_init_port(struct dsa_switch *ds, int port); int rtl8365mb_port_get_default_prio(struct dsa_switch *ds, int port); int rtl8365mb_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio); +int rtl8365mb_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, + int *nsel); +int rtl8365mb_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, + int nsel); #endif /* _REALTEK_RTL8365MB_DCB_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 5a3f4328354e..77f971abf783 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -3450,6 +3450,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .port_bridge_flags = rtl83xx_port_bridge_flags, .port_get_default_prio = rtl8365mb_port_get_default_prio, .port_set_default_prio = rtl8365mb_port_set_default_prio, + .port_get_apptrust = rtl8365mb_port_get_apptrust, + .port_set_apptrust = rtl8365mb_port_set_apptrust, .port_stp_state_set = rtl8365mb_port_stp_state_set, .port_fast_age = rtl83xx_port_fast_age, .port_fdb_add = rtl83xx_port_fdb_add, -- 2.47.3 Wire up port_get_dscp_prio/port_add_dscp_prio/port_del_dscp_prio so dcbnl can program the DSCP-to-internal-priority mapping. The mapping only takes effect on ports that trust DSCP via apptrust, so it stays inert on the default (untrusted) configuration. The DSCP table is a single switch-wide resource, so set ds->dscp_prio_mapping_is_global and let the DSA core keep the app table consistent across ports; no per-port bookkeeping is needed. add and del write the global table; del reverts the entry to the standard IETF default for that DSCP (ietf_dscp_to_ieee8021q_tt()) and only when the table still holds the priority being removed, matching dcbnl's replace sequence. rtl8365mb_dcb_init() seeds the whole table with the same IETF mapping so the switch behaves predictably as soon as DSCP is trusted, rather than relying on the chip reset value. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/realtek/rtl8365mb_dcb.c | 113 ++++++++++++++++++++++- drivers/net/dsa/realtek/rtl8365mb_dcb.h | 5 + drivers/net/dsa/realtek/rtl8365mb_main.c | 4 + 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.c b/drivers/net/dsa/realtek/rtl8365mb_dcb.c index 4afcae6e85a1..091dc8189e73 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_dcb.c +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.c @@ -54,10 +54,16 @@ static_assert(RTL8365MB_NUM_IPMS == IEEE8021Q_TT_MAX); #define RTL8365MB_QOS_PORT_PRI_OFFSET(_p) (((_p) & 0x3) << 2) /* Default internal priority for unmarked traffic. Best Effort, to match what - * an untagged (PCP 0) frame resolves to via ieee8021q_pcp_to_tt(). + * an untagged (PCP 0) frame and a default-marked (DSCP CS0) frame resolve to + * via ieee8021q_pcp_to_tt() and ietf_dscp_to_ieee8021q_tt(). */ #define RTL8365MB_QOS_DEFAULT_PRIO IEEE8021Q_TT_BE +/* DSCP -> internal priority. Global table, four DSCP per register, 3-bit. */ +#define RTL8365MB_QOS_DSCP_PRI_REG(_d) (0x0867 + ((_d) >> 2)) +#define RTL8365MB_QOS_DSCP_PRI_OFFSET(_d) (((_d) & 0x3) << 2) +#define RTL8365MB_DSCP_MAX 64 + /* Priority-decision weight tables. Two tables (each port selects one), eight * sources, one 8-bit weight each, two sources per register. Higher weight * wins; a weight of zero disables the source. @@ -142,6 +148,15 @@ static int rtl8365mb_get_field(struct realtek_priv *priv, u32 reg, u32 mask, return 0; } +static int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, + u8 prio) +{ + int off = RTL8365MB_QOS_DSCP_PRI_OFFSET(dscp); + + return rtl8365mb_set_field(priv, RTL8365MB_QOS_DSCP_PRI_REG(dscp), + rtl8365mb_qos_sel_field_mask(off), prio); +} + static u32 rtl8365mb_qos_pridec_reg(int table, int src) { return table ? RTL8365MB_QOS_PRIDEC_TBL1_REG(src) : @@ -234,6 +249,27 @@ static int rtl8365mb_qos_setup_pcp(struct realtek_priv *priv) return 0; } +/* Seed the DSCP -> priority table with the standard IETF mapping, so it is + * meaningful once a port opts in to trusting DSCP via apptrust. + */ +static int rtl8365mb_qos_setup_dscp(struct realtek_priv *priv) +{ + int dscp, ret; + + for (dscp = 0; dscp < RTL8365MB_DSCP_MAX; dscp++) { + int tt = ietf_dscp_to_ieee8021q_tt(dscp); + + if (tt < 0) + return tt; + + ret = rtl8365mb_qos_set_dscp_prio(priv, dscp, tt); + if (ret) + return ret; + } + + return 0; +} + int rtl8365mb_dcb_init(struct dsa_switch *ds) { struct realtek_priv *priv = ds->priv; @@ -250,6 +286,10 @@ int rtl8365mb_dcb_init(struct dsa_switch *ds) if (ret) return ret; + ret = rtl8365mb_qos_setup_dscp(priv); + if (ret) + return ret; + /* Program every decision source in both tables rather than relying on * the reset state: only the port default carries weight, the other * seven sources are disabled. apptrust later raises the weights in the @@ -278,7 +318,7 @@ int rtl8365mb_dcb_init_port(struct dsa_switch *ds, int port) struct realtek_priv *priv = ds->priv; /* All ports default to Best Effort: with no source trusted, every port - * treats its traffic as unmarked, matching the default PCP result + * treats its traffic as unmarked, matching the default PCP/DSCP result * so classification stays consistent once the admin opts a source in. */ return rtl8365mb_set_field(priv, RTL8365MB_QOS_PORT_PRI_REG(port), @@ -324,6 +364,75 @@ int rtl8365mb_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) rtl8365mb_qos_sel_field_mask(off), tt); } +int rtl8365mb_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) +{ + int off = RTL8365MB_QOS_DSCP_PRI_OFFSET(dscp); + struct realtek_priv *priv = ds->priv; + u32 val; + int ret; + + if (dscp >= RTL8365MB_DSCP_MAX) + return -EINVAL; + + ret = rtl8365mb_get_field(priv, RTL8365MB_QOS_DSCP_PRI_REG(dscp), + rtl8365mb_qos_sel_field_mask(off), &val); + if (ret) + return ret; + + /* The register holds the internal priority (an 802.1Q traffic type); + * dcbnl expects an 802.1p priority. + */ + return ieee8021q_tt_to_pcp(val); +} + +int rtl8365mb_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct realtek_priv *priv = ds->priv; + int tt; + + if (dscp >= RTL8365MB_DSCP_MAX) + return -EINVAL; + + if (prio >= IEEE_8021Q_MAX_PRIORITIES) + return -ERANGE; + + /* dcbnl passes an 802.1p priority; the register holds the internal + * priority (an 802.1Q traffic type). + */ + tt = ieee8021q_pcp_to_tt(prio); + if (tt < 0) + return tt; + + return rtl8365mb_qos_set_dscp_prio(priv, dscp, tt); +} + +int rtl8365mb_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct realtek_priv *priv = ds->priv; + int tt, ret; + + if (dscp >= RTL8365MB_DSCP_MAX) + return -EINVAL; + + /* dcbnl replaces an entry by adding the new one before deleting the + * old, so only revert if the table still holds the removed priority. + */ + ret = rtl8365mb_port_get_dscp_prio(ds, port, dscp); + if (ret < 0) + return ret; + if (ret != prio) + return 0; + + /* Revert to the standard IETF default mapping for this DSCP. */ + tt = ietf_dscp_to_ieee8021q_tt(dscp); + if (tt < 0) + return tt; + + return rtl8365mb_qos_set_dscp_prio(priv, dscp, tt); +} + /* Read which sources a decision table trusts (weight != 0), indexed like * rtl8365mb_apptrust_map[]. */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_dcb.h b/drivers/net/dsa/realtek/rtl8365mb_dcb.h index 156fa6e1c4fd..ebe6f1628734 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_dcb.h +++ b/drivers/net/dsa/realtek/rtl8365mb_dcb.h @@ -20,5 +20,10 @@ int rtl8365mb_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel); int rtl8365mb_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, int nsel); +int rtl8365mb_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp); +int rtl8365mb_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); +int rtl8365mb_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); #endif /* _REALTEK_RTL8365MB_DCB_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 77f971abf783..9fe87c295795 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -3283,6 +3283,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) /* Establish a defined QoS baseline: program the priority-to-queue map * for the chip's queue count and trust only the port default priority. */ + ds->dscp_prio_mapping_is_global = true; ret = rtl8365mb_dcb_init(ds); if (ret) goto out_teardown_irq; @@ -3452,6 +3453,9 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .port_set_default_prio = rtl8365mb_port_set_default_prio, .port_get_apptrust = rtl8365mb_port_get_apptrust, .port_set_apptrust = rtl8365mb_port_set_apptrust, + .port_get_dscp_prio = rtl8365mb_port_get_dscp_prio, + .port_add_dscp_prio = rtl8365mb_port_add_dscp_prio, + .port_del_dscp_prio = rtl8365mb_port_del_dscp_prio, .port_stp_state_set = rtl8365mb_port_stp_state_set, .port_fast_age = rtl83xx_port_fast_age, .port_fdb_add = rtl83xx_port_fdb_add, -- 2.47.3