coresight: Self-hosted tracing updates for Linux v7.2

Updates for the CoreSight self hosted tracing subsystem includes:
  - Better power management for components based on the CPU PM, including
    support for components on the trace path for CPUs. Add support for
    save/restore for TRBE
  - Miscellaneous fixes to the drivers
    * Fix overflow when the buffer size is > 2GB for tmc-etr
    * Ultrasoc SMB Perf buffer OOB access
 
 Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuFy0byloRoXZHaWBxcXRZPKyBqEFAmohhJYACgkQxcXRZPKy
 BqHOUxAAmy9T6+KKFFDKCHRbzPzl7iRxRHedknrqaaVOJ9KFD8mRASnX/CtXvjmN
 e/6KS5rrUwoG7NUSkkP8R5/DQcOiUThhRJfZT0cE0505Z7m12+A6c1fBSJcE706Q
 kL6oDYbmkrh1Hs2xvhvrVCLzu2xMOCdKBzsxAopjKsMzu+VWx32Ee771l74fKKKw
 ClI8Gl7K+nr2P5SmVb4WSJTtSUIXL/Ed5X6sMkzYFjD5N5KLMu2pECCvzi/0wUAv
 wfeS8Ubdth5EVOdm+UnxoZUaXCsWO+qkamUUmh6mRq4/wg4dbPvRi2APzQySyVBW
 uleDiFMI4v9kXe25uzog6dnIr108lc7udBGpVMnr1TfxeOYLV97qrQw7nTgxA++x
 yoWFUIlNGM+XGXtZx1QJ4+fjV2xayxhO0AdNqcpIfhYRgrPX2FLmryUCeSnjpYaC
 Dd9/emq4PRS7zL5+QNqRTyatKxSGUWf8bXKlyptUQCWZaWhZfkbFCkctAyfV/Ajw
 UErUPyJe4DMr2SiOJDltAOxeqkPlxSMFO2ppe7ppngoubRYoo05Mtz/MY3rFqhFO
 VQ63cNG1Cu3JYEglre4e8jsxlyPf10+XAzKJ6FfeQMbsGw3s72B70wTNzUJlnJpl
 vhKicLjcbyOie5wrUdaE2qZzMp8lsYUzWByaHDVHE8AjOwfvQ1A=
 =PhTE
 -----END PGP SIGNATURE-----

Merge tag 'coresight-next-v7.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/coresight/linux into char-misc-next

Suzuki writes:

coresight: Self-hosted tracing updates for Linux v7.2

Updates for the CoreSight self hosted tracing subsystem includes:
 - Better power management for components based on the CPU PM, including
   support for components on the trace path for CPUs. Add support for
   save/restore for TRBE
 - Miscellaneous fixes to the drivers
   * Fix overflow when the buffer size is > 2GB for tmc-etr
   * Ultrasoc SMB Perf buffer OOB access

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>

* tag 'coresight-next-v7.2' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/coresight/linux: (36 commits)
  coresight: ultrasoc-smb: Fix OOB write in smb_sync_perf_buffer()
  coresight: etb10: restore atomic_t for shared reading state
  coresight: platform: defer connection counter increment until alloc succeeds
  coresight: sysfs: Validate CPU online status for per-CPU sources
  coresight: Move CPU hotplug callbacks to core layer
  coresight: sysfs: Increment refcount only for software source
  coresight: trbe: Save and restore state across CPU low power state
  coresight: Add PM callbacks for sink device
  coresight: Control path during CPU idle
  coresight: sysfs: Use source's path pointer for path control
  coresight: etm3x: Set active path on target CPU
  coresight: etm4x: Set active path on target CPU
  coresight: Save active path for system tracers
  coresight: Introduce coresight_enable_source() helper
  coresight: Use helpers to fetch first and last nodes
  coresight: Control path with range
  coresight: Disable source helpers in coresight_disable_path()
  coresight: syscfg: Use IRQ-safe spinlock to protect active variables
  coresight: etm4x: Remove redundant checks in PM save and restore
  coresight: etm4x: Hook CPU PM callbacks
  ...
This commit is contained in:
Greg Kroah-Hartman 2026-06-08 21:35:34 +02:00
commit 8b98f9f42e
18 changed files with 938 additions and 520 deletions

View File

@ -514,7 +514,7 @@ static int __catu_probe(struct device *dev, struct resource *res)
int ret = 0;
u32 dma_mask;
struct catu_drvdata *drvdata;
struct coresight_desc catu_desc;
struct coresight_desc catu_desc = { 0 };
struct coresight_platform_data *pdata = NULL;
void __iomem *base;

View File

@ -6,6 +6,7 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/build_bug.h>
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@ -35,6 +36,10 @@
DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
static DEFINE_RAW_SPINLOCK(coresight_dev_lock);
static DEFINE_PER_CPU(struct coresight_device *, csdev_source);
static DEFINE_PER_CPU(bool, percpu_pm_failed);
/**
* struct coresight_node - elements of a path, from source to sink
* @csdev: Address of an element.
@ -58,6 +63,24 @@ static LIST_HEAD(coresight_dev_idx_list);
static const struct cti_assoc_op *cti_assoc_ops;
static struct coresight_node *
coresight_path_first_node(struct coresight_path *path)
{
if (list_empty(&path->path_list))
return NULL;
return list_first_entry(&path->path_list, struct coresight_node, link);
}
static struct coresight_node *
coresight_path_last_node(struct coresight_path *path)
{
if (list_empty(&path->path_list))
return NULL;
return list_last_entry(&path->path_list, struct coresight_node, link);
}
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@ -82,14 +105,89 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
static struct coresight_device *coresight_get_source(struct coresight_path *path)
static void coresight_set_percpu_source(struct coresight_device *csdev)
{
if (!csdev || !coresight_is_percpu_source(csdev))
return;
guard(raw_spinlock_irqsave)(&coresight_dev_lock);
/* Expect no device to be set yet */
WARN_ON(per_cpu(csdev_source, csdev->cpu));
per_cpu(csdev_source, csdev->cpu) = csdev;
}
static void coresight_clear_percpu_source(struct coresight_device *csdev)
{
if (!csdev || !coresight_is_percpu_source(csdev))
return;
/* Clear percpu_pm_failed */
per_cpu(percpu_pm_failed, csdev->cpu) = false;
guard(raw_spinlock_irqsave)(&coresight_dev_lock);
/* The per-CPU pointer should contain the same csdev */
WARN_ON(per_cpu(csdev_source, csdev->cpu) != csdev);
per_cpu(csdev_source, csdev->cpu) = NULL;
}
struct coresight_device *coresight_get_percpu_source_ref(int cpu)
{
struct coresight_device *csdev;
if (WARN_ON(cpu < 0))
return NULL;
guard(raw_spinlock_irqsave)(&coresight_dev_lock);
csdev = per_cpu(csdev_source, cpu);
if (!csdev)
return NULL;
/*
* Holding a reference to the csdev->dev ensures that the
* coresight_device is live for the caller. The path building
* logic can safely either build a path to the sink or fail
* if the device is being unregistered (if there was a race).
* The caller can skip the "source" device, if no path could
* be built.
*/
get_device(&csdev->dev);
return csdev;
}
void coresight_put_percpu_source_ref(struct coresight_device *csdev)
{
if (!csdev || !coresight_is_percpu_source(csdev))
return;
guard(raw_spinlock_irqsave)(&coresight_dev_lock);
/*
* TODO: coresight_device_release() is invoked to release resources when
* the device's refcount reaches zero. It then calls free_percpu(),
* which acquires pcpu_lock a sleepable lock when PREEMPT_RT is
* enabled. Since the raw spinlock coresight_dev_lock is held, this can
* lead to a potential "scheduling while atomic" issue.
*/
put_device(&csdev->dev);
}
struct coresight_device *coresight_get_source(struct coresight_path *path)
{
struct coresight_device *csdev;
struct coresight_node *nd;
if (!path)
return NULL;
csdev = list_first_entry(&path->path_list, struct coresight_node, link)->csdev;
nd = coresight_path_first_node(path);
if (!nd)
return NULL;
csdev = nd->csdev;
if (!coresight_is_device_source(csdev))
return NULL;
@ -385,19 +483,47 @@ static void coresight_disable_helpers(struct coresight_device *csdev,
}
/*
* Helper function to call source_ops(csdev)->disable and also disable the
* helpers.
*
* There is an imbalance between coresight_enable_path() and
* coresight_disable_path(). Enabling also enables the source's helpers as part
* of the path, but disabling always skips the first item in the path (which is
* the source), so sources and their helpers don't get disabled as part of that
* function and we need the extra step here.
* coresight_enable_source() and coresight_disable_source() only enable and
* disable the source, but do nothing for the associated helpers, which are
* controlled as part of the path.
*/
int coresight_enable_source(struct coresight_device *csdev,
struct perf_event *event, enum cs_mode mode,
struct coresight_path *path)
{
int ret;
if (!coresight_is_device_source(csdev))
return -EINVAL;
ret = source_ops(csdev)->enable(csdev, event, mode, path);
if (ret)
return ret;
/*
* Update the path pointer until after the source is enabled to avoid
* races where multiple paths attempt to enable the same source.
*
* Do not set the path pointer here for per-CPU sources; set it locally
* on the CPU instead. Otherwise, there is a window where the path is
* enabled but the pointer is not yet set, causing CPU PM notifiers to
* miss PM operations due to reading a NULL pointer.
*/
if (!coresight_is_percpu_source(csdev))
csdev->path = path;
return 0;
}
void coresight_disable_source(struct coresight_device *csdev, void *data)
{
if (!coresight_is_device_source(csdev))
return;
if (!coresight_is_percpu_source(csdev))
csdev->path = NULL;
source_ops(csdev)->disable(csdev, data);
coresight_disable_helpers(csdev, NULL);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
@ -424,20 +550,42 @@ int coresight_resume_source(struct coresight_device *csdev)
EXPORT_SYMBOL_GPL(coresight_resume_source);
/*
* coresight_disable_path_from : Disable components in the given path beyond
* @nd in the list. If @nd is NULL, all the components, except the SOURCE are
* disabled.
* Callers must fetch nodes from the path and pass @from and @to to the path
* enable/disable functions. Walk the path from @from to locate @to. If @to
* is found, it indicates @from and @to are in order. Otherwise, they are out
* of order.
*/
static void coresight_disable_path_from(struct coresight_path *path,
struct coresight_node *nd)
static bool coresight_path_nodes_in_order(struct coresight_path *path,
struct coresight_node *from,
struct coresight_node *to)
{
struct coresight_node *nd;
if (WARN_ON_ONCE(!from || !to))
return false;
nd = from;
list_for_each_entry_from(nd, &path->path_list, link) {
if (nd == to)
return true;
}
return false;
}
static void coresight_disable_path_from_to(struct coresight_path *path,
struct coresight_node *from,
struct coresight_node *to)
{
u32 type;
struct coresight_device *csdev, *parent, *child;
struct coresight_node *nd;
if (!nd)
nd = list_first_entry(&path->path_list, struct coresight_node, link);
if (!coresight_path_nodes_in_order(path, from, to))
return;
list_for_each_entry_continue(nd, &path->path_list, link) {
nd = from;
list_for_each_entry_from(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@ -457,12 +605,6 @@ static void coresight_disable_path_from(struct coresight_path *path,
coresight_disable_sink(csdev);
break;
case CORESIGHT_DEV_TYPE_SOURCE:
/*
* We skip the first node in the path assuming that it
* is the source. So we don't expect a source device in
* the middle of a path.
*/
WARN_ON(1);
break;
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
@ -476,12 +618,18 @@ static void coresight_disable_path_from(struct coresight_path *path,
/* Disable all helpers adjacent along the path last */
coresight_disable_helpers(csdev, path);
/* Iterate up to and including @to */
if (nd == to)
break;
}
}
void coresight_disable_path(struct coresight_path *path)
{
coresight_disable_path_from(path, NULL);
coresight_disable_path_from_to(path,
coresight_path_first_node(path),
coresight_path_last_node(path));
}
EXPORT_SYMBOL_GPL(coresight_disable_path);
@ -499,22 +647,36 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
ret = coresight_enable_helper(helper, mode, path);
if (ret)
return ret;
goto err;
}
return 0;
err:
while (i--) {
helper = csdev->pdata->out_conns[i]->dest_dev;
if (helper && coresight_is_helper(helper))
coresight_disable_helper(helper, path);
}
return ret;
}
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
static int coresight_enable_path_from_to(struct coresight_path *path,
enum cs_mode mode,
struct coresight_node *from,
struct coresight_node *to)
{
int ret = 0;
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
struct coresight_device *source;
source = coresight_get_source(path);
list_for_each_entry_reverse(nd, &path->path_list, link) {
if (!coresight_path_nodes_in_order(path, from, to))
return -EINVAL;
nd = to;
list_for_each_entry_from_reverse(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@ -553,7 +715,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
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, source);
ret = coresight_enable_link(csdev, parent, child,
coresight_get_source(path));
if (ret)
goto err_disable_helpers;
break;
@ -561,6 +724,10 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
ret = -EINVAL;
goto err_disable_helpers;
}
/* Iterate down to and including @from */
if (nd == from)
break;
}
out:
@ -568,18 +735,36 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
err_disable_helpers:
coresight_disable_helpers(csdev, path);
err_disable_path:
coresight_disable_path_from(path, nd);
/* No device is actually enabled */
if (nd == to)
goto out;
/* Fetch the previous node, the last successfully enabled one */
nd = list_next_entry(nd, link);
coresight_disable_path_from_to(path, nd, to);
goto out;
}
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
{
return coresight_enable_path_from_to(path, mode,
coresight_path_first_node(path),
coresight_path_last_node(path));
}
struct coresight_device *coresight_get_sink(struct coresight_path *path)
{
struct coresight_device *csdev;
struct coresight_node *nd;
if (!path)
return NULL;
csdev = list_last_entry(&path->path_list, struct coresight_node, link)->csdev;
nd = coresight_path_last_node(path);
if (!nd)
return NULL;
csdev = nd->csdev;
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
return NULL;
@ -642,15 +827,29 @@ struct coresight_device *coresight_get_sink_by_id(u32 id)
*/
static bool coresight_get_ref(struct coresight_device *csdev)
{
struct device *dev = csdev->dev.parent;
struct device *dev = &csdev->dev;
struct device *parent = csdev->dev.parent;
struct device_driver *drv;
/* Make sure csdev can't go away */
get_device(dev);
/* Make sure parent device can't go away */
get_device(parent);
/* Make sure the driver can't be removed */
if (!try_module_get(dev->driver->owner))
return false;
/* Make sure the device can't go away */
get_device(dev);
pm_runtime_get_sync(dev);
drv = parent->driver;
if (!drv || !try_module_get(drv->owner))
goto err_module;
/* Make sure the device is powered on */
pm_runtime_get_sync(parent);
return true;
err_module:
put_device(parent);
put_device(dev);
return false;
}
/**
@ -661,11 +860,15 @@ static bool coresight_get_ref(struct coresight_device *csdev)
*/
static void coresight_put_ref(struct coresight_device *csdev)
{
struct device *dev = csdev->dev.parent;
struct device *dev = &csdev->dev;
struct device *parent = csdev->dev.parent;
struct device_driver *drv = parent->driver;
pm_runtime_put(dev);
pm_runtime_put(parent);
if (drv)
module_put(drv->owner);
put_device(parent);
put_device(dev);
module_put(dev->driver->owner);
}
/*
@ -739,8 +942,8 @@ static int coresight_get_trace_id(struct coresight_device *csdev,
* Call this after creating the path and before enabling it. This leaves
* the trace ID set on the path, or it remains 0 if it couldn't be assigned.
*/
void coresight_path_assign_trace_id(struct coresight_path *path,
enum cs_mode mode)
int coresight_path_assign_trace_id(struct coresight_path *path,
enum cs_mode mode)
{
struct coresight_device *sink = coresight_get_sink(path);
struct coresight_node *nd;
@ -750,15 +953,18 @@ void coresight_path_assign_trace_id(struct coresight_path *path,
/* Assign a trace ID to the path for the first device that wants to do it */
trace_id = coresight_get_trace_id(nd->csdev, mode, sink);
/*
* 0 in this context is that it didn't want to assign so keep searching.
* Non 0 is either success or fail.
*/
if (trace_id != 0) {
path->trace_id = trace_id;
return;
}
/* 0 means the device has no ID assignment, so keep searching */
if (trace_id == 0)
continue;
if (!IS_VALID_CS_TRACE_ID(trace_id))
return -EINVAL;
path->trace_id = trace_id;
return 0;
}
return -EINVAL;
}
/**
@ -787,7 +993,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
goto out;
if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) &&
sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) {
sink == per_cpu(csdev_sink, csdev->cpu)) {
if (_coresight_build_path(sink, source, sink, path) == 0) {
found = true;
goto out;
@ -1014,7 +1220,7 @@ coresight_find_default_sink(struct coresight_device *csdev)
/* look for a default sink if we have not found for this device */
if (!csdev->def_sink) {
if (coresight_is_percpu_source(csdev))
csdev->def_sink = per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev));
csdev->def_sink = per_cpu(csdev_sink, csdev->cpu);
if (!csdev->def_sink)
csdev->def_sink = coresight_find_sink(csdev, &depth);
}
@ -1322,32 +1528,56 @@ void coresight_release_platform_data(struct device *dev,
devm_kfree(dev, pdata);
}
struct coresight_device *coresight_register(struct coresight_desc *desc)
static struct coresight_device *
coresight_init_device(struct coresight_desc *desc)
{
int ret;
struct coresight_device *csdev;
bool registered = false;
csdev = kzalloc_obj(*csdev);
if (!csdev) {
ret = -ENOMEM;
goto err_out;
}
if (!csdev)
return ERR_PTR(-ENOMEM);
csdev->pdata = desc->pdata;
csdev->type = desc->type;
csdev->subtype = desc->subtype;
csdev->ops = desc->ops;
csdev->access = desc->access;
csdev->orphan = true;
if (desc->flags & CORESIGHT_DESC_CPU_BOUND) {
csdev->cpu = desc->cpu;
} else {
/* A per-CPU source or sink must set CPU_BOUND flag */
if (coresight_is_percpu_source(csdev) ||
coresight_is_percpu_sink(csdev)) {
kfree(csdev);
return ERR_PTR(-EINVAL);
}
csdev->cpu = -1;
}
csdev->dev.type = &coresight_dev_type[desc->type];
csdev->dev.groups = desc->groups;
csdev->dev.parent = desc->dev;
csdev->dev.release = coresight_device_release;
csdev->dev.bus = &coresight_bustype;
return csdev;
}
struct coresight_device *coresight_register(struct coresight_desc *desc)
{
int ret;
struct coresight_device *csdev;
bool registered = false;
csdev = coresight_init_device(desc);
if (IS_ERR(csdev)) {
ret = PTR_ERR(csdev);
goto err_out;
}
if (csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) {
raw_spin_lock_init(&csdev->perf_sink_id_map.lock);
@ -1398,6 +1628,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
if (ret)
goto out_unlock;
coresight_set_percpu_source(csdev);
mutex_unlock(&coresight_mutex);
if (cti_assoc_ops && cti_assoc_ops->add)
@ -1427,6 +1658,7 @@ void coresight_unregister(struct coresight_device *csdev)
cti_assoc_ops->remove(csdev);
mutex_lock(&coresight_mutex);
coresight_clear_percpu_source(csdev);
etm_perf_del_symlink_sink(csdev);
coresight_remove_conns(csdev);
coresight_clear_default_sink(csdev);
@ -1615,6 +1847,211 @@ static void coresight_release_device_list(void)
}
}
static struct coresight_path *coresight_cpu_get_active_path(enum cs_mode mode)
{
struct coresight_device *source;
bool is_active = false;
source = coresight_get_percpu_source_ref(smp_processor_id());
if (!source)
return NULL;
if (coresight_get_mode(source) & mode)
is_active = true;
coresight_put_percpu_source_ref(source);
/*
* It is expected to run in atomic context or with the CPU lock held for
* sysfs mode, so it cannot be preempted to disable the path. Here
* returns the active path pointer without concern that its state may
* change. Since the build path has taken a reference on the component,
* the path can be safely used by the caller.
*/
return is_active ? source->path : NULL;
}
/* Return: 1 if PM is required, 0 if skip, or a negative error */
static int coresight_pm_is_needed(struct coresight_path *path)
{
struct coresight_device *source, *sink;
if (this_cpu_read(percpu_pm_failed))
return -EIO;
if (!path)
return 0;
source = coresight_get_source(path);
sink = coresight_get_sink(path);
if (!source || !sink)
return 0;
/* pm_save_disable() and pm_restore_enable() must be paired */
if (coresight_ops(source)->pm_save_disable &&
coresight_ops(source)->pm_restore_enable)
return 1;
/*
* It is not permitted that the source has no callbacks while the sink
* does, as the sink cannot be disabled without disabling the source,
* which may lead to lockups. Fix this by enabling self-hosted PM
* mode for ETM (see etm4_probe()).
*/
if (coresight_ops(sink)->pm_save_disable &&
coresight_ops(sink)->pm_restore_enable) {
pr_warn_once("coresight PM failed: source has no PM callbacks; "
"cannot safely control sink\n");
return -EINVAL;
}
return 0;
}
static int coresight_pm_device_save(struct coresight_device *csdev)
{
if (!csdev || !coresight_ops(csdev)->pm_save_disable)
return 0;
return coresight_ops(csdev)->pm_save_disable(csdev);
}
static void coresight_pm_device_restore(struct coresight_device *csdev)
{
if (!csdev || !coresight_ops(csdev)->pm_restore_enable)
return;
coresight_ops(csdev)->pm_restore_enable(csdev);
}
static int coresight_pm_save(struct coresight_path *path)
{
struct coresight_device *source = coresight_get_source(path);
struct coresight_node *from, *to;
int ret;
ret = coresight_pm_device_save(source);
if (ret)
return ret;
from = coresight_path_first_node(path);
/* Disable up to the node before sink */
to = list_prev_entry(coresight_path_last_node(path), link);
coresight_disable_path_from_to(path, from, to);
/*
* Save the sink. Most sinks do not implement a save callback to avoid
* latency from memory copying. We assume the sink's save and restore
* always succeed.
*/
coresight_pm_device_save(coresight_get_sink(path));
return 0;
}
static void coresight_pm_restore(struct coresight_path *path)
{
struct coresight_device *source = coresight_get_source(path);
struct coresight_device *sink = coresight_get_sink(path);
struct coresight_node *from, *to;
int ret;
coresight_pm_device_restore(sink);
from = coresight_path_first_node(path);
/* Enable up to the node before sink */
to = list_prev_entry(coresight_path_last_node(path), link);
ret = coresight_enable_path_from_to(path, coresight_get_mode(source),
from, to);
if (ret)
goto path_failed;
coresight_pm_device_restore(source);
return;
path_failed:
coresight_pm_device_save(sink);
pr_err("Failed in coresight PM restore on CPU%d: %d\n",
smp_processor_id(), ret);
/*
* Once PM fails on a CPU, set percpu_pm_failed and leave it set until
* reboot. This prevents repeated partial transitions during idle
* entry and exit.
*/
this_cpu_write(percpu_pm_failed, true);
}
static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
void *v)
{
struct coresight_path *path =
coresight_cpu_get_active_path(CS_MODE_SYSFS | CS_MODE_PERF);
int ret;
ret = coresight_pm_is_needed(path);
if (ret <= 0)
return ret ? NOTIFY_BAD : NOTIFY_DONE;
switch (cmd) {
case CPU_PM_ENTER:
if (coresight_pm_save(path))
return NOTIFY_BAD;
break;
case CPU_PM_EXIT:
case CPU_PM_ENTER_FAILED:
coresight_pm_restore(path);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block coresight_cpu_pm_nb = {
.notifier_call = coresight_cpu_pm_notify,
};
static int coresight_dying_cpu(unsigned int cpu)
{
struct coresight_path *path;
/*
* The perf event layer will disable PMU events in the CPU
* hotplug. Here only handles SYSFS case.
*/
path = coresight_cpu_get_active_path(CS_MODE_SYSFS);
if (!path)
return 0;
coresight_disable_sysfs(coresight_get_source(path));
return 0;
}
static int __init coresight_pm_setup(void)
{
int ret;
ret = cpu_pm_register_notifier(&coresight_cpu_pm_nb);
if (ret)
return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE,
"arm/coresight-core:dying",
NULL, coresight_dying_cpu);
if (ret)
cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
return ret;
}
static void coresight_pm_cleanup(void)
{
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE);
cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
}
const struct bus_type coresight_bustype = {
.name = "coresight",
};
@ -1669,9 +2106,15 @@ static int __init coresight_init(void)
/* initialise the coresight syscfg API */
ret = cscfg_init();
if (ret)
goto exit_notifier;
ret = coresight_pm_setup();
if (!ret)
return 0;
cscfg_exit();
exit_notifier:
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
exit_perf:
@ -1683,6 +2126,7 @@ static int __init coresight_init(void)
static void __exit coresight_exit(void)
{
coresight_pm_cleanup();
cscfg_exit();
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
@ -1728,10 +2172,10 @@ int coresight_etm_get_trace_id(struct coresight_device *csdev, enum cs_mode mode
{
int cpu, trace_id;
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE || !source_ops(csdev)->cpu_id)
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
return -EINVAL;
cpu = source_ops(csdev)->cpu_id(csdev);
cpu = csdev->cpu;
switch (mode) {
case CS_MODE_SYSFS:
trace_id = coresight_trace_id_get_cpu_id(cpu);

View File

@ -659,7 +659,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = NULL;
struct coresight_desc cti_desc;
struct coresight_desc cti_desc = { 0 };
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
@ -702,11 +702,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
* eCPU ID. System CTIs will have the name cti_sys<I> where I is an
* index allocated by order of discovery.
*/
if (drvdata->ctidev.cpu >= 0)
if (drvdata->ctidev.cpu >= 0) {
cti_desc.cpu = drvdata->ctidev.cpu;
cti_desc.flags = CORESIGHT_DESC_CPU_BOUND;
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu);
else
} else {
cti_desc.name = coresight_alloc_device_name("cti_sys", dev);
}
if (!cti_desc.name)
return -ENOMEM;

View File

@ -329,6 +329,7 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
if (!tg)
return -ENOMEM;
tg->nr_sigs = nr_filter_sigs;
err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
if (!err)
drvdata->config.trig_out_filter |= tg->used_mask;

View File

@ -83,7 +83,7 @@ struct etb_drvdata {
struct coresight_device *csdev;
struct miscdevice miscdev;
raw_spinlock_t spinlock;
local_t reading;
atomic_t reading;
pid_t pid;
u8 *buf;
u32 buffer_depth;
@ -601,7 +601,7 @@ static int etb_open(struct inode *inode, struct file *file)
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
if (local_cmpxchg(&drvdata->reading, 0, 1))
if (atomic_cmpxchg(&drvdata->reading, 0, 1))
return -EBUSY;
dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__);
@ -639,7 +639,7 @@ static int etb_release(struct inode *inode, struct file *file)
{
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
local_set(&drvdata->reading, 0);
atomic_set(&drvdata->reading, 0);
dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__);
return 0;

View File

@ -49,7 +49,6 @@ struct etm_ctxt {
};
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
GEN_PMU_FORMAT_ATTR(cycacc);
GEN_PMU_FORMAT_ATTR(timestamp);
@ -315,6 +314,115 @@ static bool sinks_compatible(struct coresight_device *a,
(sink_ops(a) == sink_ops(b));
}
/*
* This helper is used for fetching the path pointer via the ctxt.
*
* Perf event callbacks run on the same CPU in atomic context, but AUX pause
* and resume may run in NMI context and preempt other callbacks. Since the
* event stop callback clears ctxt->event_data before the data is released,
* AUX pause/resume will either observe a NULL pointer and stop fetching the
* path pointer, or safely access event_data and the path, as the data has
* not yet been freed.
*/
static struct coresight_path *etm_event_get_ctxt_path(struct etm_ctxt *ctxt)
{
struct etm_event_data *event_data;
struct coresight_path *path;
if (!ctxt)
return NULL;
event_data = READ_ONCE(ctxt->event_data);
if (!event_data)
return NULL;
path = etm_event_cpu_path(event_data, smp_processor_id());
if (!path)
return NULL;
return path;
}
static struct coresight_path *
etm_event_build_path(struct perf_event *event, int cpu,
struct coresight_device *user_sink,
struct coresight_device *match_sink)
{
struct coresight_path *path = NULL;
struct coresight_device *source, *sink;
int ret;
source = coresight_get_percpu_source_ref(cpu);
/*
* If there is no ETM associated with this CPU or ever we try to trace
* on this CPU, we handle it accordingly.
*/
if (!source)
return NULL;
/*
* If AUX pause feature is enabled but the ETM driver does not
* support the operations, skip for this source.
*/
if (event->attr.aux_start_paused &&
(!source_ops(source)->pause_perf ||
!source_ops(source)->resume_perf)) {
dev_err_once(&source->dev, "AUX pause is not supported.\n");
goto out;
}
/* If sink has been specified by user, directly use it */
if (user_sink) {
sink = user_sink;
} else {
/*
* No sink provided - look for a default sink for all the ETMs,
* where this event can be scheduled.
*
* We allocate the sink specific buffers only once for this
* event. If the ETMs have different default sink devices, we
* can only use a single "type" of sink as the event can carry
* only one sink specific buffer. Thus we have to make sure
* that the sinks are of the same type and driven by the same
* driver, as the one we allocate the buffer for. We don't
* trace on a CPU if the sink is not compatible.
*/
/* Find the default sink for this ETM */
sink = coresight_find_default_sink(source);
if (!sink)
goto out;
/* Check if this sink compatible with the last sink */
if (match_sink && !sinks_compatible(match_sink, sink))
goto out;
}
/*
* Building a path doesn't enable it, it simply builds a
* list of devices from source to sink that can be
* referenced later when the path is actually needed.
*/
path = coresight_build_path(source, sink);
if (IS_ERR(path))
goto out;
/* ensure we can allocate a trace ID for this CPU */
ret = coresight_path_assign_trace_id(path, CS_MODE_PERF);
if (ret) {
coresight_release_path(path);
path = NULL;
goto out;
}
coresight_trace_id_perf_start(&sink->perf_sink_id_map);
out:
coresight_put_percpu_source_ref(source);
return IS_ERR_OR_NULL(path) ? NULL : path;
}
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
@ -322,7 +430,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
struct coresight_device *user_sink = NULL, *last_sink = NULL;
struct coresight_device *user_sink = NULL;
struct etm_event_data *event_data = NULL;
event_data = alloc_event_data(cpu);
@ -354,80 +462,25 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
*/
for_each_cpu(cpu, mask) {
struct coresight_path *path;
struct coresight_device *csdev;
csdev = per_cpu(csdev_src, cpu);
/*
* If there is no ETM associated with this CPU clear it from
* the mask and continue with the rest. If ever we try to trace
* on this CPU, we handle it accordingly.
*/
if (!csdev) {
path = etm_event_build_path(event, cpu, user_sink, sink);
if (!path) {
/*
* Failed to create a path for the CPU, clear it from
* the mask and continue to next one.
*/
cpumask_clear_cpu(cpu, mask);
continue;
}
/*
* If AUX pause feature is enabled but the ETM driver does not
* support the operations, clear this CPU from the mask and
* continue to next one.
* The first found sink is saved here and passed to
* etm_event_build_path() to check whether the remaining ETMs
* have a compatible default sink.
*/
if (event->attr.aux_start_paused &&
(!source_ops(csdev)->pause_perf || !source_ops(csdev)->resume_perf)) {
dev_err_once(&csdev->dev, "AUX pause is not supported.\n");
cpumask_clear_cpu(cpu, mask);
continue;
}
if (!user_sink && !sink)
sink = coresight_get_sink(path);
/*
* No sink provided - look for a default sink for all the ETMs,
* where this event can be scheduled.
* We allocate the sink specific buffers only once for this
* event. If the ETMs have different default sink devices, we
* can only use a single "type" of sink as the event can carry
* only one sink specific buffer. Thus we have to make sure
* that the sinks are of the same type and driven by the same
* driver, as the one we allocate the buffer for. As such
* we choose the first sink and check if the remaining ETMs
* have a compatible default sink. We don't trace on a CPU
* if the sink is not compatible.
*/
if (!user_sink) {
/* Find the default sink for this ETM */
sink = coresight_find_default_sink(csdev);
if (!sink) {
cpumask_clear_cpu(cpu, mask);
continue;
}
/* Check if this sink compatible with the last sink */
if (last_sink && !sinks_compatible(last_sink, sink)) {
cpumask_clear_cpu(cpu, mask);
continue;
}
last_sink = sink;
}
/*
* Building a path doesn't enable it, it simply builds a
* list of devices from source to sink that can be
* referenced later when the path is actually needed.
*/
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
cpumask_clear_cpu(cpu, mask);
continue;
}
/* ensure we can allocate a trace ID for this CPU */
coresight_path_assign_trace_id(path, CS_MODE_PERF);
if (!IS_VALID_CS_TRACE_ID(path->trace_id)) {
cpumask_clear_cpu(cpu, mask);
coresight_release_path(path);
continue;
}
coresight_trace_id_perf_start(&sink->perf_sink_id_map);
*etm_event_cpu_path_ptr(event_data, cpu) = path;
}
@ -464,13 +517,23 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
goto out;
}
static int etm_event_resume(struct coresight_device *csdev,
struct etm_ctxt *ctxt)
static int etm_event_resume(struct coresight_path *path)
{
if (!ctxt->event_data)
struct coresight_device *source;
int ret;
if (!path)
return 0;
return coresight_resume_source(csdev);
source = coresight_get_source(path);
if (!source)
return 0;
ret = coresight_resume_source(source);
if (ret < 0)
dev_err(&source->dev, "Failed to resume ETM event.\n");
return ret;
}
static void etm_event_start(struct perf_event *event, int flags)
@ -479,23 +542,19 @@ static void etm_event_start(struct perf_event *event, int flags)
struct etm_event_data *event_data;
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
struct perf_output_handle *handle = &ctxt->handle;
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
struct coresight_device *source, *sink;
struct coresight_path *path;
u64 hw_id;
if (!csdev)
goto fail;
if (flags & PERF_EF_RESUME) {
if (etm_event_resume(csdev, ctxt) < 0) {
dev_err(&csdev->dev, "Failed to resume ETM event.\n");
path = etm_event_get_ctxt_path(ctxt);
if (etm_event_resume(path) < 0)
goto fail;
}
return;
}
/* Have we messed up our tracking ? */
if (WARN_ON(ctxt->event_data))
if (WARN_ON(READ_ONCE(ctxt->event_data)))
goto fail;
/*
@ -523,9 +582,10 @@ static void etm_event_start(struct perf_event *event, int flags)
path = etm_event_cpu_path(event_data, cpu);
path->handle = handle;
/* We need a sink, no need to continue without one */
/* We need source and sink, no need to continue if any is not set */
source = coresight_get_source(path);
sink = coresight_get_sink(path);
if (WARN_ON_ONCE(!sink))
if (WARN_ON_ONCE(!source || !sink))
goto fail_end_stop;
/* Nothing will happen without a path */
@ -533,7 +593,7 @@ static void etm_event_start(struct perf_event *event, int flags)
goto fail_end_stop;
/* Finally enable the tracer */
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path))
if (coresight_enable_source(source, event, CS_MODE_PERF, path))
goto fail_disable_path;
/*
@ -557,7 +617,7 @@ static void etm_event_start(struct perf_event *event, int flags)
/* Tell the perf core the event is alive */
event->hw.state = 0;
/* Save the event_data for this ETM */
ctxt->event_data = event_data;
WRITE_ONCE(ctxt->event_data, event_data);
return;
fail_disable_path:
@ -577,26 +637,25 @@ static void etm_event_start(struct perf_event *event, int flags)
return;
}
static void etm_event_pause(struct perf_event *event,
struct coresight_device *csdev,
static void etm_event_pause(struct coresight_path *path,
struct perf_event *event,
struct etm_ctxt *ctxt)
{
int cpu = smp_processor_id();
struct coresight_device *sink;
struct perf_output_handle *handle = &ctxt->handle;
struct coresight_path *path;
struct coresight_device *source, *sink;
struct etm_event_data *event_data;
unsigned long size;
if (!ctxt->event_data)
if (!path)
return;
source = coresight_get_source(path);
sink = coresight_get_sink(path);
if (WARN_ON_ONCE(!source || !sink))
return;
/* Stop tracer */
coresight_pause_source(csdev);
path = etm_event_cpu_path(ctxt->event_data, cpu);
sink = coresight_get_sink(path);
if (WARN_ON_ONCE(!sink))
return;
coresight_pause_source(source);
/*
* The per CPU sink has own interrupt handling, it might have
@ -613,8 +672,9 @@ static void etm_event_pause(struct perf_event *event,
if (!sink_ops(sink)->update_buffer)
return;
event_data = READ_ONCE(ctxt->event_data);
size = sink_ops(sink)->update_buffer(sink, handle,
ctxt->event_data->snk_config);
event_data->snk_config);
if (READ_ONCE(handle->event)) {
if (!size)
return;
@ -630,14 +690,14 @@ static void etm_event_stop(struct perf_event *event, int mode)
{
int cpu = smp_processor_id();
unsigned long size;
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
struct coresight_device *source, *sink;
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
struct perf_output_handle *handle = &ctxt->handle;
struct coresight_path *path = etm_event_get_ctxt_path(ctxt);
struct etm_event_data *event_data;
struct coresight_path *path;
if (mode & PERF_EF_PAUSE)
return etm_event_pause(event, csdev, ctxt);
return etm_event_pause(path, event, ctxt);
/*
* If we still have access to the event_data via handle,
@ -647,9 +707,9 @@ static void etm_event_stop(struct perf_event *event, int mode)
WARN_ON(perf_get_aux(handle) != ctxt->event_data))
return;
event_data = ctxt->event_data;
event_data = READ_ONCE(ctxt->event_data);
/* Clear the event_data as this ETM is stopping the trace. */
ctxt->event_data = NULL;
WRITE_ONCE(ctxt->event_data, NULL);
if (event->hw.state == PERF_HES_STOPPED)
return;
@ -671,19 +731,13 @@ static void etm_event_stop(struct perf_event *event, int mode)
return;
}
if (!csdev)
return;
path = etm_event_cpu_path(event_data, cpu);
if (!path)
return;
source = coresight_get_source(path);
sink = coresight_get_sink(path);
if (!sink)
if (!source || !sink)
return;
/* stop tracer */
coresight_disable_source(csdev, event);
coresight_disable_source(source, event);
/* tell the core */
event->hw.state = PERF_HES_STOPPED;
@ -824,7 +878,7 @@ static void etm_addr_filters_sync(struct perf_event *event)
int etm_perf_symlink(struct coresight_device *csdev, bool link)
{
char entry[sizeof("cpu9999999")];
int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev);
int ret = 0, cpu = csdev->cpu;
struct device *pmu_dev = etm_pmu.dev;
struct device *cs_dev = &csdev->dev;
@ -833,17 +887,12 @@ int etm_perf_symlink(struct coresight_device *csdev, bool link)
if (!etm_perf_up)
return -EPROBE_DEFER;
if (link) {
if (link)
ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry);
if (ret)
return ret;
per_cpu(csdev_src, cpu) = csdev;
} else {
else
sysfs_remove_link(&pmu_dev->kobj, entry);
per_cpu(csdev_src, cpu) = NULL;
}
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(etm_perf_symlink);

View File

@ -441,6 +441,7 @@ static int etm_enable_hw(struct etm_drvdata *drvdata)
struct etm_enable_arg {
struct etm_drvdata *drvdata;
struct coresight_path *path;
int rc;
};
@ -462,15 +463,12 @@ static void etm_enable_sysfs_smp_call(void *info)
arg->rc = etm_enable_hw(arg->drvdata);
/* The tracer didn't start */
if (arg->rc)
if (arg->rc) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
return;
}
static int etm_cpu_id(struct coresight_device *csdev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return drvdata->cpu;
csdev->path = arg->path;
}
void etm_release_trace_id(struct etm_drvdata *drvdata)
@ -499,10 +497,13 @@ static int etm_enable_perf(struct coresight_device *csdev,
ret = etm_enable_hw(drvdata);
/* Failed to start tracer; roll back to DISABLED mode */
if (ret)
if (ret) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
return ret;
}
return ret;
csdev->path = path;
return 0;
}
static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@ -521,6 +522,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_pat
*/
if (cpu_online(drvdata->cpu)) {
arg.drvdata = drvdata;
arg.path = path;
ret = smp_call_function_single(drvdata->cpu,
etm_enable_sysfs_smp_call, &arg, 1);
if (!ret)
@ -590,6 +592,7 @@ static void etm_disable_sysfs_smp_call(void *info)
etm_disable_hw(drvdata);
drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
}
@ -614,6 +617,7 @@ static void etm_disable_perf(struct coresight_device *csdev)
CS_LOCK(drvdata->csa.base);
drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
@ -627,13 +631,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* Taking hotplug lock here protects from clocks getting disabled
* with tracing being left on (crash scenario) if user disable occurs
* after cpu online mask indicates the cpu is offline but before the
* DYING hotplug callback is serviced by the ETM driver.
*/
cpus_read_lock();
spin_lock(&drvdata->spinlock);
/*
@ -644,7 +641,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
drvdata, 1);
spin_unlock(&drvdata->spinlock);
cpus_read_unlock();
/*
* we only release trace IDs when resetting sysfs.
@ -684,7 +680,6 @@ static void etm_disable(struct coresight_device *csdev,
}
static const struct coresight_ops_source etm_source_ops = {
.cpu_id = etm_cpu_id,
.enable = etm_enable,
.disable = etm_disable,
};
@ -704,35 +699,6 @@ static int etm_online_cpu(unsigned int cpu)
return 0;
}
static int etm_starting_cpu(unsigned int cpu)
{
if (!etmdrvdata[cpu])
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (!etmdrvdata[cpu]->os_unlock) {
etm_os_unlock(etmdrvdata[cpu]);
etmdrvdata[cpu]->os_unlock = true;
}
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_enable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
}
static int etm_dying_cpu(unsigned int cpu)
{
if (!etmdrvdata[cpu])
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_disable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
}
static bool etm_arch_supported(u8 arch)
{
switch (arch) {
@ -800,13 +766,6 @@ static int __init etm_hp_setup(void)
{
int ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight:starting",
etm_starting_cpu, etm_dying_cpu);
if (ret)
return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight:online",
etm_online_cpu, NULL);
@ -817,15 +776,11 @@ static int __init etm_hp_setup(void)
return 0;
}
/* failed dyn state - remove others */
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
return ret;
}
static void etm_hp_clear(void)
{
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
hp_online = 0;
@ -891,6 +846,8 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
desc.pdata = pdata;
desc.dev = dev;
desc.groups = coresight_etm_groups;
desc.cpu = drvdata->cpu;
desc.flags = CORESIGHT_DESC_CPU_BOUND;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);

View File

@ -56,10 +56,14 @@ MODULE_PARM_DESC(boot_enable, "Enable tracing on boot");
#define PARAM_PM_SAVE_NEVER 1 /* never save any state */
#define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */
/*
* Save option for ETM4. ETE, sysreg ETM4s and ACPI boots ignore this option and
* will always save.
*/
static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE;
module_param(pm_save_enable, int, 0444);
MODULE_PARM_DESC(pm_save_enable,
"Save/restore state on power down: 1 = never, 2 = self-hosted");
"Save/restore state on power down: 1 = never, 2 = self-hosted. MMIO and DT only.");
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
static void etm4_set_default_config(struct etmv4_config *config);
@ -227,13 +231,6 @@ static void etm4_cs_unlock(struct etmv4_drvdata *drvdata,
CS_UNLOCK(csa->base);
}
static int etm4_cpu_id(struct coresight_device *csdev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return drvdata->cpu;
}
void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
{
coresight_trace_id_put_cpu_id(drvdata->cpu);
@ -241,6 +238,7 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
struct etm4_enable_arg {
struct etmv4_drvdata *drvdata;
struct coresight_path *path;
int rc;
};
@ -628,8 +626,12 @@ static void etm4_enable_sysfs_smp_call(void *info)
arg->rc = etm4_enable_hw(arg->drvdata);
/* The tracer didn't start */
if (arg->rc)
if (arg->rc) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
return;
}
csdev->path = arg->path;
}
/*
@ -897,9 +899,13 @@ static int etm4_enable_perf(struct coresight_device *csdev,
out:
/* Failed to start tracer; roll back to DISABLED mode */
if (ret)
if (ret) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
return ret;
return ret;
}
csdev->path = path;
return 0;
}
static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@ -929,6 +935,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
* ensures that register writes occur when cpu is powered.
*/
arg.drvdata = drvdata;
arg.path = path;
ret = smp_call_function_single(drvdata->cpu,
etm4_enable_sysfs_smp_call, &arg, 1);
if (!ret)
@ -1070,6 +1077,7 @@ static void etm4_disable_sysfs_smp_call(void *info)
etm4_disable_hw(drvdata);
drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
}
@ -1099,6 +1107,7 @@ static int etm4_disable_perf(struct coresight_device *csdev,
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
@ -1113,13 +1122,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* Taking hotplug lock here protects from clocks getting disabled
* with tracing being left on (crash scenario) if user disable occurs
* after cpu online mask indicates the cpu is offline but before the
* DYING hotplug callback is serviced by the ETM driver.
*/
cpus_read_lock();
raw_spin_lock(&drvdata->spinlock);
/*
@ -1133,8 +1135,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
cscfg_csdev_disable_active_config(csdev);
cpus_read_unlock();
/*
* we only release trace IDs when resetting sysfs.
* This permits sysfs users to read the trace ID after the trace
@ -1201,18 +1201,12 @@ static void etm4_pause_perf(struct coresight_device *csdev)
}
static const struct coresight_ops_source etm4_source_ops = {
.cpu_id = etm4_cpu_id,
.enable = etm4_enable,
.disable = etm4_disable,
.resume_perf = etm4_resume_perf,
.pause_perf = etm4_pause_perf,
};
static const struct coresight_ops etm4_cs_ops = {
.trace_id = coresight_etm_get_trace_id,
.source_ops = &etm4_source_ops,
};
static bool cpu_supports_sysreg_trace(void)
{
u64 dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1);
@ -1839,44 +1833,19 @@ static int etm4_online_cpu(unsigned int cpu)
return 0;
}
static int etm4_starting_cpu(unsigned int cpu)
static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata)
{
if (!etmdrvdata[cpu])
return 0;
raw_spin_lock(&etmdrvdata[cpu]->spinlock);
if (!etmdrvdata[cpu]->os_unlock)
etm4_os_unlock(etmdrvdata[cpu]);
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_enable_hw(etmdrvdata[cpu]);
raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
return !!drvdata->save_state;
}
static int etm4_dying_cpu(unsigned int cpu)
{
if (!etmdrvdata[cpu])
return 0;
raw_spin_lock(&etmdrvdata[cpu]->spinlock);
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_disable_hw(etmdrvdata[cpu]);
raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
}
static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
static int etm4_cpu_save(struct coresight_device *csdev)
{
int i, ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_save_state *state;
struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa;
struct device *etm_dev;
if (WARN_ON(!csdev))
return -ENODEV;
etm_dev = &csdev->dev;
csa = &csdev->access;
@ -1979,7 +1948,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR0);
if (drvdata->numvmidc > 4)
state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR1);
state->trcvmidcctlr1 = etm4x_read32(csa, TRCVMIDCCTLR1);
state->trcclaimset = etm4x_read32(csa, TRCCLAIMCLR);
@ -2008,31 +1977,13 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
return ret;
}
static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int ret = 0;
if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED)
return 0;
/*
* Save and restore the ETM Trace registers only if
* the ETM is active.
*/
if (coresight_get_mode(drvdata->csdev))
ret = __etm4_cpu_save(drvdata);
return ret;
}
static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
static void etm4_cpu_restore(struct coresight_device *csdev)
{
int i;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_save_state *state = drvdata->save_state;
struct csdev_access *csa = &drvdata->csdev->access;
if (WARN_ON(!drvdata->csdev))
return;
etm4_cs_unlock(drvdata, csa);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
@ -2102,7 +2053,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR0);
if (drvdata->numvmidc > 4)
etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR1);
etm4x_relaxed_write32(csa, state->trcvmidcctlr1, TRCVMIDCCTLR1);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
@ -2125,47 +2076,16 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4_cs_lock(drvdata, csa);
}
static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
{
if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED)
return;
static const struct coresight_ops etm4_cs_ops = {
.trace_id = coresight_etm_get_trace_id,
.source_ops = &etm4_source_ops,
};
if (coresight_get_mode(drvdata->csdev))
__etm4_cpu_restore(drvdata);
}
static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
void *v)
{
struct etmv4_drvdata *drvdata;
unsigned int cpu = smp_processor_id();
if (!etmdrvdata[cpu])
return NOTIFY_OK;
drvdata = etmdrvdata[cpu];
if (WARN_ON_ONCE(drvdata->cpu != cpu))
return NOTIFY_BAD;
switch (cmd) {
case CPU_PM_ENTER:
if (etm4_cpu_save(drvdata))
return NOTIFY_BAD;
break;
case CPU_PM_EXIT:
case CPU_PM_ENTER_FAILED:
etm4_cpu_restore(drvdata);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block etm4_cpu_pm_nb = {
.notifier_call = etm4_cpu_pm_notify,
static const struct coresight_ops etm4_cs_pm_ops = {
.trace_id = coresight_etm_get_trace_id,
.source_ops = &etm4_source_ops,
.pm_save_disable = etm4_cpu_save,
.pm_restore_enable = etm4_cpu_restore,
};
/* Setup PM. Deals with error conditions and counts */
@ -2173,17 +2093,6 @@ static int __init etm4_pm_setup(void)
{
int ret;
ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb);
if (ret)
return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight4:starting",
etm4_starting_cpu, etm4_dying_cpu);
if (ret)
goto unregister_notifier;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight4:online",
etm4_online_cpu, NULL);
@ -2194,24 +2103,28 @@ static int __init etm4_pm_setup(void)
return 0;
}
/* failed dyn state - remove others */
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
unregister_notifier:
cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
return ret;
}
static void etm4_pm_clear(void)
{
cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
hp_online = 0;
}
}
static bool etm4x_always_pm_save(struct device *dev, struct csdev_access *csa)
{
/*
* Only IO mem ETM devices will benefit from skipping PM save and only
* DT has the option to control it, not ACPI. Otherwise system register
* based ETMs and ETEs will always lose context on CPU power down, so
* always save.
*/
return !csa->io_mem || is_acpi_device_node(dev_fwnode(dev));
}
static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
{
int ret;
@ -2221,6 +2134,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
struct coresight_desc desc = { 0 };
u8 major, minor;
char *type_name;
bool pm_save;
if (!drvdata)
return -EINVAL;
@ -2248,6 +2162,21 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
etm4_set_default(&drvdata->config);
if (etm4x_always_pm_save(dev, init_arg->csa))
pm_save = true;
else if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
pm_save = coresight_loses_context_with_cpu(dev);
else
pm_save = pm_save_enable != PARAM_PM_SAVE_NEVER;
if (pm_save) {
drvdata->save_state = devm_kmalloc(dev,
sizeof(struct etmv4_save_state),
GFP_KERNEL);
if (!drvdata->save_state)
return -ENOMEM;
}
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
@ -2256,10 +2185,12 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
desc.ops = &etm4_cs_ops;
desc.ops = etm4_pm_save_needed(drvdata) ? &etm4_cs_pm_ops : &etm4_cs_ops;
desc.pdata = pdata;
desc.dev = dev;
desc.groups = coresight_etmv4_groups;
desc.cpu = drvdata->cpu;
desc.flags = CORESIGHT_DESC_CPU_BOUND;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
@ -2305,17 +2236,6 @@ static int etm4_probe(struct device *dev)
if (ret)
return ret;
if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
pm_save_enable = coresight_loses_context_with_cpu(dev) ?
PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
drvdata->save_state = devm_kmalloc(dev,
sizeof(struct etmv4_save_state), GFP_KERNEL);
if (!drvdata->save_state)
return -ENOMEM;
}
raw_spin_lock_init(&drvdata->spinlock);
drvdata->cpu = coresight_get_cpu(dev);

View File

@ -45,9 +45,8 @@ coresight_add_out_conn(struct device *dev,
}
}
pdata->nr_outconns++;
pdata->out_conns =
devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns,
devm_krealloc_array(dev, pdata->out_conns, pdata->nr_outconns + 1,
sizeof(*pdata->out_conns), GFP_KERNEL);
if (!pdata->out_conns)
return ERR_PTR(-ENOMEM);
@ -63,7 +62,8 @@ coresight_add_out_conn(struct device *dev,
* used right away.
*/
*conn = *new_conn;
pdata->out_conns[pdata->nr_outconns - 1] = conn;
pdata->out_conns[pdata->nr_outconns] = conn;
pdata->nr_outconns++;
return conn;
}
EXPORT_SYMBOL_GPL(coresight_add_out_conn);
@ -86,13 +86,13 @@ int coresight_add_in_conn(struct coresight_connection *out_conn)
return 0;
}
pdata->nr_inconns++;
pdata->in_conns =
devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns,
devm_krealloc_array(dev, pdata->in_conns, pdata->nr_inconns + 1,
sizeof(*pdata->in_conns), GFP_KERNEL);
if (!pdata->in_conns)
return -ENOMEM;
pdata->in_conns[pdata->nr_inconns - 1] = out_conn;
pdata->in_conns[pdata->nr_inconns] = out_conn;
pdata->nr_inconns++;
return 0;
}
EXPORT_SYMBOL_GPL(coresight_add_in_conn);

View File

@ -153,7 +153,7 @@ int coresight_make_links(struct coresight_device *orig,
void coresight_remove_links(struct coresight_device *orig,
struct coresight_connection *conn);
u32 coresight_get_sink_id(struct coresight_device *csdev);
void coresight_path_assign_trace_id(struct coresight_path *path,
int coresight_path_assign_trace_id(struct coresight_path *path,
enum cs_mode mode);
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X)
@ -248,6 +248,12 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
struct coresight_device *coresight_get_source(struct coresight_path *path);
struct coresight_device *coresight_get_percpu_source_ref(int cpu);
void coresight_put_percpu_source_ref(struct coresight_device *csdev);
int coresight_enable_source(struct coresight_device *csdev,
struct perf_event *event, enum cs_mode mode,
struct coresight_path *path);
void coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_pause_source(struct coresight_device *csdev);
int coresight_resume_source(struct coresight_device *csdev);

View File

@ -953,39 +953,41 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti
unsigned long cfg_hash;
int err = 0;
mutex_lock(&cscfg_mutex);
guard(mutex)(&cscfg_mutex);
cfg_hash = (unsigned long)config_desc->event_ea->var;
if (activate) {
/* cannot be a current active value to activate this */
if (cscfg_mgr->sysfs_active_config) {
err = -EBUSY;
goto exit_unlock;
}
err = _cscfg_activate_config(cfg_hash);
if (!err)
if (cscfg_mgr->sysfs_active_config)
return -EBUSY;
scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) {
err = _cscfg_activate_config(cfg_hash);
if (err)
return err;
cscfg_mgr->sysfs_active_config = cfg_hash;
}
} else {
/* disable if matching current value */
if (cscfg_mgr->sysfs_active_config == cfg_hash) {
if (cscfg_mgr->sysfs_active_config != cfg_hash)
return -EINVAL;
scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) {
/* disable if matching current value */
_cscfg_deactivate_config(cfg_hash);
cscfg_mgr->sysfs_active_config = 0;
} else
err = -EINVAL;
}
}
exit_unlock:
mutex_unlock(&cscfg_mutex);
return err;
return 0;
}
/* set the sysfs preset value */
void cscfg_config_sysfs_set_preset(int preset)
{
mutex_lock(&cscfg_mutex);
guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock);
cscfg_mgr->sysfs_active_preset = preset;
mutex_unlock(&cscfg_mutex);
}
/*
@ -994,10 +996,9 @@ void cscfg_config_sysfs_set_preset(int preset)
*/
void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset)
{
mutex_lock(&cscfg_mutex);
guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock);
*preset = cscfg_mgr->sysfs_active_preset;
*cfg_hash = cscfg_mgr->sysfs_active_config;
mutex_unlock(&cscfg_mutex);
}
EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg);
@ -1201,6 +1202,7 @@ static int cscfg_create_device(void)
INIT_LIST_HEAD(&cscfg_mgr->load_order_list);
atomic_set(&cscfg_mgr->sys_active_cnt, 0);
cscfg_mgr->load_state = CSCFG_NONE;
raw_spin_lock_init(&cscfg_mgr->sysfs_store_lock);
/* setup the device */
dev = cscfg_device();

View File

@ -42,6 +42,7 @@ enum cscfg_load_ops {
* @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs.
* @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs.
* @load_state: A multi-stage load/unload operation is in progress.
* @sysfs_store_lock: Exclusive access sysfs stored variables.
*/
struct cscfg_manager {
struct device dev;
@ -54,6 +55,7 @@ struct cscfg_manager {
u32 sysfs_active_config;
int sysfs_active_preset;
enum cscfg_load_ops load_state;
raw_spinlock_t sysfs_store_lock;
};
/* get reference to dev in cscfg_manager */

View File

@ -5,26 +5,12 @@
*/
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/property.h>
#include "coresight-priv.h"
#include "coresight-trace-id.h"
/*
* Use IDR to map the hash of the source's device name
* to the pointer of path for the source. The idr is for
* the sources which aren't associated with CPU.
*/
static DEFINE_IDR(path_idr);
/*
* When operating Coresight drivers from the sysFS interface, only a single
* path can exist from a tracer (associated to a CPU) to a sink.
*/
static DEFINE_PER_CPU(struct coresight_path *, tracer_path);
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
@ -53,6 +39,26 @@ ssize_t coresight_simple_show32(struct device *_dev,
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
static void coresight_source_get_refcnt(struct coresight_device *csdev)
{
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*
* No need to increment the reference counter for other source
* types, as multiple enables are the same as a single enable.
*/
if (coresight_is_software_source(csdev))
csdev->refcnt++;
}
static void coresight_source_put_refcnt(struct coresight_device *csdev)
{
if (coresight_is_software_source(csdev))
csdev->refcnt--;
}
static int coresight_enable_source_sysfs(struct coresight_device *csdev,
enum cs_mode mode,
struct coresight_path *path)
@ -66,19 +72,19 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev,
*/
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
ret = source_ops(csdev)->enable(csdev, NULL, mode, path);
ret = coresight_enable_source(csdev, NULL, mode, path);
if (ret)
return ret;
}
csdev->refcnt++;
coresight_source_get_refcnt(csdev);
return 0;
}
/**
* coresight_disable_source_sysfs - Drop the reference count by 1 and disable
* the device if there are no users left.
* coresight_disable_source_sysfs - Drop the reference count by 1 for software
* sources. Disable the device if there are no users left.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
@ -93,7 +99,7 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
return false;
csdev->refcnt--;
coresight_source_put_refcnt(csdev);
if (csdev->refcnt == 0) {
coresight_disable_source(csdev, data);
return true;
@ -162,18 +168,17 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev,
return -EINVAL;
}
if (coresight_is_percpu_source(csdev) && !cpu_online(csdev->cpu))
return -ENODEV;
return 0;
}
int coresight_enable_sysfs(struct coresight_device *csdev)
{
int cpu, ret = 0;
int ret = 0;
struct coresight_device *sink;
struct coresight_path *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
@ -188,13 +193,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
* doesn't hold coresight_mutex.
*/
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
csdev->refcnt++;
coresight_source_get_refcnt(csdev);
goto out;
}
@ -211,8 +210,8 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
goto out;
}
coresight_path_assign_trace_id(path, CS_MODE_SYSFS);
if (!IS_VALID_CS_TRACE_ID(path->trace_id))
ret = coresight_path_assign_trace_id(path, CS_MODE_SYSFS);
if (ret)
goto err_path;
ret = coresight_enable_path(path, CS_MODE_SYSFS);
@ -223,35 +222,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
if (ret)
goto err_source;
switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
* and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
if (ret)
goto err_source;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
return ret;
@ -267,9 +237,8 @@ EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
void coresight_disable_sysfs(struct coresight_device *csdev)
{
int cpu, ret;
struct coresight_path *path = NULL;
u32 hash;
struct coresight_path *path;
int ret;
mutex_lock(&coresight_mutex);
@ -277,32 +246,15 @@ void coresight_disable_sysfs(struct coresight_device *csdev)
if (ret)
goto out;
/*
* coresight_disable_source_sysfs() clears the 'csdev->path' pointer
* when disabling the source. Retrieve the path pointer here.
*/
path = csdev->path;
if (!coresight_disable_source_sysfs(csdev, NULL))
goto out;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */
path = idr_find(&path_idr, hash);
if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
goto out;
}
idr_remove(&path_idr, hash);
break;
default:
/* We can't be here */
break;
}
coresight_disable_path(path);
coresight_release_path(path);
@ -360,6 +312,13 @@ static ssize_t enable_source_store(struct device *dev,
if (ret)
return ret;
/*
* CoreSight hotplug callbacks in core layer control a activated path
* from its source to sink. Taking hotplug lock here protects a race
* condition with hotplug callbacks.
*/
guard(cpus_read_lock)();
if (val) {
ret = coresight_enable_sysfs(csdev);
if (ret)

View File

@ -154,7 +154,7 @@ tmc_pages_get_offset(struct tmc_pages *tmc_pages, dma_addr_t addr)
for (i = 0; i < tmc_pages->nr_pages; i++) {
page_start = tmc_pages->daddrs[i];
if (addr >= page_start && addr < (page_start + PAGE_SIZE))
return i * PAGE_SIZE + (addr - page_start);
return (long)i * PAGE_SIZE + (addr - page_start);
}
return -EINVAL;
@ -1379,7 +1379,7 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event,
node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu);
/* Use the minimum limit if the required size is smaller */
size = nr_pages << PAGE_SHIFT;
size = (ssize_t)nr_pages << PAGE_SHIFT;
size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE);
/*

View File

@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = {
*/
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
/*
* struct trbe_save_state: Register values representing TRBE state
* @trblimitr - Trace Buffer Limit Address Register value
* @trbbaser - Trace Buffer Base Register value
* @trbptr - Trace Buffer Write Pointer Register value
* @trbsr - Trace Buffer Status Register value
*/
struct trbe_save_state {
u64 trblimitr;
u64 trbbaser;
u64 trbptr;
u64 trbsr;
};
/*
* struct trbe_cpudata: TRBE instance specific data
* @trbe_flag - TRBE dirty/access flag support
@ -134,6 +148,7 @@ struct trbe_cpudata {
enum cs_mode mode;
struct trbe_buf *buf;
struct trbe_drvdata *drvdata;
struct trbe_save_state save_state;
DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
};
@ -1189,6 +1204,46 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
return IRQ_HANDLED;
}
static int arm_trbe_save(struct coresight_device *csdev)
{
struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
struct trbe_save_state *state = &cpudata->save_state;
state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
/* Disable the unit, ensure the writes to memory are complete */
if (state->trblimitr & TRBLIMITR_EL1_E)
trbe_drain_and_disable_local(cpudata);
state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1);
state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1);
state->trbsr = read_sysreg_s(SYS_TRBSR_EL1);
return 0;
}
static void arm_trbe_restore(struct coresight_device *csdev)
{
struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
struct trbe_save_state *state = &cpudata->save_state;
write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1);
write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1);
write_sysreg_s(state->trbsr, SYS_TRBSR_EL1);
if (!(state->trblimitr & TRBLIMITR_EL1_E)) {
write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1);
} else {
/*
* The section K5.5 Context switching, Arm ARM (ARM DDI 0487
* L.a), S_PKLXF requires a Context synchronization event to
* guarantee the Trace Buffer Unit will observe the new values
* of the system registers.
*/
isb();
set_trbe_enabled(cpudata, state->trblimitr);
}
}
static const struct coresight_ops_sink arm_trbe_sink_ops = {
.enable = arm_trbe_enable,
.disable = arm_trbe_disable,
@ -1198,7 +1253,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = {
};
static const struct coresight_ops arm_trbe_cs_ops = {
.sink_ops = &arm_trbe_sink_ops,
.pm_save_disable = arm_trbe_save,
.pm_restore_enable = arm_trbe_restore,
.sink_ops = &arm_trbe_sink_ops,
};
static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf)
@ -1289,6 +1346,8 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp
desc.ops = &arm_trbe_cs_ops;
desc.groups = arm_trbe_groups;
desc.dev = dev;
desc.cpu = cpu;
desc.flags = CORESIGHT_DESC_CPU_BOUND;
trbe_csdev = coresight_register(&desc);
if (IS_ERR(trbe_csdev))
goto cpu_clear;

View File

@ -337,6 +337,7 @@ static void smb_sync_perf_buffer(struct smb_drv_data *drvdata,
unsigned long to_copy;
long pg_idx, pg_offset;
head %= (unsigned long)buf->nr_pages << PAGE_SHIFT;
pg_idx = head >> PAGE_SHIFT;
pg_offset = head & (PAGE_SIZE - 1);

View File

@ -141,6 +141,8 @@ struct csdev_access {
.base = (_addr), \
})
#define CORESIGHT_DESC_CPU_BOUND BIT(0)
/**
* struct coresight_desc - description of a component required from drivers
* @type: as defined by @coresight_dev_type.
@ -153,6 +155,8 @@ struct csdev_access {
* in the component's sysfs sub-directory.
* @name: name for the coresight device, also shown under sysfs.
* @access: Describe access to the device
* @flags: The descritpion flags.
* @cpu: The CPU this component is affined to.
*/
struct coresight_desc {
enum coresight_dev_type type;
@ -163,6 +167,8 @@ struct coresight_desc {
const struct attribute_group **groups;
const char *name;
struct csdev_access access;
u32 flags;
int cpu;
};
/**
@ -251,6 +257,7 @@ struct coresight_trace_id_map {
* by @coresight_ops.
* @access: Device i/o access abstraction for this device.
* @dev: The device entity associated to this component.
* @path: Activated path pointer (only used for source device).
* @mode: The device mode, i.e sysFS, Perf or disabled. This is actually
* an 'enum cs_mode' but stored in an atomic type. Access is always
* through atomic APIs, ensuring SMP-safe synchronisation between
@ -260,6 +267,7 @@ struct coresight_trace_id_map {
* device's spinlock when the coresight_mutex held and mode ==
* CS_MODE_SYSFS. Otherwise it must be accessed from inside the
* spinlock.
* @cpu: The CPU this component is affined to (-1 for not CPU bound).
* @orphan: true if the component has connections that haven't been linked.
* @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs
* by writing a 1 to the 'enable_sink' file. A sink can be
@ -284,8 +292,10 @@ struct coresight_device {
const struct coresight_ops *ops;
struct csdev_access access;
struct device dev;
struct coresight_path *path;
atomic_t mode;
int refcnt;
int cpu;
bool orphan;
/* sink specific fields */
bool sysfs_sink_activated;
@ -334,9 +344,9 @@ struct coresight_path {
};
enum cs_mode {
CS_MODE_DISABLED,
CS_MODE_SYSFS,
CS_MODE_PERF,
CS_MODE_DISABLED = 0,
CS_MODE_SYSFS = BIT(0),
CS_MODE_PERF = BIT(1),
};
#define coresight_ops(csdev) csdev->ops
@ -387,15 +397,12 @@ struct coresight_ops_link {
/**
* struct coresight_ops_source - basic operations for a source
* Operations available for sources.
* @cpu_id: returns the value of the CPU number this component
* is associated to.
* @enable: enables tracing for a source.
* @disable: disables tracing for a source.
* @resume_perf: resumes tracing for a source in perf session.
* @pause_perf: pauses tracing for a source in perf session.
*/
struct coresight_ops_source {
int (*cpu_id)(struct coresight_device *csdev);
int (*enable)(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode, struct coresight_path *path);
void (*disable)(struct coresight_device *csdev,
@ -433,6 +440,8 @@ struct coresight_ops_panic {
struct coresight_ops {
int (*trace_id)(struct coresight_device *csdev, enum cs_mode mode,
struct coresight_device *sink);
int (*pm_save_disable)(struct coresight_device *csdev);
void (*pm_restore_enable)(struct coresight_device *csdev);
const struct coresight_ops_sink *sink_ops;
const struct coresight_ops_link *link_ops;
const struct coresight_ops_source *source_ops;
@ -602,6 +611,12 @@ static inline bool coresight_is_percpu_source(struct coresight_device *csdev)
(csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_PROC);
}
static inline bool coresight_is_software_source(struct coresight_device *csdev)
{
return csdev && coresight_is_device_source(csdev) &&
(csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE);
}
static inline bool coresight_is_percpu_sink(struct coresight_device *csdev)
{
return csdev && (csdev->type == CORESIGHT_DEV_TYPE_SINK) &&

View File

@ -180,7 +180,6 @@ enum cpuhp_state {
CPUHP_AP_DUMMY_TIMER_STARTING,
CPUHP_AP_ARM_XEN_STARTING,
CPUHP_AP_ARM_XEN_RUNSTATE_STARTING,
CPUHP_AP_ARM_CORESIGHT_STARTING,
CPUHP_AP_ARM_CORESIGHT_CTI_STARTING,
CPUHP_AP_ARM64_ISNDEP_STARTING,
CPUHP_AP_SMPCFD_DYING,
@ -200,6 +199,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_AFFINITY_ONLINE,
CPUHP_AP_BLK_MQ_ONLINE,
CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
CPUHP_AP_ARM_CORESIGHT_ONLINE,
CPUHP_AP_X86_INTEL_EPB_ONLINE,
CPUHP_AP_PERF_ONLINE,
CPUHP_AP_PERF_X86_ONLINE,