[PATCH v1 05/11] coresight: Save activated path into source device
James Clark
james.clark at linaro.org
Tue Jun 24 05:52:07 PDT 2025
On 20/06/2025 3:24 pm, Leo Yan wrote:
> On Tue, Jun 03, 2025 at 03:49:26PM +0100, James Clark wrote:
>
> [...]
>
>>> @@ -577,6 +583,10 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode,
>>> }
>>> }
>>> + source = coresight_get_source(path);
>>> + if (coresight_is_percpu_source(source))
>>> + source->path = path;
>>> +
>>> out:
>>> return ret;
>>> err_disable_helpers:
>>> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
>>> index 64cee4040daa..cd9709a36b9c 100644
>>> --- a/include/linux/coresight.h
>>> +++ b/include/linux/coresight.h
>>> @@ -265,6 +265,7 @@ struct coresight_trace_id_map {
>>> * CS_MODE_SYSFS. Otherwise it must be accessed from inside the
>>> * spinlock.
>>> * @orphan: true if the component has connections that haven't been linked.
>>> + * @path: activated path pointer (only used for source device).
>>> * @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
>>> * activated but not yet enabled. Enabling for a _sink_ happens
>>> @@ -291,6 +292,8 @@ struct coresight_device {
>>> local_t mode;
>>> int refcnt;
>>> bool orphan;
>>> + /* activated path (for source only) */
>>> + struct coresight_path *path;
>>
>> This is probably cleaner if it's saved in the new global per-cpu you added
>> in patch 2. It's even more specific than "for source only", it's actually
>> only for per-cpu sources so it's not worth having it on every device.
>
> We can maintain Coresight path pointers in a global per-CPU data
> structure. These path pointers are set and cleared during path enable
> and disable operations, respectively. Idle threads can then access
> these pointers for power management purposes.
>
> However, the challenge is how we can safely access the path pointer from
> an idle thread - particularly because idle threads cannot acquire
> coresight_mutex, which is a sleepable lock. Consider a race condition
> scenario where SysFS knobs frequently enable and disable paths, while
> the corresponding CPUs enter and exit idle states. In such cases, if
> an idle thread attempts to access the path pointers, we would need to
> use raw spinlocks to ensure safety. This is problematic, as it could
> result in the CPU suspend/resume flow waiting on SysFS operations.
>
> This is why it turns out to use lockless approach. Two key background
> info:
>
> - Path pointers are not guaranteed to be safe to access in CPU power
> management (PM) notifiers. In contrast, the coresight_device pointer
> of a source device (e.g., an ETM) is safe to access.
>
> A path's lifecycle begins and ends with SysFS enable and disable
> operations. OTOH, an ETM’s coresight_device pointer is initialized
> during the probe phase and cleared during module removal. Module
> loading and unloading trigger SMP calls for initialization and
> cleanup. These synchronized SMP calls ensure that it is safe for CPU
> PM notifiers to access the source device structure.
>
> - Another factor is that the device mode is used to determine whether
> it is safe to access the path pointer. When a source device is
> enabled, we must ensure that its state transitions from DISABLED to
> an enabled state on the target CPU. This guarantees that the mode,
> and any subsequent operations dependent on it, follow a strictly
> sequential order.
>
> CPU0 CPU1
> etm4_enable()
> ` etm4_enable_sysfs()
> ` smp_call_function_single() ----> etm4_enable_hw_smp_call()
> ` coresight_take_mode(SYSFS)
>
> CPU idle:
> etm4_cpu_save()
> ` coresight_get_mode()
> ^^^
> Read out the SYSFS mode
> Safely access coresight_device::path
>
> Therefore, safe access to the Coresight path heavily relies on the
> mode (as part of the state machine). This is why the patch adds the
> path pointer to the same structure as the mode.
>
> Thanks,
> Leo
>
I'm not suggesting to change the flow for accessing it, only where it's
stored to not waste space storing it for devices that never need it. It
also makes the code a bit more readable because you know that variable
is never accessed for other device types by the fact that it doesn't exist.
Because we only have 1 per-CPU source for each CPU, there is no
functional difference between storing it in the device or in a global
per-cpu area.
If we imagine that the path pointer saved in the device is actually a
pointer to a pointer, and that pointer happens to point to the per-CPU area,
struct coresight_device {
struct coresight_path **path; ---------------------
} |
|
\ /
static DEFINE_PER_CPU(struct coresight_device *, csdev_cpu_path);
then the argument for safely accessing doesn't really make sense, it's
just a stored somewhere else. Moving it doesn't add or remove any safety
or concurrency issues, it just has a different address than the one in
the struct coresight_device and looks a bit different in the code.
>>> /* sink specific fields */
>>> bool sysfs_sink_activated;
>>> struct dev_ext_attribute *ea;
>>
More information about the linux-arm-kernel
mailing list