Make pvid a pointer to a vlan struct and RCUify the access to it. Vlans are already RCU-protected so the pvid vlan entry cannot disappear without being initialized to NULL and going through a grace period first. This change is necessary for the upcoming vlan counters and also would serve to later move to vlan passing via a pointer instead of id.
Signed-off-by: Nikolay Aleksandrov <niko...@cumulusnetworks.com> --- net/bridge/br_netlink.c | 23 ++++++++++----------- net/bridge/br_private.h | 16 +-------------- net/bridge/br_vlan.c | 54 +++++++++++++++++++++++-------------------------- 3 files changed, 37 insertions(+), 56 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e9c635eae24d..f33d95b0f5d3 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -24,22 +24,22 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg, u32 filter_mask) { - struct net_bridge_vlan *v; + struct net_bridge_vlan *v, *pvid; u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0; - u16 flags, pvid; int num_vlans = 0; + u16 flags; if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) return 0; - pvid = br_get_pvid(vg); + pvid = rcu_dereference(vg->pvid); /* Count number of vlan infos */ list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { flags = 0; /* only a context, bridge vlan not activated */ if (!br_vlan_should_use(v)) continue; - if (v->vid == pvid) + if (v == pvid) flags |= BRIDGE_VLAN_INFO_PVID; if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED) @@ -243,21 +243,21 @@ nla_put_failure: static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb, struct net_bridge_vlan_group *vg) { - struct net_bridge_vlan *v; + struct net_bridge_vlan *v, *pvid; u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0; - u16 flags, pvid; int err = 0; + u16 flags; /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan * and mark vlan info with begin and end flags * if vlaninfo represents a range */ - pvid = br_get_pvid(vg); + pvid = rcu_dereference(vg->pvid); list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { flags = 0; if (!br_vlan_should_use(v)) continue; - if (v->vid == pvid) + if (v == pvid) flags |= BRIDGE_VLAN_INFO_PVID; if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED) @@ -298,18 +298,17 @@ initvars: static int br_fill_ifvlaninfo(struct sk_buff *skb, struct net_bridge_vlan_group *vg) { + struct net_bridge_vlan *v, *pvid; struct bridge_vlan_info vinfo; - struct net_bridge_vlan *v; - u16 pvid; - pvid = br_get_pvid(vg); + pvid = rcu_dereference(vg->pvid); list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; vinfo.vid = v->vid; vinfo.flags = 0; - if (v->vid == pvid) + if (v == pvid) vinfo.flags |= BRIDGE_VLAN_INFO_PVID; if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1b5d145dfcbf..50d70b5eb307 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -130,8 +130,8 @@ struct net_bridge_vlan { struct net_bridge_vlan_group { struct rhashtable vlan_hash; struct list_head vlan_list; + struct net_bridge_vlan __rcu *pvid; u16 num_vlans; - u16 pvid; }; struct net_bridge_fdb_entry @@ -741,15 +741,6 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) return err; } -static inline u16 br_get_pvid(const struct net_bridge_vlan_group *vg) -{ - if (!vg) - return 0; - - smp_rmb(); - return vg->pvid; -} - static inline int br_vlan_enabled(struct net_bridge *br) { return br->vlan_enabled; @@ -835,11 +826,6 @@ static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) return 0; } -static inline u16 br_get_pvid(const struct net_bridge_vlan_group *vg) -{ - return 0; -} - static inline int br_vlan_enabled(struct net_bridge *br) { return 0; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index e001152d6ad1..4fab7665df8c 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -31,22 +31,11 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid) return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params); } -static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid) +/* __vlan_delete_pvid is just __vlan_set_pvid(vg, NULL) */ +static void __vlan_set_pvid(struct net_bridge_vlan_group *vg, + struct net_bridge_vlan *v) { - if (vg->pvid == vid) - return; - - smp_wmb(); - vg->pvid = vid; -} - -static void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid) -{ - if (vg->pvid != vid) - return; - - smp_wmb(); - vg->pvid = 0; + rcu_assign_pointer(vg->pvid, v); } static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) @@ -59,9 +48,9 @@ static void __vlan_add_flags(struct net_bridge_vlan *v, u16 flags) vg = nbp_vlan_group(v->port); if (flags & BRIDGE_VLAN_INFO_PVID) - __vlan_add_pvid(vg, v->vid); - else - __vlan_delete_pvid(vg, v->vid); + __vlan_set_pvid(vg, v); + else if (rtnl_dereference(vg->pvid) == v) + __vlan_set_pvid(vg, NULL); if (flags & BRIDGE_VLAN_INFO_UNTAGGED) v->flags |= BRIDGE_VLAN_INFO_UNTAGGED; @@ -285,7 +274,9 @@ static int __vlan_del(struct net_bridge_vlan *v) masterv = v->brvlan; } - __vlan_delete_pvid(vg, v->vid); + if (rtnl_dereference(vg->pvid) == v) + __vlan_set_pvid(vg, NULL); + if (p) { err = __vlan_vid_del(p->dev, p->br, v->vid); if (err) @@ -320,7 +311,7 @@ static void __vlan_flush(struct net_bridge_vlan_group *vg) { struct net_bridge_vlan *vlan, *tmp; - __vlan_delete_pvid(vg, vg->pvid); + __vlan_set_pvid(vg, NULL); list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) __vlan_del(vlan); } @@ -404,29 +395,29 @@ static bool __allowed_ingress(struct net_bridge_vlan_group *vg, __be16 proto, } if (!*vid) { - u16 pvid = br_get_pvid(vg); + v = rcu_dereference(vg->pvid); /* Frame had a tag with VID 0 or did not have a tag. * See if pvid is set on this port. That tells us which * vlan untagged or priority-tagged traffic belongs to. */ - if (!pvid) + if (!v) goto drop; /* PVID is set on this port. Any untagged or priority-tagged * ingress frame is considered to belong to this vlan. */ - *vid = pvid; + *vid = v->vid; if (likely(!tagged)) /* Untagged Frame. */ - __vlan_hwaccel_put_tag(skb, proto, pvid); + __vlan_hwaccel_put_tag(skb, proto, v->vid); else /* Priority-tagged Frame. * At this point, We know that skb->vlan_tci had * VLAN_TAG_PRESENT bit and its VID field was 0x000. * We update only VID field and preserve PCP field. */ - skb->vlan_tci |= pvid; + skb->vlan_tci |= v->vid; return true; } @@ -451,6 +442,9 @@ bool br_allowed_ingress(const struct net_bridge *br, BR_INPUT_SKB_CB(skb)->vlan_filtered = false; return true; } + /* if there's no vlan_group, there's nothing to match against */ + if (!vg) + return false; return __allowed_ingress(vg, br->vlan_proto, skb, vid); } @@ -492,9 +486,11 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) *vid = 0; if (!*vid) { - *vid = br_get_pvid(vg); - if (!*vid) + struct net_bridge_vlan *v = rcu_dereference(vg->pvid); + + if (!v) return false; + *vid = v->vid; return true; } @@ -713,9 +709,9 @@ int br_vlan_set_proto(struct net_bridge *br, unsigned long val) static bool vlan_default_pvid(struct net_bridge_vlan_group *vg, u16 vid) { - struct net_bridge_vlan *v; + struct net_bridge_vlan *v = rtnl_dereference(vg->pvid); - if (vid != vg->pvid) + if (v && vid != v->vid) return false; v = br_vlan_lookup(&vg->vlan_hash, vid); -- 2.4.11