[PATCH i-g-t 2/2] tests/intel/xe_oa: Tests for OA syncs
Kamil Konieczny
kamil.konieczny at linux.intel.com
Wed Oct 30 12:54:11 UTC 2024
Hi Ashutosh,
On 2024-10-29 at 19:53:27 -0700, Ashutosh Dixit wrote:
> Verify OA syncs signal correctly in open and change config code
> paths. Verify with different types of sync objects as well as by both
> waiting and skipping the wait for syncs to signal.
>
> v2: Significantly expand oa syncs testing as described above
> v3: Always wait in userptr/ufence cases to avoid -EFAULT errors
>
> Reviewed-by: Umesh Nerlige Ramappa <umesh.nerlige.ramappa at intel.com>
> Signed-off-by: Ashutosh Dixit <ashutosh.dixit at intel.com>
> ---
> lib/xe/xe_oa.c | 6 +-
> lib/xe/xe_oa.h | 2 +
> tests/intel/xe_oa.c | 282 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 287 insertions(+), 3 deletions(-)
>
> diff --git a/lib/xe/xe_oa.c b/lib/xe/xe_oa.c
> index 4fdd0b8c95..c168669c5d 100644
> --- a/lib/xe/xe_oa.c
> +++ b/lib/xe/xe_oa.c
> @@ -1025,8 +1025,8 @@ const char *intel_xe_perf_read_report_reason(const struct intel_xe_perf *perf,
> return "unknown";
> }
>
Please describe public lib function here.
Regards,
Kamil
> -static void xe_oa_prop_to_ext(struct intel_xe_oa_open_prop *properties,
> - struct drm_xe_ext_set_property *extn)
> +void intel_xe_oa_prop_to_ext(struct intel_xe_oa_open_prop *properties,
> + struct drm_xe_ext_set_property *extn)
> {
> __u64 *prop = from_user_pointer(properties->properties_ptr);
> struct drm_xe_ext_set_property *ext = extn;
> @@ -1063,7 +1063,7 @@ int intel_xe_perf_ioctl(int fd, enum drm_xe_observation_op op, void *arg)
> struct intel_xe_oa_open_prop *oprop = (struct intel_xe_oa_open_prop *)arg;
>
> igt_assert_lte(oprop->num_properties, XE_OA_MAX_SET_PROPERTIES);
> - xe_oa_prop_to_ext(oprop, ext);
> + intel_xe_oa_prop_to_ext(oprop, ext);
> }
>
> return igt_ioctl(fd, DRM_IOCTL_XE_OBSERVATION, &p);
> diff --git a/lib/xe/xe_oa.h b/lib/xe/xe_oa.h
> index 962f9dddcc..7d3d074560 100644
> --- a/lib/xe/xe_oa.h
> +++ b/lib/xe/xe_oa.h
> @@ -398,6 +398,8 @@ uint64_t intel_xe_perf_read_record_timestamp_raw(const struct intel_xe_perf *per
> const char *intel_xe_perf_read_report_reason(const struct intel_xe_perf *perf,
> const struct intel_xe_perf_record_header *record);
>
> +void intel_xe_oa_prop_to_ext(struct intel_xe_oa_open_prop *properties,
> + struct drm_xe_ext_set_property *extn);
> int intel_xe_perf_ioctl(int fd, enum drm_xe_observation_op op, void *arg);
> void intel_xe_perf_ioctl_err(int fd, enum drm_xe_observation_op op, void *arg, int err);
>
> diff --git a/tests/intel/xe_oa.c b/tests/intel/xe_oa.c
> index 92f5828c79..33e978a488 100644
> --- a/tests/intel/xe_oa.c
> +++ b/tests/intel/xe_oa.c
> @@ -22,6 +22,7 @@
> #include "drm.h"
> #include "igt.h"
> #include "igt_device.h"
> +#include "igt_syncobj.h"
> #include "igt_sysfs.h"
> #include "xe/xe_ioctl.h"
> #include "xe/xe_query.h"
> @@ -4463,6 +4464,255 @@ static void test_mapped_oa_buffer(map_oa_buffer_test_t test_with_fd_open,
> __perf_close(stream_fd);
> }
>
> +
> +/* Return alternative config_id if available, else just return config_id */
> +static void find_alt_oa_config(u32 config_id, u32 *alt_config_id)
> +{
> + struct dirent *entry;
> + int metrics_fd, dir_fd;
> + DIR *metrics_dir;
> + bool ret;
> +
> + metrics_fd = openat(sysfs, "metrics", O_DIRECTORY);
> + igt_assert_lte(0, metrics_fd);
> +
> + metrics_dir = fdopendir(metrics_fd);
> + igt_assert(metrics_dir);
> +
> + while ((entry = readdir(metrics_dir))) {
> + if (entry->d_type != DT_DIR)
> + continue;
> +
> + dir_fd = openat(metrics_fd, entry->d_name, O_RDONLY);
> + ret = __igt_sysfs_get_u32(dir_fd, "id", alt_config_id);
> + close(dir_fd);
> + if (!ret)
> + continue;
> +
> + if (config_id != *alt_config_id)
> + goto exit;
> + }
> +
> + *alt_config_id = config_id;
> +exit:
> + closedir(metrics_dir);
> +}
> +
> +#define USER_FENCE_VALUE 0xdeadbeefdeadbeefull
> +
> +#define WAIT (0x1 << 0)
> +#define CONFIG (0x1 << 1)
> +
> +enum oa_sync_type {
> + OA_SYNC_TYPE_SYNCOBJ,
> + OA_SYNC_TYPE_USERPTR,
> + OA_SYNC_TYPE_UFENCE,
> +};
> +
> +struct oa_sync {
> + enum oa_sync_type sync_type;
> + u32 syncobj;
> + u32 vm;
> + u32 bo;
> + size_t bo_size;
> + struct {
> + uint64_t vm_sync;
> + uint64_t pad;
> + uint64_t oa_sync;
> + } *data;
> +};
> +
> +static void
> +oa_sync_init(enum oa_sync_type sync_type, const struct drm_xe_engine_class_instance *hwe,
> + struct oa_sync *osync, struct drm_xe_sync *sync)
> +{
> + uint64_t addr = 0x1a0000;
> +
> + osync->sync_type = sync_type;
> + sync->flags = DRM_XE_SYNC_FLAG_SIGNAL;
> +
> + switch (osync->sync_type) {
> + case OA_SYNC_TYPE_SYNCOBJ:
> + osync->syncobj = syncobj_create(drm_fd, 0);
> + sync->handle = osync->syncobj;
> + sync->type = DRM_XE_SYNC_TYPE_SYNCOBJ;
> + break;
> + case OA_SYNC_TYPE_USERPTR:
> + case OA_SYNC_TYPE_UFENCE:
> + sync->type = DRM_XE_SYNC_TYPE_USER_FENCE;
> + sync->timeline_value = USER_FENCE_VALUE;
> +
> + osync->vm = xe_vm_create(drm_fd, 0, 0);
> + osync->bo_size = xe_bb_size(drm_fd, sizeof(*osync->data));
> + if (osync->sync_type == OA_SYNC_TYPE_USERPTR) {
> + osync->data = aligned_alloc(xe_get_default_alignment(drm_fd),
> + osync->bo_size);
> + igt_assert(osync->data);
> + } else {
> + osync->bo = xe_bo_create(drm_fd, osync->vm, osync->bo_size,
> + vram_if_possible(drm_fd, hwe->gt_id),
> + DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM);
> + osync->data = xe_bo_map(drm_fd, osync->bo, osync->bo_size);
> + }
> + memset(osync->data, 0, osync->bo_size);
> +
> + sync->addr = to_user_pointer(&osync->data[0].vm_sync);
> + if (osync->bo)
> + xe_vm_bind_async(drm_fd, osync->vm, 0, osync->bo, 0,
> + addr, osync->bo_size, sync, 1);
> + else
> + xe_vm_bind_userptr_async(drm_fd, osync->vm, 0,
> + to_user_pointer(osync->data),
> + addr, osync->bo_size, sync, 1);
> + xe_wait_ufence(drm_fd, &osync->data[0].vm_sync, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> +
> + sync->addr = to_user_pointer(&osync->data[0].oa_sync);
> + break;
> + default:
> + igt_assert(false);
> + }
> +}
> +
> +static void oa_sync_wait(struct oa_sync *osync)
> +{
> + switch (osync->sync_type) {
> + case OA_SYNC_TYPE_SYNCOBJ:
> + igt_assert(syncobj_wait(drm_fd, &osync->syncobj, 1, INT64_MAX, 0, NULL));
> + syncobj_reset(drm_fd, &osync->syncobj, 1);
> + break;
> + case OA_SYNC_TYPE_USERPTR:
> + case OA_SYNC_TYPE_UFENCE:
> + xe_wait_ufence(drm_fd, &osync->data[0].oa_sync, USER_FENCE_VALUE, 0, NSEC_PER_SEC);
> + osync->data[0].oa_sync = 0;
> + break;
> + default:
> + igt_assert(false);
> + }
> +}
> +
> +static void oa_sync_free(struct oa_sync *osync)
> +{
> + switch (osync->sync_type) {
> + case OA_SYNC_TYPE_SYNCOBJ:
> + syncobj_destroy(drm_fd, osync->syncobj);
> + break;
> + case OA_SYNC_TYPE_USERPTR:
> + case OA_SYNC_TYPE_UFENCE:
> + if (osync->bo) {
> + munmap(osync->data, osync->bo_size);
> + gem_close(drm_fd, osync->bo);
> + } else {
> + free(osync->data);
> + }
> + xe_vm_destroy(drm_fd, osync->vm);
> + break;
> + default:
> + igt_assert(false);
> + }
> +}
> +
> +/**
> + * SUBTEST: syncs-%s-%s
> + *
> + * Description: Test OA syncs (with %arg[1] sync types and %arg[2] wait and
> + * reconfig flags) signal correctly in open and reconfig code
> + * paths
> + *
> + * arg[1]:
> + *
> + * @syncobj: sync type syncobj
> + *
> + * arg[2]:
> + *
> + * @wait-cfg: Exercise reconfig path and wait for syncs to signal
> + * @wait: Don't exercise reconfig path and wait for syncs to signal
> + * @cfg: Exercise reconfig path but don't wait for syncs to signal
> + * @none: Don't exercise reconfig path and don't wait for syncs to signal
> + */
> +
> +/**
> + * SUBTEST: syncs-%s-%s
> + *
> + * Description: Test OA syncs (with %arg[1] sync types and %arg[2] wait and
> + * reconfig flags) signal correctly in open and reconfig code
> + * paths
> + *
> + * arg[1]:
> + *
> + * @userptr: sync type userptr
> + * @ufence: sync type ufence
> + *
> + * arg[2]:
> + *
> + * @wait-cfg: Exercise reconfig path and wait for syncs to signal
> + * @wait: Don't exercise reconfig path and wait for syncs to signal
> + */
> +static void test_syncs(const struct drm_xe_engine_class_instance *hwe,
> + enum oa_sync_type sync_type, int flags)
> +{
> + struct drm_xe_ext_set_property extn[XE_OA_MAX_SET_PROPERTIES] = {};
> + struct intel_xe_perf_metric_set *test_set = metric_set(hwe);
> + struct drm_xe_sync sync = {};
> + struct oa_sync osync = {};
> + uint64_t open_properties[] = {
> + DRM_XE_OA_PROPERTY_OA_UNIT_ID, 0,
> + DRM_XE_OA_PROPERTY_SAMPLE_OA, true,
> + DRM_XE_OA_PROPERTY_OA_METRIC_SET, test_set->perf_oa_metrics_set,
> + DRM_XE_OA_PROPERTY_OA_FORMAT, __ff(test_set->perf_oa_format),
> + DRM_XE_OA_PROPERTY_NUM_SYNCS, 1,
> + DRM_XE_OA_PROPERTY_SYNCS, to_user_pointer(&sync),
> + };
> + struct intel_xe_oa_open_prop open_param = {
> + .num_properties = ARRAY_SIZE(open_properties) / 2,
> + .properties_ptr = to_user_pointer(open_properties),
> + };
> + uint64_t config_properties[] = {
> + DRM_XE_OA_PROPERTY_OA_METRIC_SET, 0, /* Filled later */
> + DRM_XE_OA_PROPERTY_NUM_SYNCS, 1,
> + DRM_XE_OA_PROPERTY_SYNCS, to_user_pointer(&sync),
> + };
> + struct intel_xe_oa_open_prop config_param = {
> + .num_properties = ARRAY_SIZE(config_properties) / 2,
> + .properties_ptr = to_user_pointer(config_properties),
> + };
> + uint32_t alt_config_id;
> + int ret;
> +
> + /*
> + * Necessarily wait in userptr/ufence cases, otherwise IGT process can exit
> + * after calling oa_sync_free, which results in -EFAULT when kernel signals
> + * the userptr/ufence
> + */
> + if (sync_type == OA_SYNC_TYPE_USERPTR || sync_type == OA_SYNC_TYPE_UFENCE)
> + flags |= WAIT;
> +
> + oa_sync_init(sync_type, hwe, &osync, &sync);
> +
> + stream_fd = __perf_open(drm_fd, &open_param, false);
> +
> + /* Reset the sync object if we are going to reconfig the stream */
> + if (flags & (WAIT | CONFIG))
> + oa_sync_wait(&osync);
> +
> + if (!(flags & CONFIG))
> + goto exit;
> +
> + /* Change stream configuration */
> + find_alt_oa_config(test_set->perf_oa_metrics_set, &alt_config_id);
> +
> + config_properties[1] = alt_config_id;
> + intel_xe_oa_prop_to_ext(&config_param, extn);
> +
> + ret = igt_ioctl(stream_fd, DRM_XE_OBSERVATION_IOCTL_CONFIG, extn);
> + igt_assert_eq(ret, test_set->perf_oa_metrics_set);
> +
> + if (flags & WAIT)
> + oa_sync_wait(&osync);
> +exit:
> + __perf_close(stream_fd);
> + oa_sync_free(&osync);
> +}
> +
> static const char *xe_engine_class_name(uint32_t engine_class)
> {
> switch (engine_class) {
> @@ -4511,6 +4761,22 @@ static const char *xe_engine_class_name(uint32_t engine_class)
>
> igt_main
> {
> + const struct sync_section {
> + const char *name;
> + enum oa_sync_type sync_type;
> + unsigned int flags;
> + } sync_sections[] = {
> + { "syncobj-wait-cfg", OA_SYNC_TYPE_SYNCOBJ, WAIT | CONFIG},
> + { "syncobj-wait", OA_SYNC_TYPE_SYNCOBJ, WAIT },
> + { "syncobj-cfg", OA_SYNC_TYPE_SYNCOBJ, CONFIG },
> + { "syncobj-none", OA_SYNC_TYPE_SYNCOBJ, 0 },
> + /* userptr/ufence cases always set WAIT (see test_syncs) */
> + { "userptr-wait-cfg", OA_SYNC_TYPE_USERPTR, WAIT | CONFIG},
> + { "userptr-wait", OA_SYNC_TYPE_USERPTR, WAIT },
> + { "ufence-wait-cfg", OA_SYNC_TYPE_UFENCE, WAIT | CONFIG},
> + { "ufence-wait", OA_SYNC_TYPE_UFENCE, WAIT },
> + { NULL },
> + };
> struct drm_xe_engine_class_instance *hwe = NULL;
> struct xe_device *xe_dev;
>
> @@ -4713,6 +4979,22 @@ igt_main
> }
> }
>
> + igt_subtest_group {
> + igt_fixture {
> + struct drm_xe_query_oa_units *qoa = xe_oa_units(drm_fd);
> + struct drm_xe_oa_unit *oau = (struct drm_xe_oa_unit *)&qoa->oa_units[0];
> +
> + igt_require(oau->capabilities & DRM_XE_OA_CAPS_SYNCS);
> + }
> +
> + for (const struct sync_section *s = sync_sections; s->name; s++) {
> + igt_subtest_with_dynamic_f("syncs-%s", s->name)
> + __for_one_render_engine(hwe)
> + test_syncs(hwe, s->sync_type, s->flags);
> +
> + }
> + }
> +
> igt_fixture {
> /* leave sysctl options in their default state... */
> write_u64_file("/proc/sys/dev/xe/observation_paranoid", 1);
> --
> 2.41.0
>
More information about the igt-dev
mailing list