diff options
author | Nikolay Aleksandrov <nikolay@cumulusnetworks.com> | 2020-01-14 19:56:14 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-01-15 13:48:18 +0100 |
commit | f545923b4a6bd7abedac50ea3fce76c713be1b74 (patch) | |
tree | fabdf1329730096e3334d1a478749d8dd6be344d /net/bridge/br_netlink.c | |
parent | cf5bddb95cbe5340e486e84d46cf2f0bb324078c (diff) | |
download | linux-f545923b4a6bd7abedac50ea3fce76c713be1b74.tar.bz2 |
net: bridge: vlan: notify on vlan add/delete/change flags
Now that we can notify, send a notification on add/del or change of flags.
Notifications are also compressed when possible to reduce their number
and relieve user-space of extra processing, due to that we have to
manually notify after each add/del in order to avoid double
notifications. We try hard to notify only about the vlans which actually
changed, thus a single command can result in multiple notifications
about disjoint ranges if there were vlans which didn't change inside.
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_netlink.c')
-rw-r--r-- | net/bridge/br_netlink.c | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index b3da4f46dc64..43dab4066f91 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -568,9 +568,14 @@ int br_process_vlan_info(struct net_bridge *br, bool *changed, struct netlink_ext_ack *extack) { + int err, rtm_cmd; + if (!br_vlan_valid_id(vinfo_curr->vid, extack)) return -EINVAL; + /* needed for vlan-only NEWVLAN/DELVLAN notifications */ + rtm_cmd = br_afspec_cmd_to_rtm(cmd); + if (vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { if (!br_vlan_valid_range(vinfo_curr, *vinfo_last, extack)) return -EINVAL; @@ -580,7 +585,7 @@ int br_process_vlan_info(struct net_bridge *br, if (*vinfo_last) { struct bridge_vlan_info tmp_vinfo; - int v, err; + int v, v_change_start = 0; if (!br_vlan_valid_range(vinfo_curr, *vinfo_last, extack)) return -EINVAL; @@ -588,18 +593,41 @@ int br_process_vlan_info(struct net_bridge *br, memcpy(&tmp_vinfo, *vinfo_last, sizeof(struct bridge_vlan_info)); for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) { + bool curr_change = false; + tmp_vinfo.vid = v; - err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed, + err = br_vlan_info(br, p, cmd, &tmp_vinfo, &curr_change, extack); if (err) break; + if (curr_change) { + *changed = curr_change; + if (!v_change_start) + v_change_start = v; + } else { + /* nothing to notify yet */ + if (!v_change_start) + continue; + br_vlan_notify(br, p, v_change_start, + v - 1, rtm_cmd); + v_change_start = 0; + } } + /* v_change_start is set only if the last/whole range changed */ + if (v_change_start) + br_vlan_notify(br, p, v_change_start, + v - 1, rtm_cmd); + *vinfo_last = NULL; return err; } - return br_vlan_info(br, p, cmd, vinfo_curr, changed, extack); + err = br_vlan_info(br, p, cmd, vinfo_curr, changed, extack); + if (*changed) + br_vlan_notify(br, p, vinfo_curr->vid, 0, rtm_cmd); + + return err; } static int br_afspec(struct net_bridge *br, |