Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ab83928 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-support-binding-vlan-dev-link-state-to-vlan-member-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: default avatarDavid S. Miller <davem@davemloft.net>
parents 7d26c960 8e1acd4f
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ enum vlan_flags {
	VLAN_FLAG_GVRP			= 0x2,
	VLAN_FLAG_LOOSE_BINDING		= 0x4,
	VLAN_FLAG_MVRP			= 0x8,
	VLAN_FLAG_BRIDGE_BINDING	= 0x10,
};

enum vlan_name_types {
+14 −4
Original line number Diff line number Diff line
@@ -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);
@@ -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
@@ -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:
@@ -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);
@@ -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;

+14 −8
Original line number Diff line number Diff line
@@ -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);
@@ -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;

@@ -326,6 +328,7 @@ 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);

	if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
		netif_carrier_off(dev);
	return 0;
}
@@ -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);

@@ -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 |
@@ -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 {
@@ -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;
+2 −1
Original line number Diff line number Diff line
@@ -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;
		}
+9 −4
Original line number Diff line number Diff line
@@ -40,11 +40,14 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
	bool changed_addr;
	int err;

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

	/* not a port of a bridge */
	p = br_port_get_rtnl(dev);
@@ -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))
Loading