[RFC PATCH 7/8] coresight: etm4x: Update perf event resource handling.

Mike Leach mike.leach at linaro.org
Wed May 12 14:17:51 PDT 2021


Modifies current resource handling when parsing the perf event
options to ensure that these are correctly allocated according
to availability to ensure that configuration selection and other
perf command line options can operated correctly together.

Signed-off-by: Mike Leach <mike.leach at linaro.org>
---
 .../coresight/coresight-etm4x-core.c          | 245 ++++++------------
 1 file changed, 82 insertions(+), 163 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 2637096c4621..4cc207d42976 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -58,7 +58,8 @@ MODULE_PARM_DESC(pm_save_enable,
 static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
 static void etm4_set_default_config(struct etmv4_config *config);
 static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
-				  struct perf_event *event);
+				  struct perf_event *event,
+				  struct coresight_device *csdev);
 static u64 etm4_get_access_type(struct etmv4_config *config);
 
 static enum cpuhp_state hp_online;
@@ -478,91 +479,6 @@ static void etm4_enable_hw_smp_call(void *info)
 	arg->rc = etm4_enable_hw(arg->drvdata);
 }
 
-/*
- * The goal of function etm4_config_timestamp_event() is to configure a
- * counter that will tell the tracer to emit a timestamp packet when it
- * reaches zero.  This is done in order to get a more fine grained idea
- * of when instructions are executed so that they can be correlated
- * with execution on other CPUs.
- *
- * To do this the counter itself is configured to self reload and
- * TRCRSCTLR1 (always true) used to get the counter to decrement.  From
- * there a resource selector is configured with the counter and the
- * timestamp control register to use the resource selector to trigger the
- * event that will insert a timestamp packet in the stream.
- */
-static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
-{
-	int ctridx, ret = -EINVAL;
-	int counter, rselector;
-	u32 val = 0;
-	struct etmv4_config *config = &drvdata->config;
-
-	/* No point in trying if we don't have at least one counter */
-	if (!drvdata->nr_cntr)
-		goto out;
-
-	/* Find a counter that hasn't been initialised */
-	for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++)
-		if (config->cntr_val[ctridx] == 0)
-			break;
-
-	/* All the counters have been configured already, bail out */
-	if (ctridx == drvdata->nr_cntr) {
-		pr_debug("%s: no available counter found\n", __func__);
-		ret = -ENOSPC;
-		goto out;
-	}
-
-	/*
-	 * Searching for an available resource selector to use, starting at
-	 * '2' since every implementation has at least 2 resource selector.
-	 * ETMIDR4 gives the number of resource selector _pairs_,
-	 * hence multiply by 2.
-	 */
-	for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++)
-		if (!config->res_ctrl[rselector])
-			break;
-
-	if (rselector == drvdata->nr_resource * 2) {
-		pr_debug("%s: no available resource selector found\n",
-			 __func__);
-		ret = -ENOSPC;
-		goto out;
-	}
-
-	/* Remember what counter we used */
-	counter = 1 << ctridx;
-
-	/*
-	 * Initialise original and reload counter value to the smallest
-	 * possible value in order to get as much precision as we can.
-	 */
-	config->cntr_val[ctridx] = 1;
-	config->cntrldvr[ctridx] = 1;
-
-	/* Set the trace counter control register */
-	val =  0x1 << 16	|  /* Bit 16, reload counter automatically */
-	       0x0 << 7		|  /* Select single resource selector */
-	       0x1;		   /* Resource selector 1, i.e always true */
-
-	config->cntr_ctrl[ctridx] = val;
-
-	val = 0x2 << 16		| /* Group 0b0010 - Counter and sequencers */
-	      counter << 0;	  /* Counter to use */
-
-	config->res_ctrl[rselector] = val;
-
-	val = 0x0 << 7		| /* Select single resource selector */
-	      rselector;	  /* Resource selector */
-
-	config->ts_ctrl = val;
-
-	ret = 0;
-out:
-	return ret;
-}
-
 static int etm4_parse_event_config(struct coresight_device *csdev,
 				   struct perf_event *event)
 {
@@ -570,6 +486,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	struct etmv4_config *config = &drvdata->config;
 	struct perf_event_attr *attr = &event->attr;
+	struct etm4_cfg_resources *res_inuse;
 	unsigned long cfg_hash;
 	int preset;
 
@@ -581,6 +498,10 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
 	/* Clear configuration from previous run */
 	memset(config, 0, sizeof(struct etmv4_config));
 
+	/* clear any resources still marked as inuse */
+	res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
+	memset(res_inuse, 0, sizeof(struct etm4_cfg_resources));
+
 	if (attr->exclude_kernel)
 		config->mode = ETM_MODE_EXCL_KERN;
 
@@ -590,37 +511,51 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
 	/* Always start from the default config */
 	etm4_set_default_config(config);
 
+	/*
+	 * First set any selected configuration and preset.
+	 *
+	 * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
+	 * in the perf attributes defined in coresight-etm-perf.c.
+	 * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
+	 * A zero configid means no configuration active, preset = 0 means no preset selected.
+	 */
+	if (attr->config2 & GENMASK_ULL(63, 32)) {
+		cfg_hash = (u32)(attr->config2 >> 32);
+		preset = attr->config & 0xF;
+		ret =  cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
+		if (ret)
+			goto out;
+	}
+
+	/*
+	 * once any config is set, then we can add any perf options -
+	 * using resource management to prevent clashes & not set items that
+	 * are currently set.
+	 */
+
 	/* Configure filters specified on the perf cmd line, if any. */
-	ret = etm4_set_event_filters(drvdata, event);
+	ret = etm4_set_event_filters(drvdata, event, csdev);
 	if (ret)
-		goto out;
+		goto out_disable_cfg;
 
 	/* Go from generic option to ETMv4 specifics */
-	if (attr->config & BIT(ETM_OPT_CYCACC)) {
+	if ((attr->config & BIT(ETM_OPT_CYCACC)) && !res_inuse->cycle_cnt) {
 		config->cfg |= BIT(4);
 		/* TRM: Must program this for cycacc to work */
 		config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
 	}
-	if (attr->config & BIT(ETM_OPT_TS)) {
+	if ((attr->config & BIT(ETM_OPT_TS)) && !res_inuse->ts_rate) {
 		/*
 		 * Configure timestamps to be emitted at regular intervals in
 		 * order to correlate instructions executed on different CPUs
-		 * (CPU-wide trace scenarios).
+		 * (CPU-wide trace scenarios). We use the built in ts rate function
+		 * to manage any resource conflicts. Request a rate of 1 to maximise
+		 * freqency of timestamps.
 		 */
-		ret = etm4_config_timestamp_event(drvdata);
-
-		/*
-		 * No need to go further if timestamp intervals can't
-		 * be configured.
-		 */
-		if (ret)
-			goto out;
-
-		/* bit[11], Global timestamp tracing bit */
-		config->cfg |= BIT(11);
+		etm4_cfg_set_ts_rate(csdev, 1);
 	}
 
-	if (attr->config & BIT(ETM_OPT_CTXTID))
+	if ((attr->config & BIT(ETM_OPT_CTXTID)) && !res_inuse->ctxt_id)
 		/* bit[6], Context ID tracing bit */
 		config->cfg |= BIT(ETM4_CFG_BIT_CTXTID);
 
@@ -632,30 +567,20 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
 	if (attr->config & BIT(ETM_OPT_CTXTID2)) {
 		if (!is_kernel_in_hyp_mode()) {
 			ret = -EINVAL;
-			goto out;
+			goto out_disable_cfg;
 		}
 		config->cfg |= BIT(ETM4_CFG_BIT_VMID) | BIT(ETM4_CFG_BIT_VMID_OPT);
 	}
 
 	/* return stack - enable if selected and supported */
-	if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack)
+	if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack && !res_inuse->return_stack)
 		/* bit[12], Return stack enable bit */
 		config->cfg |= BIT(12);
 
-	/*
-	 * Set any selected configuration and preset.
-	 *
-	 * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
-	 * in the perf attributes defined in coresight-etm-perf.c.
-	 * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
-	 * A zero configid means no configuration active, preset = 0 means no preset selected.
-	 */
-	if (attr->config2 & GENMASK_ULL(63, 32)) {
-		cfg_hash = (u32)(attr->config2 >> 32);
-		preset = attr->config & 0xF;
-		ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
-	}
+	return 0;
 
+out_disable_cfg:
+	cscfg_csdev_disable_active_config(csdev);
 out:
 	return ret;
 }
@@ -871,6 +796,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
 	spin_unlock(&drvdata->spinlock);
 	cpus_read_unlock();
 
+	cscfg_csdev_disable_active_config(csdev);
+
 	dev_dbg(&csdev->dev, "ETM tracing disabled\n");
 }
 
@@ -1375,52 +1302,37 @@ static void etm4_set_default(struct etmv4_config *config)
 	etm4_set_default_filter(config);
 }
 
-static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
+static int etm4_get_next_comparator_res(struct cscfg_res_impl_used *res,
+					u32 type)
 {
-	int nr_comparator, index = 0;
-	struct etmv4_config *config = &drvdata->config;
-
-	/*
-	 * nr_addr_cmp holds the number of comparator _pair_, so time 2
-	 * for the total number of comparators.
-	 */
-	nr_comparator = drvdata->nr_addr_cmp * 2;
+	switch (type) {
+	case ETM_ADDR_TYPE_RANGE:
+		return etm4_res_find_addr_comp_pair(res);
 
-	/* Go through the tally of comparators looking for a free one. */
-	while (index < nr_comparator) {
-		switch (type) {
-		case ETM_ADDR_TYPE_RANGE:
-			if (config->addr_type[index] == ETM_ADDR_TYPE_NONE &&
-			    config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE)
-				return index;
-
-			/* Address range comparators go in pairs */
-			index += 2;
-			break;
-		case ETM_ADDR_TYPE_START:
-		case ETM_ADDR_TYPE_STOP:
-			if (config->addr_type[index] == ETM_ADDR_TYPE_NONE)
-				return index;
-
-			/* Start/stop address can have odd indexes */
-			index += 1;
-			break;
-		default:
-			return -EINVAL;
-		}
+	case ETM_ADDR_TYPE_START:
+	case ETM_ADDR_TYPE_STOP:
+		return etm4_res_find_addr_comparator(res);
 	}
-
-	/* If we are here all the comparators have been used. */
-	return -ENOSPC;
+	return -EINVAL;
 }
 
+/*
+ * Called to set up perf event filters - after any selected configuration.
+ * This allows the perf command line filters to be used in combination with
+ * any selected configuration.
+ *
+ * Use the config resources to find available comparators - only set default
+ * filter if no previous configuration has set up filtering.
+ */
 static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
-				  struct perf_event *event)
+				  struct perf_event *event,
+				  struct coresight_device *csdev)
 {
 	int i, comparator, ret = 0;
 	u64 address;
 	struct etmv4_config *config = &drvdata->config;
 	struct etm_filters *filters = event->hw.addr_filters;
+	struct cscfg_res_impl_used *res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
 
 	if (!filters)
 		goto default_filter;
@@ -1440,7 +1352,7 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 		enum etm_addr_type type = filter->type;
 
 		/* See if a comparator is free. */
-		comparator = etm4_get_next_comparator(drvdata, type);
+		comparator = etm4_get_next_comparator_res(res, type);
 		if (comparator < 0) {
 			ret = comparator;
 			goto out;
@@ -1457,10 +1369,8 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 			 * in the started state
 			 */
 			config->vinst_ctrl |= BIT(9);
-
-			/* No start-stop filtering for ViewInst */
-			config->vissctlr = 0x0;
 			break;
+
 		case ETM_ADDR_TYPE_START:
 		case ETM_ADDR_TYPE_STOP:
 			/* Get the right start or stop address */
@@ -1485,10 +1395,8 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 			 */
 			if (filters->ssstatus)
 				config->vinst_ctrl |= BIT(9);
-
-			/* No include/exclude filtering for ViewInst */
-			config->viiectlr = 0x0;
 			break;
+
 		default:
 			ret = -EINVAL;
 			goto out;
@@ -1497,9 +1405,10 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 
 	goto out;
 
-
 default_filter:
-	etm4_set_default_filter(config);
+	/* if no filtering has been set by any other method - set default here */
+	if (!config->viiectlr && !config->vissctlr)
+		etm4_set_default_filter(config);
 
 out:
 	return ret;
@@ -2161,10 +2070,19 @@ static int __init etm4x_init(void)
 	}
 
 	ret = platform_driver_register(&etm4_platform_driver);
+	if (ret) {
+		pr_err("Error registering etm4x platform driver\n");
+		goto clear_amba;
+	}
+
+	ret = etm4_cscfg_load_builtin_cfg();
 	if (!ret)
 		return 0;
 
-	pr_err("Error registering etm4x platform driver\n");
+	pr_err("Error loading etm4x coresight configurations\n");
+	platform_driver_unregister(&etm4_platform_driver);
+
+clear_amba:
 	amba_driver_unregister(&etm4x_amba_driver);
 
 clear_pm:
@@ -2174,6 +2092,7 @@ static int __init etm4x_init(void)
 
 static void __exit etm4x_exit(void)
 {
+	etm4_cscfg_unload_builtin_cfg();
 	amba_driver_unregister(&etm4x_amba_driver);
 	platform_driver_unregister(&etm4_platform_driver);
 	etm4_pm_clear();
-- 
2.17.1




More information about the linux-arm-kernel mailing list