diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_netlink.c | 11 | ||||
-rw-r--r-- | net/bridge/br_private.h | 8 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 47 |
3 files changed, 49 insertions, 17 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index fe1980d5a7e..e044cc0b565 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -120,6 +120,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_port_vlans *pv; struct bridge_vlan_info vinfo; u16 vid; + u16 pvid; if (port) pv = nbp_get_vlan_info(port); @@ -133,12 +134,15 @@ static int br_fill_ifinfo(struct sk_buff *skb, if (!af) goto nla_put_failure; + pvid = br_get_pvid(pv); for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); vid < BR_VLAN_BITMAP_LEN; vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) { vinfo.vid = vid; vinfo.flags = 0; + if (vid == pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo)) goto nla_put_failure; @@ -239,14 +243,15 @@ static int br_afspec(struct net_bridge *br, switch (cmd) { case RTM_SETLINK: if (p) { - err = nbp_vlan_add(p, vinfo->vid); + err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); if (err) break; if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) - err = br_vlan_add(p->br, vinfo->vid); + err = br_vlan_add(p->br, vinfo->vid, + vinfo->flags); } else - err = br_vlan_add(br, vinfo->vid); + err = br_vlan_add(br, vinfo->vid, vinfo->flags); if (err) break; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ea8e7efd913..1ae6395a036 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -562,11 +562,11 @@ extern bool br_allowed_egress(struct net_bridge *br, extern struct sk_buff *br_handle_vlan(struct net_bridge *br, const struct net_port_vlans *v, struct sk_buff *skb); -extern int br_vlan_add(struct net_bridge *br, u16 vid); +extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern void br_vlan_flush(struct net_bridge *br); extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); -extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); +extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); extern void nbp_vlan_flush(struct net_bridge_port *port); @@ -633,7 +633,7 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, return skb; } -static inline int br_vlan_add(struct net_bridge *br, u16 vid) +static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) { return -EOPNOTSUPP; } @@ -647,7 +647,7 @@ static inline void br_vlan_flush(struct net_bridge *br) { } -static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) { return -EOPNOTSUPP; } diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 20057de56db..c79940cff3a 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -5,12 +5,33 @@ #include "br_private.h" -static int __vlan_add(struct net_port_vlans *v, u16 vid) +static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid) +{ + if (v->pvid == vid) + return; + + smp_wmb(); + v->pvid = vid; +} + +static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid) +{ + if (v->pvid != vid) + return; + + smp_wmb(); + v->pvid = 0; +} + +static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) { int err; - if (test_bit(vid, v->vlan_bitmap)) - return -EEXIST; + if (test_bit(vid, v->vlan_bitmap)) { + if (flags & BRIDGE_VLAN_INFO_PVID) + __vlan_add_pvid(v, vid); + return 0; + } if (v->port_idx && vid) { struct net_device *dev = v->parent.port->dev; @@ -29,6 +50,9 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid) set_bit(vid, v->vlan_bitmap); v->num_vlans++; + if (flags & BRIDGE_VLAN_INFO_PVID) + __vlan_add_pvid(v, vid); + return 0; } @@ -37,6 +61,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) if (!test_bit(vid, v->vlan_bitmap)) return -EINVAL; + __vlan_delete_pvid(v, vid); + if (v->port_idx && vid) { struct net_device *dev = v->parent.port->dev; @@ -58,6 +84,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) static void __vlan_flush(struct net_port_vlans *v) { + smp_wmb(); + v->pvid = 0; bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); @@ -185,7 +213,7 @@ bool br_allowed_egress(struct net_bridge *br, } /* Must be protected by RTNL */ -int br_vlan_add(struct net_bridge *br, u16 vid) +int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) { struct net_port_vlans *pv = NULL; int err; @@ -194,7 +222,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid) pv = rtnl_dereference(br->vlan_info); if (pv) - return __vlan_add(pv, vid); + return __vlan_add(pv, vid, flags); /* Create port vlan infomration */ @@ -203,7 +231,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid) return -ENOMEM; pv->parent.br = br; - err = __vlan_add(pv, vid); + err = __vlan_add(pv, vid, flags); if (err) goto out; @@ -234,7 +262,6 @@ void br_vlan_flush(struct net_bridge *br) struct net_port_vlans *pv; ASSERT_RTNL(); - pv = rtnl_dereference(br->vlan_info); if (!pv) return; @@ -258,7 +285,7 @@ unlock: } /* Must be protected by RTNL */ -int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) { struct net_port_vlans *pv = NULL; int err; @@ -267,7 +294,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid) pv = rtnl_dereference(port->vlan_info); if (pv) - return __vlan_add(pv, vid); + return __vlan_add(pv, vid, flags); /* Create port vlan infomration */ @@ -279,7 +306,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid) pv->port_idx = port->port_no; pv->parent.port = port; - err = __vlan_add(pv, vid); + err = __vlan_add(pv, vid, flags); if (err) goto clean_up; |