aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2015-02-24 16:15:33 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-25 17:03:38 -0500
commitb73adef67765b72f2a0d01ef15aff9d784dc85da (patch)
tree829c9c90cffe94d00a1a7ee568c9464fdcf0efae
parentd87d6f44d7c1254fd9560a5191659cb00882db56 (diff)
net: dsa: integrate with SWITCHDEV for HW bridging
In order to support bridging offloads in DSA switch drivers, select NET_SWITCHDEV to get access to the port_stp_update and parent_get_id NDOs that we are required to implement. To facilitate the integratation at the DSA driver level, we implement 3 types of operations: - port_join_bridge - port_leave_bridge - port_stp_update DSA will resolve which switch ports that are currently bridge port members as some Switch hardware/drivers need to know about that to limit the register programming to just the relevant registers (especially for slow MDIO buses). We also take care of setting the correct STP state when slave network devices are brought up/down while being bridge members. Finally, when a port is leaving the bridge, we make sure we set in BR_STATE_FORWARDING state, otherwise the bridge layer would leave it disabled as a result of having left the bridge. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Tested-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/dsa.h10
-rw-r--r--net/dsa/Kconfig1
-rw-r--r--net/dsa/dsa.c7
-rw-r--r--net/dsa/dsa_priv.h4
-rw-r--r--net/dsa/slave.c149
5 files changed, 171 insertions, 0 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h
index ed3c34bbb67a..92be34791963 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -275,6 +275,16 @@ struct dsa_switch_driver {
275 int (*get_regs_len)(struct dsa_switch *ds, int port); 275 int (*get_regs_len)(struct dsa_switch *ds, int port);
276 void (*get_regs)(struct dsa_switch *ds, int port, 276 void (*get_regs)(struct dsa_switch *ds, int port,
277 struct ethtool_regs *regs, void *p); 277 struct ethtool_regs *regs, void *p);
278
279 /*
280 * Bridge integration
281 */
282 int (*port_join_bridge)(struct dsa_switch *ds, int port,
283 u32 br_port_mask);
284 int (*port_leave_bridge)(struct dsa_switch *ds, int port,
285 u32 br_port_mask);
286 int (*port_stp_update)(struct dsa_switch *ds, int port,
287 u8 state);
278}; 288};
279 289
280void register_switch_driver(struct dsa_switch_driver *type); 290void register_switch_driver(struct dsa_switch_driver *type);
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 5f8ac404535b..b45206e8dd3e 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -8,6 +8,7 @@ config NET_DSA
8 tristate 8 tristate
9 depends on HAVE_NET_DSA 9 depends on HAVE_NET_DSA
10 select PHYLIB 10 select PHYLIB
11 select NET_SWITCHDEV
11 12
12if NET_DSA 13if NET_DSA
13 14
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index fc1813140be6..9c208f0dab08 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -826,6 +826,10 @@ static struct packet_type dsa_pack_type __read_mostly = {
826 .func = dsa_switch_rcv, 826 .func = dsa_switch_rcv,
827}; 827};
828 828
829static struct notifier_block dsa_netdevice_nb __read_mostly = {
830 .notifier_call = dsa_slave_netdevice_event,
831};
832
829#ifdef CONFIG_PM_SLEEP 833#ifdef CONFIG_PM_SLEEP
830static int dsa_suspend(struct device *d) 834static int dsa_suspend(struct device *d)
831{ 835{
@@ -884,6 +888,8 @@ static int __init dsa_init_module(void)
884{ 888{
885 int rc; 889 int rc;
886 890
891 register_netdevice_notifier(&dsa_netdevice_nb);
892
887 rc = platform_driver_register(&dsa_driver); 893 rc = platform_driver_register(&dsa_driver);
888 if (rc) 894 if (rc)
889 return rc; 895 return rc;
@@ -896,6 +902,7 @@ module_init(dsa_init_module);
896 902
897static void __exit dsa_cleanup_module(void) 903static void __exit dsa_cleanup_module(void)
898{ 904{
905 unregister_netdevice_notifier(&dsa_netdevice_nb);
899 dev_remove_pack(&dsa_pack_type); 906 dev_remove_pack(&dsa_pack_type);
900 platform_driver_unregister(&dsa_driver); 907 platform_driver_unregister(&dsa_driver);
901} 908}
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 7eb1a6acd46c..d5f1f9b862ea 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -45,6 +45,8 @@ struct dsa_slave_priv {
45 int old_link; 45 int old_link;
46 int old_pause; 46 int old_pause;
47 int old_duplex; 47 int old_duplex;
48
49 struct net_device *bridge_dev;
48}; 50};
49 51
50/* dsa.c */ 52/* dsa.c */
@@ -57,6 +59,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
57 int port, char *name); 59 int port, char *name);
58int dsa_slave_suspend(struct net_device *slave_dev); 60int dsa_slave_suspend(struct net_device *slave_dev);
59int dsa_slave_resume(struct net_device *slave_dev); 61int dsa_slave_resume(struct net_device *slave_dev);
62int dsa_slave_netdevice_event(struct notifier_block *unused,
63 unsigned long event, void *ptr);
60 64
61/* tag_dsa.c */ 65/* tag_dsa.c */
62extern const struct dsa_device_ops dsa_netdev_ops; 66extern const struct dsa_device_ops dsa_netdev_ops;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 5be4c928c9c9..b5a4d8974b76 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -10,10 +10,13 @@
10 10
11#include <linux/list.h> 11#include <linux/list.h>
12#include <linux/etherdevice.h> 12#include <linux/etherdevice.h>
13#include <linux/netdevice.h>
13#include <linux/phy.h> 14#include <linux/phy.h>
14#include <linux/phy_fixed.h> 15#include <linux/phy_fixed.h>
15#include <linux/of_net.h> 16#include <linux/of_net.h>
16#include <linux/of_mdio.h> 17#include <linux/of_mdio.h>
18#include <net/rtnetlink.h>
19#include <linux/if_bridge.h>
17#include "dsa_priv.h" 20#include "dsa_priv.h"
18 21
19/* slave mii_bus handling ***************************************************/ 22/* slave mii_bus handling ***************************************************/
@@ -60,11 +63,18 @@ static int dsa_slave_init(struct net_device *dev)
60 return 0; 63 return 0;
61} 64}
62 65
66static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
67{
68 return !!p->bridge_dev;
69}
70
63static int dsa_slave_open(struct net_device *dev) 71static int dsa_slave_open(struct net_device *dev)
64{ 72{
65 struct dsa_slave_priv *p = netdev_priv(dev); 73 struct dsa_slave_priv *p = netdev_priv(dev);
66 struct net_device *master = p->parent->dst->master_netdev; 74 struct net_device *master = p->parent->dst->master_netdev;
67 struct dsa_switch *ds = p->parent; 75 struct dsa_switch *ds = p->parent;
76 u8 stp_state = dsa_port_is_bridged(p) ?
77 BR_STATE_BLOCKING : BR_STATE_FORWARDING;
68 int err; 78 int err;
69 79
70 if (!(master->flags & IFF_UP)) 80 if (!(master->flags & IFF_UP))
@@ -93,6 +103,9 @@ static int dsa_slave_open(struct net_device *dev)
93 goto clear_promisc; 103 goto clear_promisc;
94 } 104 }
95 105
106 if (ds->drv->port_stp_update)
107 ds->drv->port_stp_update(ds, p->port, stp_state);
108
96 if (p->phy) 109 if (p->phy)
97 phy_start(p->phy); 110 phy_start(p->phy);
98 111
@@ -133,6 +146,9 @@ static int dsa_slave_close(struct net_device *dev)
133 if (ds->drv->port_disable) 146 if (ds->drv->port_disable)
134 ds->drv->port_disable(ds, p->port, p->phy); 147 ds->drv->port_disable(ds, p->port, p->phy);
135 148
149 if (ds->drv->port_stp_update)
150 ds->drv->port_stp_update(ds, p->port, BR_STATE_DISABLED);
151
136 return 0; 152 return 0;
137} 153}
138 154
@@ -194,6 +210,95 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
194 return -EOPNOTSUPP; 210 return -EOPNOTSUPP;
195} 211}
196 212
213/* Return a bitmask of all ports being currently bridged within a given bridge
214 * device. Note that on leave, the mask will still return the bitmask of ports
215 * currently bridged, prior to port removal, and this is exactly what we want.
216 */
217static u32 dsa_slave_br_port_mask(struct dsa_switch *ds,
218 struct net_device *bridge)
219{
220 struct dsa_slave_priv *p;
221 unsigned int port;
222 u32 mask = 0;
223
224 for (port = 0; port < DSA_MAX_PORTS; port++) {
225 if (!((1 << port) & ds->phys_port_mask))
226 continue;
227
228 if (!ds->ports[port])
229 continue;
230
231 p = netdev_priv(ds->ports[port]);
232
233 if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT &&
234 p->bridge_dev == bridge)
235 mask |= 1 << port;
236 }
237
238 return mask;
239}
240
241static int dsa_slave_stp_update(struct net_device *dev, u8 state)
242{
243 struct dsa_slave_priv *p = netdev_priv(dev);
244 struct dsa_switch *ds = p->parent;
245 int ret = -EOPNOTSUPP;
246
247 if (ds->drv->port_stp_update)
248 ret = ds->drv->port_stp_update(ds, p->port, state);
249
250 return ret;
251}
252
253static int dsa_slave_bridge_port_join(struct net_device *dev,
254 struct net_device *br)
255{
256 struct dsa_slave_priv *p = netdev_priv(dev);
257 struct dsa_switch *ds = p->parent;
258 int ret = -EOPNOTSUPP;
259
260 p->bridge_dev = br;
261
262 if (ds->drv->port_join_bridge)
263 ret = ds->drv->port_join_bridge(ds, p->port,
264 dsa_slave_br_port_mask(ds, br));
265
266 return ret;
267}
268
269static int dsa_slave_bridge_port_leave(struct net_device *dev)
270{
271 struct dsa_slave_priv *p = netdev_priv(dev);
272 struct dsa_switch *ds = p->parent;
273 int ret = -EOPNOTSUPP;
274
275
276 if (ds->drv->port_leave_bridge)
277 ret = ds->drv->port_leave_bridge(ds, p->port,
278 dsa_slave_br_port_mask(ds, p->bridge_dev));
279
280 p->bridge_dev = NULL;
281
282 /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
283 * so allow it to be in BR_STATE_FORWARDING to be kept functional
284 */
285 dsa_slave_stp_update(dev, BR_STATE_FORWARDING);
286
287 return ret;
288}
289
290static int dsa_slave_parent_id_get(struct net_device *dev,
291 struct netdev_phys_item_id *psid)
292{
293 struct dsa_slave_priv *p = netdev_priv(dev);
294 struct dsa_switch *ds = p->parent;
295
296 psid->id_len = sizeof(ds->index);
297 memcpy(&psid->id, &ds->index, psid->id_len);
298
299 return 0;
300}
301
197static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) 302static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
198{ 303{
199 struct dsa_slave_priv *p = netdev_priv(dev); 304 struct dsa_slave_priv *p = netdev_priv(dev);
@@ -470,6 +575,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
470 .ndo_set_rx_mode = dsa_slave_set_rx_mode, 575 .ndo_set_rx_mode = dsa_slave_set_rx_mode,
471 .ndo_set_mac_address = dsa_slave_set_mac_address, 576 .ndo_set_mac_address = dsa_slave_set_mac_address,
472 .ndo_do_ioctl = dsa_slave_ioctl, 577 .ndo_do_ioctl = dsa_slave_ioctl,
578 .ndo_switch_parent_id_get = dsa_slave_parent_id_get,
579 .ndo_switch_port_stp_update = dsa_slave_stp_update,
473}; 580};
474 581
475static void dsa_slave_adjust_link(struct net_device *dev) 582static void dsa_slave_adjust_link(struct net_device *dev)
@@ -684,3 +791,45 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
684 791
685 return 0; 792 return 0;
686} 793}
794
795static bool dsa_slave_dev_check(struct net_device *dev)
796{
797 return dev->netdev_ops == &dsa_slave_netdev_ops;
798}
799
800static int dsa_slave_master_changed(struct net_device *dev)
801{
802 struct net_device *master = netdev_master_upper_dev_get(dev);
803 int err = 0;
804
805 if (master && master->rtnl_link_ops &&
806 !strcmp(master->rtnl_link_ops->kind, "bridge"))
807 err = dsa_slave_bridge_port_join(dev, master);
808 else
809 err = dsa_slave_bridge_port_leave(dev);
810
811 return err;
812}
813
814int dsa_slave_netdevice_event(struct notifier_block *unused,
815 unsigned long event, void *ptr)
816{
817 struct net_device *dev;
818 int err = 0;
819
820 switch (event) {
821 case NETDEV_CHANGEUPPER:
822 dev = netdev_notifier_info_to_dev(ptr);
823 if (!dsa_slave_dev_check(dev))
824 goto out;
825
826 err = dsa_slave_master_changed(dev);
827 if (err)
828 netdev_warn(dev, "failed to reflect master change\n");
829
830 break;
831 }
832
833out:
834 return NOTIFY_DONE;
835}