]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: dsa: sja1105: expose static config as devlink region
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 25 Sep 2020 23:04:20 +0000 (02:04 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 Sep 2020 23:35:27 +0000 (16:35 -0700)
As explained in Documentation/networking/dsa/sja1105.rst, this switch
has a static config held in the driver's memory and re-uploaded from
time to time into the device (after any major change).

The format of this static config is in fact described in UM10944.pdf and
it contains all the switch's settings (it also contains device ID, table
CRCs, etc, just like in the manual). So it is a useful and universal
devlink region to expose to user space, for debugging purposes.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/sja1105/sja1105.h
drivers/net/dsa/sja1105/sja1105_devlink.c
drivers/net/dsa/sja1105/sja1105_spi.c

index d043332cbc02fa32310f42c89a53110907e42a95..4af70f619d8e9a36d45104931e03f4c5a1315cc5 100644 (file)
@@ -218,6 +218,7 @@ struct sja1105_private {
        struct mutex mgmt_lock;
        struct dsa_8021q_context *dsa_8021q_ctx;
        enum sja1105_vlan_state vlan_state;
+       struct devlink_region **regions;
        struct sja1105_cbs_entry *cbs;
        struct sja1105_tagger_data tagger_data;
        struct sja1105_ptp_data ptp_data;
@@ -265,6 +266,8 @@ int sja1105_xfer_u32(const struct sja1105_private *priv,
 int sja1105_xfer_u64(const struct sja1105_private *priv,
                     sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value,
                     struct ptp_system_timestamp *ptp_sts);
+int static_config_buf_prepare_for_upload(struct sja1105_private *priv,
+                                        void *config_buf, int buf_len);
 int sja1105_static_config_upload(struct sja1105_private *priv);
 int sja1105_inhibit_tx(const struct sja1105_private *priv,
                       unsigned long port_bitmap, bool tx_inhibited);
index 03454638c5a8417a6474a728faac76b1bf9492bb..07ae6913d18826af65065e385855ab2107efdd1d 100644 (file)
@@ -1,8 +1,120 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
+ * Copyright 2020 NXP Semiconductors
  */
 #include "sja1105.h"
 
+/* Since devlink regions have a fixed size and the static config has a variable
+ * size, we need to calculate the maximum possible static config size by
+ * creating a dummy config with all table entries populated to the max, and get
+ * its packed length. This is done dynamically as opposed to simply hardcoding
+ * a number, since currently not all static config tables are implemented, so
+ * we are avoiding a possible code desynchronization.
+ */
+static size_t sja1105_static_config_get_max_size(struct sja1105_private *priv)
+{
+       struct sja1105_static_config config;
+       enum sja1105_blk_idx blk_idx;
+       int rc;
+
+       rc = sja1105_static_config_init(&config,
+                                       priv->info->static_ops,
+                                       priv->info->device_id);
+       if (rc)
+               return 0;
+
+       for (blk_idx = 0; blk_idx < BLK_IDX_MAX; blk_idx++) {
+               struct sja1105_table *table = &config.tables[blk_idx];
+
+               table->entry_count = table->ops->max_entry_count;
+       }
+
+       return sja1105_static_config_get_length(&config);
+}
+
+static int
+sja1105_region_static_config_snapshot(struct devlink *dl,
+                                     const struct devlink_region_ops *ops,
+                                     struct netlink_ext_ack *extack,
+                                     u8 **data)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+       struct sja1105_private *priv = ds->priv;
+       size_t max_len, len;
+
+       len = sja1105_static_config_get_length(&priv->static_config);
+       max_len = sja1105_static_config_get_max_size(priv);
+
+       *data = kcalloc(max_len, sizeof(u8), GFP_KERNEL);
+       if (!*data)
+               return -ENOMEM;
+
+       return static_config_buf_prepare_for_upload(priv, *data, len);
+}
+
+static struct devlink_region_ops sja1105_region_static_config_ops = {
+       .name = "static-config",
+       .snapshot = sja1105_region_static_config_snapshot,
+       .destructor = kfree,
+};
+
+enum sja1105_region_id {
+       SJA1105_REGION_STATIC_CONFIG = 0,
+};
+
+struct sja1105_region {
+       const struct devlink_region_ops *ops;
+       size_t (*get_size)(struct sja1105_private *priv);
+};
+
+static struct sja1105_region sja1105_regions[] = {
+       [SJA1105_REGION_STATIC_CONFIG] = {
+               .ops = &sja1105_region_static_config_ops,
+               .get_size = sja1105_static_config_get_max_size,
+       },
+};
+
+static int sja1105_setup_devlink_regions(struct dsa_switch *ds)
+{
+       int i, num_regions = ARRAY_SIZE(sja1105_regions);
+       struct sja1105_private *priv = ds->priv;
+       const struct devlink_region_ops *ops;
+       struct devlink_region *region;
+       u64 size;
+
+       priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *),
+                               GFP_KERNEL);
+       if (!priv->regions)
+               return -ENOMEM;
+
+       for (i = 0; i < num_regions; i++) {
+               size = sja1105_regions[i].get_size(priv);
+               ops = sja1105_regions[i].ops;
+
+               region = dsa_devlink_region_create(ds, ops, 1, size);
+               if (IS_ERR(region)) {
+                       while (i-- >= 0)
+                               dsa_devlink_region_destroy(priv->regions[i]);
+                       return PTR_ERR(region);
+               }
+
+               priv->regions[i] = region;
+       }
+
+       return 0;
+}
+
+static void sja1105_teardown_devlink_regions(struct dsa_switch *ds)
+{
+       int i, num_regions = ARRAY_SIZE(sja1105_regions);
+       struct sja1105_private *priv = ds->priv;
+
+       for (i = 0; i < num_regions; i++)
+               dsa_devlink_region_destroy(priv->regions[i]);
+
+       kfree(priv->regions);
+}
+
 static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv,
                                                  bool *be_vlan)
 {
@@ -110,10 +222,17 @@ int sja1105_devlink_setup(struct dsa_switch *ds)
        if (rc)
                return rc;
 
+       rc = sja1105_setup_devlink_regions(ds);
+       if (rc < 0) {
+               sja1105_teardown_devlink_params(ds);
+               return rc;
+       }
+
        return 0;
 }
 
 void sja1105_devlink_teardown(struct dsa_switch *ds)
 {
        sja1105_teardown_devlink_params(ds);
+       sja1105_teardown_devlink_regions(ds);
 }
index 704dcf1d1c01d12124dd4acd60fae1bf2af7edc4..591c5734747d95cc1f9247c7c0aa0467e52d195e 100644 (file)
@@ -302,9 +302,8 @@ static int sja1105_status_get(struct sja1105_private *priv,
  * for upload requires the recalculation of table CRCs and updating the
  * structures with these.
  */
-static int
-static_config_buf_prepare_for_upload(struct sja1105_private *priv,
-                                    void *config_buf, int buf_len)
+int static_config_buf_prepare_for_upload(struct sja1105_private *priv,
+                                        void *config_buf, int buf_len)
 {
        struct sja1105_static_config *config = &priv->static_config;
        struct sja1105_table_header final_header;