2019-10-28 22:06:43

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 0/7] net: dsa: replace routing tables with a list

This branch gets rid of the ds->rtable static arrays in favor of
a single dst->rtable list. This allows us to move away from the
DSA_MAX_SWITCHES limitation and simplify the switch fabric setup.

This branch applies on top of Colin's "net: dsa: fix dereference on
ds->dev before null check error" commit.

Vivien Didelot (7):
net: dsa: list DSA links in the fabric
net: dsa: remove ds->rtable
net: dsa: remove switch routing table setup code
net: dsa: remove the dst->ds array
net: dsa: remove tree functions related to switches
net: dsa: remove limitation of switch index value
net: dsa: tag_8021q: clarify index limitation

drivers/net/dsa/mv88e6xxx/chip.c | 8 +--
include/net/dsa.h | 39 ++++++----
net/dsa/dsa2.c | 119 +++++++++++++------------------
net/dsa/tag_8021q.c | 5 +-
4 files changed, 83 insertions(+), 88 deletions(-)

--
2.23.0


2019-10-28 22:06:49

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 1/7] net: dsa: list DSA links in the fabric

Implement a new list of DSA links in the switch fabric itself, to
provide an alterative to the ds->rtable static arrays.

At the same time, provide a new dsa_routing_port() helper to abstract
the usage of ds->rtable in drivers. If there's no port to reach a
given device, return the first invalid port, ds->num_ports. This avoids
potential signedness errors or the need to define special values.

Signed-off-by: Vivien Didelot <[email protected]>
---
drivers/net/dsa/mv88e6xxx/chip.c | 8 +++----
include/net/dsa.h | 29 +++++++++++++++++++++++-
net/dsa/dsa2.c | 39 +++++++++++++++++++++++++++++++-
3 files changed, 70 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5fdf6d6ebe27..e8990dd77193 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1143,6 +1143,7 @@ static int mv88e6xxx_pri_setup(struct mv88e6xxx_chip *chip)

static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip)
{
+ struct dsa_switch *ds = chip->ds;
int target, port;
int err;

@@ -1151,10 +1152,9 @@ static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip)

/* Initialize the routing port to the 32 possible target devices */
for (target = 0; target < 32; target++) {
- port = 0x1f;
- if (target < DSA_MAX_SWITCHES)
- if (chip->ds->rtable[target] != DSA_RTABLE_NONE)
- port = chip->ds->rtable[target];
+ port = dsa_routing_port(ds, target);
+ if (port == ds->num_ports)
+ port = 0x1f;

err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
if (err)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 0b46b63fef67..fa401ed65e0c 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -123,6 +123,9 @@ struct dsa_switch_tree {
/* List of switch ports */
struct list_head ports;

+ /* List of DSA links composing the routing table */
+ struct list_head rtable;
+
/*
* Data for the individual switch chips.
*/
@@ -214,6 +217,17 @@ struct dsa_port {
bool setup;
};

+/* TODO: ideally DSA ports would have a single dp->link_dp member,
+ * and no dst->rtable nor this struct dsa_link would be needed,
+ * but this would require some more complex tree walking,
+ * so keep it stupid at the moment and list them all.
+ */
+struct dsa_link {
+ struct dsa_port *dp;
+ struct dsa_port *link_dp;
+ struct list_head list;
+};
+
struct dsa_switch {
bool setup;

@@ -324,6 +338,19 @@ static inline u32 dsa_user_ports(struct dsa_switch *ds)
return mask;
}

+/* Return the local port used to reach an arbitrary switch device */
+static inline unsigned int dsa_routing_port(struct dsa_switch *ds, int device)
+{
+ struct dsa_switch_tree *dst = ds->dst;
+ struct dsa_link *dl;
+
+ list_for_each_entry(dl, &dst->rtable, list)
+ if (dl->dp->ds == ds && dl->link_dp->ds->index == device)
+ return dl->dp->index;
+
+ return ds->num_ports;
+}
+
/* Return the local port used to reach an arbitrary switch port */
static inline unsigned int dsa_towards_port(struct dsa_switch *ds, int device,
int port)
@@ -331,7 +358,7 @@ static inline unsigned int dsa_towards_port(struct dsa_switch *ds, int device,
if (device == ds->index)
return port;
else
- return ds->rtable[device];
+ return dsa_routing_port(ds, device);
}

/* Return the local port used to reach the dedicated CPU port */
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 214dd703b0cc..79e8f9c34478 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -45,6 +45,8 @@ static struct dsa_switch_tree *dsa_tree_alloc(int index)

dst->index = index;

+ INIT_LIST_HEAD(&dst->rtable);
+
INIT_LIST_HEAD(&dst->ports);

INIT_LIST_HEAD(&dst->list);
@@ -122,6 +124,29 @@ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
return NULL;
}

+struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp)
+{
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_switch_tree *dst = ds->dst;
+ struct dsa_link *dl;
+
+ list_for_each_entry(dl, &dst->rtable, list)
+ if (dl->dp == dp && dl->link_dp == link_dp)
+ return dl;
+
+ dl = kzalloc(sizeof(*dl), GFP_KERNEL);
+ if (!dl)
+ return NULL;
+
+ dl->dp = dp;
+ dl->link_dp = link_dp;
+
+ INIT_LIST_HEAD(&dl->list);
+ list_add_tail(&dl->list, &dst->rtable);
+
+ return dl;
+}
+
static bool dsa_port_setup_routing_table(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
@@ -129,6 +154,7 @@ static bool dsa_port_setup_routing_table(struct dsa_port *dp)
struct device_node *dn = dp->dn;
struct of_phandle_iterator it;
struct dsa_port *link_dp;
+ struct dsa_link *dl;
int err;

of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
@@ -138,7 +164,11 @@ static bool dsa_port_setup_routing_table(struct dsa_port *dp)
return false;
}

- ds->rtable[link_dp->ds->index] = dp->index;
+ dl = dsa_link_touch(dp, link_dp);
+ if (!dl) {
+ of_node_put(it.node);
+ return false;
+ }
}

return true;
@@ -539,6 +569,8 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst)

static void dsa_tree_teardown(struct dsa_switch_tree *dst)
{
+ struct dsa_link *dl, *next;
+
if (!dst->setup)
return;

@@ -548,6 +580,11 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst)

dsa_tree_teardown_default_cpu(dst);

+ list_for_each_entry_safe(dl, next, &dst->rtable, list) {
+ list_del(&dl->list);
+ kfree(dl);
+ }
+
pr_info("DSA: tree %d torn down\n", dst->index);

dst->setup = false;
--
2.23.0

2019-10-28 22:06:54

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 7/7] net: dsa: tag_8021q: clarify index limitation

Now that there's no restriction from the DSA core side regarding
the switch IDs and port numbers, only tag_8021q which is currently
reserving 3 bits for the switch ID and 4 bits for the port number, has
limitation for these values. Update their descriptions to reflect that.

Signed-off-by: Vivien Didelot <[email protected]>
---
net/dsa/tag_8021q.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c
index bf91fc55fc44..bc5cb91bf052 100644
--- a/net/dsa/tag_8021q.c
+++ b/net/dsa/tag_8021q.c
@@ -31,15 +31,14 @@
* Must be transmitted as zero and ignored on receive.
*
* SWITCH_ID - VID[8:6]:
- * Index of switch within DSA tree. Must be between 0 and
- * DSA_MAX_SWITCHES - 1.
+ * Index of switch within DSA tree. Must be between 0 and 7.
*
* RSV - VID[5:4]:
* To be used for further expansion of PORT or for other purposes.
* Must be transmitted as zero and ignored on receive.
*
* PORT - VID[3:0]:
- * Index of switch port. Must be between 0 and DSA_MAX_PORTS - 1.
+ * Index of switch port. Must be between 0 and 15.
*/

#define DSA_8021Q_DIR_SHIFT 10
--
2.23.0

2019-10-28 22:07:01

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 6/7] net: dsa: remove limitation of switch index value

Because there is no static array describing the links between switches
anymore, we have no reason to force a limitation of the index value
set by the device tree.

Signed-off-by: Vivien Didelot <[email protected]>
---
net/dsa/dsa2.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 5d030e2d0b7d..4d00a0516cc0 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -704,8 +704,6 @@ static int dsa_switch_parse_member_of(struct dsa_switch *ds,
return sz;

ds->index = m[1];
- if (ds->index >= DSA_MAX_SWITCHES)
- return -EINVAL;

ds->dst = dsa_tree_touch(m[0]);
if (!ds->dst)
--
2.23.0

2019-10-28 22:07:03

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 4/7] net: dsa: remove the dst->ds array

Now that the DSA ports are listed in the switch fabric, there is
no need to store the dsa_switch structures from the drivers in the
fabric anymore. So get rid of the dst->ds static array.

Signed-off-by: Vivien Didelot <[email protected]>
---
include/net/dsa.h | 5 -----
net/dsa/dsa2.c | 7 -------
2 files changed, 12 deletions(-)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 2c9eb18e34e7..470e7b638c12 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -125,11 +125,6 @@ struct dsa_switch_tree {

/* List of DSA links composing the routing table */
struct list_head rtable;
-
- /*
- * Data for the individual switch chips.
- */
- struct dsa_switch *ds[DSA_MAX_SWITCHES];
};

/* TC matchall action types, only mirroring for now */
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 54df0845dda7..8cddc1b3304f 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -571,25 +571,18 @@ static void dsa_tree_remove_switch(struct dsa_switch_tree *dst,
{
dsa_tree_teardown(dst);

- dst->ds[index] = NULL;
dsa_tree_put(dst);
}

static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
struct dsa_switch *ds)
{
- unsigned int index = ds->index;
int err;

- if (dst->ds[index])
- return -EBUSY;
-
dsa_tree_get(dst);
- dst->ds[index] = ds;

err = dsa_tree_setup(dst);
if (err) {
- dst->ds[index] = NULL;
dsa_tree_put(dst);
}

--
2.23.0

2019-10-28 22:07:19

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 2/7] net: dsa: remove ds->rtable

Drivers do not use the ds->rtable static arrays anymore, get rid of it.

Signed-off-by: Vivien Didelot <[email protected]>
---
include/net/dsa.h | 7 -------
net/dsa/dsa2.c | 4 ----
2 files changed, 11 deletions(-)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index fa401ed65e0c..2c9eb18e34e7 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -258,13 +258,6 @@ struct dsa_switch {
*/
const struct dsa_switch_ops *ops;

- /*
- * An array of which element [a] indicates which port on this
- * switch should be used to send packets to that are destined
- * for switch a. Can be NULL if there is only one switch chip.
- */
- s8 rtable[DSA_MAX_SWITCHES];
-
/*
* Slave mii_bus and devices for the individual ports.
*/
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 79e8f9c34478..248cd13b0ad1 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -179,10 +179,6 @@ static bool dsa_switch_setup_routing_table(struct dsa_switch *ds)
struct dsa_switch_tree *dst = ds->dst;
bool complete = true;
struct dsa_port *dp;
- int i;
-
- for (i = 0; i < DSA_MAX_SWITCHES; i++)
- ds->rtable[i] = DSA_RTABLE_NONE;

list_for_each_entry(dp, &dst->ports, list) {
if (dp->ds == ds && dsa_port_is_dsa(dp)) {
--
2.23.0

2019-10-28 22:08:03

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 5/7] net: dsa: remove tree functions related to switches

The DSA fabric setup code has been simplified a lot so get rid of
the dsa_tree_remove_switch, dsa_tree_add_switch and dsa_switch_add
helpers, and keep the code simple with only the dsa_switch_probe and
dsa_switch_remove functions.

Signed-off-by: Vivien Didelot <[email protected]>
---
net/dsa/dsa2.c | 43 ++++++++++---------------------------------
1 file changed, 10 insertions(+), 33 deletions(-)

diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 8cddc1b3304f..5d030e2d0b7d 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -566,29 +566,6 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst)
dst->setup = false;
}

-static void dsa_tree_remove_switch(struct dsa_switch_tree *dst,
- unsigned int index)
-{
- dsa_tree_teardown(dst);
-
- dsa_tree_put(dst);
-}
-
-static int dsa_tree_add_switch(struct dsa_switch_tree *dst,
- struct dsa_switch *ds)
-{
- int err;
-
- dsa_tree_get(dst);
-
- err = dsa_tree_setup(dst);
- if (err) {
- dsa_tree_put(dst);
- }
-
- return err;
-}
-
static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
{
struct dsa_switch_tree *dst = ds->dst;
@@ -839,15 +816,9 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
return dsa_switch_parse_ports(ds, cd);
}

-static int dsa_switch_add(struct dsa_switch *ds)
-{
- struct dsa_switch_tree *dst = ds->dst;
-
- return dsa_tree_add_switch(dst, ds);
-}
-
static int dsa_switch_probe(struct dsa_switch *ds)
{
+ struct dsa_switch_tree *dst;
struct dsa_chip_data *pdata;
struct device_node *np;
int err;
@@ -871,7 +842,13 @@ static int dsa_switch_probe(struct dsa_switch *ds)
if (err)
return err;

- return dsa_switch_add(ds);
+ dst = ds->dst;
+ dsa_tree_get(dst);
+ err = dsa_tree_setup(dst);
+ if (err)
+ dsa_tree_put(dst);
+
+ return err;
}

int dsa_register_switch(struct dsa_switch *ds)
@@ -890,7 +867,6 @@ EXPORT_SYMBOL_GPL(dsa_register_switch);
static void dsa_switch_remove(struct dsa_switch *ds)
{
struct dsa_switch_tree *dst = ds->dst;
- unsigned int index = ds->index;
struct dsa_port *dp, *next;

list_for_each_entry_safe(dp, next, &dst->ports, list) {
@@ -898,7 +874,8 @@ static void dsa_switch_remove(struct dsa_switch *ds)
kfree(dp);
}

- dsa_tree_remove_switch(dst, index);
+ dsa_tree_teardown(dst);
+ dsa_tree_put(dst);
}

void dsa_unregister_switch(struct dsa_switch *ds)
--
2.23.0

2019-10-29 06:50:06

by Vivien Didelot

[permalink] [raw]
Subject: [PATCH net-next 3/7] net: dsa: remove switch routing table setup code

The dsa_switch structure has no routing table specific data to setup,
so the switch fabric can directly walk its ports and initialize its
routing table from them.

This allows us to remove the dsa_switch_setup_routing_table function.

Signed-off-by: Vivien Didelot <[email protected]>
---
net/dsa/dsa2.c | 24 ++----------------------
1 file changed, 2 insertions(+), 22 deletions(-)

diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 248cd13b0ad1..54df0845dda7 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -174,14 +174,13 @@ static bool dsa_port_setup_routing_table(struct dsa_port *dp)
return true;
}

-static bool dsa_switch_setup_routing_table(struct dsa_switch *ds)
+static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
{
- struct dsa_switch_tree *dst = ds->dst;
bool complete = true;
struct dsa_port *dp;

list_for_each_entry(dp, &dst->ports, list) {
- if (dp->ds == ds && dsa_port_is_dsa(dp)) {
+ if (dsa_port_is_dsa(dp)) {
complete = dsa_port_setup_routing_table(dp);
if (!complete)
break;
@@ -191,25 +190,6 @@ static bool dsa_switch_setup_routing_table(struct dsa_switch *ds)
return complete;
}

-static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
-{
- struct dsa_switch *ds;
- bool complete = true;
- int device;
-
- for (device = 0; device < DSA_MAX_SWITCHES; device++) {
- ds = dst->ds[device];
- if (!ds)
- continue;
-
- complete = dsa_switch_setup_routing_table(ds);
- if (!complete)
- break;
- }
-
- return complete;
-}
-
static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
{
struct dsa_port *dp;
--
2.23.0

2019-10-30 21:40:53

by David Miller

[permalink] [raw]
Subject: Re: [PATCH net-next 1/7] net: dsa: list DSA links in the fabric

From: Vivien Didelot <[email protected]>
Date: Mon, 28 Oct 2019 15:52:14 -0400

> @@ -122,6 +124,29 @@ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
> return NULL;
> }
>
> +struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp)
> +{
> + struct dsa_switch *ds = dp->ds;
> + struct dsa_switch_tree *dst = ds->dst;
> + struct dsa_link *dl;

Please fix the reverse christmas tree here, two suggestions:

struct dsa_switch *ds = dp->ds;
struct dsa_switch_tree *dst;
struct dsa_link *dl;

dst = ds->dst;

Or, alternatively, since the dst variable is used only once, get rid of it
and change:

> + list_add_tail(&dl->list, &dst->rtable);

to

> + list_add_tail(&dl->list, &ds->dst->rtable);

2019-10-31 02:16:57

by Vivien Didelot

[permalink] [raw]
Subject: Re: [PATCH net-next 1/7] net: dsa: list DSA links in the fabric

Hi David,

On Wed, 30 Oct 2019 14:39:49 -0700 (PDT), David Miller <[email protected]> wrote:
> From: Vivien Didelot <[email protected]>
> Date: Mon, 28 Oct 2019 15:52:14 -0400
>
> > @@ -122,6 +124,29 @@ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
> > return NULL;
> > }
> >
> > +struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp)
> > +{
> > + struct dsa_switch *ds = dp->ds;
> > + struct dsa_switch_tree *dst = ds->dst;
> > + struct dsa_link *dl;
>
> Please fix the reverse christmas tree here, two suggestions:
>
> struct dsa_switch *ds = dp->ds;
> struct dsa_switch_tree *dst;
> struct dsa_link *dl;
>
> dst = ds->dst;
>
> Or, alternatively, since the dst variable is used only once, get rid of it
> and change:
>
> > + list_add_tail(&dl->list, &dst->rtable);
>
> to
>
> > + list_add_tail(&dl->list, &ds->dst->rtable);

No problem, I've sent a v2 using the first suggestion, since dst is in fact
used twice in this function.


Thank you,

Vivien