Skip to content

Commit

Permalink
Merge branch 'net-support-binding-vlan-dev-link-state-to-vlan-member-…
Browse files Browse the repository at this point in the history
…bridge-ports'

Mike Manning says:

====================
net: support binding vlan dev link state to vlan member bridge ports

For vlan filtering on bridges, the bridge may also have vlan devices
as upper devices. For switches, these are used to provide L3 packet
processing for ports that are members of a given vlan.

While it is correct that the admin state for these vlan devices is
either set directly for the device or inherited from the lower device,
the link state is also transferred from the lower device. So this is
always up if the bridge is in admin up state and there is at least one
bridge port that is up, regardless of the vlan that the port is in.

The link state of the vlan device may need to track only the state of
the subset of ports that are also members of the corresponding vlan,
rather than that of all ports.

This series provides an optional vlan flag so that the link state of
the vlan device is only up if there is at least one bridge port that is
up AND is a member of the corresponding vlan.

v2:
   - Address review comments from Nikolay Aleksandrov
     in patches 3 & 4 and add patch 5 to address bridge link down due to STP
v3:
   - Address review comment from Nikolay Aleksandrov
     in patch 4 so as to remove unnecessary inline #ifdef
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Apr 19, 2019
2 parents 7d26c96 + 8e1acd4 commit 1ab8392
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 21 deletions.
9 changes: 5 additions & 4 deletions include/uapi/linux/if_vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ enum vlan_ioctl_cmds {
};

enum vlan_flags {
VLAN_FLAG_REORDER_HDR = 0x1,
VLAN_FLAG_GVRP = 0x2,
VLAN_FLAG_LOOSE_BINDING = 0x4,
VLAN_FLAG_MVRP = 0x8,
VLAN_FLAG_REORDER_HDR = 0x1,
VLAN_FLAG_GVRP = 0x2,
VLAN_FLAG_LOOSE_BINDING = 0x4,
VLAN_FLAG_MVRP = 0x8,
VLAN_FLAG_BRIDGE_BINDING = 0x10,
};

enum vlan_name_types {
Expand Down
18 changes: 14 additions & 4 deletions net/8021q/vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg,
return 0;
}

static void vlan_stacked_transfer_operstate(const struct net_device *rootdev,
struct net_device *dev,
struct vlan_dev_priv *vlan)
{
if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
netif_stacked_transfer_operstate(rootdev, dev);
}

void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
Expand Down Expand Up @@ -180,7 +188,7 @@ int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack)
/* Account for reference in struct vlan_dev_priv */
dev_hold(real_dev);

netif_stacked_transfer_operstate(real_dev, dev);
vlan_stacked_transfer_operstate(real_dev, dev, vlan);
linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */

/* So, got the sucker initialized, now lets place
Expand Down Expand Up @@ -399,7 +407,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
case NETDEV_CHANGE:
/* Propagate real device state to vlan devices */
vlan_group_for_each_dev(grp, i, vlandev)
netif_stacked_transfer_operstate(dev, vlandev);
vlan_stacked_transfer_operstate(dev, vlandev,
vlan_dev_priv(vlandev));
break;

case NETDEV_CHANGEADDR:
Expand Down Expand Up @@ -446,7 +455,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
dev_close_many(&close_list, false);

list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) {
netif_stacked_transfer_operstate(dev, vlandev);
vlan_stacked_transfer_operstate(dev, vlandev,
vlan_dev_priv(vlandev));
list_del_init(&vlandev->close_list);
}
list_del(&close_list);
Expand All @@ -463,7 +473,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
dev_change_flags(vlandev, flgs | IFF_UP,
extack);
netif_stacked_transfer_operstate(dev, vlandev);
vlan_stacked_transfer_operstate(dev, vlandev, vlan);
}
break;

Expand Down
22 changes: 14 additions & 8 deletions net/8021q/vlan_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask)
u32 old_flags = vlan->flags;

if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP))
VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP |
VLAN_FLAG_BRIDGE_BINDING))
return -EINVAL;

vlan->flags = (old_flags & ~mask) | (flags & mask);
Expand Down Expand Up @@ -296,7 +297,8 @@ static int vlan_dev_open(struct net_device *dev)
if (vlan->flags & VLAN_FLAG_MVRP)
vlan_mvrp_request_join(dev);

if (netif_carrier_ok(real_dev))
if (netif_carrier_ok(real_dev) &&
!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
netif_carrier_on(dev);
return 0;

Expand Down Expand Up @@ -326,7 +328,8 @@ static int vlan_dev_stop(struct net_device *dev)
if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr))
dev_uc_del(real_dev, dev->dev_addr);

netif_carrier_off(dev);
if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
netif_carrier_off(dev);
return 0;
}

Expand Down Expand Up @@ -550,7 +553,8 @@ static const struct net_device_ops vlan_netdev_ops;

static int vlan_dev_init(struct net_device *dev)
{
struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct net_device *real_dev = vlan->real_dev;

netif_carrier_off(dev);

Expand All @@ -561,6 +565,9 @@ static int vlan_dev_init(struct net_device *dev)
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);

if (vlan->flags & VLAN_FLAG_BRIDGE_BINDING)
dev->state |= (1 << __LINK_STATE_NOCARRIER);

dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |
NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
NETIF_F_GSO_ENCAP_ALL |
Expand Down Expand Up @@ -591,8 +598,7 @@ static int vlan_dev_init(struct net_device *dev)
#endif

dev->needed_headroom = real_dev->needed_headroom;
if (vlan_hw_offload_capable(real_dev->features,
vlan_dev_priv(dev)->vlan_proto)) {
if (vlan_hw_offload_capable(real_dev->features, vlan->vlan_proto)) {
dev->header_ops = &vlan_passthru_header_ops;
dev->hard_header_len = real_dev->hard_header_len;
} else {
Expand All @@ -606,8 +612,8 @@ static int vlan_dev_init(struct net_device *dev)

vlan_dev_set_lockdep_class(dev, vlan_dev_get_lock_subclass(dev));

vlan_dev_priv(dev)->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
if (!vlan_dev_priv(dev)->vlan_pcpu_stats)
vlan->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
if (!vlan->vlan_pcpu_stats)
return -ENOMEM;

return 0;
Expand Down
3 changes: 2 additions & 1 deletion net/8021q/vlan_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[],
flags = nla_data(data[IFLA_VLAN_FLAGS]);
if ((flags->flags & flags->mask) &
~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) {
VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP |
VLAN_FLAG_BRIDGE_BINDING)) {
NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN flags");
return -EINVAL;
}
Expand Down
13 changes: 9 additions & 4 deletions net/bridge/br.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
bool changed_addr;
int err;

/* register of bridge completed, add sysfs entries */
if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
br_sysfs_addbr(dev);
return NOTIFY_DONE;
if (dev->priv_flags & IFF_EBRIDGE) {
if (event == NETDEV_REGISTER) {
/* register of bridge completed, add sysfs entries */
br_sysfs_addbr(dev);
return NOTIFY_DONE;
}
br_vlan_bridge_event(dev, event, ptr);
}

/* not a port of a bridge */
Expand Down Expand Up @@ -126,6 +129,8 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break;
}

br_vlan_port_event(p, event);

/* Events that may cause spanning tree to refresh */
if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
event == NETDEV_CHANGE || event == NETDEV_DOWN))
Expand Down
14 changes: 14 additions & 0 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ enum net_bridge_opts {
BROPT_MTU_SET_BY_USER,
BROPT_VLAN_STATS_PER_PORT,
BROPT_NO_LL_LEARN,
BROPT_VLAN_BRIDGE_BINDING,
};

struct net_bridge {
Expand Down Expand Up @@ -895,6 +896,9 @@ int nbp_vlan_init(struct net_bridge_port *port, struct netlink_ext_ack *extack);
int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
void br_vlan_get_stats(const struct net_bridge_vlan *v,
struct br_vlan_stats *stats);
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
void br_vlan_bridge_event(struct net_device *dev, unsigned long event,
void *ptr);

static inline struct net_bridge_vlan_group *br_vlan_group(
const struct net_bridge *br)
Expand Down Expand Up @@ -1078,6 +1082,16 @@ static inline void br_vlan_get_stats(const struct net_bridge_vlan *v,
struct br_vlan_stats *stats)
{
}

static inline void br_vlan_port_event(struct net_bridge_port *p,
unsigned long event)
{
}

static inline void br_vlan_bridge_event(struct net_device *dev,
unsigned long event, void *ptr)
{
}
#endif

struct nf_br_ops {
Expand Down
Loading

0 comments on commit 1ab8392

Please sign in to comment.