aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Pirko <jiri@resnulli.us>2015-01-15 17:49:37 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-18 00:23:57 -0500
commit3aeb66176ffa8fefd7a9f7d37bda1d8adcf469a1 (patch)
tree9f2232db459c95cf0d000ab95322c6b7aac86235
parent03bf0c281234028388108d0aee720954f5fe6924 (diff)
net: replace br_fdb_external_learn_* calls with switchdev notifier events
This patch benefits from newly introduced switchdev notifier and uses it to propagate fdb learn events from rocker driver to bridge. That avoids direct function calls and possible use by other listeners (ovs). Suggested-by: Thomas Graf <tgraf@suug.ch> Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/rocker/rocker.c10
-rw-r--r--include/linux/if_bridge.h18
-rw-r--r--include/net/switchdev.h11
-rw-r--r--net/bridge/br.c52
-rw-r--r--net/bridge/br_fdb.c38
-rw-r--r--net/bridge/br_private.h4
6 files changed, 78 insertions, 55 deletions
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index cad8cf962cdf..964d719b150f 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -3026,11 +3026,17 @@ static void rocker_port_fdb_learn_work(struct work_struct *work)
3026 container_of(work, struct rocker_fdb_learn_work, work); 3026 container_of(work, struct rocker_fdb_learn_work, work);
3027 bool removing = (lw->flags & ROCKER_OP_FLAG_REMOVE); 3027 bool removing = (lw->flags & ROCKER_OP_FLAG_REMOVE);
3028 bool learned = (lw->flags & ROCKER_OP_FLAG_LEARNED); 3028 bool learned = (lw->flags & ROCKER_OP_FLAG_LEARNED);
3029 struct netdev_switch_notifier_fdb_info info;
3030
3031 info.addr = lw->addr;
3032 info.vid = lw->vid;
3029 3033
3030 if (learned && removing) 3034 if (learned && removing)
3031 br_fdb_external_learn_del(lw->dev, lw->addr, lw->vid); 3035 call_netdev_switch_notifiers(NETDEV_SWITCH_FDB_DEL,
3036 lw->dev, &info.info);
3032 else if (learned && !removing) 3037 else if (learned && !removing)
3033 br_fdb_external_learn_add(lw->dev, lw->addr, lw->vid); 3038 call_netdev_switch_notifiers(NETDEV_SWITCH_FDB_ADD,
3039 lw->dev, &info.info);
3034 3040
3035 kfree(work); 3041 kfree(work);
3036} 3042}
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 0a8ce762a47f..a57bca2ea97e 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -50,24 +50,6 @@ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __use
50typedef int br_should_route_hook_t(struct sk_buff *skb); 50typedef int br_should_route_hook_t(struct sk_buff *skb);
51extern br_should_route_hook_t __rcu *br_should_route_hook; 51extern br_should_route_hook_t __rcu *br_should_route_hook;
52 52
53#if IS_ENABLED(CONFIG_BRIDGE)
54int br_fdb_external_learn_add(struct net_device *dev,
55 const unsigned char *addr, u16 vid);
56int br_fdb_external_learn_del(struct net_device *dev,
57 const unsigned char *addr, u16 vid);
58#else
59static inline int br_fdb_external_learn_add(struct net_device *dev,
60 const unsigned char *addr, u16 vid)
61{
62 return 0;
63}
64static inline int br_fdb_external_learn_del(struct net_device *dev,
65 const unsigned char *addr, u16 vid)
66{
67 return 0;
68}
69#endif
70
71#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) 53#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
72int br_multicast_list_adjacent(struct net_device *dev, 54int br_multicast_list_adjacent(struct net_device *dev,
73 struct list_head *br_ip_list); 55 struct list_head *br_ip_list);
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 7f8d74372d87..201120e18e4d 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -13,10 +13,21 @@
13#include <linux/netdevice.h> 13#include <linux/netdevice.h>
14#include <linux/notifier.h> 14#include <linux/notifier.h>
15 15
16enum netdev_switch_notifier_type {
17 NETDEV_SWITCH_FDB_ADD = 1,
18 NETDEV_SWITCH_FDB_DEL,
19};
20
16struct netdev_switch_notifier_info { 21struct netdev_switch_notifier_info {
17 struct net_device *dev; 22 struct net_device *dev;
18}; 23};
19 24
25struct netdev_switch_notifier_fdb_info {
26 struct netdev_switch_notifier_info info; /* must be first */
27 const unsigned char *addr;
28 u16 vid;
29};
30
20static inline struct net_device * 31static inline struct net_device *
21netdev_switch_notifier_info_to_dev(const struct netdev_switch_notifier_info *info) 32netdev_switch_notifier_info_to_dev(const struct netdev_switch_notifier_info *info)
22{ 33{
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 44425aff7cba..fb57ab6b24f9 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -19,6 +19,7 @@
19#include <linux/llc.h> 19#include <linux/llc.h>
20#include <net/llc.h> 20#include <net/llc.h>
21#include <net/stp.h> 21#include <net/stp.h>
22#include <net/switchdev.h>
22 23
23#include "br_private.h" 24#include "br_private.h"
24 25
@@ -120,6 +121,48 @@ static struct notifier_block br_device_notifier = {
120 .notifier_call = br_device_event 121 .notifier_call = br_device_event
121}; 122};
122 123
124static int br_netdev_switch_event(struct notifier_block *unused,
125 unsigned long event, void *ptr)
126{
127 struct net_device *dev = netdev_switch_notifier_info_to_dev(ptr);
128 struct net_bridge_port *p;
129 struct net_bridge *br;
130 struct netdev_switch_notifier_fdb_info *fdb_info;
131 int err = NOTIFY_DONE;
132
133 rtnl_lock();
134 p = br_port_get_rtnl(dev);
135 if (!p)
136 goto out;
137
138 br = p->br;
139
140 switch (event) {
141 case NETDEV_SWITCH_FDB_ADD:
142 fdb_info = ptr;
143 err = br_fdb_external_learn_add(br, p, fdb_info->addr,
144 fdb_info->vid);
145 if (err)
146 err = notifier_from_errno(err);
147 break;
148 case NETDEV_SWITCH_FDB_DEL:
149 fdb_info = ptr;
150 err = br_fdb_external_learn_del(br, p, fdb_info->addr,
151 fdb_info->vid);
152 if (err)
153 err = notifier_from_errno(err);
154 break;
155 }
156
157out:
158 rtnl_unlock();
159 return err;
160}
161
162static struct notifier_block br_netdev_switch_notifier = {
163 .notifier_call = br_netdev_switch_event,
164};
165
123static void __net_exit br_net_exit(struct net *net) 166static void __net_exit br_net_exit(struct net *net)
124{ 167{
125 struct net_device *dev; 168 struct net_device *dev;
@@ -169,10 +212,14 @@ static int __init br_init(void)
169 if (err) 212 if (err)
170 goto err_out3; 213 goto err_out3;
171 214
172 err = br_netlink_init(); 215 err = register_netdev_switch_notifier(&br_netdev_switch_notifier);
173 if (err) 216 if (err)
174 goto err_out4; 217 goto err_out4;
175 218
219 err = br_netlink_init();
220 if (err)
221 goto err_out5;
222
176 brioctl_set(br_ioctl_deviceless_stub); 223 brioctl_set(br_ioctl_deviceless_stub);
177 224
178#if IS_ENABLED(CONFIG_ATM_LANE) 225#if IS_ENABLED(CONFIG_ATM_LANE)
@@ -185,6 +232,8 @@ static int __init br_init(void)
185 232
186 return 0; 233 return 0;
187 234
235err_out5:
236 unregister_netdev_switch_notifier(&br_netdev_switch_notifier);
188err_out4: 237err_out4:
189 unregister_netdevice_notifier(&br_device_notifier); 238 unregister_netdevice_notifier(&br_device_notifier);
190err_out3: 239err_out3:
@@ -202,6 +251,7 @@ static void __exit br_deinit(void)
202{ 251{
203 stp_proto_unregister(&br_stp_proto); 252 stp_proto_unregister(&br_stp_proto);
204 br_netlink_fini(); 253 br_netlink_fini();
254 unregister_netdev_switch_notifier(&br_netdev_switch_notifier);
205 unregister_netdevice_notifier(&br_device_notifier); 255 unregister_netdevice_notifier(&br_device_notifier);
206 brioctl_set(NULL); 256 brioctl_set(NULL);
207 unregister_pernet_subsys(&br_net_ops); 257 unregister_pernet_subsys(&br_net_ops);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index e6e0372bc3cd..03667e65cc29 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -990,26 +990,14 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
990 } 990 }
991} 991}
992 992
993int br_fdb_external_learn_add(struct net_device *dev, 993int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
994 const unsigned char *addr, u16 vid) 994 const unsigned char *addr, u16 vid)
995{ 995{
996 struct net_bridge_port *p;
997 struct net_bridge *br;
998 struct hlist_head *head; 996 struct hlist_head *head;
999 struct net_bridge_fdb_entry *fdb; 997 struct net_bridge_fdb_entry *fdb;
1000 int err = 0; 998 int err = 0;
1001 999
1002 rtnl_lock(); 1000 ASSERT_RTNL();
1003
1004 p = br_port_get_rtnl(dev);
1005 if (!p) {
1006 pr_info("bridge: %s not a bridge port\n", dev->name);
1007 err = -EINVAL;
1008 goto err_rtnl_unlock;
1009 }
1010
1011 br = p->br;
1012
1013 spin_lock_bh(&br->hash_lock); 1001 spin_lock_bh(&br->hash_lock);
1014 1002
1015 head = &br->hash[br_mac_hash(addr, vid)]; 1003 head = &br->hash[br_mac_hash(addr, vid)];
@@ -1034,33 +1022,18 @@ int br_fdb_external_learn_add(struct net_device *dev,
1034 1022
1035err_unlock: 1023err_unlock:
1036 spin_unlock_bh(&br->hash_lock); 1024 spin_unlock_bh(&br->hash_lock);
1037err_rtnl_unlock:
1038 rtnl_unlock();
1039 1025
1040 return err; 1026 return err;
1041} 1027}
1042EXPORT_SYMBOL(br_fdb_external_learn_add);
1043 1028
1044int br_fdb_external_learn_del(struct net_device *dev, 1029int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
1045 const unsigned char *addr, u16 vid) 1030 const unsigned char *addr, u16 vid)
1046{ 1031{
1047 struct net_bridge_port *p;
1048 struct net_bridge *br;
1049 struct hlist_head *head; 1032 struct hlist_head *head;
1050 struct net_bridge_fdb_entry *fdb; 1033 struct net_bridge_fdb_entry *fdb;
1051 int err = 0; 1034 int err = 0;
1052 1035
1053 rtnl_lock(); 1036 ASSERT_RTNL();
1054
1055 p = br_port_get_rtnl(dev);
1056 if (!p) {
1057 pr_info("bridge: %s not a bridge port\n", dev->name);
1058 err = -EINVAL;
1059 goto err_rtnl_unlock;
1060 }
1061
1062 br = p->br;
1063
1064 spin_lock_bh(&br->hash_lock); 1037 spin_lock_bh(&br->hash_lock);
1065 1038
1066 head = &br->hash[br_mac_hash(addr, vid)]; 1039 head = &br->hash[br_mac_hash(addr, vid)];
@@ -1071,9 +1044,6 @@ int br_fdb_external_learn_del(struct net_device *dev,
1071 err = -ENOENT; 1044 err = -ENOENT;
1072 1045
1073 spin_unlock_bh(&br->hash_lock); 1046 spin_unlock_bh(&br->hash_lock);
1074err_rtnl_unlock:
1075 rtnl_unlock();
1076 1047
1077 return err; 1048 return err;
1078} 1049}
1079EXPORT_SYMBOL(br_fdb_external_learn_del);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d808d766334d..e8e3f3681680 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -402,6 +402,10 @@ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
402 struct net_device *dev, struct net_device *fdev, int idx); 402 struct net_device *dev, struct net_device *fdev, int idx);
403int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); 403int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
404void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); 404void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
405int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
406 const unsigned char *addr, u16 vid);
407int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
408 const unsigned char *addr, u16 vid);
405 409
406/* br_forward.c */ 410/* br_forward.c */
407void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); 411void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);