Commit ce272973 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa_to_port-loops'

Vladimir Oltean says:

====================
Remove the "dsa_to_port in a loop" antipattern

v1->v2: more patches
v2->v3: less patches

As opposed to previous series, I would now like to first refactor the
DSA core, since that sees fewer patches than drivers, and make the
helpers available. Since the refactoring is fairly noisy, I don't want
to force it on driver maintainers right away, patches can be submitted
independently.

The original cover letter is below:

The DSA core and drivers currently iterate too much through the port
list of a switch. For example, this snippet:

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_cpu_port(ds, port))
			continue;

		ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
	}

iterates through ds->num_ports once, and then calls dsa_is_cpu_port to
filter out the other types of ports. But that function has a hidden call
to dsa_to_port() in it, which contains:

	list_for_each_entry(dp, &dst->ports, list)
		if (dp->ds == ds && dp->index == p)
			return dp;

where the only thing we wanted to know in the first place was whether
dp->type == DSA_PORT_TYPE_CPU or not.

So it seems that the problem is that we are not iterating with the right
variable. We have an "int port" but in fact need a "struct dsa_port *dp".

This has started being an issue since this patch series:
https://patchwork.ozlabs.org/project/netdev/cover/20191020031941.3805884-1-vivien.didelot@gmail.com/



The currently proposed set of changes iterates like this:

	dsa_switch_for_each_cpu_port(cpu_dp, ds)
		err = ds->ops->change_tag_protocol(ds, cpu_dp->index,
						   tag_ops->proto);

which iterates directly over ds->dst->ports, which is a list of struct
dsa_port *dp. This makes it much easier and more efficient to check
dp->type.

As a nice side effect, with the proposed driver API, driver writers are
now encouraged to use more efficient patterns, and not only due to less
iterations through the port list. For example, something like this:

	for (port = 0; port < ds->num_ports; port++)
		do_something();

probably does not need to do_something() for the ports that are disabled
in the device tree. But adding extra code for that would look like this:

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_unused_port(ds, port))
			continue;

		do_something();
	}

and therefore, it is understandable that some driver writers may decide
to not bother. This patch series introduces a "dsa_switch_for_each_available_port"
macro which comes at no extra cost in terms of lines of code / number of
braces to the driver writer, but it has the "dsa_is_unused_port" check
embedded within it.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dedb0809 992e5cc7
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -394,7 +394,8 @@ static int sja1105_init_virtual_links(struct sja1105_private *priv,
				vl_lookup[k].vlanid = rule->key.vl.vid;
				vl_lookup[k].vlanprior = rule->key.vl.pcp;
			} else {
				u16 vid = dsa_8021q_rx_vid(priv->ds, port);
				struct dsa_port *dp = dsa_to_port(priv->ds, port);
				u16 vid = dsa_tag_8021q_rx_vid(dp);

				vl_lookup[k].vlanid = vid;
				vl_lookup[k].vlanprior = 0;
+3 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/types.h>

struct dsa_switch;
struct dsa_port;
struct sk_buff;
struct net_device;

@@ -45,9 +46,9 @@ void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,

u16 dsa_8021q_bridge_tx_fwd_offload_vid(int bridge_num);

u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port);
u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp);

u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port);
u16 dsa_tag_8021q_rx_vid(const struct dsa_port *dp);

int dsa_8021q_rx_switch_id(u16 vid);

+31 −4
Original line number Diff line number Diff line
@@ -474,14 +474,41 @@ static inline bool dsa_is_user_port(struct dsa_switch *ds, int p)
	return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER;
}

#define dsa_tree_for_each_user_port(_dp, _dst) \
	list_for_each_entry((_dp), &(_dst)->ports, list) \
		if (dsa_port_is_user((_dp)))

#define dsa_switch_for_each_port(_dp, _ds) \
	list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
		if ((_dp)->ds == (_ds))

#define dsa_switch_for_each_port_safe(_dp, _next, _ds) \
	list_for_each_entry_safe((_dp), (_next), &(_ds)->dst->ports, list) \
		if ((_dp)->ds == (_ds))

#define dsa_switch_for_each_port_continue_reverse(_dp, _ds) \
	list_for_each_entry_continue_reverse((_dp), &(_ds)->dst->ports, list) \
		if ((_dp)->ds == (_ds))

#define dsa_switch_for_each_available_port(_dp, _ds) \
	dsa_switch_for_each_port((_dp), (_ds)) \
		if (!dsa_port_is_unused((_dp)))

#define dsa_switch_for_each_user_port(_dp, _ds) \
	dsa_switch_for_each_port((_dp), (_ds)) \
		if (dsa_port_is_user((_dp)))

#define dsa_switch_for_each_cpu_port(_dp, _ds) \
	dsa_switch_for_each_port((_dp), (_ds)) \
		if (dsa_port_is_cpu((_dp)))

static inline u32 dsa_user_ports(struct dsa_switch *ds)
{
	struct dsa_port *dp;
	u32 mask = 0;
	int p;

	for (p = 0; p < ds->num_ports; p++)
		if (dsa_is_user_port(ds, p))
			mask |= BIT(p);
	dsa_switch_for_each_user_port(dp, ds)
		mask |= BIT(dp->index);

	return mask;
}
+11 −11
Original line number Diff line number Diff line
@@ -280,23 +280,22 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
}

#ifdef CONFIG_PM_SLEEP
static bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
static bool dsa_port_is_initialized(const struct dsa_port *dp)
{
	const struct dsa_port *dp = dsa_to_port(ds, p);

	return dp->type == DSA_PORT_TYPE_USER && dp->slave;
}

int dsa_switch_suspend(struct dsa_switch *ds)
{
	int i, ret = 0;
	struct dsa_port *dp;
	int ret = 0;

	/* Suspend slave network devices */
	for (i = 0; i < ds->num_ports; i++) {
		if (!dsa_is_port_initialized(ds, i))
	dsa_switch_for_each_port(dp, ds) {
		if (!dsa_port_is_initialized(dp))
			continue;

		ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave);
		ret = dsa_slave_suspend(dp->slave);
		if (ret)
			return ret;
	}
@@ -310,7 +309,8 @@ EXPORT_SYMBOL_GPL(dsa_switch_suspend);

int dsa_switch_resume(struct dsa_switch *ds)
{
	int i, ret = 0;
	struct dsa_port *dp;
	int ret = 0;

	if (ds->ops->resume)
		ret = ds->ops->resume(ds);
@@ -319,11 +319,11 @@ int dsa_switch_resume(struct dsa_switch *ds)
		return ret;

	/* Resume slave network devices */
	for (i = 0; i < ds->num_ports; i++) {
		if (!dsa_is_port_initialized(ds, i))
	dsa_switch_for_each_port(dp, ds) {
		if (!dsa_port_is_initialized(dp))
			continue;

		ret = dsa_slave_resume(dsa_to_port(ds, i)->slave);
		ret = dsa_slave_resume(dp->slave);
		if (ret)
			return ret;
	}
+20 −37
Original line number Diff line number Diff line
@@ -399,11 +399,8 @@ static int dsa_tree_setup_cpu_ports(struct dsa_switch_tree *dst)
		if (!dsa_port_is_cpu(cpu_dp))
			continue;

		list_for_each_entry(dp, &dst->ports, list) {
		/* Prefer a local CPU port */
			if (dp->ds != cpu_dp->ds)
				continue;

		dsa_switch_for_each_port(dp, cpu_dp->ds) {
			/* Prefer the first local CPU port found */
			if (dp->cpu_dp)
				continue;
@@ -802,17 +799,16 @@ static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds)
{
	const struct dsa_device_ops *tag_ops = ds->dst->tag_ops;
	struct dsa_switch_tree *dst = ds->dst;
	int port, err;
	struct dsa_port *cpu_dp;
	int err;

	if (tag_ops->proto == dst->default_proto)
		return 0;

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_cpu_port(ds, port))
			continue;

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		rtnl_lock();
		err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
		err = ds->ops->change_tag_protocol(ds, cpu_dp->index,
						   tag_ops->proto);
		rtnl_unlock();
		if (err) {
			dev_err(ds->dev, "Unable to use tag protocol \"%s\": %pe\n",
@@ -853,13 +849,11 @@ static int dsa_switch_setup(struct dsa_switch *ds)
	/* Setup devlink port instances now, so that the switch
	 * setup() can register regions etc, against the ports
	 */
	list_for_each_entry(dp, &ds->dst->ports, list) {
		if (dp->ds == ds) {
	dsa_switch_for_each_port(dp, ds) {
		err = dsa_port_devlink_setup(dp);
		if (err)
			goto unregister_devlink_ports;
	}
	}

	err = dsa_switch_register_notifier(ds);
	if (err)
@@ -902,8 +896,7 @@ static int dsa_switch_setup(struct dsa_switch *ds)
unregister_notifier:
	dsa_switch_unregister_notifier(ds);
unregister_devlink_ports:
	list_for_each_entry(dp, &ds->dst->ports, list)
		if (dp->ds == ds)
	dsa_switch_for_each_port(dp, ds)
		dsa_port_devlink_teardown(dp);
	devlink_free(ds->devlink);
	ds->devlink = NULL;
@@ -932,8 +925,7 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
	dsa_switch_unregister_notifier(ds);

	if (ds->devlink) {
		list_for_each_entry(dp, &ds->dst->ports, list)
			if (dp->ds == ds)
		dsa_switch_for_each_port(dp, ds)
			dsa_port_devlink_teardown(dp);
		devlink_free(ds->devlink);
		ds->devlink = NULL;
@@ -1150,7 +1142,7 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
		goto out_unlock;

	list_for_each_entry(dp, &dst->ports, list) {
		if (!dsa_is_user_port(dp->ds, dp->index))
		if (!dsa_port_is_user(dp))
			continue;

		if (dp->slave->flags & IFF_UP)
@@ -1181,8 +1173,8 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
	struct dsa_switch_tree *dst = ds->dst;
	struct dsa_port *dp;

	list_for_each_entry(dp, &dst->ports, list)
		if (dp->ds == ds && dp->index == index)
	dsa_switch_for_each_port(dp, ds)
		if (dp->index == index)
			return dp;

	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
@@ -1523,12 +1515,9 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)

static void dsa_switch_release_ports(struct dsa_switch *ds)
{
	struct dsa_switch_tree *dst = ds->dst;
	struct dsa_port *dp, *next;

	list_for_each_entry_safe(dp, next, &dst->ports, list) {
		if (dp->ds != ds)
			continue;
	dsa_switch_for_each_port_safe(dp, next, ds) {
		list_del(&dp->list);
		kfree(dp);
	}
@@ -1620,13 +1609,7 @@ void dsa_switch_shutdown(struct dsa_switch *ds)
	mutex_lock(&dsa2_mutex);
	rtnl_lock();

	list_for_each_entry(dp, &ds->dst->ports, list) {
		if (dp->ds != ds)
			continue;

		if (!dsa_port_is_user(dp))
			continue;

	dsa_switch_for_each_user_port(dp, ds) {
		master = dp->cpu_dp->master;
		slave_dev = dp->slave;

Loading