diff options
Diffstat (limited to 'net/dsa')
| -rw-r--r-- | net/dsa/Makefile | 12 | ||||
| -rw-r--r-- | net/dsa/dsa.c | 19 | ||||
| -rw-r--r-- | net/dsa/master.c | 50 | ||||
| -rw-r--r-- | net/dsa/master.h | 3 | ||||
| -rw-r--r-- | net/dsa/port.c | 34 | ||||
| -rw-r--r-- | net/dsa/port.h | 2 | ||||
| -rw-r--r-- | net/dsa/slave.c | 121 | ||||
| -rw-r--r-- | net/dsa/stubs.c | 10 | ||||
| -rw-r--r-- | net/dsa/switch.c | 85 | ||||
| -rw-r--r-- | net/dsa/tag.h | 2 | ||||
| -rw-r--r-- | net/dsa/tag_8021q.c | 4 | ||||
| -rw-r--r-- | net/dsa/tag_ksz.c | 18 | ||||
| -rw-r--r-- | net/dsa/tag_ocelot.c | 4 | ||||
| -rw-r--r-- | net/dsa/tag_sja1105.c | 4 | ||||
| -rw-r--r-- | net/dsa/trace.c | 39 | ||||
| -rw-r--r-- | net/dsa/trace.h | 447 | 
16 files changed, 764 insertions, 90 deletions
diff --git a/net/dsa/Makefile b/net/dsa/Makefile index cc7e93a562fe..12e305824a96 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -1,4 +1,10 @@  # SPDX-License-Identifier: GPL-2.0 + +# the stubs are built-in whenever DSA is built-in or module +ifdef CONFIG_NET_DSA +obj-y := stubs.o +endif +  # the core  obj-$(CONFIG_NET_DSA) += dsa_core.o  dsa_core-y += \ @@ -10,7 +16,8 @@ dsa_core-y += \  	slave.o \  	switch.o \  	tag.o \ -	tag_8021q.o +	tag_8021q.o \ +	trace.o  # tagging formats  obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o @@ -31,3 +38,6 @@ obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o  obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o  obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o  obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index e5f156940c67..ab1afe67fd18 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -17,6 +17,7 @@  #include <linux/of.h>  #include <linux/of_mdio.h>  #include <linux/of_net.h> +#include <net/dsa_stubs.h>  #include <net/sch_generic.h>  #include "devlink.h" @@ -1702,6 +1703,20 @@ bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port,  }  EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); +static const struct dsa_stubs __dsa_stubs = { +	.master_hwtstamp_validate = __dsa_master_hwtstamp_validate, +}; + +static void dsa_register_stubs(void) +{ +	dsa_stubs = &__dsa_stubs; +} + +static void dsa_unregister_stubs(void) +{ +	dsa_stubs = NULL; +} +  static int __init dsa_init_module(void)  {  	int rc; @@ -1721,6 +1736,8 @@ static int __init dsa_init_module(void)  	if (rc)  		goto netlink_register_fail; +	dsa_register_stubs(); +  	return 0;  netlink_register_fail: @@ -1735,6 +1752,8 @@ module_init(dsa_init_module);  static void __exit dsa_cleanup_module(void)  { +	dsa_unregister_stubs(); +  	rtnl_link_unregister(&dsa_link_ops);  	dsa_slave_unregister_notifier(); diff --git a/net/dsa/master.c b/net/dsa/master.c index 22d3f16b0e6d..6be89ab0cc01 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -195,38 +195,31 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,  	}  } -static int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +/* Deny PTP operations on master if there is at least one switch in the tree + * that is PTP capable. + */ +int __dsa_master_hwtstamp_validate(struct net_device *dev, +				   const struct kernel_hwtstamp_config *config, +				   struct netlink_ext_ack *extack)  {  	struct dsa_port *cpu_dp = dev->dsa_ptr;  	struct dsa_switch *ds = cpu_dp->ds;  	struct dsa_switch_tree *dst; -	int err = -EOPNOTSUPP;  	struct dsa_port *dp;  	dst = ds->dst; -	switch (cmd) { -	case SIOCGHWTSTAMP: -	case SIOCSHWTSTAMP: -		/* Deny PTP operations on master if there is at least one -		 * switch in the tree that is PTP capable. -		 */ -		list_for_each_entry(dp, &dst->ports, list) -			if (dsa_port_supports_hwtstamp(dp, ifr)) -				return -EBUSY; -		break; +	list_for_each_entry(dp, &dst->ports, list) { +		if (dsa_port_supports_hwtstamp(dp)) { +			NL_SET_ERR_MSG(extack, +				       "HW timestamping not allowed on DSA master when switch supports the operation"); +			return -EBUSY; +		}  	} -	if (dev->netdev_ops->ndo_eth_ioctl) -		err = dev->netdev_ops->ndo_eth_ioctl(dev, ifr, cmd); - -	return err; +	return 0;  } -static const struct dsa_netdevice_ops dsa_netdev_ops = { -	.ndo_eth_ioctl = dsa_master_ioctl, -}; -  static int dsa_master_ethtool_setup(struct net_device *dev)  {  	struct dsa_port *cpu_dp = dev->dsa_ptr; @@ -267,15 +260,6 @@ static void dsa_master_ethtool_teardown(struct net_device *dev)  	cpu_dp->orig_ethtool_ops = NULL;  } -static void dsa_netdev_ops_set(struct net_device *dev, -			       const struct dsa_netdevice_ops *ops) -{ -	if (netif_is_lag_master(dev)) -		return; - -	dev->dsa_ptr->netdev_ops = ops; -} -  /* Keep the master always promiscuous if the tagging protocol requires that   * (garbles MAC DA) or if it doesn't support unicast filtering, case in which   * it would revert to promiscuous mode as soon as we call dev_uc_add() on it @@ -414,16 +398,13 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)  	if (ret)  		goto out_err_reset_promisc; -	dsa_netdev_ops_set(dev, &dsa_netdev_ops); -  	ret = sysfs_create_group(&dev->dev.kobj, &dsa_group);  	if (ret) -		goto out_err_ndo_teardown; +		goto out_err_ethtool_teardown;  	return ret; -out_err_ndo_teardown: -	dsa_netdev_ops_set(dev, NULL); +out_err_ethtool_teardown:  	dsa_master_ethtool_teardown(dev);  out_err_reset_promisc:  	dsa_master_set_promiscuity(dev, -1); @@ -433,7 +414,6 @@ out_err_reset_promisc:  void dsa_master_teardown(struct net_device *dev)  {  	sysfs_remove_group(&dev->dev.kobj, &dsa_group); -	dsa_netdev_ops_set(dev, NULL);  	dsa_master_ethtool_teardown(dev);  	dsa_master_reset_mtu(dev);  	dsa_master_set_promiscuity(dev, -1); diff --git a/net/dsa/master.h b/net/dsa/master.h index 3fc0e610b5b5..76e39d3ec909 100644 --- a/net/dsa/master.h +++ b/net/dsa/master.h @@ -15,5 +15,8 @@ int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp,  			 struct netlink_ext_ack *extack);  void dsa_master_lag_teardown(struct net_device *lag_dev,  			     struct dsa_port *cpu_dp); +int __dsa_master_hwtstamp_validate(struct net_device *dev, +				   const struct kernel_hwtstamp_config *config, +				   struct netlink_ext_ack *extack);  #endif diff --git a/net/dsa/port.c b/net/dsa/port.c index 67ad1adec2a2..71ba30538411 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -114,19 +114,21 @@ static bool dsa_port_can_configure_learning(struct dsa_port *dp)  	return !err;  } -bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr) +bool dsa_port_supports_hwtstamp(struct dsa_port *dp)  {  	struct dsa_switch *ds = dp->ds; +	struct ifreq ifr = {};  	int err;  	if (!ds->ops->port_hwtstamp_get || !ds->ops->port_hwtstamp_set)  		return false;  	/* "See through" shim implementations of the "get" method. -	 * This will clobber the ifreq structure, but we will either return an -	 * error, or the master will overwrite it with proper values. +	 * Since we can't cook up a complete ioctl request structure, this will +	 * fail in copy_to_user() with -EFAULT, which hopefully is enough to +	 * detect a valid implementation.  	 */ -	err = ds->ops->port_hwtstamp_get(ds, dp->index, ifr); +	err = ds->ops->port_hwtstamp_get(ds, dp->index, &ifr);  	return err != -EOPNOTSUPP;  } @@ -1028,9 +1030,6 @@ static int dsa_port_host_fdb_add(struct dsa_port *dp,  		.db = db,  	}; -	if (!dp->ds->fdb_isolation) -		info.db.bridge.num = 0; -  	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info);  } @@ -1055,6 +1054,9 @@ int dsa_port_bridge_host_fdb_add(struct dsa_port *dp,  	};  	int err; +	if (!dp->ds->fdb_isolation) +		db.bridge.num = 0; +  	/* Avoid a call to __dev_set_promiscuity() on the master, which  	 * requires rtnl_lock(), since we can't guarantee that is held here,  	 * and we can't take it either. @@ -1079,9 +1081,6 @@ static int dsa_port_host_fdb_del(struct dsa_port *dp,  		.db = db,  	}; -	if (!dp->ds->fdb_isolation) -		info.db.bridge.num = 0; -  	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info);  } @@ -1106,6 +1105,9 @@ int dsa_port_bridge_host_fdb_del(struct dsa_port *dp,  	};  	int err; +	if (!dp->ds->fdb_isolation) +		db.bridge.num = 0; +  	if (master->priv_flags & IFF_UNICAST_FLT) {  		err = dev_uc_del(master, addr);  		if (err) @@ -1210,9 +1212,6 @@ static int dsa_port_host_mdb_add(const struct dsa_port *dp,  		.db = db,  	}; -	if (!dp->ds->fdb_isolation) -		info.db.bridge.num = 0; -  	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);  } @@ -1237,6 +1236,9 @@ int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp,  	};  	int err; +	if (!dp->ds->fdb_isolation) +		db.bridge.num = 0; +  	err = dev_mc_add(master, mdb->addr);  	if (err)  		return err; @@ -1254,9 +1256,6 @@ static int dsa_port_host_mdb_del(const struct dsa_port *dp,  		.db = db,  	}; -	if (!dp->ds->fdb_isolation) -		info.db.bridge.num = 0; -  	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);  } @@ -1281,6 +1280,9 @@ int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,  	};  	int err; +	if (!dp->ds->fdb_isolation) +		db.bridge.num = 0; +  	err = dev_mc_del(master, mdb->addr);  	if (err)  		return err; diff --git a/net/dsa/port.h b/net/dsa/port.h index 9c218660d223..dc812512fd0e 100644 --- a/net/dsa/port.h +++ b/net/dsa/port.h @@ -15,7 +15,7 @@ struct switchdev_obj_port_mdb;  struct switchdev_vlan_msti;  struct phy_device; -bool dsa_port_supports_hwtstamp(struct dsa_port *dp, struct ifreq *ifr); +bool dsa_port_supports_hwtstamp(struct dsa_port *dp);  void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,  			       const struct dsa_device_ops *tag_ops);  int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index cac17183589f..165bb2cb8431 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -57,6 +57,12 @@ struct dsa_standalone_event_work {  	u16 vid;  }; +struct dsa_host_vlan_rx_filtering_ctx { +	struct net_device *dev; +	const unsigned char *addr; +	enum dsa_standalone_event event; +}; +  static bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds)  {  	return ds->ops->port_fdb_add && ds->ops->port_fdb_del && @@ -155,18 +161,37 @@ static int dsa_slave_schedule_standalone_work(struct net_device *dev,  	return 0;  } +static int dsa_slave_host_vlan_rx_filtering(struct net_device *vdev, int vid, +					    void *arg) +{ +	struct dsa_host_vlan_rx_filtering_ctx *ctx = arg; + +	return dsa_slave_schedule_standalone_work(ctx->dev, ctx->event, +						  ctx->addr, vid); +} +  static int dsa_slave_sync_uc(struct net_device *dev,  			     const unsigned char *addr)  {  	struct net_device *master = dsa_slave_to_master(dev);  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct dsa_host_vlan_rx_filtering_ctx ctx = { +		.dev = dev, +		.addr = addr, +		.event = DSA_UC_ADD, +	}; +	int err;  	dev_uc_add(master, addr);  	if (!dsa_switch_supports_uc_filtering(dp->ds))  		return 0; -	return dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD, addr, 0); +	err = dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD, addr, 0); +	if (err) +		return err; + +	return vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, &ctx);  }  static int dsa_slave_unsync_uc(struct net_device *dev, @@ -174,13 +199,23 @@ static int dsa_slave_unsync_uc(struct net_device *dev,  {  	struct net_device *master = dsa_slave_to_master(dev);  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct dsa_host_vlan_rx_filtering_ctx ctx = { +		.dev = dev, +		.addr = addr, +		.event = DSA_UC_DEL, +	}; +	int err;  	dev_uc_del(master, addr);  	if (!dsa_switch_supports_uc_filtering(dp->ds))  		return 0; -	return dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL, addr, 0); +	err = dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL, addr, 0); +	if (err) +		return err; + +	return vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, &ctx);  }  static int dsa_slave_sync_mc(struct net_device *dev, @@ -188,13 +223,23 @@ static int dsa_slave_sync_mc(struct net_device *dev,  {  	struct net_device *master = dsa_slave_to_master(dev);  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct dsa_host_vlan_rx_filtering_ctx ctx = { +		.dev = dev, +		.addr = addr, +		.event = DSA_MC_ADD, +	}; +	int err;  	dev_mc_add(master, addr);  	if (!dsa_switch_supports_mc_filtering(dp->ds))  		return 0; -	return dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD, addr, 0); +	err = dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD, addr, 0); +	if (err) +		return err; + +	return vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, &ctx);  }  static int dsa_slave_unsync_mc(struct net_device *dev, @@ -202,13 +247,23 @@ static int dsa_slave_unsync_mc(struct net_device *dev,  {  	struct net_device *master = dsa_slave_to_master(dev);  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct dsa_host_vlan_rx_filtering_ctx ctx = { +		.dev = dev, +		.addr = addr, +		.event = DSA_MC_DEL, +	}; +	int err;  	dev_mc_del(master, addr);  	if (!dsa_switch_supports_mc_filtering(dp->ds))  		return 0; -	return dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL, addr, 0); +	err = dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL, addr, 0); +	if (err) +		return err; + +	return vlan_for_each(dev, dsa_slave_host_vlan_rx_filtering, &ctx);  }  void dsa_slave_sync_ha(struct net_device *dev) @@ -1702,6 +1757,8 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,  		.flags = 0,  	};  	struct netlink_ext_ack extack = {0}; +	struct dsa_switch *ds = dp->ds; +	struct netdev_hw_addr *ha;  	int ret;  	/* User port... */ @@ -1721,6 +1778,30 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,  		return ret;  	} +	if (!dsa_switch_supports_uc_filtering(ds) && +	    !dsa_switch_supports_mc_filtering(ds)) +		return 0; + +	netif_addr_lock_bh(dev); + +	if (dsa_switch_supports_mc_filtering(ds)) { +		netdev_for_each_synced_mc_addr(ha, dev) { +			dsa_slave_schedule_standalone_work(dev, DSA_MC_ADD, +							   ha->addr, vid); +		} +	} + +	if (dsa_switch_supports_uc_filtering(ds)) { +		netdev_for_each_synced_uc_addr(ha, dev) { +			dsa_slave_schedule_standalone_work(dev, DSA_UC_ADD, +							   ha->addr, vid); +		} +	} + +	netif_addr_unlock_bh(dev); + +	dsa_flush_workqueue(); +  	return 0;  } @@ -1733,13 +1814,43 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,  		/* This API only allows programming tagged, non-PVID VIDs */  		.flags = 0,  	}; +	struct dsa_switch *ds = dp->ds; +	struct netdev_hw_addr *ha;  	int err;  	err = dsa_port_vlan_del(dp, &vlan);  	if (err)  		return err; -	return dsa_port_host_vlan_del(dp, &vlan); +	err = dsa_port_host_vlan_del(dp, &vlan); +	if (err) +		return err; + +	if (!dsa_switch_supports_uc_filtering(ds) && +	    !dsa_switch_supports_mc_filtering(ds)) +		return 0; + +	netif_addr_lock_bh(dev); + +	if (dsa_switch_supports_mc_filtering(ds)) { +		netdev_for_each_synced_mc_addr(ha, dev) { +			dsa_slave_schedule_standalone_work(dev, DSA_MC_DEL, +							   ha->addr, vid); +		} +	} + +	if (dsa_switch_supports_uc_filtering(ds)) { +		netdev_for_each_synced_uc_addr(ha, dev) { +			dsa_slave_schedule_standalone_work(dev, DSA_UC_DEL, +							   ha->addr, vid); +		} +	} + +	netif_addr_unlock_bh(dev); + +	dsa_flush_workqueue(); + +	return 0;  }  static int dsa_slave_restore_vlan(struct net_device *vdev, int vid, void *arg) diff --git a/net/dsa/stubs.c b/net/dsa/stubs.c new file mode 100644 index 000000000000..2ed8a6c85fbf --- /dev/null +++ b/net/dsa/stubs.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Stubs for DSA functionality called by the core network stack. + * These are necessary because CONFIG_NET_DSA can be a module, and built-in + * code cannot directly call symbols exported by modules. + */ +#include <net/dsa_stubs.h> + +const struct dsa_stubs *dsa_stubs; +EXPORT_SYMBOL_GPL(dsa_stubs); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index d5bc4bb7310d..8c9a9f94b756 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -18,6 +18,7 @@  #include "slave.h"  #include "switch.h"  #include "tag_8021q.h" +#include "trace.h"  static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,  						   unsigned int ageing_time) @@ -164,14 +165,20 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,  	int err = 0;  	/* No need to bother with refcounting for user ports */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_mdb_add(ds, port, mdb, db); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_mdb_add(ds, port, mdb, db); +		trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err); + +		return err; +	}  	mutex_lock(&dp->addr_lists_lock);  	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);  	if (a) {  		refcount_inc(&a->refcount); +		trace_dsa_mdb_add_bump(dp, mdb->addr, mdb->vid, &db, +				       &a->refcount);  		goto out;  	} @@ -182,6 +189,7 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,  	}  	err = ds->ops->port_mdb_add(ds, port, mdb, db); +	trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err);  	if (err) {  		kfree(a);  		goto out; @@ -209,21 +217,30 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp,  	int err = 0;  	/* No need to bother with refcounting for user ports */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_mdb_del(ds, port, mdb, db); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_mdb_del(ds, port, mdb, db); +		trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err); + +		return err; +	}  	mutex_lock(&dp->addr_lists_lock);  	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);  	if (!a) { +		trace_dsa_mdb_del_not_found(dp, mdb->addr, mdb->vid, &db);  		err = -ENOENT;  		goto out;  	} -	if (!refcount_dec_and_test(&a->refcount)) +	if (!refcount_dec_and_test(&a->refcount)) { +		trace_dsa_mdb_del_drop(dp, mdb->addr, mdb->vid, &db, +				       &a->refcount);  		goto out; +	}  	err = ds->ops->port_mdb_del(ds, port, mdb, db); +	trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err);  	if (err) {  		refcount_set(&a->refcount, 1);  		goto out; @@ -247,14 +264,19 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,  	int err = 0;  	/* No need to bother with refcounting for user ports */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_fdb_add(ds, port, addr, vid, db); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_fdb_add(ds, port, addr, vid, db); +		trace_dsa_fdb_add_hw(dp, addr, vid, &db, err); + +		return err; +	}  	mutex_lock(&dp->addr_lists_lock);  	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);  	if (a) {  		refcount_inc(&a->refcount); +		trace_dsa_fdb_add_bump(dp, addr, vid, &db, &a->refcount);  		goto out;  	} @@ -265,6 +287,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,  	}  	err = ds->ops->port_fdb_add(ds, port, addr, vid, db); +	trace_dsa_fdb_add_hw(dp, addr, vid, &db, err);  	if (err) {  		kfree(a);  		goto out; @@ -291,21 +314,29 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,  	int err = 0;  	/* No need to bother with refcounting for user ports */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_fdb_del(ds, port, addr, vid, db); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_fdb_del(ds, port, addr, vid, db); +		trace_dsa_fdb_del_hw(dp, addr, vid, &db, err); + +		return err; +	}  	mutex_lock(&dp->addr_lists_lock);  	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);  	if (!a) { +		trace_dsa_fdb_del_not_found(dp, addr, vid, &db);  		err = -ENOENT;  		goto out;  	} -	if (!refcount_dec_and_test(&a->refcount)) +	if (!refcount_dec_and_test(&a->refcount)) { +		trace_dsa_fdb_del_drop(dp, addr, vid, &db, &a->refcount);  		goto out; +	}  	err = ds->ops->port_fdb_del(ds, port, addr, vid, db); +	trace_dsa_fdb_del_hw(dp, addr, vid, &db, err);  	if (err) {  		refcount_set(&a->refcount, 1);  		goto out; @@ -332,6 +363,8 @@ static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,  	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);  	if (a) {  		refcount_inc(&a->refcount); +		trace_dsa_lag_fdb_add_bump(lag->dev, addr, vid, &db, +					   &a->refcount);  		goto out;  	} @@ -342,6 +375,7 @@ static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,  	}  	err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); +	trace_dsa_lag_fdb_add_hw(lag->dev, addr, vid, &db, err);  	if (err) {  		kfree(a);  		goto out; @@ -370,14 +404,19 @@ static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag,  	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);  	if (!a) { +		trace_dsa_lag_fdb_del_not_found(lag->dev, addr, vid, &db);  		err = -ENOENT;  		goto out;  	} -	if (!refcount_dec_and_test(&a->refcount)) +	if (!refcount_dec_and_test(&a->refcount)) { +		trace_dsa_lag_fdb_del_drop(lag->dev, addr, vid, &db, +					   &a->refcount);  		goto out; +	}  	err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); +	trace_dsa_lag_fdb_del_hw(lag->dev, addr, vid, &db, err);  	if (err) {  		refcount_set(&a->refcount, 1);  		goto out; @@ -656,8 +695,12 @@ static int dsa_port_do_vlan_add(struct dsa_port *dp,  	int err = 0;  	/* No need to bother with refcounting for user ports. */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_vlan_add(ds, port, vlan, extack); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_vlan_add(ds, port, vlan, extack); +		trace_dsa_vlan_add_hw(dp, vlan, err); + +		return err; +	}  	/* No need to propagate on shared ports the existing VLANs that were  	 * re-notified after just the flags have changed. This would cause a @@ -672,6 +715,7 @@ static int dsa_port_do_vlan_add(struct dsa_port *dp,  	v = dsa_vlan_find(&dp->vlans, vlan);  	if (v) {  		refcount_inc(&v->refcount); +		trace_dsa_vlan_add_bump(dp, vlan, &v->refcount);  		goto out;  	} @@ -682,6 +726,7 @@ static int dsa_port_do_vlan_add(struct dsa_port *dp,  	}  	err = ds->ops->port_vlan_add(ds, port, vlan, extack); +	trace_dsa_vlan_add_hw(dp, vlan, err);  	if (err) {  		kfree(v);  		goto out; @@ -706,21 +751,29 @@ static int dsa_port_do_vlan_del(struct dsa_port *dp,  	int err = 0;  	/* No need to bother with refcounting for user ports */ -	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) -		return ds->ops->port_vlan_del(ds, port, vlan); +	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { +		err = ds->ops->port_vlan_del(ds, port, vlan); +		trace_dsa_vlan_del_hw(dp, vlan, err); + +		return err; +	}  	mutex_lock(&dp->vlans_lock);  	v = dsa_vlan_find(&dp->vlans, vlan);  	if (!v) { +		trace_dsa_vlan_del_not_found(dp, vlan);  		err = -ENOENT;  		goto out;  	} -	if (!refcount_dec_and_test(&v->refcount)) +	if (!refcount_dec_and_test(&v->refcount)) { +		trace_dsa_vlan_del_drop(dp, vlan, &v->refcount);  		goto out; +	}  	err = ds->ops->port_vlan_del(ds, port, vlan); +	trace_dsa_vlan_del_hw(dp, vlan, err);  	if (err) {  		refcount_set(&v->refcount, 1);  		goto out; diff --git a/net/dsa/tag.h b/net/dsa/tag.h index 7cfbca824f1c..32d12f4a9d73 100644 --- a/net/dsa/tag.h +++ b/net/dsa/tag.h @@ -229,7 +229,7 @@ static inline void *dsa_etype_header_pos_rx(struct sk_buff *skb)  	return skb->data - 2;  } -/* On TX, skb->data points to skb_mac_header(skb), which means that EtherType +/* On TX, skb->data points to the MAC header, which means that EtherType   * header taggers start exactly where the EtherType is (the EtherType is   * treated as part of the DSA header).   */ diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 5ee9ef00954e..cbdfc392f7e0 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -461,8 +461,8 @@ EXPORT_SYMBOL_GPL(dsa_tag_8021q_unregister);  struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,  			       u16 tpid, u16 tci)  { -	/* skb->data points at skb_mac_header, which -	 * is fine for vlan_insert_tag. +	/* skb->data points at the MAC header, which is fine +	 * for vlan_insert_tag().  	 */  	return vlan_insert_tag(skb, htons(tpid), tci);  } diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 0eb1c7784c3d..ea100bd25939 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -120,18 +120,18 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,  static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)  {  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct ethhdr *hdr;  	u8 *tag; -	u8 *addr;  	if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))  		return NULL;  	/* Tag encoding */  	tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); -	addr = skb_mac_header(skb); +	hdr = skb_eth_hdr(skb);  	*tag = 1 << dp->index; -	if (is_link_local_ether_addr(addr)) +	if (is_link_local_ether_addr(hdr->h_dest))  		*tag |= KSZ8795_TAIL_TAG_OVERRIDE;  	return skb; @@ -273,8 +273,8 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,  	u16 queue_mapping = skb_get_queue_mapping(skb);  	u8 prio = netdev_txq_to_tc(dev, queue_mapping);  	struct dsa_port *dp = dsa_slave_to_port(dev); +	struct ethhdr *hdr;  	__be16 *tag; -	u8 *addr;  	u16 val;  	if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) @@ -284,13 +284,13 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,  	ksz_xmit_timestamp(dp, skb);  	tag = skb_put(skb, KSZ9477_INGRESS_TAG_LEN); -	addr = skb_mac_header(skb); +	hdr = skb_eth_hdr(skb);  	val = BIT(dp->index);  	val |= FIELD_PREP(KSZ9477_TAIL_TAG_PRIO, prio); -	if (is_link_local_ether_addr(addr)) +	if (is_link_local_ether_addr(hdr->h_dest))  		val |= KSZ9477_TAIL_TAG_OVERRIDE;  	*tag = cpu_to_be16(val); @@ -337,7 +337,7 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,  	u16 queue_mapping = skb_get_queue_mapping(skb);  	u8 prio = netdev_txq_to_tc(dev, queue_mapping);  	struct dsa_port *dp = dsa_slave_to_port(dev); -	u8 *addr; +	struct ethhdr *hdr;  	u8 *tag;  	if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) @@ -347,13 +347,13 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,  	ksz_xmit_timestamp(dp, skb);  	tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); -	addr = skb_mac_header(skb); +	hdr = skb_eth_hdr(skb);  	*tag = BIT(dp->index);  	*tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio); -	if (is_link_local_ether_addr(addr)) +	if (is_link_local_ether_addr(hdr->h_dest))  		*tag |= KSZ9893_TAIL_TAG_OVERRIDE;  	return ksz_defer_xmit(dp, skb); diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 28ebecafdd24..20bf7074d5a6 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -26,11 +26,11 @@ static void ocelot_xmit_get_vlan_info(struct sk_buff *skb, struct dsa_port *dp,  		return;  	} -	hdr = (struct vlan_ethhdr *)skb_mac_header(skb); +	hdr = skb_vlan_eth_hdr(skb);  	br_vlan_get_proto(br, &proto);  	if (ntohs(hdr->h_vlan_proto) == proto) { -		__skb_vlan_pop(skb, &tci); +		vlan_remove_tag(skb, &tci);  		*vlan_tci = tci;  	} else {  		rcu_read_lock(); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index 1c2ceba4771b..a5f3b73da417 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -256,7 +256,7 @@ static struct sk_buff *sja1105_pvid_tag_control_pkt(struct dsa_port *dp,  			return NULL;  	} -	hdr = (struct vlan_ethhdr *)skb_mac_header(skb); +	hdr = skb_vlan_eth_hdr(skb);  	/* If skb is already VLAN-tagged, leave that VLAN ID in place */  	if (hdr->h_vlan_proto == xmit_tpid) @@ -516,7 +516,7 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)  static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,  			     int *switch_id, int *vbid, u16 *vid)  { -	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb); +	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);  	u16 vlan_tci;  	if (skb_vlan_tag_present(skb)) diff --git a/net/dsa/trace.c b/net/dsa/trace.c new file mode 100644 index 000000000000..1b107165d331 --- /dev/null +++ b/net/dsa/trace.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright 2022-2023 NXP + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" + +void dsa_db_print(const struct dsa_db *db, char buf[DSA_DB_BUFSIZ]) +{ +	switch (db->type) { +	case DSA_DB_PORT: +		sprintf(buf, "port %s", db->dp->name); +		break; +	case DSA_DB_LAG: +		sprintf(buf, "lag %s id %d", db->lag.dev->name, db->lag.id); +		break; +	case DSA_DB_BRIDGE: +		sprintf(buf, "bridge %s num %d", db->bridge.dev->name, +			db->bridge.num); +		break; +	default: +		sprintf(buf, "unknown"); +		break; +	} +} + +const char *dsa_port_kind(const struct dsa_port *dp) +{ +	switch (dp->type) { +	case DSA_PORT_TYPE_USER: +		return "user"; +	case DSA_PORT_TYPE_CPU: +		return "cpu"; +	case DSA_PORT_TYPE_DSA: +		return "dsa"; +	default: +		return "unused"; +	} +} diff --git a/net/dsa/trace.h b/net/dsa/trace.h new file mode 100644 index 000000000000..567f29a39707 --- /dev/null +++ b/net/dsa/trace.h @@ -0,0 +1,447 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright 2022-2023 NXP + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM	dsa + +#if !defined(_NET_DSA_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _NET_DSA_TRACE_H + +#include <net/dsa.h> +#include <net/switchdev.h> +#include <linux/etherdevice.h> +#include <linux/if_bridge.h> +#include <linux/refcount.h> +#include <linux/tracepoint.h> + +/* Enough to fit "bridge %s num %d" where num has 3 digits */ +#define DSA_DB_BUFSIZ	(IFNAMSIZ + 16) + +void dsa_db_print(const struct dsa_db *db, char buf[DSA_DB_BUFSIZ]); +const char *dsa_port_kind(const struct dsa_port *dp); + +DECLARE_EVENT_CLASS(dsa_port_addr_op_hw, + +	TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, u16 vid, +		 const struct dsa_db *db, int err), + +	TP_ARGS(dp, addr, vid, db, err), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(int, err) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->err = err; +	), + +	TP_printk("%s %s port %d addr %pM vid %u db \"%s\" err %d", +		  __get_str(dev), __get_str(kind), __entry->port, __entry->addr, +		  __entry->vid, __entry->db_buf, __entry->err) +); + +/* Add unicast/multicast address to hardware, either on user ports + * (where no refcounting is kept), or on shared ports when the entry + * is first seen and its refcount is 1. + */ +DEFINE_EVENT(dsa_port_addr_op_hw, dsa_fdb_add_hw, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, int err), +	     TP_ARGS(dp, addr, vid, db, err)); + +DEFINE_EVENT(dsa_port_addr_op_hw, dsa_mdb_add_hw, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, int err), +	     TP_ARGS(dp, addr, vid, db, err)); + +/* Delete unicast/multicast address from hardware, either on user ports or + * when the refcount on shared ports reaches 0 + */ +DEFINE_EVENT(dsa_port_addr_op_hw, dsa_fdb_del_hw, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, int err), +	     TP_ARGS(dp, addr, vid, db, err)); + +DEFINE_EVENT(dsa_port_addr_op_hw, dsa_mdb_del_hw, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, int err), +	     TP_ARGS(dp, addr, vid, db, err)); + +DECLARE_EVENT_CLASS(dsa_port_addr_op_refcount, + +	TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, u16 vid, +		 const struct dsa_db *db, const refcount_t *refcount), + +	TP_ARGS(dp, addr, vid, db, refcount), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(unsigned int, refcount) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->refcount = refcount_read(refcount); +	), + +	TP_printk("%s %s port %d addr %pM vid %u db \"%s\" refcount %u", +		  __get_str(dev), __get_str(kind), __entry->port, __entry->addr, +		  __entry->vid, __entry->db_buf, __entry->refcount) +); + +/* Bump the refcount of an existing unicast/multicast address on shared ports */ +DEFINE_EVENT(dsa_port_addr_op_refcount, dsa_fdb_add_bump, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, +		      const refcount_t *refcount), +	     TP_ARGS(dp, addr, vid, db, refcount)); + +DEFINE_EVENT(dsa_port_addr_op_refcount, dsa_mdb_add_bump, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, +		      const refcount_t *refcount), +	     TP_ARGS(dp, addr, vid, db, refcount)); + +/* Drop the refcount of a multicast address that we still keep on + * shared ports + */ +DEFINE_EVENT(dsa_port_addr_op_refcount, dsa_fdb_del_drop, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, +		      const refcount_t *refcount), +	     TP_ARGS(dp, addr, vid, db, refcount)); + +DEFINE_EVENT(dsa_port_addr_op_refcount, dsa_mdb_del_drop, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db, +		      const refcount_t *refcount), +	     TP_ARGS(dp, addr, vid, db, refcount)); + +DECLARE_EVENT_CLASS(dsa_port_addr_del_not_found, + +	TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, u16 vid, +		 const struct dsa_db *db), + +	TP_ARGS(dp, addr, vid, db), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +	), + +	TP_printk("%s %s port %d addr %pM vid %u db \"%s\"", +		  __get_str(dev), __get_str(kind), __entry->port, +		  __entry->addr, __entry->vid, __entry->db_buf) +); + +/* Attempt to delete a unicast/multicast address on shared ports for which + * the delete operation was called more times than the addition + */ +DEFINE_EVENT(dsa_port_addr_del_not_found, dsa_fdb_del_not_found, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db), +	     TP_ARGS(dp, addr, vid, db)); + +DEFINE_EVENT(dsa_port_addr_del_not_found, dsa_mdb_del_not_found, +	     TP_PROTO(const struct dsa_port *dp, const unsigned char *addr, +		      u16 vid, const struct dsa_db *db), +	     TP_ARGS(dp, addr, vid, db)); + +TRACE_EVENT(dsa_lag_fdb_add_hw, + +	TP_PROTO(const struct net_device *lag_dev, const unsigned char *addr, +		 u16 vid, const struct dsa_db *db, int err), + +	TP_ARGS(lag_dev, addr, vid, db, err), + +	TP_STRUCT__entry( +		__string(dev, lag_dev->name) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(int, err) +	), + +	TP_fast_assign( +		__assign_str(dev, lag_dev->name); +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->err = err; +	), + +	TP_printk("%s addr %pM vid %u db \"%s\" err %d", +		  __get_str(dev), __entry->addr, __entry->vid, +		  __entry->db_buf, __entry->err) +); + +TRACE_EVENT(dsa_lag_fdb_add_bump, + +	TP_PROTO(const struct net_device *lag_dev, const unsigned char *addr, +		 u16 vid, const struct dsa_db *db, const refcount_t *refcount), + +	TP_ARGS(lag_dev, addr, vid, db, refcount), + +	TP_STRUCT__entry( +		__string(dev, lag_dev->name) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(unsigned int, refcount) +	), + +	TP_fast_assign( +		__assign_str(dev, lag_dev->name); +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->refcount = refcount_read(refcount); +	), + +	TP_printk("%s addr %pM vid %u db \"%s\" refcount %u", +		  __get_str(dev), __entry->addr, __entry->vid, +		  __entry->db_buf, __entry->refcount) +); + +TRACE_EVENT(dsa_lag_fdb_del_hw, + +	TP_PROTO(const struct net_device *lag_dev, const unsigned char *addr, +		 u16 vid, const struct dsa_db *db, int err), + +	TP_ARGS(lag_dev, addr, vid, db, err), + +	TP_STRUCT__entry( +		__string(dev, lag_dev->name) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(int, err) +	), + +	TP_fast_assign( +		__assign_str(dev, lag_dev->name); +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->err = err; +	), + +	TP_printk("%s addr %pM vid %u db \"%s\" err %d", +		  __get_str(dev), __entry->addr, __entry->vid, +		  __entry->db_buf, __entry->err) +); + +TRACE_EVENT(dsa_lag_fdb_del_drop, + +	TP_PROTO(const struct net_device *lag_dev, const unsigned char *addr, +		 u16 vid, const struct dsa_db *db, const refcount_t *refcount), + +	TP_ARGS(lag_dev, addr, vid, db, refcount), + +	TP_STRUCT__entry( +		__string(dev, lag_dev->name) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +		__field(unsigned int, refcount) +	), + +	TP_fast_assign( +		__assign_str(dev, lag_dev->name); +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +		__entry->refcount = refcount_read(refcount); +	), + +	TP_printk("%s addr %pM vid %u db \"%s\" refcount %u", +		  __get_str(dev), __entry->addr, __entry->vid, +		  __entry->db_buf, __entry->refcount) +); + +TRACE_EVENT(dsa_lag_fdb_del_not_found, + +	TP_PROTO(const struct net_device *lag_dev, const unsigned char *addr, +		 u16 vid, const struct dsa_db *db), + +	TP_ARGS(lag_dev, addr, vid, db), + +	TP_STRUCT__entry( +		__string(dev, lag_dev->name) +		__array(unsigned char, addr, ETH_ALEN) +		__field(u16, vid) +		__array(char, db_buf, DSA_DB_BUFSIZ) +	), + +	TP_fast_assign( +		__assign_str(dev, lag_dev->name); +		ether_addr_copy(__entry->addr, addr); +		__entry->vid = vid; +		dsa_db_print(db, __entry->db_buf); +	), + +	TP_printk("%s addr %pM vid %u db \"%s\"", +		  __get_str(dev), __entry->addr, __entry->vid, __entry->db_buf) +); + +DECLARE_EVENT_CLASS(dsa_vlan_op_hw, + +	TP_PROTO(const struct dsa_port *dp, +		 const struct switchdev_obj_port_vlan *vlan, int err), + +	TP_ARGS(dp, vlan, err), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__field(u16, vid) +		__field(u16, flags) +		__field(bool, changed) +		__field(int, err) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		__entry->vid = vlan->vid; +		__entry->flags = vlan->flags; +		__entry->changed = vlan->changed; +		__entry->err = err; +	), + +	TP_printk("%s %s port %d vid %u%s%s%s", +		  __get_str(dev), __get_str(kind), __entry->port, __entry->vid, +		  __entry->flags & BRIDGE_VLAN_INFO_PVID ? " pvid" : "", +		  __entry->flags & BRIDGE_VLAN_INFO_UNTAGGED ? " untagged" : "", +		  __entry->changed ? " (changed)" : "") +); + +DEFINE_EVENT(dsa_vlan_op_hw, dsa_vlan_add_hw, +	     TP_PROTO(const struct dsa_port *dp, +		      const struct switchdev_obj_port_vlan *vlan, int err), +	     TP_ARGS(dp, vlan, err)); + +DEFINE_EVENT(dsa_vlan_op_hw, dsa_vlan_del_hw, +	     TP_PROTO(const struct dsa_port *dp, +		      const struct switchdev_obj_port_vlan *vlan, int err), +	     TP_ARGS(dp, vlan, err)); + +DECLARE_EVENT_CLASS(dsa_vlan_op_refcount, + +	TP_PROTO(const struct dsa_port *dp, +		 const struct switchdev_obj_port_vlan *vlan, +		 const refcount_t *refcount), + +	TP_ARGS(dp, vlan, refcount), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__field(u16, vid) +		__field(u16, flags) +		__field(bool, changed) +		__field(unsigned int, refcount) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		__entry->vid = vlan->vid; +		__entry->flags = vlan->flags; +		__entry->changed = vlan->changed; +		__entry->refcount = refcount_read(refcount); +	), + +	TP_printk("%s %s port %d vid %u%s%s%s refcount %u", +		  __get_str(dev), __get_str(kind), __entry->port, __entry->vid, +		  __entry->flags & BRIDGE_VLAN_INFO_PVID ? " pvid" : "", +		  __entry->flags & BRIDGE_VLAN_INFO_UNTAGGED ? " untagged" : "", +		  __entry->changed ? " (changed)" : "", __entry->refcount) +); + +DEFINE_EVENT(dsa_vlan_op_refcount, dsa_vlan_add_bump, +	     TP_PROTO(const struct dsa_port *dp, +		      const struct switchdev_obj_port_vlan *vlan, +		      const refcount_t *refcount), +	     TP_ARGS(dp, vlan, refcount)); + +DEFINE_EVENT(dsa_vlan_op_refcount, dsa_vlan_del_drop, +	     TP_PROTO(const struct dsa_port *dp, +		      const struct switchdev_obj_port_vlan *vlan, +		      const refcount_t *refcount), +	     TP_ARGS(dp, vlan, refcount)); + +TRACE_EVENT(dsa_vlan_del_not_found, + +	TP_PROTO(const struct dsa_port *dp, +		 const struct switchdev_obj_port_vlan *vlan), + +	TP_ARGS(dp, vlan), + +	TP_STRUCT__entry( +		__string(dev, dev_name(dp->ds->dev)) +		__string(kind, dsa_port_kind(dp)) +		__field(int, port) +		__field(u16, vid) +	), + +	TP_fast_assign( +		__assign_str(dev, dev_name(dp->ds->dev)); +		__assign_str(kind, dsa_port_kind(dp)); +		__entry->port = dp->index; +		__entry->vid = vlan->vid; +	), + +	TP_printk("%s %s port %d vid %u", +		  __get_str(dev), __get_str(kind), __entry->port, __entry->vid) +); + +#endif /* _NET_DSA_TRACE_H */ + +/* We don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE	trace +/* This part must be outside protection */ +#include <trace/define_trace.h>  |