Hi Lukas, I also uncovered a few issues with this patch when debugging it with your Mesa MR.
On 25.07.2025 15:57, 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_CONTROL.START, and > explicitly stopped via PERF_CONTROL.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_CONTROL 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. > > The counter set enum is added to the uapi to clarify the restrictions > on calling the interface. > > Signed-off-by: Lukas Zapolskas <[email protected]> > --- > drivers/gpu/drm/panthor/panthor_perf.c | 706 ++++++++++++++++++++++++- > drivers/gpu/drm/panthor/panthor_perf.h | 16 + > 2 files changed, 716 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/panthor/panthor_perf.c > b/drivers/gpu/drm/panthor/panthor_perf.c > index af1c58e70a78..d1f984b3302a 100644 > --- a/drivers/gpu/drm/panthor/panthor_perf.c > +++ b/drivers/gpu/drm/panthor/panthor_perf.c > @@ -2,14 +2,179 @@ > /* Copyright 2023 Collabora Ltd */ > /* Copyright 2025 Arm ltd. */ > > -#include <linux/bitops.h> > +#include <drm/drm_file.h> > +#include <drm/drm_gem.h> > #include <drm/panthor_drm.h> > +#include <linux/bitops.h> > +#include <linux/circ_buf.h> > > #include "panthor_device.h" > #include "panthor_fw.h" > #include "panthor_perf.h" > #include "panthor_regs.h" > > +/** > + * 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_ACTIVE: The session is active and can be used > for sampling. */ > + PANTHOR_PERF_SESSION_ACTIVE = 0, > + > + /** > + * @PANTHOR_PERF_SESSION_OVERFLOW: The session encountered an overflow > in one of the > + * counters during the last sampling > period. This flag > + * gets propagated as part of samples > emitted for this > + * session, to ensure the userspace > client can gracefully > + * handle this data corruption. > + */ > + PANTHOR_PERF_SESSION_OVERFLOW, > + > + /* Must be last */ > + PANTHOR_PERF_SESSION_MAX, > +}; > + > +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[]; > +}; > + > +/** > + * enum session_sample_type - Enum of the types of samples a session can > request. > + */ > +enum session_sample_type { > + /** @SAMPLE_TYPE_NONE: A sample has not been requested by this session. > */ > + SAMPLE_TYPE_NONE, > + > + /** @SAMPLE_TYPE_INITIAL: An initial sample has been requested by this > session. */ > + SAMPLE_TYPE_INITIAL, > + > + /** @SAMPLE_TYPE_REGULAR: A regular sample has been requested by this > session. */ > + SAMPLE_TYPE_REGULAR, > +}; > + > +struct panthor_perf_session { > + DECLARE_BITMAP(state, PANTHOR_PERF_SESSION_MAX); > + > + /** > + * @pending_sample_request: The type of sample request that is > currently pending: > + * - when a sample is not requested, the data > should be accumulated > + * into the next slot of its ring buffer, > but the extract index > + * should not be updated, and the user-space > session must > + * not be signaled. > + * - when an initial sample is requested, the > data must not be > + * emitted into the target ring buffer and > the userspace client > + * must not be notified. > + * - when a regular sample is requested, the > data must be emitted > + * into the target ring buffer, and the > userspace client must > + * be signalled. > + */ > + enum session_sample_type pending_sample_request; > + > + /** > + * @user_sample_size: The size of a single sample as exposed to > userspace. For the sake of > + * simplicity, the current implementation exposes > the same structure > + * as provided by firmware, after annotating the > sample and the blocks, > + * and zero-extending the counters themselves (to > account for in-kernel > + * accumulation). > + * > + * This may also allow further memory-optimizations > of compressing the > + * sample to provide only requested blocks, if > deemed to be worth the > + * additional complexity. > + */ > + size_t user_sample_size; > + > + /** > + * @accum_idx: The last insert index indicates whether the current > sample > + * needs zeroing before accumulation. This is used to > disambiguate > + * between accumulating into an intermediate slot in > the user ring buffer > + * and zero-ing the buffer before copying data over. > + */ > + u32 accum_idx; > + > + /** > + * @sample_freq_ns: Period between subsequent sample requests. Zero > indicates that > + * userspace will be responsible for requesting > samples. > + */ > + u64 sample_freq_ns; > + > + /** @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 for the userspace ringbuffer. */ > + struct drm_gem_object *ring_buf; > + > + /** > + * @control_buf: BO for the insert and extract indices. > + */ > + struct drm_gem_object *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; > + > + /** > + * @pending: The list node used by the sampler to track the sessions > that have not yet > + * received a sample. > + */ > + struct list_head pending; > + > + /** > + * @sessions: The list node used by the sampler to track the sessions > waiting for a sample. > + */ > + struct list_head sessions; > + > + /** > + * @pfile: The panthor file which was used to create a session, used > for the postclose > + * handling and to prevent a misconfigured userspace from > closing unrelated > + * sessions. > + */ > + struct panthor_file *pfile; > + > + /** > + * @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 { > /** @next_session: The ID of the next session. */ > u32 next_session; > @@ -23,11 +188,6 @@ struct panthor_perf { > struct xarray sessions; > }; > > -struct panthor_perf_counter_block { > - struct drm_panthor_perf_block_header header; > - u64 counters[]; > -}; > - > static size_t get_annotated_block_size(size_t counters_per_block) > { > return struct_size_t(struct panthor_perf_counter_block, counters, > counters_per_block); > @@ -73,6 +233,125 @@ static void panthor_perf_info_init(struct panthor_device > *const ptdev) > perf_info->sample_size = session_get_user_sample_size(perf_info); > } > > +static struct panthor_perf_enable_masks *panthor_perf_create_em(struct > drm_panthor_perf_cmd_setup > + *const > setup_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], > + setup_args->fw_enable_mask, PANTHOR_PERF_EM_BITS); > + bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_CSHW], > + setup_args->cshw_enable_mask, PANTHOR_PERF_EM_BITS); > + bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_TILER], > + setup_args->tiler_enable_mask, PANTHOR_PERF_EM_BITS); > + bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_MEMSYS], > + setup_args->memsys_enable_mask, PANTHOR_PERF_EM_BITS); > + bitmap_from_arr64(em->mask[DRM_PANTHOR_PERF_BLOCK_SHADER], > + setup_args->shader_enable_mask, PANTHOR_PERF_EM_BITS); > + > + return em; > +} > + > +static u64 session_read_extract_idx(struct panthor_perf_session *session) > +{ > + const u64 slots = session->ringbuf_slots; > + > + /* Userspace will update their own extract index to indicate that a > sample is consumed > + * from the ringbuffer, and we must ensure we read the latest value. > + */ > + return smp_load_acquire(&session->control->extract_idx) % slots; > +} > + > +static u64 session_read_insert_idx(struct panthor_perf_session *session) > +{ > + const u64 slots = session->ringbuf_slots; > + > + /* > + * Userspace is able to write to the insert index, since it is mapped > + * on the same page as the extract index. This should not happen > + * in regular operation. > + */ > + return smp_load_acquire(&session->control->insert_idx) % slots; > +} > + > +static void session_get(struct panthor_perf_session *session) > +{ > + kref_get(&session->ref); > +} > + > +static void session_free(struct kref *ref) > +{ > + struct panthor_perf_session *session = container_of(ref, > typeof(*session), ref); > + > + if (session->samples && session->ring_buf) { > + struct iosys_map map = IOSYS_MAP_INIT_VADDR(session->samples); > + > + drm_gem_vunmap(session->ring_buf, &map); > + drm_gem_object_put(session->ring_buf); > + } > + > + if (session->control && session->control_buf) { > + struct iosys_map map = IOSYS_MAP_INIT_VADDR(session->control); > + > + drm_gem_vunmap(session->control_buf, &map); > + drm_gem_object_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_find - Find a session associated with the given session ID and > + * panthor_file. > + * @pfile: Panthor file. > + * @perf: Panthor perf. > + * @sid: Session ID. > + * > + * 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 an ERR_PTR. > + */ > +static struct panthor_perf_session *session_find(struct panthor_file *pfile, > + struct panthor_perf *perf, u32 > sid) > +{ > + struct panthor_perf_session *session; > + > + if (!perf) > + return ERR_PTR(-EINVAL); > + > + xa_lock(&perf->sessions); > + session = xa_load(&perf->sessions, sid); > + > + if (!session || xa_is_err(session)) { > + xa_unlock(&perf->sessions); > + return ERR_PTR(-EBADF); > + } > + > + if (session->pfile != pfile) { > + xa_unlock(&perf->sessions); > + return ERR_PTR(-EINVAL); > + } > + > + session_get(session); > + xa_unlock(&perf->sessions); > + > + return session; > +} > + > /** > * panthor_perf_init - Initialize the performance counter subsystem. > * @ptdev: Panthor device > @@ -110,6 +389,415 @@ int panthor_perf_init(struct panthor_device *ptdev) > return ret; > } > > +static int session_validate_set(u8 set) > +{ > + if (set > DRM_PANTHOR_PERF_SET_TERTIARY) > + return -EINVAL; > + > + if (set == DRM_PANTHOR_PERF_SET_PRIMARY) > + return 0; > + > + if (set > DRM_PANTHOR_PERF_SET_PRIMARY) > + return capable(CAP_PERFMON) ? 0 : -EACCES; > + > + return -EINVAL; > +} > + > +/** > + * panthor_perf_session_setup - Create a user-visible session. > + * > + * @ptdev: Handle to the panthor device. > + * @perf: Handle to the perf control structure. > + * @setup_args: Setup arguments passed in via ioctl. > + * @pfile: Panthor file associated with the request. > + * > + * 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_CONTROL.START. > + * > + * Return: non-negative session identifier on success or negative error code > on failure. > + */ > +int panthor_perf_session_setup(struct drm_file *file, struct panthor_perf > *perf, > + struct drm_panthor_perf_cmd_setup *setup_args) > +{ > + struct panthor_file *pfile = file->driver_priv; > + struct panthor_device *ptdev = pfile->ptdev; > + struct panthor_perf_session *session; > + struct drm_gem_object *ringbuffer; > + struct drm_gem_object *control; > + const size_t slots = setup_args->sample_slots; > + struct panthor_perf_enable_masks *em; > + struct iosys_map rb_map, ctrl_map; > + size_t user_sample_size; > + int session_id; > + int ret; > + > + ret = session_validate_set(setup_args->block_set); > + if (ret) { > + drm_err(&ptdev->base, "Did not meet requirements for set %d\n", > + setup_args->block_set); > + return ret; > + } > + > + session = kzalloc(sizeof(*session), GFP_KERNEL); > + if (ZERO_OR_NULL_PTR(session)) > + return -ENOMEM; > + > + ringbuffer = drm_gem_object_lookup(file, setup_args->ringbuf_handle); > + if (!ringbuffer) { > + drm_err(&ptdev->base, "Could not find handle %d!\n", > setup_args->ringbuf_handle); > + ret = -EINVAL; > + goto cleanup_session; > + } > + > + control = drm_gem_object_lookup(file, setup_args->control_handle); > + if (!control) { > + drm_err(&ptdev->base, "Could not find handle %d!\n", > setup_args->control_handle); > + ret = -EINVAL; > + goto cleanup_ringbuf; > + } > + > + user_sample_size = session_get_user_sample_size(&ptdev->perf_info) * > slots; > + > + if (ringbuffer->size != PFN_ALIGN(user_sample_size)) { > + drm_err(&ptdev->base, > + "Incorrect ringbuffer size from userspace: user %zu vs > kernel %lu\n", > + ringbuffer->size, PFN_ALIGN(user_sample_size)); > + > + ret = -ENOMEM; > + goto cleanup_control; > + } > + > + ret = drm_gem_vmap(ringbuffer, &rb_map); > + if (ret) > + goto cleanup_control; > + > + ret = drm_gem_vmap(control, &ctrl_map); > + if (ret) > + goto cleanup_ring_map; > + > + session->eventfd = eventfd_ctx_fdget(setup_args->fd); > + if (IS_ERR(session->eventfd)) { > + drm_err(&ptdev->base, "Invalid eventfd %d!\n", setup_args->fd); > + ret = PTR_ERR_OR_ZERO(session->eventfd) ?: -EINVAL; > + goto cleanup_control_map; > + } > + > + em = panthor_perf_create_em(setup_args); > + if (IS_ERR_OR_NULL(em)) { > + ret = -ENOMEM; > + goto cleanup_eventfd; > + } > + > + INIT_LIST_HEAD(&session->sessions); > + INIT_LIST_HEAD(&session->pending); > + > + session->control = ctrl_map.vaddr; > + *session->control = (struct drm_panthor_perf_ringbuf_control) { 0 }; > + > + session->samples = rb_map.vaddr; > + > + /* TODO This will need validation when we support periodic sampling > sessions */ > + if (setup_args->sample_freq_ns) { > + ret = -EOPNOTSUPP; > + goto cleanup_em; > + } > + > + ret = xa_alloc_cyclic(&perf->sessions, &session_id, session, > perf->session_range, > + &perf->next_session, GFP_KERNEL); > + if (ret < 0) { > + drm_err(&ptdev->base, "System session limit exceeded.\n"); > + ret = -EBUSY; > + goto cleanup_em; > + } > + > + kref_init(&session->ref); > + session->enabled_counters = em; > + > + session->sample_freq_ns = setup_args->sample_freq_ns; > + session->user_sample_size = user_sample_size; > + session->ring_buf = ringbuffer; > + session->ringbuf_slots = slots; > + session->control_buf = control; > + session->pfile = pfile; > + session->accum_idx = U32_MAX; > + > + return session_id; > + > +cleanup_em: > + kfree(em); > + > +cleanup_eventfd: > + eventfd_ctx_put(session->eventfd); > + > +cleanup_control_map: > + drm_gem_vunmap(control, &ctrl_map); > + > +cleanup_ring_map: > + drm_gem_vunmap(ringbuffer, &rb_map); > + > +cleanup_control: > + drm_gem_object_put(control); > + > +cleanup_ringbuf: > + drm_gem_object_put(ringbuffer); > + > +cleanup_session: > + kfree(session); > + > + return ret; > +} > + > +static int session_stop(struct panthor_perf *perf, struct > panthor_perf_session *session, > + u64 user_data) > +{ > + if (!test_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state)) > + return 0; > + > + const u64 extract_idx = session_read_extract_idx(session); > + const u64 insert_idx = session_read_insert_idx(session); > + > + /* Must have at least one slot remaining in the ringbuffer to sample. */ > + if (WARN_ON_ONCE(!CIRC_SPACE(insert_idx, extract_idx, > session->ringbuf_slots))) > + return -EBUSY; > + > + session->user_data = user_data; > + > + clear_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state); > + > + /* TODO Calls to the FW interface will go here in later patches. */ > + return 0; > +} > + > +static int session_start(struct panthor_perf *perf, struct > panthor_perf_session *session, > + u64 user_data) > +{ > + if (test_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state)) > + return 0; > + > + set_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state); > + > + /* > + * For manual sampling sessions, a start command does not correspond to > a sample, > + * and so the user data gets discarded. > + */ > + if (session->sample_freq_ns) > + session->user_data = user_data; > + > + /* TODO Calls to the FW interface will go here in later patches. */ > + return 0; > +} > + > +static int session_sample(struct panthor_perf *perf, struct > panthor_perf_session *session, > + u64 user_data) > +{ > + if (!test_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state)) > + return 0; > + > + const u64 extract_idx = session_read_extract_idx(session); > + const u64 insert_idx = session_read_insert_idx(session); > + > + /* Manual sampling for periodic sessions is forbidden. */ > + if (session->sample_freq_ns) > + return -EINVAL; > + > + /* > + * Must have at least two slots remaining in the ringbuffer to sample: > one for > + * the current sample, and one for a stop sample, since a stop command > should > + * always be acknowledged by taking a final sample and stopping the > session. > + */ > + if (CIRC_SPACE_TO_END(insert_idx, extract_idx, session->ringbuf_slots) > < 2) > + return -EBUSY; > + > + session->sample_start_ns = ktime_get_raw_ns(); > + session->user_data = user_data; > + > + return 0; > +} > + > +static int session_destroy(struct panthor_perf *perf, struct > panthor_perf_session *session) > +{ > + session_put(session); > + > + return 0; > +} > + > +static int session_teardown(struct panthor_perf *perf, struct > panthor_perf_session *session) > +{ > + if (test_bit(PANTHOR_PERF_SESSION_ACTIVE, session->state)) > + return -EINVAL; > + > + if (READ_ONCE(session->pending_sample_request) == SAMPLE_TYPE_NONE) > + return -EBUSY; I suspect here you meant 'if (READ_ONCE(session->pending_sample_request) != SAMPLE_TYPE_NONE)', ie, do not proceed with teardown if there's a pending sample request. > + return session_destroy(perf, session); > +} > + > +/** > + * panthor_perf_session_teardown - Teardown the session associated with the > @sid. > + * @pfile: Open panthor file. > + * @perf: Handle to the perf control structure. > + * @sid: Session identifier. > + * > + * Destroys a stopped session where the last sample has been explicitly > consumed > + * or discarded. Active sessions will be ignored. > + * > + * Return: 0 on success, negative error code on failure. > + */ > +int panthor_perf_session_teardown(struct panthor_file *pfile, struct > panthor_perf *perf, u32 sid) > +{ > + int err; > + struct panthor_perf_session *session; > + > + xa_lock(&perf->sessions); > + session = __xa_store(&perf->sessions, sid, NULL, GFP_KERNEL); I think we should check for the event that __xa_store() returns NULL. In theory this shouldn't happen, but in my experience it might help to uncover hidden bugs in the implementation: if (!session) { drm_err(&pfile->ptdev->base, "Perf session does not exist.\n"); xa_unlock(&perf->sessions); return 0; } > + if (xa_is_err(session)) { > + err = xa_err(session); > + goto restore; > + } > + > + if (session->pfile != pfile) { > + err = -EINVAL; > + goto restore; > + } > + > + session_get(session); > + xa_unlock(&perf->sessions); We need to xa_erase(&perf->sessions, sid); here too, because otherwise the indices aren't freed and then any attempt to connect to the driver's HWPerf after the second attempt will be met with a 'no available sessions' error. > + err = session_teardown(perf, session); > + > + session_put(session); > + > + return err; > + > +restore: > + __xa_store(&perf->sessions, sid, session, GFP_KERNEL); > + xa_unlock(&perf->sessions); > + > + return err; > +} > + > +/** > + * panthor_perf_session_start - Start sampling on a stopped session. > + * @pfile: Open panthor file. > + * @perf: Handle to the panthor perf control structure. > + * @sid: Session identifier for the desired session. > + * @user_data: An 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. > + * > + * The @user_data parameter will be associated with all subsequent samples > for a periodic > + * sampling session and will be ignored for manual sampling ones in favor of > the user data > + * passed in the PERF_CONTROL.SAMPLE ioctl call. > + * > + * Return: 0 on success, negative error code on failure. > + */ > +int panthor_perf_session_start(struct panthor_file *pfile, struct > panthor_perf *perf, > + u32 sid, u64 user_data) > +{ > + struct panthor_perf_session *session = session_find(pfile, perf, sid); > + int err; > + > + 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. > + * @perf: Handle to the panthor perf control structure. > + * @sid: 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_CONTROL.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, struct > panthor_perf *perf, > + u32 sid, u64 user_data) > +{ > + struct panthor_perf_session *session = session_find(pfile, perf, sid); > + int err; > + > + 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. > + * @perf: Handle to the panthor perf control structure. > + * @sid: 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, struct > panthor_perf *perf, > + u32 sid, u64 user_data) > +{ > + struct panthor_perf_session *session = session_find(pfile, perf, sid); > + int err; > + > + 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_session_destroy - Destroy a sampling session associated with > the @pfile. > + * @perf: Handle to the panthor perf control structure. > + * @pfile: The file being closed. > + * > + * Must be called when the corresponding userspace process is destroyed and > cannot close its > + * own sessions. As such, we offer no guarantees about data delivery. > + */ > +void panthor_perf_session_destroy(struct panthor_file *pfile, struct > panthor_perf *perf) > +{ > + unsigned long sid; > + struct panthor_perf_session *session; > + > + if (!pfile || !perf) > + return; > + > + xa_for_each(&perf->sessions, sid, session) > + { > + if (session->pfile == pfile) { > + session_destroy(perf, session); > + xa_erase(&perf->sessions, sid); > + } > + } > +} This function seems to be left unused in later patches. I suspect you meant to call it last from within panthor_postclose() ? > /** > * panthor_perf_unplug - Terminate the performance counter subsystem. > * @ptdev: Panthor device. > @@ -125,8 +813,14 @@ void panthor_perf_unplug(struct panthor_device *ptdev) > return; > > if (!xa_empty(&perf->sessions)) { > + unsigned long sid; > + struct panthor_perf_session *session; > + > drm_err(&ptdev->base, > "Performance counter sessions active when unplugging > the driver!"); > + > + xa_for_each(&perf->sessions, sid, session) > + session_destroy(perf, session); > } > > xa_destroy(&perf->sessions); > diff --git a/drivers/gpu/drm/panthor/panthor_perf.h > b/drivers/gpu/drm/panthor/panthor_perf.h > index e4805727b9e7..c46f94ed8a96 100644 > --- a/drivers/gpu/drm/panthor/panthor_perf.h > +++ b/drivers/gpu/drm/panthor/panthor_perf.h > @@ -7,10 +7,26 @@ > > #include <linux/types.h> > > +struct drm_file; > +struct drm_panthor_perf_cmd_setup; > struct panthor_device; > +struct panthor_file; > +struct panthor_perf; > > int panthor_perf_init(struct panthor_device *ptdev); > void panthor_perf_unplug(struct panthor_device *ptdev); > > +int panthor_perf_session_setup(struct drm_file *file, struct panthor_perf > *perf, > + struct drm_panthor_perf_cmd_setup *setup_args); > +int panthor_perf_session_teardown(struct panthor_file *pfile, struct > panthor_perf *perf, > + u32 sid); > +int panthor_perf_session_start(struct panthor_file *pfile, struct > panthor_perf *perf, > + u32 sid, u64 user_data); > +int panthor_perf_session_stop(struct panthor_file *pfile, struct > panthor_perf *perf, > + u32 sid, u64 user_data); > +int panthor_perf_session_sample(struct panthor_file *pfile, struct > panthor_perf *perf, > + u32 sid, u64 user_data); > +void panthor_perf_session_destroy(struct panthor_file *pfile, struct > panthor_perf *perf); > + > #endif /* __PANTHOR_PERF_H__ */ > > -- > 2.33.0.dirty Adrian Larumbe
