struct perf_event *event, enum cs_mode mode,
struct coresight_path *path)
{
+ int ret;
+
if (!coresight_is_device_source(csdev))
return -EINVAL;
- return source_ops(csdev)->enable(csdev, event, mode, path);
+ 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);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
* 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
const struct coresight_ops *ops;
struct csdev_access access;
struct device dev;
+ struct coresight_path *path;
atomic_t mode;
int refcnt;
int cpu;