[PATCH i-g-t v2 8/9] tests/intel/xe_pxp: Termination tests
Teres Alexis, Alan Previn
alan.previn.teres.alexis at intel.com
Tue Jan 28 05:13:27 UTC 2025
Only a couple of questions (potential feedback) below - but generally everything looks okay.
...alan
On Wed, 2025-01-15 at 16:19 -0800, Daniele Ceraolo Spurio wrote:
> There are several events that can cause the PXP key to be invalidated
> and trigger a PXP termination (suspend, PXP termination irq). After a
> termination, we expect the key to be different and the raw encrypted
> data to change for the same source data.
> Additionally, all PXP objects are invalidated during a termination and
> can no longer be used in submission or kept mapped to VMs; we therefore
> need to test both the execution and bind ioctls to make sure they work
> as expected after a termination.
>
> v2: move igt_require calls inside the subtest
>
> Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio at intel.com>
> ---
> lib/intel_batchbuffer.c | 8 +-
> lib/intel_batchbuffer.h | 1 +
> lib/xe/xe_ioctl.c | 12 +-
> lib/xe/xe_ioctl.h | 2 +
> tests/intel/xe_pxp.c | 453 +++++++++++++++++++++++++++++++++++++++-
> 5 files changed, 463 insertions(+), 13 deletions(-)
>
> diff --git a/lib/intel_batchbuffer.c b/lib/intel_batchbuffer.c
> index 489e78782..a15859786 100644
> --- a/lib/intel_batchbuffer.c
> +++ b/lib/intel_batchbuffer.c
> @@ -2416,9 +2416,9 @@ static void update_offsets(struct intel_bb *ibb,
>
> #define LINELEN 76
>
> -static int
> -__xe_bb_exec(struct intel_bb *ibb, uint64_t flags, bool sync)
> +int __xe_bb_exec(struct intel_bb *ibb, uint64_t flags, bool sync)
> {
> + int ret = 0;
> uint32_t engine = flags & (I915_EXEC_BSD_MASK | I915_EXEC_RING_MASK);
> uint32_t engine_id;
> struct drm_xe_sync syncs[2] = {
> @@ -2495,12 +2495,12 @@ __xe_bb_exec(struct intel_bb *ibb, uint64_t flags, bool sync)
> ibb->engine_syncobj = syncobj_create(ibb->fd, 0);
> syncs[1].handle = ibb->engine_syncobj;
>
> - xe_exec_sync(ibb->fd, engine_id, ibb->batch_offset, syncs, 2);
> + ret = xe_exec_sync_failable(ibb->fd, engine_id, ibb->batch_offset, syncs, 2);
>
> if (sync)
> intel_bb_sync(ibb);
>
> - return 0;
> + return ret;
> }
>
> static int
> diff --git a/lib/intel_batchbuffer.h b/lib/intel_batchbuffer.h
> index 178aaa9d8..6a7e8df4a 100644
> --- a/lib/intel_batchbuffer.h
> +++ b/lib/intel_batchbuffer.h
> @@ -508,6 +508,7 @@ void intel_bb_dump_cache(struct intel_bb *ibb);
>
> void intel_bb_exec(struct intel_bb *ibb, uint32_t end_offset,
> uint64_t flags, bool sync);
> +int __xe_bb_exec(struct intel_bb *ibb, uint64_t flags, bool sync);
>
> uint64_t intel_bb_get_object_offset(struct intel_bb *ibb, uint32_t handle);
> bool intel_bb_object_offset_to_buf(struct intel_bb *ibb, struct intel_buf *buf);
> diff --git a/lib/xe/xe_ioctl.c b/lib/xe/xe_ioctl.c
> index 01ab7c758..cd7314eda 100644
> --- a/lib/xe/xe_ioctl.c
> +++ b/lib/xe/xe_ioctl.c
> @@ -462,8 +462,8 @@ void xe_exec(int fd, struct drm_xe_exec *exec)
> igt_assert_eq(__xe_exec(fd, exec), 0);
> }
>
> -void xe_exec_sync(int fd, uint32_t exec_queue, uint64_t addr,
> - struct drm_xe_sync *sync, uint32_t num_syncs)
> +int xe_exec_sync_failable(int fd, uint32_t exec_queue, uint64_t addr,
> + struct drm_xe_sync *sync, uint32_t num_syncs)
> {
> struct drm_xe_exec exec = {
> .exec_queue_id = exec_queue,
> @@ -473,7 +473,13 @@ void xe_exec_sync(int fd, uint32_t exec_queue, uint64_t addr,
> .num_batch_buffer = 1,
> };
>
> - igt_assert_eq(__xe_exec(fd, &exec), 0);
> + return __xe_exec(fd, &exec);
> +}
> +
> +void xe_exec_sync(int fd, uint32_t exec_queue, uint64_t addr,
> + struct drm_xe_sync *sync, uint32_t num_syncs)
> +{
> + igt_assert_eq(xe_exec_sync_failable(fd, exec_queue, addr, sync, num_syncs), 0);
> }
>
> void xe_exec_wait(int fd, uint32_t exec_queue, uint64_t addr)
> diff --git a/lib/xe/xe_ioctl.h b/lib/xe/xe_ioctl.h
> index c8a2d81c5..9bdf73b2b 100644
> --- a/lib/xe/xe_ioctl.h
> +++ b/lib/xe/xe_ioctl.h
> @@ -91,6 +91,8 @@ int __xe_exec(int fd, struct drm_xe_exec *exec);
> void xe_exec(int fd, struct drm_xe_exec *exec);
> void xe_exec_sync(int fd, uint32_t exec_queue, uint64_t addr,
> struct drm_xe_sync *sync, uint32_t num_syncs);
> +int xe_exec_sync_failable(int fd, uint32_t exec_queue, uint64_t addr,
> + struct drm_xe_sync *sync, uint32_t num_syncs);
> void xe_exec_wait(int fd, uint32_t exec_queue, uint64_t addr);
> int __xe_wait_ufence(int fd, uint64_t *addr, uint64_t value,
> uint32_t exec_queue, int64_t *timeout);
> diff --git a/tests/intel/xe_pxp.c b/tests/intel/xe_pxp.c
> index 57b82a3d6..86d649b2d 100644
> --- a/tests/intel/xe_pxp.c
> +++ b/tests/intel/xe_pxp.c
> @@ -4,6 +4,7 @@
> */
>
> #include "igt.h"
> +#include "igt_syncobj.h"
> #include "intel_batchbuffer.h"
> #include "intel_bufops.h"
> #include "intel_mocs.h"
> @@ -81,6 +82,15 @@ static uint32_t create_pxp_rcs_queue(int fd, uint32_t vm)
> return q;
> }
>
> +static uint32_t create_regular_rcs_queue(int fd, uint32_t vm)
> +{
> + struct drm_xe_engine_class_instance inst = {
> + .engine_class = DRM_XE_ENGINE_CLASS_RENDER,
> + };
> +
> + return xe_exec_queue_create(fd, vm, &inst, 0);
> +}
> +
> static int query_pxp_status(int fd)
> {
> struct drm_xe_query_pxp_status *pxp_query;
> @@ -338,15 +348,25 @@ static void pxp_rendercopy(int fd, uint32_t q, uint32_t vm, uint32_t copy_size,
> buf_ops_destroy(bops);
> }
>
> -/**
> - * SUBTEST: regular-src-to-pxp-dest-rendercopy
> - * Description: copy from a regular BO to a PXP one and verify the encryption
> - */
> -static void test_render_regular_src_to_pxp_dest(int fd)
> +static void copy_bo_cpu(int fd, uint32_t bo, uint32_t *dst, uint32_t size)
> +{
> + uint32_t *src_ptr;
> +
> + src_ptr = xe_bo_mmap_ext(fd, bo, size, PROT_READ);
> +
> + memcpy(dst, src_ptr, size);
> +
> + igt_assert_eq(munmap(src_ptr, size), 0);
> +}
> +
> +static void __test_render_regular_src_to_pxp_dest(int fd, uint32_t *outpixels, int outsize)
> {
> uint32_t vm, srcbo, dstbo;
> uint32_t q;
>
> + if (outpixels)
> + igt_assert_lte(TSTSURF_SIZE, outsize);
> +
> vm = xe_vm_create(fd, 0, 0);
>
> /*
> @@ -362,12 +382,24 @@ static void test_render_regular_src_to_pxp_dest(int fd)
>
> check_bo_color(fd, dstbo, TSTSURF_SIZE, TSTSURF_FILLCOLOR1, false);
>
> + if (outpixels)
> + copy_bo_cpu(fd, dstbo, outpixels, TSTSURF_SIZE);
> +
> gem_close(fd, srcbo);
> gem_close(fd, dstbo);
> xe_exec_queue_destroy(fd, q);
> xe_vm_destroy(fd, vm);
> }
>
> +/**
> + * SUBTEST: regular-src-to-pxp-dest-rendercopy
> + * Description: copy from a regular BO to a PXP one and verify the encryption
> + */
> +static void test_render_regular_src_to_pxp_dest(int fd)
> +{
> + __test_render_regular_src_to_pxp_dest(fd, NULL, 0);
> +}
> +
> static int bocmp(int fd, uint32_t bo1, uint32_t bo2, uint32_t size)
> {
> uint32_t *ptr1, *ptr2;
> @@ -429,13 +461,400 @@ static void test_render_pxp_protsrc_to_protdest(int fd)
> xe_vm_destroy(fd, vm);
> }
>
> -static void require_pxp_render(bool pxp_supported, uint32_t devid)
> +#define PS_OP_TAG_LOW 0x1234fed0
> +#define PS_OP_TAG_HI 0x5678cbaf
> +static void emit_pipectrl(struct intel_bb *ibb, struct intel_buf *fenceb)
> +{
> + uint32_t pipe_ctl_flags = 0;
> +
> + intel_bb_out(ibb, GFX_OP_PIPE_CONTROL(2));
> + intel_bb_out(ibb, pipe_ctl_flags);
> +
> + pipe_ctl_flags = (PIPE_CONTROL_FLUSH_ENABLE |
> + PIPE_CONTROL_CS_STALL |
> + PIPE_CONTROL_QW_WRITE);
> + intel_bb_out(ibb, GFX_OP_PIPE_CONTROL(6));
> + intel_bb_out(ibb, pipe_ctl_flags);
> +
> + intel_bb_emit_reloc(ibb, fenceb->handle, 0, I915_GEM_DOMAIN_COMMAND, 0,
> + fenceb->addr.offset);
> + intel_bb_out(ibb, PS_OP_TAG_LOW);
> + intel_bb_out(ibb, PS_OP_TAG_HI);
> + intel_bb_out(ibb, MI_NOOP);
> + intel_bb_out(ibb, MI_NOOP);
> +}
> +
> +static void assert_pipectl_storedw_done(int fd, uint32_t bo)
> +{
> + uint32_t *ptr;
> +
> + ptr = xe_bo_mmap_ext(fd, bo, 4096, PROT_READ|PROT_WRITE);
> + igt_assert_eq(ptr[0], PS_OP_TAG_LOW);
> + igt_assert_eq(ptr[1], PS_OP_TAG_HI);
> +
> + igt_assert(munmap(ptr, 4096) == 0);
> +}
> +
> +static int submit_flush_store_dw(int fd, uint32_t q, bool q_is_pxp, uint32_t vm,
> + uint32_t dst, bool dst_is_pxp)
> +{
> + struct intel_buf *dstbuf;
> + struct buf_ops *bops;
> + struct intel_bb *ibb;
> + int ret = 0;
> +
> + bops = buf_ops_create(fd);
> + igt_assert(bops);
> +
> + ibb = intel_bb_create_with_context(fd, q, vm, NULL, 4096);
> + igt_assert(ibb);
> + intel_bb_set_pxp(ibb, q_is_pxp, DISPLAY_APPTYPE, DRM_XE_PXP_HWDRM_DEFAULT_SESSION);
> +
> + dstbuf = buf_create(fd, bops, dst, 256, 4, 32, 4096);
> + intel_buf_set_pxp(dstbuf, dst_is_pxp);
> +
> + intel_bb_ptr_set(ibb, 0);
> + intel_bb_add_intel_buf(ibb, dstbuf, true);
> + emit_pipectrl(ibb, dstbuf);
> + intel_bb_emit_bbe(ibb);
> + ret = __xe_bb_exec(ibb, 0, false);
> + if (ret == 0)
> + ret = intel_bb_sync(ibb);
> + if (ret == 0)
> + assert_pipectl_storedw_done(fd, dst);
> +
> + intel_buf_destroy(dstbuf);
> + intel_bb_destroy(ibb);
> + buf_ops_destroy(bops);
> +
> + return ret;
> +}
> +
> +static void trigger_pxp_debugfs_forced_teardown(int xe_fd)
> +{
> + char str[32];
> + int ret;
> + int fd;
> +
> + fd = igt_debugfs_dir(xe_fd);
> + igt_assert(fd >= 0);
> + ret = igt_debugfs_simple_read(fd, "pxp/terminate", str, 32);
> + igt_assert_f(ret >= 0, "Can't open pxp termination debugfs\n");
> +
> + /* give the kernel time to handle the termination */
> + sleep(1);
> +}
> +
> +enum termination_type {
> + PXP_TERMINATION_IRQ,
> + PXP_TERMINATION_RPM,
> + PXP_TERMINATION_SUSPEND
> +};
> +
> +static void trigger_termination(int fd, enum termination_type type)
> +{
> + switch (type) {
> + case PXP_TERMINATION_IRQ:
> + trigger_pxp_debugfs_forced_teardown(fd);
> + break;
> + case PXP_TERMINATION_RPM:
> + igt_require(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED));
> + break;
> + case PXP_TERMINATION_SUSPEND:
> + igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_DEVICES);
> + break;
> + }
> +}
> +
> +/**
> + * SUBTEST: pxp-termination-key-update-post-termination-irq
> + * Description: Verify key is changed after a termination irq
> + */
> +
> +/**
> + * SUBTEST: pxp-termination-key-update-post-suspend
> + * Description: Verify key is changed after a suspend/resume cycle
> + */
> +
> +/**
> + * SUBTEST: pxp-termination-key-update-post-rpm
> + * Description: Verify key is changed after a runtime suspend/resume cycle
> + */
> +
> +static void test_pxp_teardown_keychange(int fd, enum termination_type type)
> +{
> + uint32_t* encrypted_data_before;
> + uint32_t* encrypted_data_after;
> + int matched_after_keychange = 0, loop = 0;
> +
> + encrypted_data_before = malloc(TSTSURF_SIZE);
> + encrypted_data_after = malloc(TSTSURF_SIZE);
> + igt_assert(encrypted_data_before && encrypted_data_after);
> +
> + __test_render_regular_src_to_pxp_dest(fd, encrypted_data_before, TSTSURF_SIZE);
> +
> + trigger_termination(fd, type);
> +
> + __test_render_regular_src_to_pxp_dest(fd, encrypted_data_after, TSTSURF_SIZE);
> +
> + while (loop < (TSTSURF_SIZE/TSTSURF_BYTESPP)) {
> + if (encrypted_data_before[loop] == encrypted_data_after[loop])
> + ++matched_after_keychange;
> + ++loop;
> + }
> + igt_assert_eq(matched_after_keychange, 0);
> +
> + free(encrypted_data_before);
> + free(encrypted_data_after);
> +}
> +
> +/**
> + * SUBTEST: pxp-stale-bo-bind-post-termination-irq
> + * Description: verify that VM bind on a stale BO (due to a termination irq) is rejected.
> + */
> +
> +/**
> + * SUBTEST: pxp-stale-bo-bind-post-suspend
> + * Description: verify that VM bind on a stale BO (due to a suspend/resume cycle)
> + * is rejected.
> + */
> +
> +/**
> + * SUBTEST: pxp-stale-bo-bind-post-rpm
> + * Description: verify that VM bind on a stale BO (due to a runtime suspend/resume
> + * cycle) is rejected.
> + */
> +
> +static void __test_pxp_stale_bo_bind(int fd, enum termination_type type, bool pxp)
> +{
> + uint32_t vm, q;
> + uint32_t dstbo;
> + uint32_t flags = pxp ? DRM_XE_VM_BIND_FLAG_CHECK_PXP : 0;
> + int ret;
> +
> + vm = xe_vm_create(fd, 0, 0);
> + q = create_pxp_rcs_queue(fd, vm);
> +
> + dstbo = pxp_bo_create(fd, vm, 4096, DRM_XE_PXP_TYPE_HWDRM);
> +
> + igt_assert_eq(submit_flush_store_dw(fd, q, true, vm, dstbo, true), 0);
> +
> + /*
> + * RPM cleanup is automatic as soon as we release the reference, so we
> + * neet to make sure not to release it before other types of termination
alan: nit: neet->need
> + * have occurred.
> + */
> + if (type == PXP_TERMINATION_RPM) {
> + xe_exec_queue_destroy(fd, q);
> + trigger_termination(fd, type);
> + } else {
> + trigger_termination(fd, type);
> + xe_exec_queue_destroy(fd, q);
> + }
alan: feedback/question: i'm wondering a trigger_termination() for the NON-RPM could
be broken but pass anyway if the queue destruction triggered a quick rpm
which proceeded to trigger a teardown before the xe_vm_bind below.
A very racy case no doubt. That said, i am wondering if we should only
do the queue destruction before the stale-bind-test for the rpm
trigger case and for the other cases, destroy the queue at the end?
Its been a while so i could be making some incorrect assumptions, lets
connect offline if my concern is not valid or my suggestion not work.
> +
> + /* trying to map a stale BO is an illegal op */
> + ret = __xe_vm_bind(fd, vm, 0, dstbo, 0, 0, 4096, DRM_XE_VM_BIND_OP_MAP,
> + flags, NULL, 0, 0, DEFAULT_PAT_INDEX, 0);
> + igt_assert_eq(ret, pxp ? -ENOEXEC : 0);
alan: just a question: here we are trying to map a stale bo to a vm,
a vm, which if i inderstand correctly has already binded that bo earlier
(indirectly?) before the termination as part of submit_flush_store_dw?
So question is the test is valid for both cases of a stale-bo on a previously
bound vm as well as a bo on a new VM? perhaps a future patch for dma buffer
shared handle of that same bo on a new VM?
> +
> + gem_close(fd, dstbo);
> + xe_vm_destroy(fd, vm);
> +}
> +
> +static void test_pxp_stale_bo_bind(int fd, enum termination_type type)
> +{
> + __test_pxp_stale_bo_bind(fd, type, true);
> +}
> +
> +static void pxp_vm_bind_sync(int fd, uint32_t vm, uint32_t bo, uint64_t addr,
> + uint64_t size, uint32_t op)
> +{
> + struct drm_xe_sync sync = {
> + .type = DRM_XE_SYNC_TYPE_SYNCOBJ,
> + .flags = DRM_XE_SYNC_FLAG_SIGNAL,
> + .handle = syncobj_create(fd, 0),
> + };
> +
> + __xe_vm_bind_assert(fd, vm, 0, bo, 0, addr, size, op,
> + DRM_XE_VM_BIND_FLAG_CHECK_PXP, &sync, 1, 0, 0);
> +
> + igt_assert(syncobj_wait(fd, &sync.handle, 1, INT64_MAX, 0, NULL));
> + syncobj_destroy(fd, sync.handle);
> +}
> +
> +static uint32_t create_and_bind_simple_pxp_batch(int fd, uint32_t vm,
> + uint32_t size, uint64_t addr)
> +{
> + uint32_t bo;
> + uint32_t *map;
> + bo = pxp_bo_create(fd, vm, 4096, DRM_XE_PXP_TYPE_HWDRM);
> + pxp_vm_bind_sync(fd, vm, bo, addr, size, DRM_XE_VM_BIND_OP_MAP);
> +
> + map = xe_bo_map(fd, bo, 4096);
> + *map = MI_BATCH_BUFFER_END;
> + munmap(map, 4096);
> +
> + return bo;
> +}
> +
> +/**
> + * SUBTEST: pxp-stale-bo-exec-post-termination-irq
> + * Description: verify that a submission using VM with a mapped stale BO (due to
> + * a termination irq) is rejected.
> + */
> +
> +/**
> + * SUBTEST: pxp-stale-bo-exec-post-suspend
> + * Description: verify that a submission using VM with a mapped stale BO (due to
> + * a suspend/resume cycle) is rejected.
> + */
> +
> +/**
> + * SUBTEST: pxp-stale-bo-exec-post-rpm
> + * Description: verify that a submission using VM with a mapped stale BO (due to
> + * a runtime suspend/resume cycle) is rejected.
> + */
> +
> +static void __test_pxp_stale_bo_exec(int fd, enum termination_type type, bool pxp)
> +{
> + uint32_t vm, q;
> + uint32_t bo;
> + int expected;
> +
> + vm = xe_vm_create(fd, 0, 0);
> +
> + q = create_pxp_rcs_queue(fd, vm); /* start a PXP session */
> + bo = create_and_bind_simple_pxp_batch(fd, vm, 4096, 0);
> +
> + /*
> + * RPM cleanup is automatic as soon as we release the reference, so we
> + * neet to make sure not to release it before other types of termination
alan: nit: neet->need
> + * have occurred.
> + */
> + if (type == PXP_TERMINATION_RPM) {
> + xe_exec_queue_destroy(fd, q);
> + trigger_termination(fd, type);
> + } else {
> + trigger_termination(fd, type);
> + xe_exec_queue_destroy(fd, q);
> + }
> +
> + /* create a clean queue using the VM with the invalid object mapped in */
> + if (pxp) {
> + q = create_pxp_rcs_queue(fd, vm);
> + expected = -ENOEXEC;
> + } else {
> + q = create_regular_rcs_queue(fd, vm);
> + expected = 0;
> + }
> +
> + igt_assert_eq(xe_exec_sync_failable(fd, q, 0, NULL, 0), expected);
> +
> + /* now make sure we can unmap the stale BO and have a clean exec after */
> + if (pxp) {
> + pxp_vm_bind_sync(fd, vm, 0, 0, 4096, DRM_XE_VM_BIND_OP_UNMAP);
> + gem_close(fd, bo);
> +
> + bo = create_and_bind_simple_pxp_batch(fd, vm, 4096, 0);
> + igt_assert_eq(xe_exec_sync_failable(fd, q, 0, NULL, 0), 0);
> + }
> +
> + xe_exec_queue_destroy(fd, q);
> + gem_close(fd, bo);
> + xe_vm_destroy(fd, vm);
> +}
> +
> +static void test_pxp_stale_bo_exec(int fd, enum termination_type type)
> +{
> + __test_pxp_stale_bo_exec(fd, type, true);
> +}
> +
> +/**
> + * SUBTEST: pxp-stale-queue-post-termination-irq
> + * Description: verify that submissions on a stale queue (due to a termination
> + * irq) are cancelled
> + */
> +
> +/**
> + * SUBTEST: pxp-stale-queue-post-suspend
> + * Description: verify that submissions on a stale queue (due to a suspend/resume
> + * cycle) are cancelled
> + */
> +
> +static void test_pxp_stale_queue_execution(int fd, enum termination_type type)
> +{
> + uint32_t vm, q;
> + uint32_t dstbo;
> +
> + vm = xe_vm_create(fd, 0, 0);
> + q = create_pxp_rcs_queue(fd, vm);
> +
> + dstbo = regular_bo_create_and_fill(fd, vm, 4096, 0);
> +
> + igt_assert_eq(submit_flush_store_dw(fd, q, true, vm, dstbo, false), 0);
> +
> + trigger_termination(fd, type);
> +
> + /* when we execute an invalid queue we expect the job to be canceled */
> + igt_assert_eq(submit_flush_store_dw(fd, q, true, vm, dstbo, false), -ECANCELED);
> +
> + gem_close(fd, dstbo);
> + xe_exec_queue_destroy(fd, q);
> + xe_vm_destroy(fd, vm);
> +}
> +
> +/**
> + * SUBTEST: pxp-optout
> + * Description: verify that submssions with stale objects/queues are not blocked
> + * if the user does not opt-in to the PXP checks.
> + */
> +static void test_pxp_optout(int fd)
> +{
> + __test_pxp_stale_bo_exec(fd, PXP_TERMINATION_IRQ, false);
> + __test_pxp_stale_bo_bind(fd, PXP_TERMINATION_IRQ, false);
> +}
> +
> +static void require_pxp(bool pxp_supported)
> {
> igt_require_f(pxp_supported, "PXP not supported\n");
> +}
> +
> +static void require_pxp_render(bool pxp_supported, uint32_t devid)
> +{
> + require_pxp(pxp_supported);
> igt_require_f(igt_get_render_copyfunc(devid),
> "No rendercopy found for devid=%x\n", devid);
> }
>
> +static void termination_tests(int fd, bool pxp_supported, uint32_t devid,
> + enum termination_type type, const char *tag)
> +{
> + if (type == PXP_TERMINATION_RPM)
> + igt_setup_runtime_pm(fd);
> +
> + igt_subtest_f("pxp-termination-key-update-post-%s", tag) {
> + require_pxp_render(pxp_supported, devid);
> + test_pxp_teardown_keychange(fd, type);
> + }
> + igt_subtest_f("pxp-stale-bo-bind-post-%s", tag) {
> + require_pxp(pxp_supported);
> + test_pxp_stale_bo_bind(fd, type);
> + }
> + igt_subtest_f("pxp-stale-bo-exec-post-%s", tag) {
> + require_pxp(pxp_supported);
> + test_pxp_stale_bo_exec(fd, type);
> + }
> +
> + /* An active PXP queue holds an RPM ref, so we can't test RPM with it */
> + if (type != PXP_TERMINATION_RPM)
> + igt_subtest_f("pxp-stale-queue-post-%s", tag) {
> + require_pxp(pxp_supported);
> + test_pxp_stale_queue_execution(fd, type);
> + }
> + else
> + igt_restore_runtime_pm();
> +}
> +
> igt_main
> {
> int xe_fd = -1;
> @@ -470,6 +889,28 @@ igt_main
> }
> }
>
> + igt_subtest_group {
> + const struct mode {
> + enum termination_type type;
> + const char *tag;
> + } modes[] = {
> + { PXP_TERMINATION_IRQ, "termination-irq" },
> + { PXP_TERMINATION_SUSPEND, "suspend" },
> + { PXP_TERMINATION_RPM, "rpm" },
> + { 0, NULL }
> + };
> +
> + igt_describe("Verify teardown management");
> +
> + for (const struct mode *m = modes; m->tag; m++)
> + termination_tests(xe_fd, pxp_supported, devid, m->type, m->tag);
> +
> + igt_subtest("pxp-optout") {
> + require_pxp(pxp_supported);
> + test_pxp_optout(xe_fd);
> + }
> + }
> +
> igt_fixture {
> close(xe_fd);
> }
More information about the igt-dev
mailing list