net: dsa: Register devlink ports before calling DSA driver setup()
[linux-2.6-microblaze.git] / net / dsa / dsa2.c
index c0ffc7a..183003e 100644 (file)
@@ -21,9 +21,6 @@
 static DEFINE_MUTEX(dsa2_mutex);
 LIST_HEAD(dsa_tree_list);
 
-static const struct devlink_ops dsa_devlink_ops = {
-};
-
 struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
 {
        struct dsa_switch_tree *dst;
@@ -254,22 +251,11 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
 
 static int dsa_port_setup(struct dsa_port *dp)
 {
-       struct dsa_switch *ds = dp->ds;
-       struct dsa_switch_tree *dst = ds->dst;
-       const unsigned char *id = (const unsigned char *)&dst->index;
-       const unsigned char len = sizeof(dst->index);
        struct devlink_port *dlp = &dp->devlink_port;
        bool dsa_port_link_registered = false;
-       bool devlink_port_registered = false;
-       struct devlink_port_attrs attrs = {};
-       struct devlink *dl = ds->devlink;
        bool dsa_port_enabled = false;
        int err = 0;
 
-       attrs.phys.port_number = dp->index;
-       memcpy(attrs.switch_id.id, id, len);
-       attrs.switch_id.id_len = len;
-
        if (dp->setup)
                return 0;
 
@@ -278,14 +264,6 @@ static int dsa_port_setup(struct dsa_port *dp)
                dsa_port_disable(dp);
                break;
        case DSA_PORT_TYPE_CPU:
-               memset(dlp, 0, sizeof(*dlp));
-               attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
-               devlink_port_attrs_set(dlp, &attrs);
-               err = devlink_port_register(dl, dlp, dp->index);
-               if (err)
-                       break;
-               devlink_port_registered = true;
-
                err = dsa_port_link_register_of(dp);
                if (err)
                        break;
@@ -298,14 +276,6 @@ static int dsa_port_setup(struct dsa_port *dp)
 
                break;
        case DSA_PORT_TYPE_DSA:
-               memset(dlp, 0, sizeof(*dlp));
-               attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
-               devlink_port_attrs_set(dlp, &attrs);
-               err = devlink_port_register(dl, dlp, dp->index);
-               if (err)
-                       break;
-               devlink_port_registered = true;
-
                err = dsa_port_link_register_of(dp);
                if (err)
                        break;
@@ -318,14 +288,6 @@ static int dsa_port_setup(struct dsa_port *dp)
 
                break;
        case DSA_PORT_TYPE_USER:
-               memset(dlp, 0, sizeof(*dlp));
-               attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
-               devlink_port_attrs_set(dlp, &attrs);
-               err = devlink_port_register(dl, dlp, dp->index);
-               if (err)
-                       break;
-               devlink_port_registered = true;
-
                dp->mac = of_get_mac_address(dp->dn);
                err = dsa_slave_create(dp);
                if (err)
@@ -339,8 +301,6 @@ static int dsa_port_setup(struct dsa_port *dp)
                dsa_port_disable(dp);
        if (err && dsa_port_link_registered)
                dsa_port_link_unregister_of(dp);
-       if (err && devlink_port_registered)
-               devlink_port_unregister(dlp);
        if (err)
                return err;
 
@@ -349,10 +309,50 @@ static int dsa_port_setup(struct dsa_port *dp)
        return 0;
 }
 
-static void dsa_port_teardown(struct dsa_port *dp)
+static int dsa_port_devlink_setup(struct dsa_port *dp)
 {
        struct devlink_port *dlp = &dp->devlink_port;
+       struct dsa_switch_tree *dst = dp->ds->dst;
+       struct devlink_port_attrs attrs = {};
+       struct devlink *dl = dp->ds->devlink;
+       const unsigned char *id;
+       unsigned char len;
+       int err;
+
+       id = (const unsigned char *)&dst->index;
+       len = sizeof(dst->index);
 
+       attrs.phys.port_number = dp->index;
+       memcpy(attrs.switch_id.id, id, len);
+       attrs.switch_id.id_len = len;
+       memset(dlp, 0, sizeof(*dlp));
+
+       switch (dp->type) {
+       case DSA_PORT_TYPE_UNUSED:
+               attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
+               break;
+       case DSA_PORT_TYPE_CPU:
+               attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
+               break;
+       case DSA_PORT_TYPE_DSA:
+               attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
+               break;
+       case DSA_PORT_TYPE_USER:
+               attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
+               break;
+       }
+
+       devlink_port_attrs_set(dlp, &attrs);
+       err = devlink_port_register(dl, dlp, dp->index);
+
+       if (!err)
+               dp->devlink_port_setup = true;
+
+       return err;
+}
+
+static void dsa_port_teardown(struct dsa_port *dp)
+{
        if (!dp->setup)
                return;
 
@@ -362,16 +362,13 @@ static void dsa_port_teardown(struct dsa_port *dp)
        case DSA_PORT_TYPE_CPU:
                dsa_port_disable(dp);
                dsa_tag_driver_put(dp->tag_ops);
-               devlink_port_unregister(dlp);
                dsa_port_link_unregister_of(dp);
                break;
        case DSA_PORT_TYPE_DSA:
                dsa_port_disable(dp);
-               devlink_port_unregister(dlp);
                dsa_port_link_unregister_of(dp);
                break;
        case DSA_PORT_TYPE_USER:
-               devlink_port_unregister(dlp);
                if (dp->slave) {
                        dsa_slave_destroy(dp->slave);
                        dp->slave = NULL;
@@ -382,9 +379,35 @@ static void dsa_port_teardown(struct dsa_port *dp)
        dp->setup = false;
 }
 
+static void dsa_port_devlink_teardown(struct dsa_port *dp)
+{
+       struct devlink_port *dlp = &dp->devlink_port;
+
+       if (dp->devlink_port_setup)
+               devlink_port_unregister(dlp);
+       dp->devlink_port_setup = false;
+}
+
+static int dsa_devlink_info_get(struct devlink *dl,
+                               struct devlink_info_req *req,
+                               struct netlink_ext_ack *extack)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+
+       if (ds->ops->devlink_info_get)
+               return ds->ops->devlink_info_get(ds, req, extack);
+
+       return -EOPNOTSUPP;
+}
+
+static const struct devlink_ops dsa_devlink_ops = {
+       .info_get = dsa_devlink_info_get,
+};
+
 static int dsa_switch_setup(struct dsa_switch *ds)
 {
        struct dsa_devlink_priv *dl_priv;
+       struct dsa_port *dp;
        int err;
 
        if (ds->setup)
@@ -410,9 +433,20 @@ static int dsa_switch_setup(struct dsa_switch *ds)
        if (err)
                goto free_devlink;
 
+       /* 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;
+               }
+       }
+
        err = dsa_switch_register_notifier(ds);
        if (err)
-               goto unregister_devlink;
+               goto unregister_devlink_ports;
 
        err = ds->ops->setup(ds);
        if (err < 0)
@@ -440,7 +474,10 @@ static int dsa_switch_setup(struct dsa_switch *ds)
 
 unregister_notifier:
        dsa_switch_unregister_notifier(ds);
-unregister_devlink:
+unregister_devlink_ports:
+       list_for_each_entry(dp, &ds->dst->ports, list)
+               if (dp->ds == ds)
+                       dsa_port_devlink_teardown(dp);
        devlink_unregister(ds->devlink);
 free_devlink:
        devlink_free(ds->devlink);
@@ -451,6 +488,8 @@ free_devlink:
 
 static void dsa_switch_teardown(struct dsa_switch *ds)
 {
+       struct dsa_port *dp;
+
        if (!ds->setup)
                return;
 
@@ -463,6 +502,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
                ds->ops->teardown(ds);
 
        if (ds->devlink) {
+               list_for_each_entry(dp, &ds->dst->ports, list)
+                       if (dp->ds == ds)
+                               dsa_port_devlink_teardown(dp);
                devlink_unregister(ds->devlink);
                devlink_free(ds->devlink);
                ds->devlink = NULL;