summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoopa Prabhu <roopa@cumulusnetworks.com>2015-02-25 23:55:40 -0800
committerDavid S. Miller <davem@davemloft.net>2015-02-26 11:25:43 -0500
commitfed0a159c8c5e453d79d6a73897c576efea0a8a5 (patch)
tree2240c310159853566c8916a6f324cb5f89ff342d
parent9003019192f045eee8e451916a4bc289c2a3e1be (diff)
bridge: fix link notification skb size calculation to include vlan ranges
my previous patch skipped vlan range optimizations during skb size calculations for simplicity. This incremental patch considers vlan ranges during skb size calculations. This leads to a bit of code duplication in the fill and size calculation functions. But, I could not find a prettier way to do this. will take any suggestions. Previously, I had reused the existing br_get_link_af_size size calculation function to calculate skb size for notifications. Reusing it this time around creates some change in behaviour issues for the usual .get_link_af_size callback. This patch adds a new br_get_link_af_size_filtered() function to base the size calculation on the incoming filter flag and include vlan ranges. Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/br_netlink.c92
1 files changed, 85 insertions, 7 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 17e0177467f5..3de0eefe2b82 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -22,9 +22,64 @@
#include "br_private.h"
#include "br_private_stp.h"
-static size_t br_get_link_af_size(const struct net_device *dev)
+static int br_get_num_vlan_infos(const struct net_port_vlans *pv,
+ u32 filter_mask)
+{
+ u16 vid_range_start = 0, vid_range_end = 0;
+ u16 vid_range_flags = 0;
+ u16 pvid, vid, flags;
+ int num_vlans = 0;
+
+ if (filter_mask & RTEXT_FILTER_BRVLAN)
+ return pv->num_vlans;
+
+ if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
+ return 0;
+
+ /* Count number of vlan info's
+ */
+ pvid = br_get_pvid(pv);
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+ flags = 0;
+ if (vid == pvid)
+ flags |= BRIDGE_VLAN_INFO_PVID;
+
+ if (test_bit(vid, pv->untagged_bitmap))
+ flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+
+ if (vid_range_start == 0) {
+ goto initvars;
+ } else if ((vid - vid_range_end) == 1 &&
+ flags == vid_range_flags) {
+ vid_range_end = vid;
+ continue;
+ } else {
+ if ((vid_range_end - vid_range_start) > 0)
+ num_vlans += 2;
+ else
+ num_vlans += 1;
+ }
+initvars:
+ vid_range_start = vid;
+ vid_range_end = vid;
+ vid_range_flags = flags;
+ }
+
+ if (vid_range_start != 0) {
+ if ((vid_range_end - vid_range_start) > 0)
+ num_vlans += 2;
+ else
+ num_vlans += 1;
+ }
+
+ return num_vlans;
+}
+
+static size_t br_get_link_af_size_filtered(const struct net_device *dev,
+ u32 filter_mask)
{
struct net_port_vlans *pv;
+ int num_vlan_infos;
if (br_port_exists(dev))
pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
@@ -36,8 +91,12 @@ static size_t br_get_link_af_size(const struct net_device *dev)
if (!pv)
return 0;
+ num_vlan_infos = br_get_num_vlan_infos(pv, filter_mask);
+ if (!num_vlan_infos)
+ return 0;
+
/* Each VLAN is returned in bridge_vlan_info along with flags */
- return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
+ return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
}
static inline size_t br_port_info_size(void)
@@ -54,7 +113,7 @@ static inline size_t br_port_info_size(void)
+ 0;
}
-static inline size_t br_nlmsg_size(struct net_device *dev)
+static inline size_t br_nlmsg_size(struct net_device *dev, u32 filter_mask)
{
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
@@ -64,7 +123,8 @@ static inline size_t br_nlmsg_size(struct net_device *dev)
+ nla_total_size(4) /* IFLA_LINK */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(br_port_info_size()) /* IFLA_PROTINFO */
- + nla_total_size(br_get_link_af_size(dev)); /* IFLA_AF_SPEC */
+ + nla_total_size(br_get_link_af_size_filtered(dev,
+ filter_mask)); /* IFLA_AF_SPEC */
}
static int br_port_fill_attrs(struct sk_buff *skb,
@@ -299,6 +359,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
struct net *net;
struct sk_buff *skb;
int err = -ENOBUFS;
+ u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED;
if (!port)
return;
@@ -307,12 +368,11 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
br_debug(port->br, "port %u(%s) event %d\n",
(unsigned int)port->port_no, port->dev->name, event);
- skb = nlmsg_new(br_nlmsg_size(port->dev), GFP_ATOMIC);
+ skb = nlmsg_new(br_nlmsg_size(port->dev, filter), GFP_ATOMIC);
if (skb == NULL)
goto errout;
- err = br_fill_ifinfo(skb, port, 0, 0, event, 0,
- RTEXT_FILTER_BRVLAN_COMPRESSED, port->dev);
+ err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, port->dev);
if (err < 0) {
/* -EMSGSIZE implies BUG in br_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -723,6 +783,24 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
return 0;
}
+static size_t br_get_link_af_size(const struct net_device *dev)
+{
+ struct net_port_vlans *pv;
+
+ if (br_port_exists(dev))
+ pv = nbp_get_vlan_info(br_port_get_rtnl(dev));
+ else if (dev->priv_flags & IFF_EBRIDGE)
+ pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev));
+ else
+ return 0;
+
+ if (!pv)
+ return 0;
+
+ /* Each VLAN is returned in bridge_vlan_info along with flags */
+ return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info));
+}
+
static struct rtnl_af_ops br_af_ops __read_mostly = {
.family = AF_BRIDGE,
.get_link_af_size = br_get_link_af_size,