From 0726d3a73c593ac4b6367285f6bf6e202ebbcf90 Mon Sep 17 00:00:00 2001 From: Yuanfang Zhang Date: Tue, 5 Nov 2019 18:50:57 +0800 Subject: [PATCH] coresight: fix tmc flush timeout issue cpu will hang when FlushMan bit, StopOnFl bit don't be reset to 0 and etm0 was enabled. This change disable source and link for all activated path to workaround this issue when read tmc from dev interface and switch etr out_mode. Change-Id: Idf6d8cffcef0bedaeed91f28366f9c33e22bc0cb Signed-off-by: Yuanfang Zhang Signed-off-by: Tingwei Zhang --- .../hwtracing/coresight/coresight-tmc-etf.c | 12 +- .../hwtracing/coresight/coresight-tmc-etr.c | 18 ++- drivers/hwtracing/coresight/coresight.c | 143 +++++++++++++++++- include/linux/coresight.h | 4 + 4 files changed, 167 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 160744fd97f4..c1cab70919e7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -614,9 +614,12 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->mode == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + coresight_disable_all_source_link(); + spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etb_disable_hw(drvdata); - + } drvdata->reading = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -646,6 +649,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } } + drvdata->reading = false; /* Re-enable the TMC if need be */ if (drvdata->mode == CS_MODE_SYSFS) { /* @@ -658,6 +662,9 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) */ memset(drvdata->buf, 0, drvdata->size); __tmc_etb_enable_hw(drvdata); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + coresight_enable_all_source_link(); + spin_lock_irqsave(&drvdata->spinlock, flags); } else { /* * The ETB/ETF is not tracing and the buffer was just read. @@ -667,7 +674,6 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) drvdata->buf = NULL; } - drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); /* diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 54d8973a8a4b..56b3605b7398 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -2015,12 +2015,14 @@ int tmc_etr_switch_mode(struct tmc_drvdata *drvdata, const char *out_mode) return 0; } + coresight_disable_all_source_link(); tmc_disable_etr_sink(drvdata->csdev); old_mode = drvdata->out_mode; drvdata->out_mode = new_mode; if (tmc_enable_etr_sink_sysfs(drvdata->csdev)) { drvdata->out_mode = old_mode; tmc_enable_etr_sink_sysfs(drvdata->csdev); + coresight_enable_all_source_link(); dev_err(&drvdata->csdev->dev, "Switch to %s failed. Fall back to %s.\n", str_tmc_etr_out_mode[new_mode], @@ -2028,7 +2030,7 @@ int tmc_etr_switch_mode(struct tmc_drvdata *drvdata, const char *out_mode) mutex_unlock(&drvdata->mem_lock); return -EINVAL; } - + coresight_enable_all_source_link(); mutex_unlock(&drvdata->mem_lock); return 0; } @@ -2081,9 +2083,12 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if we are trying to read from a running session. */ - if (drvdata->mode == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + coresight_disable_all_source_link(); + spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_disable_hw(drvdata); - + } drvdata->reading = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -2103,6 +2108,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) mutex_lock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); + drvdata->reading = false; /* RE-enable the TMC if need be */ if (drvdata->mode == CS_MODE_SYSFS) { /* @@ -2111,6 +2117,10 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) * be NULL. */ __tmc_etr_enable_hw(drvdata); + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + coresight_enable_all_source_link(); + spin_lock_irqsave(&drvdata->spinlock, flags); } else { /* * The ETR is not tracing and the buffer was just read. @@ -2120,14 +2130,12 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) drvdata->sysfs_buf = NULL; } - drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free allocated memory out side of the spinlock */ if (sysfs_buf) tmc_etr_free_sysfs_buf(sysfs_buf); - mutex_unlock(&drvdata->mem_lock); return 0; } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index dd54391f7820..636a76715720 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -48,6 +48,8 @@ struct coresight_path { static LIST_HEAD(cs_active_paths); +static struct coresight_device *activated_sink; + /* * When losing synchronisation a new barrier packet needs to be inserted at the * beginning of the data collected in a buffer. That way the decoder knows that @@ -918,6 +920,129 @@ static int coresight_store_path(struct list_head *path) return 0; } +static void coresight_enable_source_link(struct list_head *path) +{ + u32 type; + int ret; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; + + list_for_each_entry_reverse(nd, path, link) { + csdev = nd->csdev; + type = csdev->type; + + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { + case CORESIGHT_DEV_TYPE_SINK: + break; + case CORESIGHT_DEV_TYPE_SOURCE: + if (source_ops(csdev)->enable) { + ret = coresight_enable_reg_clk(csdev); + if (ret) + goto err; + + ret = source_ops(csdev)->enable(csdev, + NULL, CS_MODE_SYSFS); + if (ret) { + coresight_disable_reg_clk(csdev); + goto err; + } + } + csdev->enable = true; + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + ret = coresight_enable_link(csdev, parent, child, path); + if (ret) + goto err; + break; + default: + break; + } + } + + return; +err: + coresight_disable_path_from(path, nd); + coresight_release_path(path); +} + +static void coresight_disable_source_link(struct list_head *path) +{ + u32 type; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; + + list_for_each_entry(nd, path, link) { + csdev = nd->csdev; + type = csdev->type; + + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { + case CORESIGHT_DEV_TYPE_SINK: + break; + case CORESIGHT_DEV_TYPE_SOURCE: + if (source_ops(csdev)->disable) { + source_ops(csdev)->disable(csdev, NULL); + coresight_disable_reg_clk(csdev); + } + csdev->enable = false; + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + coresight_disable_link(csdev, parent, child, path); + break; + default: + break; + } + } +} +void coresight_disable_all_source_link(void) +{ + struct coresight_path *cspath = NULL; + struct coresight_path *cspath_next = NULL; + + mutex_lock(&coresight_mutex); + + list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) { + coresight_disable_source_link(cspath->path); + } + + activated_sink = coresight_get_enabled_sink(false); + if (activated_sink) + activated_sink->activated = false; + + mutex_unlock(&coresight_mutex); +} + +void coresight_enable_all_source_link(void) +{ + struct coresight_path *cspath = NULL; + struct coresight_path *cspath_next = NULL; + + mutex_lock(&coresight_mutex); + + list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) { + coresight_enable_source_link(cspath->path); + } + + if (activated_sink && activated_sink->enable) + activated_sink->activated = true; + + activated_sink = NULL; + mutex_unlock(&coresight_mutex); +} + int coresight_enable(struct coresight_device *csdev) { int ret = 0; @@ -1041,18 +1166,32 @@ static ssize_t enable_sink_store(struct device *dev, int ret; unsigned long val; struct coresight_device *csdev = to_coresight_device(dev); + struct coresight_device *sink = NULL; ret = kstrtoul(buf, 10, &val); if (ret) return ret; + mutex_lock(&coresight_mutex); - if (val) + if (val) { + sink = activated_sink ? activated_sink : + coresight_get_enabled_sink(false); + if (sink && strcmp(dev_name(&sink->dev), + dev_name(&csdev->dev))) + goto err; csdev->activated = true; - else + } else { + if (csdev->enable) + goto err; csdev->activated = false; + } + mutex_unlock(&coresight_mutex); return size; +err: + mutex_unlock(&coresight_mutex); + return -EINVAL; } static DEVICE_ATTR_RW(enable_sink); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e7dce1dd97a1..8d8be9df7498 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -313,6 +313,8 @@ extern const char *coresight_alloc_device_name(struct coresight_dev_list *devs, struct device *dev); extern void coresight_disable_reg_clk(struct coresight_device *csdev); extern int coresight_enable_reg_clk(struct coresight_device *csdev); +extern void coresight_disable_all_source_link(void); +extern void coresight_enable_all_source_link(void); #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } @@ -339,6 +341,8 @@ static inline int coresight_enable_reg_clk(struct coresight_device *csdev) { return -EINVAL; } +static void coresight_disable_all_source_link(void) {}; +static void coresight_enable_all_source_link(void) {}; #endif extern int coresight_get_cpu(struct device *dev);