Merge branch 'bond_stacked_vlans'

Vlad Yasevich says:

====================
Fixed stacked vlan usage on top of bonds

Bonding device driver now support q-in-q on top for bonds.  There are
a few issues here though.

First, when arp monitoring is used, bonding driver will not correctly
tag traffic if the source of the arp device was configured on top of
q-in-q.  It may also incorrectly pick the wrong vlan id if the ordering
of that upper devices isn't as expected (there is no guarntee on ordering).

Second, the alb/tlb may use what would be considered 'inner' vlans in
its learning announcements, as it simply announces all vlans configured
on top of the bond without regard for encapsulation/stacking.

This series fixes the above 2 issues.  This series also depends on the
functionality introduced in
	http://patchwork.ozlabs.org/patch/349766/

Since v1:
  - Changed how patch1 verifies the device path.  We no longer use the
    _all_upper version of the function.  We find the path and if it was
    found, then collect the vlan information.
  - Use the constant to devine maximum vlan nest level support on top
    of bonding.  This can be changed if 2 is too low.
  - Inlude patch2 into the series.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-05-16 22:29:17 -04:00
commit a8d0d8417c
6 changed files with 108 additions and 70 deletions

View File

@ -1045,7 +1045,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[])
/* loop through vlans and send one packet for each */
rcu_read_lock();
netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
if (upper->priv_flags & IFF_802_1Q_VLAN)
if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0)
alb_send_lp_vid(slave, mac_addr,
vlan_dev_vlan_id(upper));
}

View File

@ -2126,10 +2126,10 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
*/
static void bond_arp_send(struct net_device *slave_dev, int arp_op,
__be32 dest_ip, __be32 src_ip,
struct bond_vlan_tag *inner,
struct bond_vlan_tag *outer)
struct bond_vlan_tag *tags)
{
struct sk_buff *skb;
int i;
pr_debug("arp %d on slave %s: dst %pI4 src %pI4\n",
arp_op, slave_dev->name, &dest_ip, &src_ip);
@ -2141,21 +2141,26 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
net_err_ratelimited("ARP packet allocation failed\n");
return;
}
if (outer->vlan_id) {
if (inner->vlan_id) {
pr_debug("inner tag: proto %X vid %X\n",
ntohs(inner->vlan_proto), inner->vlan_id);
skb = __vlan_put_tag(skb, inner->vlan_proto,
inner->vlan_id);
if (!skb) {
net_err_ratelimited("failed to insert inner VLAN tag\n");
return;
}
}
pr_debug("outer reg: proto %X vid %X\n",
ntohs(outer->vlan_proto), outer->vlan_id);
skb = vlan_put_tag(skb, outer->vlan_proto, outer->vlan_id);
/* Go through all the tags backwards and add them to the packet */
for (i = BOND_MAX_VLAN_ENCAP - 1; i > 0; i--) {
if (!tags[i].vlan_id)
continue;
pr_debug("inner tag: proto %X vid %X\n",
ntohs(tags[i].vlan_proto), tags[i].vlan_id);
skb = __vlan_put_tag(skb, tags[i].vlan_proto,
tags[i].vlan_id);
if (!skb) {
net_err_ratelimited("failed to insert inner VLAN tag\n");
return;
}
}
/* Set the outer tag */
if (tags[0].vlan_id) {
pr_debug("outer tag: proto %X vid %X\n",
ntohs(tags[0].vlan_proto), tags[0].vlan_id);
skb = vlan_put_tag(skb, tags[0].vlan_proto, tags[0].vlan_id);
if (!skb) {
net_err_ratelimited("failed to insert outer VLAN tag\n");
return;
@ -2164,22 +2169,52 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
arp_xmit(skb);
}
/* Validate the device path between the @start_dev and the @end_dev.
* The path is valid if the @end_dev is reachable through device
* stacking.
* When the path is validated, collect any vlan information in the
* path.
*/
static bool bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev,
struct bond_vlan_tag *tags)
{
struct net_device *upper;
struct list_head *iter;
int idx;
if (start_dev == end_dev)
return true;
netdev_for_each_upper_dev_rcu(start_dev, upper, iter) {
if (bond_verify_device_path(upper, end_dev, tags)) {
if (is_vlan_dev(upper)) {
idx = vlan_get_encap_level(upper);
if (idx >= BOND_MAX_VLAN_ENCAP)
return false;
tags[idx].vlan_proto =
vlan_dev_vlan_proto(upper);
tags[idx].vlan_id = vlan_dev_vlan_id(upper);
}
return true;
}
}
return false;
}
static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
{
struct net_device *upper, *vlan_upper;
struct list_head *iter, *vlan_iter;
struct rtable *rt;
struct bond_vlan_tag inner, outer;
struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
__be32 *targets = bond->params.arp_targets, addr;
int i;
bool ret;
for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
pr_debug("basa: target %pI4\n", &targets[i]);
inner.vlan_proto = 0;
inner.vlan_id = 0;
outer.vlan_proto = 0;
outer.vlan_id = 0;
memset(tags, 0, sizeof(tags));
/* Find out through which dev should the packet go */
rt = ip_route_output(dev_net(bond->dev), targets[i], 0,
@ -2192,7 +2227,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
net_warn_ratelimited("%s: no route to arp_ip_target %pI4 and arp_validate is set\n",
bond->dev->name,
&targets[i]);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], 0, &inner, &outer);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
0, tags);
continue;
}
@ -2201,52 +2237,12 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
goto found;
rcu_read_lock();
/* first we search only for vlan devices. for every vlan
* found we verify its upper dev list, searching for the
* rt->dst.dev. If found we save the tag of the vlan and
* proceed to send the packet.
*/
netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper,
vlan_iter) {
if (!is_vlan_dev(vlan_upper))
continue;
if (vlan_upper == rt->dst.dev) {
outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper);
outer.vlan_id = vlan_dev_vlan_id(vlan_upper);
rcu_read_unlock();
goto found;
}
netdev_for_each_all_upper_dev_rcu(vlan_upper, upper,
iter) {
if (upper == rt->dst.dev) {
/* If the upper dev is a vlan dev too,
* set the vlan tag to inner tag.
*/
if (is_vlan_dev(upper)) {
inner.vlan_proto = vlan_dev_vlan_proto(upper);
inner.vlan_id = vlan_dev_vlan_id(upper);
}
outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper);
outer.vlan_id = vlan_dev_vlan_id(vlan_upper);
rcu_read_unlock();
goto found;
}
}
}
/* if the device we're looking for is not on top of any of
* our upper vlans, then just search for any dev that
* matches, and in case it's a vlan - save the id
*/
netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
if (upper == rt->dst.dev) {
rcu_read_unlock();
goto found;
}
}
ret = bond_verify_device_path(bond->dev, rt->dst.dev, tags);
rcu_read_unlock();
if (ret)
goto found;
/* Not our device - skip */
pr_debug("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
bond->dev->name, &targets[i],
@ -2259,7 +2255,7 @@ found:
addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
ip_rt_put(rt);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
addr, &inner, &outer);
addr, tags);
}
}

View File

@ -36,6 +36,7 @@
#define bond_version DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n"
#define BOND_MAX_VLAN_ENCAP 2
#define BOND_MAX_ARP_TARGETS 16
#define BOND_DEFAULT_MIIMON 100

View File

@ -484,4 +484,10 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb,
*/
skb->protocol = htons(ETH_P_802_2);
}
static inline int vlan_get_encap_level(struct net_device *dev)
{
BUG_ON(!is_vlan_dev(dev));
return vlan_dev_priv(dev)->nest_level;
}
#endif /* !(_LINUX_IF_VLAN_H_) */

View File

@ -3056,9 +3056,18 @@ extern int weight_p;
extern int bpf_jit_enable;
bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev);
struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
struct list_head **iter);
struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
struct list_head **iter);
/* iterate through upper list, must be called under RCU read lock */
#define netdev_for_each_upper_dev_rcu(dev, updev, iter) \
for (iter = &(dev)->adj_list.upper, \
updev = netdev_upper_get_next_dev_rcu(dev, &(iter)); \
updev; \
updev = netdev_upper_get_next_dev_rcu(dev, &(iter)))
/* iterate through upper list, must be called under RCU read lock */
#define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \
for (iter = &(dev)->all_adj_list.upper, \

View File

@ -4541,6 +4541,32 @@ void *netdev_adjacent_get_private(struct list_head *adj_list)
}
EXPORT_SYMBOL(netdev_adjacent_get_private);
/**
* netdev_upper_get_next_dev_rcu - Get the next dev from upper list
* @dev: device
* @iter: list_head ** of the current position
*
* Gets the next device from the dev's upper list, starting from iter
* position. The caller must hold RCU read lock.
*/
struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev,
struct list_head **iter)
{
struct netdev_adjacent *upper;
WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held());
upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list);
if (&upper->list == &dev->adj_list.upper)
return NULL;
*iter = &upper->list;
return upper->dev;
}
EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu);
/**
* netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list
* @dev: device