mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Liviu Dudau <liviu.dudau@arm.com>
To: Lukas Zapolskas <lukas.zapolskas@arm.com>
Cc: Daniel Almeida <daniel.almeida@collabora.com>,
	Alice Ryhl <aliceryhl@google.com>,
	Boris Brezillon <boris.brezillon@collabora.com>,
	Steven Price <steven.price@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>,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	nd@arm.com
Subject: Re: [PATCH v7 5/8] drm/panthor: Introduce sampling sessions to handle userspace clients
Date: Fri, 23 Oct 2026 15:24:55 +0100	[thread overview]
Message-ID: <attuN9c169vUGVm0@e142607> (raw)
In-Reply-To: <20261011-panthor-performance-counters-v7-5-12c32495062f@arm.com>

On Sun, Oct 11, 2026 at 06:36:52PM +0100, Lukas Zapolskas wrote:
> 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)];
> +};

Is the array properly sized here? If one bit is a single counter and we can have up to
PANTHOR_PERF_EM_BITS number of counters in each block up to DRM_PANTHOR_PERF_BLOCK_MAX blocks,
then what's the sizeof(unsigned long) doing here?

Could we use the bitmap.h API instead? Also, use u64 kernel type instead of 'unsigned long'.

> +
>  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);

I think you should move PANTHOR_PERF_EM_BITS into panthor_drm.h and use it to define
the drm_panthor_perf_session_create structure, otherwise things will get out of sync.

> +	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;

It's a bit hard to tell what the right answer is here, as there is no user of this function yet,
but shouldn't sid be cleared even if we return an error?


> +}
> +
> +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;

This can be removed as it is checked later?

> +
> +	if (session_is_stopping(session))
> +		return -EBUSY;
> +
> +	if (session_is_active(session) || session_is_starting(session))
> +		return 0;

session_try_stop() returns 1 if session_is_stopping(), but here session_try_start() is
returning 0 if session_is_starting(). Is that intentional? Can we document the meaning
of the return values as we can't see yet how they are going to be used?

> +
> +	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
> 

Best regards,
Liviu


-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ¯\_(ツ)_/¯

  reply	other threads:[~2026-10-23 14:25 UTC|newest]

Thread overview: 18+ 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-22 12:15   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 2/8] drm/panthor: Add register definitions for perf Lukas Zapolskas
2026-10-22 12:16   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 3/8] drm/panthor: Add DEV_QUERY.PERF_INFO handling Lukas Zapolskas
2026-10-23 13:54   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 4/8] drm/panthor: Add panthor perf initialization and termination Lukas Zapolskas
2026-10-23 13:58   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 5/8] drm/panthor: Introduce sampling sessions to handle userspace clients Lukas Zapolskas
2026-10-23 14:24   ` Liviu Dudau [this message]
2026-10-11 17:36 ` [PATCH v7 6/8] drm/panthor: Implement sample handling Lukas Zapolskas
2026-10-13  4:52   ` kernel test robot
2026-10-23 14:30   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 7/8] drm/panthor: Add suspend, resume and reset handling Lukas Zapolskas
2026-10-23 14:32   ` Liviu Dudau
2026-10-11 17:36 ` [PATCH v7 8/8] drm/panthor: Expose the panthor perf ioctls Lukas Zapolskas
2026-10-23 14:33   ` Liviu Dudau

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=attuN9c169vUGVm0@e142607 \
    --to=liviu.dudau@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=lukas.zapolskas@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®