net: dsa: list DSA links in the fabric
authorVivien Didelot <vivien.didelot@gmail.com>
Thu, 31 Oct 2019 02:09:13 +0000 (22:09 -0400)
committerDavid S. Miller <davem@davemloft.net>
Thu, 31 Oct 2019 21:26:38 +0000 (14:26 -0700)
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 <vivien.didelot@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/chip.c
include/net/dsa.h
net/dsa/dsa2.c

index 619cd08..66de492 100644 (file)
@@ -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)
index 9aba326..3d7366d 100644 (file)
@@ -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 */
index e7aae96..222d7db 100644 (file)
@@ -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,31 @@ 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;
+       struct dsa_link *dl;
+
+       dst = ds->dst;
+
+       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 +156,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 +166,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;
@@ -544,6 +576,8 @@ teardown_default_cpu:
 
 static void dsa_tree_teardown(struct dsa_switch_tree *dst)
 {
+       struct dsa_link *dl, *next;
+
        if (!dst->setup)
                return;
 
@@ -553,6 +587,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;