From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E6D329992A for ; Sat, 16 May 2026 03:47:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.50 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778903264; cv=none; b=nxkhEogM8ftUIL1mmyYKsYuB6a5APxsMHEUpOhDiFVdtMhMqTapo5a4y/ACJjYCobn7kZgfsyKeKeXirhS9te+3K9BgUtHNUU8Gcprf8p0+07a533ErWVCEymxSx6u7Im0CfQXNcuvvHecCt/7HilenDiodENJInSmEoj+43vPM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778903264; c=relaxed/simple; bh=uqAn/p3Xuhr7ussV2ofCClM+ENxJccl1uBIW4tGfojI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N23sGGjZKoyHWwWecE4Q3K067RxP53+bHRJSvo+6vwgDbH69xthfDPLVhGdZA1kRJED1NpFx9SRTKByjDsq2eErCWHPXimlYBweyZUpLiRPJ0RDiwVHLmjoFVwdTknKac0zwDTg7OjiKgy4rRqbY5DZxNWrlNnjcKOlEBqGnu0A= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IxY9uGSj; arc=none smtp.client-ip=209.85.219.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IxY9uGSj" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-8b7105dfb35so8721706d6.3 for ; Fri, 15 May 2026 20:47:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778903262; x=1779508062; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LH1urpZdWyi/0tcsSLzpGgmN4hcoBNM2XecVgIljFjM=; b=IxY9uGSjQPPDDHyLoxmADE4mEFtxaeGRRBB7UL2kVJDwBRD7RiFrW2guJuzYoHc2U3 xQoyilG9TNey0ZItfmCmNa/OIM6CYEYxrJeziN+gZDvw0Bzr/jKtl2cMzq++NZObp0NR 9ZRxj+XXhVYt6zHVnbThpJa7K4UWA0g8qBh59z/ZstVv7Vf7nBsBnW60Dn4YWhwL98Ux FXz0NoTs2wcY9CzE0D2NsaC8vD3yWJ4zD5dTGmzWgZT+ZUYVXcW38HgBY2lUmSRhTIvT F0F0BY4OpZZPt7uwwj+sSjqo6U2PkjFOHIa0ZeJyaFw+XQY5iAfvhrMoEPgyOSfUnbIb 9SlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778903262; x=1779508062; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LH1urpZdWyi/0tcsSLzpGgmN4hcoBNM2XecVgIljFjM=; b=jB+SaEeOchz57ejKzIA/vZZM1dBZ1xet1SDJz/zOTDokSeNdo09L0x0mKmaDZZ1R6g H+6y/Y/Lq5s3wwtKtJ/FBf0hrn8kS4i5G3W05fs5WOVefERFn8grusUq8YZo6NNfEwLi aiY/ixXkpJdV4vDGLLl3pssgZrvSnqalbv/ezpmICMdtYwsQDg5da+yIeG3rdKfkg3SM 55OqT2KiCBUKFKn05a7QwH8dP7bVEsoAOJj8AkeBy0aPRBrqi2nlAUXTcJwsiR6jclKm 6n2XzMcNe+XS2i5duYN98rt/Eohx44rQic8iws3glOEdYP+3pzNFqGNeveaDxf1KhM2M Cl0Q== X-Forwarded-Encrypted: i=1; AFNElJ9MBHIP+bbcQU0jNimeVLEpEeiDVOnFrJSkHbtSJm4LTVZdQShzY8RKE162LYTRUVrIxV51YLmm+C2RayI=@vger.kernel.org X-Gm-Message-State: AOJu0YzwYCm4tkVhXr09eIRg0Nt61grqGX7YJ60z84PEt6+ig8VRTu0+ EWHtx8yStZnPkljADT9O7AZS/jxx40DLoB/QSbkNPTn/0fKWYUhIuW+o X-Gm-Gg: Acq92OG6DrukpNZpIiD7Ofjh1gSlZXwteJ/aqbDMuJCZHIrXy89GOp9TPejAtwBEyt0 JET6j4Ee6J1xb8mm3nglr81nNCqum1PQJJTKhEP3j8r8lhvkSOlQZk4ca431xaFrUoihHctrLiS w2TYw8nVq4OLsGS8ivIlMRElsYC4A5uUphULZM6q1SLMJnh83DzU7j0YiO33dPCKAib2U3lmfeF 1RX2NbH039eVC3QRlwp9yE4xm+3ufT2VzapQ3i588BfxklepgTeWdTPOsu8R2b4l5KKboXZJDOi HJD7SbqWbA8MqpQF/QJrrRe2P+IIQFMDkhwT0EI3eL+bWsKnUWJ3H7ml7uHom8AFE9qxortuW0m OuNQoAxUSgkNGOnwgLani6pCccB/5QN7ehZoY0qkTI5xjyUuLjav9OdPXUdt7rFyQx6kKqtdqDl qWmQ6bYxMhWJzkLdtvNGT99LX9Ky3liOi67FOU+eo= X-Received: by 2002:ad4:4183:0:b0:8ac:af21:5746 with SMTP id 6a1803df08f44-8ca0f706681mr79360286d6.42.1778903261487; Fri, 15 May 2026 20:47:41 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8c908d1d2e7sm70874546d6.16.2026.05.15.20.47.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 20:47:40 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Sat, 16 May 2026 00:46:22 -0300 Subject: [net-next PATCH v4 6/8] net: dsa: realtek: rtl8365mb: add port_bridge_{join,leave} Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Message-Id: <20260516-realtek_forward-v4-6-8b6d6a1eefdc@gmail.com> References: <20260516-realtek_forward-v4-0-8b6d6a1eefdc@gmail.com> In-Reply-To: <20260516-realtek_forward-v4-0-8b6d6a1eefdc@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.2 From: Alvin Šipraga Implement hardware offloading of bridge functionality. This is achieved by using the per-port isolation registers, which contain a forwarding port mask. The switch will refuse to forward packets ingressed on a given port to a port which is not in its forwarding mask. For each bridge that is offloaded, use the DSA-provided bridge number for the Extended Filtering ID (EFID). When using Independent VLAN Learning (IVL), the forwarding database is keyed with the tuple {VID, MAC, EFID}. There are 8 EFIDs available (0~7), but we reserve the default EFID 0 for standalone ports where learning is disabled. This fits nicely because DSA indexes the bridge number starting from 1. Because of the limited number of EFIDs, we have to set the max_num_bridges property of our switch to 7: we can't offload more than that or we will fail to offer IVL as at least two bridges would end up having to share an EFID. Co-developed-by: Alvin Šipraga Signed-off-by: Alvin Šipraga Reviewed-by: Linus Walleij Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/realtek.h | 5 ++ drivers/net/dsa/realtek/rtl8365mb_main.c | 57 ++++++++++++- drivers/net/dsa/realtek/rtl83xx.c | 141 +++++++++++++++++++++++++++++++ drivers/net/dsa/realtek/rtl83xx.h | 7 ++ 4 files changed, 209 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index c03485a80d93..0942f534834d 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -107,6 +107,11 @@ struct realtek_ops { int (*enable_vlan)(struct realtek_priv *priv, bool enable); int (*enable_vlan4k)(struct realtek_priv *priv, bool enable); int (*enable_port)(struct realtek_priv *priv, int port, bool enable); + int (*port_add_isolation)(struct realtek_priv *priv, int port, + u32 mask); + int (*port_remove_isolation)(struct realtek_priv *priv, int port, + u32 mask); + int (*port_set_efid)(struct realtek_priv *priv, int port, u32 efid); int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, u16 val); diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 90c3001d31f2..b951a2d4c7e3 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -285,6 +285,15 @@ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF +/* Extended filter ID registers - used to key forwarding database with IVL */ +#define RTL8365MB_EFID_MASK GENMASK(2, 0) +#define RTL8365MB_PORT_EFID_REG_BASE 0x0A32 +#define RTL8365MB_PORT_EFID_REG(_p) \ + (RTL8365MB_PORT_EFID_REG_BASE + ((_p) >> 2)) +#define RTL8365MB_PORT_EFID_OFFSET(_p) (((_p) & 0x3) << 2) +#define RTL8365MB_PORT_EFID_MASK(_p) \ + (RTL8365MB_EFID_MASK << RTL8365MB_PORT_EFID_OFFSET(_p)) + /* MSTP port state registers - indexed by tree instance */ #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ @@ -1459,10 +1468,44 @@ static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); } +static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, + u32 efid) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_EFID_REG(port), + RTL8365MB_PORT_EFID_MASK(port), + efid << RTL8365MB_PORT_EFID_OFFSET(port)); +} + +/* Port isolation manipulation functions. + * + * The port isolation register controls the forwarding mask of a given + * port. The switch will not forward packets ingressed on a given port + * to ports which are not enabled in its forwarding mask. + * + * The port forwarding mask has the highest priority in forwarding + * decisions. The only exception to this rule is when the switch + * receives a packet on its CPU port with ALLOW=0. In that case the TX + * field of the CPU tag will override the forwarding port mask. + */ static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, u32 mask) { - return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask); +} + +static int rtl8365mb_port_add_isolation(struct realtek_priv *priv, int port, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, mask); +} + +static int rtl8365mb_port_remove_isolation(struct realtek_priv *priv, int port, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, 0); } static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, @@ -2249,6 +2292,11 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; + /* Set the default EFID 0 for standalone mode */ + ret = rtl8365mb_port_set_efid(priv, dp->index, 0); + if (ret) + goto out_teardown_irq; + /* Disable learning */ ret = rtl8365mb_port_set_learning(priv, dp->index, false); if (ret) @@ -2310,6 +2358,8 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; + /* The EFID is 3 bits, but EFID 0 is reserved for standalone ports */ + ds->max_num_bridges = FIELD_MAX(RTL8365MB_EFID_MASK); ds->configure_vlan_while_not_filtering = true; /* Set up VLAN */ @@ -2426,6 +2476,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, .phylink_get_caps = rtl8365mb_phylink_get_caps, + .port_bridge_join = rtl83xx_port_bridge_join, + .port_bridge_leave = rtl83xx_port_bridge_leave, .port_stp_state_set = rtl8365mb_port_stp_state_set, .port_vlan_add = rtl8365mb_port_vlan_add, .port_vlan_del = rtl8365mb_port_vlan_del, @@ -2445,6 +2497,9 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { static const struct realtek_ops rtl8365mb_ops = { .detect = rtl8365mb_detect, + .port_add_isolation = rtl8365mb_port_add_isolation, + .port_remove_isolation = rtl8365mb_port_remove_isolation, + .port_set_efid = rtl8365mb_port_set_efid, .phy_read = rtl8365mb_phy_read, .phy_write = rtl8365mb_phy_write, }; diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 2b9bd4462714..ae4945c917f2 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -325,6 +325,147 @@ void rtl83xx_reset_deassert(struct realtek_priv *priv) gpiod_set_value(priv->reset, false); } +/** + * rtl83xx_port_bridge_join() - join a port to a bridge + * @ds: DSA switch instance + * @port: port index + * @bridge: bridge being joined + * @tx_forward_offload: if the switch can offload TX forwarding + * @extack: netlink extended ack for reporting errors + * + * This function handles joining a port to a bridge. It updates the port + * isolation masks and EFID. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_forward_offload, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + struct dsa_port *dp; + u32 mask = 0; + int ret; + + if (!priv->ops->port_add_isolation) + return -EOPNOTSUPP; + + dev_dbg(priv->dev, "bridge %d join port %d\n", bridge.num, port); + + /* Add this port to the isolation group of every other port + * offloading this bridge. + */ + dsa_switch_for_each_user_port(dp, ds) { + /* Handle this port after */ + if (dp->index == port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dp, &bridge)) + continue; + + ret = priv->ops->port_add_isolation(priv, dp->index, BIT(port)); + if (ret) + goto undo_isolation; + + mask |= BIT(dp->index); + } + + /* If we support cascade switches, it should also include the + * downstream DSA ports to the isolation group. + */ + + /* Add those ports to the isolation group of this port */ + ret = priv->ops->port_add_isolation(priv, port, mask); + if (ret) + goto undo_isolation; + + /* Use the bridge number as the EFID for this port */ + if (priv->ops->port_set_efid) { + ret = priv->ops->port_set_efid(priv, port, bridge.num); + if (ret) + goto undo_self_isolation; + } + + return 0; + +undo_self_isolation: + priv->ops->port_remove_isolation(priv, port, mask); + +undo_isolation: + dsa_switch_for_each_port_continue_reverse(dp, ds) { + if (dp->index == port) + continue; + + if (!dsa_port_offloads_bridge(dp, &bridge)) + continue; + + if (!dsa_port_is_user((dp))) + continue; + + priv->ops->port_remove_isolation(priv, dp->index, BIT(port)); + } + + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_join, "REALTEK_DSA"); + +/** + * rtl83xx_port_bridge_leave() - leave a bridge + * @ds: DSA switch instance + * @port: port index + * @bridge: bridge being left + * + * This function handles removing a port from a bridge. It updates the port + * isolation masks and EFID. + * + * Context: Can sleep. + * Return: nothing + */ +void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct realtek_priv *priv = ds->priv; + struct dsa_port *dp; + u32 mask = 0; + + if (!priv->ops->port_remove_isolation) + return; + + dev_dbg(priv->dev, "bridge %d leave port %d\n", bridge.num, port); + + /* Remove this port from the isolation group of every other + * port offloading this bridge. + */ + dsa_switch_for_each_user_port(dp, ds) { + /* Handle this port after */ + if (dp->index == port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dp, &bridge)) + continue; + + priv->ops->port_remove_isolation(priv, dp->index, BIT(port)); + + mask |= BIT(dp->index); + } + + /* If we support cascade switches, it should also exclude the + * downstream DSA ports from the isolation group. + */ + + /* Remove those ports from the isolation group of this port */ + priv->ops->port_remove_isolation(priv, port, mask); + + /* Revert to the default EFID 0 for standalone mode */ + if (priv->ops->port_set_efid) + priv->ops->port_set_efid(priv, port, 0); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_leave, "REALTEK_DSA"); + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("Realtek DSA switches common module"); diff --git a/drivers/net/dsa/realtek/rtl83xx.h b/drivers/net/dsa/realtek/rtl83xx.h index c8a0ff8fd75e..2481a1aaa226 100644 --- a/drivers/net/dsa/realtek/rtl83xx.h +++ b/drivers/net/dsa/realtek/rtl83xx.h @@ -21,4 +21,11 @@ void rtl83xx_remove(struct realtek_priv *priv); void rtl83xx_reset_assert(struct realtek_priv *priv); void rtl83xx_reset_deassert(struct realtek_priv *priv); +int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_forward_offload, + struct netlink_ext_ack *extack); +void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); + #endif /* _RTL83XX_H */ -- 2.54.0