diff options
author | Vivien Didelot <vivien.didelot@savoirfairelinux.com> | 2017-02-03 13:20:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-02-06 16:53:29 -0500 |
commit | 04d3a4c6af52a58370795bc9f70dc15f51f8bb84 (patch) | |
tree | 07ed46ceedeb980d62da34ae26e3aac5f8af7dfb | |
parent | f515f192ab4f45bb695146b82432d63d98775787 (diff) |
net: dsa: introduce bridge notifier
A slave device will now notify the switch fabric once its port is
bridged or unbridged, instead of calling directly its switch operations.
This code allows propagating cross-chip bridging events in the fabric.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/dsa.h | 10 | ||||
-rw-r--r-- | net/dsa/slave.c | 40 | ||||
-rw-r--r-- | net/dsa/switch.c | 32 |
3 files changed, 71 insertions, 11 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h index ac4ea7c3a102..e9c940c8936f 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -268,6 +268,16 @@ struct switchdev_obj_port_fdb; | |||
268 | struct switchdev_obj_port_mdb; | 268 | struct switchdev_obj_port_mdb; |
269 | struct switchdev_obj_port_vlan; | 269 | struct switchdev_obj_port_vlan; |
270 | 270 | ||
271 | #define DSA_NOTIFIER_BRIDGE_JOIN 1 | ||
272 | #define DSA_NOTIFIER_BRIDGE_LEAVE 2 | ||
273 | |||
274 | /* DSA_NOTIFIER_BRIDGE_* */ | ||
275 | struct dsa_notifier_bridge_info { | ||
276 | struct net_device *br; | ||
277 | int sw_index; | ||
278 | int port; | ||
279 | }; | ||
280 | |||
271 | struct dsa_switch_ops { | 281 | struct dsa_switch_ops { |
272 | /* | 282 | /* |
273 | * Probing and setup. | 283 | * Probing and setup. |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index d8c3c0f00cf3..061a49c29cef 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -27,6 +27,17 @@ | |||
27 | 27 | ||
28 | static bool dsa_slave_dev_check(struct net_device *dev); | 28 | static bool dsa_slave_dev_check(struct net_device *dev); |
29 | 29 | ||
30 | static int dsa_slave_notify(struct net_device *dev, unsigned long e, void *v) | ||
31 | { | ||
32 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
33 | struct raw_notifier_head *nh = &p->dp->ds->dst->nh; | ||
34 | int err; | ||
35 | |||
36 | err = raw_notifier_call_chain(nh, e, v); | ||
37 | |||
38 | return notifier_to_errno(err); | ||
39 | } | ||
40 | |||
30 | /* slave mii_bus handling ***************************************************/ | 41 | /* slave mii_bus handling ***************************************************/ |
31 | static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) | 42 | static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) |
32 | { | 43 | { |
@@ -562,39 +573,46 @@ static int dsa_slave_bridge_port_join(struct net_device *dev, | |||
562 | struct net_device *br) | 573 | struct net_device *br) |
563 | { | 574 | { |
564 | struct dsa_slave_priv *p = netdev_priv(dev); | 575 | struct dsa_slave_priv *p = netdev_priv(dev); |
565 | struct dsa_switch *ds = p->dp->ds; | 576 | struct dsa_notifier_bridge_info info = { |
566 | int ret = -EOPNOTSUPP; | 577 | .sw_index = p->dp->ds->index, |
578 | .port = p->dp->index, | ||
579 | .br = br, | ||
580 | }; | ||
581 | int err; | ||
567 | 582 | ||
568 | /* Here the port is already bridged. Reflect the current configuration | 583 | /* Here the port is already bridged. Reflect the current configuration |
569 | * so that drivers can program their chips accordingly. | 584 | * so that drivers can program their chips accordingly. |
570 | */ | 585 | */ |
571 | p->dp->bridge_dev = br; | 586 | p->dp->bridge_dev = br; |
572 | 587 | ||
573 | if (ds->ops->port_bridge_join) | 588 | err = dsa_slave_notify(dev, DSA_NOTIFIER_BRIDGE_JOIN, &info); |
574 | ret = ds->ops->port_bridge_join(ds, p->dp->index, br); | ||
575 | 589 | ||
576 | /* The bridging is rolled back on error */ | 590 | /* The bridging is rolled back on error */ |
577 | if (ret && ret != -EOPNOTSUPP) { | 591 | if (err) |
578 | p->dp->bridge_dev = NULL; | 592 | p->dp->bridge_dev = NULL; |
579 | return ret; | ||
580 | } | ||
581 | 593 | ||
582 | return 0; | 594 | return err; |
583 | } | 595 | } |
584 | 596 | ||
585 | static void dsa_slave_bridge_port_leave(struct net_device *dev, | 597 | static void dsa_slave_bridge_port_leave(struct net_device *dev, |
586 | struct net_device *br) | 598 | struct net_device *br) |
587 | { | 599 | { |
588 | struct dsa_slave_priv *p = netdev_priv(dev); | 600 | struct dsa_slave_priv *p = netdev_priv(dev); |
589 | struct dsa_switch *ds = p->dp->ds; | 601 | struct dsa_notifier_bridge_info info = { |
602 | .sw_index = p->dp->ds->index, | ||
603 | .port = p->dp->index, | ||
604 | .br = br, | ||
605 | }; | ||
606 | int err; | ||
590 | 607 | ||
591 | /* Here the port is already unbridged. Reflect the current configuration | 608 | /* Here the port is already unbridged. Reflect the current configuration |
592 | * so that drivers can program their chips accordingly. | 609 | * so that drivers can program their chips accordingly. |
593 | */ | 610 | */ |
594 | p->dp->bridge_dev = NULL; | 611 | p->dp->bridge_dev = NULL; |
595 | 612 | ||
596 | if (ds->ops->port_bridge_leave) | 613 | err = dsa_slave_notify(dev, DSA_NOTIFIER_BRIDGE_LEAVE, &info); |
597 | ds->ops->port_bridge_leave(ds, p->dp->index, br); | 614 | if (err) |
615 | netdev_err(dev, "failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); | ||
598 | 616 | ||
599 | /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, | 617 | /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, |
600 | * so allow it to be in BR_STATE_FORWARDING to be kept functional | 618 | * so allow it to be in BR_STATE_FORWARDING to be kept functional |
diff --git a/net/dsa/switch.c b/net/dsa/switch.c index e22fa7633d03..6456dacf9ae9 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c | |||
@@ -13,6 +13,32 @@ | |||
13 | #include <linux/notifier.h> | 13 | #include <linux/notifier.h> |
14 | #include <net/dsa.h> | 14 | #include <net/dsa.h> |
15 | 15 | ||
16 | static int dsa_switch_bridge_join(struct dsa_switch *ds, | ||
17 | struct dsa_notifier_bridge_info *info) | ||
18 | { | ||
19 | if (ds->index == info->sw_index && ds->ops->port_bridge_join) | ||
20 | return ds->ops->port_bridge_join(ds, info->port, info->br); | ||
21 | |||
22 | if (ds->index != info->sw_index) | ||
23 | dev_dbg(ds->dev, "crosschip DSA port %d.%d bridged to %s\n", | ||
24 | info->sw_index, info->port, netdev_name(info->br)); | ||
25 | |||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | static int dsa_switch_bridge_leave(struct dsa_switch *ds, | ||
30 | struct dsa_notifier_bridge_info *info) | ||
31 | { | ||
32 | if (ds->index == info->sw_index && ds->ops->port_bridge_leave) | ||
33 | ds->ops->port_bridge_leave(ds, info->port, info->br); | ||
34 | |||
35 | if (ds->index != info->sw_index) | ||
36 | dev_dbg(ds->dev, "crosschip DSA port %d.%d unbridged from %s\n", | ||
37 | info->sw_index, info->port, netdev_name(info->br)); | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | |||
16 | static int dsa_switch_event(struct notifier_block *nb, | 42 | static int dsa_switch_event(struct notifier_block *nb, |
17 | unsigned long event, void *info) | 43 | unsigned long event, void *info) |
18 | { | 44 | { |
@@ -20,6 +46,12 @@ static int dsa_switch_event(struct notifier_block *nb, | |||
20 | int err; | 46 | int err; |
21 | 47 | ||
22 | switch (event) { | 48 | switch (event) { |
49 | case DSA_NOTIFIER_BRIDGE_JOIN: | ||
50 | err = dsa_switch_bridge_join(ds, info); | ||
51 | break; | ||
52 | case DSA_NOTIFIER_BRIDGE_LEAVE: | ||
53 | err = dsa_switch_bridge_leave(ds, info); | ||
54 | break; | ||
23 | default: | 55 | default: |
24 | err = -EOPNOTSUPP; | 56 | err = -EOPNOTSUPP; |
25 | break; | 57 | break; |