Coresight trace path holds the reference to devices on the path. Disable trace path before really remove coresight device.
Signed-off-by: Tingwei Zhang tingwei@codeaurora.org --- drivers/hwtracing/coresight/coresight.c | 137 ++++++++++++++++++++++++ 1 file changed, 137 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index cf7079f4b99c..0870316ec4c4 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -490,6 +490,138 @@ void coresight_disable_path(struct list_head *path) } EXPORT_SYMBOL_GPL(coresight_disable_path);
+/* + * coresight_dev_on_path - Check if device is on trace path. + * + * @path - The trace path to check with. + * @csdev - The coresight device to check + * + * Returns true if the device is on trace path. + */ +bool coresight_dev_on_path(struct list_head *path, + struct coresight_device *csdev) +{ + struct coresight_node *nd; + + list_for_each_entry(nd, path, link) { + if (csdev == nd->csdev) + return true; + } + return false; +} + +/* + * coresight_disable_path_with - Disable trace path + * if trace path has concerned device on it. + * + * @path - The trace path to check. + * @csdev - Concerned coresight device + * + * Returns true if the device is on trace path. + */ +static bool coresight_disable_path_with(struct list_head *path, + struct coresight_device *csdev) +{ + struct coresight_node *src_nd; + struct coresight_device *srcdev; + bool ret; + + ret = coresight_dev_on_path(path, csdev); + if (ret) { + src_nd = list_first_entry(path, struct coresight_node, link); + srcdev = src_nd->csdev; + if (source_ops(srcdev)->disable) + source_ops(srcdev)->disable(srcdev, NULL); + srcdev->enable = false; + coresight_disable_path(path); + coresight_release_path(path); + } + + return ret; +} + +/* + * __coresight_disable_with - Check cpu trace path and + * stm trace path. Disable any path with conerned device. + * + * @csdev - Concerned coresight device + * + */ +static void __coresight_disable_with(struct coresight_device *csdev) +{ + int cpu; + struct list_head *path = NULL; + + for_each_possible_cpu(cpu) { + path = per_cpu(tracer_path, cpu); + if (path) { + if (coresight_disable_path_with(path, csdev)) + per_cpu(tracer_path, cpu) = NULL; + } + } + if (stm_path) { + path = stm_path; + if (coresight_disable_path_with(path, csdev)) + stm_path = NULL; + } +} + +static int coresight_disable_match(struct device *dev, void *data) +{ + int i; + struct coresight_device *csdev, *iterator; + struct coresight_connection *conn; + + csdev = data; + iterator = to_coresight_device(dev); + + /* No need to check oneself */ + if (csdev == iterator) + return 0; + + /* + * Circle throuch all the connection of that component. If we find + * a connection whose name matches @csdev, disable path with it. + */ + for (i = 0; i < iterator->pdata->nr_outport; i++) { + conn = &iterator->pdata->conns[i]; + + if (conn->child_dev == NULL) + continue; + + if (csdev->dev.fwnode == conn->child_fwnode) { + __coresight_disable_with(iterator); + + /* No need to continue */ + break; + } + } + + /* + * Returning '0' ensures that all known components on the + * bus will be checked. + */ + return 0; +} + +/** + * coresight_disable_with - disable any path with @csdev. + * @csdev: The device to check. + * + * Search for all the active paths. If @csdev is part of that path, + * disable the whole path. Device with CORESIGHT_DEV_TYPE_HELPER type + * is not part of path. Search for its parent device and disable any + * path with that device. + */ +void coresight_disable_with(struct coresight_device *csdev) +{ + if (csdev->type != CORESIGHT_DEV_TYPE_HELPER) + return __coresight_disable_with(csdev); + if (csdev->pdata->nr_inport) + bus_for_each_dev(&coresight_bustype, NULL, + csdev, coresight_disable_match); +} + int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) {
@@ -1243,6 +1375,7 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata)
for (i = 0; i < pdata->nr_outport; i++) { if (pdata->conns[i].child_fwnode) { + pdata->conns[i].child_dev = NULL; fwnode_handle_put(pdata->conns[i].child_fwnode); pdata->conns[i].child_fwnode = NULL; } @@ -1349,9 +1482,13 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { etm_perf_del_symlink_sink(csdev); + + mutex_lock(&coresight_mutex); + coresight_disable_with(csdev); /* Remove references of that device in the topology */ coresight_remove_conns(csdev); coresight_release_platform_data(csdev->pdata); + mutex_unlock(&coresight_mutex); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister);