From patchwork Wed Feb 10 09:14:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380749 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0B0B6C433E0 for ; Wed, 10 Feb 2021 09:23:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BC0E964DB1 for ; Wed, 10 Feb 2021 09:23:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230327AbhBJJXP (ORCPT ); Wed, 10 Feb 2021 04:23:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229918AbhBJJUt (ORCPT ); Wed, 10 Feb 2021 04:20:49 -0500 Received: from mail-ej1-x62e.google.com (mail-ej1-x62e.google.com [IPv6:2a00:1450:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 61525C06178B; Wed, 10 Feb 2021 01:19:15 -0800 (PST) Received: by mail-ej1-x62e.google.com with SMTP id hs11so2850583ejc.1; Wed, 10 Feb 2021 01:19:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ofMEhxMiCqoUxSdxQGlg/4fbYnTq6VRKlipmzktyglE=; b=DNc4ZMfUPTMxhOlufkOpA7zgX+G+m3fUf5tNwkzpXRNJF7X5zUPiEfJ9l0hMgwzqvD PFADy3Slpv34ezNKoJLyc4wd13Di3YF7kCeZ1pKZDE1FBQYYqYwkTCFm9UnzPkFTNwBW jaFsSF5wFCd1BwsJmdg0HQbaPWoJSqFqT4e699xTjK3+p5yz2akkow0p594WxDPfOnmK c0ajFqfGMPEBd7Vl71mu6s4/dcvEgjUz26rGhjtdegcFKxSHM6QQr0xA3Sx+/D00IzhY kVtUHCk1NLbl0O4y59JLCC3sL7pRC7WQdKB+goA+i/OjQwfE0ERMCRUQCb3geKJ3bShb UUlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ofMEhxMiCqoUxSdxQGlg/4fbYnTq6VRKlipmzktyglE=; b=R2Ch5advtJ63P4lXL8pYSVHu14F+wSvbGbS/hg/d1vFGXYMx8W3dRjIvJSR7Z+gK7b boM5m6FAvp6mY/f05DFKSMK/Yuddx9Lh4WMjbXMi9RPqO+a60rQ0gQnW/M7DAcXmVGfX Dhj6Z1k9kSkxOvVk2xC46vr4tABUGTTl2P2QOj7A+U60NamL4nfHiigHLd752MUvnCs6 hNKAnwFj9MBnCgkZQHwG8rQlAZAr0cUTvjx6fsPYyG2GHVb3w7MFPfaEpH0N80bZosbF rrlyIQ5AEVmviHcPg2rOPXCcaINwIete7QCYFthssmKyVErrjo4rFQrCXFw71ht4yKjF OhCg== X-Gm-Message-State: AOAM533lq8UfycBPwlZGFr7yPZGSH/YpTIXN53/9ki7eeeycMPSWW9VQ ug2NLvxjiqkETjRGwUBpi5s= X-Google-Smtp-Source: ABdhPJwIbcfyGXTW6Rrkd4z46Iqso4r4/kBMI989QbAv80kvpDifPCN3Y8U6mup+NyI/Ycv68/Ly8w== X-Received: by 2002:a17:906:cc56:: with SMTP id mm22mr2008438ejb.181.1612948754098; Wed, 10 Feb 2021 01:19:14 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:13 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 01/11] net: switchdev: propagate extack to port attributes Date: Wed, 10 Feb 2021 11:14:35 +0200 Message-Id: <20210210091445.741269-2-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean When a struct switchdev_attr is notified through switchdev, there is no way to report informational messages, unlike for struct switchdev_obj. Signed-off-by: Vladimir Oltean Reviewed-by: Ido Schimmel Reviewed-by: Florian Fainelli --- Changes in v3: None. Changes in v2: Patch is new. .../ethernet/marvell/prestera/prestera_switchdev.c | 3 ++- .../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 3 ++- drivers/net/ethernet/mscc/ocelot_net.c | 3 ++- drivers/net/ethernet/ti/cpsw_switchdev.c | 3 ++- include/net/switchdev.h | 6 ++++-- net/dsa/slave.c | 3 ++- net/switchdev/switchdev.c | 11 ++++++++--- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index 8c2b03151736..2c1619715a4b 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -695,7 +695,8 @@ static int prestera_port_attr_stp_state_set(struct prestera_port *port, } static int prestera_port_obj_attr_set(struct net_device *dev, - const struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) { struct prestera_port *port = netdev_priv(dev); int err = 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 20c4f3c2cf23..18e4f1cd5587 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -887,7 +887,8 @@ mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err; diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 8f12fa45b1b5..f9da4aa39444 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -1005,7 +1005,8 @@ static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) } static int ocelot_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) { struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot *ocelot = priv->port.ocelot; diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c index 9967cf985728..13524cbaa8b6 100644 --- a/drivers/net/ethernet/ti/cpsw_switchdev.c +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c @@ -83,7 +83,8 @@ static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, } static int cpsw_port_attr_set(struct net_device *ndev, - const struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) { struct cpsw_priv *priv = netdev_priv(ndev); int ret; diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 88fcac140966..84c765312001 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -283,7 +283,8 @@ int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), int (*set_cb)(struct net_device *dev, - const struct switchdev_attr *attr)); + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack)); #else static inline void switchdev_deferred_process(void) @@ -374,7 +375,8 @@ switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), int (*set_cb)(struct net_device *dev, - const struct switchdev_attr *attr)) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack)) { return 0; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 431bdbdd8473..8f4c7c232e2c 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -271,7 +271,8 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } static int dsa_slave_port_attr_set(struct net_device *dev, - const struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) { struct dsa_port *dp = dsa_slave_to_port(dev); int ret; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 94113ca29dcf..0b84f076591e 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -488,14 +488,18 @@ static int __switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), int (*set_cb)(struct net_device *dev, - const struct switchdev_attr *attr)) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack)) { + struct netlink_ext_ack *extack; struct net_device *lower_dev; struct list_head *iter; int err = -EOPNOTSUPP; + extack = switchdev_notifier_info_to_extack(&port_attr_info->info); + if (check_cb(dev)) { - err = set_cb(dev, port_attr_info->attr); + err = set_cb(dev, port_attr_info->attr, extack); if (err != -EOPNOTSUPP) port_attr_info->handled = true; return err; @@ -525,7 +529,8 @@ int switchdev_handle_port_attr_set(struct net_device *dev, struct switchdev_notifier_port_attr_info *port_attr_info, bool (*check_cb)(const struct net_device *dev), int (*set_cb)(struct net_device *dev, - const struct switchdev_attr *attr)) + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack)) { int err; From patchwork Wed Feb 10 09:14:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380748 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2A951C433E0 for ; Wed, 10 Feb 2021 09:24:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E0DFA64E0B for ; Wed, 10 Feb 2021 09:24:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230156AbhBJJYL (ORCPT ); Wed, 10 Feb 2021 04:24:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59828 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229937AbhBJJUx (ORCPT ); Wed, 10 Feb 2021 04:20:53 -0500 Received: from mail-ej1-x62f.google.com (mail-ej1-x62f.google.com [IPv6:2a00:1450:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C5DC2C061794; Wed, 10 Feb 2021 01:19:20 -0800 (PST) Received: by mail-ej1-x62f.google.com with SMTP id bl23so2827399ejb.5; Wed, 10 Feb 2021 01:19:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=oxbGJHr2KscLJg3Jp7wNmlUMCtlznjqgTotwuzodnos=; b=EQtiSoYI3Xw5uRAtZwkexizLRt1f3HgSFAUttbijJLbYb7M9rB3WAG5SyY/tmTW/VS p9ZFxnK77Gq1ff6vY0astsy6Ra7I3k1DGWtULUD8v+gPV0hnOHKNIwoGLK4//rkFAqZD Sb4wTXl+ndq87klUNQYtyEm/WI2OMW1gB1l3Qt0dYnUsGoYPF3q+wy7+TrHjC/Xem2QA G3V0MrOAZsvPmCX92c0Uj2C56N/tqHrjgM6x0qQgrRkTJ+fg8g2Oj+KFqkoT4tJnf5gF /ZLhk+ieYSrrekB5uxl7fCjHlR5Suy+UXFY7Iv+YTA8nB4eLE0KQbXL5mvKNz2nWFUtN 8Z+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=oxbGJHr2KscLJg3Jp7wNmlUMCtlznjqgTotwuzodnos=; b=rRJQt9c9CNJIFXiN0CaenQZJgsBtwSb+K+ZX6A90atxvRWVcqUWm+5hSABGhqm0aXu FMhjFUeeqkd3lrPq5mTFDDOR3Z8sp+Qu5OUbaKc5lnFKpQaNxAdds7+hkOkF+3bi5QNN crjirkbjVWN2AJN5EL8DcWJ/JCEL9u258i9r62aY4F7J0vg22AAaKR5e0uNs08RF4Ntv LxvHUTw4gZmfsmEEKEX98eZAE6o+GeNMvZOJluOSItnKT96ldreeODpXCz6jC3aN3Bqi 37lgYNjFpOIaLyqGyveInDujmBdXzHkcnkfdjePFJy+yjnAujYjsqvnokO02qiTAESgv YC0A== X-Gm-Message-State: AOAM533YvRfqoeE5fC41GV8mE+pUr8QRSD2XW3LuQeFTgC0Uy9lzVwnE sWFmH/Ib1EzLP231OR/vjdo= X-Google-Smtp-Source: ABdhPJxdlFHG7ODZylr+Ph6nD4vX1N2CPPbhFJFobSzxHe1QgaEkyuGjSlGSO1p0ZUQ/gc5q8zdZbA== X-Received: by 2002:a17:906:17d5:: with SMTP id u21mr2039544eje.541.1612948759519; Wed, 10 Feb 2021 01:19:19 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:18 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 04/11] net: dsa: configure proper brport flags when ports leave the bridge Date: Wed, 10 Feb 2021 11:14:38 +0200 Message-Id: <20210210091445.741269-5-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean For a DSA switch port operating in standalone mode, address learning doesn't make much sense since that is a bridge function. In fact, address learning even breaks setups such as this one: +---------------------------------------------+ | | | +-------------------+ | | | br0 | send receive | | +--------+-+--------+ +--------+ +--------+ | | | | | | | | | | | | | swp0 | | swp1 | | swp2 | | swp3 | | | | | | | | | | | | +-+--------+-+--------+-+--------+-+--------+-+ | ^ | ^ | | | | | +-----------+ | | | +--------------------------------+ because if the switch has a single FDB (can offload a single bridge) then source address learning on swp3 can "steal" the source MAC address of swp2 from br0's FDB, because learning frames coming from swp2 will be done twice: first on the swp1 ingress port, second on the swp3 ingress port. So the hardware FDB will become out of sync with the software bridge, and when swp2 tries to send one more packet towards swp1, the ASIC will attempt to short-circuit the forwarding path and send it directly to swp3 (since that's the last port it learned that address on), which it obviously can't, because swp3 operates in standalone mode. So DSA drivers operating in standalone mode should still configure a list of bridge port flags even when they are standalone. Currently DSA attempts to call dsa_port_bridge_flags with 0, which disables egress flooding of unknown unicast and multicast, something which doesn't make much sense. For the switches that implement .port_egress_floods - b53 and mv88e6xxx, it probably doesn't matter too much either, since they can possibly inject traffic from the CPU into a standalone port, regardless of MAC DA, even if egress flooding is turned off for that port, but certainly not all DSA switches can do that - sja1105, for example, can't. So it makes sense to use a better common default there, such as "flood everything". It should also be noted that what DSA calls "dsa_port_bridge_flags()" is a degenerate name for just calling .port_egress_floods(), since nothing else is implemented - not learning, in particular. But disabling address learning, something that this driver is also coding up for, will be supported by individual drivers once .port_egress_floods is replaced with a more generic .port_bridge_flags. Previous attempts to code up this logic have been in the common bridge layer, but as pointed out by Ido Schimmel, there are corner cases that are missed when doing that: https://patchwork.kernel.org/project/netdevbpf/patch/20210209151936.97382-5-olteanv@gmail.com/ So, at least for now, let's leave DSA in charge of setting port flags before and after the bridge join and leave. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli --- Changes in v3: Patch is new, logically it was moved from the bridge layer to the DSA layer. net/dsa/port.c | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/net/dsa/port.c b/net/dsa/port.c index 5e079a61528e..9f65ba11ad00 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -122,6 +122,27 @@ void dsa_port_disable(struct dsa_port *dp) rtnl_unlock(); } +static void dsa_port_change_brport_flags(struct dsa_port *dp, + bool bridge_offload) +{ + unsigned long mask, flags; + int flag, err; + + mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + if (bridge_offload) + flags = mask; + else + flags = mask & ~BR_LEARNING; + + for_each_set_bit(flag, &mask, 32) { + err = dsa_port_pre_bridge_flags(dp, BIT(flag)); + if (err) + continue; + + dsa_port_bridge_flags(dp, flags & BIT(flag)); + } +} + int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) { struct dsa_notifier_bridge_info info = { @@ -132,10 +153,10 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) }; int err; - /* Set the flooding mode before joining the port in the switch */ - err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD); - if (err) - return err; + /* Notify the port driver to set its configurable flags in a way that + * matches the initial settings of a bridge port. + */ + dsa_port_change_brport_flags(dp, true); /* Here the interface is already bridged. Reflect the current * configuration so that drivers can program their chips accordingly. @@ -146,7 +167,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) /* The bridging is rolled back on error */ if (err) { - dsa_port_bridge_flags(dp, 0); + dsa_port_change_brport_flags(dp, false); dp->bridge_dev = NULL; } @@ -172,8 +193,18 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) if (err) pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); - /* Port is leaving the bridge, disable flooding */ - dsa_port_bridge_flags(dp, 0); + /* Configure the port for standalone mode (no address learning, + * flood everything). + * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events + * when the user requests it through netlink or sysfs, but not + * automatically at port join or leave, so we need to handle resetting + * the brport flags ourselves. But we even prefer it that way, because + * otherwise, some setups might never get the notification they need, + * for example, when a port leaves a LAG that offloads the bridge, + * it becomes standalone, but as far as the bridge is concerned, no + * port ever left. + */ + dsa_port_change_brport_flags(dp, false); /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, * so allow it to be in BR_STATE_FORWARDING to be kept functional From patchwork Wed Feb 10 09:14:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380747 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1818AC433DB for ; Wed, 10 Feb 2021 09:25:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AB7DB64E40 for ; Wed, 10 Feb 2021 09:25:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230390AbhBJJZK (ORCPT ); Wed, 10 Feb 2021 04:25:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59906 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229485AbhBJJVP (ORCPT ); Wed, 10 Feb 2021 04:21:15 -0500 Received: from mail-ej1-x632.google.com (mail-ej1-x632.google.com [IPv6:2a00:1450:4864:20::632]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8BD6BC061797; Wed, 10 Feb 2021 01:19:22 -0800 (PST) Received: by mail-ej1-x632.google.com with SMTP id b9so2747243ejy.12; Wed, 10 Feb 2021 01:19:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=CXjCmzUECth2G3l3PXrELz5l4B9Xa3RRpLzHdvC5vhA=; b=Fx7mvDMWNovGTLJbpSArsu/Gq+1sgkx7cs2dCn4YW0zH7Qur0Xrlh+GsvD9zh0pCuW 9nu0qZ4tmDTfaINXhqglSn5OFkDwThY5ikhrX1gpETMz8/pIWQglO4VJeH0QJbjIkGqQ gEDl74vTveTsFDOViq6F6PtN+2Qn5BemOYbevq5Fb/BF7pp9BmLq37GmuIVT+OYksg7B BSALdPTVPrxENRWR+lqZNYQsRhc/JMCaY7+7ydkW527l1LGttJR0A8ThgnSnZ2ZXwKCc otPEAvAdaPZFhnyrErLB2kC7S7eRA1406FdAUOZnYlBpqBtwInP39fk6oobRZgNyRwOG JHvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=CXjCmzUECth2G3l3PXrELz5l4B9Xa3RRpLzHdvC5vhA=; b=ogdMUWd0529DuVfhCu/z0t9Z9Ceo5eWnfw36uLirrUJUfgwoTDdOC1WpRnlt/pQCKm E4gW3NuQ6HyCWEtsQaeQ0BkzEXWNtvyyx/FSlnG3d48fBk53xCp2DV1KO7qZbHgKOZPf QXI1hVU3u4ImfSnfFVQw78J0onfYZssojWpoj1qYkwRjmhpXR1gz8qfchCuo7OdWgr3+ +VrB0cZVdP/jglU+/q29xqOWENLKuDZfRFhccT/jqLMCs0JW6ctfaSVIdgtbIbnx7L1M 2P/6HcTKdq9RRWqllq8ycTrdIynCmfiNyHyOGwo9B4g2AWrTwUjlserFZPncYAOZMIqb yqzQ== X-Gm-Message-State: AOAM532gR8pFcQTE6Jr7JGDMXIAPVXYDRMHI763/LbvDZHbKvaGeGMUt AXwrkARMaNzg64g/w355H3U= X-Google-Smtp-Source: ABdhPJxATxtELfV/YEBtt9kTgw6RwN/3CnRgGAMpEFwTJfk2sghvrJ0VoWsXzOGqII+UIH/mOYNoFw== X-Received: by 2002:a17:906:9b4f:: with SMTP id ep15mr1984202ejc.423.1612948761239; Wed, 10 Feb 2021 01:19:21 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:20 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 05/11] net: squash switchdev attributes PRE_BRIDGE_FLAGS and BRIDGE_FLAGS Date: Wed, 10 Feb 2021 11:14:39 +0200 Message-Id: <20210210091445.741269-6-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean There does not appear to be any strong reason why br_switchdev_set_port_flag issues a separate notification for checking the supported brport flags rather than just attempting to apply them and propagating the error if that fails. However, there is a reason why this switchdev API is counterproductive for a driver writer, and that is because although br_switchdev_set_port_flag gets passed a "flags" and a "mask", those are passed piecemeal to the driver, so while the PRE_BRIDGE_FLAGS listener knows what changed because it has the "mask", the BRIDGE_FLAGS listener doesn't, because it only has the final value. This means that "edge detection" needs to be done by each individual BRIDGE_FLAGS listener by XOR-ing the old and the new flags, which in turn means that copying the flags into a driver private variable is strictly necessary. This can be solved by passing the "flags" and the "mask" together into a single switchdev attribute, and it also reduces some boilerplate in the drivers that offload this. Signed-off-by: Vladimir Oltean --- Changes in v3: - Reworked mlxsw to check mask before performing any change. - Also adapted the newly introduced dsa_port_change_brport_flags to the new API. Changes in v2: - Renamed "val" to "flags". - Reworked drivers to check mask before performing any change. .../marvell/prestera/prestera_switchdev.c | 29 +++++---- .../mellanox/mlxsw/spectrum_switchdev.c | 59 +++++++++---------- drivers/net/ethernet/rocker/rocker_main.c | 24 ++------ drivers/net/ethernet/ti/cpsw_switchdev.c | 32 ++++------ drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 43 +++++++------- include/net/switchdev.h | 8 ++- net/bridge/br_switchdev.c | 15 +---- net/dsa/dsa_priv.h | 4 +- net/dsa/port.c | 43 ++++++-------- net/dsa/slave.c | 3 - 10 files changed, 109 insertions(+), 151 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index 2c1619715a4b..a797a7ff0cfe 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -581,24 +581,32 @@ int prestera_bridge_port_event(struct net_device *dev, unsigned long event, static int prestera_port_attr_br_flags_set(struct prestera_port *port, struct net_device *dev, - unsigned long flags) + struct switchdev_brport_flags flags) { struct prestera_bridge_port *br_port; int err; + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) + err = -EINVAL; + br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); if (!br_port) return 0; - err = prestera_hw_port_flood_set(port, flags & BR_FLOOD); - if (err) - return err; + if (flags.mask & BR_FLOOD) { + err = prestera_hw_port_flood_set(port, flags.val & BR_FLOOD); + if (err) + return err; + } - err = prestera_hw_port_learning_set(port, flags & BR_LEARNING); - if (err) - return err; + if (flags.mask & BR_LEARNING) { + err = prestera_hw_port_learning_set(port, + flags.val & BR_LEARNING); + if (err) + return err; + } - memcpy(&br_port->flags, &flags, sizeof(flags)); + memcpy(&br_port->flags, &flags.val, sizeof(flags.val)); return 0; } @@ -706,11 +714,6 @@ static int prestera_port_obj_attr_set(struct net_device *dev, err = prestera_port_attr_stp_state_set(port, attr->orig_dev, attr->u.stp_state); break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - if (attr->u.brport_flags & - ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) - err = -EINVAL; - break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = prestera_port_attr_br_flags_set(port, attr->orig_dev, attr->u.brport_flags); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 18e4f1cd5587..7af79e3a3c7a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -653,51 +653,52 @@ mlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, return err; } -static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port - *mlxsw_sp_port, - unsigned long brport_flags) -{ - if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) - return -EINVAL; - - return 0; -} - static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, struct net_device *orig_dev, - unsigned long brport_flags) + struct switchdev_brport_flags flags) { struct mlxsw_sp_bridge_port *bridge_port; int err; + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) + return -EINVAL; + bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, orig_dev); if (!bridge_port) return 0; - err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, - MLXSW_SP_FLOOD_TYPE_UC, - brport_flags & BR_FLOOD); - if (err) - return err; + if (flags.mask & BR_FLOOD) { + err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, + bridge_port, + MLXSW_SP_FLOOD_TYPE_UC, + flags.val & BR_FLOOD); + if (err) + return err; + } - err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, bridge_port, - brport_flags & BR_LEARNING); - if (err) - return err; + if (flags.mask & BR_LEARNING) { + err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, + bridge_port, + flags.val & BR_LEARNING); + if (err) + return err; + } if (bridge_port->bridge_device->multicast_enabled) goto out; - err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, - MLXSW_SP_FLOOD_TYPE_MC, - brport_flags & - BR_MCAST_FLOOD); - if (err) - return err; + if (flags.mask & BR_MCAST_FLOOD) { + err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, + bridge_port, + MLXSW_SP_FLOOD_TYPE_MC, + flags.val & BR_MCAST_FLOOD); + if (err) + return err; + } out: - memcpy(&bridge_port->flags, &brport_flags, sizeof(brport_flags)); + memcpy(&bridge_port->flags, &flags.val, sizeof(flags.val)); return 0; } @@ -899,10 +900,6 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, attr->orig_dev, attr->u.stp_state); break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, - attr->u.brport_flags); - break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, attr->orig_dev, diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 740a715c49c6..898abf3d14d0 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1575,8 +1575,8 @@ rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * } static int -rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags) +rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, + struct switchdev_brport_flags flags) { struct rocker_world_ops *wops = rocker_port->rocker->wops; unsigned long brport_flags_s; @@ -1590,22 +1590,10 @@ rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port, if (err) return err; - if (brport_flags & ~brport_flags_s) + if (flags.mask & ~brport_flags_s) return -EINVAL; - return 0; -} - -static int -rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_attr_bridge_flags_set) - return -EOPNOTSUPP; - - return wops->port_attr_bridge_flags_set(rocker_port, brport_flags); + return wops->port_attr_bridge_flags_set(rocker_port, flags.val); } static int @@ -2056,10 +2044,6 @@ static int rocker_port_attr_set(struct net_device *dev, err = rocker_world_port_attr_stp_state_set(rocker_port, attr->u.stp_state); break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port, - attr->u.brport_flags); - break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = rocker_world_port_attr_bridge_flags_set(rocker_port, attr->u.brport_flags); diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c index 13524cbaa8b6..5d8ec34f82ad 100644 --- a/drivers/net/ethernet/ti/cpsw_switchdev.c +++ b/drivers/net/ethernet/ti/cpsw_switchdev.c @@ -57,27 +57,25 @@ static int cpsw_port_stp_state_set(struct cpsw_priv *priv, u8 state) static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv, struct net_device *orig_dev, - unsigned long brport_flags) + struct switchdev_brport_flags flags) { struct cpsw_common *cpsw = priv->cpsw; - bool unreg_mcast_add = false; - if (brport_flags & BR_MCAST_FLOOD) - unreg_mcast_add = true; - dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n", - unreg_mcast_add, priv->emac_port); + if (flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD)) + return -EINVAL; - cpsw_ale_set_unreg_mcast(cpsw->ale, BIT(priv->emac_port), - unreg_mcast_add); + if (flags.mask & BR_MCAST_FLOOD) { + bool unreg_mcast_add = false; - return 0; -} + if (flags.val & BR_MCAST_FLOOD) + unreg_mcast_add = true; -static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, - unsigned long flags) -{ - if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD)) - return -EINVAL; + dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n", + unreg_mcast_add, priv->emac_port); + + cpsw_ale_set_unreg_mcast(cpsw->ale, BIT(priv->emac_port), + unreg_mcast_add); + } return 0; } @@ -92,10 +90,6 @@ static int cpsw_port_attr_set(struct net_device *ndev, dev_dbg(priv->dev, "attr: id %u port: %u\n", attr->id, priv->emac_port); switch (attr->id) { - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - ret = cpsw_port_attr_br_flags_pre_set(ndev, - attr->u.brport_flags); - break; case SWITCHDEV_ATTR_ID_PORT_STP_STATE: ret = cpsw_port_stp_state_set(priv, attr->u.stp_state); dev_dbg(priv->dev, "stp state: %u\n", attr->u.stp_state); diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c index ca3d07fe7f58..f675a2ba4dce 100644 --- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c @@ -908,31 +908,32 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev, return dpaa2_switch_port_set_stp_state(port_priv, state); } -static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev, - unsigned long flags) -{ - if (flags & ~(BR_LEARNING | BR_FLOOD)) - return -EINVAL; - - return 0; -} - -static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev, - unsigned long flags) +static int +dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev, + struct switchdev_brport_flags flags) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err = 0; - /* Learning is enabled per switch */ - err = dpaa2_switch_set_learning(port_priv->ethsw_data, - !!(flags & BR_LEARNING)); - if (err) - goto exit; + if (flags.mask & ~(BR_LEARNING | BR_FLOOD)) + return -EINVAL; + + if (flags.mask & BR_LEARNING) { + /* Learning is enabled per switch */ + err = dpaa2_switch_set_learning(port_priv->ethsw_data, + !!(flags.val & BR_LEARNING)); + if (err) + return err; + } - err = dpaa2_switch_port_set_flood(port_priv, !!(flags & BR_FLOOD)); + if (flags.mask & BR_FLOOD) { + err = dpaa2_switch_port_set_flood(port_priv, + !!(flags.val & BR_FLOOD)); + if (err) + return err; + } -exit: - return err; + return 0; } static int dpaa2_switch_port_attr_set(struct net_device *netdev, @@ -945,10 +946,6 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev, err = dpaa2_switch_port_attr_stp_state_set(netdev, attr->u.stp_state); break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - err = dpaa2_switch_port_attr_br_flags_pre_set(netdev, - attr->u.brport_flags); - break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = dpaa2_switch_port_attr_br_flags_set(netdev, attr->u.brport_flags); diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 84c765312001..aa9cad9bad7d 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -20,7 +20,6 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_UNDEFINED, SWITCHDEV_ATTR_ID_PORT_STP_STATE, SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, - SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS, SWITCHDEV_ATTR_ID_PORT_MROUTER, SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, @@ -33,6 +32,11 @@ enum switchdev_attr_id { #endif }; +struct switchdev_brport_flags { + unsigned long val; + unsigned long mask; +}; + struct switchdev_attr { struct net_device *orig_dev; enum switchdev_attr_id id; @@ -41,7 +45,7 @@ struct switchdev_attr { void (*complete)(struct net_device *dev, int err, void *priv); union { u8 stp_state; /* PORT_STP_STATE */ - unsigned long brport_flags; /* PORT_{PRE}_BRIDGE_FLAGS */ + struct switchdev_brport_flags brport_flags; /* PORT_BRIDGE_FLAGS */ bool mrouter; /* PORT_MROUTER */ clock_t ageing_time; /* BRIDGE_AGEING_TIME */ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */ diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index ac8dead86bf2..92d207c8a30e 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -65,7 +65,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, { struct switchdev_attr attr = { .orig_dev = p->dev, - .id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS, + .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, }; struct switchdev_notifier_port_attr_info info = { .attr = &attr, @@ -77,7 +77,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, if (!mask) return 0; - attr.u.brport_flags = mask; + attr.u.brport_flags.val = flags; + attr.u.brport_flags.mask = mask; /* We run from atomic context here */ err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, @@ -93,16 +94,6 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, return -EOPNOTSUPP; } - attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; - attr.flags = SWITCHDEV_F_DEFER; - attr.u.brport_flags = flags; - - err = switchdev_port_attr_set(p->dev, &attr); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "error setting offload flag on port"); - return err; - } - return 0; } diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 8a1bcb2b4208..63770e421e4d 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -174,8 +174,8 @@ int dsa_port_mdb_add(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb); int dsa_port_mdb_del(const struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb); -int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags); -int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags); +int dsa_port_bridge_flags(const struct dsa_port *dp, + struct switchdev_brport_flags flags); int dsa_port_mrouter(struct dsa_port *dp, bool mrouter); int dsa_port_vlan_add(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan); diff --git a/net/dsa/port.c b/net/dsa/port.c index 9f65ba11ad00..736c5debcb96 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -125,21 +125,22 @@ void dsa_port_disable(struct dsa_port *dp) static void dsa_port_change_brport_flags(struct dsa_port *dp, bool bridge_offload) { - unsigned long mask, flags; - int flag, err; + struct switchdev_brport_flags flags; + int flag; - mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; if (bridge_offload) - flags = mask; + flags.val = flags.mask; else - flags = mask & ~BR_LEARNING; + flags.val = flags.mask & ~BR_LEARNING; - for_each_set_bit(flag, &mask, 32) { - err = dsa_port_pre_bridge_flags(dp, BIT(flag)); - if (err) - continue; + for_each_set_bit(flag, &flags.mask, 32) { + struct switchdev_brport_flags tmp; - dsa_port_bridge_flags(dp, flags & BIT(flag)); + tmp.val = flags.val & BIT(flag); + tmp.mask = BIT(flag); + + dsa_port_bridge_flags(dp, tmp); } } @@ -423,28 +424,18 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock) return 0; } -int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags) +int dsa_port_bridge_flags(const struct dsa_port *dp, + struct switchdev_brport_flags flags) { struct dsa_switch *ds = dp->ds; + int port = dp->index; if (!ds->ops->port_egress_floods || - (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) + (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))) return -EINVAL; - return 0; -} - -int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags) -{ - struct dsa_switch *ds = dp->ds; - int port = dp->index; - int err = 0; - - if (ds->ops->port_egress_floods) - err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, - flags & BR_MCAST_FLOOD); - - return err; + return ds->ops->port_egress_floods(ds, port, flags.val & BR_FLOOD, + flags.val & BR_MCAST_FLOOD); } int dsa_port_mrouter(struct dsa_port *dp, bool mrouter) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 8f4c7c232e2c..0e1f8f1d4e2c 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -290,9 +290,6 @@ static int dsa_slave_port_attr_set(struct net_device *dev, case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: ret = dsa_port_ageing_time(dp, attr->u.ageing_time); break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags); - break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: ret = dsa_port_bridge_flags(dp, attr->u.brport_flags); break; From patchwork Wed Feb 10 09:14:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380745 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D623DC433DB for ; Wed, 10 Feb 2021 09:27:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 739D464DB1 for ; Wed, 10 Feb 2021 09:27:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229485AbhBJJ0g (ORCPT ); Wed, 10 Feb 2021 04:26:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59924 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230014AbhBJJVS (ORCPT ); Wed, 10 Feb 2021 04:21:18 -0500 Received: from mail-ej1-x62e.google.com (mail-ej1-x62e.google.com [IPv6:2a00:1450:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D38FAC0617A9; Wed, 10 Feb 2021 01:19:25 -0800 (PST) Received: by mail-ej1-x62e.google.com with SMTP id jj19so2828839ejc.4; Wed, 10 Feb 2021 01:19:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=nqM+4Yr8F4+GxUXddkhAIS+khdj8bCCJ5YLHti1P+HU=; b=t1iDYvmnaAH3vkKod3rlfHYrB22+6raztYFdCkY0RnPZc1dOHA/yevv2oPYrc+DWvI VjU8cpj9gN7VU7tteqZdiv3BioQVyL9e6PTS51cXYhQfoCJFcKV85quRsaZgVCVzuYhK dXCenpS7r7AIPWdtzVKmHr1ktYGQU4nlkSC/9twTVc0SFTAmqZ/jGyPr3elV+G1FMR0L sqMI/EzunQM3dygBPtSXHf+pRfL96XzcKJ9sLeKLt4op3PwQm7aBV2rZCIhHKUEf3Nud LDXwOv0KktTgIv23+rDowtMNZ2t3zsxyCLvJ/3Z0Y1ANpexOuulTW9IvS0E1x/jRZ/6X EVeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=nqM+4Yr8F4+GxUXddkhAIS+khdj8bCCJ5YLHti1P+HU=; b=cJpD1Hcjg3fNjxYdtff5iMSR5HRGpWr1cdk/AjqaB5GW+VD7X6QhdbimWwBM3XhqA9 lTmyiKLzrsM6GLEvFUU3OIFD8xQKS7rtOU5wjngjan0DitJ2vpxvEsiE3MQrMUqc+v6w w+7EG0hIr43+YWQ3Ms+A+y0TpWhtDL/ZZqayGNGGRrr5rbj3ldtKqMzkuhYrulJhVWtB AYloHjoBCYLBcJmu1sH+GFC5YKh9viSlqgivcxwDbjwImsnFXWAca87ohuA0kjV+hAJl ozar33ZQstL3RVLf2jvcds8/czrjn9MYQiTRyijmARi8uKjwXMFwjXs1z8h+xYqx06ek oyBQ== X-Gm-Message-State: AOAM530TkMU6efSgtE1jVxVtYIQbLt8oQIDrYUxKexHDItwFnJn2HllN MhKtE4YRJ15oKWiJ4hhf08U= X-Google-Smtp-Source: ABdhPJyB8ABrW/EpJlY1kB//5lvfpH+84Uu0gquf8YModSDLQh0167tcKrRSdygSPMrW+wHfKIRtJg== X-Received: by 2002:a17:906:259a:: with SMTP id m26mr2042269ejb.399.1612948764529; Wed, 10 Feb 2021 01:19:24 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:24 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 07/11] net: prep switchdev drivers for concurrent SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS Date: Wed, 10 Feb 2021 11:14:41 +0200 Message-Id: <20210210091445.741269-8-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean Because the bridge will start offloading SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS while not serialized by any lock such as the br->lock spinlock, existing drivers that treat that attribute and cache the brport flags might no longer work correctly. The issue is that the brport flags are a single unsigned long bitmask, and the bridge only guarantees the validity of the changed bits, not the full state. So when offloading two concurrent switchdev attributes, such as one for BR_LEARNING and another for BR_FLOOD, it might happen that the flags having BR_FLOOD are written into the cached value, and this in turn disables the BR_LEARNING bit which was set previously. We can fix this across the board by keeping individual boolean variables for each brport flag. Note that mlxsw and prestera were setting the BR_LEARNING_SYNC flag too, but that appears to be just dead code, so I removed it. Signed-off-by: Vladimir Oltean --- Changes in v3: Patch is new. .../marvell/prestera/prestera_switchdev.c | 32 ++++++++++------ .../mellanox/mlxsw/spectrum_switchdev.c | 38 ++++++++++++------- drivers/net/ethernet/rocker/rocker.h | 2 +- drivers/net/ethernet/rocker/rocker_main.c | 2 +- drivers/net/ethernet/rocker/rocker_ofdpa.c | 26 +++++++------ net/bridge/br_switchdev.c | 3 +- 6 files changed, 62 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index a797a7ff0cfe..76030c4c1546 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -49,7 +49,9 @@ struct prestera_bridge_port { struct prestera_bridge *bridge; struct list_head vlan_list; refcount_t ref_count; - unsigned long flags; + bool learning; + bool ucast_flood; + bool mcast_flood; u8 stp_state; }; @@ -339,8 +341,9 @@ prestera_bridge_port_create(struct prestera_bridge *bridge, if (!br_port) return NULL; - br_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | - BR_MCAST_FLOOD; + br_port->learning = true; + br_port->ucast_flood = true; + br_port->mcast_flood = true; br_port->stp_state = BR_STATE_DISABLED; refcount_set(&br_port->ref_count, 1); br_port->bridge = bridge; @@ -404,11 +407,11 @@ prestera_bridge_1d_port_join(struct prestera_bridge_port *br_port) if (err) return err; - err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD); + err = prestera_hw_port_flood_set(port, br_port->ucast_flood); if (err) goto err_port_flood_set; - err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); + err = prestera_hw_port_learning_set(port, br_port->learning); if (err) goto err_port_learning_set; @@ -594,19 +597,24 @@ static int prestera_port_attr_br_flags_set(struct prestera_port *port, return 0; if (flags.mask & BR_FLOOD) { - err = prestera_hw_port_flood_set(port, flags.val & BR_FLOOD); + bool ucast_flood = !!(flags.val & BR_FLOOD); + + err = prestera_hw_port_flood_set(port, ucast_flood); if (err) return err; + + br_port->ucast_flood = ucast_flood; } if (flags.mask & BR_LEARNING) { - err = prestera_hw_port_learning_set(port, - flags.val & BR_LEARNING); + bool learning = !!(flags.val & BR_LEARNING); + + err = prestera_hw_port_learning_set(port, learning); if (err) return err; - } - memcpy(&br_port->flags, &flags.val, sizeof(flags.val)); + br_port->learning = learning; + } return 0; } @@ -899,11 +907,11 @@ prestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, if (port_vlan->br_port) return 0; - err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD); + err = prestera_hw_port_flood_set(port, br_port->ucast_flood); if (err) return err; - err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); + err = prestera_hw_port_learning_set(port, br_port->learning); if (err) goto err_port_learning_set; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 7af79e3a3c7a..0c00754e0cb5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -62,9 +62,11 @@ struct mlxsw_sp_bridge_port { struct list_head vlans_list; unsigned int ref_count; u8 stp_state; - unsigned long flags; bool mrouter; bool lagged; + bool ucast_flood; + bool mcast_flood; + bool learning; union { u16 lag_id; u16 system_port; @@ -349,8 +351,9 @@ mlxsw_sp_bridge_port_create(struct mlxsw_sp_bridge_device *bridge_device, bridge_port->dev = brport_dev; bridge_port->bridge_device = bridge_device; bridge_port->stp_state = BR_STATE_DISABLED; - bridge_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | - BR_MCAST_FLOOD; + bridge_port->learning = true; + bridge_port->ucast_flood = true; + bridge_port->mcast_flood = true; INIT_LIST_HEAD(&bridge_port->vlans_list); list_add(&bridge_port->list, &bridge_device->ports_list); bridge_port->ref_count = 1; @@ -669,36 +672,45 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, return 0; if (flags.mask & BR_FLOOD) { + bool ucast_flood = !!(flags.val & BR_FLOOD); + err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, MLXSW_SP_FLOOD_TYPE_UC, - flags.val & BR_FLOOD); + ucast_flood); if (err) return err; + + bridge_port->ucast_flood = ucast_flood; } if (flags.mask & BR_LEARNING) { + bool learning = !!(flags.val & BR_LEARNING); + err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, - bridge_port, - flags.val & BR_LEARNING); + bridge_port, learning); if (err) return err; + + bridge_port->learning = learning; } if (bridge_port->bridge_device->multicast_enabled) - goto out; + return 0; if (flags.mask & BR_MCAST_FLOOD) { + bool mcast_flood = !!(flags.val & BR_MCAST_FLOOD); + err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, MLXSW_SP_FLOOD_TYPE_MC, - flags.val & BR_MCAST_FLOOD); + mcast_flood); if (err) return err; + + bridge_port->mcast_flood = mcast_flood; } -out: - memcpy(&bridge_port->flags, &flags.val, sizeof(flags.val)); return 0; } @@ -796,7 +808,7 @@ static bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port) bridge_device = bridge_port->bridge_device; return bridge_device->multicast_enabled ? bridge_port->mrouter : - bridge_port->flags & BR_MCAST_FLOOD; + bridge_port->mcast_flood; } static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, @@ -962,7 +974,7 @@ mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, return PTR_ERR(fid); err = mlxsw_sp_fid_flood_set(fid, MLXSW_SP_FLOOD_TYPE_UC, local_port, - bridge_port->flags & BR_FLOOD); + bridge_port->ucast_flood); if (err) goto err_fid_uc_flood_set; @@ -1043,7 +1055,7 @@ mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, return err; err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, - bridge_port->flags & BR_LEARNING); + bridge_port->learning); if (err) goto err_port_vid_learning_set; diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index 315a6e5c0f59..2f533c44fe88 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -103,7 +103,7 @@ struct rocker_world_ops { int (*port_attr_stp_state_set)(struct rocker_port *rocker_port, u8 state); int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port, - unsigned long brport_flags); + struct switchdev_brport_flags flags); int (*port_attr_bridge_flags_support_get)(const struct rocker_port * rocker_port, unsigned long * diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 898abf3d14d0..b1a67c6423a8 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1593,7 +1593,7 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, if (flags.mask & ~brport_flags_s) return -EINVAL; - return wops->port_attr_bridge_flags_set(rocker_port, flags.val); + return wops->port_attr_bridge_flags_set(rocker_port, flags); } static int diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 967a634ee9ac..eb36c2a2da7b 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -198,7 +198,7 @@ struct ofdpa_port { struct net_device *bridge_dev; __be16 internal_vlan_id; int stp_state; - u32 brport_flags; + bool learning; unsigned long ageing_time; bool ctrls[OFDPA_CTRL_MAX]; unsigned long vlan_bitmap[OFDPA_VLAN_BITMAP_LEN]; @@ -2423,7 +2423,7 @@ static int ofdpa_port_pre_init(struct rocker_port *rocker_port) ofdpa_port->rocker_port = rocker_port; ofdpa_port->dev = rocker_port->dev; ofdpa_port->pport = rocker_port->pport; - ofdpa_port->brport_flags = BR_LEARNING; + ofdpa_port->learning = true; ofdpa_port->ageing_time = BR_DEFAULT_AGEING_TIME; return 0; } @@ -2433,8 +2433,7 @@ static int ofdpa_port_init(struct rocker_port *rocker_port) struct ofdpa_port *ofdpa_port = rocker_port->wpriv; int err; - rocker_port_set_learning(rocker_port, - !!(ofdpa_port->brport_flags & BR_LEARNING)); + rocker_port_set_learning(rocker_port, ofdpa_port->learning); err = ofdpa_port_ig_tbl(ofdpa_port, 0); if (err) { @@ -2488,20 +2487,23 @@ static int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port, } static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port, - unsigned long brport_flags) + struct switchdev_brport_flags flags) { struct ofdpa_port *ofdpa_port = rocker_port->wpriv; - unsigned long orig_flags; - int err = 0; + int err; - orig_flags = ofdpa_port->brport_flags; - ofdpa_port->brport_flags = brport_flags; + if (flags.mask & BR_LEARNING) { + bool learning = !!(flags.val & BR_LEARNING); - if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING) err = rocker_port_set_learning(ofdpa_port->rocker_port, - !!(ofdpa_port->brport_flags & BR_LEARNING)); + learning); + if (err) + return err; - return err; + ofdpa_port->learning = learning; + } + + return 0; } static int diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index 92d207c8a30e..dbd94156960f 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -72,12 +72,11 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, }; int err; - flags &= BR_PORT_FLAGS_HW_OFFLOAD; mask &= BR_PORT_FLAGS_HW_OFFLOAD; if (!mask) return 0; - attr.u.brport_flags.val = flags; + attr.u.brport_flags.val = flags & mask; attr.u.brport_flags.mask = mask; /* We run from atomic context here */ From patchwork Wed Feb 10 09:14:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380746 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0A290C433E0 for ; Wed, 10 Feb 2021 09:26:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B162464E3B for ; Wed, 10 Feb 2021 09:26:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230420AbhBJJ0R (ORCPT ); Wed, 10 Feb 2021 04:26:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59926 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230023AbhBJJVS (ORCPT ); Wed, 10 Feb 2021 04:21:18 -0500 Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7749FC0617AA; Wed, 10 Feb 2021 01:19:27 -0800 (PST) Received: by mail-ej1-x62b.google.com with SMTP id bl23so2828119ejb.5; Wed, 10 Feb 2021 01:19:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aF26U34aBL8K6bVtwaJCBU2DkCwMTfcd91sYoywprTY=; b=WCKQ+LtKE/UOlQrWFxrHn28+qeGzJlG6joOQzDOEXxMuc52/bvbwwIWCJfpSdEnemZ i9+ZCibdt0/ARIwOmkJfNQQx3wjQfdNwdri68i5YfKJC57taNHzwEPJoLse0GkTvHUBu d3oCV0gH5620B/1/yyQ4yYuEUQNqbhWqbJClWRwEJJRbT06RhSnIMfyV06exk1J9ZNrb Q64okac0fkO1ek5M9R7yG/XL04bchQfjJxOIQzLwwxD3ZPcwLvU0H4pEWMwA6HAWRpT2 yRVark7GGOD3fhXGEPbP7xu6nHE/k6LJijyA0s/cg4QhLuCl+evXa7iYZk4tLFOupnen 81KQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aF26U34aBL8K6bVtwaJCBU2DkCwMTfcd91sYoywprTY=; b=NuL8WBwMWTr0nZUVmTni0gGobHiChQtd4WZx+/qI3prY1HqfgD2fxfpRzGWXY1t3fy gbAl9uEtCnN8qmBo9gdUtchrSgY7foXnVFLUjK/jH6Zp36hc8cpuE0EnjfPBTj5vDpn+ 7JOGvHPA+Zw8tZxwn1qqklnu7D15yHpGhh2++GsQp1VcVfzcYCbUi5uCHU2FE6mYuvG4 tFbGScLHzSP/rq7sfjdQP8OyTQNyLZgwq202CAau5+LGFNfIq9UyiFHFFkU5qg6ADblf 0v25fmjABWyy0p3TIEqhwrtJBM27fhFlXqVadG+E+N6v/QzlJiu0KchKj75NN73A7Y9n FPTw== X-Gm-Message-State: AOAM532svIlG6suCMdYA+CoNwB+WMl20fYpwU1zSrQPiJijH1fXKKtZA G8XOqEF99xl5kg8BHa14KvZI03MeV0w= X-Google-Smtp-Source: ABdhPJyOFyh2p51yy4aH1LdzJT0MofdRrOkRGV9M/hCkCO5WkB20mLsXy8PAiOIM/CjJqHRPKNce/w== X-Received: by 2002:a17:906:4c85:: with SMTP id q5mr1987915eju.375.1612948766169; Wed, 10 Feb 2021 01:19:26 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:25 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 08/11] net: bridge: put SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS on the blocking call chain Date: Wed, 10 Feb 2021 11:14:42 +0200 Message-Id: <20210210091445.741269-9-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean Since we would like br_switchdev_set_port_flag to not use an atomic notifier, it should be called from outside spinlock context. We can temporarily drop br->lock, but that creates some concurrency complications (example below is given for sysfs): - There might be an "echo 1 > multicast_flood" simultaneous with an "echo 0 > multicast_flood". The result of this is nondeterministic either way, so I'm not too concerned as long as the result is consistent (no other flags have changed). - There might be an "echo 1 > multicast_flood" simultaneous with an "echo 0 > learning". My expectation is that none of the two writes are "eaten", and the final flags contain BR_MCAST_FLOOD=1 and BR_LEARNING=0 regardless of the order of execution. That is actually possible if, on the commit path, we don't do a trivial "p->flags = flags" which might overwrite bits outside of our mask, but instead we just change the flags corresponding to our mask. Now that br_switchdev_set_port_flag is never called from under br->lock, it runs in sleepable context. All switchdev drivers handle SWITCHDEV_PORT_ATTR_SET as both blocking and atomic, so no changes are needed on that front. Signed-off-by: Vladimir Oltean --- Changes in v3: - Drop the br->lock around br_switchdev_set_port_flag in this patch, for both sysfs and netlink. - Only set/restore the masked bits in p->flags to avoid concurrency issues. Changes in v2: Patch is new. net/bridge/br_netlink.c | 10 +++++++--- net/bridge/br_switchdev.c | 5 ++--- net/bridge/br_sysfs_if.c | 22 ++++++++++++++-------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index b7731614c036..8f09106966c4 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -869,7 +869,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], struct netlink_ext_ack *extack) { - unsigned long old_flags, changed_mask; + unsigned long flags, old_flags, changed_mask; bool br_vlan_tunnel_old; int err; @@ -896,10 +896,14 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); changed_mask = old_flags ^ p->flags; + flags = p->flags; - err = br_switchdev_set_port_flag(p, p->flags, changed_mask, extack); + spin_unlock_bh(&p->br->lock); + err = br_switchdev_set_port_flag(p, flags, changed_mask, extack); + spin_lock_bh(&p->br->lock); if (err) { - p->flags = old_flags; + p->flags &= ~changed_mask; + p->flags |= (old_flags & changed_mask); goto out; } diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index dbd94156960f..a79164ee65b9 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -79,9 +79,8 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, attr.u.brport_flags.val = flags & mask; attr.u.brport_flags.mask = mask; - /* We run from atomic context here */ - err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, - &info.info, extack); + err = call_switchdev_blocking_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, + &info.info, extack); err = notifier_to_errno(err); if (err == -EOPNOTSUPP) return 0; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 72e92376eef1..3f21fdd1cdaa 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -68,16 +68,22 @@ static int store_flag(struct net_bridge_port *p, unsigned long v, else flags &= ~mask; - if (flags != p->flags) { - err = br_switchdev_set_port_flag(p, flags, mask, &extack); - if (err) { - netdev_err(p->dev, "%s\n", extack._msg); - return err; - } + if (flags == p->flags) + return 0; - p->flags = flags; - br_port_flags_change(p, mask); + spin_unlock_bh(&p->br->lock); + err = br_switchdev_set_port_flag(p, flags, mask, &extack); + spin_lock_bh(&p->br->lock); + if (err) { + netdev_err(p->dev, "%s\n", extack._msg); + return err; } + + p->flags &= ~mask; + p->flags |= (flags & mask); + + br_port_flags_change(p, mask); + return 0; } From patchwork Wed Feb 10 09:14:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 380744 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B307BC433DB for ; Wed, 10 Feb 2021 09:27:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6719364E3B for ; Wed, 10 Feb 2021 09:27:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230197AbhBJJ1g (ORCPT ); Wed, 10 Feb 2021 04:27:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59608 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229898AbhBJJVx (ORCPT ); Wed, 10 Feb 2021 04:21:53 -0500 Received: from mail-ej1-x62f.google.com (mail-ej1-x62f.google.com [IPv6:2a00:1450:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91A35C06121C; Wed, 10 Feb 2021 01:19:32 -0800 (PST) Received: by mail-ej1-x62f.google.com with SMTP id b9so2748303ejy.12; Wed, 10 Feb 2021 01:19:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mfnGIdcutyBgokUsopUvatj+owccT3VEUvgckyZH2YY=; b=BOKrfu70IlQUbl5wtpLED4Na7q6Y0w393yJawpjl+y5oB4vIMSCk393Mvf1Y0DTo/5 6bT9bBcwz/ddV4wua5KbxRHRNgPEKtLFpBPIPXzj7m08GjM1iowgDaUxbfrF2xgg3SeI xmPrsdRMR0mOEH5Tuo2THdVS2W5RKOaCNKp5wCpbQYtcgVaedulRpfUcw4ShBV+9FLYS DKFEvpkZciDlay/bRIjHPjPIrXItH6srg8cdriUDB7F/M9NskEVqKP7pjFMRGGNSA3vq T7cVNl3ILfNlEiF/o7s5sMqMj+sEdX+o7oBgyVQ6XRLTXlVyFGsUj71IDjq0wIb7RX1+ Vd/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mfnGIdcutyBgokUsopUvatj+owccT3VEUvgckyZH2YY=; b=rvOD20pA2kuqr9QxXW6Et74xr8Cwegk3E0jncNbFJCwlU3hYMHJYqsUDUKbsprN9nc YnYkNxG5sbmqIFjPsfnmK4hbcQVu6hBfELSLbHzA9EXA0tmr2tkTi0SuK7ZKU3ZpYuVR jEs6ZQcYpgQv+1wvr8eb1x2OM96W/IlhIHeJAls0IlqP3xnJ0bvjMZRlbzUUZ1Y3vpnQ ArxF84I+S7QotL+REoy4eBoskTQLie1g5ErbLJ8VZyl6Xhe35a60UGy9MV6p06Y+RKQQ ZnPl95WjHCHpp4RChXox9hbSRYMBoC4rAXE/Oi3ic+5yrCG6pwbFHItcL3yyzT5rtw6Y fuvw== X-Gm-Message-State: AOAM530+nlO6Bf9RU6FVnyTACnbIMNH7nI4DLBCUOIrPETDDe1SgNJYb 4DM2D4XOV83CjfJzvX6BpHs= X-Google-Smtp-Source: ABdhPJzoakxM8LFOOP+wRfVTfOipn/abz3UzzgAhMqmI1EXHWQnOkxIzMmeC7l0+OtgXigSvBuqEBg== X-Received: by 2002:a17:906:ce24:: with SMTP id sd4mr1981043ejb.21.1612948771210; Wed, 10 Feb 2021 01:19:31 -0800 (PST) Received: from localhost.localdomain (5-12-227-87.residential.rdsnet.ro. [5.12.227.87]) by smtp.gmail.com with ESMTPSA id u2sm701801ejb.65.2021.02.10.01.19.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Feb 2021 01:19:30 -0800 (PST) From: Vladimir Oltean To: Jakub Kicinski , "David S. Miller" Cc: Andrew Lunn , Vivien Didelot , Florian Fainelli , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, bridge@lists.linux-foundation.org, Roopa Prabhu , Nikolay Aleksandrov , Jiri Pirko , Ido Schimmel , Claudiu Manoil , Alexandre Belloni , UNGLinuxDriver@microchip.com, Vadym Kochan , Taras Chornyi , Grygorii Strashko , Ioana Ciornei , Ivan Vecera , linux-omap@vger.kernel.org Subject: [PATCH v3 net-next 11/11] net: dsa: sja1105: offload bridge port flags to device Date: Wed, 10 Feb 2021 11:14:45 +0200 Message-Id: <20210210091445.741269-12-olteanv@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210210091445.741269-1-olteanv@gmail.com> References: <20210210091445.741269-1-olteanv@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Vladimir Oltean The chip can configure unicast flooding, broadcast flooding and learning. Learning is per port, while flooding is per {ingress, egress} port pair and we need to configure the same value for all possible ingress ports towards the requested one. While multicast flooding is not officially supported, we can hack it by using a feature of the second generation (P/Q/R/S) devices, which is that FDB entries are maskable, and multicast addresses always have an odd first octet. So by putting a match-all for 00:01:00:00:00:00 addr and 00:01:00:00:00:00 mask at the end of the FDB, we make sure that it is always checked last, and does not take precedence in front of any other MDB. So it behaves effectively as an unknown multicast entry. For the first generation switches, this feature is not available, so unknown multicast will always be treated the same as unknown unicast. So the only thing we can do is request the user to offload the settings for these 2 flags in tandem, i.e. ip link set swp2 type bridge_slave flood off Error: sja1105: This chip cannot configure multicast flooding independently of unicast. ip link set swp2 type bridge_slave flood off mcast_flood off ip link set swp2 type bridge_slave mcast_flood on Error: sja1105: This chip cannot configure multicast flooding independently of unicast. Signed-off-by: Vladimir Oltean --- Changes in v3: None. Changes in v2: Patch is new. drivers/net/dsa/sja1105/sja1105.h | 2 + drivers/net/dsa/sja1105/sja1105_main.c | 212 +++++++++++++++++++++++-- drivers/net/dsa/sja1105/sja1105_spi.c | 6 + 3 files changed, 209 insertions(+), 11 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index d582308c2401..15a0893d0ff1 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -94,6 +94,7 @@ struct sja1105_info { * pop it when it's equal to TPID2. */ u16 qinq_tpid; + bool can_limit_mcast_flood; int (*reset_cmd)(struct dsa_switch *ds); int (*setup_rgmii_delay)(const void *ctx, int port); /* Prototypes from include/net/dsa.h */ @@ -204,6 +205,7 @@ struct sja1105_private { bool rgmii_rx_delay[SJA1105_NUM_PORTS]; bool rgmii_tx_delay[SJA1105_NUM_PORTS]; bool best_effort_vlan_filtering; + unsigned long learn_ena; const struct sja1105_info *info; struct gpio_desc *reset_gpio; struct spi_device *spidev; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 282253543f3b..8373cc1f5df1 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -25,6 +25,8 @@ #include "sja1105_sgmii.h" #include "sja1105_tas.h" +#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull + static const struct dsa_switch_ops sja1105_switch_ops; static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, @@ -42,15 +44,10 @@ static void sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, int from, int to, bool allow) { - if (allow) { - l2_fwd[from].bc_domain |= BIT(to); + if (allow) l2_fwd[from].reach_port |= BIT(to); - l2_fwd[from].fl_domain |= BIT(to); - } else { - l2_fwd[from].bc_domain &= ~BIT(to); + else l2_fwd[from].reach_port &= ~BIT(to); - l2_fwd[from].fl_domain &= ~BIT(to); - } } /* Structure used to temporarily transport device tree @@ -220,17 +217,43 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv, static int sja1105_init_static_fdb(struct sja1105_private *priv) { + struct sja1105_l2_lookup_entry *l2_lookup; struct sja1105_table *table; + int port; table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; - /* We only populate the FDB table through dynamic - * L2 Address Lookup entries + /* We only populate the FDB table through dynamic L2 Address Lookup + * entries, except for a special entry at the end which is a catch-all + * for unknown multicast and will be used to control flooding domain. */ if (table->entry_count) { kfree(table->entries); table->entry_count = 0; } + + if (!priv->info->can_limit_mcast_flood) + return 0; + + table->entries = kcalloc(1, table->ops->unpacked_entry_size, + GFP_KERNEL); + if (!table->entries) + return -ENOMEM; + + table->entry_count = 1; + l2_lookup = table->entries; + + /* All L2 multicast addresses have an odd first octet */ + l2_lookup[0].macaddr = SJA1105_UNKNOWN_MULTICAST; + l2_lookup[0].mask_macaddr = SJA1105_UNKNOWN_MULTICAST; + l2_lookup[0].lockeds = true; + l2_lookup[0].index = SJA1105_MAX_L2_LOOKUP_COUNT - 1; + + /* Flood multicast to every port by default */ + for (port = 0; port < priv->ds->num_ports; port++) + if (!dsa_is_unused_port(priv->ds, port)) + l2_lookup[0].destports |= BIT(port); + return 0; } @@ -390,6 +413,12 @@ static int sja1105_init_l2_forwarding(struct sja1105_private *priv) sja1105_port_allow_traffic(l2fwd, i, upstream, true); sja1105_port_allow_traffic(l2fwd, upstream, i, true); + + l2fwd[i].bc_domain = BIT(upstream); + l2fwd[i].fl_domain = BIT(upstream); + + l2fwd[upstream].bc_domain |= BIT(i); + l2fwd[upstream].fl_domain |= BIT(i); } /* Next 8 entries define VLAN PCP mapping from ingress to egress. * Create a one-to-one mapping. @@ -1514,6 +1543,12 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, */ if (!(l2_lookup.destports & BIT(port))) continue; + + /* We need to hide the FDB entry for unknown multicast */ + if (l2_lookup.macaddr == SJA1105_UNKNOWN_MULTICAST && + l2_lookup.mask_macaddr == SJA1105_UNKNOWN_MULTICAST) + continue; + u64_to_ether_addr(l2_lookup.macaddr, macaddr); /* We need to hide the dsa_8021q VLANs from the user. */ @@ -1605,12 +1640,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, case BR_STATE_LEARNING: mac[port].ingress = true; mac[port].egress = false; - mac[port].dyn_learn = true; + mac[port].dyn_learn = !!(priv->learn_ena & BIT(port)); break; case BR_STATE_FORWARDING: mac[port].ingress = true; mac[port].egress = true; - mac[port].dyn_learn = true; + mac[port].dyn_learn = !!(priv->learn_ena & BIT(port)); break; default: dev_err(ds->dev, "invalid STP state: %d\n", state); @@ -3239,6 +3274,160 @@ static void sja1105_port_policer_del(struct dsa_switch *ds, int port) sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); } +static int sja1105_port_set_learning(struct sja1105_private *priv, int port, + bool enabled) +{ + struct sja1105_mac_config_entry *mac; + int rc; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + mac[port].dyn_learn = !!(priv->learn_ena & BIT(port)); + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, + &mac[port], true); + if (rc) + return rc; + + if (enabled) + priv->learn_ena |= BIT(port); + else + priv->learn_ena &= ~BIT(port); + + return 0; +} + +/* Common function for unicast and broadcast flood configuration. + * Flooding is configured between each {ingress, egress} port pair, and since + * the bridge's semantics are those of "egress flooding", it means we must + * enable flooding towards this port from all ingress ports that are in the + * same bridge. In practice, we just enable flooding from all possible ingress + * ports regardless of whether they're in the same bridge or not, since the + * reach_port configuration will not allow flooded frames to leak across + * bridging domains anyway. + */ +static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to, + struct switchdev_brport_flags flags) +{ + struct sja1105_l2_forwarding_entry *l2_fwd; + int from, rc; + + l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; + + for (from = 0; from < priv->ds->num_ports; from++) { + if (dsa_is_unused_port(priv->ds, from)) + continue; + if (from == to) + continue; + + /* Unicast */ + if (flags.mask & BR_FLOOD) { + if (flags.val & BR_FLOOD) + l2_fwd[from].fl_domain |= BIT(to); + else + l2_fwd[from].fl_domain &= ~BIT(to); + } + /* Broadcast */ + if (flags.mask & BR_BCAST_FLOOD) { + if (flags.val & BR_BCAST_FLOOD) + l2_fwd[from].bc_domain |= BIT(to); + else + l2_fwd[from].bc_domain &= ~BIT(to); + } + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, + from, &l2_fwd[from], true); + if (rc < 0) + return rc; + } + + return 0; +} + +static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct sja1105_l2_lookup_entry *l2_lookup; + struct sja1105_table *table; + int match; + + table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; + l2_lookup = table->entries; + + for (match = 0; match < table->entry_count; match++) + if (l2_lookup[match].macaddr == SJA1105_UNKNOWN_MULTICAST && + l2_lookup[match].mask_macaddr == SJA1105_UNKNOWN_MULTICAST) + break; + + if (match == table->entry_count) { + NL_SET_ERR_MSG_MOD(extack, + "Could not find FDB entry for unknown multicast"); + return -ENOSPC; + } + + if (flags.val & BR_MCAST_FLOOD) + l2_lookup[match].destports |= BIT(to); + else + l2_lookup[match].destports &= ~BIT(to); + + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup[match].index, + &l2_lookup[match], + true); +} + +static int sja1105_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | + BR_BCAST_FLOOD)) + return -EINVAL; + + if (flags.mask & BR_LEARNING) { + bool learn_ena = !!(flags.val & BR_LEARNING); + + rc = sja1105_port_set_learning(priv, port, learn_ena); + if (rc) + return rc; + } + + if (flags.mask & (BR_FLOOD | BR_BCAST_FLOOD)) { + rc = sja1105_port_ucast_bcast_flood(priv, port, flags); + if (rc) + return rc; + } + + if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD) && + !priv->info->can_limit_mcast_flood) { + bool multicast = !!(flags.val & BR_MCAST_FLOOD); + bool unicast = !!(flags.val & BR_FLOOD); + + if (unicast != multicast) { + NL_SET_ERR_MSG_MOD(extack, + "This chip cannot configure multicast flooding independently of unicast"); + return -EINVAL; + } + } + + /* For chips that can't offload BR_MCAST_FLOOD independently, there + * is nothing to do here, we ensured the configuration is in sync by + * offloading BR_FLOOD. + */ + if (flags.mask & BR_MCAST_FLOOD && priv->info->can_limit_mcast_flood) { + rc = sja1105_port_mcast_flood(priv, port, flags, + extack); + if (rc) + return rc; + } + + return 0; +} + static const struct dsa_switch_ops sja1105_switch_ops = { .get_tag_protocol = sja1105_get_tag_protocol, .setup = sja1105_setup, @@ -3262,6 +3451,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .port_fdb_del = sja1105_fdb_del, .port_bridge_join = sja1105_bridge_join, .port_bridge_leave = sja1105_bridge_leave, + .port_bridge_flags = sja1105_port_bridge_flags, .port_stp_state_set = sja1105_bridge_stp_state_set, .port_vlan_filtering = sja1105_vlan_filtering, .port_vlan_add = sja1105_vlan_add, diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 591c5734747d..f7a1514f81e8 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -512,6 +512,7 @@ const struct sja1105_info sja1105e_info = { .static_ops = sja1105e_table_ops, .dyn_ops = sja1105et_dyn_ops, .qinq_tpid = ETH_P_8021Q, + .can_limit_mcast_flood = false, .ptp_ts_bits = 24, .ptpegr_ts_bytes = 4, .num_cbs_shapers = SJA1105ET_MAX_CBS_COUNT, @@ -529,6 +530,7 @@ const struct sja1105_info sja1105t_info = { .static_ops = sja1105t_table_ops, .dyn_ops = sja1105et_dyn_ops, .qinq_tpid = ETH_P_8021Q, + .can_limit_mcast_flood = false, .ptp_ts_bits = 24, .ptpegr_ts_bytes = 4, .num_cbs_shapers = SJA1105ET_MAX_CBS_COUNT, @@ -546,6 +548,7 @@ const struct sja1105_info sja1105p_info = { .static_ops = sja1105p_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .qinq_tpid = ETH_P_8021AD, + .can_limit_mcast_flood = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT, @@ -564,6 +567,7 @@ const struct sja1105_info sja1105q_info = { .static_ops = sja1105q_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .qinq_tpid = ETH_P_8021AD, + .can_limit_mcast_flood = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT, @@ -582,6 +586,7 @@ const struct sja1105_info sja1105r_info = { .static_ops = sja1105r_table_ops, .dyn_ops = sja1105pqrs_dyn_ops, .qinq_tpid = ETH_P_8021AD, + .can_limit_mcast_flood = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT, @@ -601,6 +606,7 @@ const struct sja1105_info sja1105s_info = { .dyn_ops = sja1105pqrs_dyn_ops, .regs = &sja1105pqrs_regs, .qinq_tpid = ETH_P_8021AD, + .can_limit_mcast_flood = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .num_cbs_shapers = SJA1105PQRS_MAX_CBS_COUNT,