xref: /openbmc/linux/net/dsa/slave.c (revision fac59652993f075d57860769c99045b3ca18780d)
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