coresight: tmc: Make etr buffer mode user configurable from sysfs
authorAnshuman Khandual <anshuman.khandual@arm.com>
Fri, 18 Aug 2023 08:21:12 +0000 (13:51 +0530)
committerSuzuki K Poulose <suzuki.poulose@arm.com>
Thu, 16 Nov 2023 11:35:12 +0000 (11:35 +0000)
Currently TMC-ETR automatically selects the buffer mode from all available
methods in the following sequentially fallback manner - also in that order.

1. FLAT mode with or without IOMMU
2. TMC-ETR-SG (scatter gather) mode when available
3. CATU mode when available

But this order might not be ideal for all situations. For example if there
is a CATU connected to ETR, it may be better to use TMC-ETR scatter gather
method, rather than CATU. But hard coding such order changes will prevent
us from testing or using a particular mode. This change provides following
new sysfs tunables for the user to control TMC-ETR buffer mode explicitly,
if required. This adds following new sysfs files for buffer mode selection
purpose explicitly in the user space.

/sys/bus/coresight/devices/tmc_etr<N>/buf_modes_available
/sys/bus/coresight/devices/tmc_etr<N>/buf_mode_preferred

$ cat buf_modes_available
auto flat tmc-sg catu ------------------> Supported TMC-ETR buffer modes

$ echo catu > buf_mode_preferred   -------> Explicit buffer mode request

But explicit user request has to be within supported ETR buffer modes only.
These sysfs interface files are exclussive to ETR, and hence these are not
available for other TMC devices such as ETB or ETF etc.

A new auto' mode (i.e ETR_MODE_AUTO) has been added to help fallback to the
existing default behaviour, when user provided preferred buffer mode fails.
ETR_MODE_FLAT and ETR_MODE_AUTO are always available as preferred modes.

Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: James Clark <james.clark@arm.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
[Fixup year in sysfs ABI documentation]
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20230818082112.554638-1-anshuman.khandual@arm.com
Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc
drivers/hwtracing/coresight/coresight-tmc-core.c
drivers/hwtracing/coresight/coresight-tmc-etr.c
drivers/hwtracing/coresight/coresight-tmc.h

index 6aa527296c71080cbbf8ecf3ce53e375a2e70022..96aafa66b4a5806c47f085100a15d16a8842639c 100644 (file)
@@ -91,3 +91,19 @@ Contact:     Mathieu Poirier <mathieu.poirier@linaro.org>
 Description:   (RW) Size of the trace buffer for TMC-ETR when used in SYSFS
                mode. Writable only for TMC-ETR configurations. The value
                should be aligned to the kernel pagesize.
+
+What:          /sys/bus/coresight/devices/<memory_map>.tmc/buf_modes_available
+Date:          August 2023
+KernelVersion: 6.7
+Contact:       Anshuman Khandual <anshuman.khandual@arm.com>
+Description:   (Read) Shows all supported Coresight TMC-ETR buffer modes available
+               for the users to configure explicitly. This file is avaialble only
+               for TMC ETR devices.
+
+What:          /sys/bus/coresight/devices/<memory_map>.tmc/buf_mode_preferred
+Date:          August 2023
+KernelVersion: 6.7
+Contact:       Anshuman Khandual <anshuman.khandual@arm.com>
+Description:   (RW) Current Coresight TMC-ETR buffer mode selected. But user could
+               only provide a mode which is supported for a given ETR device. This
+               file is available only for TMC ETR devices.
index c106d142e63221df0e7ebc42c9a2799495199b2a..7ec5365e2b6429dcc3da499d7cef9ba2ab0967a5 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/device.h>
 #include <linux/idr.h>
 #include <linux/io.h>
+#include <linux/iommu.h>
 #include <linux/err.h>
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
@@ -344,7 +345,14 @@ static const struct attribute_group coresight_tmc_mgmt_group = {
        .name = "mgmt",
 };
 
-static const struct attribute_group *coresight_tmc_groups[] = {
+static const struct attribute_group *coresight_etf_groups[] = {
+       &coresight_tmc_group,
+       &coresight_tmc_mgmt_group,
+       NULL,
+};
+
+static const struct attribute_group *coresight_etr_groups[] = {
+       &coresight_etr_group,
        &coresight_tmc_group,
        &coresight_tmc_mgmt_group,
        NULL,
@@ -465,6 +473,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
        drvdata->memwidth = tmc_get_memwidth(devid);
        /* This device is not associated with a session */
        drvdata->pid = -1;
+       drvdata->etr_mode = ETR_MODE_AUTO;
 
        if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
                drvdata->size = tmc_etr_get_default_buffer_size(dev);
@@ -474,16 +483,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
        }
 
        desc.dev = dev;
-       desc.groups = coresight_tmc_groups;
 
        switch (drvdata->config_type) {
        case TMC_CONFIG_TYPE_ETB:
+               desc.groups = coresight_etf_groups;
                desc.type = CORESIGHT_DEV_TYPE_SINK;
                desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
                desc.ops = &tmc_etb_cs_ops;
                dev_list = &etb_devs;
                break;
        case TMC_CONFIG_TYPE_ETR:
+               desc.groups = coresight_etr_groups;
                desc.type = CORESIGHT_DEV_TYPE_SINK;
                desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM;
                desc.ops = &tmc_etr_cs_ops;
@@ -496,6 +506,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
                dev_list = &etr_devs;
                break;
        case TMC_CONFIG_TYPE_ETF:
+               desc.groups = coresight_etf_groups;
                desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
                desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
                desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
index 8311e1028ddb03096f207798f9226e29b4025efc..af02ba5d5f15de7160421f8ba4d256907e4bd7dd 100644 (file)
@@ -26,6 +26,12 @@ struct etr_flat_buf {
        size_t          size;
 };
 
+struct etr_buf_hw {
+       bool    has_iommu;
+       bool    has_etr_sg;
+       bool    has_catu;
+};
+
 /*
  * etr_perf_buffer - Perf buffer used for ETR
  * @drvdata            - The ETR drvdaga this buffer has been allocated for.
@@ -830,6 +836,22 @@ static inline int tmc_etr_mode_alloc_buf(int mode,
        }
 }
 
+static void get_etr_buf_hw(struct device *dev, struct etr_buf_hw *buf_hw)
+{
+       struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+       buf_hw->has_iommu = iommu_get_domain_for_dev(dev->parent);
+       buf_hw->has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
+       buf_hw->has_catu = !!tmc_etr_get_catu_device(drvdata);
+}
+
+static bool etr_can_use_flat_mode(struct etr_buf_hw *buf_hw, ssize_t etr_buf_size)
+{
+       bool has_sg = buf_hw->has_catu || buf_hw->has_etr_sg;
+
+       return !has_sg || buf_hw->has_iommu || etr_buf_size < SZ_1M;
+}
+
 /*
  * tmc_alloc_etr_buf: Allocate a buffer use by ETR.
  * @drvdata    : ETR device details.
@@ -843,23 +865,22 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata,
                                         int node, void **pages)
 {
        int rc = -ENOMEM;
-       bool has_etr_sg, has_iommu;
-       bool has_sg, has_catu;
        struct etr_buf *etr_buf;
+       struct etr_buf_hw buf_hw;
        struct device *dev = &drvdata->csdev->dev;
 
-       has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
-       has_iommu = iommu_get_domain_for_dev(dev->parent);
-       has_catu = !!tmc_etr_get_catu_device(drvdata);
-
-       has_sg = has_catu || has_etr_sg;
-
+       get_etr_buf_hw(dev, &buf_hw);
        etr_buf = kzalloc(sizeof(*etr_buf), GFP_KERNEL);
        if (!etr_buf)
                return ERR_PTR(-ENOMEM);
 
        etr_buf->size = size;
 
+       /* If there is user directive for buffer mode, try that first */
+       if (drvdata->etr_mode != ETR_MODE_AUTO)
+               rc = tmc_etr_mode_alloc_buf(drvdata->etr_mode, drvdata,
+                                           etr_buf, node, pages);
+
        /*
         * If we have to use an existing list of pages, we cannot reliably
         * use a contiguous DMA memory (even if we have an IOMMU). Otherwise,
@@ -872,14 +893,13 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata,
         * Fallback to available mechanisms.
         *
         */
-       if (!pages &&
-           (!has_sg || has_iommu || size < SZ_1M))
+       if (rc && !pages && etr_can_use_flat_mode(&buf_hw, size))
                rc = tmc_etr_mode_alloc_buf(ETR_MODE_FLAT, drvdata,
                                            etr_buf, node, pages);
-       if (rc && has_etr_sg)
+       if (rc && buf_hw.has_etr_sg)
                rc = tmc_etr_mode_alloc_buf(ETR_MODE_ETR_SG, drvdata,
                                            etr_buf, node, pages);
-       if (rc && has_catu)
+       if (rc && buf_hw.has_catu)
                rc = tmc_etr_mode_alloc_buf(ETR_MODE_CATU, drvdata,
                                            etr_buf, node, pages);
        if (rc) {
@@ -1804,3 +1824,70 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 
        return 0;
 }
+
+static const char *const buf_modes_str[] = {
+       [ETR_MODE_FLAT]         = "flat",
+       [ETR_MODE_ETR_SG]       = "tmc-sg",
+       [ETR_MODE_CATU]         = "catu",
+       [ETR_MODE_AUTO]         = "auto",
+};
+
+static ssize_t buf_modes_available_show(struct device *dev,
+                                           struct device_attribute *attr, char *buf)
+{
+       struct etr_buf_hw buf_hw;
+       ssize_t size = 0;
+
+       get_etr_buf_hw(dev, &buf_hw);
+       size += sysfs_emit(buf, "%s ", buf_modes_str[ETR_MODE_AUTO]);
+       size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_FLAT]);
+       if (buf_hw.has_etr_sg)
+               size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_ETR_SG]);
+
+       if (buf_hw.has_catu)
+               size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_CATU]);
+
+       size += sysfs_emit_at(buf, size, "\n");
+       return size;
+}
+static DEVICE_ATTR_RO(buf_modes_available);
+
+static ssize_t buf_mode_preferred_show(struct device *dev,
+                                        struct device_attribute *attr, char *buf)
+{
+       struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+       return sysfs_emit(buf, "%s\n", buf_modes_str[drvdata->etr_mode]);
+}
+
+static ssize_t buf_mode_preferred_store(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t size)
+{
+       struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+       struct etr_buf_hw buf_hw;
+
+       get_etr_buf_hw(dev, &buf_hw);
+       if (sysfs_streq(buf, buf_modes_str[ETR_MODE_FLAT]))
+               drvdata->etr_mode = ETR_MODE_FLAT;
+       else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_ETR_SG]) && buf_hw.has_etr_sg)
+               drvdata->etr_mode = ETR_MODE_ETR_SG;
+       else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_CATU]) && buf_hw.has_catu)
+               drvdata->etr_mode = ETR_MODE_CATU;
+       else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_AUTO]))
+               drvdata->etr_mode = ETR_MODE_AUTO;
+       else
+               return -EINVAL;
+       return size;
+}
+static DEVICE_ATTR_RW(buf_mode_preferred);
+
+static struct attribute *coresight_etr_attrs[] = {
+       &dev_attr_buf_modes_available.attr,
+       &dev_attr_buf_mode_preferred.attr,
+       NULL,
+};
+
+const struct attribute_group coresight_etr_group = {
+       .attrs = coresight_etr_attrs,
+};
index 0ee48c5ba764d1f7e9c943540138b953f80e9a33..8dcb426ac3e7aa259393ab2e550d4bf186dbbc46 100644 (file)
@@ -135,6 +135,7 @@ enum etr_mode {
        ETR_MODE_FLAT,          /* Uses contiguous flat buffer */
        ETR_MODE_ETR_SG,        /* Uses in-built TMC ETR SG mechanism */
        ETR_MODE_CATU,          /* Use SG mechanism in CATU */
+       ETR_MODE_AUTO,          /* Use the default mechanism */
 };
 
 struct etr_buf_operations;
@@ -207,6 +208,7 @@ struct tmc_drvdata {
        enum tmc_mem_intf_width memwidth;
        u32                     trigger_cntr;
        u32                     etr_caps;
+       enum etr_mode           etr_mode;
        struct idr              idr;
        struct mutex            idr_mutex;
        struct etr_buf          *sysfs_buf;
@@ -334,5 +336,6 @@ void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
 void tmc_etr_remove_catu_ops(void);
 struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
                                   enum cs_mode mode, void *data);
+extern const struct attribute_group coresight_etr_group;
 
 #endif