On 17/04/2019 21:16, Mike Manning wrote:
> If vlan bridge binding is enabled, then the link state of a vlan device
> that is an upper device of the bridge tracks the state of bridge ports
> that are members of that vlan. But this can only be done when the link
> state of the bridge is up. If it is down, then the link state of the
> vlan devices must also be down. This is to maintain existing behavior
> for when STP is enabled and there are no live ports, in which case the
> link state for the bridge and any vlan devices is down.
> 
> Signed-off-by: Mike Manning <[email protected]>
> ---
>  net/bridge/br_vlan.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 47 insertions(+), 3 deletions(-)
> 
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 89146a5f0c23..2db63997f313 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -1343,6 +1343,11 @@ static void br_vlan_set_vlan_dev_state(const struct 
> net_bridge *br,
>       struct net_bridge_port *p;
>       bool has_carrier = false;
>  
> +     if (!netif_carrier_ok(br->dev)) {
> +             netif_carrier_off(vlan_dev);
> +             return;
> +     }
> +
>       list_for_each_entry(p, &br->port_list, list) {
>               vg = nbp_vlan_group(p);
>               if (br_vlan_find(vg, vid) && br_vlan_is_dev_up(p->dev)) {
> @@ -1367,10 +1372,12 @@ static void br_vlan_set_all_vlan_dev_state(struct 
> net_bridge_port *p)
>               vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev,
>                                                          vlan->vid);
>               if (vlan_dev) {
> -                     if (br_vlan_is_dev_up(p->dev))
> -                             netif_carrier_on(vlan_dev);
> -                     else
> +                     if (br_vlan_is_dev_up(p->dev)) {
> +                             if (netif_carrier_ok(p->br->dev))
> +                                     netif_carrier_on(vlan_dev);
> +                     } else {
>                               br_vlan_set_vlan_dev_state(p->br, vlan_dev);
> +                     }
>               }
>       }
>  }
> @@ -1393,6 +1400,34 @@ static void br_vlan_upper_change(struct net_device 
> *dev,
>       }
>  }
>  
> +struct br_vlan_link_state_walk_data {
> +     struct net_bridge *br;
> +};
> +
> +static int br_vlan_link_state_change_fn(struct net_device *vlan_dev,
> +                                     void *data_in)
> +{
> +     struct br_vlan_link_state_walk_data *data = data_in;
> +
> +     if (br_vlan_is_bind_vlan_dev(vlan_dev))
> +             br_vlan_set_vlan_dev_state(data->br, vlan_dev);
> +
> +     return 0;
> +}
> +
> +static void br_vlan_link_state_change(struct net_device *dev,
> +                                   struct net_bridge *br)
> +{
> +     struct br_vlan_link_state_walk_data data = {
> +             .br = br
> +     };
> +
> +     rcu_read_lock();
> +     netdev_walk_all_upper_dev_rcu(dev, br_vlan_link_state_change_fn,
> +                                   &data);
> +     rcu_read_unlock();
> +}
> +
>  /* Must be protected by RTNL. */
>  static void nbp_vlan_set_vlan_dev_state(struct net_bridge_port *p, u16 vid)
>  {
> @@ -1411,12 +1446,21 @@ void br_vlan_bridge_event(struct net_device *dev, 
> unsigned long event,
>                         void *ptr)
>  {
>       struct netdev_notifier_changeupper_info *info;
> +     struct net_bridge *br;
>  
>       switch (event) {
>       case NETDEV_CHANGEUPPER:
>               info = ptr;
>               br_vlan_upper_change(dev, info->upper_dev, info->linking);
>               break;
> +
> +     case NETDEV_CHANGE:
> +     case NETDEV_UP:
> +             br = netdev_priv(dev);
> +             if (!br_opt_get(br, BROPT_VLAN_BRIDGE_BINDING))
> +                     return;
> +             br_vlan_link_state_change(dev, br);
> +             break;
>       }
>  }
>  
> 

Acked-by: Nikolay Aleksandrov <[email protected]>

Reply via email to