mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Lukas Zapolskas <lukas.zapolskas@arm.com>
To: Daniel Almeida <daniel.almeida@collabora.com>,
	 Alice Ryhl <aliceryhl@google.com>,
	 Boris Brezillon <boris.brezillon@collabora.com>,
	 Steven Price <steven.price@arm.com>,
	Liviu Dudau <liviu.dudau@arm.com>,
	 David Airlie <airlied@gmail.com>,
	Simona Vetter <simona@ffwll.ch>,
	 Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	 Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>
Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	 nd@arm.com, Lukas Zapolskas <lukas.zapolskas@arm.com>
Subject: [PATCH v7 5/8] drm/panthor: Introduce sampling sessions to handle userspace clients
Date: Sun, 11 Oct 2026 18:36:52 +0100	[thread overview]
Message-ID: <20261011-panthor-performance-counters-v7-5-12c32495062f@arm.com> (raw)
In-Reply-To: <20261011-panthor-performance-counters-v7-0-12c32495062f@arm.com>

To allow for combining the requests from multiple userspace clients,
an intermediary layer between the HW/FW interfaces and userspace is
created, containing the information for the counter requests and
tracking of insert and extract indices. Each session starts inactive
and must be explicitly activated via PERF_SESSION_START and
explicitly stopped via PERF_SESSION_STOP. Userspace identifies a
single client with its session ID and the panthor file it is
associated with.

The SAMPLE and STOP commands both produce a single sample when called,
and these samples can be disambiguated via the opaque user data field
passed in the PERF_SESSION uAPI. If this functionality is not desired,
these fields can be kept as zero, as the kernel copies this value into
the corresponding sample without attempting to interpret it.

Currently, only manual sampling sessions are supported, providing
samples when userspace calls PERF_CONTROL_SAMPLE, and only a single
session is allowed at a time. Multiple sessions and periodic sampling
will be enabled in following patches.

No protection is provided against the 32-bit hardware counter
overflows, so for the moment it is up to userspace to ensure that
the counters are sampled at a reasonable frequency.

Signed-off-by: Lukas Zapolskas <lukas.zapolskas@arm.com>
---
 drivers/gpu/drm/panthor/panthor_perf.c | 657 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/panthor/panthor_perf.h |  11 +
 2 files changed, 664 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_perf.c b/drivers/gpu/drm/panthor/panthor_perf.c
index ba466e22c50d..76c346981150 100644
--- a/drivers/gpu/drm/panthor/panthor_perf.c
+++ b/drivers/gpu/drm/panthor/panthor_perf.c
@@ -2,10 +2,12 @@
 /* Copyright 2023 Collabora Ltd */
 /* Copyright 2025 Arm ltd. */
 
-#include <linux/bitops.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
 #include <drm/drm_managed.h>
 #include <drm/panthor_drm.h>
 #include <drm/drm_print.h>
+#include <linux/bitops.h>
 
 #include "panthor_device.h"
 #include "panthor_fw.h"
@@ -27,14 +29,130 @@
 
 #define MAX_SESSIONS_PER_FILE 1
 
+/**
+ * PANTHOR_PERF_EM_BITS - Number of bits in a user-facing enable mask. This must correspond
+ *                        to the maximum number of counters available for selection on the newest
+ *                        Mali GPUs (128 as of the Mali-Gx15).
+ */
+#define PANTHOR_PERF_EM_BITS (BITS_PER_TYPE(u64) * 2)
+
+enum panthor_perf_session_state {
+	/** @PANTHOR_PERF_SESSION_STOPPED: The session cannot sample. It starts out stopped. */
+	PANTHOR_PERF_SESSION_STOPPED = 0,
+
+	/** @PANTHOR_PERF_SESSION_STARTING: The session has not yet been started and is in the
+	 *				    process of handling the clearing sample.
+	 */
+	PANTHOR_PERF_SESSION_STARTING,
+
+	/** @PANTHOR_PERF_SESSION_ACTIVE: The session is active and can be used for sampling. */
+	PANTHOR_PERF_SESSION_ACTIVE,
+
+	/** @PANTHOR_PERF_SESSION_STOPPING: The session is currently stopping. After emitting the
+	 *				    next sample, it will transition to inactive.
+	 */
+	PANTHOR_PERF_SESSION_STOPPING,
+};
+
+struct panthor_perf_enable_masks {
+	/**
+	 * @mask: Array of bitmasks indicating the counters userspace requested, where
+	 *        one bit represents a single counter. Used to build the firmware configuration
+	 *        and ensure that userspace clients obtain only the counters they requested.
+	 */
+	unsigned long mask[DRM_PANTHOR_PERF_BLOCK_MAX][BITS_TO_LONGS(PANTHOR_PERF_EM_BITS)];
+};
+
 struct panthor_perf_counter_block {
 	struct drm_panthor_perf_block_header header;
 	u64 counters[];
 };
 
+struct panthor_perf_bo {
+	struct drm_gem_object *obj;
+	void *vaddr;
+};
+
+struct panthor_perf_session {
+	/** @state_lock: Used to protect session state information and overflow bit. */
+	struct mutex state_lock;
+
+	/** @state: The current state of the session. */
+	enum panthor_perf_session_state state;
+
+	/** @overflow: The session is affected by a FW ring buffer overflow. */
+	bool overflow;
+
+	/**  @insert_idx: The insert index in the user-visible buffer for the current session. */
+	u64 insert_idx;
+
+	/**
+	 * @accum_idx: Insert index of the populated slot. When this matches @insert_idx,
+	 *             accumulate into that slot; otherwise populate a new slot.
+	 */
+	u64 accum_idx;
+
+	/** @sample_start_ns: Sample request time, obtained from a monotonic raw clock. */
+	u64 sample_start_ns;
+
+	/**
+	 * @user_data: Opaque handle passed in when starting a session, requesting a sample (for
+	 *             manual sampling sessions only) and when stopping a session. This handle
+	 *             allows the disambiguation of a sample in the ringbuffer.
+	 */
+	u64 user_data;
+
+	/**
+	 * @eventfd: Event file descriptor context used to signal userspace of a new sample
+	 *           being emitted.
+	 */
+	struct eventfd_ctx *eventfd;
+
+	/**
+	 * @enabled_counters: This session's requested counters. Note that these cannot change
+	 *                    for the lifetime of the session.
+	 */
+	struct panthor_perf_enable_masks *enabled_counters;
+
+	/** @ringbuf_slots: Slots in the user-facing ringbuffer. */
+	size_t ringbuf_slots;
+
+	/** @ring_buf: BO and mapping for the userspace ringbuffer. */
+	struct panthor_perf_bo ring_buf;
+
+	/**
+	 * @control_buf: BO and mapping for the insert and extract indices.
+	 */
+	struct panthor_perf_bo control_buf;
+
+	/** @control: The mapped insert and extract indices. */
+	struct drm_panthor_perf_ringbuf_control *control;
+
+	/** @samples: The mapping of the @ring_buf into the kernel's VA space. */
+	u8 *samples;
+
+	/**
+	 * @sessions: The list node tracking all registered sessions.
+	 */
+	struct list_head sessions;
+
+	/**
+	 * @ref: Session reference count. The sample delivery to userspace is asynchronous, meaning
+	 *       the lifetime of the session must extend at least until the sample is exposed to
+	 *       userspace.
+	 */
+	struct kref ref;
+};
+
 struct panthor_perf {
 	/** @info: Pointer to the previously initialized perf info. */
 	struct drm_panthor_perf_info *info;
+
+	/**
+	 * @op_lock: Serializes session operations. Acquire before looking up session
+	 *           handles and before a session's state_lock.
+	 */
+	struct mutex op_lock;
 };
 
 struct panthor_perf_pool {
@@ -43,6 +161,9 @@ struct panthor_perf_pool {
 
 	/** @xa: Xarray used to manage session handles. */
 	struct xarray xa;
+
+	/** @next: Next handle to allocate. */
+	u32 next;
 };
 
 static size_t get_annotated_block_size(size_t counters_per_block)
@@ -87,6 +208,163 @@ static void panthor_perf_info_init(struct panthor_device *const ptdev)
 	perf_info->sample_size = session_get_sample_size(perf_info);
 }
 
+static struct panthor_perf_enable_masks *
+panthor_perf_create_em(struct drm_panthor_perf_session_create *const create_args)
+{
+	struct panthor_perf_enable_masks *const em = kmalloc(sizeof(*em), GFP_KERNEL);
+
+	if (IS_ERR_OR_NULL(em))
+		return em;
+
+	bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_FW],
+			  create_args->fw_enable_mask, PANTHOR_PERF_EM_BITS);
+	bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_CSHW],
+			  create_args->cshw_enable_mask, PANTHOR_PERF_EM_BITS);
+	bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_TILER],
+			  create_args->tiler_enable_mask, PANTHOR_PERF_EM_BITS);
+	bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_MEMSYS],
+			  create_args->memsys_enable_mask, PANTHOR_PERF_EM_BITS);
+	bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_SHADER],
+			  create_args->shader_enable_mask, PANTHOR_PERF_EM_BITS);
+
+	return em;
+}
+
+static struct panthor_perf_session *
+session_get(struct panthor_perf_session *session)
+{
+	if (session)
+		kref_get(&session->ref);
+
+	return session;
+}
+
+static int
+panthor_perf_bo_get(struct drm_file *file, u32 handle, size_t expected_size,
+		    struct panthor_perf_bo *bo)
+{
+	struct drm_device *ddev = file->minor->dev;
+	struct drm_gem_object *obj;
+	struct iosys_map map;
+	int ret;
+
+	if (WARN_ON_ONCE(!bo))
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj) {
+		drm_err(ddev, "Could not find handle %d!\n", handle);
+		return -EINVAL;
+	}
+
+	if (expected_size && obj->size != PFN_ALIGN(expected_size)) {
+		drm_err(ddev,
+			"Incorrect BO size from userspace: user %zu vs kernel %lu\n",
+			obj->size, PFN_ALIGN(expected_size));
+		ret = -ENOMEM;
+		goto err_put;
+	}
+
+	ret = drm_gem_vmap(obj, &map);
+	if (ret)
+		goto err_put;
+
+	bo->obj = obj;
+	bo->vaddr = map.vaddr;
+
+	return 0;
+
+err_put:
+	drm_gem_object_put(obj);
+	return ret;
+}
+
+static void panthor_perf_bo_put(struct panthor_perf_bo *bo)
+{
+	if (!bo->obj)
+		return;
+
+	if (bo->vaddr) {
+		struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->vaddr);
+
+		drm_gem_vunmap(bo->obj, &map);
+	}
+
+	drm_gem_object_put(bo->obj);
+
+	memset(bo, 0, sizeof(*bo));
+}
+
+static void session_free(struct kref *ref)
+{
+	struct panthor_perf_session *session = container_of(ref, typeof(*session), ref);
+
+	panthor_perf_bo_put(&session->ring_buf);
+	panthor_perf_bo_put(&session->control_buf);
+
+	kfree(session->enabled_counters);
+
+	eventfd_ctx_put(session->eventfd);
+
+	kfree(session);
+}
+
+static void session_put(struct panthor_perf_session *session)
+{
+	kref_put(&session->ref, session_free);
+}
+
+/**
+ * session_from_handle - Find a session associated with the pool and session_handle.
+ *
+ * @pool: Per-file pool.
+ * @handle: Session handle.
+ *
+ * The reference count of a valid session is increased to ensure it does not disappear
+ * in the window between the XA lock being dropped and the internal session functions
+ * being called.
+ *
+ * Return: valid session pointer or NULL.
+ */
+static struct panthor_perf_session *
+session_from_handle(struct panthor_perf_pool *pool, unsigned long handle)
+{
+	struct panthor_perf_session *session;
+
+	xa_lock(&pool->xa);
+	session = session_get(xa_load(&pool->xa, handle));
+	xa_unlock(&pool->xa);
+
+	return session;
+}
+
+#define DEFINE_SESSION_STATE_CHECK(name, _state)			     \
+	static bool session_is_## name(struct panthor_perf_session *session) \
+	{								     \
+		lockdep_assert_held(&session->state_lock);		     \
+									     \
+		return session->state == (_state);			    \
+	}
+
+DEFINE_SESSION_STATE_CHECK(active, PANTHOR_PERF_SESSION_ACTIVE);
+DEFINE_SESSION_STATE_CHECK(starting, PANTHOR_PERF_SESSION_STARTING);
+DEFINE_SESSION_STATE_CHECK(stopping, PANTHOR_PERF_SESSION_STOPPING);
+
+static bool session_has_overflow(struct panthor_perf_session *session)
+{
+	lockdep_assert_held(&session->state_lock);
+
+	return session->overflow;
+}
+
+static void session_set_state(struct panthor_perf_session *session,
+			      enum panthor_perf_session_state state)
+{
+	lockdep_assert_held(&session->state_lock);
+
+	session->state = state;
+}
+
 /**
  * panthor_perf_init - Initialize the performance counter subsystem.
  * @ptdev: Panthor device
@@ -110,6 +388,10 @@ int panthor_perf_init(struct panthor_device *ptdev)
 	if (!perf)
 		return -ENOMEM;
 
+	ret = drmm_mutex_init(&ptdev->base, &perf->op_lock);
+	if (ret)
+		return ret;
+
 	drm_info(&ptdev->base, "Performance counter subsystem initialized");
 
 	perf->info = &ptdev->perf_info;
@@ -118,6 +400,371 @@ int panthor_perf_init(struct panthor_device *ptdev)
 	return ret;
 }
 
+/**
+ * panthor_perf_session_create - Create a user-visible session.
+ *
+ * @file: DRM file associated with the request.
+ * @create_args: Creation arguments passed in via ioctl.
+ * @sid: Session identifier output.
+ *
+ * Creates a new session associated with the session ID returned. When initialized, the
+ * session must explicitly request sampling to start with a successive call to PERF_SESSION_START.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int panthor_perf_session_create(struct drm_file *file,
+				struct drm_panthor_perf_session_create *create_args,
+				unsigned long *sid)
+{
+	struct panthor_file *pfile = file->driver_priv;
+	struct panthor_device *ptdev = pfile->ptdev;
+	struct panthor_perf_bo ringbuffer = {}, control = {};
+	const size_t slots = create_args->sample_slots;
+	struct panthor_perf_pool *pool = pfile->sessions;
+	struct panthor_perf_session *session;
+	struct panthor_perf_enable_masks *em;
+	size_t user_sample_size;
+	u32 session_id;
+	int ret;
+
+	if (!ptdev->perf)
+		return -EOPNOTSUPP;
+
+	if (!pool)
+		return -EINVAL;
+
+	if (!perfmon_capable())
+		return -EACCES;
+
+	session = kzalloc_obj(*session);
+	if (ZERO_OR_NULL_PTR(session))
+		return -ENOMEM;
+
+	if (!is_power_of_2(slots)) {
+		drm_err(&ptdev->base,
+			"Number of slots is not power-of-2 aligned\n");
+		ret = -EINVAL;
+		goto cleanup_session;
+	}
+
+	user_sample_size = ptdev->perf_info.sample_size * slots;
+
+	ret = panthor_perf_bo_get(file, create_args->ringbuf_handle,
+				  user_sample_size, &ringbuffer);
+	if (ret)
+		goto cleanup_session;
+
+	ret = panthor_perf_bo_get(file, create_args->control_handle, 0, &control);
+	if (ret)
+		goto cleanup_ringbuf;
+
+	session->eventfd = eventfd_ctx_fdget(create_args->fd);
+	if (IS_ERR(session->eventfd)) {
+		drm_err(&ptdev->base, "Invalid eventfd %d!\n", create_args->fd);
+		ret = PTR_ERR_OR_ZERO(session->eventfd) ?: -EINVAL;
+		goto cleanup_control;
+	}
+
+	em = panthor_perf_create_em(create_args);
+	if (IS_ERR_OR_NULL(em)) {
+		ret = -ENOMEM;
+		goto cleanup_eventfd;
+	}
+
+	kref_init(&session->ref);
+	INIT_LIST_HEAD(&session->sessions);
+	mutex_init(&session->state_lock);
+	session->control = control.vaddr;
+	*session->control = (struct drm_panthor_perf_ringbuf_control) { 0 };
+	session->samples = ringbuffer.vaddr;
+	session->enabled_counters = em;
+	session->ring_buf = ringbuffer;
+	session->ringbuf_slots = slots;
+	session->control_buf = control;
+	session->accum_idx = U32_MAX;
+
+	ret = xa_alloc_cyclic(&pfile->sessions->xa, &session_id, session,
+			      pfile->sessions->session_range,
+			      &pfile->sessions->next, GFP_KERNEL);
+	if (ret < 0) {
+		drm_err(&ptdev->base, "File session limit exceeded.\n");
+		ret = -EBUSY;
+		goto cleanup_obj;
+	}
+
+	*sid = session_id;
+	return 0;
+
+cleanup_obj:
+	session_put(session);
+	return ret;
+
+cleanup_eventfd:
+	eventfd_ctx_put(session->eventfd);
+
+cleanup_control:
+	panthor_perf_bo_put(&control);
+
+cleanup_ringbuf:
+	panthor_perf_bo_put(&ringbuffer);
+
+cleanup_session:
+	kfree(session);
+
+	return ret;
+}
+
+static int session_try_stop(struct panthor_perf_session *session, u64 user_data)
+{
+	guard(mutex)(&session->state_lock);
+
+	if (!session_is_active(session))
+		return 0;
+
+	if (session_is_starting(session))
+		return -EBUSY;
+
+	if (session_is_stopping(session))
+		return 1;
+
+	session_set_state(session, PANTHOR_PERF_SESSION_STOPPING);
+	session->user_data = user_data;
+
+	return 1;
+}
+
+static int session_stop(struct panthor_perf *perf, struct panthor_perf_session *session,
+			u64 user_data)
+{
+	int ret;
+
+	ret = session_try_stop(session, user_data);
+	if (ret <= 0)
+		return ret;
+
+	/* TODO Calls to the FW interface will go here in later patches. */
+	return 0;
+}
+
+static int session_try_start(struct panthor_perf_session *session, u64 user_data)
+{
+	guard(mutex)(&session->state_lock);
+
+	if (session_is_active(session))
+		return 0;
+
+	if (session_is_stopping(session))
+		return -EBUSY;
+
+	if (session_is_active(session) || session_is_starting(session))
+		return 0;
+
+	session_set_state(session, PANTHOR_PERF_SESSION_STARTING);
+	session->user_data = user_data;
+
+	return 1;
+}
+
+static int session_start(struct panthor_perf *perf, struct panthor_perf_session *session,
+			 u64 user_data)
+{
+	int ret;
+
+	ret = session_try_start(session, user_data);
+	if (ret <= 0)
+		return ret;
+
+	/* TODO Calls to the FW interface will go here in later patches. */
+	return 0;
+}
+
+static int session_try_sample(struct panthor_perf_session *session, u64 user_data)
+{
+	guard(mutex)(&session->state_lock);
+
+	if (!session_is_active(session))
+		return -EINVAL;
+
+	if (session_is_stopping(session))
+		return -EBUSY;
+
+	if (session_has_overflow(session))
+		return -EACCES;
+
+	session->user_data = user_data;
+
+	return 1;
+}
+
+static int session_sample(struct panthor_perf *perf, struct panthor_perf_session *session,
+			  u64 user_data)
+{
+
+	int ret;
+
+	ret = session_try_sample(session, user_data);
+	if (ret <= 0)
+		return ret;
+
+	/* TODO Calls to the FW interface will go here in later patches. */
+	return 0;
+}
+
+static int session_try_destroy(struct panthor_perf_session *session)
+{
+	guard(mutex)(&session->state_lock);
+
+	if (session_is_active(session))
+		return -EINVAL;
+
+	if (session_is_starting(session) || session_is_stopping(session))
+		return -EBUSY;
+
+	return 0;
+}
+
+/**
+ * panthor_perf_session_destroy - Destroy the session associated with @handle.
+ * @pfile: Open panthor file.
+ * @handle: Session identifier.
+ *
+ * Only stopped sessions can be destroyed. Other session states return an error.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int panthor_perf_session_destroy(struct panthor_file *pfile, u32 handle)
+{
+	struct panthor_perf_session *session;
+	struct panthor_perf *perf = pfile->ptdev->perf;
+	int ret;
+
+	if (!perf)
+		return -EOPNOTSUPP;
+
+	scoped_guard(mutex, &perf->op_lock) {
+		session = xa_load(&pfile->sessions->xa, handle);
+		if (!session)
+			return -EINVAL;
+
+		ret = session_try_destroy(session);
+		if (ret)
+			return ret;
+
+		xa_erase(&pfile->sessions->xa, handle);
+	}
+
+	session_put(session);
+
+	return ret;
+}
+
+/**
+ * panthor_perf_session_start - Start sampling on a stopped session.
+ * @pfile: Open panthor file.
+ * @handle: Session identifier for the desired session.
+ * @user_data: Opaque value passed in from userspace.
+ *
+ * A session counts as stopped when it is created or when it is explicitly stopped after being
+ * started. Starting an active session is treated as a no-op.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int panthor_perf_session_start(struct panthor_file *pfile, u32 handle, u64 user_data)
+{
+	struct panthor_perf_session *session;
+	struct panthor_perf *perf = pfile->ptdev->perf;
+	int err;
+
+	if (!perf)
+		return -EOPNOTSUPP;
+
+	guard(mutex)(&perf->op_lock);
+
+	session = session_from_handle(pfile->sessions, handle);
+	if (IS_ERR_OR_NULL(session))
+		return IS_ERR(session) ? PTR_ERR(session) : -EINVAL;
+
+	err = session_start(perf, session, user_data);
+
+	session_put(session);
+
+	return err;
+}
+
+/**
+ * panthor_perf_session_stop - Stop sampling on an active session.
+ * @pfile: Open panthor file.
+ * @handle: Session identifier for the desired session.
+ * @user_data: An opaque value passed in from userspace.
+ *
+ * A session counts as active when it has been explicitly started via the PERF_SESSION_START
+ * ioctl. Stopping a stopped session is treated as a no-op.
+ *
+ * To ensure data is not lost when sampling is stopping, there must always be at least one slot
+ * available for the final automatic sample, and the stop command will be rejected if there is not.
+ *
+ * The @user_data will always be associated with the final sample.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int panthor_perf_session_stop(struct panthor_file *pfile, unsigned int handle, u64 user_data)
+{
+	struct panthor_perf_session *session;
+	struct panthor_perf *perf = pfile->ptdev->perf;
+	int err;
+
+	if (!perf)
+		return -EOPNOTSUPP;
+
+	guard(mutex)(&perf->op_lock);
+
+	session = session_from_handle(pfile->sessions, handle);
+	if (IS_ERR_OR_NULL(session))
+		return IS_ERR(session) ? PTR_ERR(session) : -EINVAL;
+
+	err = session_stop(perf, session, user_data);
+
+	session_put(session);
+
+	return err;
+}
+
+/**
+ * panthor_perf_session_sample - Request a sample on a manual sampling session.
+ * @pfile: Open panthor file.
+ * @handle: Session identifier for the desired session.
+ * @user_data: An opaque value passed in from userspace.
+ *
+ * Only an active manual sampler is permitted to request samples directly. Failing to meet either
+ * of these conditions will cause the sampling request to be rejected. Requesting a manual sample
+ * with a full ringbuffer will see the request being rejected.
+ *
+ * The @user_data will always be unambiguously associated one-to-one with the resultant sample.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int panthor_perf_session_sample(struct panthor_file *pfile, u32 handle, u64 user_data)
+{
+	struct panthor_perf_session *session;
+	struct panthor_perf *perf = pfile->ptdev->perf;
+	int err;
+
+	if (!perf)
+		return -EOPNOTSUPP;
+
+	guard(mutex)(&perf->op_lock);
+
+	session = session_from_handle(pfile->sessions, handle);
+	if (IS_ERR_OR_NULL(session))
+		return IS_ERR(session) ? PTR_ERR(session) : -EINVAL;
+
+	err = session_sample(perf, session, user_data);
+
+	session_put(session);
+
+	return err;
+}
+
 /**
  * panthor_perf_unplug - Terminate the performance counter subsystem.
  * @ptdev: Panthor device.
@@ -156,13 +803,15 @@ int panthor_perf_pool_create(struct panthor_file *pfile)
 void panthor_perf_pool_destroy(struct panthor_file *pfile)
 {
 	struct panthor_perf_pool *pool = pfile->sessions;
+	unsigned long handle;
+	struct panthor_perf_session *session;
 
 	if (IS_ERR_OR_NULL(pool))
 		return;
 
-	if (!xa_empty(&pool->xa)) {
-		drm_err(&pfile->ptdev->base,
-			"Performance counter sessions active when unplugging the driver!");
+	xa_for_each(&pool->xa, handle, session) {
+		xa_erase(&pool->xa, handle);
+		session_put(session);
 	}
 
 	xa_destroy(&pool->xa);
diff --git a/drivers/gpu/drm/panthor/panthor_perf.h b/drivers/gpu/drm/panthor/panthor_perf.h
index ad01fc88f18f..641a88edd75a 100644
--- a/drivers/gpu/drm/panthor/panthor_perf.h
+++ b/drivers/gpu/drm/panthor/panthor_perf.h
@@ -7,8 +7,11 @@
 
 #include <linux/types.h>
 
+struct drm_file;
+struct drm_panthor_perf_session_create;
 struct panthor_device;
 struct panthor_file;
+struct panthor_perf;
 struct panthor_perf_pool;
 
 int panthor_perf_pool_create(struct panthor_file *pfile);
@@ -17,5 +20,13 @@ void panthor_perf_pool_destroy(struct panthor_file *pfile);
 int panthor_perf_init(struct panthor_device *ptdev);
 void panthor_perf_unplug(struct panthor_device *ptdev);
 
+int panthor_perf_session_create(struct drm_file *file,
+				struct drm_panthor_perf_session_create *setup_args,
+				unsigned long *sid);
+int panthor_perf_session_destroy(struct panthor_file *pfile, unsigned int handle);
+int panthor_perf_session_start(struct panthor_file *pfile, u32 handle, u64 user_data);
+int panthor_perf_session_stop(struct panthor_file *pfile, unsigned int sid, u64 user_data);
+int panthor_perf_session_sample(struct panthor_file *pfile, unsigned int handle, u64 user_data);
+
 #endif /* __PANTHOR_PERF_H__ */
 

-- 
2.43.0


  parent reply	other threads:[~2026-10-11 17:40 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 17:36 [PATCH v7 0/8] drm/panthor: Performance counter implementation with single manual client support Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 1/8] drm/panthor: Add performance counter uAPI Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 2/8] drm/panthor: Add register definitions for perf Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 3/8] drm/panthor: Add DEV_QUERY.PERF_INFO handling Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 4/8] drm/panthor: Add panthor perf initialization and termination Lukas Zapolskas
2026-10-11 17:36 ` Lukas Zapolskas [this message]
2026-10-11 17:36 ` [PATCH v7 6/8] drm/panthor: Implement sample handling Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 7/8] drm/panthor: Add suspend, resume and reset handling Lukas Zapolskas
2026-10-11 17:36 ` [PATCH v7 8/8] drm/panthor: Expose the panthor perf ioctls Lukas Zapolskas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261011-panthor-performance-counters-v7-5-12c32495062f@arm.com \
    --to=lukas.zapolskas@arm.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=boris.brezillon@collabora.com \
    --cc=daniel.almeida@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liviu.dudau@arm.com \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=nd@arm.com \
    --cc=simona@ffwll.ch \
    --cc=steven.price@arm.com \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®