On 16.05.2025 16:49, 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 <lukas.zapols...@arm.com> > --- > drivers/gpu/drm/panthor/panthor_device.h | 3 + > drivers/gpu/drm/panthor/panthor_drv.c | 1 + > drivers/gpu/drm/panthor/panthor_perf.c | 694 ++++++++++++++++++++++- > drivers/gpu/drm/panthor/panthor_perf.h | 16 + > 4 files changed, 713 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/panthor/panthor_device.h > b/drivers/gpu/drm/panthor/panthor_device.h > index 818c4d96d448..3fa0882fe81b 100644 > --- a/drivers/gpu/drm/panthor/panthor_device.h > +++ b/drivers/gpu/drm/panthor/panthor_device.h > @@ -225,6 +225,9 @@ struct panthor_file { > /** @ptdev: Device attached to this file. */ > struct panthor_device *ptdev; > > + /** @drm_file: Corresponding drm_file */
> + struct drm_file *drm_file; I'm sceptical about adding this here, and suspect we don't need it. I mentioned why in the review for the next patch. > + > /** @vms: VM pool attached to this file. */ > struct panthor_vm_pool *vms; > > diff --git a/drivers/gpu/drm/panthor/panthor_drv.c > b/drivers/gpu/drm/panthor/panthor_drv.c > index 9d2b716cca45..4c1381320859 100644 > --- a/drivers/gpu/drm/panthor/panthor_drv.c > +++ b/drivers/gpu/drm/panthor/panthor_drv.c > @@ -1356,6 +1356,7 @@ panthor_open(struct drm_device *ddev, struct drm_file > *file) > } > > pfile->ptdev = ptdev; > + pfile->drm_file = file; > > ret = panthor_vm_pool_create(pfile); > if (ret) > diff --git a/drivers/gpu/drm/panthor/panthor_perf.c > b/drivers/gpu/drm/panthor/panthor_perf.c > index 9365ce9fed04..15fa533731f3 100644 > --- a/drivers/gpu/drm/panthor/panthor_perf.c > +++ b/drivers/gpu/drm/panthor/panthor_perf.c > @@ -2,13 +2,177 @@ > /* Copyright 2023 Collabora Ltd */ > /* Copyright 2025 Arm ltd. */ > > -#include <linux/bitops.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" > > +/** > + * 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[]; > +}; This is a redefinition. > +/** > + * 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; > @@ -72,6 +236,122 @@ static void panthor_perf_info_init(struct panthor_device > *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 > + *setup_args) > +{ > + struct panthor_perf_enable_masks *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. Why would userspace be able to write into the insert index? I guess in a ringbuffer setup, UM updates the extract index when it consumes a sample, and the kernel increases the insert index when it writes a new sample into the user-facing ringbuffer. > + */ > + 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_unlocked(session->ring_buf, &map); drm_gem_vunmap_unlocked() isn't declared in drm_gem.h when I rebase the patch series onto drm-misc. I guess it means either you're basing this patch series on a previous WIP branch or else it's misspelt? > + 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_unlocked(session->control_buf, &map); > + drm_gem_object_put(session->control_buf); > + } > + > + 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 > @@ -109,6 +389,412 @@ 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 panthor_device *ptdev, struct > panthor_perf *perf, > + struct drm_panthor_perf_cmd_setup *setup_args, > + struct panthor_file *pfile) > +{ > + 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(pfile->drm_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(pfile->drm_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_unlocked(ringbuffer, &rb_map); Same here, drm_gem_vmap_unlocked() isn't declared in any header files. > + if (ret) > + goto cleanup_control; > + > + ret = drm_gem_vmap_unlocked(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_unlocked(control, &ctrl_map); > + > +cleanup_ring_map: > + drm_gem_vunmap_unlocked(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_TO_END(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; > + > + 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); > + > + 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); > + > + 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); > + } > + } > +} > + > /** > * panthor_perf_unplug - Terminate the performance counter subsystem. > * @ptdev: Panthor device. > @@ -124,8 +810,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..89d61cd1f017 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_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 panthor_device *ptdev, struct > panthor_perf *perf, > + struct drm_panthor_perf_cmd_setup *setup_args, > + struct panthor_file *pfile); > +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