12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
291da11f8SLennert Buytenhek /*
391da11f8SLennert Buytenhek * net/dsa/slave.c - Slave device handling
4e84665c9SLennert Buytenhek * Copyright (c) 2008-2009 Marvell Semiconductor
591da11f8SLennert Buytenhek */
691da11f8SLennert Buytenhek
791da11f8SLennert Buytenhek #include <linux/list.h>
8df02c6ffSLennert Buytenhek #include <linux/etherdevice.h>
9b73adef6SFlorian Fainelli #include <linux/netdevice.h>
1091da11f8SLennert Buytenhek #include <linux/phy.h>
11a2820543SFlorian Fainelli #include <linux/phy_fixed.h>
12aab9c406SFlorian Fainelli #include <linux/phylink.h>
130d8bcdd3SFlorian Fainelli #include <linux/of_net.h>
140d8bcdd3SFlorian Fainelli #include <linux/of_mdio.h>
157f854420SAndrew Lunn #include <linux/mdio.h>
16b73adef6SFlorian Fainelli #include <net/rtnetlink.h>
17f50f2127SFlorian Fainelli #include <net/pkt_cls.h>
18a71acad9SOleksij Rempel #include <net/selftests.h>
19f50f2127SFlorian Fainelli #include <net/tc_act/tc_mirred.h>
20b73adef6SFlorian Fainelli #include <linux/if_bridge.h>
2118596f50SGeorge McCollister #include <linux/if_hsr.h>
22d538eca8SVladimir Oltean #include <net/dcbnl.h>
2304ff53f9SFlorian Fainelli #include <linux/netpoll.h>
245c9f7b04Sjustinstitt@google.com #include <linux/string.h>
25ea5dd34bSVivien Didelot
2647d2ce03SVladimir Oltean #include "dsa.h"
27022bba63SVladimir Oltean #include "port.h"
2894ef6fadSVladimir Oltean #include "master.h"
295917bfe6SVladimir Oltean #include "netlink.h"
3009f92341SVladimir Oltean #include "slave.h"
31d06f925fSVladimir Oltean #include "switch.h"
32bd954b82SVladimir Oltean #include "tag.h"
3391da11f8SLennert Buytenhek
348e396fecSVladimir Oltean struct dsa_switchdev_event_work {
358e396fecSVladimir Oltean struct net_device *dev;
368e396fecSVladimir Oltean struct net_device *orig_dev;
378e396fecSVladimir Oltean struct work_struct work;
388e396fecSVladimir Oltean unsigned long event;
398e396fecSVladimir Oltean /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and
408e396fecSVladimir Oltean * SWITCHDEV_FDB_DEL_TO_DEVICE
418e396fecSVladimir Oltean */
428e396fecSVladimir Oltean unsigned char addr[ETH_ALEN];
438e396fecSVladimir Oltean u16 vid;
448e396fecSVladimir Oltean bool host_addr;
458e396fecSVladimir Oltean };
468e396fecSVladimir Oltean
478e396fecSVladimir Oltean enum dsa_standalone_event {
488e396fecSVladimir Oltean DSA_UC_ADD,
498e396fecSVladimir Oltean DSA_UC_DEL,
508e396fecSVladimir Oltean DSA_MC_ADD,
518e396fecSVladimir Oltean DSA_MC_DEL,
528e396fecSVladimir Oltean };
538e396fecSVladimir Oltean
548e396fecSVladimir Oltean struct dsa_standalone_event_work {
558e396fecSVladimir Oltean struct work_struct work;
568e396fecSVladimir Oltean struct net_device *dev;
578e396fecSVladimir Oltean enum dsa_standalone_event event;
588e396fecSVladimir Oltean unsigned char addr[ETH_ALEN];
598e396fecSVladimir Oltean u16 vid;
608e396fecSVladimir Oltean };
618e396fecSVladimir Oltean
6264fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx {
6364fdc5f3SVladimir Oltean struct net_device *dev;
6464fdc5f3SVladimir Oltean const unsigned char *addr;
6564fdc5f3SVladimir Oltean enum dsa_standalone_event event;
6664fdc5f3SVladimir Oltean };
6764fdc5f3SVladimir Oltean
dsa_switch_supports_uc_filtering(struct dsa_switch * ds)688e396fecSVladimir Oltean static bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds)
698e396fecSVladimir Oltean {
708e396fecSVladimir Oltean return ds->ops->port_fdb_add && ds->ops->port_fdb_del &&
718e396fecSVladimir Oltean ds->fdb_isolation && !ds->vlan_filtering_is_global &&
728e396fecSVladimir Oltean !ds->needs_standalone_vlan_filtering;
738e396fecSVladimir Oltean }
748e396fecSVladimir Oltean
dsa_switch_supports_mc_filtering(struct dsa_switch * ds)758e396fecSVladimir Oltean static bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds)
768e396fecSVladimir Oltean {
778e396fecSVladimir Oltean return ds->ops->port_mdb_add && ds->ops->port_mdb_del &&
788e396fecSVladimir Oltean ds->fdb_isolation && !ds->vlan_filtering_is_global &&
798e396fecSVladimir Oltean !ds->needs_standalone_vlan_filtering;
808e396fecSVladimir Oltean }
818e396fecSVladimir Oltean
dsa_slave_standalone_event_work(struct work_struct * work)825e8a1e03SVladimir Oltean static void dsa_slave_standalone_event_work(struct work_struct *work)
835e8a1e03SVladimir Oltean {
845e8a1e03SVladimir Oltean struct dsa_standalone_event_work *standalone_work =
855e8a1e03SVladimir Oltean container_of(work, struct dsa_standalone_event_work, work);
865e8a1e03SVladimir Oltean const unsigned char *addr = standalone_work->addr;
875e8a1e03SVladimir Oltean struct net_device *dev = standalone_work->dev;
885e8a1e03SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
895e8a1e03SVladimir Oltean struct switchdev_obj_port_mdb mdb;
905e8a1e03SVladimir Oltean struct dsa_switch *ds = dp->ds;
915e8a1e03SVladimir Oltean u16 vid = standalone_work->vid;
925e8a1e03SVladimir Oltean int err;
935e8a1e03SVladimir Oltean
945e8a1e03SVladimir Oltean switch (standalone_work->event) {
955e8a1e03SVladimir Oltean case DSA_UC_ADD:
965e8a1e03SVladimir Oltean err = dsa_port_standalone_host_fdb_add(dp, addr, vid);
975e8a1e03SVladimir Oltean if (err) {
985e8a1e03SVladimir Oltean dev_err(ds->dev,
995e8a1e03SVladimir Oltean "port %d failed to add %pM vid %d to fdb: %d\n",
1005e8a1e03SVladimir Oltean dp->index, addr, vid, err);
1015e8a1e03SVladimir Oltean break;
1025e8a1e03SVladimir Oltean }
1035e8a1e03SVladimir Oltean break;
1045e8a1e03SVladimir Oltean
1055e8a1e03SVladimir Oltean case DSA_UC_DEL:
1065e8a1e03SVladimir Oltean err = dsa_port_standalone_host_fdb_del(dp, addr, vid);
1075e8a1e03SVladimir Oltean if (err) {
1085e8a1e03SVladimir Oltean dev_err(ds->dev,
1095e8a1e03SVladimir Oltean "port %d failed to delete %pM vid %d from fdb: %d\n",
1105e8a1e03SVladimir Oltean dp->index, addr, vid, err);
1115e8a1e03SVladimir Oltean }
1125e8a1e03SVladimir Oltean
1135e8a1e03SVladimir Oltean break;
1145e8a1e03SVladimir Oltean case DSA_MC_ADD:
1155e8a1e03SVladimir Oltean ether_addr_copy(mdb.addr, addr);
1165e8a1e03SVladimir Oltean mdb.vid = vid;
1175e8a1e03SVladimir Oltean
1185e8a1e03SVladimir Oltean err = dsa_port_standalone_host_mdb_add(dp, &mdb);
1195e8a1e03SVladimir Oltean if (err) {
1205e8a1e03SVladimir Oltean dev_err(ds->dev,
1215e8a1e03SVladimir Oltean "port %d failed to add %pM vid %d to mdb: %d\n",
1225e8a1e03SVladimir Oltean dp->index, addr, vid, err);
1235e8a1e03SVladimir Oltean break;
1245e8a1e03SVladimir Oltean }
1255e8a1e03SVladimir Oltean break;
1265e8a1e03SVladimir Oltean case DSA_MC_DEL:
1275e8a1e03SVladimir Oltean ether_addr_copy(mdb.addr, addr);
1285e8a1e03SVladimir Oltean mdb.vid = vid;
1295e8a1e03SVladimir Oltean
1305e8a1e03SVladimir Oltean err = dsa_port_standalone_host_mdb_del(dp, &mdb);
1315e8a1e03SVladimir Oltean if (err) {
1325e8a1e03SVladimir Oltean dev_err(ds->dev,
1335e8a1e03SVladimir Oltean "port %d failed to delete %pM vid %d from mdb: %d\n",
1345e8a1e03SVladimir Oltean dp->index, addr, vid, err);
1355e8a1e03SVladimir Oltean }
1365e8a1e03SVladimir Oltean
1375e8a1e03SVladimir Oltean break;
1385e8a1e03SVladimir Oltean }
1395e8a1e03SVladimir Oltean
1405e8a1e03SVladimir Oltean kfree(standalone_work);
1415e8a1e03SVladimir Oltean }
1425e8a1e03SVladimir Oltean
dsa_slave_schedule_standalone_work(struct net_device * dev,enum dsa_standalone_event event,const unsigned char * addr,u16 vid)1435e8a1e03SVladimir Oltean static int dsa_slave_schedule_standalone_work(struct net_device *dev,
1445e8a1e03SVladimir Oltean enum dsa_standalone_event event,
1455e8a1e03SVladimir Oltean const unsigned char *addr,
1465e8a1e03SVladimir Oltean u16 vid)
1475e8a1e03SVladimir Oltean {
1485e8a1e03SVladimir Oltean struct dsa_standalone_event_work *standalone_work;
1495e8a1e03SVladimir Oltean
1505e8a1e03SVladimir Oltean standalone_work = kzalloc(sizeof(*standalone_work), GFP_ATOMIC);
1515e8a1e03SVladimir Oltean if (!standalone_work)
1525e8a1e03SVladimir Oltean return -ENOMEM;
1535e8a1e03SVladimir Oltean
1545e8a1e03SVladimir Oltean INIT_WORK(&standalone_work->work, dsa_slave_standalone_event_work);
1555e8a1e03SVladimir Oltean standalone_work->event = event;
1565e8a1e03SVladimir Oltean standalone_work->dev = dev;
1575e8a1e03SVladimir Oltean
1585e8a1e03SVladimir Oltean ether_addr_copy(standalone_work->addr, addr);
1595e8a1e03SVladimir Oltean standalone_work->vid = vid;
1605e8a1e03SVladimir Oltean
1615e8a1e03SVladimir Oltean dsa_schedule_work(&standalone_work->work);
1625e8a1e03SVladimir Oltean
1635e8a1e03SVladimir Oltean return 0;
1645e8a1e03SVladimir Oltean }
1655e8a1e03SVladimir Oltean
dsa_slave_host_vlan_rx_filtering(void * arg,int vid)166d06f925fSVladimir Oltean static int dsa_slave_host_vlan_rx_filtering(void *arg, int vid)
16764fdc5f3SVladimir Oltean {
16864fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx *ctx = arg;
16964fdc5f3SVladimir Oltean
17064fdc5f3SVladimir Oltean return dsa_slave_schedule_standalone_work(ctx->dev, ctx->event,
17164fdc5f3SVladimir Oltean ctx->addr, vid);
17264fdc5f3SVladimir Oltean }
17364fdc5f3SVladimir Oltean
dsa_slave_vlan_for_each(struct net_device * dev,int (* cb)(void * arg,int vid),void * arg)174d06f925fSVladimir Oltean static int dsa_slave_vlan_for_each(struct net_device *dev,
175d06f925fSVladimir Oltean int (*cb)(void *arg, int vid), void *arg)
176d06f925fSVladimir Oltean {
177d06f925fSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
178d06f925fSVladimir Oltean struct dsa_vlan *v;
179d06f925fSVladimir Oltean int err;
180d06f925fSVladimir Oltean
181d06f925fSVladimir Oltean lockdep_assert_held(&dev->addr_list_lock);
182d06f925fSVladimir Oltean
183d06f925fSVladimir Oltean err = cb(arg, 0);
184d06f925fSVladimir Oltean if (err)
185d06f925fSVladimir Oltean return err;
186d06f925fSVladimir Oltean
187d06f925fSVladimir Oltean list_for_each_entry(v, &dp->user_vlans, list) {
188d06f925fSVladimir Oltean err = cb(arg, v->vid);
189d06f925fSVladimir Oltean if (err)
190d06f925fSVladimir Oltean return err;
191d06f925fSVladimir Oltean }
192d06f925fSVladimir Oltean
193d06f925fSVladimir Oltean return 0;
194d06f925fSVladimir Oltean }
195d06f925fSVladimir Oltean
dsa_slave_sync_uc(struct net_device * dev,const unsigned char * addr)1965e8a1e03SVladimir Oltean static int dsa_slave_sync_uc(struct net_device *dev,
1975e8a1e03SVladimir Oltean const unsigned char *addr)
1985e8a1e03SVladimir Oltean {
1995077e2c8SVladimir Oltean struct net_device *master = dsa_slave_to_master(dev);
2005077e2c8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
20164fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx ctx = {
20264fdc5f3SVladimir Oltean .dev = dev,
20364fdc5f3SVladimir Oltean .addr = addr,
20464fdc5f3SVladimir Oltean .event = DSA_UC_ADD,
20564fdc5f3SVladimir Oltean };
2065077e2c8SVladimir Oltean
2075077e2c8SVladimir Oltean dev_uc_add(master, addr);
2085077e2c8SVladimir Oltean
2095077e2c8SVladimir Oltean if (!dsa_switch_supports_uc_filtering(dp->ds))
2105077e2c8SVladimir Oltean return 0;
2115077e2c8SVladimir Oltean
212d06f925fSVladimir Oltean return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering,
213d06f925fSVladimir Oltean &ctx);
2145e8a1e03SVladimir Oltean }
2155e8a1e03SVladimir Oltean
dsa_slave_unsync_uc(struct net_device * dev,const unsigned char * addr)2165e8a1e03SVladimir Oltean static int dsa_slave_unsync_uc(struct net_device *dev,
2175e8a1e03SVladimir Oltean const unsigned char *addr)
2185e8a1e03SVladimir Oltean {
2195077e2c8SVladimir Oltean struct net_device *master = dsa_slave_to_master(dev);
2205077e2c8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
22164fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx ctx = {
22264fdc5f3SVladimir Oltean .dev = dev,
22364fdc5f3SVladimir Oltean .addr = addr,
22464fdc5f3SVladimir Oltean .event = DSA_UC_DEL,
22564fdc5f3SVladimir Oltean };
2265077e2c8SVladimir Oltean
2275077e2c8SVladimir Oltean dev_uc_del(master, addr);
2285077e2c8SVladimir Oltean
2295077e2c8SVladimir Oltean if (!dsa_switch_supports_uc_filtering(dp->ds))
2305077e2c8SVladimir Oltean return 0;
2315077e2c8SVladimir Oltean
232d06f925fSVladimir Oltean return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering,
233d06f925fSVladimir Oltean &ctx);
2345e8a1e03SVladimir Oltean }
2355e8a1e03SVladimir Oltean
dsa_slave_sync_mc(struct net_device * dev,const unsigned char * addr)2365e8a1e03SVladimir Oltean static int dsa_slave_sync_mc(struct net_device *dev,
2375e8a1e03SVladimir Oltean const unsigned char *addr)
2385e8a1e03SVladimir Oltean {
2395077e2c8SVladimir Oltean struct net_device *master = dsa_slave_to_master(dev);
2405077e2c8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
24164fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx ctx = {
24264fdc5f3SVladimir Oltean .dev = dev,
24364fdc5f3SVladimir Oltean .addr = addr,
24464fdc5f3SVladimir Oltean .event = DSA_MC_ADD,
24564fdc5f3SVladimir Oltean };
2465077e2c8SVladimir Oltean
2475077e2c8SVladimir Oltean dev_mc_add(master, addr);
2485077e2c8SVladimir Oltean
2495077e2c8SVladimir Oltean if (!dsa_switch_supports_mc_filtering(dp->ds))
2505077e2c8SVladimir Oltean return 0;
2515077e2c8SVladimir Oltean
252d06f925fSVladimir Oltean return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering,
253d06f925fSVladimir Oltean &ctx);
2545e8a1e03SVladimir Oltean }
2555e8a1e03SVladimir Oltean
dsa_slave_unsync_mc(struct net_device * dev,const unsigned char * addr)2565e8a1e03SVladimir Oltean static int dsa_slave_unsync_mc(struct net_device *dev,
2575e8a1e03SVladimir Oltean const unsigned char *addr)
2585e8a1e03SVladimir Oltean {
2595077e2c8SVladimir Oltean struct net_device *master = dsa_slave_to_master(dev);
2605077e2c8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
26164fdc5f3SVladimir Oltean struct dsa_host_vlan_rx_filtering_ctx ctx = {
26264fdc5f3SVladimir Oltean .dev = dev,
26364fdc5f3SVladimir Oltean .addr = addr,
26464fdc5f3SVladimir Oltean .event = DSA_MC_DEL,
26564fdc5f3SVladimir Oltean };
2665077e2c8SVladimir Oltean
2675077e2c8SVladimir Oltean dev_mc_del(master, addr);
2685077e2c8SVladimir Oltean
2695077e2c8SVladimir Oltean if (!dsa_switch_supports_mc_filtering(dp->ds))
2705077e2c8SVladimir Oltean return 0;
2715077e2c8SVladimir Oltean
272d06f925fSVladimir Oltean return dsa_slave_vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering,
273d06f925fSVladimir Oltean &ctx);
2745e8a1e03SVladimir Oltean }
2755e8a1e03SVladimir Oltean
dsa_slave_sync_ha(struct net_device * dev)27695f510d0SVladimir Oltean void dsa_slave_sync_ha(struct net_device *dev)
27795f510d0SVladimir Oltean {
27895f510d0SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
27995f510d0SVladimir Oltean struct dsa_switch *ds = dp->ds;
28095f510d0SVladimir Oltean struct netdev_hw_addr *ha;
28195f510d0SVladimir Oltean
28295f510d0SVladimir Oltean netif_addr_lock_bh(dev);
28395f510d0SVladimir Oltean
28495f510d0SVladimir Oltean netdev_for_each_synced_mc_addr(ha, dev)
28595f510d0SVladimir Oltean dsa_slave_sync_mc(dev, ha->addr);
28695f510d0SVladimir Oltean
28795f510d0SVladimir Oltean netdev_for_each_synced_uc_addr(ha, dev)
28895f510d0SVladimir Oltean dsa_slave_sync_uc(dev, ha->addr);
28995f510d0SVladimir Oltean
29095f510d0SVladimir Oltean netif_addr_unlock_bh(dev);
29195f510d0SVladimir Oltean
29295f510d0SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds) ||
29395f510d0SVladimir Oltean dsa_switch_supports_mc_filtering(ds))
29495f510d0SVladimir Oltean dsa_flush_workqueue();
29595f510d0SVladimir Oltean }
29695f510d0SVladimir Oltean
dsa_slave_unsync_ha(struct net_device * dev)29795f510d0SVladimir Oltean void dsa_slave_unsync_ha(struct net_device *dev)
29895f510d0SVladimir Oltean {
29995f510d0SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
30095f510d0SVladimir Oltean struct dsa_switch *ds = dp->ds;
30195f510d0SVladimir Oltean struct netdev_hw_addr *ha;
30295f510d0SVladimir Oltean
30395f510d0SVladimir Oltean netif_addr_lock_bh(dev);
30495f510d0SVladimir Oltean
30595f510d0SVladimir Oltean netdev_for_each_synced_uc_addr(ha, dev)
30695f510d0SVladimir Oltean dsa_slave_unsync_uc(dev, ha->addr);
30795f510d0SVladimir Oltean
30895f510d0SVladimir Oltean netdev_for_each_synced_mc_addr(ha, dev)
30995f510d0SVladimir Oltean dsa_slave_unsync_mc(dev, ha->addr);
31095f510d0SVladimir Oltean
31195f510d0SVladimir Oltean netif_addr_unlock_bh(dev);
31295f510d0SVladimir Oltean
31395f510d0SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds) ||
31495f510d0SVladimir Oltean dsa_switch_supports_mc_filtering(ds))
31595f510d0SVladimir Oltean dsa_flush_workqueue();
31695f510d0SVladimir Oltean }
31795f510d0SVladimir Oltean
31891da11f8SLennert Buytenhek /* slave mii_bus handling ***************************************************/
dsa_slave_phy_read(struct mii_bus * bus,int addr,int reg)31991da11f8SLennert Buytenhek static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
32091da11f8SLennert Buytenhek {
32191da11f8SLennert Buytenhek struct dsa_switch *ds = bus->priv;
32291da11f8SLennert Buytenhek
3230d8bcdd3SFlorian Fainelli if (ds->phys_mii_mask & (1 << addr))
3249d490b4eSVivien Didelot return ds->ops->phy_read(ds, addr, reg);
32591da11f8SLennert Buytenhek
32691da11f8SLennert Buytenhek return 0xffff;
32791da11f8SLennert Buytenhek }
32891da11f8SLennert Buytenhek
dsa_slave_phy_write(struct mii_bus * bus,int addr,int reg,u16 val)32991da11f8SLennert Buytenhek static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
33091da11f8SLennert Buytenhek {
33191da11f8SLennert Buytenhek struct dsa_switch *ds = bus->priv;
33291da11f8SLennert Buytenhek
3330d8bcdd3SFlorian Fainelli if (ds->phys_mii_mask & (1 << addr))
3349d490b4eSVivien Didelot return ds->ops->phy_write(ds, addr, reg, val);
33591da11f8SLennert Buytenhek
33691da11f8SLennert Buytenhek return 0;
33791da11f8SLennert Buytenhek }
33891da11f8SLennert Buytenhek
dsa_slave_mii_bus_init(struct dsa_switch * ds)33991da11f8SLennert Buytenhek void dsa_slave_mii_bus_init(struct dsa_switch *ds)
34091da11f8SLennert Buytenhek {
34191da11f8SLennert Buytenhek ds->slave_mii_bus->priv = (void *)ds;
34291da11f8SLennert Buytenhek ds->slave_mii_bus->name = "dsa slave smi";
34391da11f8SLennert Buytenhek ds->slave_mii_bus->read = dsa_slave_phy_read;
34491da11f8SLennert Buytenhek ds->slave_mii_bus->write = dsa_slave_phy_write;
3450b7b498dSFlorian Fainelli snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d",
34649463b7fSVivien Didelot ds->dst->index, ds->index);
347c33063d6SAndrew Lunn ds->slave_mii_bus->parent = ds->dev;
34824df8986SVivien Didelot ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
34991da11f8SLennert Buytenhek }
35091da11f8SLennert Buytenhek
35191da11f8SLennert Buytenhek
35291da11f8SLennert Buytenhek /* slave device handling ****************************************************/
dsa_slave_get_iflink(const struct net_device * dev)353abd2be00SNicolas Dichtel static int dsa_slave_get_iflink(const struct net_device *dev)
354c0840801SLennert Buytenhek {
355d0006b00SVivien Didelot return dsa_slave_to_master(dev)->ifindex;
356c0840801SLennert Buytenhek }
357c0840801SLennert Buytenhek
dsa_slave_open(struct net_device * dev)35891da11f8SLennert Buytenhek static int dsa_slave_open(struct net_device *dev)
35991da11f8SLennert Buytenhek {
360d0006b00SVivien Didelot struct net_device *master = dsa_slave_to_master(dev);
361d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
362499aa9e1SVladimir Oltean struct dsa_switch *ds = dp->ds;
363df02c6ffSLennert Buytenhek int err;
364df02c6ffSLennert Buytenhek
3659d5ef190SVladimir Oltean err = dev_open(master, NULL);
3669d5ef190SVladimir Oltean if (err < 0) {
3679d5ef190SVladimir Oltean netdev_err(dev, "failed to open master %s\n", master->name);
3689d5ef190SVladimir Oltean goto out;
3699d5ef190SVladimir Oltean }
370df02c6ffSLennert Buytenhek
371499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds)) {
372499aa9e1SVladimir Oltean err = dsa_port_standalone_host_fdb_add(dp, dev->dev_addr, 0);
373499aa9e1SVladimir Oltean if (err)
374499aa9e1SVladimir Oltean goto out;
375499aa9e1SVladimir Oltean }
376499aa9e1SVladimir Oltean
3778feedbb4SJoe Perches if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
378a748ee24SJiri Pirko err = dev_uc_add(master, dev->dev_addr);
379df02c6ffSLennert Buytenhek if (err < 0)
380499aa9e1SVladimir Oltean goto del_host_addr;
381df02c6ffSLennert Buytenhek }
382df02c6ffSLennert Buytenhek
3838640f8dcSRussell King err = dsa_port_enable_rt(dp, dev->phydev);
384b2f2af21SFlorian Fainelli if (err)
38535aae5abSVladimir Oltean goto del_unicast;
386b73adef6SFlorian Fainelli
38791da11f8SLennert Buytenhek return 0;
388df02c6ffSLennert Buytenhek
389df02c6ffSLennert Buytenhek del_unicast:
3908feedbb4SJoe Perches if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
391a748ee24SJiri Pirko dev_uc_del(master, dev->dev_addr);
392499aa9e1SVladimir Oltean del_host_addr:
393499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds))
394499aa9e1SVladimir Oltean dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0);
395df02c6ffSLennert Buytenhek out:
396df02c6ffSLennert Buytenhek return err;
39791da11f8SLennert Buytenhek }
39891da11f8SLennert Buytenhek
dsa_slave_close(struct net_device * dev)39991da11f8SLennert Buytenhek static int dsa_slave_close(struct net_device *dev)
40091da11f8SLennert Buytenhek {
401d0006b00SVivien Didelot struct net_device *master = dsa_slave_to_master(dev);
402d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
403499aa9e1SVladimir Oltean struct dsa_switch *ds = dp->ds;
404df02c6ffSLennert Buytenhek
4058640f8dcSRussell King dsa_port_disable_rt(dp);
4066457edfeSVivien Didelot
4078feedbb4SJoe Perches if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
408a748ee24SJiri Pirko dev_uc_del(master, dev->dev_addr);
409df02c6ffSLennert Buytenhek
410499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds))
411499aa9e1SVladimir Oltean dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0);
412499aa9e1SVladimir Oltean
41391da11f8SLennert Buytenhek return 0;
41491da11f8SLennert Buytenhek }
41591da11f8SLennert Buytenhek
dsa_slave_manage_host_flood(struct net_device * dev)41672c3b0c7SVladimir Oltean static void dsa_slave_manage_host_flood(struct net_device *dev)
4177569459aSVladimir Oltean {
41872c3b0c7SVladimir Oltean bool mc = dev->flags & (IFF_PROMISC | IFF_ALLMULTI);
41972c3b0c7SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
42072c3b0c7SVladimir Oltean bool uc = dev->flags & IFF_PROMISC;
4217569459aSVladimir Oltean
42272c3b0c7SVladimir Oltean dsa_port_set_host_flood(dp, uc, mc);
4237569459aSVladimir Oltean }
4247569459aSVladimir Oltean
dsa_slave_change_rx_flags(struct net_device * dev,int change)42591da11f8SLennert Buytenhek static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
42691da11f8SLennert Buytenhek {
427d0006b00SVivien Didelot struct net_device *master = dsa_slave_to_master(dev);
4287569459aSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
4297569459aSVladimir Oltean struct dsa_switch *ds = dp->ds;
43035aae5abSVladimir Oltean
43191da11f8SLennert Buytenhek if (change & IFF_ALLMULTI)
43217ab4f61SRundong Ge dev_set_allmulti(master,
43317ab4f61SRundong Ge dev->flags & IFF_ALLMULTI ? 1 : -1);
43491da11f8SLennert Buytenhek if (change & IFF_PROMISC)
43517ab4f61SRundong Ge dev_set_promiscuity(master,
43617ab4f61SRundong Ge dev->flags & IFF_PROMISC ? 1 : -1);
4377569459aSVladimir Oltean
4387569459aSVladimir Oltean if (dsa_switch_supports_uc_filtering(ds) &&
4397569459aSVladimir Oltean dsa_switch_supports_mc_filtering(ds))
44072c3b0c7SVladimir Oltean dsa_slave_manage_host_flood(dev);
44117ab4f61SRundong Ge }
44291da11f8SLennert Buytenhek
dsa_slave_set_rx_mode(struct net_device * dev)44391da11f8SLennert Buytenhek static void dsa_slave_set_rx_mode(struct net_device *dev)
44491da11f8SLennert Buytenhek {
4455e8a1e03SVladimir Oltean __dev_mc_sync(dev, dsa_slave_sync_mc, dsa_slave_unsync_mc);
4465e8a1e03SVladimir Oltean __dev_uc_sync(dev, dsa_slave_sync_uc, dsa_slave_unsync_uc);
44791da11f8SLennert Buytenhek }
44891da11f8SLennert Buytenhek
dsa_slave_set_mac_address(struct net_device * dev,void * a)449df02c6ffSLennert Buytenhek static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
45091da11f8SLennert Buytenhek {
451d0006b00SVivien Didelot struct net_device *master = dsa_slave_to_master(dev);
452499aa9e1SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
453499aa9e1SVladimir Oltean struct dsa_switch *ds = dp->ds;
454df02c6ffSLennert Buytenhek struct sockaddr *addr = a;
455df02c6ffSLennert Buytenhek int err;
456df02c6ffSLennert Buytenhek
457df02c6ffSLennert Buytenhek if (!is_valid_ether_addr(addr->sa_data))
458df02c6ffSLennert Buytenhek return -EADDRNOTAVAIL;
459df02c6ffSLennert Buytenhek
460e2d0576fSVladimir Oltean /* If the port is down, the address isn't synced yet to hardware or
461e2d0576fSVladimir Oltean * to the DSA master, so there is nothing to change.
462e2d0576fSVladimir Oltean */
463e2d0576fSVladimir Oltean if (!(dev->flags & IFF_UP))
464e2d0576fSVladimir Oltean goto out_change_dev_addr;
465e2d0576fSVladimir Oltean
466499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds)) {
467499aa9e1SVladimir Oltean err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0);
468499aa9e1SVladimir Oltean if (err)
469499aa9e1SVladimir Oltean return err;
470499aa9e1SVladimir Oltean }
471499aa9e1SVladimir Oltean
4728feedbb4SJoe Perches if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
473a748ee24SJiri Pirko err = dev_uc_add(master, addr->sa_data);
474df02c6ffSLennert Buytenhek if (err < 0)
475499aa9e1SVladimir Oltean goto del_unicast;
476df02c6ffSLennert Buytenhek }
477df02c6ffSLennert Buytenhek
4788feedbb4SJoe Perches if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
479a748ee24SJiri Pirko dev_uc_del(master, dev->dev_addr);
480df02c6ffSLennert Buytenhek
481499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds))
482499aa9e1SVladimir Oltean dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0);
483499aa9e1SVladimir Oltean
484e2d0576fSVladimir Oltean out_change_dev_addr:
485e35b8d7dSJakub Kicinski eth_hw_addr_set(dev, addr->sa_data);
48691da11f8SLennert Buytenhek
48791da11f8SLennert Buytenhek return 0;
488499aa9e1SVladimir Oltean
489499aa9e1SVladimir Oltean del_unicast:
490499aa9e1SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds))
491499aa9e1SVladimir Oltean dsa_port_standalone_host_fdb_del(dp, addr->sa_data, 0);
492499aa9e1SVladimir Oltean
493499aa9e1SVladimir Oltean return err;
49491da11f8SLennert Buytenhek }
49591da11f8SLennert Buytenhek
4962bedde1aSArkadi Sharshevsky struct dsa_slave_dump_ctx {
4972bedde1aSArkadi Sharshevsky struct net_device *dev;
4982bedde1aSArkadi Sharshevsky struct sk_buff *skb;
4992bedde1aSArkadi Sharshevsky struct netlink_callback *cb;
5002bedde1aSArkadi Sharshevsky int idx;
5012bedde1aSArkadi Sharshevsky };
5022bedde1aSArkadi Sharshevsky
5032bedde1aSArkadi Sharshevsky static int
dsa_slave_port_fdb_do_dump(const unsigned char * addr,u16 vid,bool is_static,void * data)5042bedde1aSArkadi Sharshevsky dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid,
5052bedde1aSArkadi Sharshevsky bool is_static, void *data)
5062bedde1aSArkadi Sharshevsky {
5072bedde1aSArkadi Sharshevsky struct dsa_slave_dump_ctx *dump = data;
5082bedde1aSArkadi Sharshevsky u32 portid = NETLINK_CB(dump->cb->skb).portid;
5092bedde1aSArkadi Sharshevsky u32 seq = dump->cb->nlh->nlmsg_seq;
5102bedde1aSArkadi Sharshevsky struct nlmsghdr *nlh;
5112bedde1aSArkadi Sharshevsky struct ndmsg *ndm;
5122bedde1aSArkadi Sharshevsky
5132bedde1aSArkadi Sharshevsky if (dump->idx < dump->cb->args[2])
5142bedde1aSArkadi Sharshevsky goto skip;
5152bedde1aSArkadi Sharshevsky
5162bedde1aSArkadi Sharshevsky nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
5172bedde1aSArkadi Sharshevsky sizeof(*ndm), NLM_F_MULTI);
5182bedde1aSArkadi Sharshevsky if (!nlh)
5192bedde1aSArkadi Sharshevsky return -EMSGSIZE;
5202bedde1aSArkadi Sharshevsky
5212bedde1aSArkadi Sharshevsky ndm = nlmsg_data(nlh);
5222bedde1aSArkadi Sharshevsky ndm->ndm_family = AF_BRIDGE;
5232bedde1aSArkadi Sharshevsky ndm->ndm_pad1 = 0;
5242bedde1aSArkadi Sharshevsky ndm->ndm_pad2 = 0;
5252bedde1aSArkadi Sharshevsky ndm->ndm_flags = NTF_SELF;
5262bedde1aSArkadi Sharshevsky ndm->ndm_type = 0;
5272bedde1aSArkadi Sharshevsky ndm->ndm_ifindex = dump->dev->ifindex;
5282bedde1aSArkadi Sharshevsky ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
5292bedde1aSArkadi Sharshevsky
5302bedde1aSArkadi Sharshevsky if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
5312bedde1aSArkadi Sharshevsky goto nla_put_failure;
5322bedde1aSArkadi Sharshevsky
5332bedde1aSArkadi Sharshevsky if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
5342bedde1aSArkadi Sharshevsky goto nla_put_failure;
5352bedde1aSArkadi Sharshevsky
5362bedde1aSArkadi Sharshevsky nlmsg_end(dump->skb, nlh);
5372bedde1aSArkadi Sharshevsky
5382bedde1aSArkadi Sharshevsky skip:
5392bedde1aSArkadi Sharshevsky dump->idx++;
5402bedde1aSArkadi Sharshevsky return 0;
5412bedde1aSArkadi Sharshevsky
5422bedde1aSArkadi Sharshevsky nla_put_failure:
5432bedde1aSArkadi Sharshevsky nlmsg_cancel(dump->skb, nlh);
5442bedde1aSArkadi Sharshevsky return -EMSGSIZE;
5452bedde1aSArkadi Sharshevsky }
5462bedde1aSArkadi Sharshevsky
5472bedde1aSArkadi Sharshevsky 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)5482bedde1aSArkadi Sharshevsky dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
5492bedde1aSArkadi Sharshevsky struct net_device *dev, struct net_device *filter_dev,
5502bedde1aSArkadi Sharshevsky int *idx)
5512bedde1aSArkadi Sharshevsky {
552d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
5532bedde1aSArkadi Sharshevsky struct dsa_slave_dump_ctx dump = {
5542bedde1aSArkadi Sharshevsky .dev = dev,
5552bedde1aSArkadi Sharshevsky .skb = skb,
5562bedde1aSArkadi Sharshevsky .cb = cb,
5572bedde1aSArkadi Sharshevsky .idx = *idx,
5582bedde1aSArkadi Sharshevsky };
5592bedde1aSArkadi Sharshevsky int err;
5602bedde1aSArkadi Sharshevsky
561de40fc5dSVivien Didelot err = dsa_port_fdb_dump(dp, dsa_slave_port_fdb_do_dump, &dump);
5622bedde1aSArkadi Sharshevsky *idx = dump.idx;
563de40fc5dSVivien Didelot
5642bedde1aSArkadi Sharshevsky return err;
5652bedde1aSArkadi Sharshevsky }
5662bedde1aSArkadi Sharshevsky
dsa_slave_ioctl(struct net_device * dev,struct ifreq * ifr,int cmd)56791da11f8SLennert Buytenhek static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
56891da11f8SLennert Buytenhek {
5690336369dSBrandon Streiff struct dsa_slave_priv *p = netdev_priv(dev);
5700336369dSBrandon Streiff struct dsa_switch *ds = p->dp->ds;
5710336369dSBrandon Streiff int port = p->dp->index;
5720336369dSBrandon Streiff
5730336369dSBrandon Streiff /* Pass through to switch driver if it supports timestamping */
5740336369dSBrandon Streiff switch (cmd) {
5750336369dSBrandon Streiff case SIOCGHWTSTAMP:
5760336369dSBrandon Streiff if (ds->ops->port_hwtstamp_get)
5770336369dSBrandon Streiff return ds->ops->port_hwtstamp_get(ds, port, ifr);
5780336369dSBrandon Streiff break;
5790336369dSBrandon Streiff case SIOCSHWTSTAMP:
5800336369dSBrandon Streiff if (ds->ops->port_hwtstamp_set)
5810336369dSBrandon Streiff return ds->ops->port_hwtstamp_set(ds, port, ifr);
5820336369dSBrandon Streiff break;
5830336369dSBrandon Streiff }
5840336369dSBrandon Streiff
585aab9c406SFlorian Fainelli return phylink_mii_ioctl(p->dp->pl, ifr, cmd);
58691da11f8SLennert Buytenhek }
58791da11f8SLennert Buytenhek
dsa_slave_port_attr_set(struct net_device * dev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)58869bfac96SVladimir Oltean static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx,
5894c08c586SVladimir Oltean const struct switchdev_attr *attr,
5904c08c586SVladimir Oltean struct netlink_ext_ack *extack)
59135636062SScott Feldman {
592d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
593b8d866acSVivien Didelot int ret;
59435636062SScott Feldman
5950d2cfbd4SVladimir Oltean if (ctx && ctx != dp)
5960d2cfbd4SVladimir Oltean return 0;
5970d2cfbd4SVladimir Oltean
59835636062SScott Feldman switch (attr->id) {
5991f868398SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
60003cbb870SVladimir Oltean if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
60103cbb870SVladimir Oltean return -EOPNOTSUPP;
60203cbb870SVladimir Oltean
60339f32101SVladimir Oltean ret = dsa_port_set_state(dp, attr->u.stp_state, true);
60435636062SScott Feldman break;
6057414af30STobias Waldekranz case SWITCHDEV_ATTR_ID_PORT_MST_STATE:
6067414af30STobias Waldekranz if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
6077414af30STobias Waldekranz return -EOPNOTSUPP;
6087414af30STobias Waldekranz
6097414af30STobias Waldekranz ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack);
6107414af30STobias Waldekranz break;
611fb2dabadSVivien Didelot case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
612936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
61303cbb870SVladimir Oltean return -EOPNOTSUPP;
61403cbb870SVladimir Oltean
61589153ed6SVladimir Oltean ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
61689153ed6SVladimir Oltean extack);
617fb2dabadSVivien Didelot break;
61834a79f63SVivien Didelot case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
619936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
62003cbb870SVladimir Oltean return -EOPNOTSUPP;
62103cbb870SVladimir Oltean
622bae33f2bSVladimir Oltean ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
62334a79f63SVivien Didelot break;
624332afc4cSTobias Waldekranz case SWITCHDEV_ATTR_ID_BRIDGE_MST:
625332afc4cSTobias Waldekranz if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
626332afc4cSTobias Waldekranz return -EOPNOTSUPP;
627332afc4cSTobias Waldekranz
628332afc4cSTobias Waldekranz ret = dsa_port_mst_enable(dp, attr->u.mst, extack);
629332afc4cSTobias Waldekranz break;
630ea87005aSFlorian Fainelli case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
63103cbb870SVladimir Oltean if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
63203cbb870SVladimir Oltean return -EOPNOTSUPP;
63303cbb870SVladimir Oltean
634a8b659e7SVladimir Oltean ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
635a8b659e7SVladimir Oltean extack);
636ea87005aSFlorian Fainelli break;
63757652796SRussell King case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
63803cbb870SVladimir Oltean if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
63903cbb870SVladimir Oltean return -EOPNOTSUPP;
64003cbb870SVladimir Oltean
641a8b659e7SVladimir Oltean ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);
64257652796SRussell King break;
6438e6598a7STobias Waldekranz case SWITCHDEV_ATTR_ID_VLAN_MSTI:
6448e6598a7STobias Waldekranz if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
6458e6598a7STobias Waldekranz return -EOPNOTSUPP;
6468e6598a7STobias Waldekranz
6478e6598a7STobias Waldekranz ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti);
6488e6598a7STobias Waldekranz break;
64935636062SScott Feldman default:
65035636062SScott Feldman ret = -EOPNOTSUPP;
65135636062SScott Feldman break;
65235636062SScott Feldman }
65335636062SScott Feldman
65435636062SScott Feldman return ret;
65535636062SScott Feldman }
65635636062SScott Feldman
6571ce39f0eSVladimir Oltean /* Must be called under rcu_read_lock() */
6581ce39f0eSVladimir Oltean static int
dsa_slave_vlan_check_for_8021q_uppers(struct net_device * slave,const struct switchdev_obj_port_vlan * vlan)6591ce39f0eSVladimir Oltean dsa_slave_vlan_check_for_8021q_uppers(struct net_device *slave,
6601ce39f0eSVladimir Oltean const struct switchdev_obj_port_vlan *vlan)
6611ce39f0eSVladimir Oltean {
6621ce39f0eSVladimir Oltean struct net_device *upper_dev;
6631ce39f0eSVladimir Oltean struct list_head *iter;
6641ce39f0eSVladimir Oltean
6651ce39f0eSVladimir Oltean netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
6661ce39f0eSVladimir Oltean u16 vid;
6671ce39f0eSVladimir Oltean
6681ce39f0eSVladimir Oltean if (!is_vlan_dev(upper_dev))
6691ce39f0eSVladimir Oltean continue;
6701ce39f0eSVladimir Oltean
6711ce39f0eSVladimir Oltean vid = vlan_dev_vlan_id(upper_dev);
672b7a9e0daSVladimir Oltean if (vid == vlan->vid)
6731ce39f0eSVladimir Oltean return -EBUSY;
6741ce39f0eSVladimir Oltean }
6751ce39f0eSVladimir Oltean
6761ce39f0eSVladimir Oltean return 0;
6771ce39f0eSVladimir Oltean }
6781ce39f0eSVladimir Oltean
dsa_slave_vlan_add(struct net_device * dev,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)679bdcff080SVivien Didelot static int dsa_slave_vlan_add(struct net_device *dev,
6800ee2af4eSVladimir Oltean const struct switchdev_obj *obj,
6810ee2af4eSVladimir Oltean struct netlink_ext_ack *extack)
682bdcff080SVivien Didelot {
683bdcff080SVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
684134ef238SVladimir Oltean struct switchdev_obj_port_vlan *vlan;
685b7a9e0daSVladimir Oltean int err;
686bdcff080SVivien Didelot
6870ee2af4eSVladimir Oltean if (dsa_port_skip_vlan_configuration(dp)) {
6880ee2af4eSVladimir Oltean NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");
689c5335d73SVivien Didelot return 0;
6900ee2af4eSVladimir Oltean }
691c5335d73SVivien Didelot
692134ef238SVladimir Oltean vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
693bdcff080SVivien Didelot
6941ce39f0eSVladimir Oltean /* Deny adding a bridge VLAN when there is already an 802.1Q upper with
6951ce39f0eSVladimir Oltean * the same VID.
6961ce39f0eSVladimir Oltean */
69736cbf39bSVladimir Oltean if (br_vlan_enabled(dsa_port_bridge_dev_get(dp))) {
6981ce39f0eSVladimir Oltean rcu_read_lock();
699134ef238SVladimir Oltean err = dsa_slave_vlan_check_for_8021q_uppers(dev, vlan);
7001ce39f0eSVladimir Oltean rcu_read_unlock();
70131046a5fSVladimir Oltean if (err) {
70231046a5fSVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
70331046a5fSVladimir Oltean "Port already has a VLAN upper with this VID");
7041ce39f0eSVladimir Oltean return err;
7051ce39f0eSVladimir Oltean }
70631046a5fSVladimir Oltean }
7071ce39f0eSVladimir Oltean
708134ef238SVladimir Oltean return dsa_port_vlan_add(dp, vlan, extack);
709134ef238SVladimir Oltean }
710bdcff080SVivien Didelot
711164f861bSVladimir Oltean /* Offload a VLAN installed on the bridge or on a foreign interface by
712164f861bSVladimir Oltean * installing it as a VLAN towards the CPU port.
713164f861bSVladimir Oltean */
dsa_slave_host_vlan_add(struct net_device * dev,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)714134ef238SVladimir Oltean static int dsa_slave_host_vlan_add(struct net_device *dev,
715134ef238SVladimir Oltean const struct switchdev_obj *obj,
716134ef238SVladimir Oltean struct netlink_ext_ack *extack)
717134ef238SVladimir Oltean {
718134ef238SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
719134ef238SVladimir Oltean struct switchdev_obj_port_vlan vlan;
720134ef238SVladimir Oltean
721164f861bSVladimir Oltean /* Do nothing if this is a software bridge */
722164f861bSVladimir Oltean if (!dp->bridge)
723164f861bSVladimir Oltean return -EOPNOTSUPP;
724164f861bSVladimir Oltean
725134ef238SVladimir Oltean if (dsa_port_skip_vlan_configuration(dp)) {
726134ef238SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");
727134ef238SVladimir Oltean return 0;
728134ef238SVladimir Oltean }
729134ef238SVladimir Oltean
730134ef238SVladimir Oltean vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj);
731134ef238SVladimir Oltean
732134ef238SVladimir Oltean /* Even though drivers often handle CPU membership in special ways,
733b9499904SVivien Didelot * it doesn't make sense to program a PVID, so clear this flag.
734b9499904SVivien Didelot */
735b9499904SVivien Didelot vlan.flags &= ~BRIDGE_VLAN_INFO_PVID;
736b9499904SVivien Didelot
737134ef238SVladimir Oltean return dsa_port_host_vlan_add(dp, &vlan, extack);
738bdcff080SVivien Didelot }
739bdcff080SVivien Didelot
dsa_slave_port_obj_add(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)74069bfac96SVladimir Oltean static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx,
741648b4a99SJiri Pirko const struct switchdev_obj *obj,
74279b139f4SVivien Didelot struct netlink_ext_ack *extack)
743ba14d9ebSVivien Didelot {
744d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
745ba14d9ebSVivien Didelot int err;
746ba14d9ebSVivien Didelot
7470d2cfbd4SVladimir Oltean if (ctx && ctx != dp)
7480d2cfbd4SVladimir Oltean return 0;
7490d2cfbd4SVladimir Oltean
7509e8f4a54SJiri Pirko switch (obj->id) {
7518df30255SVivien Didelot case SWITCHDEV_OBJ_ID_PORT_MDB:
75203cbb870SVladimir Oltean if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
75379b139f4SVivien Didelot return -EOPNOTSUPP;
75403cbb870SVladimir Oltean
755ffb68fc5SVladimir Oltean err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
7568df30255SVivien Didelot break;
7575f4dbc50SAndrew Lunn case SWITCHDEV_OBJ_ID_HOST_MDB:
758936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
75903cbb870SVladimir Oltean return -EOPNOTSUPP;
76003cbb870SVladimir Oltean
76168d6d71eSVladimir Oltean err = dsa_port_bridge_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
7625f4dbc50SAndrew Lunn break;
76357d80838SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN:
764164f861bSVladimir Oltean if (dsa_port_offloads_bridge_port(dp, obj->orig_dev))
7650ee2af4eSVladimir Oltean err = dsa_slave_vlan_add(dev, obj, extack);
766164f861bSVladimir Oltean else
767164f861bSVladimir Oltean err = dsa_slave_host_vlan_add(dev, obj, extack);
76811149536SVivien Didelot break;
769c595c433SHoratiu Vultur case SWITCHDEV_OBJ_ID_MRP:
770936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
771c595c433SHoratiu Vultur return -EOPNOTSUPP;
77203cbb870SVladimir Oltean
773c595c433SHoratiu Vultur err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj));
774c595c433SHoratiu Vultur break;
775c595c433SHoratiu Vultur case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
776936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
777c595c433SHoratiu Vultur return -EOPNOTSUPP;
77803cbb870SVladimir Oltean
779c595c433SHoratiu Vultur err = dsa_port_mrp_add_ring_role(dp,
780c595c433SHoratiu Vultur SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
781c595c433SHoratiu Vultur break;
782ba14d9ebSVivien Didelot default:
783ba14d9ebSVivien Didelot err = -EOPNOTSUPP;
784ba14d9ebSVivien Didelot break;
785ba14d9ebSVivien Didelot }
786ba14d9ebSVivien Didelot
787ba14d9ebSVivien Didelot return err;
788ba14d9ebSVivien Didelot }
789ba14d9ebSVivien Didelot
dsa_slave_vlan_del(struct net_device * dev,const struct switchdev_obj * obj)790bdcff080SVivien Didelot static int dsa_slave_vlan_del(struct net_device *dev,
791bdcff080SVivien Didelot const struct switchdev_obj *obj)
792bdcff080SVivien Didelot {
793bdcff080SVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
7942209158cSVladimir Oltean struct switchdev_obj_port_vlan *vlan;
795bdcff080SVivien Didelot
79654a0ed0dSRussell King if (dsa_port_skip_vlan_configuration(dp))
797c5335d73SVivien Didelot return 0;
798c5335d73SVivien Didelot
7992209158cSVladimir Oltean vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
8002209158cSVladimir Oltean
801134ef238SVladimir Oltean return dsa_port_vlan_del(dp, vlan);
802134ef238SVladimir Oltean }
8032209158cSVladimir Oltean
dsa_slave_host_vlan_del(struct net_device * dev,const struct switchdev_obj * obj)804134ef238SVladimir Oltean static int dsa_slave_host_vlan_del(struct net_device *dev,
805134ef238SVladimir Oltean const struct switchdev_obj *obj)
806134ef238SVladimir Oltean {
807134ef238SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
808134ef238SVladimir Oltean struct switchdev_obj_port_vlan *vlan;
8092209158cSVladimir Oltean
810164f861bSVladimir Oltean /* Do nothing if this is a software bridge */
811164f861bSVladimir Oltean if (!dp->bridge)
812164f861bSVladimir Oltean return -EOPNOTSUPP;
813164f861bSVladimir Oltean
814134ef238SVladimir Oltean if (dsa_port_skip_vlan_configuration(dp))
8152209158cSVladimir Oltean return 0;
816134ef238SVladimir Oltean
817134ef238SVladimir Oltean vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
818134ef238SVladimir Oltean
819134ef238SVladimir Oltean return dsa_port_host_vlan_del(dp, vlan);
820bdcff080SVivien Didelot }
821bdcff080SVivien Didelot
dsa_slave_port_obj_del(struct net_device * dev,const void * ctx,const struct switchdev_obj * obj)82269bfac96SVladimir Oltean static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx,
823648b4a99SJiri Pirko const struct switchdev_obj *obj)
824ba14d9ebSVivien Didelot {
825d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
826ba14d9ebSVivien Didelot int err;
827ba14d9ebSVivien Didelot
8280d2cfbd4SVladimir Oltean if (ctx && ctx != dp)
8290d2cfbd4SVladimir Oltean return 0;
8300d2cfbd4SVladimir Oltean
8319e8f4a54SJiri Pirko switch (obj->id) {
8328df30255SVivien Didelot case SWITCHDEV_OBJ_ID_PORT_MDB:
83303cbb870SVladimir Oltean if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
83479b139f4SVivien Didelot return -EOPNOTSUPP;
83503cbb870SVladimir Oltean
836bcebb976SVivien Didelot err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
8378df30255SVivien Didelot break;
8385f4dbc50SAndrew Lunn case SWITCHDEV_OBJ_ID_HOST_MDB:
839936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
84003cbb870SVladimir Oltean return -EOPNOTSUPP;
84103cbb870SVladimir Oltean
84268d6d71eSVladimir Oltean err = dsa_port_bridge_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
8435f4dbc50SAndrew Lunn break;
84457d80838SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN:
845164f861bSVladimir Oltean if (dsa_port_offloads_bridge_port(dp, obj->orig_dev))
846bdcff080SVivien Didelot err = dsa_slave_vlan_del(dev, obj);
847164f861bSVladimir Oltean else
848164f861bSVladimir Oltean err = dsa_slave_host_vlan_del(dev, obj);
84911149536SVivien Didelot break;
850c595c433SHoratiu Vultur case SWITCHDEV_OBJ_ID_MRP:
851936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
852c595c433SHoratiu Vultur return -EOPNOTSUPP;
85303cbb870SVladimir Oltean
854c595c433SHoratiu Vultur err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj));
855c595c433SHoratiu Vultur break;
856c595c433SHoratiu Vultur case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
857936db8a2SVladimir Oltean if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev))
858c595c433SHoratiu Vultur return -EOPNOTSUPP;
85903cbb870SVladimir Oltean
860c595c433SHoratiu Vultur err = dsa_port_mrp_del_ring_role(dp,
861c595c433SHoratiu Vultur SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
862c595c433SHoratiu Vultur break;
863ba14d9ebSVivien Didelot default:
864ba14d9ebSVivien Didelot err = -EOPNOTSUPP;
865ba14d9ebSVivien Didelot break;
866ba14d9ebSVivien Didelot }
867ba14d9ebSVivien Didelot
868ba14d9ebSVivien Didelot return err;
869ba14d9ebSVivien Didelot }
870ba14d9ebSVivien Didelot
dsa_slave_netpoll_send_skb(struct net_device * dev,struct sk_buff * skb)8714fa7b718SVivien Didelot static inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev,
87204ff53f9SFlorian Fainelli struct sk_buff *skb)
87304ff53f9SFlorian Fainelli {
87404ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
8754fa7b718SVivien Didelot struct dsa_slave_priv *p = netdev_priv(dev);
8764fa7b718SVivien Didelot
877f78ed220SEric Dumazet return netpoll_send_skb(p->netpoll, skb);
87804ff53f9SFlorian Fainelli #else
87904ff53f9SFlorian Fainelli BUG();
88004ff53f9SFlorian Fainelli return NETDEV_TX_OK;
881f78ed220SEric Dumazet #endif
88204ff53f9SFlorian Fainelli }
88304ff53f9SFlorian Fainelli
dsa_skb_tx_timestamp(struct dsa_slave_priv * p,struct sk_buff * skb)88490af1059SBrandon Streiff static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
88590af1059SBrandon Streiff struct sk_buff *skb)
88690af1059SBrandon Streiff {
88790af1059SBrandon Streiff struct dsa_switch *ds = p->dp->ds;
88890af1059SBrandon Streiff
889cfd12c06SYangbo Lu if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
890cfd12c06SYangbo Lu return;
891cfd12c06SYangbo Lu
89290af1059SBrandon Streiff if (!ds->ops->port_txtstamp)
89390af1059SBrandon Streiff return;
89490af1059SBrandon Streiff
8955c5416f5SYangbo Lu ds->ops->port_txtstamp(ds, p->dp->index, skb);
89690af1059SBrandon Streiff }
89790af1059SBrandon Streiff
dsa_enqueue_skb(struct sk_buff * skb,struct net_device * dev)89897a69a0dSVladimir Oltean netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev)
89997a69a0dSVladimir Oltean {
90097a69a0dSVladimir Oltean /* SKB for netpoll still need to be mangled with the protocol-specific
90197a69a0dSVladimir Oltean * tag to be successfully transmitted
90297a69a0dSVladimir Oltean */
90397a69a0dSVladimir Oltean if (unlikely(netpoll_tx_running(dev)))
90497a69a0dSVladimir Oltean return dsa_slave_netpoll_send_skb(dev, skb);
90597a69a0dSVladimir Oltean
90697a69a0dSVladimir Oltean /* Queue the SKB for transmission on the parent interface, but
90797a69a0dSVladimir Oltean * do not modify its EtherType
90897a69a0dSVladimir Oltean */
90997a69a0dSVladimir Oltean skb->dev = dsa_slave_to_master(dev);
91097a69a0dSVladimir Oltean dev_queue_xmit(skb);
91197a69a0dSVladimir Oltean
91297a69a0dSVladimir Oltean return NETDEV_TX_OK;
91397a69a0dSVladimir Oltean }
91497a69a0dSVladimir Oltean EXPORT_SYMBOL_GPL(dsa_enqueue_skb);
91597a69a0dSVladimir Oltean
dsa_realloc_skb(struct sk_buff * skb,struct net_device * dev)916a3b0b647SVladimir Oltean static int dsa_realloc_skb(struct sk_buff *skb, struct net_device *dev)
917a3b0b647SVladimir Oltean {
918a3b0b647SVladimir Oltean int needed_headroom = dev->needed_headroom;
919a3b0b647SVladimir Oltean int needed_tailroom = dev->needed_tailroom;
920a3b0b647SVladimir Oltean
921a3b0b647SVladimir Oltean /* For tail taggers, we need to pad short frames ourselves, to ensure
922a3b0b647SVladimir Oltean * that the tail tag does not fail at its role of being at the end of
923a3b0b647SVladimir Oltean * the packet, once the master interface pads the frame. Account for
924a3b0b647SVladimir Oltean * that pad length here, and pad later.
925a3b0b647SVladimir Oltean */
926a3b0b647SVladimir Oltean if (unlikely(needed_tailroom && skb->len < ETH_ZLEN))
927a3b0b647SVladimir Oltean needed_tailroom += ETH_ZLEN - skb->len;
928a3b0b647SVladimir Oltean /* skb_headroom() returns unsigned int... */
929a3b0b647SVladimir Oltean needed_headroom = max_t(int, needed_headroom - skb_headroom(skb), 0);
930a3b0b647SVladimir Oltean needed_tailroom = max_t(int, needed_tailroom - skb_tailroom(skb), 0);
931a3b0b647SVladimir Oltean
932a3b0b647SVladimir Oltean if (likely(!needed_headroom && !needed_tailroom && !skb_cloned(skb)))
933a3b0b647SVladimir Oltean /* No reallocation needed, yay! */
934a3b0b647SVladimir Oltean return 0;
935a3b0b647SVladimir Oltean
936a3b0b647SVladimir Oltean return pskb_expand_head(skb, needed_headroom, needed_tailroom,
937a3b0b647SVladimir Oltean GFP_ATOMIC);
938a3b0b647SVladimir Oltean }
939a3b0b647SVladimir Oltean
dsa_slave_xmit(struct sk_buff * skb,struct net_device * dev)9403e8a72d1SFlorian Fainelli static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
9413e8a72d1SFlorian Fainelli {
9423e8a72d1SFlorian Fainelli struct dsa_slave_priv *p = netdev_priv(dev);
9434ed70ce9SFlorian Fainelli struct sk_buff *nskb;
9443e8a72d1SFlorian Fainelli
9456a900628SHeiner Kallweit dev_sw_netstats_tx_add(dev, 1, skb->len);
9463e8a72d1SFlorian Fainelli
947c4b364ceSYangbo Lu memset(skb->cb, 0, sizeof(skb->cb));
94887671375SVladimir Oltean
949cf536ea3SYangbo Lu /* Handle tx timestamp if any */
95090af1059SBrandon Streiff dsa_skb_tx_timestamp(p, skb);
95190af1059SBrandon Streiff
952a3b0b647SVladimir Oltean if (dsa_realloc_skb(skb, dev)) {
953a3b0b647SVladimir Oltean dev_kfree_skb_any(skb);
954a3b0b647SVladimir Oltean return NETDEV_TX_OK;
955a3b0b647SVladimir Oltean }
956a3b0b647SVladimir Oltean
957a3b0b647SVladimir Oltean /* needed_tailroom should still be 'warm' in the cache line from
958a3b0b647SVladimir Oltean * dsa_realloc_skb(), which has also ensured that padding is safe.
959a3b0b647SVladimir Oltean */
960a3b0b647SVladimir Oltean if (dev->needed_tailroom)
961a3b0b647SVladimir Oltean eth_skb_pad(skb);
962a3b0b647SVladimir Oltean
963fe47d563SVivien Didelot /* Transmit function may have to reallocate the original SKB,
964fe47d563SVivien Didelot * in which case it must have freed it. Only free it here on error.
965fe47d563SVivien Didelot */
9664ed70ce9SFlorian Fainelli nskb = p->xmit(skb, dev);
967fe47d563SVivien Didelot if (!nskb) {
968fe47d563SVivien Didelot kfree_skb(skb);
9694ed70ce9SFlorian Fainelli return NETDEV_TX_OK;
970fe47d563SVivien Didelot }
9715aed85ceSFlorian Fainelli
97297a69a0dSVladimir Oltean return dsa_enqueue_skb(nskb, dev);
97397a69a0dSVladimir Oltean }
97404ff53f9SFlorian Fainelli
97591da11f8SLennert Buytenhek /* ethtool operations *******************************************************/
97691da11f8SLennert Buytenhek
dsa_slave_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)97791da11f8SLennert Buytenhek static void dsa_slave_get_drvinfo(struct net_device *dev,
97891da11f8SLennert Buytenhek struct ethtool_drvinfo *drvinfo)
97991da11f8SLennert Buytenhek {
980e4d44b3dSWolfram Sang strscpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
981e4d44b3dSWolfram Sang strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
982e4d44b3dSWolfram Sang strscpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
98391da11f8SLennert Buytenhek }
98491da11f8SLennert Buytenhek
dsa_slave_get_regs_len(struct net_device * dev)9853d762a0fSGuenter Roeck static int dsa_slave_get_regs_len(struct net_device *dev)
9863d762a0fSGuenter Roeck {
987d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
988d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
9893d762a0fSGuenter Roeck
9909d490b4eSVivien Didelot if (ds->ops->get_regs_len)
991d945097bSVivien Didelot return ds->ops->get_regs_len(ds, dp->index);
9923d762a0fSGuenter Roeck
9933d762a0fSGuenter Roeck return -EOPNOTSUPP;
9943d762a0fSGuenter Roeck }
9953d762a0fSGuenter Roeck
9963d762a0fSGuenter Roeck static void
dsa_slave_get_regs(struct net_device * dev,struct ethtool_regs * regs,void * _p)9973d762a0fSGuenter Roeck dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
9983d762a0fSGuenter Roeck {
999d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1000d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
10013d762a0fSGuenter Roeck
10029d490b4eSVivien Didelot if (ds->ops->get_regs)
1003d945097bSVivien Didelot ds->ops->get_regs(ds, dp->index, regs, _p);
10043d762a0fSGuenter Roeck }
10053d762a0fSGuenter Roeck
dsa_slave_nway_reset(struct net_device * dev)1006aab9c406SFlorian Fainelli static int dsa_slave_nway_reset(struct net_device *dev)
1007aab9c406SFlorian Fainelli {
1008aab9c406SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(dev);
1009aab9c406SFlorian Fainelli
1010aab9c406SFlorian Fainelli return phylink_ethtool_nway_reset(dp->pl);
1011aab9c406SFlorian Fainelli }
1012aab9c406SFlorian Fainelli
dsa_slave_get_eeprom_len(struct net_device * dev)10136793abb4SGuenter Roeck static int dsa_slave_get_eeprom_len(struct net_device *dev)
10146793abb4SGuenter Roeck {
1015d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1016d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
10176793abb4SGuenter Roeck
10180e576044SAndrew Lunn if (ds->cd && ds->cd->eeprom_len)
1019ff04955cSAndrew Lunn return ds->cd->eeprom_len;
10206793abb4SGuenter Roeck
10219d490b4eSVivien Didelot if (ds->ops->get_eeprom_len)
10229d490b4eSVivien Didelot return ds->ops->get_eeprom_len(ds);
10236793abb4SGuenter Roeck
10246793abb4SGuenter Roeck return 0;
10256793abb4SGuenter Roeck }
10266793abb4SGuenter Roeck
dsa_slave_get_eeprom(struct net_device * dev,struct ethtool_eeprom * eeprom,u8 * data)10276793abb4SGuenter Roeck static int dsa_slave_get_eeprom(struct net_device *dev,
10286793abb4SGuenter Roeck struct ethtool_eeprom *eeprom, u8 *data)
10296793abb4SGuenter Roeck {
1030d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1031d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
10326793abb4SGuenter Roeck
10339d490b4eSVivien Didelot if (ds->ops->get_eeprom)
10349d490b4eSVivien Didelot return ds->ops->get_eeprom(ds, eeprom, data);
10356793abb4SGuenter Roeck
10366793abb4SGuenter Roeck return -EOPNOTSUPP;
10376793abb4SGuenter Roeck }
10386793abb4SGuenter Roeck
dsa_slave_set_eeprom(struct net_device * dev,struct ethtool_eeprom * eeprom,u8 * data)10396793abb4SGuenter Roeck static int dsa_slave_set_eeprom(struct net_device *dev,
10406793abb4SGuenter Roeck struct ethtool_eeprom *eeprom, u8 *data)
10416793abb4SGuenter Roeck {
1042d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1043d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
10446793abb4SGuenter Roeck
10459d490b4eSVivien Didelot if (ds->ops->set_eeprom)
10469d490b4eSVivien Didelot return ds->ops->set_eeprom(ds, eeprom, data);
10476793abb4SGuenter Roeck
10486793abb4SGuenter Roeck return -EOPNOTSUPP;
10496793abb4SGuenter Roeck }
10506793abb4SGuenter Roeck
dsa_slave_get_strings(struct net_device * dev,uint32_t stringset,uint8_t * data)105191da11f8SLennert Buytenhek static void dsa_slave_get_strings(struct net_device *dev,
105291da11f8SLennert Buytenhek uint32_t stringset, uint8_t *data)
105391da11f8SLennert Buytenhek {
1054d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1055d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
105691da11f8SLennert Buytenhek
105791da11f8SLennert Buytenhek if (stringset == ETH_SS_STATS) {
105891da11f8SLennert Buytenhek int len = ETH_GSTRING_LEN;
105991da11f8SLennert Buytenhek
10605c9f7b04Sjustinstitt@google.com strscpy_pad(data, "tx_packets", len);
10615c9f7b04Sjustinstitt@google.com strscpy_pad(data + len, "tx_bytes", len);
10625c9f7b04Sjustinstitt@google.com strscpy_pad(data + 2 * len, "rx_packets", len);
10635c9f7b04Sjustinstitt@google.com strscpy_pad(data + 3 * len, "rx_bytes", len);
10649d490b4eSVivien Didelot if (ds->ops->get_strings)
106589f09048SFlorian Fainelli ds->ops->get_strings(ds, dp->index, stringset,
106689f09048SFlorian Fainelli data + 4 * len);
1067a71acad9SOleksij Rempel } else if (stringset == ETH_SS_TEST) {
1068a71acad9SOleksij Rempel net_selftest_get_strings(data);
106991da11f8SLennert Buytenhek }
1070a71acad9SOleksij Rempel
107191da11f8SLennert Buytenhek }
107291da11f8SLennert Buytenhek
dsa_slave_get_ethtool_stats(struct net_device * dev,struct ethtool_stats * stats,uint64_t * data)107391da11f8SLennert Buytenhek static void dsa_slave_get_ethtool_stats(struct net_device *dev,
107491da11f8SLennert Buytenhek struct ethtool_stats *stats,
107591da11f8SLennert Buytenhek uint64_t *data)
107691da11f8SLennert Buytenhek {
1077d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1078d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
10795f6b4e14SFlorian Fainelli struct pcpu_sw_netstats *s;
1080f613ed66SFlorian Fainelli unsigned int start;
10815f6b4e14SFlorian Fainelli int i;
108291da11f8SLennert Buytenhek
10835f6b4e14SFlorian Fainelli for_each_possible_cpu(i) {
10845f6b4e14SFlorian Fainelli u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
10855f6b4e14SFlorian Fainelli
10866a900628SHeiner Kallweit s = per_cpu_ptr(dev->tstats, i);
1087f613ed66SFlorian Fainelli do {
1088d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&s->syncp);
10899962acefSEric Dumazet tx_packets = u64_stats_read(&s->tx_packets);
10909962acefSEric Dumazet tx_bytes = u64_stats_read(&s->tx_bytes);
10919962acefSEric Dumazet rx_packets = u64_stats_read(&s->rx_packets);
10929962acefSEric Dumazet rx_bytes = u64_stats_read(&s->rx_bytes);
1093d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&s->syncp, start));
10945f6b4e14SFlorian Fainelli data[0] += tx_packets;
10955f6b4e14SFlorian Fainelli data[1] += tx_bytes;
10965f6b4e14SFlorian Fainelli data[2] += rx_packets;
10975f6b4e14SFlorian Fainelli data[3] += rx_bytes;
10985f6b4e14SFlorian Fainelli }
10999d490b4eSVivien Didelot if (ds->ops->get_ethtool_stats)
1100d945097bSVivien Didelot ds->ops->get_ethtool_stats(ds, dp->index, data + 4);
110191da11f8SLennert Buytenhek }
110291da11f8SLennert Buytenhek
dsa_slave_get_sset_count(struct net_device * dev,int sset)110391da11f8SLennert Buytenhek static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
110491da11f8SLennert Buytenhek {
1105d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1106d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
110791da11f8SLennert Buytenhek
110891da11f8SLennert Buytenhek if (sset == ETH_SS_STATS) {
1109b94cbc90SVladimir Oltean int count = 0;
111091da11f8SLennert Buytenhek
1111b94cbc90SVladimir Oltean if (ds->ops->get_sset_count) {
1112b94cbc90SVladimir Oltean count = ds->ops->get_sset_count(ds, dp->index, sset);
1113b94cbc90SVladimir Oltean if (count < 0)
111491da11f8SLennert Buytenhek return count;
1115b94cbc90SVladimir Oltean }
1116b94cbc90SVladimir Oltean
1117b94cbc90SVladimir Oltean return count + 4;
1118a71acad9SOleksij Rempel } else if (sset == ETH_SS_TEST) {
1119a71acad9SOleksij Rempel return net_selftest_get_count();
112091da11f8SLennert Buytenhek }
112191da11f8SLennert Buytenhek
112291da11f8SLennert Buytenhek return -EOPNOTSUPP;
112391da11f8SLennert Buytenhek }
112491da11f8SLennert Buytenhek
dsa_slave_get_eth_phy_stats(struct net_device * dev,struct ethtool_eth_phy_stats * phy_stats)1125487d3855SAlvin Šipraga static void dsa_slave_get_eth_phy_stats(struct net_device *dev,
1126487d3855SAlvin Šipraga struct ethtool_eth_phy_stats *phy_stats)
1127487d3855SAlvin Šipraga {
1128487d3855SAlvin Šipraga struct dsa_port *dp = dsa_slave_to_port(dev);
1129487d3855SAlvin Šipraga struct dsa_switch *ds = dp->ds;
1130487d3855SAlvin Šipraga
1131487d3855SAlvin Šipraga if (ds->ops->get_eth_phy_stats)
1132487d3855SAlvin Šipraga ds->ops->get_eth_phy_stats(ds, dp->index, phy_stats);
1133487d3855SAlvin Šipraga }
1134487d3855SAlvin Šipraga
dsa_slave_get_eth_mac_stats(struct net_device * dev,struct ethtool_eth_mac_stats * mac_stats)1135487d3855SAlvin Šipraga static void dsa_slave_get_eth_mac_stats(struct net_device *dev,
1136487d3855SAlvin Šipraga struct ethtool_eth_mac_stats *mac_stats)
1137487d3855SAlvin Šipraga {
1138487d3855SAlvin Šipraga struct dsa_port *dp = dsa_slave_to_port(dev);
1139487d3855SAlvin Šipraga struct dsa_switch *ds = dp->ds;
1140487d3855SAlvin Šipraga
1141487d3855SAlvin Šipraga if (ds->ops->get_eth_mac_stats)
1142487d3855SAlvin Šipraga ds->ops->get_eth_mac_stats(ds, dp->index, mac_stats);
1143487d3855SAlvin Šipraga }
1144487d3855SAlvin Šipraga
1145487d3855SAlvin Šipraga static void
dsa_slave_get_eth_ctrl_stats(struct net_device * dev,struct ethtool_eth_ctrl_stats * ctrl_stats)1146487d3855SAlvin Šipraga dsa_slave_get_eth_ctrl_stats(struct net_device *dev,
1147487d3855SAlvin Šipraga struct ethtool_eth_ctrl_stats *ctrl_stats)
1148487d3855SAlvin Šipraga {
1149487d3855SAlvin Šipraga struct dsa_port *dp = dsa_slave_to_port(dev);
1150487d3855SAlvin Šipraga struct dsa_switch *ds = dp->ds;
1151487d3855SAlvin Šipraga
1152487d3855SAlvin Šipraga if (ds->ops->get_eth_ctrl_stats)
1153487d3855SAlvin Šipraga ds->ops->get_eth_ctrl_stats(ds, dp->index, ctrl_stats);
1154487d3855SAlvin Šipraga }
1155487d3855SAlvin Šipraga
115667f38b1cSClément Léger static void
dsa_slave_get_rmon_stats(struct net_device * dev,struct ethtool_rmon_stats * rmon_stats,const struct ethtool_rmon_hist_range ** ranges)115767f38b1cSClément Léger dsa_slave_get_rmon_stats(struct net_device *dev,
115867f38b1cSClément Léger struct ethtool_rmon_stats *rmon_stats,
115967f38b1cSClément Léger const struct ethtool_rmon_hist_range **ranges)
116067f38b1cSClément Léger {
116167f38b1cSClément Léger struct dsa_port *dp = dsa_slave_to_port(dev);
116267f38b1cSClément Léger struct dsa_switch *ds = dp->ds;
116367f38b1cSClément Léger
116467f38b1cSClément Léger if (ds->ops->get_rmon_stats)
116567f38b1cSClément Léger ds->ops->get_rmon_stats(ds, dp->index, rmon_stats, ranges);
116667f38b1cSClément Léger }
116767f38b1cSClément Léger
dsa_slave_net_selftest(struct net_device * ndev,struct ethtool_test * etest,u64 * buf)1168a71acad9SOleksij Rempel static void dsa_slave_net_selftest(struct net_device *ndev,
1169a71acad9SOleksij Rempel struct ethtool_test *etest, u64 *buf)
1170a71acad9SOleksij Rempel {
1171a71acad9SOleksij Rempel struct dsa_port *dp = dsa_slave_to_port(ndev);
1172a71acad9SOleksij Rempel struct dsa_switch *ds = dp->ds;
1173a71acad9SOleksij Rempel
1174a71acad9SOleksij Rempel if (ds->ops->self_test) {
1175a71acad9SOleksij Rempel ds->ops->self_test(ds, dp->index, etest, buf);
1176a71acad9SOleksij Rempel return;
1177a71acad9SOleksij Rempel }
1178a71acad9SOleksij Rempel
1179a71acad9SOleksij Rempel net_selftest(ndev, etest, buf);
1180a71acad9SOleksij Rempel }
1181a71acad9SOleksij Rempel
dsa_slave_get_mm(struct net_device * dev,struct ethtool_mm_state * state)11825f6c2d49SVladimir Oltean static int dsa_slave_get_mm(struct net_device *dev,
11835f6c2d49SVladimir Oltean struct ethtool_mm_state *state)
11845f6c2d49SVladimir Oltean {
11855f6c2d49SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
11865f6c2d49SVladimir Oltean struct dsa_switch *ds = dp->ds;
11875f6c2d49SVladimir Oltean
11885f6c2d49SVladimir Oltean if (!ds->ops->get_mm)
11895f6c2d49SVladimir Oltean return -EOPNOTSUPP;
11905f6c2d49SVladimir Oltean
11915f6c2d49SVladimir Oltean return ds->ops->get_mm(ds, dp->index, state);
11925f6c2d49SVladimir Oltean }
11935f6c2d49SVladimir Oltean
dsa_slave_set_mm(struct net_device * dev,struct ethtool_mm_cfg * cfg,struct netlink_ext_ack * extack)11945f6c2d49SVladimir Oltean static int dsa_slave_set_mm(struct net_device *dev, struct ethtool_mm_cfg *cfg,
11955f6c2d49SVladimir Oltean struct netlink_ext_ack *extack)
11965f6c2d49SVladimir Oltean {
11975f6c2d49SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
11985f6c2d49SVladimir Oltean struct dsa_switch *ds = dp->ds;
11995f6c2d49SVladimir Oltean
12005f6c2d49SVladimir Oltean if (!ds->ops->set_mm)
12015f6c2d49SVladimir Oltean return -EOPNOTSUPP;
12025f6c2d49SVladimir Oltean
12035f6c2d49SVladimir Oltean return ds->ops->set_mm(ds, dp->index, cfg, extack);
12045f6c2d49SVladimir Oltean }
12055f6c2d49SVladimir Oltean
dsa_slave_get_mm_stats(struct net_device * dev,struct ethtool_mm_stats * stats)12065f6c2d49SVladimir Oltean static void dsa_slave_get_mm_stats(struct net_device *dev,
12075f6c2d49SVladimir Oltean struct ethtool_mm_stats *stats)
12085f6c2d49SVladimir Oltean {
12095f6c2d49SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
12105f6c2d49SVladimir Oltean struct dsa_switch *ds = dp->ds;
12115f6c2d49SVladimir Oltean
12125f6c2d49SVladimir Oltean if (ds->ops->get_mm_stats)
12135f6c2d49SVladimir Oltean ds->ops->get_mm_stats(ds, dp->index, stats);
12145f6c2d49SVladimir Oltean }
12155f6c2d49SVladimir Oltean
dsa_slave_get_wol(struct net_device * dev,struct ethtool_wolinfo * w)121619e57c4eSFlorian Fainelli static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
121719e57c4eSFlorian Fainelli {
1218d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1219d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
122019e57c4eSFlorian Fainelli
1221aab9c406SFlorian Fainelli phylink_ethtool_get_wol(dp->pl, w);
1222aab9c406SFlorian Fainelli
12239d490b4eSVivien Didelot if (ds->ops->get_wol)
1224d945097bSVivien Didelot ds->ops->get_wol(ds, dp->index, w);
122519e57c4eSFlorian Fainelli }
122619e57c4eSFlorian Fainelli
dsa_slave_set_wol(struct net_device * dev,struct ethtool_wolinfo * w)122719e57c4eSFlorian Fainelli static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
122819e57c4eSFlorian Fainelli {
1229d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1230d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
123119e57c4eSFlorian Fainelli int ret = -EOPNOTSUPP;
123219e57c4eSFlorian Fainelli
1233aab9c406SFlorian Fainelli phylink_ethtool_set_wol(dp->pl, w);
1234aab9c406SFlorian Fainelli
12359d490b4eSVivien Didelot if (ds->ops->set_wol)
1236d945097bSVivien Didelot ret = ds->ops->set_wol(ds, dp->index, w);
123719e57c4eSFlorian Fainelli
123819e57c4eSFlorian Fainelli return ret;
123919e57c4eSFlorian Fainelli }
124019e57c4eSFlorian Fainelli
dsa_slave_set_eee(struct net_device * dev,struct ethtool_eee * e)12417905288fSFlorian Fainelli static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
12427905288fSFlorian Fainelli {
1243d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1244d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
12457905288fSFlorian Fainelli int ret;
12467905288fSFlorian Fainelli
12477b9cc738SVivien Didelot /* Port's PHY and MAC both need to be EEE capable */
124800670cb8SDan Carpenter if (!dev->phydev || !dp->pl)
12497b9cc738SVivien Didelot return -ENODEV;
12507b9cc738SVivien Didelot
125108f50061SVivien Didelot if (!ds->ops->set_mac_eee)
12527905288fSFlorian Fainelli return -EOPNOTSUPP;
12537905288fSFlorian Fainelli
1254d945097bSVivien Didelot ret = ds->ops->set_mac_eee(ds, dp->index, e);
12557905288fSFlorian Fainelli if (ret)
12567905288fSFlorian Fainelli return ret;
12577905288fSFlorian Fainelli
1258aab9c406SFlorian Fainelli return phylink_ethtool_set_eee(dp->pl, e);
12597905288fSFlorian Fainelli }
12607905288fSFlorian Fainelli
dsa_slave_get_eee(struct net_device * dev,struct ethtool_eee * e)12617905288fSFlorian Fainelli static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
12627905288fSFlorian Fainelli {
1263d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1264d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
12657905288fSFlorian Fainelli int ret;
12667905288fSFlorian Fainelli
12677b9cc738SVivien Didelot /* Port's PHY and MAC both need to be EEE capable */
126800670cb8SDan Carpenter if (!dev->phydev || !dp->pl)
12697b9cc738SVivien Didelot return -ENODEV;
12707b9cc738SVivien Didelot
127108f50061SVivien Didelot if (!ds->ops->get_mac_eee)
12727905288fSFlorian Fainelli return -EOPNOTSUPP;
12737905288fSFlorian Fainelli
1274d945097bSVivien Didelot ret = ds->ops->get_mac_eee(ds, dp->index, e);
12757905288fSFlorian Fainelli if (ret)
12767905288fSFlorian Fainelli return ret;
12777905288fSFlorian Fainelli
1278aab9c406SFlorian Fainelli return phylink_ethtool_get_eee(dp->pl, e);
1279aab9c406SFlorian Fainelli }
1280aab9c406SFlorian Fainelli
dsa_slave_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * cmd)1281aab9c406SFlorian Fainelli static int dsa_slave_get_link_ksettings(struct net_device *dev,
1282aab9c406SFlorian Fainelli struct ethtool_link_ksettings *cmd)
1283aab9c406SFlorian Fainelli {
1284aab9c406SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(dev);
1285aab9c406SFlorian Fainelli
1286aab9c406SFlorian Fainelli return phylink_ethtool_ksettings_get(dp->pl, cmd);
1287aab9c406SFlorian Fainelli }
1288aab9c406SFlorian Fainelli
dsa_slave_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * cmd)1289aab9c406SFlorian Fainelli static int dsa_slave_set_link_ksettings(struct net_device *dev,
1290aab9c406SFlorian Fainelli const struct ethtool_link_ksettings *cmd)
1291aab9c406SFlorian Fainelli {
1292aab9c406SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(dev);
1293aab9c406SFlorian Fainelli
1294aab9c406SFlorian Fainelli return phylink_ethtool_ksettings_set(dp->pl, cmd);
12957905288fSFlorian Fainelli }
12967905288fSFlorian Fainelli
dsa_slave_get_pause_stats(struct net_device * dev,struct ethtool_pause_stats * pause_stats)12973d410403SOleksij Rempel static void dsa_slave_get_pause_stats(struct net_device *dev,
12983d410403SOleksij Rempel struct ethtool_pause_stats *pause_stats)
12993d410403SOleksij Rempel {
13003d410403SOleksij Rempel struct dsa_port *dp = dsa_slave_to_port(dev);
13013d410403SOleksij Rempel struct dsa_switch *ds = dp->ds;
13023d410403SOleksij Rempel
13033d410403SOleksij Rempel if (ds->ops->get_pause_stats)
13043d410403SOleksij Rempel ds->ops->get_pause_stats(ds, dp->index, pause_stats);
13053d410403SOleksij Rempel }
13063d410403SOleksij Rempel
dsa_slave_get_pauseparam(struct net_device * dev,struct ethtool_pauseparam * pause)1307a2a1a13bSHeiner Kallweit static void dsa_slave_get_pauseparam(struct net_device *dev,
1308a2a1a13bSHeiner Kallweit struct ethtool_pauseparam *pause)
1309a2a1a13bSHeiner Kallweit {
1310a2a1a13bSHeiner Kallweit struct dsa_port *dp = dsa_slave_to_port(dev);
1311a2a1a13bSHeiner Kallweit
1312a2a1a13bSHeiner Kallweit phylink_ethtool_get_pauseparam(dp->pl, pause);
1313a2a1a13bSHeiner Kallweit }
1314a2a1a13bSHeiner Kallweit
dsa_slave_set_pauseparam(struct net_device * dev,struct ethtool_pauseparam * pause)1315a2a1a13bSHeiner Kallweit static int dsa_slave_set_pauseparam(struct net_device *dev,
1316a2a1a13bSHeiner Kallweit struct ethtool_pauseparam *pause)
1317a2a1a13bSHeiner Kallweit {
1318a2a1a13bSHeiner Kallweit struct dsa_port *dp = dsa_slave_to_port(dev);
1319a2a1a13bSHeiner Kallweit
1320a2a1a13bSHeiner Kallweit return phylink_ethtool_set_pauseparam(dp->pl, pause);
1321a2a1a13bSHeiner Kallweit }
1322a2a1a13bSHeiner Kallweit
132304ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
dsa_slave_netpoll_setup(struct net_device * dev,struct netpoll_info * ni)132404ff53f9SFlorian Fainelli static int dsa_slave_netpoll_setup(struct net_device *dev,
132504ff53f9SFlorian Fainelli struct netpoll_info *ni)
132604ff53f9SFlorian Fainelli {
1327d0006b00SVivien Didelot struct net_device *master = dsa_slave_to_master(dev);
132804ff53f9SFlorian Fainelli struct dsa_slave_priv *p = netdev_priv(dev);
132904ff53f9SFlorian Fainelli struct netpoll *netpoll;
133004ff53f9SFlorian Fainelli int err = 0;
133104ff53f9SFlorian Fainelli
133204ff53f9SFlorian Fainelli netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
133304ff53f9SFlorian Fainelli if (!netpoll)
133404ff53f9SFlorian Fainelli return -ENOMEM;
133504ff53f9SFlorian Fainelli
133604ff53f9SFlorian Fainelli err = __netpoll_setup(netpoll, master);
133704ff53f9SFlorian Fainelli if (err) {
133804ff53f9SFlorian Fainelli kfree(netpoll);
133904ff53f9SFlorian Fainelli goto out;
134004ff53f9SFlorian Fainelli }
134104ff53f9SFlorian Fainelli
134204ff53f9SFlorian Fainelli p->netpoll = netpoll;
134304ff53f9SFlorian Fainelli out:
134404ff53f9SFlorian Fainelli return err;
134504ff53f9SFlorian Fainelli }
134604ff53f9SFlorian Fainelli
dsa_slave_netpoll_cleanup(struct net_device * dev)134704ff53f9SFlorian Fainelli static void dsa_slave_netpoll_cleanup(struct net_device *dev)
134804ff53f9SFlorian Fainelli {
134904ff53f9SFlorian Fainelli struct dsa_slave_priv *p = netdev_priv(dev);
135004ff53f9SFlorian Fainelli struct netpoll *netpoll = p->netpoll;
135104ff53f9SFlorian Fainelli
135204ff53f9SFlorian Fainelli if (!netpoll)
135304ff53f9SFlorian Fainelli return;
135404ff53f9SFlorian Fainelli
135504ff53f9SFlorian Fainelli p->netpoll = NULL;
135604ff53f9SFlorian Fainelli
1357c9fbd71fSDebabrata Banerjee __netpoll_free(netpoll);
135804ff53f9SFlorian Fainelli }
135904ff53f9SFlorian Fainelli
dsa_slave_poll_controller(struct net_device * dev)136004ff53f9SFlorian Fainelli static void dsa_slave_poll_controller(struct net_device *dev)
136104ff53f9SFlorian Fainelli {
136204ff53f9SFlorian Fainelli }
136304ff53f9SFlorian Fainelli #endif
136404ff53f9SFlorian Fainelli
1365f50f2127SFlorian Fainelli static struct dsa_mall_tc_entry *
dsa_slave_mall_tc_entry_find(struct net_device * dev,unsigned long cookie)13664fa7b718SVivien Didelot dsa_slave_mall_tc_entry_find(struct net_device *dev, unsigned long cookie)
1367f50f2127SFlorian Fainelli {
13684fa7b718SVivien Didelot struct dsa_slave_priv *p = netdev_priv(dev);
1369f50f2127SFlorian Fainelli struct dsa_mall_tc_entry *mall_tc_entry;
1370f50f2127SFlorian Fainelli
1371f50f2127SFlorian Fainelli list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list)
1372f50f2127SFlorian Fainelli if (mall_tc_entry->cookie == cookie)
1373f50f2127SFlorian Fainelli return mall_tc_entry;
1374f50f2127SFlorian Fainelli
1375f50f2127SFlorian Fainelli return NULL;
1376f50f2127SFlorian Fainelli }
1377f50f2127SFlorian Fainelli
1378e13c2075SVladimir Oltean static int
dsa_slave_add_cls_matchall_mirred(struct net_device * dev,struct tc_cls_matchall_offload * cls,bool ingress)1379e13c2075SVladimir Oltean dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
1380f50f2127SFlorian Fainelli struct tc_cls_matchall_offload *cls,
1381f50f2127SFlorian Fainelli bool ingress)
1382f50f2127SFlorian Fainelli {
13830148bb50SVladimir Oltean struct netlink_ext_ack *extack = cls->common.extack;
1384d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1385f50f2127SFlorian Fainelli struct dsa_slave_priv *p = netdev_priv(dev);
1386e13c2075SVladimir Oltean struct dsa_mall_mirror_tc_entry *mirror;
1387f50f2127SFlorian Fainelli struct dsa_mall_tc_entry *mall_tc_entry;
1388d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
13899681e8b3SPieter Jansen van Vuuren struct flow_action_entry *act;
1390d945097bSVivien Didelot struct dsa_port *to_dp;
1391e13c2075SVladimir Oltean int err;
1392e13c2075SVladimir Oltean
1393f50f2127SFlorian Fainelli if (!ds->ops->port_mirror_add)
139434297176SVladimir Oltean return -EOPNOTSUPP;
1395f50f2127SFlorian Fainelli
139653eca1f3SJakub Kicinski if (!flow_action_basic_hw_stats_check(&cls->rule->action,
1397319a1d19SJiri Pirko cls->common.extack))
139834297176SVladimir Oltean return -EOPNOTSUPP;
1399319a1d19SJiri Pirko
14009681e8b3SPieter Jansen van Vuuren act = &cls->rule->action.entries[0];
1401f50f2127SFlorian Fainelli
140265722159SVladimir Oltean if (!act->dev)
140365722159SVladimir Oltean return -EINVAL;
140465722159SVladimir Oltean
14059681e8b3SPieter Jansen van Vuuren if (!dsa_slave_dev_check(act->dev))
1406f50f2127SFlorian Fainelli return -EOPNOTSUPP;
1407f50f2127SFlorian Fainelli
1408f50f2127SFlorian Fainelli mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
1409f50f2127SFlorian Fainelli if (!mall_tc_entry)
1410f50f2127SFlorian Fainelli return -ENOMEM;
1411f50f2127SFlorian Fainelli
1412f50f2127SFlorian Fainelli mall_tc_entry->cookie = cls->cookie;
1413f50f2127SFlorian Fainelli mall_tc_entry->type = DSA_PORT_MALL_MIRROR;
1414f50f2127SFlorian Fainelli mirror = &mall_tc_entry->mirror;
1415f50f2127SFlorian Fainelli
14169681e8b3SPieter Jansen van Vuuren to_dp = dsa_slave_to_port(act->dev);
1417f50f2127SFlorian Fainelli
1418d945097bSVivien Didelot mirror->to_local_port = to_dp->index;
1419f50f2127SFlorian Fainelli mirror->ingress = ingress;
1420f50f2127SFlorian Fainelli
14210148bb50SVladimir Oltean err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress, extack);
1422f50f2127SFlorian Fainelli if (err) {
1423f50f2127SFlorian Fainelli kfree(mall_tc_entry);
1424f50f2127SFlorian Fainelli return err;
1425f50f2127SFlorian Fainelli }
1426f50f2127SFlorian Fainelli
1427f50f2127SFlorian Fainelli list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
1428e13c2075SVladimir Oltean
1429e13c2075SVladimir Oltean return err;
1430f50f2127SFlorian Fainelli }
1431f50f2127SFlorian Fainelli
143234297176SVladimir Oltean static int
dsa_slave_add_cls_matchall_police(struct net_device * dev,struct tc_cls_matchall_offload * cls,bool ingress)143334297176SVladimir Oltean dsa_slave_add_cls_matchall_police(struct net_device *dev,
143434297176SVladimir Oltean struct tc_cls_matchall_offload *cls,
143534297176SVladimir Oltean bool ingress)
143634297176SVladimir Oltean {
143734297176SVladimir Oltean struct netlink_ext_ack *extack = cls->common.extack;
143834297176SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
143934297176SVladimir Oltean struct dsa_slave_priv *p = netdev_priv(dev);
144034297176SVladimir Oltean struct dsa_mall_policer_tc_entry *policer;
144134297176SVladimir Oltean struct dsa_mall_tc_entry *mall_tc_entry;
144234297176SVladimir Oltean struct dsa_switch *ds = dp->ds;
144334297176SVladimir Oltean struct flow_action_entry *act;
144434297176SVladimir Oltean int err;
144534297176SVladimir Oltean
144634297176SVladimir Oltean if (!ds->ops->port_policer_add) {
144734297176SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
1448c75a33c8SJacob Keller "Policing offload not implemented");
144934297176SVladimir Oltean return -EOPNOTSUPP;
145034297176SVladimir Oltean }
145134297176SVladimir Oltean
145234297176SVladimir Oltean if (!ingress) {
145334297176SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
1454c75a33c8SJacob Keller "Only supported on ingress qdisc");
145534297176SVladimir Oltean return -EOPNOTSUPP;
145634297176SVladimir Oltean }
145734297176SVladimir Oltean
145834297176SVladimir Oltean if (!flow_action_basic_hw_stats_check(&cls->rule->action,
145934297176SVladimir Oltean cls->common.extack))
146034297176SVladimir Oltean return -EOPNOTSUPP;
146134297176SVladimir Oltean
146234297176SVladimir Oltean list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) {
146334297176SVladimir Oltean if (mall_tc_entry->type == DSA_PORT_MALL_POLICER) {
146434297176SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
1465c75a33c8SJacob Keller "Only one port policer allowed");
146634297176SVladimir Oltean return -EEXIST;
146734297176SVladimir Oltean }
146834297176SVladimir Oltean }
146934297176SVladimir Oltean
147034297176SVladimir Oltean act = &cls->rule->action.entries[0];
147134297176SVladimir Oltean
147234297176SVladimir Oltean mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
147334297176SVladimir Oltean if (!mall_tc_entry)
147434297176SVladimir Oltean return -ENOMEM;
147534297176SVladimir Oltean
147634297176SVladimir Oltean mall_tc_entry->cookie = cls->cookie;
147734297176SVladimir Oltean mall_tc_entry->type = DSA_PORT_MALL_POLICER;
147834297176SVladimir Oltean policer = &mall_tc_entry->policer;
147934297176SVladimir Oltean policer->rate_bytes_per_sec = act->police.rate_bytes_ps;
148034297176SVladimir Oltean policer->burst = act->police.burst;
148134297176SVladimir Oltean
148234297176SVladimir Oltean err = ds->ops->port_policer_add(ds, dp->index, policer);
148334297176SVladimir Oltean if (err) {
148434297176SVladimir Oltean kfree(mall_tc_entry);
148534297176SVladimir Oltean return err;
148634297176SVladimir Oltean }
148734297176SVladimir Oltean
148834297176SVladimir Oltean list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
148934297176SVladimir Oltean
149034297176SVladimir Oltean return err;
149134297176SVladimir Oltean }
149234297176SVladimir Oltean
dsa_slave_add_cls_matchall(struct net_device * dev,struct tc_cls_matchall_offload * cls,bool ingress)1493e13c2075SVladimir Oltean static int dsa_slave_add_cls_matchall(struct net_device *dev,
1494e13c2075SVladimir Oltean struct tc_cls_matchall_offload *cls,
1495e13c2075SVladimir Oltean bool ingress)
1496e13c2075SVladimir Oltean {
1497e13c2075SVladimir Oltean int err = -EOPNOTSUPP;
1498e13c2075SVladimir Oltean
1499e13c2075SVladimir Oltean if (cls->common.protocol == htons(ETH_P_ALL) &&
1500e13c2075SVladimir Oltean flow_offload_has_one_action(&cls->rule->action) &&
1501e13c2075SVladimir Oltean cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED)
1502e13c2075SVladimir Oltean err = dsa_slave_add_cls_matchall_mirred(dev, cls, ingress);
150334297176SVladimir Oltean else if (flow_offload_has_one_action(&cls->rule->action) &&
150434297176SVladimir Oltean cls->rule->action.entries[0].id == FLOW_ACTION_POLICE)
150534297176SVladimir Oltean err = dsa_slave_add_cls_matchall_police(dev, cls, ingress);
1506e13c2075SVladimir Oltean
1507e13c2075SVladimir Oltean return err;
1508f50f2127SFlorian Fainelli }
1509f50f2127SFlorian Fainelli
dsa_slave_del_cls_matchall(struct net_device * dev,struct tc_cls_matchall_offload * cls)1510f50f2127SFlorian Fainelli static void dsa_slave_del_cls_matchall(struct net_device *dev,
1511f50f2127SFlorian Fainelli struct tc_cls_matchall_offload *cls)
1512f50f2127SFlorian Fainelli {
1513d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1514f50f2127SFlorian Fainelli struct dsa_mall_tc_entry *mall_tc_entry;
1515d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
1516f50f2127SFlorian Fainelli
15174fa7b718SVivien Didelot mall_tc_entry = dsa_slave_mall_tc_entry_find(dev, cls->cookie);
1518f50f2127SFlorian Fainelli if (!mall_tc_entry)
1519f50f2127SFlorian Fainelli return;
1520f50f2127SFlorian Fainelli
1521f50f2127SFlorian Fainelli list_del(&mall_tc_entry->list);
1522f50f2127SFlorian Fainelli
1523f50f2127SFlorian Fainelli switch (mall_tc_entry->type) {
1524f50f2127SFlorian Fainelli case DSA_PORT_MALL_MIRROR:
152534297176SVladimir Oltean if (ds->ops->port_mirror_del)
152634297176SVladimir Oltean ds->ops->port_mirror_del(ds, dp->index,
152734297176SVladimir Oltean &mall_tc_entry->mirror);
152834297176SVladimir Oltean break;
152934297176SVladimir Oltean case DSA_PORT_MALL_POLICER:
153034297176SVladimir Oltean if (ds->ops->port_policer_del)
153134297176SVladimir Oltean ds->ops->port_policer_del(ds, dp->index);
1532f50f2127SFlorian Fainelli break;
1533f50f2127SFlorian Fainelli default:
1534f50f2127SFlorian Fainelli WARN_ON(1);
1535f50f2127SFlorian Fainelli }
1536f50f2127SFlorian Fainelli
1537f50f2127SFlorian Fainelli kfree(mall_tc_entry);
1538f50f2127SFlorian Fainelli }
1539f50f2127SFlorian Fainelli
dsa_slave_setup_tc_cls_matchall(struct net_device * dev,struct tc_cls_matchall_offload * cls,bool ingress)15403fbae382SJiri Pirko static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev,
15416b3eb752SJiri Pirko struct tc_cls_matchall_offload *cls,
15426b3eb752SJiri Pirko bool ingress)
1543f50f2127SFlorian Fainelli {
15445fd9fc4eSJiri Pirko if (cls->common.chain_index)
1545a5fcf8a6SJiri Pirko return -EOPNOTSUPP;
1546f50f2127SFlorian Fainelli
15473fbae382SJiri Pirko switch (cls->command) {
15483fbae382SJiri Pirko case TC_CLSMATCHALL_REPLACE:
15495fd9fc4eSJiri Pirko return dsa_slave_add_cls_matchall(dev, cls, ingress);
15503fbae382SJiri Pirko case TC_CLSMATCHALL_DESTROY:
15513fbae382SJiri Pirko dsa_slave_del_cls_matchall(dev, cls);
15523fbae382SJiri Pirko return 0;
15533fbae382SJiri Pirko default:
15543fbae382SJiri Pirko return -EOPNOTSUPP;
15553fbae382SJiri Pirko }
15563fbae382SJiri Pirko }
15573fbae382SJiri Pirko
dsa_slave_add_cls_flower(struct net_device * dev,struct flow_cls_offload * cls,bool ingress)1558ed11bb1fSVladimir Oltean static int dsa_slave_add_cls_flower(struct net_device *dev,
1559ed11bb1fSVladimir Oltean struct flow_cls_offload *cls,
1560ed11bb1fSVladimir Oltean bool ingress)
1561ed11bb1fSVladimir Oltean {
1562ed11bb1fSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
1563ed11bb1fSVladimir Oltean struct dsa_switch *ds = dp->ds;
1564ed11bb1fSVladimir Oltean int port = dp->index;
1565ed11bb1fSVladimir Oltean
1566ed11bb1fSVladimir Oltean if (!ds->ops->cls_flower_add)
1567ed11bb1fSVladimir Oltean return -EOPNOTSUPP;
1568ed11bb1fSVladimir Oltean
1569ed11bb1fSVladimir Oltean return ds->ops->cls_flower_add(ds, port, cls, ingress);
1570ed11bb1fSVladimir Oltean }
1571ed11bb1fSVladimir Oltean
dsa_slave_del_cls_flower(struct net_device * dev,struct flow_cls_offload * cls,bool ingress)1572ed11bb1fSVladimir Oltean static int dsa_slave_del_cls_flower(struct net_device *dev,
1573ed11bb1fSVladimir Oltean struct flow_cls_offload *cls,
1574ed11bb1fSVladimir Oltean bool ingress)
1575ed11bb1fSVladimir Oltean {
1576ed11bb1fSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
1577ed11bb1fSVladimir Oltean struct dsa_switch *ds = dp->ds;
1578ed11bb1fSVladimir Oltean int port = dp->index;
1579ed11bb1fSVladimir Oltean
1580ed11bb1fSVladimir Oltean if (!ds->ops->cls_flower_del)
1581ed11bb1fSVladimir Oltean return -EOPNOTSUPP;
1582ed11bb1fSVladimir Oltean
1583ed11bb1fSVladimir Oltean return ds->ops->cls_flower_del(ds, port, cls, ingress);
1584ed11bb1fSVladimir Oltean }
1585ed11bb1fSVladimir Oltean
dsa_slave_stats_cls_flower(struct net_device * dev,struct flow_cls_offload * cls,bool ingress)1586ed11bb1fSVladimir Oltean static int dsa_slave_stats_cls_flower(struct net_device *dev,
1587ed11bb1fSVladimir Oltean struct flow_cls_offload *cls,
1588ed11bb1fSVladimir Oltean bool ingress)
1589ed11bb1fSVladimir Oltean {
1590ed11bb1fSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
1591ed11bb1fSVladimir Oltean struct dsa_switch *ds = dp->ds;
1592ed11bb1fSVladimir Oltean int port = dp->index;
1593ed11bb1fSVladimir Oltean
1594ed11bb1fSVladimir Oltean if (!ds->ops->cls_flower_stats)
1595ed11bb1fSVladimir Oltean return -EOPNOTSUPP;
1596ed11bb1fSVladimir Oltean
1597ed11bb1fSVladimir Oltean return ds->ops->cls_flower_stats(ds, port, cls, ingress);
1598ed11bb1fSVladimir Oltean }
1599ed11bb1fSVladimir Oltean
dsa_slave_setup_tc_cls_flower(struct net_device * dev,struct flow_cls_offload * cls,bool ingress)1600ed11bb1fSVladimir Oltean static int dsa_slave_setup_tc_cls_flower(struct net_device *dev,
1601ed11bb1fSVladimir Oltean struct flow_cls_offload *cls,
1602ed11bb1fSVladimir Oltean bool ingress)
1603ed11bb1fSVladimir Oltean {
1604ed11bb1fSVladimir Oltean switch (cls->command) {
1605ed11bb1fSVladimir Oltean case FLOW_CLS_REPLACE:
1606ed11bb1fSVladimir Oltean return dsa_slave_add_cls_flower(dev, cls, ingress);
1607ed11bb1fSVladimir Oltean case FLOW_CLS_DESTROY:
1608ed11bb1fSVladimir Oltean return dsa_slave_del_cls_flower(dev, cls, ingress);
1609ed11bb1fSVladimir Oltean case FLOW_CLS_STATS:
1610ed11bb1fSVladimir Oltean return dsa_slave_stats_cls_flower(dev, cls, ingress);
1611ed11bb1fSVladimir Oltean default:
1612ed11bb1fSVladimir Oltean return -EOPNOTSUPP;
1613ed11bb1fSVladimir Oltean }
1614ed11bb1fSVladimir Oltean }
1615ed11bb1fSVladimir Oltean
dsa_slave_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv,bool ingress)16166b3eb752SJiri Pirko static int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
16176b3eb752SJiri Pirko void *cb_priv, bool ingress)
16186b3eb752SJiri Pirko {
16196b3eb752SJiri Pirko struct net_device *dev = cb_priv;
16206b3eb752SJiri Pirko
162144ae12a7SJiri Pirko if (!tc_can_offload(dev))
162244ae12a7SJiri Pirko return -EOPNOTSUPP;
162344ae12a7SJiri Pirko
16246b3eb752SJiri Pirko switch (type) {
16256b3eb752SJiri Pirko case TC_SETUP_CLSMATCHALL:
16266b3eb752SJiri Pirko return dsa_slave_setup_tc_cls_matchall(dev, type_data, ingress);
1627ed11bb1fSVladimir Oltean case TC_SETUP_CLSFLOWER:
1628ed11bb1fSVladimir Oltean return dsa_slave_setup_tc_cls_flower(dev, type_data, ingress);
16296b3eb752SJiri Pirko default:
16306b3eb752SJiri Pirko return -EOPNOTSUPP;
16316b3eb752SJiri Pirko }
16326b3eb752SJiri Pirko }
16336b3eb752SJiri Pirko
dsa_slave_setup_tc_block_cb_ig(enum tc_setup_type type,void * type_data,void * cb_priv)16346b3eb752SJiri Pirko static int dsa_slave_setup_tc_block_cb_ig(enum tc_setup_type type,
16356b3eb752SJiri Pirko void *type_data, void *cb_priv)
16366b3eb752SJiri Pirko {
16376b3eb752SJiri Pirko return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, true);
16386b3eb752SJiri Pirko }
16396b3eb752SJiri Pirko
dsa_slave_setup_tc_block_cb_eg(enum tc_setup_type type,void * type_data,void * cb_priv)16406b3eb752SJiri Pirko static int dsa_slave_setup_tc_block_cb_eg(enum tc_setup_type type,
16416b3eb752SJiri Pirko void *type_data, void *cb_priv)
16426b3eb752SJiri Pirko {
16436b3eb752SJiri Pirko return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, false);
16446b3eb752SJiri Pirko }
16456b3eb752SJiri Pirko
1646955bcb6eSPablo Neira Ayuso static LIST_HEAD(dsa_slave_block_cb_list);
1647955bcb6eSPablo Neira Ayuso
dsa_slave_setup_tc_block(struct net_device * dev,struct flow_block_offload * f)16486b3eb752SJiri Pirko static int dsa_slave_setup_tc_block(struct net_device *dev,
1649955bcb6eSPablo Neira Ayuso struct flow_block_offload *f)
16506b3eb752SJiri Pirko {
1651955bcb6eSPablo Neira Ayuso struct flow_block_cb *block_cb;
1652a7323311SPablo Neira Ayuso flow_setup_cb_t *cb;
16536b3eb752SJiri Pirko
165432f8c409SPablo Neira Ayuso if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
16556b3eb752SJiri Pirko cb = dsa_slave_setup_tc_block_cb_ig;
165632f8c409SPablo Neira Ayuso else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
16576b3eb752SJiri Pirko cb = dsa_slave_setup_tc_block_cb_eg;
16586b3eb752SJiri Pirko else
16596b3eb752SJiri Pirko return -EOPNOTSUPP;
16606b3eb752SJiri Pirko
1661955bcb6eSPablo Neira Ayuso f->driver_block_list = &dsa_slave_block_cb_list;
1662955bcb6eSPablo Neira Ayuso
16636b3eb752SJiri Pirko switch (f->command) {
16649c0e189eSPablo Neira Ayuso case FLOW_BLOCK_BIND:
16650d4fd02eSPablo Neira Ayuso if (flow_block_cb_is_busy(cb, dev, &dsa_slave_block_cb_list))
16660d4fd02eSPablo Neira Ayuso return -EBUSY;
16670d4fd02eSPablo Neira Ayuso
16680c7294ddSPablo Neira Ayuso block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
1669955bcb6eSPablo Neira Ayuso if (IS_ERR(block_cb))
1670955bcb6eSPablo Neira Ayuso return PTR_ERR(block_cb);
1671955bcb6eSPablo Neira Ayuso
1672955bcb6eSPablo Neira Ayuso flow_block_cb_add(block_cb, f);
1673955bcb6eSPablo Neira Ayuso list_add_tail(&block_cb->driver_list, &dsa_slave_block_cb_list);
1674955bcb6eSPablo Neira Ayuso return 0;
16759c0e189eSPablo Neira Ayuso case FLOW_BLOCK_UNBIND:
167614bfb13fSPablo Neira Ayuso block_cb = flow_block_cb_lookup(f->block, cb, dev);
1677955bcb6eSPablo Neira Ayuso if (!block_cb)
1678955bcb6eSPablo Neira Ayuso return -ENOENT;
1679955bcb6eSPablo Neira Ayuso
1680955bcb6eSPablo Neira Ayuso flow_block_cb_remove(block_cb, f);
1681955bcb6eSPablo Neira Ayuso list_del(&block_cb->driver_list);
16826b3eb752SJiri Pirko return 0;
16836b3eb752SJiri Pirko default:
16846b3eb752SJiri Pirko return -EOPNOTSUPP;
16856b3eb752SJiri Pirko }
16866b3eb752SJiri Pirko }
16876b3eb752SJiri Pirko
dsa_slave_setup_ft_block(struct dsa_switch * ds,int port,void * type_data)16883fb24a43SPablo Neira Ayuso static int dsa_slave_setup_ft_block(struct dsa_switch *ds, int port,
16893fb24a43SPablo Neira Ayuso void *type_data)
16903fb24a43SPablo Neira Ayuso {
16918f6a19c0SVladimir Oltean struct net_device *master = dsa_port_to_master(dsa_to_port(ds, port));
16923fb24a43SPablo Neira Ayuso
16933fb24a43SPablo Neira Ayuso if (!master->netdev_ops->ndo_setup_tc)
16943fb24a43SPablo Neira Ayuso return -EOPNOTSUPP;
16953fb24a43SPablo Neira Ayuso
16963fb24a43SPablo Neira Ayuso return master->netdev_ops->ndo_setup_tc(master, TC_SETUP_FT, type_data);
16973fb24a43SPablo Neira Ayuso }
16983fb24a43SPablo Neira Ayuso
dsa_slave_setup_tc(struct net_device * dev,enum tc_setup_type type,void * type_data)16993fbae382SJiri Pirko static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
1700de4784caSJiri Pirko void *type_data)
17013fbae382SJiri Pirko {
170247d23af2SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
170347d23af2SVladimir Oltean struct dsa_switch *ds = dp->ds;
170447d23af2SVladimir Oltean
17053fb24a43SPablo Neira Ayuso switch (type) {
17063fb24a43SPablo Neira Ayuso case TC_SETUP_BLOCK:
17076b3eb752SJiri Pirko return dsa_slave_setup_tc_block(dev, type_data);
17083fb24a43SPablo Neira Ayuso case TC_SETUP_FT:
17093fb24a43SPablo Neira Ayuso return dsa_slave_setup_ft_block(ds, dp->index, type_data);
17103fb24a43SPablo Neira Ayuso default:
17113fb24a43SPablo Neira Ayuso break;
17123fb24a43SPablo Neira Ayuso }
171347d23af2SVladimir Oltean
171447d23af2SVladimir Oltean if (!ds->ops->port_setup_tc)
1715a5fcf8a6SJiri Pirko return -EOPNOTSUPP;
171647d23af2SVladimir Oltean
171747d23af2SVladimir Oltean return ds->ops->port_setup_tc(ds, dp->index, type, type_data);
1718f50f2127SFlorian Fainelli }
1719f50f2127SFlorian Fainelli
dsa_slave_get_rxnfc(struct net_device * dev,struct ethtool_rxnfc * nfc,u32 * rule_locs)1720bf9f2648SFlorian Fainelli static int dsa_slave_get_rxnfc(struct net_device *dev,
1721bf9f2648SFlorian Fainelli struct ethtool_rxnfc *nfc, u32 *rule_locs)
1722bf9f2648SFlorian Fainelli {
1723d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1724d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
1725bf9f2648SFlorian Fainelli
1726bf9f2648SFlorian Fainelli if (!ds->ops->get_rxnfc)
1727bf9f2648SFlorian Fainelli return -EOPNOTSUPP;
1728bf9f2648SFlorian Fainelli
1729d945097bSVivien Didelot return ds->ops->get_rxnfc(ds, dp->index, nfc, rule_locs);
1730bf9f2648SFlorian Fainelli }
1731bf9f2648SFlorian Fainelli
dsa_slave_set_rxnfc(struct net_device * dev,struct ethtool_rxnfc * nfc)1732bf9f2648SFlorian Fainelli static int dsa_slave_set_rxnfc(struct net_device *dev,
1733bf9f2648SFlorian Fainelli struct ethtool_rxnfc *nfc)
1734bf9f2648SFlorian Fainelli {
1735d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev);
1736d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
1737bf9f2648SFlorian Fainelli
1738bf9f2648SFlorian Fainelli if (!ds->ops->set_rxnfc)
1739bf9f2648SFlorian Fainelli return -EOPNOTSUPP;
1740bf9f2648SFlorian Fainelli
1741d945097bSVivien Didelot return ds->ops->set_rxnfc(ds, dp->index, nfc);
1742bf9f2648SFlorian Fainelli }
1743bf9f2648SFlorian Fainelli
dsa_slave_get_ts_info(struct net_device * dev,struct ethtool_ts_info * ts)17440336369dSBrandon Streiff static int dsa_slave_get_ts_info(struct net_device *dev,
17450336369dSBrandon Streiff struct ethtool_ts_info *ts)
17460336369dSBrandon Streiff {
17470336369dSBrandon Streiff struct dsa_slave_priv *p = netdev_priv(dev);
17480336369dSBrandon Streiff struct dsa_switch *ds = p->dp->ds;
17490336369dSBrandon Streiff
17500336369dSBrandon Streiff if (!ds->ops->get_ts_info)
17510336369dSBrandon Streiff return -EOPNOTSUPP;
17520336369dSBrandon Streiff
17530336369dSBrandon Streiff return ds->ops->get_ts_info(ds, p->dp->index, ts);
17540336369dSBrandon Streiff }
17550336369dSBrandon Streiff
dsa_slave_vlan_rx_add_vid(struct net_device * dev,__be16 proto,u16 vid)1756061f6a50SFlorian Fainelli static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
1757061f6a50SFlorian Fainelli u16 vid)
1758061f6a50SFlorian Fainelli {
1759061f6a50SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(dev);
176088236591SVladimir Oltean struct switchdev_obj_port_vlan vlan = {
176188236591SVladimir Oltean .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
1762b7a9e0daSVladimir Oltean .vid = vid,
176388236591SVladimir Oltean /* This API only allows programming tagged, non-PVID VIDs */
176488236591SVladimir Oltean .flags = 0,
176588236591SVladimir Oltean };
176631046a5fSVladimir Oltean struct netlink_ext_ack extack = {0};
176764fdc5f3SVladimir Oltean struct dsa_switch *ds = dp->ds;
176864fdc5f3SVladimir Oltean struct netdev_hw_addr *ha;
1769d06f925fSVladimir Oltean struct dsa_vlan *v;
1770061f6a50SFlorian Fainelli int ret;
1771061f6a50SFlorian Fainelli
177288236591SVladimir Oltean /* User port... */
177331046a5fSVladimir Oltean ret = dsa_port_vlan_add(dp, &vlan, &extack);
177431046a5fSVladimir Oltean if (ret) {
177531046a5fSVladimir Oltean if (extack._msg)
177631046a5fSVladimir Oltean netdev_err(dev, "%s\n", extack._msg);
177788236591SVladimir Oltean return ret;
177831046a5fSVladimir Oltean }
177988236591SVladimir Oltean
178088236591SVladimir Oltean /* And CPU port... */
1781134ef238SVladimir Oltean ret = dsa_port_host_vlan_add(dp, &vlan, &extack);
178231046a5fSVladimir Oltean if (ret) {
178331046a5fSVladimir Oltean if (extack._msg)
178431046a5fSVladimir Oltean netdev_err(dev, "CPU port %d: %s\n", dp->cpu_dp->index,
178531046a5fSVladimir Oltean extack._msg);
17867e1741b4SVivien Didelot return ret;
178731046a5fSVladimir Oltean }
17887e1741b4SVivien Didelot
178964fdc5f3SVladimir Oltean if (!dsa_switch_supports_uc_filtering(ds) &&
179064fdc5f3SVladimir Oltean !dsa_switch_supports_mc_filtering(ds))
179164fdc5f3SVladimir Oltean return 0;
179264fdc5f3SVladimir Oltean
1793d06f925fSVladimir Oltean v = kzalloc(sizeof(*v), GFP_KERNEL);
1794d06f925fSVladimir Oltean if (!v) {
1795d06f925fSVladimir Oltean ret = -ENOMEM;
1796d06f925fSVladimir Oltean goto rollback;
1797d06f925fSVladimir Oltean }
1798d06f925fSVladimir Oltean
179964fdc5f3SVladimir Oltean netif_addr_lock_bh(dev);
180064fdc5f3SVladimir Oltean
1801d06f925fSVladimir Oltean v->vid = vid;
1802d06f925fSVladimir Oltean list_add_tail(&v->list, &dp->user_vlans);
1803d06f925fSVladimir Oltean
180464fdc5f3SVladimir Oltean if (dsa_switch_supports_mc_filtering(ds)) {
180564fdc5f3SVladimir Oltean netdev_for_each_synced_mc_addr(ha, dev) {
180664fdc5f3SVladimir Oltean dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD,
180764fdc5f3SVladimir Oltean ha->addr, vid);
180864fdc5f3SVladimir Oltean }
180964fdc5f3SVladimir Oltean }
181064fdc5f3SVladimir Oltean
181164fdc5f3SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds)) {
181264fdc5f3SVladimir Oltean netdev_for_each_synced_uc_addr(ha, dev) {
181364fdc5f3SVladimir Oltean dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD,
181464fdc5f3SVladimir Oltean ha->addr, vid);
181564fdc5f3SVladimir Oltean }
181664fdc5f3SVladimir Oltean }
181764fdc5f3SVladimir Oltean
181864fdc5f3SVladimir Oltean netif_addr_unlock_bh(dev);
181964fdc5f3SVladimir Oltean
182064fdc5f3SVladimir Oltean dsa_flush_workqueue();
182164fdc5f3SVladimir Oltean
1822134ef238SVladimir Oltean return 0;
1823d06f925fSVladimir Oltean
1824d06f925fSVladimir Oltean rollback:
1825d06f925fSVladimir Oltean dsa_port_host_vlan_del(dp, &vlan);
1826d06f925fSVladimir Oltean dsa_port_vlan_del(dp, &vlan);
1827d06f925fSVladimir Oltean
1828d06f925fSVladimir Oltean return ret;
1829061f6a50SFlorian Fainelli }
1830061f6a50SFlorian Fainelli
dsa_slave_vlan_rx_kill_vid(struct net_device * dev,__be16 proto,u16 vid)1831061f6a50SFlorian Fainelli static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
1832061f6a50SFlorian Fainelli u16 vid)
1833061f6a50SFlorian Fainelli {
1834061f6a50SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(dev);
183588236591SVladimir Oltean struct switchdev_obj_port_vlan vlan = {
1836b7a9e0daSVladimir Oltean .vid = vid,
183788236591SVladimir Oltean /* This API only allows programming tagged, non-PVID VIDs */
183888236591SVladimir Oltean .flags = 0,
183988236591SVladimir Oltean };
184064fdc5f3SVladimir Oltean struct dsa_switch *ds = dp->ds;
184164fdc5f3SVladimir Oltean struct netdev_hw_addr *ha;
1842d06f925fSVladimir Oltean struct dsa_vlan *v;
18432209158cSVladimir Oltean int err;
1844061f6a50SFlorian Fainelli
18452209158cSVladimir Oltean err = dsa_port_vlan_del(dp, &vlan);
18462209158cSVladimir Oltean if (err)
18472209158cSVladimir Oltean return err;
18482209158cSVladimir Oltean
184964fdc5f3SVladimir Oltean err = dsa_port_host_vlan_del(dp, &vlan);
185064fdc5f3SVladimir Oltean if (err)
185164fdc5f3SVladimir Oltean return err;
185264fdc5f3SVladimir Oltean
185364fdc5f3SVladimir Oltean if (!dsa_switch_supports_uc_filtering(ds) &&
185464fdc5f3SVladimir Oltean !dsa_switch_supports_mc_filtering(ds))
185564fdc5f3SVladimir Oltean return 0;
185664fdc5f3SVladimir Oltean
185764fdc5f3SVladimir Oltean netif_addr_lock_bh(dev);
185864fdc5f3SVladimir Oltean
1859d06f925fSVladimir Oltean v = dsa_vlan_find(&dp->user_vlans, &vlan);
1860d06f925fSVladimir Oltean if (!v) {
1861d06f925fSVladimir Oltean netif_addr_unlock_bh(dev);
1862d06f925fSVladimir Oltean return -ENOENT;
1863d06f925fSVladimir Oltean }
1864d06f925fSVladimir Oltean
1865d06f925fSVladimir Oltean list_del(&v->list);
1866d06f925fSVladimir Oltean kfree(v);
1867d06f925fSVladimir Oltean
186864fdc5f3SVladimir Oltean if (dsa_switch_supports_mc_filtering(ds)) {
186964fdc5f3SVladimir Oltean netdev_for_each_synced_mc_addr(ha, dev) {
187064fdc5f3SVladimir Oltean dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL,
187164fdc5f3SVladimir Oltean ha->addr, vid);
187264fdc5f3SVladimir Oltean }
187364fdc5f3SVladimir Oltean }
187464fdc5f3SVladimir Oltean
187564fdc5f3SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds)) {
187664fdc5f3SVladimir Oltean netdev_for_each_synced_uc_addr(ha, dev) {
187764fdc5f3SVladimir Oltean dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL,
187864fdc5f3SVladimir Oltean ha->addr, vid);
187964fdc5f3SVladimir Oltean }
188064fdc5f3SVladimir Oltean }
188164fdc5f3SVladimir Oltean
188264fdc5f3SVladimir Oltean netif_addr_unlock_bh(dev);
188364fdc5f3SVladimir Oltean
188464fdc5f3SVladimir Oltean dsa_flush_workqueue();
188564fdc5f3SVladimir Oltean
188664fdc5f3SVladimir Oltean return 0;
1887061f6a50SFlorian Fainelli }
1888061f6a50SFlorian Fainelli
dsa_slave_restore_vlan(struct net_device * vdev,int vid,void * arg)188906cfb2dfSVladimir Oltean static int dsa_slave_restore_vlan(struct net_device *vdev, int vid, void *arg)
189006cfb2dfSVladimir Oltean {
189106cfb2dfSVladimir Oltean __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q);
189206cfb2dfSVladimir Oltean
189306cfb2dfSVladimir Oltean return dsa_slave_vlan_rx_add_vid(arg, proto, vid);
189406cfb2dfSVladimir Oltean }
189506cfb2dfSVladimir Oltean
dsa_slave_clear_vlan(struct net_device * vdev,int vid,void * arg)189606cfb2dfSVladimir Oltean static int dsa_slave_clear_vlan(struct net_device *vdev, int vid, void *arg)
189706cfb2dfSVladimir Oltean {
189806cfb2dfSVladimir Oltean __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q);
189906cfb2dfSVladimir Oltean
190006cfb2dfSVladimir Oltean return dsa_slave_vlan_rx_kill_vid(arg, proto, vid);
190106cfb2dfSVladimir Oltean }
190206cfb2dfSVladimir Oltean
190306cfb2dfSVladimir Oltean /* Keep the VLAN RX filtering list in sync with the hardware only if VLAN
190406cfb2dfSVladimir Oltean * filtering is enabled. The baseline is that only ports that offload a
190506cfb2dfSVladimir Oltean * VLAN-aware bridge are VLAN-aware, and standalone ports are VLAN-unaware,
190606cfb2dfSVladimir Oltean * but there are exceptions for quirky hardware.
190706cfb2dfSVladimir Oltean *
190806cfb2dfSVladimir Oltean * If ds->vlan_filtering_is_global = true, then standalone ports which share
190906cfb2dfSVladimir Oltean * the same switch with other ports that offload a VLAN-aware bridge are also
191006cfb2dfSVladimir Oltean * inevitably VLAN-aware.
191106cfb2dfSVladimir Oltean *
191206cfb2dfSVladimir Oltean * To summarize, a DSA switch port offloads:
191306cfb2dfSVladimir Oltean *
191406cfb2dfSVladimir Oltean * - If standalone (this includes software bridge, software LAG):
191558adf9dcSVladimir Oltean * - if ds->needs_standalone_vlan_filtering = true, OR if
191658adf9dcSVladimir Oltean * (ds->vlan_filtering_is_global = true AND there are bridges spanning
191758adf9dcSVladimir Oltean * this switch chip which have vlan_filtering=1)
191806cfb2dfSVladimir Oltean * - the 8021q upper VLANs
191958adf9dcSVladimir Oltean * - else (standalone VLAN filtering is not needed, VLAN filtering is not
192058adf9dcSVladimir Oltean * global, or it is, but no port is under a VLAN-aware bridge):
192106cfb2dfSVladimir Oltean * - no VLAN (any 8021q upper is a software VLAN)
192206cfb2dfSVladimir Oltean *
192306cfb2dfSVladimir Oltean * - If under a vlan_filtering=0 bridge which it offload:
192406cfb2dfSVladimir Oltean * - if ds->configure_vlan_while_not_filtering = true (default):
192506cfb2dfSVladimir Oltean * - the bridge VLANs. These VLANs are committed to hardware but inactive.
192606cfb2dfSVladimir Oltean * - else (deprecated):
192706cfb2dfSVladimir Oltean * - no VLAN. The bridge VLANs are not restored when VLAN awareness is
192806cfb2dfSVladimir Oltean * enabled, so this behavior is broken and discouraged.
192906cfb2dfSVladimir Oltean *
193006cfb2dfSVladimir Oltean * - If under a vlan_filtering=1 bridge which it offload:
193106cfb2dfSVladimir Oltean * - the bridge VLANs
193206cfb2dfSVladimir Oltean * - the 8021q upper VLANs
193306cfb2dfSVladimir Oltean */
dsa_slave_manage_vlan_filtering(struct net_device * slave,bool vlan_filtering)193406cfb2dfSVladimir Oltean int dsa_slave_manage_vlan_filtering(struct net_device *slave,
193506cfb2dfSVladimir Oltean bool vlan_filtering)
193606cfb2dfSVladimir Oltean {
193706cfb2dfSVladimir Oltean int err;
193806cfb2dfSVladimir Oltean
193906cfb2dfSVladimir Oltean if (vlan_filtering) {
194006cfb2dfSVladimir Oltean slave->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
194106cfb2dfSVladimir Oltean
194206cfb2dfSVladimir Oltean err = vlan_for_each(slave, dsa_slave_restore_vlan, slave);
194306cfb2dfSVladimir Oltean if (err) {
194406cfb2dfSVladimir Oltean vlan_for_each(slave, dsa_slave_clear_vlan, slave);
194506cfb2dfSVladimir Oltean slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
194606cfb2dfSVladimir Oltean return err;
194706cfb2dfSVladimir Oltean }
194806cfb2dfSVladimir Oltean } else {
194906cfb2dfSVladimir Oltean err = vlan_for_each(slave, dsa_slave_clear_vlan, slave);
195006cfb2dfSVladimir Oltean if (err)
195106cfb2dfSVladimir Oltean return err;
195206cfb2dfSVladimir Oltean
195306cfb2dfSVladimir Oltean slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
195406cfb2dfSVladimir Oltean }
195506cfb2dfSVladimir Oltean
195606cfb2dfSVladimir Oltean return 0;
195706cfb2dfSVladimir Oltean }
195806cfb2dfSVladimir Oltean
1959bff33f7eSVladimir Oltean struct dsa_hw_port {
1960bff33f7eSVladimir Oltean struct list_head list;
1961bff33f7eSVladimir Oltean struct net_device *dev;
1962bff33f7eSVladimir Oltean int old_mtu;
1963bff33f7eSVladimir Oltean };
1964bff33f7eSVladimir Oltean
dsa_hw_port_list_set_mtu(struct list_head * hw_port_list,int mtu)1965bff33f7eSVladimir Oltean static int dsa_hw_port_list_set_mtu(struct list_head *hw_port_list, int mtu)
1966bff33f7eSVladimir Oltean {
1967bff33f7eSVladimir Oltean const struct dsa_hw_port *p;
1968bff33f7eSVladimir Oltean int err;
1969bff33f7eSVladimir Oltean
1970bff33f7eSVladimir Oltean list_for_each_entry(p, hw_port_list, list) {
1971bff33f7eSVladimir Oltean if (p->dev->mtu == mtu)
1972bff33f7eSVladimir Oltean continue;
1973bff33f7eSVladimir Oltean
1974bff33f7eSVladimir Oltean err = dev_set_mtu(p->dev, mtu);
1975bff33f7eSVladimir Oltean if (err)
1976bff33f7eSVladimir Oltean goto rollback;
1977bff33f7eSVladimir Oltean }
1978bff33f7eSVladimir Oltean
1979bff33f7eSVladimir Oltean return 0;
1980bff33f7eSVladimir Oltean
1981bff33f7eSVladimir Oltean rollback:
1982bff33f7eSVladimir Oltean list_for_each_entry_continue_reverse(p, hw_port_list, list) {
1983bff33f7eSVladimir Oltean if (p->dev->mtu == p->old_mtu)
1984bff33f7eSVladimir Oltean continue;
1985bff33f7eSVladimir Oltean
1986bff33f7eSVladimir Oltean if (dev_set_mtu(p->dev, p->old_mtu))
1987bff33f7eSVladimir Oltean netdev_err(p->dev, "Failed to restore MTU\n");
1988bff33f7eSVladimir Oltean }
1989bff33f7eSVladimir Oltean
1990bff33f7eSVladimir Oltean return err;
1991bff33f7eSVladimir Oltean }
1992bff33f7eSVladimir Oltean
dsa_hw_port_list_free(struct list_head * hw_port_list)1993bff33f7eSVladimir Oltean static void dsa_hw_port_list_free(struct list_head *hw_port_list)
1994bff33f7eSVladimir Oltean {
1995bff33f7eSVladimir Oltean struct dsa_hw_port *p, *n;
1996bff33f7eSVladimir Oltean
1997bff33f7eSVladimir Oltean list_for_each_entry_safe(p, n, hw_port_list, list)
1998bff33f7eSVladimir Oltean kfree(p);
1999bff33f7eSVladimir Oltean }
2000bff33f7eSVladimir Oltean
2001bff33f7eSVladimir Oltean /* Make the hardware datapath to/from @dev limited to a common MTU */
dsa_bridge_mtu_normalization(struct dsa_port * dp)2002bf88dc32Skbuild test robot static void dsa_bridge_mtu_normalization(struct dsa_port *dp)
2003bff33f7eSVladimir Oltean {
2004bff33f7eSVladimir Oltean struct list_head hw_port_list;
2005bff33f7eSVladimir Oltean struct dsa_switch_tree *dst;
2006bff33f7eSVladimir Oltean int min_mtu = ETH_MAX_MTU;
2007bff33f7eSVladimir Oltean struct dsa_port *other_dp;
2008bff33f7eSVladimir Oltean int err;
2009bff33f7eSVladimir Oltean
2010bff33f7eSVladimir Oltean if (!dp->ds->mtu_enforcement_ingress)
2011bff33f7eSVladimir Oltean return;
2012bff33f7eSVladimir Oltean
2013d3eed0e5SVladimir Oltean if (!dp->bridge)
2014bff33f7eSVladimir Oltean return;
2015bff33f7eSVladimir Oltean
2016bff33f7eSVladimir Oltean INIT_LIST_HEAD(&hw_port_list);
2017bff33f7eSVladimir Oltean
2018bff33f7eSVladimir Oltean /* Populate the list of ports that are part of the same bridge
2019bff33f7eSVladimir Oltean * as the newly added/modified port
2020bff33f7eSVladimir Oltean */
2021bff33f7eSVladimir Oltean list_for_each_entry(dst, &dsa_tree_list, list) {
2022bff33f7eSVladimir Oltean list_for_each_entry(other_dp, &dst->ports, list) {
2023bff33f7eSVladimir Oltean struct dsa_hw_port *hw_port;
2024bff33f7eSVladimir Oltean struct net_device *slave;
2025bff33f7eSVladimir Oltean
2026bff33f7eSVladimir Oltean if (other_dp->type != DSA_PORT_TYPE_USER)
2027bff33f7eSVladimir Oltean continue;
2028bff33f7eSVladimir Oltean
202936cbf39bSVladimir Oltean if (!dsa_port_bridge_same(dp, other_dp))
2030bff33f7eSVladimir Oltean continue;
2031bff33f7eSVladimir Oltean
2032bff33f7eSVladimir Oltean if (!other_dp->ds->mtu_enforcement_ingress)
2033bff33f7eSVladimir Oltean continue;
2034bff33f7eSVladimir Oltean
2035bff33f7eSVladimir Oltean slave = other_dp->slave;
2036bff33f7eSVladimir Oltean
2037bff33f7eSVladimir Oltean if (min_mtu > slave->mtu)
2038bff33f7eSVladimir Oltean min_mtu = slave->mtu;
2039bff33f7eSVladimir Oltean
2040bff33f7eSVladimir Oltean hw_port = kzalloc(sizeof(*hw_port), GFP_KERNEL);
2041bff33f7eSVladimir Oltean if (!hw_port)
2042bff33f7eSVladimir Oltean goto out;
2043bff33f7eSVladimir Oltean
2044bff33f7eSVladimir Oltean hw_port->dev = slave;
2045bff33f7eSVladimir Oltean hw_port->old_mtu = slave->mtu;
2046bff33f7eSVladimir Oltean
2047bff33f7eSVladimir Oltean list_add(&hw_port->list, &hw_port_list);
2048bff33f7eSVladimir Oltean }
2049bff33f7eSVladimir Oltean }
2050bff33f7eSVladimir Oltean
2051bff33f7eSVladimir Oltean /* Attempt to configure the entire hardware bridge to the newly added
2052bff33f7eSVladimir Oltean * interface's MTU first, regardless of whether the intention of the
2053bff33f7eSVladimir Oltean * user was to raise or lower it.
2054bff33f7eSVladimir Oltean */
2055bff33f7eSVladimir Oltean err = dsa_hw_port_list_set_mtu(&hw_port_list, dp->slave->mtu);
2056bff33f7eSVladimir Oltean if (!err)
2057bff33f7eSVladimir Oltean goto out;
2058bff33f7eSVladimir Oltean
2059bff33f7eSVladimir Oltean /* Clearly that didn't work out so well, so just set the minimum MTU on
2060bff33f7eSVladimir Oltean * all hardware bridge ports now. If this fails too, then all ports will
2061bff33f7eSVladimir Oltean * still have their old MTU rolled back anyway.
2062bff33f7eSVladimir Oltean */
2063bff33f7eSVladimir Oltean dsa_hw_port_list_set_mtu(&hw_port_list, min_mtu);
2064bff33f7eSVladimir Oltean
2065bff33f7eSVladimir Oltean out:
2066bff33f7eSVladimir Oltean dsa_hw_port_list_free(&hw_port_list);
2067bff33f7eSVladimir Oltean }
2068bff33f7eSVladimir Oltean
dsa_slave_change_mtu(struct net_device * dev,int new_mtu)206953da0ebaSVladimir Oltean int dsa_slave_change_mtu(struct net_device *dev, int new_mtu)
2070bfcb8132SVladimir Oltean {
2071bfcb8132SVladimir Oltean struct net_device *master = dsa_slave_to_master(dev);
2072bfcb8132SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
2073cf1c39d3SVladimir Oltean struct dsa_port *cpu_dp = dp->cpu_dp;
20744715029fSVladimir Oltean struct dsa_switch *ds = dp->ds;
2075b2033a05SVladimir Oltean struct dsa_port *other_dp;
2076bfcb8132SVladimir Oltean int largest_mtu = 0;
2077bfcb8132SVladimir Oltean int new_master_mtu;
2078bfcb8132SVladimir Oltean int old_master_mtu;
2079bfcb8132SVladimir Oltean int mtu_limit;
2080636e8adfSVladimir Oltean int overhead;
2081bfcb8132SVladimir Oltean int cpu_mtu;
20824e4ab795SVladimir Oltean int err;
2083bfcb8132SVladimir Oltean
2084bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu)
2085bfcb8132SVladimir Oltean return -EOPNOTSUPP;
2086bfcb8132SVladimir Oltean
2087b2033a05SVladimir Oltean dsa_tree_for_each_user_port(other_dp, ds->dst) {
2088bfcb8132SVladimir Oltean int slave_mtu;
2089bfcb8132SVladimir Oltean
2090bfcb8132SVladimir Oltean /* During probe, this function will be called for each slave
2091bfcb8132SVladimir Oltean * device, while not all of them have been allocated. That's
2092bfcb8132SVladimir Oltean * ok, it doesn't change what the maximum is, so ignore it.
2093bfcb8132SVladimir Oltean */
2094b2033a05SVladimir Oltean if (!other_dp->slave)
2095bfcb8132SVladimir Oltean continue;
2096bfcb8132SVladimir Oltean
2097bfcb8132SVladimir Oltean /* Pretend that we already applied the setting, which we
2098bfcb8132SVladimir Oltean * actually haven't (still haven't done all integrity checks)
2099bfcb8132SVladimir Oltean */
2100b2033a05SVladimir Oltean if (dp == other_dp)
2101bfcb8132SVladimir Oltean slave_mtu = new_mtu;
2102bfcb8132SVladimir Oltean else
2103b2033a05SVladimir Oltean slave_mtu = other_dp->slave->mtu;
2104bfcb8132SVladimir Oltean
2105bfcb8132SVladimir Oltean if (largest_mtu < slave_mtu)
2106bfcb8132SVladimir Oltean largest_mtu = slave_mtu;
2107bfcb8132SVladimir Oltean }
2108bfcb8132SVladimir Oltean
2109636e8adfSVladimir Oltean overhead = dsa_tag_protocol_overhead(cpu_dp->tag_ops);
2110636e8adfSVladimir Oltean mtu_limit = min_t(int, master->max_mtu, dev->max_mtu + overhead);
2111bfcb8132SVladimir Oltean old_master_mtu = master->mtu;
2112636e8adfSVladimir Oltean new_master_mtu = largest_mtu + overhead;
2113bfcb8132SVladimir Oltean if (new_master_mtu > mtu_limit)
2114bfcb8132SVladimir Oltean return -ERANGE;
2115bfcb8132SVladimir Oltean
2116bfcb8132SVladimir Oltean /* If the master MTU isn't over limit, there's no need to check the CPU
2117bfcb8132SVladimir Oltean * MTU, since that surely isn't either.
2118bfcb8132SVladimir Oltean */
2119bfcb8132SVladimir Oltean cpu_mtu = largest_mtu;
2120bfcb8132SVladimir Oltean
2121bfcb8132SVladimir Oltean /* Start applying stuff */
2122bfcb8132SVladimir Oltean if (new_master_mtu != old_master_mtu) {
2123bfcb8132SVladimir Oltean err = dev_set_mtu(master, new_master_mtu);
2124bfcb8132SVladimir Oltean if (err < 0)
2125bfcb8132SVladimir Oltean goto out_master_failed;
2126bfcb8132SVladimir Oltean
2127bfcb8132SVladimir Oltean /* We only need to propagate the MTU of the CPU port to
2128be6ff966SVladimir Oltean * upstream switches, so emit a notifier which updates them.
2129bfcb8132SVladimir Oltean */
2130be6ff966SVladimir Oltean err = dsa_port_mtu_change(cpu_dp, cpu_mtu);
2131bfcb8132SVladimir Oltean if (err)
2132bfcb8132SVladimir Oltean goto out_cpu_failed;
2133bfcb8132SVladimir Oltean }
2134bfcb8132SVladimir Oltean
2135be6ff966SVladimir Oltean err = ds->ops->port_change_mtu(ds, dp->index, new_mtu);
2136bfcb8132SVladimir Oltean if (err)
2137bfcb8132SVladimir Oltean goto out_port_failed;
2138bfcb8132SVladimir Oltean
2139bfcb8132SVladimir Oltean dev->mtu = new_mtu;
2140bfcb8132SVladimir Oltean
2141bff33f7eSVladimir Oltean dsa_bridge_mtu_normalization(dp);
2142bff33f7eSVladimir Oltean
2143bfcb8132SVladimir Oltean return 0;
2144bfcb8132SVladimir Oltean
2145bfcb8132SVladimir Oltean out_port_failed:
2146bfcb8132SVladimir Oltean if (new_master_mtu != old_master_mtu)
2147636e8adfSVladimir Oltean dsa_port_mtu_change(cpu_dp, old_master_mtu - overhead);
2148bfcb8132SVladimir Oltean out_cpu_failed:
2149bfcb8132SVladimir Oltean if (new_master_mtu != old_master_mtu)
2150bfcb8132SVladimir Oltean dev_set_mtu(master, old_master_mtu);
2151bfcb8132SVladimir Oltean out_master_failed:
2152bfcb8132SVladimir Oltean return err;
2153bfcb8132SVladimir Oltean }
2154bfcb8132SVladimir Oltean
2155d538eca8SVladimir Oltean static int __maybe_unused
dsa_slave_dcbnl_set_default_prio(struct net_device * dev,struct dcb_app * app)2156d538eca8SVladimir Oltean dsa_slave_dcbnl_set_default_prio(struct net_device *dev, struct dcb_app *app)
2157d538eca8SVladimir Oltean {
2158d538eca8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
2159d538eca8SVladimir Oltean struct dsa_switch *ds = dp->ds;
2160d538eca8SVladimir Oltean unsigned long mask, new_prio;
2161d538eca8SVladimir Oltean int err, port = dp->index;
2162d538eca8SVladimir Oltean
2163d538eca8SVladimir Oltean if (!ds->ops->port_set_default_prio)
2164d538eca8SVladimir Oltean return -EOPNOTSUPP;
2165d538eca8SVladimir Oltean
2166d538eca8SVladimir Oltean err = dcb_ieee_setapp(dev, app);
2167d538eca8SVladimir Oltean if (err)
2168d538eca8SVladimir Oltean return err;
2169d538eca8SVladimir Oltean
2170d538eca8SVladimir Oltean mask = dcb_ieee_getapp_mask(dev, app);
2171d538eca8SVladimir Oltean new_prio = __fls(mask);
2172d538eca8SVladimir Oltean
2173d538eca8SVladimir Oltean err = ds->ops->port_set_default_prio(ds, port, new_prio);
2174d538eca8SVladimir Oltean if (err) {
2175d538eca8SVladimir Oltean dcb_ieee_delapp(dev, app);
2176d538eca8SVladimir Oltean return err;
2177d538eca8SVladimir Oltean }
2178d538eca8SVladimir Oltean
2179d538eca8SVladimir Oltean return 0;
2180d538eca8SVladimir Oltean }
2181d538eca8SVladimir Oltean
218247d75f78SVladimir Oltean static int __maybe_unused
dsa_slave_dcbnl_add_dscp_prio(struct net_device * dev,struct dcb_app * app)218347d75f78SVladimir Oltean dsa_slave_dcbnl_add_dscp_prio(struct net_device *dev, struct dcb_app *app)
218447d75f78SVladimir Oltean {
218547d75f78SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
218647d75f78SVladimir Oltean struct dsa_switch *ds = dp->ds;
218747d75f78SVladimir Oltean unsigned long mask, new_prio;
218847d75f78SVladimir Oltean int err, port = dp->index;
218947d75f78SVladimir Oltean u8 dscp = app->protocol;
219047d75f78SVladimir Oltean
219147d75f78SVladimir Oltean if (!ds->ops->port_add_dscp_prio)
219247d75f78SVladimir Oltean return -EOPNOTSUPP;
219347d75f78SVladimir Oltean
219447d75f78SVladimir Oltean if (dscp >= 64) {
219547d75f78SVladimir Oltean netdev_err(dev, "DSCP APP entry with protocol value %u is invalid\n",
219647d75f78SVladimir Oltean dscp);
219747d75f78SVladimir Oltean return -EINVAL;
219847d75f78SVladimir Oltean }
219947d75f78SVladimir Oltean
220047d75f78SVladimir Oltean err = dcb_ieee_setapp(dev, app);
220147d75f78SVladimir Oltean if (err)
220247d75f78SVladimir Oltean return err;
220347d75f78SVladimir Oltean
220447d75f78SVladimir Oltean mask = dcb_ieee_getapp_mask(dev, app);
220547d75f78SVladimir Oltean new_prio = __fls(mask);
220647d75f78SVladimir Oltean
220747d75f78SVladimir Oltean err = ds->ops->port_add_dscp_prio(ds, port, dscp, new_prio);
220847d75f78SVladimir Oltean if (err) {
220947d75f78SVladimir Oltean dcb_ieee_delapp(dev, app);
221047d75f78SVladimir Oltean return err;
221147d75f78SVladimir Oltean }
221247d75f78SVladimir Oltean
221347d75f78SVladimir Oltean return 0;
221447d75f78SVladimir Oltean }
221547d75f78SVladimir Oltean
dsa_slave_dcbnl_ieee_setapp(struct net_device * dev,struct dcb_app * app)2216d538eca8SVladimir Oltean static int __maybe_unused dsa_slave_dcbnl_ieee_setapp(struct net_device *dev,
2217d538eca8SVladimir Oltean struct dcb_app *app)
2218d538eca8SVladimir Oltean {
2219d538eca8SVladimir Oltean switch (app->selector) {
2220d538eca8SVladimir Oltean case IEEE_8021QAZ_APP_SEL_ETHERTYPE:
2221d538eca8SVladimir Oltean switch (app->protocol) {
2222d538eca8SVladimir Oltean case 0:
2223d538eca8SVladimir Oltean return dsa_slave_dcbnl_set_default_prio(dev, app);
2224d538eca8SVladimir Oltean default:
2225d538eca8SVladimir Oltean return -EOPNOTSUPP;
2226d538eca8SVladimir Oltean }
2227d538eca8SVladimir Oltean break;
222847d75f78SVladimir Oltean case IEEE_8021QAZ_APP_SEL_DSCP:
222947d75f78SVladimir Oltean return dsa_slave_dcbnl_add_dscp_prio(dev, app);
2230d538eca8SVladimir Oltean default:
2231d538eca8SVladimir Oltean return -EOPNOTSUPP;
2232d538eca8SVladimir Oltean }
2233d538eca8SVladimir Oltean }
2234d538eca8SVladimir Oltean
2235d538eca8SVladimir Oltean static int __maybe_unused
dsa_slave_dcbnl_del_default_prio(struct net_device * dev,struct dcb_app * app)2236d538eca8SVladimir Oltean dsa_slave_dcbnl_del_default_prio(struct net_device *dev, struct dcb_app *app)
2237d538eca8SVladimir Oltean {
2238d538eca8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
2239d538eca8SVladimir Oltean struct dsa_switch *ds = dp->ds;
2240d538eca8SVladimir Oltean unsigned long mask, new_prio;
2241d538eca8SVladimir Oltean int err, port = dp->index;
2242d538eca8SVladimir Oltean
2243d538eca8SVladimir Oltean if (!ds->ops->port_set_default_prio)
2244d538eca8SVladimir Oltean return -EOPNOTSUPP;
2245d538eca8SVladimir Oltean
2246d538eca8SVladimir Oltean err = dcb_ieee_delapp(dev, app);
2247d538eca8SVladimir Oltean if (err)
2248d538eca8SVladimir Oltean return err;
2249d538eca8SVladimir Oltean
2250d538eca8SVladimir Oltean mask = dcb_ieee_getapp_mask(dev, app);
2251d538eca8SVladimir Oltean new_prio = mask ? __fls(mask) : 0;
2252d538eca8SVladimir Oltean
2253d538eca8SVladimir Oltean err = ds->ops->port_set_default_prio(ds, port, new_prio);
2254d538eca8SVladimir Oltean if (err) {
2255d538eca8SVladimir Oltean dcb_ieee_setapp(dev, app);
2256d538eca8SVladimir Oltean return err;
2257d538eca8SVladimir Oltean }
2258d538eca8SVladimir Oltean
2259d538eca8SVladimir Oltean return 0;
2260d538eca8SVladimir Oltean }
2261d538eca8SVladimir Oltean
226247d75f78SVladimir Oltean static int __maybe_unused
dsa_slave_dcbnl_del_dscp_prio(struct net_device * dev,struct dcb_app * app)226347d75f78SVladimir Oltean dsa_slave_dcbnl_del_dscp_prio(struct net_device *dev, struct dcb_app *app)
226447d75f78SVladimir Oltean {
226547d75f78SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
226647d75f78SVladimir Oltean struct dsa_switch *ds = dp->ds;
226747d75f78SVladimir Oltean int err, port = dp->index;
226847d75f78SVladimir Oltean u8 dscp = app->protocol;
226947d75f78SVladimir Oltean
227047d75f78SVladimir Oltean if (!ds->ops->port_del_dscp_prio)
227147d75f78SVladimir Oltean return -EOPNOTSUPP;
227247d75f78SVladimir Oltean
227347d75f78SVladimir Oltean err = dcb_ieee_delapp(dev, app);
227447d75f78SVladimir Oltean if (err)
227547d75f78SVladimir Oltean return err;
227647d75f78SVladimir Oltean
227747d75f78SVladimir Oltean err = ds->ops->port_del_dscp_prio(ds, port, dscp, app->priority);
227847d75f78SVladimir Oltean if (err) {
227947d75f78SVladimir Oltean dcb_ieee_setapp(dev, app);
228047d75f78SVladimir Oltean return err;
228147d75f78SVladimir Oltean }
228247d75f78SVladimir Oltean
228347d75f78SVladimir Oltean return 0;
228447d75f78SVladimir Oltean }
228547d75f78SVladimir Oltean
dsa_slave_dcbnl_ieee_delapp(struct net_device * dev,struct dcb_app * app)2286d538eca8SVladimir Oltean static int __maybe_unused dsa_slave_dcbnl_ieee_delapp(struct net_device *dev,
2287d538eca8SVladimir Oltean struct dcb_app *app)
2288d538eca8SVladimir Oltean {
2289d538eca8SVladimir Oltean switch (app->selector) {
2290d538eca8SVladimir Oltean case IEEE_8021QAZ_APP_SEL_ETHERTYPE:
2291d538eca8SVladimir Oltean switch (app->protocol) {
2292d538eca8SVladimir Oltean case 0:
2293d538eca8SVladimir Oltean return dsa_slave_dcbnl_del_default_prio(dev, app);
2294d538eca8SVladimir Oltean default:
2295d538eca8SVladimir Oltean return -EOPNOTSUPP;
2296d538eca8SVladimir Oltean }
2297d538eca8SVladimir Oltean break;
229847d75f78SVladimir Oltean case IEEE_8021QAZ_APP_SEL_DSCP:
229947d75f78SVladimir Oltean return dsa_slave_dcbnl_del_dscp_prio(dev, app);
2300d538eca8SVladimir Oltean default:
2301d538eca8SVladimir Oltean return -EOPNOTSUPP;
2302d538eca8SVladimir Oltean }
2303d538eca8SVladimir Oltean }
2304d538eca8SVladimir Oltean
2305d538eca8SVladimir Oltean /* Pre-populate the DCB application priority table with the priorities
2306d538eca8SVladimir Oltean * configured during switch setup, which we read from hardware here.
2307d538eca8SVladimir Oltean */
dsa_slave_dcbnl_init(struct net_device * dev)2308d538eca8SVladimir Oltean static int dsa_slave_dcbnl_init(struct net_device *dev)
2309d538eca8SVladimir Oltean {
2310d538eca8SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
2311d538eca8SVladimir Oltean struct dsa_switch *ds = dp->ds;
2312d538eca8SVladimir Oltean int port = dp->index;
2313d538eca8SVladimir Oltean int err;
2314d538eca8SVladimir Oltean
2315d538eca8SVladimir Oltean if (ds->ops->port_get_default_prio) {
2316d538eca8SVladimir Oltean int prio = ds->ops->port_get_default_prio(ds, port);
2317d538eca8SVladimir Oltean struct dcb_app app = {
2318d538eca8SVladimir Oltean .selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE,
2319d538eca8SVladimir Oltean .protocol = 0,
2320d538eca8SVladimir Oltean .priority = prio,
2321d538eca8SVladimir Oltean };
2322d538eca8SVladimir Oltean
2323d538eca8SVladimir Oltean if (prio < 0)
2324d538eca8SVladimir Oltean return prio;
2325d538eca8SVladimir Oltean
2326d538eca8SVladimir Oltean err = dcb_ieee_setapp(dev, &app);
2327d538eca8SVladimir Oltean if (err)
2328d538eca8SVladimir Oltean return err;
2329d538eca8SVladimir Oltean }
2330d538eca8SVladimir Oltean
233147d75f78SVladimir Oltean if (ds->ops->port_get_dscp_prio) {
233247d75f78SVladimir Oltean int protocol;
233347d75f78SVladimir Oltean
233447d75f78SVladimir Oltean for (protocol = 0; protocol < 64; protocol++) {
233547d75f78SVladimir Oltean struct dcb_app app = {
233647d75f78SVladimir Oltean .selector = IEEE_8021QAZ_APP_SEL_DSCP,
233747d75f78SVladimir Oltean .protocol = protocol,
233847d75f78SVladimir Oltean };
233947d75f78SVladimir Oltean int prio;
234047d75f78SVladimir Oltean
234147d75f78SVladimir Oltean prio = ds->ops->port_get_dscp_prio(ds, port, protocol);
234247d75f78SVladimir Oltean if (prio == -EOPNOTSUPP)
234347d75f78SVladimir Oltean continue;
234447d75f78SVladimir Oltean if (prio < 0)
234547d75f78SVladimir Oltean return prio;
234647d75f78SVladimir Oltean
234747d75f78SVladimir Oltean app.priority = prio;
234847d75f78SVladimir Oltean
234947d75f78SVladimir Oltean err = dcb_ieee_setapp(dev, &app);
235047d75f78SVladimir Oltean if (err)
235147d75f78SVladimir Oltean return err;
235247d75f78SVladimir Oltean }
235347d75f78SVladimir Oltean }
235447d75f78SVladimir Oltean
2355d538eca8SVladimir Oltean return 0;
2356d538eca8SVladimir Oltean }
2357d538eca8SVladimir Oltean
235891da11f8SLennert Buytenhek static const struct ethtool_ops dsa_slave_ethtool_ops = {
235991da11f8SLennert Buytenhek .get_drvinfo = dsa_slave_get_drvinfo,
23603d762a0fSGuenter Roeck .get_regs_len = dsa_slave_get_regs_len,
23613d762a0fSGuenter Roeck .get_regs = dsa_slave_get_regs,
2362aab9c406SFlorian Fainelli .nway_reset = dsa_slave_nway_reset,
2363c4aef9fcSFlorian Fainelli .get_link = ethtool_op_get_link,
23646793abb4SGuenter Roeck .get_eeprom_len = dsa_slave_get_eeprom_len,
23656793abb4SGuenter Roeck .get_eeprom = dsa_slave_get_eeprom,
23666793abb4SGuenter Roeck .set_eeprom = dsa_slave_set_eeprom,
236791da11f8SLennert Buytenhek .get_strings = dsa_slave_get_strings,
236891da11f8SLennert Buytenhek .get_ethtool_stats = dsa_slave_get_ethtool_stats,
236991da11f8SLennert Buytenhek .get_sset_count = dsa_slave_get_sset_count,
2370487d3855SAlvin Šipraga .get_eth_phy_stats = dsa_slave_get_eth_phy_stats,
2371487d3855SAlvin Šipraga .get_eth_mac_stats = dsa_slave_get_eth_mac_stats,
2372487d3855SAlvin Šipraga .get_eth_ctrl_stats = dsa_slave_get_eth_ctrl_stats,
237367f38b1cSClément Léger .get_rmon_stats = dsa_slave_get_rmon_stats,
237419e57c4eSFlorian Fainelli .set_wol = dsa_slave_set_wol,
237519e57c4eSFlorian Fainelli .get_wol = dsa_slave_get_wol,
23767905288fSFlorian Fainelli .set_eee = dsa_slave_set_eee,
23777905288fSFlorian Fainelli .get_eee = dsa_slave_get_eee,
2378aab9c406SFlorian Fainelli .get_link_ksettings = dsa_slave_get_link_ksettings,
2379aab9c406SFlorian Fainelli .set_link_ksettings = dsa_slave_set_link_ksettings,
23803d410403SOleksij Rempel .get_pause_stats = dsa_slave_get_pause_stats,
2381a2a1a13bSHeiner Kallweit .get_pauseparam = dsa_slave_get_pauseparam,
2382a2a1a13bSHeiner Kallweit .set_pauseparam = dsa_slave_set_pauseparam,
2383bf9f2648SFlorian Fainelli .get_rxnfc = dsa_slave_get_rxnfc,
2384bf9f2648SFlorian Fainelli .set_rxnfc = dsa_slave_set_rxnfc,
23850336369dSBrandon Streiff .get_ts_info = dsa_slave_get_ts_info,
2386a71acad9SOleksij Rempel .self_test = dsa_slave_net_selftest,
23875f6c2d49SVladimir Oltean .get_mm = dsa_slave_get_mm,
23885f6c2d49SVladimir Oltean .set_mm = dsa_slave_set_mm,
23895f6c2d49SVladimir Oltean .get_mm_stats = dsa_slave_get_mm_stats,
239091da11f8SLennert Buytenhek };
239191da11f8SLennert Buytenhek
2392d538eca8SVladimir Oltean static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = {
2393d538eca8SVladimir Oltean .ieee_setapp = dsa_slave_dcbnl_ieee_setapp,
2394d538eca8SVladimir Oltean .ieee_delapp = dsa_slave_dcbnl_ieee_delapp,
2395d538eca8SVladimir Oltean };
2396d538eca8SVladimir Oltean
dsa_slave_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * s)2397c2ec5f2eSOleksij Rempel static void dsa_slave_get_stats64(struct net_device *dev,
2398c2ec5f2eSOleksij Rempel struct rtnl_link_stats64 *s)
2399c2ec5f2eSOleksij Rempel {
2400c2ec5f2eSOleksij Rempel struct dsa_port *dp = dsa_slave_to_port(dev);
2401c2ec5f2eSOleksij Rempel struct dsa_switch *ds = dp->ds;
2402c2ec5f2eSOleksij Rempel
2403c2ec5f2eSOleksij Rempel if (ds->ops->get_stats64)
2404c2ec5f2eSOleksij Rempel ds->ops->get_stats64(ds, dp->index, s);
2405c2ec5f2eSOleksij Rempel else
2406c2ec5f2eSOleksij Rempel dev_get_tstats64(dev, s);
2407c2ec5f2eSOleksij Rempel }
2408c2ec5f2eSOleksij Rempel
dsa_slave_fill_forward_path(struct net_device_path_ctx * ctx,struct net_device_path * path)24090994d492SFelix Fietkau static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx,
24100994d492SFelix Fietkau struct net_device_path *path)
24110994d492SFelix Fietkau {
24120994d492SFelix Fietkau struct dsa_port *dp = dsa_slave_to_port(ctx->dev);
24138f6a19c0SVladimir Oltean struct net_device *master = dsa_port_to_master(dp);
24140994d492SFelix Fietkau struct dsa_port *cpu_dp = dp->cpu_dp;
24150994d492SFelix Fietkau
24160994d492SFelix Fietkau path->dev = ctx->dev;
24170994d492SFelix Fietkau path->type = DEV_PATH_DSA;
24180994d492SFelix Fietkau path->dsa.proto = cpu_dp->tag_ops->proto;
24190994d492SFelix Fietkau path->dsa.port = dp->index;
24208f6a19c0SVladimir Oltean ctx->dev = master;
24210994d492SFelix Fietkau
24220994d492SFelix Fietkau return 0;
24230994d492SFelix Fietkau }
24240994d492SFelix Fietkau
24253e8a72d1SFlorian Fainelli static const struct net_device_ops dsa_slave_netdev_ops = {
2426d442ad4aSStephen Hemminger .ndo_open = dsa_slave_open,
2427d442ad4aSStephen Hemminger .ndo_stop = dsa_slave_close,
24283e8a72d1SFlorian Fainelli .ndo_start_xmit = dsa_slave_xmit,
2429d442ad4aSStephen Hemminger .ndo_change_rx_flags = dsa_slave_change_rx_flags,
2430d442ad4aSStephen Hemminger .ndo_set_rx_mode = dsa_slave_set_rx_mode,
2431d442ad4aSStephen Hemminger .ndo_set_mac_address = dsa_slave_set_mac_address,
24322bedde1aSArkadi Sharshevsky .ndo_fdb_dump = dsa_slave_fdb_dump,
2433a7605370SArnd Bergmann .ndo_eth_ioctl = dsa_slave_ioctl,
2434abd2be00SNicolas Dichtel .ndo_get_iflink = dsa_slave_get_iflink,
243504ff53f9SFlorian Fainelli #ifdef CONFIG_NET_POLL_CONTROLLER
243604ff53f9SFlorian Fainelli .ndo_netpoll_setup = dsa_slave_netpoll_setup,
243704ff53f9SFlorian Fainelli .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup,
243804ff53f9SFlorian Fainelli .ndo_poll_controller = dsa_slave_poll_controller,
243904ff53f9SFlorian Fainelli #endif
2440f50f2127SFlorian Fainelli .ndo_setup_tc = dsa_slave_setup_tc,
2441c2ec5f2eSOleksij Rempel .ndo_get_stats64 = dsa_slave_get_stats64,
2442061f6a50SFlorian Fainelli .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid,
2443061f6a50SFlorian Fainelli .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid,
2444bfcb8132SVladimir Oltean .ndo_change_mtu = dsa_slave_change_mtu,
24450994d492SFelix Fietkau .ndo_fill_forward_path = dsa_slave_fill_forward_path,
244698237d43SScott Feldman };
244798237d43SScott Feldman
2448f37db85dSFlorian Fainelli static struct device_type dsa_type = {
2449f37db85dSFlorian Fainelli .name = "dsa",
2450f37db85dSFlorian Fainelli };
2451f37db85dSFlorian Fainelli
dsa_port_phylink_mac_change(struct dsa_switch * ds,int port,bool up)2452aab9c406SFlorian Fainelli void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
2453aab9c406SFlorian Fainelli {
2454aab9c406SFlorian Fainelli const struct dsa_port *dp = dsa_to_port(ds, port);
2455aab9c406SFlorian Fainelli
2456765bda93SRussell King if (dp->pl)
2457aab9c406SFlorian Fainelli phylink_mac_change(dp->pl, up);
2458aab9c406SFlorian Fainelli }
2459aab9c406SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
2460aab9c406SFlorian Fainelli
dsa_slave_phylink_fixed_state(struct phylink_config * config,struct phylink_link_state * state)24615c05c1dbSRussell King static void dsa_slave_phylink_fixed_state(struct phylink_config *config,
2462aab9c406SFlorian Fainelli struct phylink_link_state *state)
2463aab9c406SFlorian Fainelli {
24645c05c1dbSRussell King struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
2465aab9c406SFlorian Fainelli struct dsa_switch *ds = dp->ds;
2466aab9c406SFlorian Fainelli
2467aab9c406SFlorian Fainelli /* No need to check that this operation is valid, the callback would
2468aab9c406SFlorian Fainelli * not be called if it was not.
2469aab9c406SFlorian Fainelli */
2470aab9c406SFlorian Fainelli ds->ops->phylink_fixed_state(ds, dp->index, state);
2471ce31b31cSFlorian Fainelli }
2472ce31b31cSFlorian Fainelli
247391da11f8SLennert Buytenhek /* slave device setup *******************************************************/
dsa_slave_phy_connect(struct net_device * slave_dev,int addr,u32 flags)2474c916e8e1SOleksij Rempel static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr,
2475c916e8e1SOleksij Rempel u32 flags)
2476c305c165SFlorian Fainelli {
2477d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(slave_dev);
2478d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
2479c305c165SFlorian Fainelli
24800115dcd1SVivien Didelot slave_dev->phydev = mdiobus_get_phy(ds->slave_mii_bus, addr);
24810115dcd1SVivien Didelot if (!slave_dev->phydev) {
2482d25b8e74SRussell King netdev_err(slave_dev, "no phy at %d\n", addr);
2483c305c165SFlorian Fainelli return -ENODEV;
2484d25b8e74SRussell King }
2485c305c165SFlorian Fainelli
2486c916e8e1SOleksij Rempel slave_dev->phydev->dev_flags |= flags;
2487c916e8e1SOleksij Rempel
2488aab9c406SFlorian Fainelli return phylink_connect_phy(dp->pl, slave_dev->phydev);
2489c305c165SFlorian Fainelli }
2490c305c165SFlorian Fainelli
dsa_slave_phy_setup(struct net_device * slave_dev)24914fa7b718SVivien Didelot static int dsa_slave_phy_setup(struct net_device *slave_dev)
24920d8bcdd3SFlorian Fainelli {
2493d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(slave_dev);
2494d945097bSVivien Didelot struct device_node *port_dn = dp->dn;
2495d945097bSVivien Didelot struct dsa_switch *ds = dp->ds;
24966819563eSFlorian Fainelli u32 phy_flags = 0;
24970c65b2b9SAndrew Lunn int ret;
24980d8bcdd3SFlorian Fainelli
249944cc27e4SIoana Ciornei dp->pl_config.dev = &slave_dev->dev;
250044cc27e4SIoana Ciornei dp->pl_config.type = PHYLINK_NETDEV;
250144cc27e4SIoana Ciornei
25025c05c1dbSRussell King /* The get_fixed_state callback takes precedence over polling the
25035c05c1dbSRussell King * link GPIO in PHYLINK (see phylink_get_fixed_state). Only set
25045c05c1dbSRussell King * this if the switch provides such a callback.
25055c05c1dbSRussell King */
25065c05c1dbSRussell King if (ds->ops->phylink_fixed_state) {
25075c05c1dbSRussell King dp->pl_config.get_fixed_state = dsa_slave_phylink_fixed_state;
25085c05c1dbSRussell King dp->pl_config.poll_fixed_state = true;
25095c05c1dbSRussell King }
25105c05c1dbSRussell King
251121bd64bdSRussell King (Oracle) ret = dsa_port_phylink_create(dp);
251221bd64bdSRussell King (Oracle) if (ret)
251321bd64bdSRussell King (Oracle) return ret;
2514aab9c406SFlorian Fainelli
25159d490b4eSVivien Didelot if (ds->ops->get_phy_flags)
2516d945097bSVivien Didelot phy_flags = ds->ops->get_phy_flags(ds, dp->index);
25176819563eSFlorian Fainelli
2518aab9c406SFlorian Fainelli ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
25196146dd45SVladimir Oltean if (ret == -ENODEV && ds->slave_mii_bus) {
25206146dd45SVladimir Oltean /* We could not connect to a designated PHY or SFP, so try to
25216146dd45SVladimir Oltean * use the switch internal MDIO bus instead
25220d8bcdd3SFlorian Fainelli */
2523c916e8e1SOleksij Rempel ret = dsa_slave_phy_connect(slave_dev, dp->index, phy_flags);
2524d25b8e74SRussell King }
25256a52e733SVladimir Oltean if (ret) {
25266a52e733SVladimir Oltean netdev_err(slave_dev, "failed to connect to PHY: %pe\n",
25276a52e733SVladimir Oltean ERR_PTR(ret));
2528cf5ca4ddSVladimir Oltean dsa_port_phylink_destroy(dp);
25290d8bcdd3SFlorian Fainelli }
25309697f1cdSFlorian Fainelli
25316146dd45SVladimir Oltean return ret;
2532b31f65fbSAndrew Lunn }
25330d8bcdd3SFlorian Fainelli
dsa_slave_setup_tagger(struct net_device * slave)253453da0ebaSVladimir Oltean void dsa_slave_setup_tagger(struct net_device *slave)
253553da0ebaSVladimir Oltean {
253653da0ebaSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(slave);
25378f6a19c0SVladimir Oltean struct net_device *master = dsa_port_to_master(dp);
253853da0ebaSVladimir Oltean struct dsa_slave_priv *p = netdev_priv(slave);
253953da0ebaSVladimir Oltean const struct dsa_port *cpu_dp = dp->cpu_dp;
254058adf9dcSVladimir Oltean const struct dsa_switch *ds = dp->ds;
254153da0ebaSVladimir Oltean
25424e500251SVladimir Oltean slave->needed_headroom = cpu_dp->tag_ops->needed_headroom;
25434e500251SVladimir Oltean slave->needed_tailroom = cpu_dp->tag_ops->needed_tailroom;
254453da0ebaSVladimir Oltean /* Try to save one extra realloc later in the TX path (in the master)
254553da0ebaSVladimir Oltean * by also inheriting the master's needed headroom and tailroom.
254653da0ebaSVladimir Oltean * The 8021q driver also does this.
254753da0ebaSVladimir Oltean */
254853da0ebaSVladimir Oltean slave->needed_headroom += master->needed_headroom;
254953da0ebaSVladimir Oltean slave->needed_tailroom += master->needed_tailroom;
255053da0ebaSVladimir Oltean
255153da0ebaSVladimir Oltean p->xmit = cpu_dp->tag_ops->xmit;
255221cf377aSLino Sanfilippo
255321cf377aSLino Sanfilippo slave->features = master->vlan_features | NETIF_F_HW_TC;
255421cf377aSLino Sanfilippo slave->hw_features |= NETIF_F_HW_TC;
255521cf377aSLino Sanfilippo slave->features |= NETIF_F_LLTX;
255621cf377aSLino Sanfilippo if (slave->needed_tailroom)
255721cf377aSLino Sanfilippo slave->features &= ~(NETIF_F_SG | NETIF_F_FRAGLIST);
255858adf9dcSVladimir Oltean if (ds->needs_standalone_vlan_filtering)
255958adf9dcSVladimir Oltean slave->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
256053da0ebaSVladimir Oltean }
256153da0ebaSVladimir Oltean
dsa_slave_suspend(struct net_device * slave_dev)256224462549SFlorian Fainelli int dsa_slave_suspend(struct net_device *slave_dev)
256324462549SFlorian Fainelli {
2564aab9c406SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(slave_dev);
256524462549SFlorian Fainelli
2566a94c689eSFlorian Fainelli if (!netif_running(slave_dev))
2567a94c689eSFlorian Fainelli return 0;
2568a94c689eSFlorian Fainelli
2569f154be24SFlorian Fainelli netif_device_detach(slave_dev);
2570f154be24SFlorian Fainelli
2571aab9c406SFlorian Fainelli rtnl_lock();
2572aab9c406SFlorian Fainelli phylink_stop(dp->pl);
2573aab9c406SFlorian Fainelli rtnl_unlock();
257424462549SFlorian Fainelli
257524462549SFlorian Fainelli return 0;
257624462549SFlorian Fainelli }
257724462549SFlorian Fainelli
dsa_slave_resume(struct net_device * slave_dev)257824462549SFlorian Fainelli int dsa_slave_resume(struct net_device *slave_dev)
257924462549SFlorian Fainelli {
2580aab9c406SFlorian Fainelli struct dsa_port *dp = dsa_slave_to_port(slave_dev);
2581aab9c406SFlorian Fainelli
2582a94c689eSFlorian Fainelli if (!netif_running(slave_dev))
2583a94c689eSFlorian Fainelli return 0;
2584a94c689eSFlorian Fainelli
258524462549SFlorian Fainelli netif_device_attach(slave_dev);
258624462549SFlorian Fainelli
2587aab9c406SFlorian Fainelli rtnl_lock();
2588aab9c406SFlorian Fainelli phylink_start(dp->pl);
2589aab9c406SFlorian Fainelli rtnl_unlock();
259024462549SFlorian Fainelli
259124462549SFlorian Fainelli return 0;
259224462549SFlorian Fainelli }
259324462549SFlorian Fainelli
dsa_slave_create(struct dsa_port * port)2594951259aaSVivien Didelot int dsa_slave_create(struct dsa_port *port)
259591da11f8SLennert Buytenhek {
25968f6a19c0SVladimir Oltean struct net_device *master = dsa_port_to_master(port);
25974cfbf09cSVivien Didelot struct dsa_switch *ds = port->ds;
259891da11f8SLennert Buytenhek struct net_device *slave_dev;
259991da11f8SLennert Buytenhek struct dsa_slave_priv *p;
26000171a1d2SRasmus Villemoes const char *name;
26010171a1d2SRasmus Villemoes int assign_type;
260291da11f8SLennert Buytenhek int ret;
260391da11f8SLennert Buytenhek
260455199df6SFlorian Fainelli if (!ds->num_tx_queues)
260555199df6SFlorian Fainelli ds->num_tx_queues = 1;
260655199df6SFlorian Fainelli
26070171a1d2SRasmus Villemoes if (port->name) {
26080171a1d2SRasmus Villemoes name = port->name;
26096fdb0384SRasmus Villemoes assign_type = NET_NAME_PREDICTABLE;
26100171a1d2SRasmus Villemoes } else {
26110171a1d2SRasmus Villemoes name = "eth%d";
2612b8790661SRasmus Villemoes assign_type = NET_NAME_ENUM;
26130171a1d2SRasmus Villemoes }
26140171a1d2SRasmus Villemoes
261555199df6SFlorian Fainelli slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name,
26160171a1d2SRasmus Villemoes assign_type, ether_setup,
261755199df6SFlorian Fainelli ds->num_tx_queues, 1);
261891da11f8SLennert Buytenhek if (slave_dev == NULL)
2619d87d6f44SGuenter Roeck return -ENOMEM;
262091da11f8SLennert Buytenhek
262195f510d0SVladimir Oltean slave_dev->rtnl_link_ops = &dsa_link_ops;
26227ad24ea4SWilfried Klaebe slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
2623d538eca8SVladimir Oltean #if IS_ENABLED(CONFIG_DCB)
2624d538eca8SVladimir Oltean slave_dev->dcbnl_ops = &dsa_slave_dcbnl_ops;
2625d538eca8SVladimir Oltean #endif
262683216e39SMichael Walle if (!is_zero_ether_addr(port->mac))
2627e35b8d7dSJakub Kicinski eth_hw_addr_set(slave_dev, port->mac);
2628a2c7023fSXiaofei Shen else
26292fcc8005SBjørn Mork eth_hw_addr_inherit(slave_dev, master);
26300a5f107bSPhil Sutter slave_dev->priv_flags |= IFF_NO_QUEUE;
26315e8a1e03SVladimir Oltean if (dsa_switch_supports_uc_filtering(ds))
26325e8a1e03SVladimir Oltean slave_dev->priv_flags |= IFF_UNICAST_FLT;
26333e8a72d1SFlorian Fainelli slave_dev->netdev_ops = &dsa_slave_netdev_ops;
2634bfcb8132SVladimir Oltean if (ds->ops->port_max_mtu)
2635bfcb8132SVladimir Oltean slave_dev->max_mtu = ds->ops->port_max_mtu(ds, port->index);
2636f37db85dSFlorian Fainelli SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
2637d442ad4aSStephen Hemminger
26384cfbf09cSVivien Didelot SET_NETDEV_DEV(slave_dev, port->ds->dev);
2639ac73d4bfSJiri Pirko SET_NETDEV_DEVLINK_PORT(slave_dev, &port->devlink_port);
26404cfbf09cSVivien Didelot slave_dev->dev.of_node = port->dn;
264191da11f8SLennert Buytenhek slave_dev->vlan_features = master->vlan_features;
264291da11f8SLennert Buytenhek
264391da11f8SLennert Buytenhek p = netdev_priv(slave_dev);
26446a900628SHeiner Kallweit slave_dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
26456a900628SHeiner Kallweit if (!slave_dev->tstats) {
26465f6b4e14SFlorian Fainelli free_netdev(slave_dev);
26475f6b4e14SFlorian Fainelli return -ENOMEM;
26485f6b4e14SFlorian Fainelli }
2649e131a563SAlexander Lobakin
2650e131a563SAlexander Lobakin ret = gro_cells_init(&p->gcells, slave_dev);
2651e131a563SAlexander Lobakin if (ret)
2652e131a563SAlexander Lobakin goto out_free;
2653e131a563SAlexander Lobakin
26544cfbf09cSVivien Didelot p->dp = port;
2655f50f2127SFlorian Fainelli INIT_LIST_HEAD(&p->mall_tc_list);
2656f8b8b1cdSVivien Didelot port->slave = slave_dev;
265753da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave_dev);
265891da11f8SLennert Buytenhek
265991da11f8SLennert Buytenhek netif_carrier_off(slave_dev);
266091da11f8SLennert Buytenhek
26614fa7b718SVivien Didelot ret = dsa_slave_phy_setup(slave_dev);
26620071f56eSAndrew Lunn if (ret) {
2663c9ebf126SVladimir Oltean netdev_err(slave_dev,
2664c9ebf126SVladimir Oltean "error %d setting up PHY for tree %d, switch %d, port %d\n",
2665c9ebf126SVladimir Oltean ret, ds->dst->index, ds->index, port->index);
2666e131a563SAlexander Lobakin goto out_gcells;
2667e804441cSFlorian Fainelli }
2668e804441cSFlorian Fainelli
26692f1e8ea7SVladimir Oltean rtnl_lock();
2670e31dbd3bSVladimir Oltean
2671904e112aSVladimir Oltean ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN);
2672904e112aSVladimir Oltean if (ret && ret != -EOPNOTSUPP)
2673904e112aSVladimir Oltean dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n",
2674904e112aSVladimir Oltean ret, ETH_DATA_LEN, port->index);
2675904e112aSVladimir Oltean
26762f1e8ea7SVladimir Oltean ret = register_netdevice(slave_dev);
2677e804441cSFlorian Fainelli if (ret) {
2678e804441cSFlorian Fainelli netdev_err(master, "error %d registering interface %s\n",
2679e804441cSFlorian Fainelli ret, slave_dev->name);
26802f1e8ea7SVladimir Oltean rtnl_unlock();
2681e804441cSFlorian Fainelli goto out_phy;
26820071f56eSAndrew Lunn }
26830071f56eSAndrew Lunn
2684d538eca8SVladimir Oltean if (IS_ENABLED(CONFIG_DCB)) {
2685d538eca8SVladimir Oltean ret = dsa_slave_dcbnl_init(slave_dev);
2686d538eca8SVladimir Oltean if (ret) {
2687d538eca8SVladimir Oltean netdev_err(slave_dev,
2688d538eca8SVladimir Oltean "failed to initialize DCB: %pe\n",
2689d538eca8SVladimir Oltean ERR_PTR(ret));
2690d538eca8SVladimir Oltean rtnl_unlock();
2691d538eca8SVladimir Oltean goto out_unregister;
2692d538eca8SVladimir Oltean }
2693d538eca8SVladimir Oltean }
2694d538eca8SVladimir Oltean
26952f1e8ea7SVladimir Oltean ret = netdev_upper_dev_link(master, slave_dev, NULL);
26962f1e8ea7SVladimir Oltean
26972f1e8ea7SVladimir Oltean rtnl_unlock();
26982f1e8ea7SVladimir Oltean
26992f1e8ea7SVladimir Oltean if (ret)
27002f1e8ea7SVladimir Oltean goto out_unregister;
27012f1e8ea7SVladimir Oltean
2702d87d6f44SGuenter Roeck return 0;
2703e804441cSFlorian Fainelli
27042f1e8ea7SVladimir Oltean out_unregister:
27052f1e8ea7SVladimir Oltean unregister_netdev(slave_dev);
2706e804441cSFlorian Fainelli out_phy:
2707aab9c406SFlorian Fainelli rtnl_lock();
2708aab9c406SFlorian Fainelli phylink_disconnect_phy(p->dp->pl);
2709aab9c406SFlorian Fainelli rtnl_unlock();
2710cf5ca4ddSVladimir Oltean dsa_port_phylink_destroy(p->dp);
2711e131a563SAlexander Lobakin out_gcells:
2712e131a563SAlexander Lobakin gro_cells_destroy(&p->gcells);
2713e804441cSFlorian Fainelli out_free:
27146a900628SHeiner Kallweit free_percpu(slave_dev->tstats);
2715e804441cSFlorian Fainelli free_netdev(slave_dev);
2716f8b8b1cdSVivien Didelot port->slave = NULL;
2717e804441cSFlorian Fainelli return ret;
271891da11f8SLennert Buytenhek }
2719b73adef6SFlorian Fainelli
dsa_slave_destroy(struct net_device * slave_dev)2720cda5c15bSNeil Armstrong void dsa_slave_destroy(struct net_device *slave_dev)
2721cda5c15bSNeil Armstrong {
27222f1e8ea7SVladimir Oltean struct net_device *master = dsa_slave_to_master(slave_dev);
2723d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(slave_dev);
2724cda5c15bSNeil Armstrong struct dsa_slave_priv *p = netdev_priv(slave_dev);
2725cda5c15bSNeil Armstrong
2726cda5c15bSNeil Armstrong netif_carrier_off(slave_dev);
2727aab9c406SFlorian Fainelli rtnl_lock();
27282f1e8ea7SVladimir Oltean netdev_upper_dev_unlink(master, slave_dev);
27292f1e8ea7SVladimir Oltean unregister_netdevice(slave_dev);
2730aab9c406SFlorian Fainelli phylink_disconnect_phy(dp->pl);
2731aab9c406SFlorian Fainelli rtnl_unlock();
2732881eadabSJohan Hovold
2733cf5ca4ddSVladimir Oltean dsa_port_phylink_destroy(dp);
2734e131a563SAlexander Lobakin gro_cells_destroy(&p->gcells);
27356a900628SHeiner Kallweit free_percpu(slave_dev->tstats);
2736cda5c15bSNeil Armstrong free_netdev(slave_dev);
2737cda5c15bSNeil Armstrong }
2738cda5c15bSNeil Armstrong
dsa_slave_change_master(struct net_device * dev,struct net_device * master,struct netlink_ext_ack * extack)273995f510d0SVladimir Oltean int dsa_slave_change_master(struct net_device *dev, struct net_device *master,
274095f510d0SVladimir Oltean struct netlink_ext_ack *extack)
274195f510d0SVladimir Oltean {
274295f510d0SVladimir Oltean struct net_device *old_master = dsa_slave_to_master(dev);
274395f510d0SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
274495f510d0SVladimir Oltean struct dsa_switch *ds = dp->ds;
274595f510d0SVladimir Oltean struct net_device *upper;
274695f510d0SVladimir Oltean struct list_head *iter;
274795f510d0SVladimir Oltean int err;
274895f510d0SVladimir Oltean
274995f510d0SVladimir Oltean if (master == old_master)
275095f510d0SVladimir Oltean return 0;
275195f510d0SVladimir Oltean
275295f510d0SVladimir Oltean if (!ds->ops->port_change_master) {
275395f510d0SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
275495f510d0SVladimir Oltean "Driver does not support changing DSA master");
275595f510d0SVladimir Oltean return -EOPNOTSUPP;
275695f510d0SVladimir Oltean }
275795f510d0SVladimir Oltean
275895f510d0SVladimir Oltean if (!netdev_uses_dsa(master)) {
275995f510d0SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
276095f510d0SVladimir Oltean "Interface not eligible as DSA master");
276195f510d0SVladimir Oltean return -EOPNOTSUPP;
276295f510d0SVladimir Oltean }
276395f510d0SVladimir Oltean
276495f510d0SVladimir Oltean netdev_for_each_upper_dev_rcu(master, upper, iter) {
276595f510d0SVladimir Oltean if (dsa_slave_dev_check(upper))
276695f510d0SVladimir Oltean continue;
276795f510d0SVladimir Oltean if (netif_is_bridge_master(upper))
276895f510d0SVladimir Oltean continue;
276995f510d0SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, "Cannot join master with unknown uppers");
277095f510d0SVladimir Oltean return -EOPNOTSUPP;
277195f510d0SVladimir Oltean }
277295f510d0SVladimir Oltean
277395f510d0SVladimir Oltean /* Since we allow live-changing the DSA master, plus we auto-open the
277495f510d0SVladimir Oltean * DSA master when the user port opens => we need to ensure that the
277595f510d0SVladimir Oltean * new DSA master is open too.
277695f510d0SVladimir Oltean */
277795f510d0SVladimir Oltean if (dev->flags & IFF_UP) {
277895f510d0SVladimir Oltean err = dev_open(master, extack);
277995f510d0SVladimir Oltean if (err)
278095f510d0SVladimir Oltean return err;
278195f510d0SVladimir Oltean }
278295f510d0SVladimir Oltean
278395f510d0SVladimir Oltean netdev_upper_dev_unlink(old_master, dev);
278495f510d0SVladimir Oltean
278595f510d0SVladimir Oltean err = netdev_upper_dev_link(master, dev, extack);
278695f510d0SVladimir Oltean if (err)
278795f510d0SVladimir Oltean goto out_revert_old_master_unlink;
278895f510d0SVladimir Oltean
278995f510d0SVladimir Oltean err = dsa_port_change_master(dp, master, extack);
279095f510d0SVladimir Oltean if (err)
279195f510d0SVladimir Oltean goto out_revert_master_link;
279295f510d0SVladimir Oltean
279395f510d0SVladimir Oltean /* Update the MTU of the new CPU port through cross-chip notifiers */
279495f510d0SVladimir Oltean err = dsa_slave_change_mtu(dev, dev->mtu);
279595f510d0SVladimir Oltean if (err && err != -EOPNOTSUPP) {
279695f510d0SVladimir Oltean netdev_warn(dev,
279795f510d0SVladimir Oltean "nonfatal error updating MTU with new master: %pe\n",
279895f510d0SVladimir Oltean ERR_PTR(err));
279995f510d0SVladimir Oltean }
280095f510d0SVladimir Oltean
280195f510d0SVladimir Oltean /* If the port doesn't have its own MAC address and relies on the DSA
280295f510d0SVladimir Oltean * master's one, inherit it again from the new DSA master.
280395f510d0SVladimir Oltean */
280495f510d0SVladimir Oltean if (is_zero_ether_addr(dp->mac))
280595f510d0SVladimir Oltean eth_hw_addr_inherit(dev, master);
280695f510d0SVladimir Oltean
280795f510d0SVladimir Oltean return 0;
280895f510d0SVladimir Oltean
280995f510d0SVladimir Oltean out_revert_master_link:
281095f510d0SVladimir Oltean netdev_upper_dev_unlink(master, dev);
281195f510d0SVladimir Oltean out_revert_old_master_unlink:
281295f510d0SVladimir Oltean netdev_upper_dev_link(old_master, dev, NULL);
281395f510d0SVladimir Oltean return err;
281495f510d0SVladimir Oltean }
281595f510d0SVladimir Oltean
dsa_slave_dev_check(const struct net_device * dev)28164d776482SFlorian Fainelli bool dsa_slave_dev_check(const struct net_device *dev)
2817b73adef6SFlorian Fainelli {
2818b73adef6SFlorian Fainelli return dev->netdev_ops == &dsa_slave_netdev_ops;
2819b73adef6SFlorian Fainelli }
2820a5e3c9baSVladimir Oltean EXPORT_SYMBOL_GPL(dsa_slave_dev_check);
2821b73adef6SFlorian Fainelli
dsa_slave_changeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)28228e92ab3aSVivien Didelot static int dsa_slave_changeupper(struct net_device *dev,
28238e92ab3aSVivien Didelot struct netdev_notifier_changeupper_info *info)
2824b73adef6SFlorian Fainelli {
28252afc526aSVladimir Oltean struct netlink_ext_ack *extack;
28268e92ab3aSVivien Didelot int err = NOTIFY_DONE;
2827*69a1e2d9SVladimir Oltean struct dsa_port *dp;
2828b73adef6SFlorian Fainelli
28294c3f80d2SVladimir Oltean if (!dsa_slave_dev_check(dev))
28304c3f80d2SVladimir Oltean return err;
28314c3f80d2SVladimir Oltean
2832*69a1e2d9SVladimir Oltean dp = dsa_slave_to_port(dev);
28332afc526aSVladimir Oltean extack = netdev_notifier_info_to_extack(&info->info);
28342afc526aSVladimir Oltean
28358e92ab3aSVivien Didelot if (netif_is_bridge_master(info->upper_dev)) {
28368e92ab3aSVivien Didelot if (info->linking) {
28372afc526aSVladimir Oltean err = dsa_port_bridge_join(dp, info->upper_dev, extack);
2838bff33f7eSVladimir Oltean if (!err)
2839bff33f7eSVladimir Oltean dsa_bridge_mtu_normalization(dp);
284067b5fb5dSVladimir Oltean if (err == -EOPNOTSUPP) {
2841d795527dSVladimir Oltean NL_SET_ERR_MSG_WEAK_MOD(extack,
2842d795527dSVladimir Oltean "Offloading not supported");
284367b5fb5dSVladimir Oltean err = 0;
284467b5fb5dSVladimir Oltean }
28458e92ab3aSVivien Didelot err = notifier_from_errno(err);
28468e92ab3aSVivien Didelot } else {
284717d7802bSVivien Didelot dsa_port_bridge_leave(dp, info->upper_dev);
28488e92ab3aSVivien Didelot err = NOTIFY_OK;
28498e92ab3aSVivien Didelot }
2850058102a6STobias Waldekranz } else if (netif_is_lag_master(info->upper_dev)) {
2851058102a6STobias Waldekranz if (info->linking) {
2852058102a6STobias Waldekranz err = dsa_port_lag_join(dp, info->upper_dev,
28532afc526aSVladimir Oltean info->upper_info, extack);
2854058102a6STobias Waldekranz if (err == -EOPNOTSUPP) {
2855d795527dSVladimir Oltean NL_SET_ERR_MSG_WEAK_MOD(extack,
2856058102a6STobias Waldekranz "Offloading not supported");
2857058102a6STobias Waldekranz err = 0;
2858058102a6STobias Waldekranz }
2859058102a6STobias Waldekranz err = notifier_from_errno(err);
2860058102a6STobias Waldekranz } else {
2861058102a6STobias Waldekranz dsa_port_lag_leave(dp, info->upper_dev);
2862058102a6STobias Waldekranz err = NOTIFY_OK;
2863058102a6STobias Waldekranz }
286418596f50SGeorge McCollister } else if (is_hsr_master(info->upper_dev)) {
286518596f50SGeorge McCollister if (info->linking) {
286618596f50SGeorge McCollister err = dsa_port_hsr_join(dp, info->upper_dev);
286718596f50SGeorge McCollister if (err == -EOPNOTSUPP) {
2868d795527dSVladimir Oltean NL_SET_ERR_MSG_WEAK_MOD(extack,
286918596f50SGeorge McCollister "Offloading not supported");
287018596f50SGeorge McCollister err = 0;
287118596f50SGeorge McCollister }
287218596f50SGeorge McCollister err = notifier_from_errno(err);
287318596f50SGeorge McCollister } else {
287418596f50SGeorge McCollister dsa_port_hsr_leave(dp, info->upper_dev);
287518596f50SGeorge McCollister err = NOTIFY_OK;
287618596f50SGeorge McCollister }
2877058102a6STobias Waldekranz }
2878058102a6STobias Waldekranz
2879058102a6STobias Waldekranz return err;
2880058102a6STobias Waldekranz }
2881058102a6STobias Waldekranz
dsa_slave_prechangeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)288274918945SVladimir Oltean static int dsa_slave_prechangeupper(struct net_device *dev,
288374918945SVladimir Oltean struct netdev_notifier_changeupper_info *info)
288474918945SVladimir Oltean {
2885*69a1e2d9SVladimir Oltean struct dsa_port *dp;
288674918945SVladimir Oltean
28874c3f80d2SVladimir Oltean if (!dsa_slave_dev_check(dev))
28884c3f80d2SVladimir Oltean return NOTIFY_DONE;
28894c3f80d2SVladimir Oltean
2890*69a1e2d9SVladimir Oltean dp = dsa_slave_to_port(dev);
2891*69a1e2d9SVladimir Oltean
289274918945SVladimir Oltean if (netif_is_bridge_master(info->upper_dev) && !info->linking)
28934e51bf44SVladimir Oltean dsa_port_pre_bridge_leave(dp, info->upper_dev);
289474918945SVladimir Oltean else if (netif_is_lag_master(info->upper_dev) && !info->linking)
28954e51bf44SVladimir Oltean dsa_port_pre_lag_leave(dp, info->upper_dev);
289674918945SVladimir Oltean /* dsa_port_pre_hsr_leave is not yet necessary since hsr cannot be
289774918945SVladimir Oltean * meaningfully enslaved to a bridge yet
289874918945SVladimir Oltean */
289974918945SVladimir Oltean
29004e51bf44SVladimir Oltean return NOTIFY_DONE;
290174918945SVladimir Oltean }
290274918945SVladimir Oltean
2903058102a6STobias Waldekranz static int
dsa_slave_lag_changeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)2904058102a6STobias Waldekranz dsa_slave_lag_changeupper(struct net_device *dev,
2905058102a6STobias Waldekranz struct netdev_notifier_changeupper_info *info)
2906058102a6STobias Waldekranz {
2907058102a6STobias Waldekranz struct net_device *lower;
2908058102a6STobias Waldekranz struct list_head *iter;
2909058102a6STobias Waldekranz int err = NOTIFY_DONE;
2910058102a6STobias Waldekranz struct dsa_port *dp;
2911058102a6STobias Waldekranz
29124c3f80d2SVladimir Oltean if (!netif_is_lag_master(dev))
29134c3f80d2SVladimir Oltean return err;
29144c3f80d2SVladimir Oltean
2915058102a6STobias Waldekranz netdev_for_each_lower_dev(dev, lower, iter) {
2916058102a6STobias Waldekranz if (!dsa_slave_dev_check(lower))
2917058102a6STobias Waldekranz continue;
2918058102a6STobias Waldekranz
2919058102a6STobias Waldekranz dp = dsa_slave_to_port(lower);
2920dedd6a00SVladimir Oltean if (!dp->lag)
2921058102a6STobias Waldekranz /* Software LAG */
2922058102a6STobias Waldekranz continue;
2923058102a6STobias Waldekranz
2924058102a6STobias Waldekranz err = dsa_slave_changeupper(lower, info);
2925058102a6STobias Waldekranz if (notifier_to_errno(err))
2926058102a6STobias Waldekranz break;
29276debb68aSVivien Didelot }
2928b73adef6SFlorian Fainelli
29298e92ab3aSVivien Didelot return err;
2930b73adef6SFlorian Fainelli }
2931b73adef6SFlorian Fainelli
293274918945SVladimir Oltean /* Same as dsa_slave_lag_changeupper() except that it calls
293374918945SVladimir Oltean * dsa_slave_prechangeupper()
293474918945SVladimir Oltean */
293574918945SVladimir Oltean static int
dsa_slave_lag_prechangeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)293674918945SVladimir Oltean dsa_slave_lag_prechangeupper(struct net_device *dev,
293774918945SVladimir Oltean struct netdev_notifier_changeupper_info *info)
293874918945SVladimir Oltean {
293974918945SVladimir Oltean struct net_device *lower;
294074918945SVladimir Oltean struct list_head *iter;
294174918945SVladimir Oltean int err = NOTIFY_DONE;
294274918945SVladimir Oltean struct dsa_port *dp;
294374918945SVladimir Oltean
29444c3f80d2SVladimir Oltean if (!netif_is_lag_master(dev))
29454c3f80d2SVladimir Oltean return err;
29464c3f80d2SVladimir Oltean
294774918945SVladimir Oltean netdev_for_each_lower_dev(dev, lower, iter) {
294874918945SVladimir Oltean if (!dsa_slave_dev_check(lower))
294974918945SVladimir Oltean continue;
295074918945SVladimir Oltean
295174918945SVladimir Oltean dp = dsa_slave_to_port(lower);
2952dedd6a00SVladimir Oltean if (!dp->lag)
295374918945SVladimir Oltean /* Software LAG */
295474918945SVladimir Oltean continue;
295574918945SVladimir Oltean
295674918945SVladimir Oltean err = dsa_slave_prechangeupper(lower, info);
295774918945SVladimir Oltean if (notifier_to_errno(err))
295874918945SVladimir Oltean break;
295974918945SVladimir Oltean }
296074918945SVladimir Oltean
296174918945SVladimir Oltean return err;
296274918945SVladimir Oltean }
296374918945SVladimir Oltean
2964eb46e8daSVladimir Oltean static int
dsa_prevent_bridging_8021q_upper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)2965eb46e8daSVladimir Oltean dsa_prevent_bridging_8021q_upper(struct net_device *dev,
2966eb46e8daSVladimir Oltean struct netdev_notifier_changeupper_info *info)
2967cc1d5bdaSFlorian Fainelli {
2968cc1d5bdaSFlorian Fainelli struct netlink_ext_ack *ext_ack;
296936cbf39bSVladimir Oltean struct net_device *slave, *br;
2970cc1d5bdaSFlorian Fainelli struct dsa_port *dp;
2971cc1d5bdaSFlorian Fainelli
2972cc1d5bdaSFlorian Fainelli ext_ack = netdev_notifier_info_to_extack(&info->info);
2973cc1d5bdaSFlorian Fainelli
2974cc1d5bdaSFlorian Fainelli if (!is_vlan_dev(dev))
2975cc1d5bdaSFlorian Fainelli return NOTIFY_DONE;
2976cc1d5bdaSFlorian Fainelli
2977cc1d5bdaSFlorian Fainelli slave = vlan_dev_real_dev(dev);
2978cc1d5bdaSFlorian Fainelli if (!dsa_slave_dev_check(slave))
2979cc1d5bdaSFlorian Fainelli return NOTIFY_DONE;
2980cc1d5bdaSFlorian Fainelli
2981cc1d5bdaSFlorian Fainelli dp = dsa_slave_to_port(slave);
298236cbf39bSVladimir Oltean br = dsa_port_bridge_dev_get(dp);
298336cbf39bSVladimir Oltean if (!br)
2984cc1d5bdaSFlorian Fainelli return NOTIFY_DONE;
2985cc1d5bdaSFlorian Fainelli
2986cc1d5bdaSFlorian Fainelli /* Deny enslaving a VLAN device into a VLAN-aware bridge */
298736cbf39bSVladimir Oltean if (br_vlan_enabled(br) &&
2988cc1d5bdaSFlorian Fainelli netif_is_bridge_master(info->upper_dev) && info->linking) {
2989cc1d5bdaSFlorian Fainelli NL_SET_ERR_MSG_MOD(ext_ack,
2990cc1d5bdaSFlorian Fainelli "Cannot enslave VLAN device into VLAN aware bridge");
2991cc1d5bdaSFlorian Fainelli return notifier_from_errno(-EINVAL);
2992cc1d5bdaSFlorian Fainelli }
2993cc1d5bdaSFlorian Fainelli
2994cc1d5bdaSFlorian Fainelli return NOTIFY_DONE;
2995cc1d5bdaSFlorian Fainelli }
2996cc1d5bdaSFlorian Fainelli
29972b138406SVladimir Oltean static int
dsa_slave_check_8021q_upper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)29982b138406SVladimir Oltean dsa_slave_check_8021q_upper(struct net_device *dev,
29992b138406SVladimir Oltean struct netdev_notifier_changeupper_info *info)
30002b138406SVladimir Oltean {
30012b138406SVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
300236cbf39bSVladimir Oltean struct net_device *br = dsa_port_bridge_dev_get(dp);
30032b138406SVladimir Oltean struct bridge_vlan_info br_info;
30042b138406SVladimir Oltean struct netlink_ext_ack *extack;
30052b138406SVladimir Oltean int err = NOTIFY_DONE;
30062b138406SVladimir Oltean u16 vid;
30072b138406SVladimir Oltean
3008adb256ebSVladimir Oltean if (!br || !br_vlan_enabled(br))
30092b138406SVladimir Oltean return NOTIFY_DONE;
30102b138406SVladimir Oltean
30112b138406SVladimir Oltean extack = netdev_notifier_info_to_extack(&info->info);
30122b138406SVladimir Oltean vid = vlan_dev_vlan_id(info->upper_dev);
30132b138406SVladimir Oltean
30142b138406SVladimir Oltean /* br_vlan_get_info() returns -EINVAL or -ENOENT if the
30152b138406SVladimir Oltean * device, respectively the VID is not found, returning
30162b138406SVladimir Oltean * 0 means success, which is a failure for us here.
30172b138406SVladimir Oltean */
30182b138406SVladimir Oltean err = br_vlan_get_info(br, vid, &br_info);
30192b138406SVladimir Oltean if (err == 0) {
30202b138406SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
30212b138406SVladimir Oltean "This VLAN is already configured by the bridge");
30222b138406SVladimir Oltean return notifier_from_errno(-EBUSY);
30232b138406SVladimir Oltean }
30242b138406SVladimir Oltean
30252b138406SVladimir Oltean return NOTIFY_DONE;
30262b138406SVladimir Oltean }
30272b138406SVladimir Oltean
30284ede74e7SVladimir Oltean static int
dsa_slave_prechangeupper_sanity_check(struct net_device * dev,struct netdev_notifier_changeupper_info * info)30294ede74e7SVladimir Oltean dsa_slave_prechangeupper_sanity_check(struct net_device *dev,
30304ede74e7SVladimir Oltean struct netdev_notifier_changeupper_info *info)
3031b73adef6SFlorian Fainelli {
3032e358bef7SVladimir Oltean struct dsa_switch *ds;
3033e358bef7SVladimir Oltean struct dsa_port *dp;
3034e358bef7SVladimir Oltean int err;
30352b138406SVladimir Oltean
303653bade8aSFlorian Fainelli if (!dsa_slave_dev_check(dev))
30374ede74e7SVladimir Oltean return dsa_prevent_bridging_8021q_upper(dev, info);
30382b138406SVladimir Oltean
3039e358bef7SVladimir Oltean dp = dsa_slave_to_port(dev);
3040e358bef7SVladimir Oltean ds = dp->ds;
3041e358bef7SVladimir Oltean
3042e358bef7SVladimir Oltean if (ds->ops->port_prechangeupper) {
3043e358bef7SVladimir Oltean err = ds->ops->port_prechangeupper(ds, dp->index, info);
3044e358bef7SVladimir Oltean if (err)
3045e358bef7SVladimir Oltean return notifier_from_errno(err);
3046e358bef7SVladimir Oltean }
3047e358bef7SVladimir Oltean
30482b138406SVladimir Oltean if (is_vlan_dev(info->upper_dev))
30494ede74e7SVladimir Oltean return dsa_slave_check_8021q_upper(dev, info);
30504ede74e7SVladimir Oltean
30514ede74e7SVladimir Oltean return NOTIFY_DONE;
30524ede74e7SVladimir Oltean }
30534ede74e7SVladimir Oltean
3054acc43b7bSVladimir Oltean /* To be eligible as a DSA master, a LAG must have all lower interfaces be
3055acc43b7bSVladimir Oltean * eligible DSA masters. Additionally, all LAG slaves must be DSA masters of
3056acc43b7bSVladimir Oltean * switches in the same switch tree.
3057acc43b7bSVladimir Oltean */
dsa_lag_master_validate(struct net_device * lag_dev,struct netlink_ext_ack * extack)3058acc43b7bSVladimir Oltean static int dsa_lag_master_validate(struct net_device *lag_dev,
3059acc43b7bSVladimir Oltean struct netlink_ext_ack *extack)
3060acc43b7bSVladimir Oltean {
3061acc43b7bSVladimir Oltean struct net_device *lower1, *lower2;
3062acc43b7bSVladimir Oltean struct list_head *iter1, *iter2;
3063acc43b7bSVladimir Oltean
3064acc43b7bSVladimir Oltean netdev_for_each_lower_dev(lag_dev, lower1, iter1) {
3065acc43b7bSVladimir Oltean netdev_for_each_lower_dev(lag_dev, lower2, iter2) {
3066acc43b7bSVladimir Oltean if (!netdev_uses_dsa(lower1) ||
3067acc43b7bSVladimir Oltean !netdev_uses_dsa(lower2)) {
3068acc43b7bSVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
3069acc43b7bSVladimir Oltean "All LAG ports must be eligible as DSA masters");
3070acc43b7bSVladimir Oltean return notifier_from_errno(-EINVAL);
3071acc43b7bSVladimir Oltean }
3072acc43b7bSVladimir Oltean
3073acc43b7bSVladimir Oltean if (lower1 == lower2)
3074acc43b7bSVladimir Oltean continue;
3075acc43b7bSVladimir Oltean
3076acc43b7bSVladimir Oltean if (!dsa_port_tree_same(lower1->dsa_ptr,
3077acc43b7bSVladimir Oltean lower2->dsa_ptr)) {
3078acc43b7bSVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
3079acc43b7bSVladimir Oltean "LAG contains DSA masters of disjoint switch trees");
3080acc43b7bSVladimir Oltean return notifier_from_errno(-EINVAL);
3081acc43b7bSVladimir Oltean }
3082acc43b7bSVladimir Oltean }
3083acc43b7bSVladimir Oltean }
3084acc43b7bSVladimir Oltean
3085acc43b7bSVladimir Oltean return NOTIFY_DONE;
3086acc43b7bSVladimir Oltean }
3087acc43b7bSVladimir Oltean
30884f03dcc6SVladimir Oltean static int
dsa_master_prechangeupper_sanity_check(struct net_device * master,struct netdev_notifier_changeupper_info * info)30894f03dcc6SVladimir Oltean dsa_master_prechangeupper_sanity_check(struct net_device *master,
30904f03dcc6SVladimir Oltean struct netdev_notifier_changeupper_info *info)
30914f03dcc6SVladimir Oltean {
3092acc43b7bSVladimir Oltean struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info);
30934f03dcc6SVladimir Oltean
30944f03dcc6SVladimir Oltean if (!netdev_uses_dsa(master))
30954f03dcc6SVladimir Oltean return NOTIFY_DONE;
30964f03dcc6SVladimir Oltean
30974f03dcc6SVladimir Oltean if (!info->linking)
30984f03dcc6SVladimir Oltean return NOTIFY_DONE;
30994f03dcc6SVladimir Oltean
31004f03dcc6SVladimir Oltean /* Allow DSA switch uppers */
31014f03dcc6SVladimir Oltean if (dsa_slave_dev_check(info->upper_dev))
31024f03dcc6SVladimir Oltean return NOTIFY_DONE;
31034f03dcc6SVladimir Oltean
31044f03dcc6SVladimir Oltean /* Allow bridge uppers of DSA masters, subject to further
31054f03dcc6SVladimir Oltean * restrictions in dsa_bridge_prechangelower_sanity_check()
31064f03dcc6SVladimir Oltean */
31074f03dcc6SVladimir Oltean if (netif_is_bridge_master(info->upper_dev))
31084f03dcc6SVladimir Oltean return NOTIFY_DONE;
31094f03dcc6SVladimir Oltean
3110acc43b7bSVladimir Oltean /* Allow LAG uppers, subject to further restrictions in
3111acc43b7bSVladimir Oltean * dsa_lag_master_prechangelower_sanity_check()
3112acc43b7bSVladimir Oltean */
3113acc43b7bSVladimir Oltean if (netif_is_lag_master(info->upper_dev))
3114acc43b7bSVladimir Oltean return dsa_lag_master_validate(info->upper_dev, extack);
31154f03dcc6SVladimir Oltean
31164f03dcc6SVladimir Oltean NL_SET_ERR_MSG_MOD(extack,
31174f03dcc6SVladimir Oltean "DSA master cannot join unknown upper interfaces");
31184f03dcc6SVladimir Oltean return notifier_from_errno(-EBUSY);
31194f03dcc6SVladimir Oltean }
31204f03dcc6SVladimir Oltean
3121acc43b7bSVladimir Oltean static int
dsa_lag_master_prechangelower_sanity_check(struct net_device * dev,struct netdev_notifier_changeupper_info * info)3122acc43b7bSVladimir Oltean dsa_lag_master_prechangelower_sanity_check(struct net_device *dev,
3123acc43b7bSVladimir Oltean struct netdev_notifier_changeupper_info *info)
3124acc43b7bSVladimir Oltean {
3125acc43b7bSVladimir Oltean struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info);
3126acc43b7bSVladimir Oltean struct net_device *lag_dev = info->upper_dev;
3127acc43b7bSVladimir Oltean struct net_device *lower;
3128acc43b7bSVladimir Oltean struct list_head *iter;
3129acc43b7bSVladimir Oltean
3130acc43b7bSVladimir Oltean if (!netdev_uses_dsa(lag_dev) || !netif_is_lag_master(lag_dev))
3131acc43b7bSVladimir Oltean return NOTIFY_DONE;
3132acc43b7bSVladimir Oltean
3133acc43b7bSVladimir Oltean if (!info->linking)
3134acc43b7bSVladimir Oltean return NOTIFY_DONE;
3135acc43b7bSVladimir Oltean
3136acc43b7bSVladimir Oltean if (!netdev_uses_dsa(dev)) {
3137acc43b7bSVladimir Oltean NL_SET_ERR_MSG(extack,
3138acc43b7bSVladimir Oltean "Only DSA masters can join a LAG DSA master");
3139acc43b7bSVladimir Oltean return notifier_from_errno(-EINVAL);
3140acc43b7bSVladimir Oltean }
3141acc43b7bSVladimir Oltean
3142acc43b7bSVladimir Oltean netdev_for_each_lower_dev(lag_dev, lower, iter) {
3143acc43b7bSVladimir Oltean if (!dsa_port_tree_same(dev->dsa_ptr, lower->dsa_ptr)) {
3144acc43b7bSVladimir Oltean NL_SET_ERR_MSG(extack,
3145acc43b7bSVladimir Oltean "Interface is DSA master for a different switch tree than this LAG");
3146acc43b7bSVladimir Oltean return notifier_from_errno(-EINVAL);
3147acc43b7bSVladimir Oltean }
3148acc43b7bSVladimir Oltean
3149acc43b7bSVladimir Oltean break;
3150acc43b7bSVladimir Oltean }
3151acc43b7bSVladimir Oltean
3152acc43b7bSVladimir Oltean return NOTIFY_DONE;
3153acc43b7bSVladimir Oltean }
3154acc43b7bSVladimir Oltean
3155920a33cdSVladimir Oltean /* Don't allow bridging of DSA masters, since the bridge layer rx_handler
3156920a33cdSVladimir Oltean * prevents the DSA fake ethertype handler to be invoked, so we don't get the
3157920a33cdSVladimir Oltean * chance to strip off and parse the DSA switch tag protocol header (the bridge
3158920a33cdSVladimir Oltean * layer just returns RX_HANDLER_CONSUMED, stopping RX processing for these
3159920a33cdSVladimir Oltean * frames).
3160920a33cdSVladimir Oltean * The only case where that would not be an issue is when bridging can already
3161920a33cdSVladimir Oltean * be offloaded, such as when the DSA master is itself a DSA or plain switchdev
3162920a33cdSVladimir Oltean * port, and is bridged only with other ports from the same hardware device.
3163920a33cdSVladimir Oltean */
3164920a33cdSVladimir Oltean static int
dsa_bridge_prechangelower_sanity_check(struct net_device * new_lower,struct netdev_notifier_changeupper_info * info)3165920a33cdSVladimir Oltean dsa_bridge_prechangelower_sanity_check(struct net_device *new_lower,
3166920a33cdSVladimir Oltean struct netdev_notifier_changeupper_info *info)
3167920a33cdSVladimir Oltean {
3168920a33cdSVladimir Oltean struct net_device *br = info->upper_dev;
3169920a33cdSVladimir Oltean struct netlink_ext_ack *extack;
3170920a33cdSVladimir Oltean struct net_device *lower;
3171920a33cdSVladimir Oltean struct list_head *iter;
3172920a33cdSVladimir Oltean
3173920a33cdSVladimir Oltean if (!netif_is_bridge_master(br))
3174920a33cdSVladimir Oltean return NOTIFY_DONE;
3175920a33cdSVladimir Oltean
3176920a33cdSVladimir Oltean if (!info->linking)
3177920a33cdSVladimir Oltean return NOTIFY_DONE;
3178920a33cdSVladimir Oltean
3179920a33cdSVladimir Oltean extack = netdev_notifier_info_to_extack(&info->info);
3180920a33cdSVladimir Oltean
3181920a33cdSVladimir Oltean netdev_for_each_lower_dev(br, lower, iter) {
3182920a33cdSVladimir Oltean if (!netdev_uses_dsa(new_lower) && !netdev_uses_dsa(lower))
3183920a33cdSVladimir Oltean continue;
3184920a33cdSVladimir Oltean
3185920a33cdSVladimir Oltean if (!netdev_port_same_parent_id(lower, new_lower)) {
3186920a33cdSVladimir Oltean NL_SET_ERR_MSG(extack,
3187920a33cdSVladimir Oltean "Cannot do software bridging with a DSA master");
3188920a33cdSVladimir Oltean return notifier_from_errno(-EINVAL);
3189920a33cdSVladimir Oltean }
3190920a33cdSVladimir Oltean }
3191920a33cdSVladimir Oltean
3192920a33cdSVladimir Oltean return NOTIFY_DONE;
3193920a33cdSVladimir Oltean }
3194920a33cdSVladimir Oltean
dsa_tree_migrate_ports_from_lag_master(struct dsa_switch_tree * dst,struct net_device * lag_dev)3195acc43b7bSVladimir Oltean static void dsa_tree_migrate_ports_from_lag_master(struct dsa_switch_tree *dst,
3196acc43b7bSVladimir Oltean struct net_device *lag_dev)
3197acc43b7bSVladimir Oltean {
3198acc43b7bSVladimir Oltean struct net_device *new_master = dsa_tree_find_first_master(dst);
3199acc43b7bSVladimir Oltean struct dsa_port *dp;
3200acc43b7bSVladimir Oltean int err;
3201acc43b7bSVladimir Oltean
3202acc43b7bSVladimir Oltean dsa_tree_for_each_user_port(dp, dst) {
3203acc43b7bSVladimir Oltean if (dsa_port_to_master(dp) != lag_dev)
3204acc43b7bSVladimir Oltean continue;
3205acc43b7bSVladimir Oltean
3206acc43b7bSVladimir Oltean err = dsa_slave_change_master(dp->slave, new_master, NULL);
3207acc43b7bSVladimir Oltean if (err) {
3208acc43b7bSVladimir Oltean netdev_err(dp->slave,
3209acc43b7bSVladimir Oltean "failed to restore master to %s: %pe\n",
3210acc43b7bSVladimir Oltean new_master->name, ERR_PTR(err));
3211acc43b7bSVladimir Oltean }
3212acc43b7bSVladimir Oltean }
3213acc43b7bSVladimir Oltean }
3214acc43b7bSVladimir Oltean
dsa_master_lag_join(struct net_device * master,struct net_device * lag_dev,struct netdev_lag_upper_info * uinfo,struct netlink_ext_ack * extack)3215acc43b7bSVladimir Oltean static int dsa_master_lag_join(struct net_device *master,
3216acc43b7bSVladimir Oltean struct net_device *lag_dev,
3217acc43b7bSVladimir Oltean struct netdev_lag_upper_info *uinfo,
3218acc43b7bSVladimir Oltean struct netlink_ext_ack *extack)
3219acc43b7bSVladimir Oltean {
3220acc43b7bSVladimir Oltean struct dsa_port *cpu_dp = master->dsa_ptr;
3221acc43b7bSVladimir Oltean struct dsa_switch_tree *dst = cpu_dp->dst;
3222acc43b7bSVladimir Oltean struct dsa_port *dp;
3223acc43b7bSVladimir Oltean int err;
3224acc43b7bSVladimir Oltean
3225acc43b7bSVladimir Oltean err = dsa_master_lag_setup(lag_dev, cpu_dp, uinfo, extack);
3226acc43b7bSVladimir Oltean if (err)
3227acc43b7bSVladimir Oltean return err;
3228acc43b7bSVladimir Oltean
3229acc43b7bSVladimir Oltean dsa_tree_for_each_user_port(dp, dst) {
3230acc43b7bSVladimir Oltean if (dsa_port_to_master(dp) != master)
3231acc43b7bSVladimir Oltean continue;
3232acc43b7bSVladimir Oltean
3233acc43b7bSVladimir Oltean err = dsa_slave_change_master(dp->slave, lag_dev, extack);
3234acc43b7bSVladimir Oltean if (err)
3235acc43b7bSVladimir Oltean goto restore;
3236acc43b7bSVladimir Oltean }
3237acc43b7bSVladimir Oltean
3238acc43b7bSVladimir Oltean return 0;
3239acc43b7bSVladimir Oltean
3240acc43b7bSVladimir Oltean restore:
3241acc43b7bSVladimir Oltean dsa_tree_for_each_user_port_continue_reverse(dp, dst) {
3242acc43b7bSVladimir Oltean if (dsa_port_to_master(dp) != lag_dev)
3243acc43b7bSVladimir Oltean continue;
3244acc43b7bSVladimir Oltean
3245acc43b7bSVladimir Oltean err = dsa_slave_change_master(dp->slave, master, NULL);
3246acc43b7bSVladimir Oltean if (err) {
3247acc43b7bSVladimir Oltean netdev_err(dp->slave,
3248acc43b7bSVladimir Oltean "failed to restore master to %s: %pe\n",
3249acc43b7bSVladimir Oltean master->name, ERR_PTR(err));
3250acc43b7bSVladimir Oltean }
3251acc43b7bSVladimir Oltean }
3252acc43b7bSVladimir Oltean
3253acc43b7bSVladimir Oltean dsa_master_lag_teardown(lag_dev, master->dsa_ptr);
3254acc43b7bSVladimir Oltean
3255acc43b7bSVladimir Oltean return err;
3256acc43b7bSVladimir Oltean }
3257acc43b7bSVladimir Oltean
dsa_master_lag_leave(struct net_device * master,struct net_device * lag_dev)3258acc43b7bSVladimir Oltean static void dsa_master_lag_leave(struct net_device *master,
3259acc43b7bSVladimir Oltean struct net_device *lag_dev)
3260acc43b7bSVladimir Oltean {
3261acc43b7bSVladimir Oltean struct dsa_port *dp, *cpu_dp = lag_dev->dsa_ptr;
3262acc43b7bSVladimir Oltean struct dsa_switch_tree *dst = cpu_dp->dst;
3263acc43b7bSVladimir Oltean struct dsa_port *new_cpu_dp = NULL;
3264acc43b7bSVladimir Oltean struct net_device *lower;
3265acc43b7bSVladimir Oltean struct list_head *iter;
3266acc43b7bSVladimir Oltean
3267acc43b7bSVladimir Oltean netdev_for_each_lower_dev(lag_dev, lower, iter) {
3268acc43b7bSVladimir Oltean if (netdev_uses_dsa(lower)) {
3269acc43b7bSVladimir Oltean new_cpu_dp = lower->dsa_ptr;
3270acc43b7bSVladimir Oltean break;
3271acc43b7bSVladimir Oltean }
3272acc43b7bSVladimir Oltean }
3273acc43b7bSVladimir Oltean
3274acc43b7bSVladimir Oltean if (new_cpu_dp) {
3275acc43b7bSVladimir Oltean /* Update the CPU port of the user ports still under the LAG
3276acc43b7bSVladimir Oltean * so that dsa_port_to_master() continues to work properly
3277acc43b7bSVladimir Oltean */
3278acc43b7bSVladimir Oltean dsa_tree_for_each_user_port(dp, dst)
3279acc43b7bSVladimir Oltean if (dsa_port_to_master(dp) == lag_dev)
3280acc43b7bSVladimir Oltean dp->cpu_dp = new_cpu_dp;
3281acc43b7bSVladimir Oltean
3282acc43b7bSVladimir Oltean /* Update the index of the virtual CPU port to match the lowest
3283acc43b7bSVladimir Oltean * physical CPU port
3284acc43b7bSVladimir Oltean */
3285acc43b7bSVladimir Oltean lag_dev->dsa_ptr = new_cpu_dp;
3286acc43b7bSVladimir Oltean wmb();
3287acc43b7bSVladimir Oltean } else {
3288acc43b7bSVladimir Oltean /* If the LAG DSA master has no ports left, migrate back all
3289acc43b7bSVladimir Oltean * user ports to the first physical CPU port
3290acc43b7bSVladimir Oltean */
3291acc43b7bSVladimir Oltean dsa_tree_migrate_ports_from_lag_master(dst, lag_dev);
3292acc43b7bSVladimir Oltean }
3293acc43b7bSVladimir Oltean
3294acc43b7bSVladimir Oltean /* This DSA master has left its LAG in any case, so let
3295acc43b7bSVladimir Oltean * the CPU port leave the hardware LAG as well
3296acc43b7bSVladimir Oltean */
3297acc43b7bSVladimir Oltean dsa_master_lag_teardown(lag_dev, master->dsa_ptr);
3298acc43b7bSVladimir Oltean }
3299acc43b7bSVladimir Oltean
dsa_master_changeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)3300acc43b7bSVladimir Oltean static int dsa_master_changeupper(struct net_device *dev,
3301acc43b7bSVladimir Oltean struct netdev_notifier_changeupper_info *info)
3302acc43b7bSVladimir Oltean {
3303acc43b7bSVladimir Oltean struct netlink_ext_ack *extack;
3304acc43b7bSVladimir Oltean int err = NOTIFY_DONE;
3305acc43b7bSVladimir Oltean
3306acc43b7bSVladimir Oltean if (!netdev_uses_dsa(dev))
3307acc43b7bSVladimir Oltean return err;
3308acc43b7bSVladimir Oltean
3309acc43b7bSVladimir Oltean extack = netdev_notifier_info_to_extack(&info->info);
3310acc43b7bSVladimir Oltean
3311acc43b7bSVladimir Oltean if (netif_is_lag_master(info->upper_dev)) {
3312acc43b7bSVladimir Oltean if (info->linking) {
3313acc43b7bSVladimir Oltean err = dsa_master_lag_join(dev, info->upper_dev,
3314acc43b7bSVladimir Oltean info->upper_info, extack);
3315acc43b7bSVladimir Oltean err = notifier_from_errno(err);
3316acc43b7bSVladimir Oltean } else {
3317acc43b7bSVladimir Oltean dsa_master_lag_leave(dev, info->upper_dev);
3318acc43b7bSVladimir Oltean err = NOTIFY_OK;
3319acc43b7bSVladimir Oltean }
3320acc43b7bSVladimir Oltean }
3321acc43b7bSVladimir Oltean
3322acc43b7bSVladimir Oltean return err;
3323acc43b7bSVladimir Oltean }
3324acc43b7bSVladimir Oltean
dsa_slave_netdevice_event(struct notifier_block * nb,unsigned long event,void * ptr)33254ede74e7SVladimir Oltean static int dsa_slave_netdevice_event(struct notifier_block *nb,
33264ede74e7SVladimir Oltean unsigned long event, void *ptr)
33274ede74e7SVladimir Oltean {
33284ede74e7SVladimir Oltean struct net_device *dev = netdev_notifier_info_to_dev(ptr);
33294ede74e7SVladimir Oltean
33304ede74e7SVladimir Oltean switch (event) {
33314ede74e7SVladimir Oltean case NETDEV_PRECHANGEUPPER: {
33324ede74e7SVladimir Oltean struct netdev_notifier_changeupper_info *info = ptr;
33334ede74e7SVladimir Oltean int err;
33344ede74e7SVladimir Oltean
33354ede74e7SVladimir Oltean err = dsa_slave_prechangeupper_sanity_check(dev, info);
33360498277eSVladimir Oltean if (notifier_to_errno(err))
33374ede74e7SVladimir Oltean return err;
33384ede74e7SVladimir Oltean
33394f03dcc6SVladimir Oltean err = dsa_master_prechangeupper_sanity_check(dev, info);
33404f03dcc6SVladimir Oltean if (notifier_to_errno(err))
33414f03dcc6SVladimir Oltean return err;
33424f03dcc6SVladimir Oltean
3343acc43b7bSVladimir Oltean err = dsa_lag_master_prechangelower_sanity_check(dev, info);
3344acc43b7bSVladimir Oltean if (notifier_to_errno(err))
3345acc43b7bSVladimir Oltean return err;
3346acc43b7bSVladimir Oltean
3347920a33cdSVladimir Oltean err = dsa_bridge_prechangelower_sanity_check(dev, info);
3348920a33cdSVladimir Oltean if (notifier_to_errno(err))
3349920a33cdSVladimir Oltean return err;
3350920a33cdSVladimir Oltean
33514c3f80d2SVladimir Oltean err = dsa_slave_prechangeupper(dev, ptr);
33524c3f80d2SVladimir Oltean if (notifier_to_errno(err))
33534c3f80d2SVladimir Oltean return err;
335474918945SVladimir Oltean
33554c3f80d2SVladimir Oltean err = dsa_slave_lag_prechangeupper(dev, ptr);
33564c3f80d2SVladimir Oltean if (notifier_to_errno(err))
33574c3f80d2SVladimir Oltean return err;
335874918945SVladimir Oltean
335983501299SVladimir Oltean break;
33602b138406SVladimir Oltean }
33614c3f80d2SVladimir Oltean case NETDEV_CHANGEUPPER: {
33624c3f80d2SVladimir Oltean int err;
3363058102a6STobias Waldekranz
33644c3f80d2SVladimir Oltean err = dsa_slave_changeupper(dev, ptr);
33654c3f80d2SVladimir Oltean if (notifier_to_errno(err))
33664c3f80d2SVladimir Oltean return err;
33674c3f80d2SVladimir Oltean
33684c3f80d2SVladimir Oltean err = dsa_slave_lag_changeupper(dev, ptr);
33694c3f80d2SVladimir Oltean if (notifier_to_errno(err))
33704c3f80d2SVladimir Oltean return err;
3371058102a6STobias Waldekranz
3372acc43b7bSVladimir Oltean err = dsa_master_changeupper(dev, ptr);
3373acc43b7bSVladimir Oltean if (notifier_to_errno(err))
3374acc43b7bSVladimir Oltean return err;
3375acc43b7bSVladimir Oltean
3376058102a6STobias Waldekranz break;
33774c3f80d2SVladimir Oltean }
3378058102a6STobias Waldekranz case NETDEV_CHANGELOWERSTATE: {
3379058102a6STobias Waldekranz struct netdev_notifier_changelowerstate_info *info = ptr;
3380058102a6STobias Waldekranz struct dsa_port *dp;
33810a6d58a7SDan Carpenter int err = 0;
3382058102a6STobias Waldekranz
3383acc43b7bSVladimir Oltean if (dsa_slave_dev_check(dev)) {
3384058102a6STobias Waldekranz dp = dsa_slave_to_port(dev);
3385058102a6STobias Waldekranz
3386058102a6STobias Waldekranz err = dsa_port_lag_change(dp, info->lower_state_info);
3387acc43b7bSVladimir Oltean }
3388acc43b7bSVladimir Oltean
3389acc43b7bSVladimir Oltean /* Mirror LAG port events on DSA masters that are in
3390acc43b7bSVladimir Oltean * a LAG towards their respective switch CPU ports
3391acc43b7bSVladimir Oltean */
3392acc43b7bSVladimir Oltean if (netdev_uses_dsa(dev)) {
3393acc43b7bSVladimir Oltean dp = dev->dsa_ptr;
3394acc43b7bSVladimir Oltean
3395acc43b7bSVladimir Oltean err = dsa_port_lag_change(dp, info->lower_state_info);
3396acc43b7bSVladimir Oltean }
3397acc43b7bSVladimir Oltean
3398058102a6STobias Waldekranz return notifier_from_errno(err);
3399058102a6STobias Waldekranz }
3400295ab96fSVladimir Oltean case NETDEV_CHANGE:
3401295ab96fSVladimir Oltean case NETDEV_UP: {
3402295ab96fSVladimir Oltean /* Track state of master port.
3403295ab96fSVladimir Oltean * DSA driver may require the master port (and indirectly
3404295ab96fSVladimir Oltean * the tagger) to be available for some special operation.
3405295ab96fSVladimir Oltean */
3406295ab96fSVladimir Oltean if (netdev_uses_dsa(dev)) {
3407295ab96fSVladimir Oltean struct dsa_port *cpu_dp = dev->dsa_ptr;
3408295ab96fSVladimir Oltean struct dsa_switch_tree *dst = cpu_dp->ds->dst;
3409295ab96fSVladimir Oltean
3410295ab96fSVladimir Oltean /* Track when the master port is UP */
3411295ab96fSVladimir Oltean dsa_tree_master_oper_state_change(dst, dev,
3412295ab96fSVladimir Oltean netif_oper_up(dev));
3413295ab96fSVladimir Oltean
3414295ab96fSVladimir Oltean /* Track when the master port is ready and can accept
3415295ab96fSVladimir Oltean * packet.
3416295ab96fSVladimir Oltean * NETDEV_UP event is not enough to flag a port as ready.
3417295ab96fSVladimir Oltean * We also have to wait for linkwatch_do_dev to dev_activate
3418295ab96fSVladimir Oltean * and emit a NETDEV_CHANGE event.
3419295ab96fSVladimir Oltean * We check if a master port is ready by checking if the dev
3420295ab96fSVladimir Oltean * have a qdisc assigned and is not noop.
3421295ab96fSVladimir Oltean */
3422295ab96fSVladimir Oltean dsa_tree_master_admin_state_change(dst, dev,
3423295ab96fSVladimir Oltean !qdisc_tx_is_noop(dev));
3424295ab96fSVladimir Oltean
3425295ab96fSVladimir Oltean return NOTIFY_OK;
3426295ab96fSVladimir Oltean }
3427295ab96fSVladimir Oltean
3428295ab96fSVladimir Oltean return NOTIFY_DONE;
3429295ab96fSVladimir Oltean }
3430c0a8a9c2SVladimir Oltean case NETDEV_GOING_DOWN: {
3431c0a8a9c2SVladimir Oltean struct dsa_port *dp, *cpu_dp;
3432c0a8a9c2SVladimir Oltean struct dsa_switch_tree *dst;
3433c0a8a9c2SVladimir Oltean LIST_HEAD(close_list);
3434c0a8a9c2SVladimir Oltean
3435c0a8a9c2SVladimir Oltean if (!netdev_uses_dsa(dev))
3436c0a8a9c2SVladimir Oltean return NOTIFY_DONE;
3437c0a8a9c2SVladimir Oltean
3438c0a8a9c2SVladimir Oltean cpu_dp = dev->dsa_ptr;
3439c0a8a9c2SVladimir Oltean dst = cpu_dp->ds->dst;
3440c0a8a9c2SVladimir Oltean
3441295ab96fSVladimir Oltean dsa_tree_master_admin_state_change(dst, dev, false);
3442295ab96fSVladimir Oltean
3443c0a8a9c2SVladimir Oltean list_for_each_entry(dp, &dst->ports, list) {
3444d0004a02SVladimir Oltean if (!dsa_port_is_user(dp))
3445c0a8a9c2SVladimir Oltean continue;
3446c0a8a9c2SVladimir Oltean
34477136097eSVladimir Oltean if (dp->cpu_dp != cpu_dp)
34487136097eSVladimir Oltean continue;
34497136097eSVladimir Oltean
3450c0a8a9c2SVladimir Oltean list_add(&dp->slave->close_list, &close_list);
3451c0a8a9c2SVladimir Oltean }
3452c0a8a9c2SVladimir Oltean
3453c0a8a9c2SVladimir Oltean dev_close_many(&close_list, true);
3454c0a8a9c2SVladimir Oltean
3455c0a8a9c2SVladimir Oltean return NOTIFY_OK;
3456c0a8a9c2SVladimir Oltean }
3457c0a8a9c2SVladimir Oltean default:
3458c0a8a9c2SVladimir Oltean break;
3459cc1d5bdaSFlorian Fainelli }
3460b73adef6SFlorian Fainelli
3461b73adef6SFlorian Fainelli return NOTIFY_DONE;
3462b73adef6SFlorian Fainelli }
346388e4f0caSVivien Didelot
3464c4bb76a9SVladimir Oltean static void
dsa_fdb_offload_notify(struct dsa_switchdev_event_work * switchdev_work)3465c4bb76a9SVladimir Oltean dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work)
3466c4bb76a9SVladimir Oltean {
3467c35b57ceSVladimir Oltean struct switchdev_notifier_fdb_info info = {};
3468c4bb76a9SVladimir Oltean
3469c4bb76a9SVladimir Oltean info.addr = switchdev_work->addr;
3470c4bb76a9SVladimir Oltean info.vid = switchdev_work->vid;
3471c4bb76a9SVladimir Oltean info.offloaded = true;
3472c4bb76a9SVladimir Oltean call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
347393c79823SVladimir Oltean switchdev_work->orig_dev, &info.info, NULL);
3474c4bb76a9SVladimir Oltean }
3475c9eb3e0fSArkadi Sharshevsky
dsa_slave_switchdev_event_work(struct work_struct * work)3476c9eb3e0fSArkadi Sharshevsky static void dsa_slave_switchdev_event_work(struct work_struct *work)
3477c9eb3e0fSArkadi Sharshevsky {
3478c9eb3e0fSArkadi Sharshevsky struct dsa_switchdev_event_work *switchdev_work =
3479c9eb3e0fSArkadi Sharshevsky container_of(work, struct dsa_switchdev_event_work, work);
348068d6d71eSVladimir Oltean const unsigned char *addr = switchdev_work->addr;
3481e35f12e9SVladimir Oltean struct net_device *dev = switchdev_work->dev;
348268d6d71eSVladimir Oltean u16 vid = switchdev_work->vid;
3483e35f12e9SVladimir Oltean struct dsa_switch *ds;
3484c4bb76a9SVladimir Oltean struct dsa_port *dp;
3485c9eb3e0fSArkadi Sharshevsky int err;
3486c9eb3e0fSArkadi Sharshevsky
3487e35f12e9SVladimir Oltean dp = dsa_slave_to_port(dev);
3488e35f12e9SVladimir Oltean ds = dp->ds;
3489c4bb76a9SVladimir Oltean
3490c9eb3e0fSArkadi Sharshevsky switch (switchdev_work->event) {
3491c9eb3e0fSArkadi Sharshevsky case SWITCHDEV_FDB_ADD_TO_DEVICE:
34923dc80afcSVladimir Oltean if (switchdev_work->host_addr)
349368d6d71eSVladimir Oltean err = dsa_port_bridge_host_fdb_add(dp, addr, vid);
3494e212fa7cSVladimir Oltean else if (dp->lag)
349568d6d71eSVladimir Oltean err = dsa_port_lag_fdb_add(dp, addr, vid);
34963dc80afcSVladimir Oltean else
349768d6d71eSVladimir Oltean err = dsa_port_fdb_add(dp, addr, vid);
3498c9eb3e0fSArkadi Sharshevsky if (err) {
3499c4bb76a9SVladimir Oltean dev_err(ds->dev,
3500c4bb76a9SVladimir Oltean "port %d failed to add %pM vid %d to fdb: %d\n",
350168d6d71eSVladimir Oltean dp->index, addr, vid, err);
3502c9eb3e0fSArkadi Sharshevsky break;
3503c9eb3e0fSArkadi Sharshevsky }
3504c4bb76a9SVladimir Oltean dsa_fdb_offload_notify(switchdev_work);
3505c9eb3e0fSArkadi Sharshevsky break;
3506c9eb3e0fSArkadi Sharshevsky
3507c9eb3e0fSArkadi Sharshevsky case SWITCHDEV_FDB_DEL_TO_DEVICE:
35083dc80afcSVladimir Oltean if (switchdev_work->host_addr)
350968d6d71eSVladimir Oltean err = dsa_port_bridge_host_fdb_del(dp, addr, vid);
3510e212fa7cSVladimir Oltean else if (dp->lag)
351168d6d71eSVladimir Oltean err = dsa_port_lag_fdb_del(dp, addr, vid);
35123dc80afcSVladimir Oltean else
351368d6d71eSVladimir Oltean err = dsa_port_fdb_del(dp, addr, vid);
3514c9eb3e0fSArkadi Sharshevsky if (err) {
3515c4bb76a9SVladimir Oltean dev_err(ds->dev,
3516c4bb76a9SVladimir Oltean "port %d failed to delete %pM vid %d from fdb: %d\n",
351768d6d71eSVladimir Oltean dp->index, addr, vid, err);
3518c9eb3e0fSArkadi Sharshevsky }
35192fd18650SVladimir Oltean
3520c9eb3e0fSArkadi Sharshevsky break;
3521c9eb3e0fSArkadi Sharshevsky }
3522c9eb3e0fSArkadi Sharshevsky
3523c9eb3e0fSArkadi Sharshevsky kfree(switchdev_work);
3524c9eb3e0fSArkadi Sharshevsky }
3525c9eb3e0fSArkadi Sharshevsky
dsa_foreign_dev_check(const struct net_device * dev,const struct net_device * foreign_dev)3526b94dc99cSVladimir Oltean static bool dsa_foreign_dev_check(const struct net_device *dev,
3527b94dc99cSVladimir Oltean const struct net_device *foreign_dev)
3528d5f19486SVladimir Oltean {
3529b94dc99cSVladimir Oltean const struct dsa_port *dp = dsa_slave_to_port(dev);
3530b94dc99cSVladimir Oltean struct dsa_switch_tree *dst = dp->ds->dst;
3531b94dc99cSVladimir Oltean
3532b94dc99cSVladimir Oltean if (netif_is_bridge_master(foreign_dev))
3533936db8a2SVladimir Oltean return !dsa_tree_offloads_bridge_dev(dst, foreign_dev);
3534b94dc99cSVladimir Oltean
3535b94dc99cSVladimir Oltean if (netif_is_bridge_port(foreign_dev))
3536b94dc99cSVladimir Oltean return !dsa_tree_offloads_bridge_port(dst, foreign_dev);
3537b94dc99cSVladimir Oltean
3538b94dc99cSVladimir Oltean /* Everything else is foreign */
3539b94dc99cSVladimir Oltean return true;
3540d5f19486SVladimir Oltean }
3541d5f19486SVladimir Oltean
dsa_slave_fdb_event(struct net_device * dev,struct net_device * orig_dev,unsigned long event,const void * ctx,const struct switchdev_notifier_fdb_info * fdb_info)3542b94dc99cSVladimir Oltean static int dsa_slave_fdb_event(struct net_device *dev,
3543716a30a9SVladimir Oltean struct net_device *orig_dev,
3544716a30a9SVladimir Oltean unsigned long event, const void *ctx,
3545716a30a9SVladimir Oltean const struct switchdev_notifier_fdb_info *fdb_info)
3546b94dc99cSVladimir Oltean {
3547b94dc99cSVladimir Oltean struct dsa_switchdev_event_work *switchdev_work;
3548b94dc99cSVladimir Oltean struct dsa_port *dp = dsa_slave_to_port(dev);
3549b94dc99cSVladimir Oltean bool host_addr = fdb_info->is_local;
3550b94dc99cSVladimir Oltean struct dsa_switch *ds = dp->ds;
3551b94dc99cSVladimir Oltean
3552b94dc99cSVladimir Oltean if (ctx && ctx != dp)
3553b94dc99cSVladimir Oltean return 0;
3554b94dc99cSVladimir Oltean
3555a860352eSTobias Waldekranz if (!dp->bridge)
3556a860352eSTobias Waldekranz return 0;
3557a860352eSTobias Waldekranz
3558e212fa7cSVladimir Oltean if (switchdev_fdb_is_dynamically_learned(fdb_info)) {
3559e212fa7cSVladimir Oltean if (dsa_port_offloads_bridge_port(dp, orig_dev))
3560b94dc99cSVladimir Oltean return 0;
3561b94dc99cSVladimir Oltean
3562e212fa7cSVladimir Oltean /* FDB entries learned by the software bridge or by foreign
3563e212fa7cSVladimir Oltean * bridge ports should be installed as host addresses only if
3564e212fa7cSVladimir Oltean * the driver requests assisted learning.
3565b94dc99cSVladimir Oltean */
3566e212fa7cSVladimir Oltean if (!ds->assisted_learning_on_cpu_port)
3567b94dc99cSVladimir Oltean return 0;
3568e212fa7cSVladimir Oltean }
3569b94dc99cSVladimir Oltean
3570b94dc99cSVladimir Oltean /* Also treat FDB entries on foreign interfaces bridged with us as host
3571b94dc99cSVladimir Oltean * addresses.
3572b94dc99cSVladimir Oltean */
3573b94dc99cSVladimir Oltean if (dsa_foreign_dev_check(dev, orig_dev))
3574b94dc99cSVladimir Oltean host_addr = true;
3575b94dc99cSVladimir Oltean
3576e212fa7cSVladimir Oltean /* Check early that we're not doing work in vain.
3577e212fa7cSVladimir Oltean * Host addresses on LAG ports still require regular FDB ops,
3578e212fa7cSVladimir Oltean * since the CPU port isn't in a LAG.
3579e212fa7cSVladimir Oltean */
3580e212fa7cSVladimir Oltean if (dp->lag && !host_addr) {
3581e212fa7cSVladimir Oltean if (!ds->ops->lag_fdb_add || !ds->ops->lag_fdb_del)
3582e212fa7cSVladimir Oltean return -EOPNOTSUPP;
3583e212fa7cSVladimir Oltean } else {
3584e212fa7cSVladimir Oltean if (!ds->ops->port_fdb_add || !ds->ops->port_fdb_del)
3585e212fa7cSVladimir Oltean return -EOPNOTSUPP;
3586e212fa7cSVladimir Oltean }
3587e212fa7cSVladimir Oltean
3588b94dc99cSVladimir Oltean switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
3589b94dc99cSVladimir Oltean if (!switchdev_work)
3590b94dc99cSVladimir Oltean return -ENOMEM;
3591b94dc99cSVladimir Oltean
3592b94dc99cSVladimir Oltean netdev_dbg(dev, "%s FDB entry towards %s, addr %pM vid %d%s\n",
3593b94dc99cSVladimir Oltean event == SWITCHDEV_FDB_ADD_TO_DEVICE ? "Adding" : "Deleting",
3594b94dc99cSVladimir Oltean orig_dev->name, fdb_info->addr, fdb_info->vid,
3595b94dc99cSVladimir Oltean host_addr ? " as host address" : "");
3596b94dc99cSVladimir Oltean
3597b94dc99cSVladimir Oltean INIT_WORK(&switchdev_work->work, dsa_slave_switchdev_event_work);
3598b94dc99cSVladimir Oltean switchdev_work->event = event;
3599b94dc99cSVladimir Oltean switchdev_work->dev = dev;
360093c79823SVladimir Oltean switchdev_work->orig_dev = orig_dev;
3601b94dc99cSVladimir Oltean
3602b94dc99cSVladimir Oltean ether_addr_copy(switchdev_work->addr, fdb_info->addr);
3603b94dc99cSVladimir Oltean switchdev_work->vid = fdb_info->vid;
3604b94dc99cSVladimir Oltean switchdev_work->host_addr = host_addr;
3605b94dc99cSVladimir Oltean
3606b94dc99cSVladimir Oltean dsa_schedule_work(&switchdev_work->work);
3607b94dc99cSVladimir Oltean
3608d5f19486SVladimir Oltean return 0;
3609d5f19486SVladimir Oltean }
3610d5f19486SVladimir Oltean
3611c9eb3e0fSArkadi Sharshevsky /* Called under rcu_read_lock() */
dsa_slave_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)3612c9eb3e0fSArkadi Sharshevsky static int dsa_slave_switchdev_event(struct notifier_block *unused,
3613c9eb3e0fSArkadi Sharshevsky unsigned long event, void *ptr)
3614c9eb3e0fSArkadi Sharshevsky {
3615c9eb3e0fSArkadi Sharshevsky struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
361679b139f4SVivien Didelot int err;
361779b139f4SVivien Didelot
3618447d290aSVladimir Oltean switch (event) {
3619447d290aSVladimir Oltean case SWITCHDEV_PORT_ATTR_SET:
362079b139f4SVivien Didelot err = switchdev_handle_port_attr_set(dev, ptr,
362179b139f4SVivien Didelot dsa_slave_dev_check,
362279b139f4SVivien Didelot dsa_slave_port_attr_set);
362379b139f4SVivien Didelot return notifier_from_errno(err);
3624447d290aSVladimir Oltean case SWITCHDEV_FDB_ADD_TO_DEVICE:
3625447d290aSVladimir Oltean case SWITCHDEV_FDB_DEL_TO_DEVICE:
3626716a30a9SVladimir Oltean err = switchdev_handle_fdb_event_to_device(dev, event, ptr,
3627b94dc99cSVladimir Oltean dsa_slave_dev_check,
3628b94dc99cSVladimir Oltean dsa_foreign_dev_check,
3629ec638740SVladimir Oltean dsa_slave_fdb_event);
3630b94dc99cSVladimir Oltean return notifier_from_errno(err);
3631c9eb3e0fSArkadi Sharshevsky default:
3632c9eb3e0fSArkadi Sharshevsky return NOTIFY_DONE;
3633c9eb3e0fSArkadi Sharshevsky }
3634c9eb3e0fSArkadi Sharshevsky
3635c9eb3e0fSArkadi Sharshevsky return NOTIFY_OK;
3636c9eb3e0fSArkadi Sharshevsky }
3637c9eb3e0fSArkadi Sharshevsky
dsa_slave_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)36382b239f67SPetr Machata static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
36392b239f67SPetr Machata unsigned long event, void *ptr)
36402b239f67SPetr Machata {
36412b239f67SPetr Machata struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
364279b139f4SVivien Didelot int err;
36432b239f67SPetr Machata
36442b239f67SPetr Machata switch (event) {
364579b139f4SVivien Didelot case SWITCHDEV_PORT_OBJ_ADD:
3646164f861bSVladimir Oltean err = switchdev_handle_port_obj_add_foreign(dev, ptr,
364779b139f4SVivien Didelot dsa_slave_dev_check,
3648164f861bSVladimir Oltean dsa_foreign_dev_check,
364979b139f4SVivien Didelot dsa_slave_port_obj_add);
365079b139f4SVivien Didelot return notifier_from_errno(err);
36512b239f67SPetr Machata case SWITCHDEV_PORT_OBJ_DEL:
3652164f861bSVladimir Oltean err = switchdev_handle_port_obj_del_foreign(dev, ptr,
365379b139f4SVivien Didelot dsa_slave_dev_check,
3654164f861bSVladimir Oltean dsa_foreign_dev_check,
365579b139f4SVivien Didelot dsa_slave_port_obj_del);
365679b139f4SVivien Didelot return notifier_from_errno(err);
36579ed1ecedSFlorian Fainelli case SWITCHDEV_PORT_ATTR_SET:
365879b139f4SVivien Didelot err = switchdev_handle_port_attr_set(dev, ptr,
365979b139f4SVivien Didelot dsa_slave_dev_check,
366079b139f4SVivien Didelot dsa_slave_port_attr_set);
366179b139f4SVivien Didelot return notifier_from_errno(err);
36622b239f67SPetr Machata }
36632b239f67SPetr Machata
36642b239f67SPetr Machata return NOTIFY_DONE;
36652b239f67SPetr Machata }
36662b239f67SPetr Machata
366788e4f0caSVivien Didelot static struct notifier_block dsa_slave_nb __read_mostly = {
366888e4f0caSVivien Didelot .notifier_call = dsa_slave_netdevice_event,
366988e4f0caSVivien Didelot };
367088e4f0caSVivien Didelot
3671010e269fSVladimir Oltean struct notifier_block dsa_slave_switchdev_notifier = {
3672c9eb3e0fSArkadi Sharshevsky .notifier_call = dsa_slave_switchdev_event,
3673c9eb3e0fSArkadi Sharshevsky };
3674c9eb3e0fSArkadi Sharshevsky
3675010e269fSVladimir Oltean struct notifier_block dsa_slave_switchdev_blocking_notifier = {
36762b239f67SPetr Machata .notifier_call = dsa_slave_switchdev_blocking_event,
36772b239f67SPetr Machata };
36782b239f67SPetr Machata
dsa_slave_register_notifier(void)367988e4f0caSVivien Didelot int dsa_slave_register_notifier(void)
368088e4f0caSVivien Didelot {
36812b239f67SPetr Machata struct notifier_block *nb;
3682c9eb3e0fSArkadi Sharshevsky int err;
3683c9eb3e0fSArkadi Sharshevsky
3684c9eb3e0fSArkadi Sharshevsky err = register_netdevice_notifier(&dsa_slave_nb);
3685c9eb3e0fSArkadi Sharshevsky if (err)
3686c9eb3e0fSArkadi Sharshevsky return err;
3687c9eb3e0fSArkadi Sharshevsky
3688c9eb3e0fSArkadi Sharshevsky err = register_switchdev_notifier(&dsa_slave_switchdev_notifier);
3689c9eb3e0fSArkadi Sharshevsky if (err)
3690c9eb3e0fSArkadi Sharshevsky goto err_switchdev_nb;
3691c9eb3e0fSArkadi Sharshevsky
36922b239f67SPetr Machata nb = &dsa_slave_switchdev_blocking_notifier;
36932b239f67SPetr Machata err = register_switchdev_blocking_notifier(nb);
36942b239f67SPetr Machata if (err)
36952b239f67SPetr Machata goto err_switchdev_blocking_nb;
36962b239f67SPetr Machata
3697c9eb3e0fSArkadi Sharshevsky return 0;
3698c9eb3e0fSArkadi Sharshevsky
36992b239f67SPetr Machata err_switchdev_blocking_nb:
37002b239f67SPetr Machata unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
3701c9eb3e0fSArkadi Sharshevsky err_switchdev_nb:
3702c9eb3e0fSArkadi Sharshevsky unregister_netdevice_notifier(&dsa_slave_nb);
3703c9eb3e0fSArkadi Sharshevsky return err;
370488e4f0caSVivien Didelot }
370588e4f0caSVivien Didelot
dsa_slave_unregister_notifier(void)370688e4f0caSVivien Didelot void dsa_slave_unregister_notifier(void)
370788e4f0caSVivien Didelot {
37082b239f67SPetr Machata struct notifier_block *nb;
370988e4f0caSVivien Didelot int err;
371088e4f0caSVivien Didelot
37112b239f67SPetr Machata nb = &dsa_slave_switchdev_blocking_notifier;
37122b239f67SPetr Machata err = unregister_switchdev_blocking_notifier(nb);
37132b239f67SPetr Machata if (err)
37142b239f67SPetr Machata pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err);
37152b239f67SPetr Machata
3716c9eb3e0fSArkadi Sharshevsky err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
3717c9eb3e0fSArkadi Sharshevsky if (err)
3718c9eb3e0fSArkadi Sharshevsky pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
3719c9eb3e0fSArkadi Sharshevsky
372088e4f0caSVivien Didelot err = unregister_netdevice_notifier(&dsa_slave_nb);
372188e4f0caSVivien Didelot if (err)
372288e4f0caSVivien Didelot pr_err("DSA: failed to unregister slave notifier (%d)\n", err);
372388e4f0caSVivien Didelot }
3724