From d87d6f44d7c1254fd9560a5191659cb00882db56 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 24 Feb 2015 13:15:32 -0800 Subject: net: dsa: Ensure that port array elements are initialized before being used A network device notifier can be called for one or more of the created slave devices before all slave devices have been registered. This can result in a mismatch between ds->phys_port_mask and the registered devices by the time the call is made, and it can result in a slave device being added to a bridge before its entry in ds->ports[] has been initialized. Rework the initialization code to initialize entries in ds->ports[] in dsa_slave_create. With this change, dsa_slave_create no longer needs to return slave_dev but can return an error code instead. Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- net/dsa/slave.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f23deadf42a0..5be4c928c9c9 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -605,9 +605,8 @@ int dsa_slave_resume(struct net_device *slave_dev) return 0; } -struct net_device * -dsa_slave_create(struct dsa_switch *ds, struct device *parent, - int port, char *name) +int dsa_slave_create(struct dsa_switch *ds, struct device *parent, + int port, char *name) { struct net_device *master = ds->dst->master_netdev; struct net_device *slave_dev; @@ -617,7 +616,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, NET_NAME_UNKNOWN, ether_setup); if (slave_dev == NULL) - return slave_dev; + return -ENOMEM; slave_dev->features = master->vlan_features; slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; @@ -667,19 +666,21 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, ret = dsa_slave_phy_setup(p, slave_dev); if (ret) { free_netdev(slave_dev); - return NULL; + return ret; } + ds->ports[port] = slave_dev; ret = register_netdev(slave_dev); if (ret) { netdev_err(master, "error %d registering interface %s\n", ret, slave_dev->name); phy_disconnect(p->phy); + ds->ports[port] = NULL; free_netdev(slave_dev); - return NULL; + return ret; } netif_carrier_off(slave_dev); - return slave_dev; + return 0; } -- cgit v1.2.3 From b73adef67765b72f2a0d01ef15aff9d784dc85da Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 24 Feb 2015 13:15:33 -0800 Subject: 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 Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: David S. Miller --- net/dsa/slave.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) (limited to 'net/dsa/slave.c') 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 @@ #include #include +#include #include #include #include #include +#include +#include #include "dsa_priv.h" /* slave mii_bus handling ***************************************************/ @@ -60,11 +63,18 @@ static int dsa_slave_init(struct net_device *dev) return 0; } +static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p) +{ + return !!p->bridge_dev; +} + static int dsa_slave_open(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); struct net_device *master = p->parent->dst->master_netdev; struct dsa_switch *ds = p->parent; + u8 stp_state = dsa_port_is_bridged(p) ? + BR_STATE_BLOCKING : BR_STATE_FORWARDING; int err; if (!(master->flags & IFF_UP)) @@ -93,6 +103,9 @@ static int dsa_slave_open(struct net_device *dev) goto clear_promisc; } + if (ds->drv->port_stp_update) + ds->drv->port_stp_update(ds, p->port, stp_state); + if (p->phy) phy_start(p->phy); @@ -133,6 +146,9 @@ static int dsa_slave_close(struct net_device *dev) if (ds->drv->port_disable) ds->drv->port_disable(ds, p->port, p->phy); + if (ds->drv->port_stp_update) + ds->drv->port_stp_update(ds, p->port, BR_STATE_DISABLED); + return 0; } @@ -194,6 +210,95 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EOPNOTSUPP; } +/* Return a bitmask of all ports being currently bridged within a given bridge + * device. Note that on leave, the mask will still return the bitmask of ports + * currently bridged, prior to port removal, and this is exactly what we want. + */ +static u32 dsa_slave_br_port_mask(struct dsa_switch *ds, + struct net_device *bridge) +{ + struct dsa_slave_priv *p; + unsigned int port; + u32 mask = 0; + + for (port = 0; port < DSA_MAX_PORTS; port++) { + if (!((1 << port) & ds->phys_port_mask)) + continue; + + if (!ds->ports[port]) + continue; + + p = netdev_priv(ds->ports[port]); + + if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT && + p->bridge_dev == bridge) + mask |= 1 << port; + } + + return mask; +} + +static int dsa_slave_stp_update(struct net_device *dev, u8 state) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->port_stp_update) + ret = ds->drv->port_stp_update(ds, p->port, state); + + return ret; +} + +static int dsa_slave_bridge_port_join(struct net_device *dev, + struct net_device *br) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + p->bridge_dev = br; + + if (ds->drv->port_join_bridge) + ret = ds->drv->port_join_bridge(ds, p->port, + dsa_slave_br_port_mask(ds, br)); + + return ret; +} + +static int dsa_slave_bridge_port_leave(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + + if (ds->drv->port_leave_bridge) + ret = ds->drv->port_leave_bridge(ds, p->port, + dsa_slave_br_port_mask(ds, p->bridge_dev)); + + p->bridge_dev = NULL; + + /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, + * so allow it to be in BR_STATE_FORWARDING to be kept functional + */ + dsa_slave_stp_update(dev, BR_STATE_FORWARDING); + + return ret; +} + +static int dsa_slave_parent_id_get(struct net_device *dev, + struct netdev_phys_item_id *psid) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + psid->id_len = sizeof(ds->index); + memcpy(&psid->id, &ds->index, psid->id_len); + + return 0; +} + static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -470,6 +575,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_do_ioctl = dsa_slave_ioctl, + .ndo_switch_parent_id_get = dsa_slave_parent_id_get, + .ndo_switch_port_stp_update = dsa_slave_stp_update, }; static void dsa_slave_adjust_link(struct net_device *dev) @@ -684,3 +791,45 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, return 0; } + +static bool dsa_slave_dev_check(struct net_device *dev) +{ + return dev->netdev_ops == &dsa_slave_netdev_ops; +} + +static int dsa_slave_master_changed(struct net_device *dev) +{ + struct net_device *master = netdev_master_upper_dev_get(dev); + int err = 0; + + if (master && master->rtnl_link_ops && + !strcmp(master->rtnl_link_ops->kind, "bridge")) + err = dsa_slave_bridge_port_join(dev, master); + else + err = dsa_slave_bridge_port_leave(dev); + + return err; +} + +int dsa_slave_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev; + int err = 0; + + switch (event) { + case NETDEV_CHANGEUPPER: + dev = netdev_notifier_info_to_dev(ptr); + if (!dsa_slave_dev_check(dev)) + goto out; + + err = dsa_slave_master_changed(dev); + if (err) + netdev_warn(dev, "failed to reflect master change\n"); + + break; + } + +out: + return NOTIFY_DONE; +} -- cgit v1.2.3 From d79d21073626cf022943e5c4c10a97cdf7cb8465 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 24 Feb 2015 23:02:02 -0800 Subject: net: dsa: Introduce dsa_is_port_initialized To avoid race conditions when using the ds->ports[] array, we need to check if the accessed port has been initialized. Introduce and use helper function dsa_is_port_initialized for that purpose and use it where needed. Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- net/dsa/slave.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b5a4d8974b76..a47305c72fcc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -222,10 +222,7 @@ static u32 dsa_slave_br_port_mask(struct dsa_switch *ds, u32 mask = 0; for (port = 0; port < DSA_MAX_PORTS; port++) { - if (!((1 << port) & ds->phys_port_mask)) - continue; - - if (!ds->ports[port]) + if (!dsa_is_port_initialized(ds, port)) continue; p = netdev_priv(ds->ports[port]); -- cgit v1.2.3 From c305c1651cb20f00d272db1615d39513365f2097 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Mar 2015 16:57:12 -0700 Subject: net: dsa: move PHY setup on DSA MII bus to its own function In preparation for dealing with indirect reads and writes towards certain PHY devices, move the code which deals with binding the PHY device to the slave MII bus created by DSA to its own function: dsa_slave_phy_connect(). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index a47305c72fcc..19bc2b39c9d1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -617,6 +617,23 @@ static int dsa_slave_fixed_link_update(struct net_device *dev, } /* slave device setup *******************************************************/ +static int dsa_slave_phy_connect(struct dsa_slave_priv *p, + struct net_device *slave_dev) +{ + struct dsa_switch *ds = p->parent; + + p->phy = ds->slave_mii_bus->phy_map[p->port]; + if (!p->phy) + return -ENODEV; + + /* Use already configured phy mode */ + p->phy_interface = p->phy->interface; + phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link, + p->phy_interface); + + return 0; +} + static int dsa_slave_phy_setup(struct dsa_slave_priv *p, struct net_device *slave_dev) { @@ -662,14 +679,9 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, * MDIO bus instead */ if (!p->phy) { - p->phy = ds->slave_mii_bus->phy_map[p->port]; - if (!p->phy) - return -ENODEV; - - /* Use already configured phy mode */ - p->phy_interface = p->phy->interface; - phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link, - p->phy_interface); + ret = dsa_slave_phy_connect(p, slave_dev); + if (ret) + return ret; } else { netdev_info(slave_dev, "attached PHY at address %d [%s]\n", p->phy->addr, p->phy->drv->name); -- cgit v1.2.3 From cd28a1a9baee7674779e46072e5dbbb6215c3c8c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Mar 2015 16:57:13 -0700 Subject: net: dsa: fully divert PHY reads/writes if requested In case a PHY is found via Device Tree, and is also flagged by the switch driver as needing indirect reads/writes using the switch driver implemented MDIO bus, make sure that we bind this PHY to the slave MII bus in order for this to happen. Without this, we would succeed in having the PHY driver probe()'s function to use slave MII bus read/write functions, because this is done during dsa_slave_mii_init(), but past that point, the PHY driver would not go through these diverted reads and writes. Fixes: 0d8bcdd383b88 ("net: dsa: allow for more complex PHY setups") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 19bc2b39c9d1..188b69773e70 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -618,11 +618,12 @@ static int dsa_slave_fixed_link_update(struct net_device *dev, /* slave device setup *******************************************************/ static int dsa_slave_phy_connect(struct dsa_slave_priv *p, - struct net_device *slave_dev) + struct net_device *slave_dev, + int addr) { struct dsa_switch *ds = p->parent; - p->phy = ds->slave_mii_bus->phy_map[p->port]; + p->phy = ds->slave_mii_bus->phy_map[addr]; if (!p->phy) return -ENODEV; @@ -667,10 +668,24 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, if (ds->drv->get_phy_flags) phy_flags = ds->drv->get_phy_flags(ds, p->port); - if (phy_dn) - p->phy = of_phy_connect(slave_dev, phy_dn, - dsa_slave_adjust_link, phy_flags, - p->phy_interface); + if (phy_dn) { + ret = of_mdio_parse_addr(&slave_dev->dev, phy_dn); + /* If this PHY address is part of phys_mii_mask, which means + * that we need to divert reads and writes to/from it, then we + * want to bind this device using the slave MII bus created by + * DSA to make that happen. + */ + if (ret >= 0 && (ds->phys_mii_mask & (1 << ret))) { + ret = dsa_slave_phy_connect(p, slave_dev, ret); + if (ret) + return ret; + } else { + p->phy = of_phy_connect(slave_dev, phy_dn, + dsa_slave_adjust_link, + phy_flags, + p->phy_interface); + } + } if (p->phy && phy_is_fixed) fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update); @@ -679,7 +694,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, * MDIO bus instead */ if (!p->phy) { - ret = dsa_slave_phy_connect(p, slave_dev); + ret = dsa_slave_phy_connect(p, slave_dev, p->port); if (ret) return ret; } else { -- cgit v1.2.3 From 96026d057a1fb7da1e314a24e3a1c528321ed45e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 14 Mar 2015 13:21:59 -0700 Subject: net: dsa: do not use slave MII bus for fixed PHYs Commit cd28a1a9baee7 ("net: dsa: fully divert PHY reads/writes if requested") introduced a check for particular PHYs that need to be accessed using the slave MII bus created by DSA, but this check was too inclusive. This would prevent fixed PHYs from being successfully registered because those should not go through the slave MII bus created by DSA. Make sure we check that the PHY is not a fixed PHY to prevent that from happening. Fixes: cd28a1a9baee7 ("net: dsa: fully divert PHY reads/writes if requested") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 188b69773e70..6511552039d6 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -675,7 +675,8 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, * want to bind this device using the slave MII bus created by * DSA to make that happen. */ - if (ret >= 0 && (ds->phys_mii_mask & (1 << ret))) { + if (!phy_is_fixed && ret >= 0 && + (ds->phys_mii_mask & (1 << ret))) { ret = dsa_slave_phy_connect(p, slave_dev, ret); if (ret) return ret; -- cgit v1.2.3 From 98237d433b98d27fdffb09e4a1a510e9f00c6f31 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Sun, 15 Mar 2015 21:07:15 -0700 Subject: switchdev: use new swdev ops Move swdev wrappers over to new swdev ops (from previous ndo ops). No functional changes to the implementation. Signed-off-by: Scott Feldman rocker: move to new swdev ops Signed-off-by: Scott Feldman dsa: move to new swdev ops Signed-off-by: Scott Feldman Signed-off-by: David S. Miller --- net/dsa/slave.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 6511552039d6..f0af7aa331c1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "dsa_priv.h" @@ -572,8 +573,11 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_do_ioctl = dsa_slave_ioctl, - .ndo_switch_parent_id_get = dsa_slave_parent_id_get, - .ndo_switch_port_stp_update = dsa_slave_stp_update, +}; + +static const struct swdev_ops dsa_slave_swdev_ops = { + .swdev_parent_id_get = dsa_slave_parent_id_get, + .swdev_port_stp_update = dsa_slave_stp_update, }; static void dsa_slave_adjust_link(struct net_device *dev) @@ -755,6 +759,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, eth_hw_addr_inherit(slave_dev, master); slave_dev->tx_queue_len = 0; slave_dev->netdev_ops = &dsa_slave_netdev_ops; + slave_dev->swdev_ops = &dsa_slave_swdev_ops; SET_NETDEV_DEV(slave_dev, parent); slave_dev->dev.of_node = ds->pd->port_dn[port]; -- cgit v1.2.3 From b06b107a4c190299e9e3f8dbcccfc7fe9e10c8cb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 25 Mar 2015 08:08:37 -0700 Subject: net: dsa: Handle non-bridge master change Master change notifications may occur other than when joining or leaving a bridge, for example when being added to or removed from a bond or Open vSwitch. In that case, do nothing instead of asking the switch driver to remove a port from a bridge that it didn't join. Signed-off-by: Guenter Roeck Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f0af7aa331c1..39555f3f263b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -830,12 +830,13 @@ static bool dsa_slave_dev_check(struct net_device *dev) static int dsa_slave_master_changed(struct net_device *dev) { struct net_device *master = netdev_master_upper_dev_get(dev); + struct dsa_slave_priv *p = netdev_priv(dev); int err = 0; if (master && master->rtnl_link_ops && !strcmp(master->rtnl_link_ops->kind, "bridge")) err = dsa_slave_bridge_port_join(dev, master); - else + else if (dsa_port_is_bridged(p)) err = dsa_slave_bridge_port_leave(dev); return err; -- cgit v1.2.3 From 339d82626d225e9b876665e4e89b7eb123e96b3d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 Mar 2015 18:36:37 -0700 Subject: net: dsa: Add basic framework to support ndo_fdb functions Provide callbacks for ndo_fdb_add, ndo_fdb_del, and ndo_fdb_dump. Reviewed-by: Andrew Lunn Tested-by: Andrew Lunn Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- net/dsa/slave.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 39555f3f263b..3597724ec3d8 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -201,6 +201,105 @@ out: return 0; } +static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid, u16 nlm_flags) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->fdb_add) + ret = ds->drv->fdb_add(ds, p->port, addr, vid); + + return ret; +} + +static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + int ret = -EOPNOTSUPP; + + if (ds->drv->fdb_del) + ret = ds->drv->fdb_del(ds, p->port, addr, vid); + + return ret; +} + +static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb, + const unsigned char *addr, u16 vid, + bool is_static, + u32 portid, u32 seq, int type, + unsigned int flags) +{ + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); + if (!nlh) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = NTF_EXT_LEARNED; + ndm->ndm_type = 0; + ndm->ndm_ifindex = dev->ifindex; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; + + if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr)) + goto nla_put_failure; + + if (vid && nla_put_u16(skb, NDA_VLAN, vid)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +/* Dump information about entries, in response to GETNEIGH */ +static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct net_device *dev, + struct net_device *filter_dev, int idx) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + unsigned char addr[ETH_ALEN] = { 0 }; + int ret; + + if (!ds->drv->fdb_getnext) + return -EOPNOTSUPP; + + for (; ; idx++) { + bool is_static; + + ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static); + if (ret < 0) + break; + + if (idx < cb->args[0]) + continue; + + ret = dsa_slave_fill_info(dev, skb, addr, 0, + is_static, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, NLM_F_MULTI); + if (ret < 0) + break; + } + + return idx; +} + static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_change_rx_flags = dsa_slave_change_rx_flags, .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, + .ndo_fdb_add = dsa_slave_fdb_add, + .ndo_fdb_del = dsa_slave_fdb_del, + .ndo_fdb_dump = dsa_slave_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, }; -- cgit v1.2.3 From abd2be00d474956c542a1c2ec848af7196b7fd51 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 2 Apr 2015 17:07:08 +0200 Subject: dsa: implement ndo_get_iflink Don't use dev->iflink anymore. CC: Florian Fainelli Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/dsa/slave.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net/dsa/slave.c') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3597724ec3d8..827cda560a55 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -55,13 +55,11 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) /* slave device handling ****************************************************/ -static int dsa_slave_init(struct net_device *dev) +static int dsa_slave_get_iflink(const struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - dev->iflink = p->parent->dst->master_netdev->ifindex; - - return 0; + return p->parent->dst->master_netdev->ifindex; } static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p) @@ -664,7 +662,6 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { }; static const struct net_device_ops dsa_slave_netdev_ops = { - .ndo_init = dsa_slave_init, .ndo_open = dsa_slave_open, .ndo_stop = dsa_slave_close, .ndo_start_xmit = dsa_slave_xmit, @@ -675,6 +672,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_fdb_del = dsa_slave_fdb_del, .ndo_fdb_dump = dsa_slave_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, + .ndo_get_iflink = dsa_slave_get_iflink, }; static const struct swdev_ops dsa_slave_swdev_ops = { -- cgit v1.2.3