aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/slave.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-02-25 17:04:15 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-25 17:04:15 -0500
commitbb66be1c549a0760500cfad404b3d79a136d0e44 (patch)
treed0a46201df407c4719abade5cd44bcb86041b195 /net/dsa/slave.c
parent92bf200881d978bc3c6a290991ae1f9ddc7b5411 (diff)
parent12f460f23423e81d6dd3efeb78906ae634ad8fc9 (diff)
Merge branch 'sf2_hwbridge'
Florian Fainelli says: ==================== net: dsa: integration with SWITCHDEV for HW bridging This patch set provides the DSA and SWITCHDEV integration bits together and modifies the bcm_sf2 driver accordingly such that it works properly with HW bridging. Changes in v3: - add back the null pointer check in dsa_slave_br_port_mask from Guenter - slightly rework patch 1 commit message not to mention the function name we add in patch 2 Changes in v2: - avoid a race condition in how DSA network devices are created, patch from Guenter Roeck - provide a consistent and work STP state once a port leaves the bridge - retain a bridge device pointer to properly flag port/bridge membership - properly flush the ARL (Address Resolution Logic) in bcm_sf2.c - properly retain port membership when individually bringing devices up/down while they are members of a bridge We discussed on the mailing-list the possibility of standardizing a "fdb_flush" operation for DSA switch drivers, looking at the Marvell and Broadcom switches, I am not convinced this is practical or diserable as the terminologies vary here, but there is nothing preventing us from doing it later. Many thanks to Guenter and Andrew for both testing and providing feedback. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa/slave.c')
-rw-r--r--net/dsa/slave.c164
1 files changed, 157 insertions, 7 deletions
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index f23deadf42a0..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)
@@ -605,9 +712,8 @@ int dsa_slave_resume(struct net_device *slave_dev)
605 return 0; 712 return 0;
606} 713}
607 714
608struct net_device * 715int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
609dsa_slave_create(struct dsa_switch *ds, struct device *parent, 716 int port, char *name)
610 int port, char *name)
611{ 717{
612 struct net_device *master = ds->dst->master_netdev; 718 struct net_device *master = ds->dst->master_netdev;
613 struct net_device *slave_dev; 719 struct net_device *slave_dev;
@@ -617,7 +723,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
617 slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, 723 slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
618 NET_NAME_UNKNOWN, ether_setup); 724 NET_NAME_UNKNOWN, ether_setup);
619 if (slave_dev == NULL) 725 if (slave_dev == NULL)
620 return slave_dev; 726 return -ENOMEM;
621 727
622 slave_dev->features = master->vlan_features; 728 slave_dev->features = master->vlan_features;
623 slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; 729 slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
@@ -667,19 +773,63 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
667 ret = dsa_slave_phy_setup(p, slave_dev); 773 ret = dsa_slave_phy_setup(p, slave_dev);
668 if (ret) { 774 if (ret) {
669 free_netdev(slave_dev); 775 free_netdev(slave_dev);
670 return NULL; 776 return ret;
671 } 777 }
672 778
779 ds->ports[port] = slave_dev;
673 ret = register_netdev(slave_dev); 780 ret = register_netdev(slave_dev);
674 if (ret) { 781 if (ret) {
675 netdev_err(master, "error %d registering interface %s\n", 782 netdev_err(master, "error %d registering interface %s\n",
676 ret, slave_dev->name); 783 ret, slave_dev->name);
677 phy_disconnect(p->phy); 784 phy_disconnect(p->phy);
785 ds->ports[port] = NULL;
678 free_netdev(slave_dev); 786 free_netdev(slave_dev);
679 return NULL; 787 return ret;
680 } 788 }
681 789
682 netif_carrier_off(slave_dev); 790 netif_carrier_off(slave_dev);
683 791
684 return slave_dev; 792 return 0;
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;
685} 835}