From patchwork Thu Oct 19 15:54:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Aleksandrov X-Patchwork-Id: 828180 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="VvK/1VwC"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHtnD2lNFz9t48 for ; Fri, 20 Oct 2017 02:55:44 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753426AbdJSPzl (ORCPT ); Thu, 19 Oct 2017 11:55:41 -0400 Received: from mail-wm0-f67.google.com ([74.125.82.67]:48771 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752220AbdJSPzj (ORCPT ); Thu, 19 Oct 2017 11:55:39 -0400 Received: by mail-wm0-f67.google.com with SMTP id i124so16805271wmf.3 for ; Thu, 19 Oct 2017 08:55:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=bY3WySyw2w8hioWJ9Qmi+m2xuTPs7dSlScj5o63wmdA=; b=VvK/1VwCL6DzVuVmtTAqOrpaQ10GLHfhdsm3NQuYZLFahG9GnpjVtBu6aRTSAYd3fx so4MXhHGc3yIzxHIH+FKLB63m0eBLws0qXeMTBDX2Dk4VGAy1CMc+V+xN//8+EMA2pEk YS5V7Sujzcqz2FfUjyePlyHv1Ht9FsfibdK7Q= 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; bh=bY3WySyw2w8hioWJ9Qmi+m2xuTPs7dSlScj5o63wmdA=; b=Scmp7mMvXG947+fHAco1fnO/5rLXd5ALxAMqjMQvU0etj44/SzR6k+lADVcJausutO JTNaNF2h/bHHNAIKl0s6wEAvvpUX8jnEuIXQSARxY10tvu9zkVwhfHlFoBVyHWTBim+8 i31I0yLRfra7Aj9PGzU57Bbjx2c/UkQNE4sfE+7909bE9ZgnFyMoa02GJ+pfXl+BfHIm r0FWMUwL5V0JcSG5Xi0b6WKbdNuovoQmPNkmrHufwIJym/9pBl58RCt2cc+dw0eMBW8E 9ecUILbhiU+4VPbsrqm43Fy0nbJFx9ZVF7esZjmZJv75Kb+/w9d19yz9TdE8D0i8YbSF BgXw== X-Gm-Message-State: AMCzsaXqlz3CSFQtyf4L8+bwjkzZooihO55CCjzyCSYGFGAhfHkxor3e VV3j5QWmAHaFxywjsrNAtYvsnmg4 X-Google-Smtp-Source: ABhQp+SkcLeFXolWcQq+taCyZ2qJK6yuTJH/MI2u5M+oVdly+DKX3trBiug9YN4HAD0q4Yrg+3nmPA== X-Received: by 10.28.68.135 with SMTP id r129mr1971566wma.28.1508428537714; Thu, 19 Oct 2017 08:55:37 -0700 (PDT) Received: from debil.mediahub-bg.com (46-10-142-144.ip.btc-net.bg. [46.10.142.144]) by smtp.gmail.com with ESMTPSA id 25sm13272645wrv.8.2017.10.19.08.55.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 19 Oct 2017 08:55:36 -0700 (PDT) From: Nikolay Aleksandrov To: netdev@vger.kernel.org Cc: roopa@cumulusnetworks.com, dsa@cumulusnetworks.com, mrv@mojatatu.com, stephen@networkplumber.org, bridge@lists.linux-foundation.org, Nikolay Aleksandrov Subject: [PATCH net-next 1/2] bridge: netlink: make setlink/dellink notifications more accurate Date: Thu, 19 Oct 2017 18:54:58 +0300 Message-Id: <1508428499-32355-2-git-send-email-nikolay@cumulusnetworks.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1508428499-32355-1-git-send-email-nikolay@cumulusnetworks.com> References: <1508428499-32355-1-git-send-email-nikolay@cumulusnetworks.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Before this patch we had cases that either sent notifications when there were in fact no changes (e.g. non-existent vlan delete) or didn't send notifications when there were changes (e.g. vlan add range with an error in the middle, port flags change + vlan update error). This patch sends down a boolean to the functions setlink/dellink use and if there is even a single configuration change (port flag, vlan add/del, port state) then we always send a notification. This is all done to keep backwards compatibility with the opportunistic vlan delete, where one could specify a vlan range that has missing vlans inside and still everything in that range will be cleared, this is mostly used to clear the whole vlan config with a single call, i.e. range 1-4094. Signed-off-by: Nikolay Aleksandrov --- net/bridge/br_netlink.c | 44 +++++++++++++++++++++++++----------------- net/bridge/br_netlink_tunnel.c | 14 +++++++++----- net/bridge/br_private_tunnel.h | 3 ++- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index f0e82682e071..e8d74a3f44f7 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -506,7 +506,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, } static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, - int cmd, struct bridge_vlan_info *vinfo) + int cmd, struct bridge_vlan_info *vinfo, bool *changed) { int err = 0; @@ -517,21 +517,24 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, * per-VLAN entry as well */ err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); - if (err) - break; } else { vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY; err = br_vlan_add(br, vinfo->vid, vinfo->flags); } + if (!err) + *changed = true; break; case RTM_DELLINK: if (p) { - nbp_vlan_delete(p, vinfo->vid); - if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) - br_vlan_delete(p->br, vinfo->vid); - } else { - br_vlan_delete(br, vinfo->vid); + if (!nbp_vlan_delete(p, vinfo->vid)) + *changed = true; + + if ((vinfo->flags & BRIDGE_VLAN_INFO_MASTER) && + !br_vlan_delete(p->br, vinfo->vid)) + *changed = true; + } else if (!br_vlan_delete(br, vinfo->vid)) { + *changed = true; } break; } @@ -542,7 +545,8 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, static int br_process_vlan_info(struct net_bridge *br, struct net_bridge_port *p, int cmd, struct bridge_vlan_info *vinfo_curr, - struct bridge_vlan_info **vinfo_last) + struct bridge_vlan_info **vinfo_last, + bool *changed) { if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK) return -EINVAL; @@ -572,7 +576,7 @@ static int br_process_vlan_info(struct net_bridge *br, sizeof(struct bridge_vlan_info)); for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) { tmp_vinfo.vid = v; - err = br_vlan_info(br, p, cmd, &tmp_vinfo); + err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed); if (err) break; } @@ -581,13 +585,13 @@ static int br_process_vlan_info(struct net_bridge *br, return 0; } - return br_vlan_info(br, p, cmd, vinfo_curr); + return br_vlan_info(br, p, cmd, vinfo_curr, changed); } static int br_afspec(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *af_spec, - int cmd) + int cmd, bool *changed) { struct bridge_vlan_info *vinfo_curr = NULL; struct bridge_vlan_info *vinfo_last = NULL; @@ -607,7 +611,8 @@ static int br_afspec(struct net_bridge *br, return err; err = br_process_vlan_tunnel_info(br, p, cmd, &tinfo_curr, - &tinfo_last); + &tinfo_last, + changed); if (err) return err; break; @@ -616,7 +621,7 @@ static int br_afspec(struct net_bridge *br, return -EINVAL; vinfo_curr = nla_data(attr); err = br_process_vlan_info(br, p, cmd, vinfo_curr, - &vinfo_last); + &vinfo_last, changed); if (err) return err; break; @@ -804,6 +809,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) struct nlattr *afspec; struct net_bridge_port *p; struct nlattr *tb[IFLA_BRPORT_MAX + 1]; + bool changed = false; int err = 0; protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO); @@ -839,14 +845,15 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) } if (err) goto out; + changed = true; } if (afspec) { err = br_afspec((struct net_bridge *)netdev_priv(dev), p, - afspec, RTM_SETLINK); + afspec, RTM_SETLINK, &changed); } - if (err == 0) + if (changed) br_ifinfo_notify(RTM_NEWLINK, p); out: return err; @@ -857,6 +864,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) { struct nlattr *afspec; struct net_bridge_port *p; + bool changed = false; int err = 0; afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); @@ -869,8 +877,8 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) return -EINVAL; err = br_afspec((struct net_bridge *)netdev_priv(dev), p, - afspec, RTM_DELLINK); - if (err == 0) + afspec, RTM_DELLINK, &changed); + if (changed) /* Send RTM_NEWLINK because userspace * expects RTM_NEWLINK for vlan dels */ diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c index 3712c7f0e00c..da8cb99fd259 100644 --- a/net/bridge/br_netlink_tunnel.c +++ b/net/bridge/br_netlink_tunnel.c @@ -198,7 +198,7 @@ static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + }; static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd, - u16 vid, u32 tun_id) + u16 vid, u32 tun_id, bool *changed) { int err = 0; @@ -208,9 +208,12 @@ static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd, switch (cmd) { case RTM_SETLINK: err = nbp_vlan_tunnel_info_add(p, vid, tun_id); + if (!err) + *changed = true; break; case RTM_DELLINK: - nbp_vlan_tunnel_info_delete(p, vid); + if (!nbp_vlan_tunnel_info_delete(p, vid)) + *changed = true; break; } @@ -254,7 +257,8 @@ int br_parse_vlan_tunnel_info(struct nlattr *attr, int br_process_vlan_tunnel_info(struct net_bridge *br, struct net_bridge_port *p, int cmd, struct vtunnel_info *tinfo_curr, - struct vtunnel_info *tinfo_last) + struct vtunnel_info *tinfo_last, + bool *changed) { int err; @@ -272,7 +276,7 @@ int br_process_vlan_tunnel_info(struct net_bridge *br, return -EINVAL; t = tinfo_last->tunid; for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) { - err = br_vlan_tunnel_info(p, cmd, v, t); + err = br_vlan_tunnel_info(p, cmd, v, t, changed); if (err) return err; t++; @@ -283,7 +287,7 @@ int br_process_vlan_tunnel_info(struct net_bridge *br, if (tinfo_last->flags) return -EINVAL; err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid, - tinfo_curr->tunid); + tinfo_curr->tunid, changed); if (err) return err; memset(tinfo_last, 0, sizeof(struct vtunnel_info)); diff --git a/net/bridge/br_private_tunnel.h b/net/bridge/br_private_tunnel.h index 4a447a378ab3..a259471bfd78 100644 --- a/net/bridge/br_private_tunnel.h +++ b/net/bridge/br_private_tunnel.h @@ -26,7 +26,8 @@ int br_process_vlan_tunnel_info(struct net_bridge *br, struct net_bridge_port *p, int cmd, struct vtunnel_info *tinfo_curr, - struct vtunnel_info *tinfo_last); + struct vtunnel_info *tinfo_last, + bool *changed); int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg); int br_fill_vlan_tunnel_info(struct sk_buff *skb, struct net_bridge_vlan_group *vg); From patchwork Thu Oct 19 15:54:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Aleksandrov X-Patchwork-Id: 828181 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="AHCQlh2x"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yHtnF5xsDz9t48 for ; Fri, 20 Oct 2017 02:55:45 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753512AbdJSPzn (ORCPT ); Thu, 19 Oct 2017 11:55:43 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:56520 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753282AbdJSPzl (ORCPT ); Thu, 19 Oct 2017 11:55:41 -0400 Received: by mail-wm0-f66.google.com with SMTP id l68so17306126wmd.5 for ; Thu, 19 Oct 2017 08:55:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=ZOUL80VGkWE+fom6nZMRcuA46Hr7+65qmQLl3x7NcyY=; b=AHCQlh2xmdu2HbO8ilybpikZimSOwNQiuiMmAUcEz4u9csBxl40U0R4LxLQKV/GAa4 mw5mWLSgioEa+tkDGtfTJbMl4CbcjbVormPpTwe52qZ9IlQ0f36QHKf4Fcc3BSjGBE/n /HGFAsqxTowwq8UehNhnI3Fn41eSAVQAIfER0= 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; bh=ZOUL80VGkWE+fom6nZMRcuA46Hr7+65qmQLl3x7NcyY=; b=pxHeKw2TcUVQG6ooq6YvcV5rDebw7K9EZA0b7vHJNbaESXN3B+OXZ/kvpmeNnyoge3 urU5lC1SgAPaRfBG08xVz8BPsBj5pBg2V1Uktft/6voPykZOBp0tcOA3j309kexb8xSC 0IqkjEC4mvujIq95ZjdVLpDud6TcSkKQ6MF1N2AHmYC7qmWyRqiKbvlUYtgjMNTI2KcE bldMLJ5Z4aSRVb1SLaee6CyMUh14mmkQpRUO+7FQIw3fNyDibOhNX51gb4nwmujqNEfQ QYV6LSUDwzim0idlDEGvMVY28/04wEc9ciuFc4S2Sy0UlvxvG8G3FJe4vmoWOYQPOgNO 1TdA== X-Gm-Message-State: AMCzsaVJUlS/skmaeVlVpxbzNJNLJir1QGkFy6oVqREWxRdU1J591ZUP oudFVe+l6MiTwzxrEU0BI0ywrSoi X-Google-Smtp-Source: ABhQp+Rasx1W+GYG/9jI3PQw6LIFAlJWQha+VHMn6+jekzWtprNRySAZoFCNYwb0Nz5dtAjrzOjxMg== X-Received: by 10.28.234.197 with SMTP id g66mr1939307wmi.76.1508428539511; Thu, 19 Oct 2017 08:55:39 -0700 (PDT) Received: from debil.mediahub-bg.com (46-10-142-144.ip.btc-net.bg. [46.10.142.144]) by smtp.gmail.com with ESMTPSA id 25sm13272645wrv.8.2017.10.19.08.55.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 19 Oct 2017 08:55:38 -0700 (PDT) From: Nikolay Aleksandrov To: netdev@vger.kernel.org Cc: roopa@cumulusnetworks.com, dsa@cumulusnetworks.com, mrv@mojatatu.com, stephen@networkplumber.org, bridge@lists.linux-foundation.org, Nikolay Aleksandrov Subject: [PATCH net-next 2/2] bridge: vlan: return EEXIST on add if nothing changed Date: Thu, 19 Oct 2017 18:54:59 +0300 Message-Id: <1508428499-32355-3-git-send-email-nikolay@cumulusnetworks.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1508428499-32355-1-git-send-email-nikolay@cumulusnetworks.com> References: <1508428499-32355-1-git-send-email-nikolay@cumulusnetworks.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Before this patch there was no way to tell if the vlan add operation actually changed anything, thus we would always generate a notification on adds. Let's make the notifications more precise and generate them only if anything changed, so use EEXIST error to signal that the vlan was not updated in any way. We just need to be careful about a few places that could re-add the same vlan with the same flags not to return any errors on EEXIST. Signed-off-by: Nikolay Aleksandrov --- net/bridge/br_netlink.c | 5 +++++ net/bridge/br_vlan.c | 38 ++++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e8d74a3f44f7..80d9334a4b46 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -523,6 +523,11 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p, } if (!err) *changed = true; + else if (err == -EEXIST) + /* nothing changed, return 0 for overlapping range add + * and compatibility reasons + */ + err = 0; break; case RTM_DELLINK: diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 233a30040c91..e021a80eb8e9 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -32,27 +32,34 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid) return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params); } -static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid) +static bool __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid) { if (vg->pvid == vid) - return; + return false; smp_wmb(); vg->pvid = vid; + + return true; } -static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) +static bool __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) { if (vg->pvid != vid) - return; + return false; smp_wmb(); vg->pvid = 0; + + return true; } -static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) +/* return true if anything changed, false otherwise */ +static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) { struct net_bridge_vlan_group *vg; + u16 old_flags = v->flags; + bool ret; if (br_vlan_is_master(v)) vg = br_vlan_group(v->br); @@ -60,14 +67,16 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) vg = nbp_vlan_group(v->port); if (flags & BRIDGE_VLAN_INFO_PVID) - __vlan_add_pvid(vg, v->vid); + ret = __vlan_add_pvid(vg, v->vid); else - __vlan_delete_pvid(vg, v->vid); + ret = __vlan_delete_pvid(vg, v->vid); if (flags & BRIDGE_VLAN_INFO_UNTAGGED) v->flags |= BRIDGE_VLAN_INFO_UNTAGGED; else v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED; + + return ret || !!(old_flags ^ v->flags); } static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br, @@ -234,7 +243,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags) if (flags & BRIDGE_VLAN_INFO_MASTER) { err = br_vlan_add(br, v->vid, flags | BRIDGE_VLAN_INFO_BRENTRY); - if (err) + if (err && err != -EEXIST) goto out_filt; } @@ -562,6 +571,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) vg = br_vlan_group(br); vlan = br_vlan_find(vg, vid); if (vlan) { + ret = -EEXIST; if (!br_vlan_is_brentry(vlan)) { /* Trying to change flags of non-existent bridge vlan */ if (!(flags & BRIDGE_VLAN_INFO_BRENTRY)) @@ -577,8 +587,10 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY; vg->num_vlans++; } - __vlan_add_flags(vlan, flags); - return 0; + if (__vlan_add_flags(vlan, flags)) + ret = 0; + + return ret; } vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); @@ -870,7 +882,7 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) err = nbp_vlan_add(p, pvid, BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED); - if (err) + if (err && err != -EEXIST) goto err_port; nbp_vlan_delete(p, old_pvid); set_bit(p->port_no, changed); @@ -1037,7 +1049,9 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) ret = switchdev_port_obj_add(port->dev, &v.obj); if (ret && ret != -EOPNOTSUPP) return ret; - __vlan_add_flags(vlan, flags); + if (!__vlan_add_flags(vlan, flags)) + return -EEXIST; + return 0; }