From: Mike Bazov mike@perception-point.io
Introducing a new mode: CS_MODE_API. This mode shall be used by the etm-api framework to expose coresight functionality to kernel mode clients. This commit makes tmc-etr aware of it.
The set_buffer()/alloc_buffer()/free_buffer() sink operations are now turned to be mode-oriented. Up to this commit they were perf-specific, because only the perf framework used it. They are now extended to also be used with CS_MODE_API, in the hope of giving a kernel mode client the power of allocating and using a coresight buffer.
tmc-etr forbids using the sink for CS_MODE_API mode concurrently with other session modes(perf/sysfs). The implementation doesn't forbid using the sink multiple times if the requested mode is CS_MODE_API.
This commit also makes the perf framework aware of the sink operations function signature changes.
Signed-off-by: Mike Bazov mike@perception-point.io --- drivers/hwtracing/coresight/coresight-etb10.c | 13 ++- drivers/hwtracing/coresight/coresight-etm-perf.c | 7 +- drivers/hwtracing/coresight/coresight-priv.h | 1 + drivers/hwtracing/coresight/coresight-tmc-etf.c | 16 ++- drivers/hwtracing/coresight/coresight-tmc-etr.c | 124 +++++++++++++++++++---- drivers/hwtracing/coresight/coresight-tmc.h | 3 +- include/linux/coresight.h | 10 +- 7 files changed, 139 insertions(+), 35 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 08fa660098f8..4fbc8af7851a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -323,12 +323,15 @@ static void etb_disable(struct coresight_device *csdev) dev_dbg(drvdata->dev, "ETB disabled\n"); }
-static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, +static void *etb_alloc_buffer(struct coresight_device *csdev, u32 mode, int cpu, void **pages, int nr_pages, bool overwrite) { int node; struct cs_buffers *buf;
+ if (mode != CS_MODE_PERF) + return NULL; + if (cpu == -1) cpu = smp_processor_id(); node = cpu_to_node(cpu); @@ -344,11 +347,13 @@ static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, return buf; }
-static void etb_free_buffer(void *config) +static void etb_free_buffer(u32 mode, void *config) { - struct cs_buffers *buf = config; + if (mode != CS_MODE_API) { + struct cs_buffers *buf = config;
- kfree(buf); + kfree(buf); + } }
static int etb_set_buffer(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index abe8249b893b..563f4b47e977 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -119,7 +119,8 @@ static void free_event_data(struct work_struct *work) cpu = cpumask_first(mask); sink = coresight_get_sink(etm_event_cpu_path(event_data, cpu)); if (sink_ops(sink)->free_buffer) - sink_ops(sink)->free_buffer(event_data->snk_config); + sink_ops(sink)->free_buffer(CS_MODE_PERF, + event_data->snk_config); }
for_each_cpu(cpu, mask) { @@ -250,7 +251,8 @@ static void *etm_setup_aux(int event_cpu, void **pages,
/* Allocate the sink buffer for this session */ event_data->snk_config = - sink_ops(sink)->alloc_buffer(sink, cpu, pages, + sink_ops(sink)->alloc_buffer(sink, CS_MODE_PERF, + cpu, pages, nr_pages, overwrite); if (!event_data->snk_config) goto err; @@ -284,6 +286,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail;
path = etm_event_cpu_path(event_data, cpu); + /* We need a sink, no need to continue without one */ sink = coresight_get_sink(path); if (WARN_ON_ONCE(!sink)) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index c11da5564a67..174e6f1fab3e 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -72,6 +72,7 @@ enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, CS_MODE_PERF, + CS_MODE_API };
/** diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4156c95ce1bb..897dfc1bcbaa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -307,12 +307,16 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, dev_dbg(drvdata->dev, "TMC-ETF disabled\n"); }
-static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, - void **pages, int nr_pages, bool overwrite) +static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, u32 mode, + int cpu, void **pages, int nr_pages, + bool overwrite) { int node; struct cs_buffers *buf;
+ if (mode == CS_MODE_API) + return NULL; + if (cpu == -1) cpu = smp_processor_id(); node = cpu_to_node(cpu); @@ -329,11 +333,13 @@ static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, return buf; }
-static void tmc_free_etf_buffer(void *config) +static void tmc_free_etf_buffer(u32 mode, void *config) { - struct cs_buffers *buf = config; + if (mode != CS_MODE_API) { + struct cs_buffers *buf = config;
- kfree(buf); + kfree(buf); + } }
static int tmc_set_etf_buffer(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 56fea4ff947e..6ffcaaf76680 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1176,32 +1176,35 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, return etr_perf; }
- -static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, - int cpu, void **pages, int nr_pages, - bool snapshot) +static void *tmc_etr_alloc_api_buffer(struct tmc_drvdata *drvdata, int cpu, + int nr_pages, void **pages, bool snapshot) { - struct etr_perf_buffer *etr_perf; - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + ssize_t size = 0;
- if (cpu == -1) - cpu = smp_processor_id(); + size = nr_pages << PAGE_SHIFT;
- etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), - nr_pages, pages, snapshot); - if (IS_ERR(etr_perf)) { - dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); - return NULL; - } + return tmc_alloc_etr_buf(drvdata, size, 0, cpu_to_node(0), pages); +}
- etr_perf->snapshot = snapshot; - etr_perf->nr_pages = nr_pages; - etr_perf->pages = pages; +void *tmc_alloc_etr_buffer(struct coresight_device *csdev, u32 mode, + int cpu, void **pages, int nr_pages, bool snapshot) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- return etr_perf; + switch (mode) { + case CS_MODE_PERF: + return tmc_etr_setup_perf_buf(drvdata, cpu, nr_pages, + pages, snapshot); + case CS_MODE_API: + return tmc_etr_alloc_api_buffer(drvdata, cpu, nr_pages, + pages, snapshot); + default: + /* We shouldn't get here. */ + return ERR_PTR(-EINVAL); + } }
-static void tmc_free_etr_buffer(void *config) +static void tmc_etr_free_perf_buffer(void *config) { struct etr_perf_buffer *etr_perf = config;
@@ -1210,6 +1213,61 @@ static void tmc_free_etr_buffer(void *config) kfree(etr_perf); }
+static void tmc_etr_free_api_buffer(void *buffer) +{ + if (buffer) + tmc_free_etr_buf(buffer); +} + +static void tmc_free_etr_buffer(u32 mode, void *buffer) +{ + switch (mode) { + case CS_MODE_PERF: + tmc_etr_free_perf_buffer(buffer); + break; + case CS_MODE_API: + tmc_etr_free_api_buffer(buffer); + default: + break; + } +} + +static int tmc_etr_set_api_buffer(struct coresight_device *csdev, void *buffer) +{ + struct etr_buf *etr_buf = buffer; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* + * Disable the etr if it's running. The user must pause any playing + * sources if it doesn't want to lose data. + */ + if (drvdata->mode == CS_MODE_API && drvdata->api_buf != NULL) + tmc_etr_disable_hw(drvdata); + + drvdata->api_buf = etr_buf; + + if (drvdata->mode == CS_MODE_API && drvdata->api_buf != NULL) + tmc_etr_enable_hw(drvdata, drvdata->api_buf); + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} + +static int tmc_etr_set_buffer(struct coresight_device *csdev, u32 mode, + struct perf_output_handle *handle, void *buffer) +{ + switch (mode) { + case CS_MODE_API: + return tmc_etr_set_api_buffer(csdev, buffer); + default: + return -EINVAL; + } +} + /* * tmc_etr_sync_perf_buffer: Copy the actual trace data from the hardware * buffer to the perf ring buffer. @@ -1350,6 +1408,29 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) return rc; }
+static int tmc_enable_etr_sink_api(struct coresight_device *csdev) +{ + int rc = 0; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* + * If the sink is already being used by someone, we cannot interfere. + */ + if (drvdata->mode != CS_MODE_DISABLED && + drvdata->mode != CS_MODE_API) { + rc = -EBUSY; + goto out; + } + + drvdata->mode = CS_MODE_API; +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return rc; +} + static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -1358,13 +1439,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, return tmc_enable_etr_sink_sysfs(csdev); case CS_MODE_PERF: return tmc_enable_etr_sink_perf(csdev, data); + case CS_MODE_API: + return tmc_enable_etr_sink_api(csdev); }
/* We shouldn't be here */ return -EINVAL; }
-static void tmc_disable_etr_sink(struct coresight_device *csdev) +void tmc_disable_etr_sink(struct coresight_device *csdev) { unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -1391,6 +1474,7 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { .disable = tmc_disable_etr_sink, .alloc_buffer = tmc_alloc_etr_buffer, .update_buffer = tmc_update_etr_buffer, + .set_buffer = tmc_etr_set_buffer, .free_buffer = tmc_free_etr_buffer, };
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 487c53701e9c..39c6646c82d3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -172,6 +172,7 @@ struct etr_buf { * device configuration register (DEVID) * @perf_data: PERF buffer for ETR. * @sysfs_data: SYSFS buffer for ETR. + * @api_buf: API buffer for ETR. */ struct tmc_drvdata { void __iomem *base; @@ -193,6 +194,7 @@ struct tmc_drvdata { u32 etr_caps; struct etr_buf *sysfs_buf; void *perf_data; + struct etr_buf *api_buf; };
struct etr_buf_operations { @@ -257,7 +259,6 @@ extern const struct coresight_ops tmc_etr_cs_ops; ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len, char **bufpp);
- #define TMC_REG_PAIR(name, lo_off, hi_off) \ static inline u64 \ tmc_read_##name(struct tmc_drvdata *drvdata) \ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 53535821dc25..8f7f98570afd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -183,16 +183,20 @@ struct coresight_device { * Operations available for sinks * @enable: enables the sink. * @disable: disables the sink. - * @alloc_buffer: initialises perf's ring buffer for trace collection. + * @alloc_buffer: allocates the trace buffer. * @free_buffer: release memory allocated in @get_config. + * @set_buffer: set the trace buffer. * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode, void *data); void (*disable)(struct coresight_device *csdev); - void *(*alloc_buffer)(struct coresight_device *csdev, int cpu, + void *(*alloc_buffer)(struct coresight_device *csdev, u32 mode, int cpu, void **pages, int nr_pages, bool overwrite); - void (*free_buffer)(void *config); + void (*free_buffer)(u32 mode, void *buffer); + int (*set_buffer)(struct coresight_device *csdev, u32 mode, + struct perf_output_handle *handle, + void *buffer); unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config);
From: Mike Bazov mike@perception-point.io
Adding support for CS_MODE_API mode in the ETMv4 implementation. The enable()/disable() function signatures are changed to a generic form, to support other modes in addition to perf.
The CS_MODE_API etmv4 implementation receives a etmv4 configuration structure(introduced in this commit and defined in linux/coresight.h), configures the etmv4 on the __current__ cpu according to the struct, and enables it.
In addition, the stm, etm3x are also made aware of the CS_MODE_API, without supporting it.
Signed-off-by: Mike Bazov mike@perception-point.io --- drivers/hwtracing/coresight/coresight-etm4x.c | 66 +++++++++++++++++++++++++-- drivers/hwtracing/coresight/coresight-stm.c | 4 +- include/linux/coresight.h | 22 ++++++++- 3 files changed, 83 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c1dcc7c289a5..f102b16617d0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -31,6 +31,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-priv.h"
static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -39,6 +40,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); +static void etm4_set_default_filter(struct etmv4_config *config); static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, struct perf_event *event);
@@ -226,6 +228,42 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, return ret; }
+static void etm4_apply_config(struct coresight_device *csdev, + struct etm_config *etm_config) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etmv4_config *config = &drvdata->config; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etmv4_config)); + + if (!etm_config->kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (!etm_config->user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm4_set_default_config(config); + etm4_set_default_filter(config); + + if (etm_config->cycacc) { + config->cfg |= BIT(4); + /* TRM: Must program this for cycacc to work */ + config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT; + } + if (etm_config->timestamp) + /* bit[11], Global timestamp tracing bit */ + config->cfg |= BIT(11); + + /* return stack - enable if selected and supported */ + if (etm_config->retstack && drvdata->retstack) + /* bit[12], Return stack enable bit */ + config->cfg |= BIT(12); + + drvdata->trcid = etm_config->trace_id; +} + static int etm4_enable_perf(struct coresight_device *csdev, struct perf_event *event) { @@ -275,8 +313,23 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) return ret; }
+static int etm4_enable_api(struct coresight_device *csdev, + struct etm_config *config) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Apply the request config. */ + etm4_apply_config(csdev, config); + etm4_enable_hw(drvdata); + + return 0; +} + static int etm4_enable(struct coresight_device *csdev, - struct perf_event *event, u32 mode) + void *config, u32 mode) { int ret; u32 val; @@ -293,7 +346,10 @@ static int etm4_enable(struct coresight_device *csdev, ret = etm4_enable_sysfs(csdev); break; case CS_MODE_PERF: - ret = etm4_enable_perf(csdev, event); + ret = etm4_enable_perf(csdev, config); + break; + case CS_MODE_API: + ret = etm4_enable_api(csdev, config); break; default: ret = -EINVAL; @@ -384,7 +440,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) }
static void etm4_disable(struct coresight_device *csdev, - struct perf_event *event) + void *config) { u32 mode; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -400,10 +456,11 @@ static void etm4_disable(struct coresight_device *csdev, case CS_MODE_DISABLED: break; case CS_MODE_SYSFS: + case CS_MODE_API: /* Disabling via API mode is the same as sysfs mode */ etm4_disable_sysfs(csdev); break; case CS_MODE_PERF: - etm4_disable_perf(csdev, event); + etm4_disable_perf(csdev, config); break; }
@@ -826,7 +883,6 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, address = (type == ETM_ADDR_TYPE_START ? filter->start_addr : filter->stop_addr); - /* Configure comparator */ etm4_set_start_stop_filter(config, address, comparator, type); diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 35d6f9709274..7cb1d7525dde 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -191,7 +191,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) }
static int stm_enable(struct coresight_device *csdev, - struct perf_event *event, u32 mode) + void *config, u32 mode) { u32 val; struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -254,7 +254,7 @@ static void stm_disable_hw(struct stm_drvdata *drvdata) }
static void stm_disable(struct coresight_device *csdev, - struct perf_event *event) + void *config) { struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 8f7f98570afd..c29a3bb4c386 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -227,9 +227,9 @@ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev, - struct perf_event *event, u32 mode); + void *config, u32 mode); void (*disable)(struct coresight_device *csdev, - struct perf_event *event); + void *config); };
/** @@ -253,6 +253,24 @@ struct coresight_ops { const struct coresight_ops_helper *helper_ops; };
+/** + * struct etm_config - Generic etm configuration. + * @retstack: enable retstack. + * @user: trace user mode. + * @kernel: trace kernel mode. + * @timestamp: enable timestamp packets. + * @cycacc: enable acc + * @trace_id: trace id. + */ +struct etm_config { + bool retstack; /* enable retstack */ + bool user; /* trace user mode */ + bool kernel; /* trace kernel mode */ + bool timestamp; /* timestamp packets */ + bool cycacc; /* cyc packets */ + int trace_id; /* etm trace id */ +}; + #ifdef CONFIG_CORESIGHT extern struct coresight_device * coresight_register(struct coresight_desc *desc);
On Tue, Sep 04, 2018 at 01:45:46PM +0300, mike@perception-point.io wrote:
From: Mike Bazov mike@perception-point.io
Adding support for CS_MODE_API mode in the ETMv4 implementation. The enable()/disable() function signatures are changed to a generic form, to support other modes in addition to perf.
The CS_MODE_API etmv4 implementation receives a etmv4 configuration structure(introduced in this commit and defined in linux/coresight.h), configures the etmv4 on the __current__ cpu according to the struct, and enables it.
When writing a changelog, please concentrate on why you're doing something rather than what you're doing.
A changelog should concentrate on _why_ rather than _what_.
In addition, the stm, etm3x are also made aware of the CS_MODE_API, without supporting it.
Here etm3x is mentionned by nothing is done about it.
Signed-off-by: Mike Bazov mike@perception-point.io
drivers/hwtracing/coresight/coresight-etm4x.c | 66 +++++++++++++++++++++++++-- drivers/hwtracing/coresight/coresight-stm.c | 4 +- include/linux/coresight.h | 22 ++++++++- 3 files changed, 83 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c1dcc7c289a5..f102b16617d0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -31,6 +31,7 @@ #include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-priv.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -39,6 +40,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); +static void etm4_set_default_filter(struct etmv4_config *config); static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, struct perf_event *event); @@ -226,6 +228,42 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, return ret; } +static void etm4_apply_config(struct coresight_device *csdev,
struct etm_config *etm_config)
+{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- struct etmv4_config *config = &drvdata->config;
- /* Clear configuration from previous run */
- memset(config, 0, sizeof(struct etmv4_config));
- if (!etm_config->kernel)
config->mode = ETM_MODE_EXCL_KERN;
- if (!etm_config->user)
config->mode = ETM_MODE_EXCL_USER;
- /* Always start from the default config */
- etm4_set_default_config(config);
- etm4_set_default_filter(config);
- if (etm_config->cycacc) {
config->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work */
config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
- }
- if (etm_config->timestamp)
/* bit[11], Global timestamp tracing bit */
config->cfg |= BIT(11);
- /* return stack - enable if selected and supported */
- if (etm_config->retstack && drvdata->retstack)
/* bit[12], Return stack enable bit */
config->cfg |= BIT(12);
- drvdata->trcid = etm_config->trace_id;
+}
Please reuse the current etm4 configuration routine rather than spinning off another one. If the original one doesn't fit your need split it up in smaller chunks. That way we have only one configuration scheme to maintain.
static int etm4_enable_perf(struct coresight_device *csdev, struct perf_event *event) { @@ -275,8 +313,23 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) return ret; } +static int etm4_enable_api(struct coresight_device *csdev,
struct etm_config *config)
+{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL;
- /* Apply the request config. */
- etm4_apply_config(csdev, config);
- etm4_enable_hw(drvdata);
- return 0;
+}
static int etm4_enable(struct coresight_device *csdev,
struct perf_event *event, u32 mode)
void *config, u32 mode)
{ int ret; u32 val; @@ -293,7 +346,10 @@ static int etm4_enable(struct coresight_device *csdev, ret = etm4_enable_sysfs(csdev); break; case CS_MODE_PERF:
ret = etm4_enable_perf(csdev, event);
ret = etm4_enable_perf(csdev, config);
break;
- case CS_MODE_API:
break; default: ret = -EINVAL;ret = etm4_enable_api(csdev, config);
@@ -384,7 +440,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) } static void etm4_disable(struct coresight_device *csdev,
struct perf_event *event)
void *config)
{ u32 mode; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -400,10 +456,11 @@ static void etm4_disable(struct coresight_device *csdev, case CS_MODE_DISABLED: break; case CS_MODE_SYSFS:
- case CS_MODE_API: /* Disabling via API mode is the same as sysfs mode */ etm4_disable_sysfs(csdev); break; case CS_MODE_PERF:
etm4_disable_perf(csdev, event);
break; }etm4_disable_perf(csdev, config);
@@ -826,7 +883,6 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, address = (type == ETM_ADDR_TYPE_START ? filter->start_addr : filter->stop_addr);
Extra cosmetic repair
/* Configure comparator */ etm4_set_start_stop_filter(config, address, comparator, type);
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 35d6f9709274..7cb1d7525dde 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -191,7 +191,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) } static int stm_enable(struct coresight_device *csdev,
struct perf_event *event, u32 mode)
void *config, u32 mode)
{ u32 val; struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -254,7 +254,7 @@ static void stm_disable_hw(struct stm_drvdata *drvdata) } static void stm_disable(struct coresight_device *csdev,
struct perf_event *event)
void *config)
{ struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 8f7f98570afd..c29a3bb4c386 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -227,9 +227,9 @@ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev,
struct perf_event *event, u32 mode);
void (*disable)(struct coresight_device *csdev,void *config, u32 mode);
struct perf_event *event);
void *config);
}; /** @@ -253,6 +253,24 @@ struct coresight_ops { const struct coresight_ops_helper *helper_ops; }; +/**
- struct etm_config - Generic etm configuration.
- @retstack: enable retstack.
- @user: trace user mode.
- @kernel: trace kernel mode.
- @timestamp: enable timestamp packets.
- @cycacc: enable acc
- @trace_id: trace id.
- */
+struct etm_config {
- bool retstack; /* enable retstack */
- bool user; /* trace user mode */
- bool kernel; /* trace kernel mode */
- bool timestamp; /* timestamp packets */
- bool cycacc; /* cyc packets */
- int trace_id; /* etm trace id */
+};
This part is very problematic. First it is bound to change as more options are required by users. This is the issue we currently have for perf were we need to expand the configuration option to reflect what the HW can do. Ultimately I'd like to have the same structure for both the API and perf mode.
A word of warning here... Fixing this will be difficult and require a fair amount of discussion.
#ifdef CONFIG_CORESIGHT extern struct coresight_device * coresight_register(struct coresight_desc *desc); -- 2.16.2
From: Mike Bazov mike@perception-point.io
Introducing the get_trace() sink operation. This is merely exposing the tmc_etr_get_sysfs_trace() functionality as a sink operation, and also implementing for CS_MODE_API.
When using get_trace() with request CS_MODE_API, the trace is fetched from the __current__ etr buffer, which is assumed to be the CS_MODE_API buffer set by set_buffer(). The get_trace() can be further exposed to kernel clients to easily get the trace buffer and copy trace data from it.
In addition, the tmc-etf is also made aware of this sink op, without supporting CS_MODE_API.
Signed-off-by: Mike Bazov mike@perception-point.io --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 14 ++++ drivers/hwtracing/coresight/coresight-tmc-etr.c | 100 +++++++++++++++++++++--- include/linux/coresight.h | 3 + 3 files changed, 108 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 897dfc1bcbaa..656a786de808 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -120,6 +120,19 @@ ssize_t tmc_etb_get_sysfs_trace(struct tmc_drvdata *drvdata, return actual; }
+ssize_t tmc_etf_get_trace(struct coresight_device *csdev, u32 mode, + loff_t pos, size_t len, char **bufpp) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + switch (mode) { + case CS_MODE_SYSFS: + return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp); + default: + return -EINVAL; + } +} + static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) { int ret = 0; @@ -490,6 +503,7 @@ static const struct coresight_ops_sink tmc_etf_sink_ops = { .alloc_buffer = tmc_alloc_etf_buffer, .free_buffer = tmc_free_etf_buffer, .update_buffer = tmc_update_etf_buffer, + .get_trace = tmc_etf_get_trace, };
static const struct coresight_ops_link tmc_etf_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 6ffcaaf76680..f60ac8942c1f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -37,6 +37,8 @@ struct etr_perf_buffer { void **pages; };
+static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); + /* Convert the perf index to an offset within the ETR buffer */ #define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT))
@@ -907,6 +909,7 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) rrp = tmc_read_rrp(drvdata); rwp = tmc_read_rwp(drvdata); status = readl_relaxed(drvdata->base + TMC_STS); + etr_buf->full = status & TMC_STS_FULL;
WARN_ON(!etr_buf->ops || !etr_buf->ops->sync); @@ -988,29 +991,91 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, * also updating the @bufpp on where to find it. Since the trace data * starts at anywhere in the buffer, depending on the RRP, we adjust the * @len returned to handle buffer wrapping around. - * - * We are protected here by drvdata->reading != 0, which ensures the - * sysfs_buf stays alive. */ -ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, - loff_t pos, size_t len, char **bufpp) +static ssize_t etr_buf_get_trace(struct etr_buf *etr_buf, + loff_t pos, size_t len, char **bufpp) { s64 offset; ssize_t actual = len; - struct etr_buf *etr_buf = drvdata->sysfs_buf; + + if (!etr_buf) + return -EINVAL;
if (pos + actual > etr_buf->len) actual = etr_buf->len - pos; if (actual <= 0) - return actual; + return 0;
/* Compute the offset from which we read the data */ offset = etr_buf->offset + pos; if (offset >= etr_buf->size) offset -= etr_buf->size; + return tmc_etr_buf_get_data(etr_buf, offset, actual, bufpp); }
+/* We are protected here by drvdata->reading != 0, which ensures + * the sysfs_buf stays alive. + */ +ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, + loff_t pos, size_t len, char **bufpp) +{ + return etr_buf_get_trace(drvdata->sysfs_buf, pos, len, bufpp); +} + +/* It is the user's responsbility to keep the coresight session buffer + * valid. + */ +static ssize_t tmc_etr_get_api_trace(struct tmc_drvdata *drvdata, + loff_t pos, size_t len, char **bufpp) +{ + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + if (drvdata->mode != CS_MODE_API) { + ret = -EINVAL; + goto out; + } + + if (drvdata->api_buf == NULL) { + ret = -ENOENT; + goto out; + } + + /* Make sure to stop and sync the buffer if we're getting a trace + * from a running session. + */ + tmc_etr_disable_hw(drvdata); + + ret = etr_buf_get_trace(drvdata->api_buf, pos, len, bufpp); + + /* Re-enable the trace session. Again, it is entirely up the user to + * read the actual trace data before reusing the existing buffer, or, + * perhaps, use a different buffer. + */ + tmc_etr_enable_hw(drvdata, drvdata->api_buf); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +ssize_t tmc_etr_get_trace(struct coresight_device *csdev, u32 mode, + loff_t poss, size_t len, char **buffpp) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + switch (mode) { + case CS_MODE_SYSFS: + return tmc_etr_get_sysfs_trace(drvdata, poss, len, buffpp); + case CS_MODE_API: + return tmc_etr_get_api_trace(drvdata, poss, len, buffpp); + default: + return -EINVAL; + } +} + static struct etr_buf * tmc_etr_setup_sysfs_buf(struct tmc_drvdata *drvdata) { @@ -1037,6 +1102,17 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) } }
+static void tmc_etr_sync_api_buf(struct tmc_drvdata *drvdata) +{ + struct etr_buf *etr_buf = drvdata->etr_buf; + + /* We aren't certain there is indeed an etr buffer here, the user + * might set it to NULL, he is controling the buffer. + */ + if (etr_buf && !WARN_ON(drvdata->api_buf != etr_buf)) + tmc_sync_etr_buf(drvdata); +} + static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -1048,9 +1124,10 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) */ if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_sync_sysfs_buf(drvdata); + else if (drvdata->mode == CS_MODE_API) + tmc_etr_sync_api_buf(drvdata);
tmc_disable_hw(drvdata); - CS_LOCK(drvdata->base);
/* Disable CATU device if this ETR is connected to one */ @@ -1460,7 +1537,11 @@ void tmc_disable_etr_sink(struct coresight_device *csdev)
/* Disable the TMC only if it needs to */ if (drvdata->mode != CS_MODE_DISABLED) { - tmc_etr_disable_hw(drvdata); + /* Only disable the hardware if the mode isn't API, + * or, if the mode is API, only if we have a valid buffer. + */ + if (drvdata->mode != CS_MODE_API || drvdata->api_buf != NULL) + tmc_etr_disable_hw(drvdata); drvdata->mode = CS_MODE_DISABLED; }
@@ -1476,6 +1557,7 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { .update_buffer = tmc_update_etr_buffer, .set_buffer = tmc_etr_set_buffer, .free_buffer = tmc_free_etr_buffer, + .get_trace = tmc_etr_get_trace };
const struct coresight_ops tmc_etr_cs_ops = { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index c29a3bb4c386..0a57a2ce8d4d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -187,6 +187,7 @@ struct coresight_device { * @free_buffer: release memory allocated in @get_config. * @set_buffer: set the trace buffer. * @update_buffer: update buffer pointers after a trace session. + * @get_trace: get a pointer to the trace data. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode, void *data); @@ -200,6 +201,8 @@ struct coresight_ops_sink { unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config); + ssize_t (*get_trace)(struct coresight_device *csdev, u32 mode, + loff_t poss, size_t len, char **buf); };
/**
On Tue, Sep 04, 2018 at 01:45:47PM +0300, mike@perception-point.io wrote:
From: Mike Bazov mike@perception-point.io
Introducing the get_trace() sink operation. This is merely exposing the tmc_etr_get_sysfs_trace() functionality as a sink operation, and also implementing for CS_MODE_API.
When using get_trace() with request CS_MODE_API, the trace is fetched from the __current__ etr buffer, which is assumed to be the CS_MODE_API buffer set by set_buffer(). The get_trace() can be further exposed to kernel clients to easily get the trace buffer and copy trace data from it.
In addition, the tmc-etf is also made aware of this sink op, without supporting CS_MODE_API.
Signed-off-by: Mike Bazov mike@perception-point.io
drivers/hwtracing/coresight/coresight-tmc-etf.c | 14 ++++ drivers/hwtracing/coresight/coresight-tmc-etr.c | 100 +++++++++++++++++++++--- include/linux/coresight.h | 3 + 3 files changed, 108 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 897dfc1bcbaa..656a786de808 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -120,6 +120,19 @@ ssize_t tmc_etb_get_sysfs_trace(struct tmc_drvdata *drvdata, return actual; } +ssize_t tmc_etf_get_trace(struct coresight_device *csdev, u32 mode,
loff_t pos, size_t len, char **bufpp)
+{
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- switch (mode) {
- case CS_MODE_SYSFS:
return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp);
- default:
return -EINVAL;
- }
+}
I'm wondering who will be using bufpp and how it will be used. This patchset gives us a better understanding of what you need but nothing about why you need it.
static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) { int ret = 0; @@ -490,6 +503,7 @@ static const struct coresight_ops_sink tmc_etf_sink_ops = { .alloc_buffer = tmc_alloc_etf_buffer, .free_buffer = tmc_free_etf_buffer, .update_buffer = tmc_update_etf_buffer,
- .get_trace = tmc_etf_get_trace,
}; static const struct coresight_ops_link tmc_etf_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 6ffcaaf76680..f60ac8942c1f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -37,6 +37,8 @@ struct etr_perf_buffer { void **pages; }; +static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
/* Convert the perf index to an offset within the ETR buffer */ #define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) @@ -907,6 +909,7 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) rrp = tmc_read_rrp(drvdata); rwp = tmc_read_rwp(drvdata); status = readl_relaxed(drvdata->base + TMC_STS);
Extra line
etr_buf->full = status & TMC_STS_FULL; WARN_ON(!etr_buf->ops || !etr_buf->ops->sync); @@ -988,29 +991,91 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
- also updating the @bufpp on where to find it. Since the trace data
- starts at anywhere in the buffer, depending on the RRP, we adjust the
- @len returned to handle buffer wrapping around.
- We are protected here by drvdata->reading != 0, which ensures the
*/
- sysfs_buf stays alive.
-ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
+static ssize_t etr_buf_get_trace(struct etr_buf *etr_buf,
loff_t pos, size_t len, char **bufpp)
{ s64 offset; ssize_t actual = len;
- struct etr_buf *etr_buf = drvdata->sysfs_buf;
- if (!etr_buf)
return -EINVAL;
if (pos + actual > etr_buf->len) actual = etr_buf->len - pos; if (actual <= 0)
return actual;
return 0;
I'll have to think further about this one.
/* Compute the offset from which we read the data */ offset = etr_buf->offset + pos; if (offset >= etr_buf->size) offset -= etr_buf->size;
Extra line
return tmc_etr_buf_get_data(etr_buf, offset, actual, bufpp); } +/* We are protected here by drvdata->reading != 0, which ensures
- the sysfs_buf stays alive.
- */
/* * We are protected here by drvdata->reading != 0, which ensures * the sysfs_buf stays alive. */
+ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
+{
- return etr_buf_get_trace(drvdata->sysfs_buf, pos, len, bufpp);
+}
+/* It is the user's responsbility to keep the coresight session buffer
- valid.
- */
Same as above
+static ssize_t tmc_etr_get_api_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
+{
- ssize_t ret = 0;
- unsigned long flags;
- spin_lock_irqsave(&drvdata->spinlock, flags);
- if (drvdata->mode != CS_MODE_API) {
ret = -EINVAL;
goto out;
- }
- if (drvdata->api_buf == NULL) {
ret = -ENOENT;
goto out;
- }
- /* Make sure to stop and sync the buffer if we're getting a trace
* from a running session.
*/
- tmc_etr_disable_hw(drvdata);
- ret = etr_buf_get_trace(drvdata->api_buf, pos, len, bufpp);
- /* Re-enable the trace session. Again, it is entirely up the user to
* read the actual trace data before reusing the existing buffer, or,
* perhaps, use a different buffer.
*/
- tmc_etr_enable_hw(drvdata, drvdata->api_buf);
+out:
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return ret;
+}
+ssize_t tmc_etr_get_trace(struct coresight_device *csdev, u32 mode,
loff_t poss, size_t len, char **buffpp)
+{
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- switch (mode) {
- case CS_MODE_SYSFS:
return tmc_etr_get_sysfs_trace(drvdata, poss, len, buffpp);
- case CS_MODE_API:
return tmc_etr_get_api_trace(drvdata, poss, len, buffpp);
- default:
return -EINVAL;
- }
+}
static struct etr_buf * tmc_etr_setup_sysfs_buf(struct tmc_drvdata *drvdata) { @@ -1037,6 +1102,17 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) } } +static void tmc_etr_sync_api_buf(struct tmc_drvdata *drvdata) +{
- struct etr_buf *etr_buf = drvdata->etr_buf;
- /* We aren't certain there is indeed an etr buffer here, the user
* might set it to NULL, he is controling the buffer.
*/
- if (etr_buf && !WARN_ON(drvdata->api_buf != etr_buf))
tmc_sync_etr_buf(drvdata);
+}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -1048,9 +1124,10 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) */ if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_sync_sysfs_buf(drvdata);
- else if (drvdata->mode == CS_MODE_API)
tmc_etr_sync_api_buf(drvdata);
tmc_disable_hw(drvdata);
- CS_LOCK(drvdata->base);
/* Disable CATU device if this ETR is connected to one */ @@ -1460,7 +1537,11 @@ void tmc_disable_etr_sink(struct coresight_device *csdev) /* Disable the TMC only if it needs to */ if (drvdata->mode != CS_MODE_DISABLED) {
tmc_etr_disable_hw(drvdata);
/* Only disable the hardware if the mode isn't API,
* or, if the mode is API, only if we have a valid buffer.
*/
if (drvdata->mode != CS_MODE_API || drvdata->api_buf != NULL)
tmc_etr_disable_hw(drvdata);
In order to avoid this kind of complexity please split the function in accordance to the mode of operation. I will be releasing a patchset later today or early next week that adds a mode of operation to sinks' disable function. I will CC you on the patchset.
drvdata->mode = CS_MODE_DISABLED;
} @@ -1476,6 +1557,7 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { .update_buffer = tmc_update_etr_buffer, .set_buffer = tmc_etr_set_buffer, .free_buffer = tmc_free_etr_buffer,
- .get_trace = tmc_etr_get_trace
}; const struct coresight_ops tmc_etr_cs_ops = { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index c29a3bb4c386..0a57a2ce8d4d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -187,6 +187,7 @@ struct coresight_device {
- @free_buffer: release memory allocated in @get_config.
- @set_buffer: set the trace buffer.
- @update_buffer: update buffer pointers after a trace session.
*/
- @get_trace: get a pointer to the trace data.
struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode, void *data); @@ -200,6 +201,8 @@ struct coresight_ops_sink { unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config);
- ssize_t (*get_trace)(struct coresight_device *csdev, u32 mode,
loff_t poss, size_t len, char **buf);
Tabulation problem
}; /** -- 2.16.2
From: Mike Bazov mike@perception-point.io
This patch introduces a new kernel-mode ETM tracers API for the use and control of kernel clients.
Kernel clients are interacting with coresight via 'coresight session'. To create a coresight session, first, you must call coresight_etm_create_session(). A session descriptor is returned. Before going futher and actually using the session to play trace data, a coresight buffer __must__ be allocated to be used by the session.
A coresight buffer is an opaque structure that is maintined internally by the implementation. The user can only control the size of the buffer, but can allocate as many buffers as it want using coresight_etm_alloc_buffer().
To make a session use this buffer, you can set it via coresight_etm_set_buffer(). Finally, to start/stop playing trace data, call coresight_etm_play()/coresight_etm_pause(). This will cause trace data to be generated on the __current__ CPU. This can be called from multiple CPUs concurrently, each call will cause the corrct ETM tracer to play/stop trace data.
Finally, trace data can be harvested by coresight_get_trace(), which returns a pointer that the trace data can be copied from.
To destroy a coresight session, call coresight_destroy_session(). You must free the coresight buffers allocated before destroying a session.
Signed-off-by: Mike Bazov mike@perception-point.io --- drivers/hwtracing/coresight/Makefile | 2 +- drivers/hwtracing/coresight/coresight-etm-api.c | 284 ++++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 2 + drivers/hwtracing/coresight/coresight-priv.h | 1 + include/linux/coresight.h | 87 ++++++++ 5 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm-api.c
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..8f0af5e7cabb 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -2,7 +2,7 @@ # # Makefile for CoreSight drivers. # -obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o +obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o coresight-etm-api.o obj-$(CONFIG_OF) += of_coresight.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ diff --git a/drivers/hwtracing/coresight/coresight-etm-api.c b/drivers/hwtracing/coresight/coresight-etm-api.c new file mode 100644 index 000000000000..a2d877077a20 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-api.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ETM kernel interface driver + * + * Author: Mike Bazov mike@perception-point.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <linux/atomic.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/slab.h> + +#include "coresight-priv.h" + +/** + * struct coresight_session - Coresight struct describing an API session. + * @path: An array of path, each slot for each CPU. + * @ncpus: Number of cpus used in the array of paths. + */ +struct coresight_session { + struct list_head __percpu **path; + size_t ncpus; +}; + +static DEFINE_PER_CPU(struct coresight_device *, csdev_src); + +void coresight_etm_register(struct coresight_device *csdev) +{ + int cpu = source_ops(csdev)->cpu_id(csdev); + + per_cpu(csdev_src, cpu) = csdev; +} + +void *coresight_etm_alloc_buffer(struct coresight_session *session, + size_t buffer_size) +{ + size_t nr_pages = 0; + struct coresight_device *sink = NULL; + + if (!session) + return ERR_PTR(-EINVAL); + + sink = coresight_get_sink(session->path[0]); + if (!sink) + return ERR_PTR(-ENOENT); + + if (!sink_ops(sink)->alloc_buffer) + return ERR_PTR(-EINVAL); + + nr_pages = PAGE_ALIGN(buffer_size) >> PAGE_SHIFT; + + return sink_ops(sink)->alloc_buffer(sink, CS_MODE_API, 0, + NULL, nr_pages, false); +} +EXPORT_SYMBOL_GPL(coresight_etm_alloc_buffer); + +void coresight_etm_free_buffer(struct coresight_session *session, + void *buffer) +{ + struct coresight_session *cs_session = session; + struct coresight_device *sink = NULL; + + if (!session || !buffer) + return; + + sink = coresight_get_sink(cs_session->path[0]); + if (!sink) + return; + + if (!sink_ops(sink)->free_buffer) + return; + + sink_ops(sink)->free_buffer(CS_MODE_API, buffer); +} +EXPORT_SYMBOL_GPL(coresight_etm_free_buffer); + +int coresight_etm_set_buffer(struct coresight_session *session, + void *buffer) +{ + struct coresight_device *sink = NULL; + + sink = coresight_get_sink(session->path[0]); + if (!sink) + return -ENOENT; + + return sink_ops(sink)->set_buffer(sink, CS_MODE_API, NULL, buffer); +} +EXPORT_SYMBOL_GPL(coresight_etm_set_buffer); + +struct coresight_session *coresight_etm_create_session(void) +{ + int ret = 0, cpu = 0; + void *err = NULL; + struct coresight_session *session = NULL; + struct coresight_device *sink = NULL; + struct list_head **path = NULL; + size_t ncpus = 0; + + /* Currently, the only way of activating a sink is via sysfs, since + * there is no comfortable way of exporting the sink/link types + * to the user, as the devtypes are extremely generic, where the user + * might want to be more specific. Also, don't deactivate the sink, + * the user will disable it as when wishes via sysfs. + */ + sink = coresight_get_enabled_sink(false); + if (!sink) + return ERR_PTR(-ENODEV); + + /* Every coresight path uses the same sink. This implies a + * coresight session uses a single sink for now. + * Mark the session as the sink owner. Prevent other concurrent API + * sessions from using that sink. + */ + if (atomic_cmpxchg(&sink->used, 0, 1)) + return ERR_PTR(-EBUSY); + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) { + err = ERR_PTR(-ENOMEM); + goto failed; + } + + get_online_cpus(); + + ncpus = num_online_cpus(); + + /* Build a path for each CPU */ + path = kcalloc(ncpus, sizeof(struct list_head *), GFP_KERNEL); + if (!path) { + err = ERR_PTR(-ENOMEM); + goto failed_put_cpus; + } + + for (cpu = 0; cpu < ncpus; ++cpu) { + struct coresight_device *source = NULL; + + source = per_cpu(csdev_src, cpu); + if (!source) { + err = ERR_PTR(-ENODEV); + goto failed_put_cpus; + } + + path[cpu] = coresight_build_path(source, sink); + if (IS_ERR(path[cpu])) { + err = path[cpu]; + path[cpu] = NULL; + goto failed_put_cpus; + } + + ret = coresight_enable_path(path[cpu], CS_MODE_API, NULL); + if (ret) { + coresight_release_path(path[cpu]); + path[cpu] = NULL; + err = ERR_PTR(ret); + goto failed_put_cpus; + } + } + + put_online_cpus(); + + session->ncpus = ncpus; + session->path = path; + + return session; + +failed_put_cpus: + put_online_cpus(); +failed: + if (path) { + for (cpu--; cpu >= 0; cpu--) { + if (path[cpu]) { + coresight_disable_path(path[cpu]); + coresight_release_path(path[cpu]); + } + } + + kfree(path); + } + + kfree(session); + + /* Allow other API sessions to use the sink. */ + atomic_set(&sink->used, 0); + + return err; +} +EXPORT_SYMBOL_GPL(coresight_etm_create_session); + +void coresight_etm_destroy_session(struct coresight_session *session) +{ + int cpu; + struct coresight_device *sink = NULL; + + if (!session) + return; + + sink = coresight_get_sink(session->path[0]); + if (sink) + /* Make sure the trace buffer is NULL. Disabling the sink + * possibly also means syncing the buffer, we don't know + * whether the user freed the used buffer or not, it is his + * responsibilty to sync the buffer when he needs to read trace + * data by calling coresight_etm_get_trace(). + */ + sink_ops(sink)->set_buffer(sink, CS_MODE_API, NULL, NULL); + + for (cpu = 0; cpu < session->ncpus; ++cpu) { + struct coresight_device *source = NULL; + + source = per_cpu(csdev_src, cpu); + if (source) + source_ops(source)->disable(source, NULL); + + coresight_disable_path(session->path[cpu]); + coresight_release_path(session->path[cpu]); + } + + /* Allow other API sessions to use this sink. */ + if (sink) + atomic_set(&sink->used, 0); + + kfree(session->path); + kfree(session); +} +EXPORT_SYMBOL_GPL(coresight_etm_destroy_session); + +int coresight_etm_play(struct coresight_session *session, + struct etm_config *config) +{ + struct coresight_device *source = NULL; + + if (!session) + return -EINVAL; + + source = per_cpu(csdev_src, smp_processor_id()); + if (!source) + return -ENXIO; + + return source_ops(source)->enable(source, config, CS_MODE_API); +} +EXPORT_SYMBOL_GPL(coresight_etm_play); + +void coresight_etm_pause(struct coresight_session *session) +{ + struct coresight_device *source = NULL; + + if (!session) + return; + + source = per_cpu(csdev_src, smp_processor_id()); + if (!source) + return; + + source_ops(source)->disable(source, NULL); +} +EXPORT_SYMBOL_GPL(coresight_etm_pause); + +ssize_t coresight_etm_get_trace(struct coresight_session *session, + loff_t poss, size_t len, char **buf) +{ + struct coresight_device *sink = NULL; + + if (!session) + return -EINVAL; + + sink = coresight_get_sink(session->path[0]); + if (!sink) + return -ENXIO; + + return sink_ops(sink)->get_trace(sink, CS_MODE_API, poss, len, buf); +} +EXPORT_SYMBOL_GPL(coresight_etm_get_trace); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index f102b16617d0..4237aab60b96 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1082,6 +1082,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
+ coresight_etm_register(drvdata->csdev); + pm_runtime_put(&adev->dev); dev_info(dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 174e6f1fab3e..803c7242864b 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -144,6 +144,7 @@ struct coresight_device *coresight_get_enabled_sink(bool reset); struct list_head *coresight_build_path(struct coresight_device *csdev, struct coresight_device *sink); void coresight_release_path(struct list_head *path); +void coresight_etm_register(struct coresight_device *csdev);
#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X extern int etm_readl_cp14(u32 off, unsigned int *val); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 0a57a2ce8d4d..09b992381645 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -156,6 +156,7 @@ struct coresight_connection { * @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it. + * @used: used only in a _sink_, 1 if the sink is used by an API session. */ struct coresight_device { struct coresight_connection *conns; @@ -169,6 +170,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */ + atomic_t used; /* 1 if used by a coresight api trace session */ };
#define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -256,6 +258,8 @@ struct coresight_ops { const struct coresight_ops_helper *helper_ops; };
+struct coresight_session; + /** * struct etm_config - Generic etm configuration. * @retstack: enable retstack. @@ -282,6 +286,89 @@ extern int coresight_enable(struct coresight_device *csdev); extern void coresight_disable(struct coresight_device *csdev); extern int coresight_timeout(void __iomem *addr, u32 offset, int position, int value); + +/** + * coresight_etm_alloc_buffer - allocate a buffer with fixed size for the + * provided session. + * + * @session: the coresight session + * @buffer_size: the size of the coresight buffer + * + * Returns a coresight buffer object or a negative errno value in case + * of a failure. + */ +extern void *coresight_etm_alloc_buffer( + struct coresight_session *session, + size_t buffer_size); +/** + * coresight_etm_free_buffer: free a coresight buffer + * + * @session: the coresight session. + * @buffer: a coresight buffer previously allocated by + * coresight_etm_alloc_buffer() + */ +extern void coresight_etm_free_buffer(struct coresight_session *session, + void *buffer); + +/** + * coresight_etm_set_buffer - Set a coresight trace buffer. + * + * @session: the coresight session. + * @buffer: a coresight buffer previosuly allocated by + * coresight_etm_alloc_buffer() + * + * Returns 0 on success, <0 on failure. + */ +extern int coresight_etm_set_buffer(struct coresight_session *session, + void *buffer); + +/** + * coresight_etm_create_session - create a coresight session. + * + * Returns a coresight session object or a negative errno value in case of + * a failure. + */ +extern struct coresight_session *coresight_etm_create_session(void); + +/** + * coresight_etm_destroy_session: destroy a coresight session. + * + * @session: the coresight session. + */ +extern void coresight_etm_destroy_session(struct coresight_session *session); + +/** + * coresight_etm_enable: make the source start playing trace data. the source + * is configured according to the config argument that is provided. + * + * @session: the coresight session. + * @config: the etm configuration + * + * Returns 0 on success, <0 on failure. + */ +extern int coresight_etm_play(struct coresight_session *session, + struct etm_config *config); + +/** + * coresight_etm_disable: pause the source from playing trace data. + * + * @session: the coresight session. + */ +extern void coresight_etm_pause(struct coresight_session *session); + +/** + * coresight_etm_get_trace: get the coresight trace from the current buffer. + * + * @session: the coresight session. + * @poss: position to get the trace from. + * @len: maximum length of trace data. + * @buff: a pointer to a pointer that will receive the trace data. + * + * returns the actual length of trace data, or a negative errno value + * in case of a failure. + */ +extern ssize_t coresight_etm_get_trace(struct coresight_session *session, + loff_t poss, size_t len, char **buf); #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; }
On Tue, 4 Sep 2018 13:45:48 +0300 mike@perception-point.io wrote:
+++ b/drivers/hwtracing/coresight/coresight-etm-api.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- ETM kernel interface driver
- Author: Mike Bazov mike@perception-point.io
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
The SPDX license identifier above makes this full license text redundant.
+int coresight_etm_play(struct coresight_session *session,
struct etm_config *config)
I thought 'record' would be more appropriate terminology here, to match the perf 'record' command. Perf doesn't have a 'play' command, or, rather, if it did, it would (re-)'play' the previously recorded data like 'perf report' and 'perf script' do.
+{
- struct coresight_device *source = NULL;
- if (!session)
return -EINVAL;
If session isn't being used, can we avoid passing it in in the first place?
- source = per_cpu(csdev_src, smp_processor_id());
- if (!source)
return -ENXIO;
Sure would be nice to be more verbose here, with a dev_err, for example.
- return source_ops(source)->enable(source, config, CS_MODE_API);
+}
Hmm, if this function results in a source enable callback, should it not be called play, nor record, rather something-enable?
+EXPORT_SYMBOL_GPL(coresight_etm_play);
+void coresight_etm_pause(struct coresight_session *session) +{
- struct coresight_device *source = NULL;
- if (!session)
return;
- source = per_cpu(csdev_src, smp_processor_id());
- if (!source)
return;
- source_ops(source)->disable(source, NULL);
+} +EXPORT_SYMBOL_GPL(coresight_etm_pause);
+ssize_t coresight_etm_get_trace(struct coresight_session *session,
loff_t poss, size_t len, char **buf)
+{
- struct coresight_device *sink = NULL;
- if (!session)
return -EINVAL;
- sink = coresight_get_sink(session->path[0]);
- if (!sink)
return -ENXIO;
- return sink_ops(sink)->get_trace(sink, CS_MODE_API, poss, len, buf);
+}
Same comments apply to the last two functions.
+++ b/include/linux/coresight.h @@ -156,6 +156,7 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
*/
- @used: used only in a _sink_, 1 if the sink is used by an API session.
struct coresight_device { struct coresight_connection *conns; @@ -169,6 +170,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
- atomic_t used; /* 1 if used by a coresight api trace session */
};
I'm not too familiar with these flags, but it seems to be their data types should match, if something, e.g., 'activated', can't already be used to imply the strict "API"-specific definition 'used' seems to be making here?
+/**
- coresight_etm_destroy_session: destroy a coresight session.
- @session: the coresight session.
- */
+extern void coresight_etm_destroy_session(struct coresight_session *session);
+/**
- coresight_etm_enable: make the source start playing trace data. the source
^^^^^^^^^^^^^^^^^^^^
- is configured according to the config argument that is provided.
- @session: the coresight session.
- @config: the etm configuration
- Returns 0 on success, <0 on failure.
- */
+extern int coresight_etm_play(struct coresight_session *session,
^^^^^^^^^^^^^^^^^^
struct etm_config *config);
The comment function name doesn't match the name in the function declaration, and in fact reinforces the naming question I had above: these functions really enable and disable, so let's not reinvent their names to some foreign play and pause terminology.
+/**
- coresight_etm_disable: pause the source from playing trace data.
- @session: the coresight session.
- */
+extern void coresight_etm_pause(struct coresight_session *session);
same here: pause is (was?) disable.
This was just a couple of things that caught my eye, and I thought I'd bring up, it's by no means a complete review of the patchseries.
Kim
On Tue, Sep 04, 2018 at 01:45:48PM +0300, mike@perception-point.io wrote:
From: Mike Bazov mike@perception-point.io
This patch introduces a new kernel-mode ETM tracers API for the use and control of kernel clients.
Kernel clients are interacting with coresight via 'coresight session'. To create a coresight session, first, you must call coresight_etm_create_session(). A session descriptor is returned. Before going futher and actually using the session to play trace data, a coresight buffer __must__ be allocated to be used by the session.
A coresight buffer is an opaque structure that is maintined internally
s/maintined/maintained
by the implementation. The user can only control the size of the buffer, but can allocate as many buffers as it want using coresight_etm_alloc_buffer().
To make a session use this buffer, you can set it via coresight_etm_set_buffer(). Finally, to start/stop playing trace data, call coresight_etm_play()/coresight_etm_pause(). This will cause trace data to be generated on the __current__ CPU. This can be called from multiple CPUs concurrently, each call will cause the corrct ETM
/s/corrct/correct
tracer to play/stop trace data.
This sounds a whole lot like what perf is doing.
Finally, trace data can be harvested by coresight_get_trace(), which returns a pointer that the trace data can be copied from.
To destroy a coresight session, call coresight_destroy_session(). You must free the coresight buffers allocated before destroying a session.
As I commented on a previous patch, this changelog is about what is done rather than why it is done.
This file tells me a little more about what you're trying to do - now I undestand why you wanted to export some of the functions in coresight.c. To me the code herein looks more like something you would do in your driver and should probably belong there rather than in the coresight subsystem. It's like a DMA driver using the DMA API.
On the flip side I am well aware that we don't have a coresight API (simply becasue we never needed one). As such I think there would be more value in working toward that goal rather than packaging existing coresight functions to reflect the needs of a specific project.
Thanks, Mathieu
Signed-off-by: Mike Bazov mike@perception-point.io
drivers/hwtracing/coresight/Makefile | 2 +- drivers/hwtracing/coresight/coresight-etm-api.c | 284 ++++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 2 + drivers/hwtracing/coresight/coresight-priv.h | 1 + include/linux/coresight.h | 87 ++++++++ 5 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm-api.c
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..8f0af5e7cabb 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -2,7 +2,7 @@ # # Makefile for CoreSight drivers. # -obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o +obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o coresight-etm-api.o obj-$(CONFIG_OF) += of_coresight.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ diff --git a/drivers/hwtracing/coresight/coresight-etm-api.c b/drivers/hwtracing/coresight/coresight-etm-api.c new file mode 100644 index 000000000000..a2d877077a20 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-api.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- ETM kernel interface driver
- Author: Mike Bazov mike@perception-point.io
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include <linux/atomic.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/slab.h>
+#include "coresight-priv.h"
+/**
- struct coresight_session - Coresight struct describing an API session.
- @path: An array of path, each slot for each CPU.
- @ncpus: Number of cpus used in the array of paths.
- */
+struct coresight_session {
- struct list_head __percpu **path;
- size_t ncpus;
+};
+static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
+void coresight_etm_register(struct coresight_device *csdev) +{
- int cpu = source_ops(csdev)->cpu_id(csdev);
- per_cpu(csdev_src, cpu) = csdev;
+}
+void *coresight_etm_alloc_buffer(struct coresight_session *session,
size_t buffer_size)
+{
- size_t nr_pages = 0;
- struct coresight_device *sink = NULL;
- if (!session)
return ERR_PTR(-EINVAL);
- sink = coresight_get_sink(session->path[0]);
- if (!sink)
return ERR_PTR(-ENOENT);
- if (!sink_ops(sink)->alloc_buffer)
return ERR_PTR(-EINVAL);
- nr_pages = PAGE_ALIGN(buffer_size) >> PAGE_SHIFT;
- return sink_ops(sink)->alloc_buffer(sink, CS_MODE_API, 0,
NULL, nr_pages, false);
+} +EXPORT_SYMBOL_GPL(coresight_etm_alloc_buffer);
+void coresight_etm_free_buffer(struct coresight_session *session,
void *buffer)
+{
- struct coresight_session *cs_session = session;
- struct coresight_device *sink = NULL;
- if (!session || !buffer)
return;
- sink = coresight_get_sink(cs_session->path[0]);
- if (!sink)
return;
- if (!sink_ops(sink)->free_buffer)
return;
- sink_ops(sink)->free_buffer(CS_MODE_API, buffer);
+} +EXPORT_SYMBOL_GPL(coresight_etm_free_buffer);
+int coresight_etm_set_buffer(struct coresight_session *session,
void *buffer)
+{
- struct coresight_device *sink = NULL;
- sink = coresight_get_sink(session->path[0]);
- if (!sink)
return -ENOENT;
- return sink_ops(sink)->set_buffer(sink, CS_MODE_API, NULL, buffer);
+} +EXPORT_SYMBOL_GPL(coresight_etm_set_buffer);
+struct coresight_session *coresight_etm_create_session(void) +{
- int ret = 0, cpu = 0;
- void *err = NULL;
- struct coresight_session *session = NULL;
- struct coresight_device *sink = NULL;
- struct list_head **path = NULL;
- size_t ncpus = 0;
- /* Currently, the only way of activating a sink is via sysfs, since
* there is no comfortable way of exporting the sink/link types
* to the user, as the devtypes are extremely generic, where the user
* might want to be more specific. Also, don't deactivate the sink,
* the user will disable it as when wishes via sysfs.
*/
- sink = coresight_get_enabled_sink(false);
- if (!sink)
return ERR_PTR(-ENODEV);
- /* Every coresight path uses the same sink. This implies a
* coresight session uses a single sink for now.
* Mark the session as the sink owner. Prevent other concurrent API
* sessions from using that sink.
*/
- if (atomic_cmpxchg(&sink->used, 0, 1))
return ERR_PTR(-EBUSY);
- session = kzalloc(sizeof(*session), GFP_KERNEL);
- if (!session) {
err = ERR_PTR(-ENOMEM);
goto failed;
- }
- get_online_cpus();
- ncpus = num_online_cpus();
- /* Build a path for each CPU */
- path = kcalloc(ncpus, sizeof(struct list_head *), GFP_KERNEL);
- if (!path) {
err = ERR_PTR(-ENOMEM);
goto failed_put_cpus;
- }
- for (cpu = 0; cpu < ncpus; ++cpu) {
struct coresight_device *source = NULL;
source = per_cpu(csdev_src, cpu);
if (!source) {
err = ERR_PTR(-ENODEV);
goto failed_put_cpus;
}
path[cpu] = coresight_build_path(source, sink);
if (IS_ERR(path[cpu])) {
err = path[cpu];
path[cpu] = NULL;
goto failed_put_cpus;
}
ret = coresight_enable_path(path[cpu], CS_MODE_API, NULL);
if (ret) {
coresight_release_path(path[cpu]);
path[cpu] = NULL;
err = ERR_PTR(ret);
goto failed_put_cpus;
}
- }
- put_online_cpus();
- session->ncpus = ncpus;
- session->path = path;
- return session;
+failed_put_cpus:
- put_online_cpus();
+failed:
- if (path) {
for (cpu--; cpu >= 0; cpu--) {
if (path[cpu]) {
coresight_disable_path(path[cpu]);
coresight_release_path(path[cpu]);
}
}
kfree(path);
- }
- kfree(session);
- /* Allow other API sessions to use the sink. */
- atomic_set(&sink->used, 0);
- return err;
+} +EXPORT_SYMBOL_GPL(coresight_etm_create_session);
+void coresight_etm_destroy_session(struct coresight_session *session) +{
- int cpu;
- struct coresight_device *sink = NULL;
- if (!session)
return;
- sink = coresight_get_sink(session->path[0]);
- if (sink)
/* Make sure the trace buffer is NULL. Disabling the sink
* possibly also means syncing the buffer, we don't know
* whether the user freed the used buffer or not, it is his
* responsibilty to sync the buffer when he needs to read trace
* data by calling coresight_etm_get_trace().
*/
sink_ops(sink)->set_buffer(sink, CS_MODE_API, NULL, NULL);
- for (cpu = 0; cpu < session->ncpus; ++cpu) {
struct coresight_device *source = NULL;
source = per_cpu(csdev_src, cpu);
if (source)
source_ops(source)->disable(source, NULL);
coresight_disable_path(session->path[cpu]);
coresight_release_path(session->path[cpu]);
- }
- /* Allow other API sessions to use this sink. */
- if (sink)
atomic_set(&sink->used, 0);
- kfree(session->path);
- kfree(session);
+} +EXPORT_SYMBOL_GPL(coresight_etm_destroy_session);
+int coresight_etm_play(struct coresight_session *session,
struct etm_config *config)
+{
- struct coresight_device *source = NULL;
- if (!session)
return -EINVAL;
- source = per_cpu(csdev_src, smp_processor_id());
- if (!source)
return -ENXIO;
- return source_ops(source)->enable(source, config, CS_MODE_API);
+} +EXPORT_SYMBOL_GPL(coresight_etm_play);
+void coresight_etm_pause(struct coresight_session *session) +{
- struct coresight_device *source = NULL;
- if (!session)
return;
- source = per_cpu(csdev_src, smp_processor_id());
- if (!source)
return;
- source_ops(source)->disable(source, NULL);
+} +EXPORT_SYMBOL_GPL(coresight_etm_pause);
+ssize_t coresight_etm_get_trace(struct coresight_session *session,
loff_t poss, size_t len, char **buf)
+{
- struct coresight_device *sink = NULL;
- if (!session)
return -EINVAL;
- sink = coresight_get_sink(session->path[0]);
- if (!sink)
return -ENXIO;
- return sink_ops(sink)->get_trace(sink, CS_MODE_API, poss, len, buf);
+} +EXPORT_SYMBOL_GPL(coresight_etm_get_trace); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index f102b16617d0..4237aab60b96 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1082,6 +1082,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
- coresight_etm_register(drvdata->csdev);
- pm_runtime_put(&adev->dev); dev_info(dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 174e6f1fab3e..803c7242864b 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -144,6 +144,7 @@ struct coresight_device *coresight_get_enabled_sink(bool reset); struct list_head *coresight_build_path(struct coresight_device *csdev, struct coresight_device *sink); void coresight_release_path(struct list_head *path); +void coresight_etm_register(struct coresight_device *csdev); #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X extern int etm_readl_cp14(u32 off, unsigned int *val); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 0a57a2ce8d4d..09b992381645 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -156,6 +156,7 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
*/
- @used: used only in a _sink_, 1 if the sink is used by an API session.
struct coresight_device { struct coresight_connection *conns; @@ -169,6 +170,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
- atomic_t used; /* 1 if used by a coresight api trace session */
}; #define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -256,6 +258,8 @@ struct coresight_ops { const struct coresight_ops_helper *helper_ops; }; +struct coresight_session;
/**
- struct etm_config - Generic etm configuration.
- @retstack: enable retstack.
@@ -282,6 +286,89 @@ extern int coresight_enable(struct coresight_device *csdev); extern void coresight_disable(struct coresight_device *csdev); extern int coresight_timeout(void __iomem *addr, u32 offset, int position, int value);
+/**
- coresight_etm_alloc_buffer - allocate a buffer with fixed size for the
- provided session.
- @session: the coresight session
- @buffer_size: the size of the coresight buffer
- Returns a coresight buffer object or a negative errno value in case
- of a failure.
- */
+extern void *coresight_etm_alloc_buffer(
struct coresight_session *session,
size_t buffer_size);
+/**
- coresight_etm_free_buffer: free a coresight buffer
- @session: the coresight session.
- @buffer: a coresight buffer previously allocated by
coresight_etm_alloc_buffer()
- */
+extern void coresight_etm_free_buffer(struct coresight_session *session,
void *buffer);
+/**
- coresight_etm_set_buffer - Set a coresight trace buffer.
- @session: the coresight session.
- @buffer: a coresight buffer previosuly allocated by
coresight_etm_alloc_buffer()
- Returns 0 on success, <0 on failure.
- */
+extern int coresight_etm_set_buffer(struct coresight_session *session,
void *buffer);
+/**
- coresight_etm_create_session - create a coresight session.
- Returns a coresight session object or a negative errno value in case of
- a failure.
- */
+extern struct coresight_session *coresight_etm_create_session(void);
+/**
- coresight_etm_destroy_session: destroy a coresight session.
- @session: the coresight session.
- */
+extern void coresight_etm_destroy_session(struct coresight_session *session);
+/**
- coresight_etm_enable: make the source start playing trace data. the source
- is configured according to the config argument that is provided.
- @session: the coresight session.
- @config: the etm configuration
- Returns 0 on success, <0 on failure.
- */
+extern int coresight_etm_play(struct coresight_session *session,
struct etm_config *config);
+/**
- coresight_etm_disable: pause the source from playing trace data.
- @session: the coresight session.
- */
+extern void coresight_etm_pause(struct coresight_session *session);
+/**
- coresight_etm_get_trace: get the coresight trace from the current buffer.
- @session: the coresight session.
- @poss: position to get the trace from.
- @len: maximum length of trace data.
- @buff: a pointer to a pointer that will receive the trace data.
- returns the actual length of trace data, or a negative errno value
- in case of a failure.
- */
+extern ssize_t coresight_etm_get_trace(struct coresight_session *session,
loff_t poss, size_t len, char **buf);
#else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } -- 2.16.2
Greetings,
I think the argument you provide here for this being specific to my project is __almost__ true. I can't really think of any other usage for the Coresight other than starting and stopping tracers.. The API indeed should be much more flexible and provide the user the power of choosing it's own Coresight path(from source to sink). This will force me to do a change that is much much larger than the current change(which quite is very simple).
My aim is the simply to provide an API that will make it possible to do what perf does, only from the outside of the infrastructure. (Notice currently perf is restricted to a single thread, and the CS_MODE_API can be used for multiple threads, e.g. a process..).
In the end i think you're right.. i might consider going further with this changeset, but like you've said you think designing a more flexible kernel API is the right approach, although the infrastructure is not quite ready for it.
Thank you for your review.
Mike.
Good day Mike,
On Tue, Sep 04, 2018 at 01:45:45PM +0300, mike@perception-point.io wrote:
From: Mike Bazov mike@perception-point.io
Introducing a new mode: CS_MODE_API. This mode shall be used by the etm-api framework to expose coresight functionality to kernel mode clients. This commit makes tmc-etr aware of it.
The set_buffer()/alloc_buffer()/free_buffer() sink operations are now turned to be mode-oriented. Up to this commit they were perf-specific, because only the perf framework used it. They are now extended to also be used with CS_MODE_API, in the hope of giving a kernel mode client the power of allocating and using a coresight buffer.
tmc-etr forbids using the sink for CS_MODE_API mode concurrently with other session modes(perf/sysfs). The implementation doesn't forbid using the sink multiple times if the requested mode is CS_MODE_API.
This commit also makes the perf framework aware of the sink operations function signature changes.
This patch is doing way too many things and needs to be broken down if we are to follow the API approach (more on that later).
Signed-off-by: Mike Bazov mike@perception-point.io
drivers/hwtracing/coresight/coresight-etb10.c | 13 ++- drivers/hwtracing/coresight/coresight-etm-perf.c | 7 +- drivers/hwtracing/coresight/coresight-priv.h | 1 + drivers/hwtracing/coresight/coresight-tmc-etf.c | 16 ++- drivers/hwtracing/coresight/coresight-tmc-etr.c | 124 +++++++++++++++++++---- drivers/hwtracing/coresight/coresight-tmc.h | 3 +- include/linux/coresight.h | 10 +- 7 files changed, 139 insertions(+), 35 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 08fa660098f8..4fbc8af7851a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -323,12 +323,15 @@ static void etb_disable(struct coresight_device *csdev) dev_dbg(drvdata->dev, "ETB disabled\n"); } -static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, +static void *etb_alloc_buffer(struct coresight_device *csdev, u32 mode, int cpu, void **pages, int nr_pages, bool overwrite) { int node; struct cs_buffers *buf;
- if (mode != CS_MODE_PERF)
return NULL;
- if (cpu == -1) cpu = smp_processor_id(); node = cpu_to_node(cpu);
@@ -344,11 +347,13 @@ static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, return buf; } -static void etb_free_buffer(void *config) +static void etb_free_buffer(u32 mode, void *config) {
- struct cs_buffers *buf = config;
- if (mode != CS_MODE_API) {
struct cs_buffers *buf = config;
- kfree(buf);
kfree(buf);
- }
See below on how to do this better.
} static int etb_set_buffer(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index abe8249b893b..563f4b47e977 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -119,7 +119,8 @@ static void free_event_data(struct work_struct *work) cpu = cpumask_first(mask); sink = coresight_get_sink(etm_event_cpu_path(event_data, cpu)); if (sink_ops(sink)->free_buffer)
sink_ops(sink)->free_buffer(event_data->snk_config);
sink_ops(sink)->free_buffer(CS_MODE_PERF,
}event_data->snk_config);
for_each_cpu(cpu, mask) { @@ -250,7 +251,8 @@ static void *etm_setup_aux(int event_cpu, void **pages, /* Allocate the sink buffer for this session */ event_data->snk_config =
sink_ops(sink)->alloc_buffer(sink, cpu, pages,
sink_ops(sink)->alloc_buffer(sink, CS_MODE_PERF,
if (!event_data->snk_config) goto err;cpu, pages, nr_pages, overwrite);
@@ -284,6 +286,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail; path = etm_event_cpu_path(event_data, cpu);
Extra cosmetic repair
/* We need a sink, no need to continue without one */ sink = coresight_get_sink(path); if (WARN_ON_ONCE(!sink)) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index c11da5564a67..174e6f1fab3e 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -72,6 +72,7 @@ enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, CS_MODE_PERF,
- CS_MODE_API
}; /** diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4156c95ce1bb..897dfc1bcbaa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -307,12 +307,16 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, dev_dbg(drvdata->dev, "TMC-ETF disabled\n"); } -static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
void **pages, int nr_pages, bool overwrite)
+static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, u32 mode,
int cpu, void **pages, int nr_pages,
bool overwrite)
{ int node; struct cs_buffers *buf;
- if (mode == CS_MODE_API)
return NULL;
- if (cpu == -1) cpu = smp_processor_id(); node = cpu_to_node(cpu);
@@ -329,11 +333,13 @@ static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, return buf; } -static void tmc_free_etf_buffer(void *config) +static void tmc_free_etf_buffer(u32 mode, void *config) {
- struct cs_buffers *buf = config;
- if (mode != CS_MODE_API) {
struct cs_buffers *buf = config;
- kfree(buf);
kfree(buf);
- }
struct cs_buffers *buf = config;
if (mode == CS_MODE_API) return;
kfree(buf);
} static int tmc_set_etf_buffer(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 56fea4ff947e..6ffcaaf76680 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1176,32 +1176,35 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, return etr_perf; }
-static void *tmc_alloc_etr_buffer(struct coresight_device *csdev,
int cpu, void **pages, int nr_pages,
bool snapshot)
+static void *tmc_etr_alloc_api_buffer(struct tmc_drvdata *drvdata, int cpu,
int nr_pages, void **pages, bool snapshot)
{
- struct etr_perf_buffer *etr_perf;
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- ssize_t size = 0;
- if (cpu == -1)
cpu = smp_processor_id();
- size = nr_pages << PAGE_SHIFT;
- etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu),
nr_pages, pages, snapshot);
- if (IS_ERR(etr_perf)) {
dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n");
return NULL;
- }
- return tmc_alloc_etr_buf(drvdata, size, 0, cpu_to_node(0), pages);
+}
- etr_perf->snapshot = snapshot;
- etr_perf->nr_pages = nr_pages;
- etr_perf->pages = pages;
+void *tmc_alloc_etr_buffer(struct coresight_device *csdev, u32 mode,
int cpu, void **pages, int nr_pages, bool snapshot)
+{
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- return etr_perf;
- switch (mode) {
- case CS_MODE_PERF:
return tmc_etr_setup_perf_buf(drvdata, cpu, nr_pages,
pages, snapshot);
Here an etr_perf buffer is returned but setting the snapshot, nr_pages and pages isn't done. The check for cpu == -1 and IS_ERR(etr_per) have also been clobbered.
I tried running a perf session after applying your patches and the kernel crashed on me. I'm pretty sure (one) of the problem is right here.
- case CS_MODE_API:
return tmc_etr_alloc_api_buffer(drvdata, cpu, nr_pages,
pages, snapshot);
- default:
/* We shouldn't get here. */
return ERR_PTR(-EINVAL);
- }
} -static void tmc_free_etr_buffer(void *config) +static void tmc_etr_free_perf_buffer(void *config) { struct etr_perf_buffer *etr_perf = config; @@ -1210,6 +1213,61 @@ static void tmc_free_etr_buffer(void *config) kfree(etr_perf); } +static void tmc_etr_free_api_buffer(void *buffer) +{
- if (buffer)
tmc_free_etr_buf(buffer);
+}
+static void tmc_free_etr_buffer(u32 mode, void *buffer) +{
- switch (mode) {
- case CS_MODE_PERF:
tmc_etr_free_perf_buffer(buffer);
break;
- case CS_MODE_API:
tmc_etr_free_api_buffer(buffer);
- default:
break;
- }
+}
+static int tmc_etr_set_api_buffer(struct coresight_device *csdev, void *buffer) +{
- struct etr_buf *etr_buf = buffer;
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- unsigned long flags;
- spin_lock_irqsave(&drvdata->spinlock, flags);
- /*
* Disable the etr if it's running. The user must pause any playing
* sources if it doesn't want to lose data.
*/
- if (drvdata->mode == CS_MODE_API && drvdata->api_buf != NULL)
tmc_etr_disable_hw(drvdata);
- drvdata->api_buf = etr_buf;
- if (drvdata->mode == CS_MODE_API && drvdata->api_buf != NULL)
tmc_etr_enable_hw(drvdata, drvdata->api_buf);
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return 0;
+}
+static int tmc_etr_set_buffer(struct coresight_device *csdev, u32 mode,
struct perf_output_handle *handle, void *buffer)
+{
- switch (mode) {
- case CS_MODE_API:
return tmc_etr_set_api_buffer(csdev, buffer);
- default:
return -EINVAL;
- }
+}
If fail to see why the set and enable functions can't be rolled out as one.
/*
- tmc_etr_sync_perf_buffer: Copy the actual trace data from the hardware
- buffer to the perf ring buffer.
@@ -1350,6 +1408,29 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) return rc; } +static int tmc_enable_etr_sink_api(struct coresight_device *csdev) +{
- int rc = 0;
- unsigned long flags;
- struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- spin_lock_irqsave(&drvdata->spinlock, flags);
- /*
* If the sink is already being used by someone, we cannot interfere.
*/
- if (drvdata->mode != CS_MODE_DISABLED &&
drvdata->mode != CS_MODE_API) {
rc = -EBUSY;
goto out;
- }
- drvdata->mode = CS_MODE_API;
+out:
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- return rc;
+}
static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -1358,13 +1439,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, return tmc_enable_etr_sink_sysfs(csdev); case CS_MODE_PERF: return tmc_enable_etr_sink_perf(csdev, data);
- case CS_MODE_API:
return tmc_enable_etr_sink_api(csdev);
Why can't the sink data be communicated at enable time just like it is the case in perf? Then it would simply be a matter of dealing with the type of data that is communicated to the sink.
} /* We shouldn't be here */ return -EINVAL; } -static void tmc_disable_etr_sink(struct coresight_device *csdev) +void tmc_disable_etr_sink(struct coresight_device *csdev) { unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -1391,6 +1474,7 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { .disable = tmc_disable_etr_sink, .alloc_buffer = tmc_alloc_etr_buffer, .update_buffer = tmc_update_etr_buffer,
- .set_buffer = tmc_etr_set_buffer, .free_buffer = tmc_free_etr_buffer,
}; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 487c53701e9c..39c6646c82d3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -172,6 +172,7 @@ struct etr_buf {
device configuration register (DEVID)
- @perf_data: PERF buffer for ETR.
- @sysfs_data: SYSFS buffer for ETR.
- @api_buf: API buffer for ETR.
Tabulation problem
*/ struct tmc_drvdata { void __iomem *base; @@ -193,6 +194,7 @@ struct tmc_drvdata { u32 etr_caps; struct etr_buf *sysfs_buf; void *perf_data;
- struct etr_buf *api_buf;
Here too
}; struct etr_buf_operations { @@ -257,7 +259,6 @@ extern const struct coresight_ops tmc_etr_cs_ops; ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len, char **bufpp);
Extra cosmetic repair
#define TMC_REG_PAIR(name, lo_off, hi_off) \ static inline u64 \ tmc_read_##name(struct tmc_drvdata *drvdata) \ diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 53535821dc25..8f7f98570afd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -183,16 +183,20 @@ struct coresight_device {
- Operations available for sinks
- @enable: enables the sink.
- @disable: disables the sink.
- @alloc_buffer: initialises perf's ring buffer for trace collection.
- @alloc_buffer: allocates the trace buffer.
- @free_buffer: release memory allocated in @get_config.
- @set_buffer: set the trace buffer.
Set the trace buffer for what? I wouldn't be looking for a 5 line descrition but something a little more intuitive would be appreciated.
- @update_buffer: update buffer pointers after a trace session.
*/ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode, void *data); void (*disable)(struct coresight_device *csdev);
- void *(*alloc_buffer)(struct coresight_device *csdev, int cpu,
- void *(*alloc_buffer)(struct coresight_device *csdev, u32 mode, int cpu, void **pages, int nr_pages, bool overwrite);
- void (*free_buffer)(void *config);
- void (*free_buffer)(u32 mode, void *buffer);
- int (*set_buffer)(struct coresight_device *csdev, u32 mode,
struct perf_output_handle *handle,
unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config);void *buffer);
-- 2.16.2