Skip to content

Commit

Permalink
Merge branch 'dsa_to_port-loops'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 21, 2021
2 parents dedb080 + 992e5cc commit ce27297
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 214 deletions.
3 changes: 2 additions & 1 deletion drivers/net/dsa/sja1105/sja1105_vl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions include/linux/dsa/8021q.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/types.h>

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

Expand Down Expand Up @@ -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);

Expand Down
35 changes: 31 additions & 4 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
22 changes: 11 additions & 11 deletions net/dsa/dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down
57 changes: 20 additions & 37 deletions net/dsa/dsa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/* Prefer a local CPU port */
dsa_switch_for_each_port(dp, cpu_dp->ds) {
/* Prefer the first local CPU port found */
if (dp->cpu_dp)
continue;
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -853,12 +849,10 @@ 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) {
err = dsa_port_devlink_setup(dp);
if (err)
goto unregister_devlink_ports;
}
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);
Expand Down Expand Up @@ -902,9 +896,8 @@ 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_port_devlink_teardown(dp);
dsa_switch_for_each_port(dp, ds)
dsa_port_devlink_teardown(dp);
devlink_free(ds->devlink);
ds->devlink = NULL;
return err;
Expand Down Expand Up @@ -932,9 +925,8 @@ 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_port_devlink_teardown(dp);
dsa_switch_for_each_port(dp, ds)
dsa_port_devlink_teardown(dp);
devlink_free(ds->devlink);
ds->devlink = NULL;
}
Expand Down Expand Up @@ -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)
Expand Down Expand 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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;

Expand Down
21 changes: 9 additions & 12 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,14 +515,15 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
struct netlink_ext_ack *extack)
{
struct dsa_switch *ds = dp->ds;
int err, i;
struct dsa_port *other_dp;
int err;

/* VLAN awareness was off, so the question is "can we turn it on".
* We may have had 8021q uppers, those need to go. Make sure we don't
* enter an inconsistent state: deny changing the VLAN awareness state
* as long as we have 8021q uppers.
*/
if (vlan_filtering && dsa_is_user_port(ds, dp->index)) {
if (vlan_filtering && dsa_port_is_user(dp)) {
struct net_device *upper_dev, *slave = dp->slave;
struct net_device *br = dp->bridge_dev;
struct list_head *iter;
Expand Down Expand Up @@ -557,10 +558,10 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
* different ports of the same switch device and one of them has a
* different setting than what is being requested.
*/
for (i = 0; i < ds->num_ports; i++) {
dsa_switch_for_each_port(other_dp, ds) {
struct net_device *other_bridge;

other_bridge = dsa_to_port(ds, i)->bridge_dev;
other_bridge = other_dp->bridge_dev;
if (!other_bridge)
continue;
/* If it's the same bridge, it also has same
Expand Down Expand Up @@ -607,20 +608,16 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
return err;

if (ds->vlan_filtering_is_global) {
int port;
struct dsa_port *other_dp;

ds->vlan_filtering = vlan_filtering;

for (port = 0; port < ds->num_ports; port++) {
struct net_device *slave;

if (!dsa_is_user_port(ds, port))
continue;
dsa_switch_for_each_user_port(other_dp, ds) {
struct net_device *slave = dp->slave;

/* We might be called in the unbind path, so not
* all slave devices might still be registered.
*/
slave = dsa_to_port(ds, port)->slave;
if (!slave)
continue;

Expand Down Expand Up @@ -1041,7 +1038,7 @@ static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
struct phy_device *phydev = NULL;
struct dsa_switch *ds = dp->ds;

if (dsa_is_user_port(ds, dp->index))
if (dsa_port_is_user(dp))
phydev = dp->slave->phydev;

if (!ds->ops->phylink_mac_link_down) {
Expand Down
2 changes: 1 addition & 1 deletion net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -2368,7 +2368,7 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
dst = cpu_dp->ds->dst;

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

list_add(&dp->slave->close_list, &close_list);
Expand Down
Loading

0 comments on commit ce27297

Please sign in to comment.