net: bridge: avoid duplicate notification on up/down/change netdev events
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Thu, 3 May 2018 10:47:24 +0000 (13:47 +0300)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 May 2018 17:40:54 +0000 (13:40 -0400)
While handling netdevice events, br_device_event() sometimes uses
br_stp_(disable|enable)_port which unconditionally send a notification,
but then a second notification for the same event is sent at the end of
the br_device_event() function. To avoid sending duplicate notifications
in such cases, check if one has already been sent (i.e.
br_stp_enable/disable_port have been called).
The patch is based on a change by Satish Ashok.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br.c
net/bridge/br_if.c
net/bridge/br_private.h

index 671d13c10f6f4ed8118fe31fcceb28d7cde6edf1..2ca0350546640db122ba843c47d17f3d020b551f 100644 (file)
@@ -34,6 +34,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net_bridge_port *p;
        struct net_bridge *br;
+       bool notified = false;
        bool changed_addr;
        int err;
 
@@ -67,7 +68,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
                break;
 
        case NETDEV_CHANGE:
-               br_port_carrier_check(p);
+               br_port_carrier_check(p, &notified);
                break;
 
        case NETDEV_FEAT_CHANGE:
@@ -76,8 +77,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
 
        case NETDEV_DOWN:
                spin_lock_bh(&br->lock);
-               if (br->dev->flags & IFF_UP)
+               if (br->dev->flags & IFF_UP) {
                        br_stp_disable_port(p);
+                       notified = true;
+               }
                spin_unlock_bh(&br->lock);
                break;
 
@@ -85,6 +88,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
                if (netif_running(br->dev) && netif_oper_up(dev)) {
                        spin_lock_bh(&br->lock);
                        br_stp_enable_port(p);
+                       notified = true;
                        spin_unlock_bh(&br->lock);
                }
                break;
@@ -110,8 +114,8 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
        }
 
        /* Events that may cause spanning tree to refresh */
-       if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
-           event == NETDEV_CHANGE || event == NETDEV_DOWN)
+       if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
+                         event == NETDEV_CHANGE || event == NETDEV_DOWN))
                br_ifinfo_notify(RTM_NEWLINK, NULL, p);
 
        return NOTIFY_DONE;
index 82c1a6f430b37d8fb9b4713ad7ab370a3ef5b990..e3a8ea1bcbe2fba148c1a538cf3783a80415cc49 100644 (file)
@@ -64,7 +64,7 @@ static int port_cost(struct net_device *dev)
 
 
 /* Check for port carrier transitions. */
-void br_port_carrier_check(struct net_bridge_port *p)
+void br_port_carrier_check(struct net_bridge_port *p, bool *notified)
 {
        struct net_device *dev = p->dev;
        struct net_bridge *br = p->br;
@@ -73,16 +73,21 @@ void br_port_carrier_check(struct net_bridge_port *p)
            netif_running(dev) && netif_oper_up(dev))
                p->path_cost = port_cost(dev);
 
+       *notified = false;
        if (!netif_running(br->dev))
                return;
 
        spin_lock_bh(&br->lock);
        if (netif_running(dev) && netif_oper_up(dev)) {
-               if (p->state == BR_STATE_DISABLED)
+               if (p->state == BR_STATE_DISABLED) {
                        br_stp_enable_port(p);
+                       *notified = true;
+               }
        } else {
-               if (p->state != BR_STATE_DISABLED)
+               if (p->state != BR_STATE_DISABLED) {
                        br_stp_disable_port(p);
+                       *notified = true;
+               }
        }
        spin_unlock_bh(&br->lock);
 }
index 1a5093115534290ae63ee8e1cf6d4811a3eba4f0..0ddeeea2c6a761d78dde1cb36e66a30ac68ab677 100644 (file)
@@ -573,7 +573,7 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
              enum br_pkt_type pkt_type, bool local_rcv, bool local_orig);
 
 /* br_if.c */
-void br_port_carrier_check(struct net_bridge_port *p);
+void br_port_carrier_check(struct net_bridge_port *p, bool *notified);
 int br_add_bridge(struct net *net, const char *name);
 int br_del_bridge(struct net *net, const char *name);
 int br_add_if(struct net_bridge *br, struct net_device *dev,