[igt-dev] [PATCH i-g-t] tests/panfrost: Add infinite job test

Steven Price
2019-09-13T10:23:45Z

Construct a loop of three job descriptors that will execute
continuously. Then free the BO backing the job descriptors (before the
timeout) causing a page fault on the GPU. The GPU fault will race with
the destruction of the BO.

Signed-off-by: Steven Price <steven.price@arm.com>
---
This is a test for the bug fixed by "Prevent race when handling page
fault"[1]

[1] https://lore.kernel.org/lkml/20190905121141.42820-1-steven.price@arm.com/

lib/igt_panfrost.c | 50 +++++++++++++++++++++++++++++++++++++++++
lib/igt_panfrost.h | 1 +
lib/panfrost-job.h | 6 ++++-
tests/panfrost_submit.c | 14 ++++++++++++
4 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/lib/igt_panfrost.c b/lib/igt_panfrost.c
index 8b0c2b77..04dac3d7 100644
--- a/lib/igt_panfrost.c
+++ b/lib/igt_panfrost.c
@@ -72,6 +72,8 @@ igt_panfrost_gem_new(int fd, size_t size)
void
igt_panfrost_free_bo(int fd, struct panfrost_bo *bo)
{
+ if (!bo)
+ return;
if (bo->map)
munmap(bo->map, bo->size);
gem_close(fd, bo->handle);
@@ -127,6 +129,54 @@ void igt_panfrost_bo_mmap(int fd, struct panfrost_bo *bo)
igt_assert(bo->map);
}

+struct panfrost_submit *igt_panfrost_infinite_job(int fd)
+{
+ struct panfrost_submit *submit;
+ struct mali_job_descriptor_header header = {
+ .job_type = JOB_TYPE_SET_VALUE,
+ .job_descriptor_size = MALI_JOB_64
+ };
+ struct mali_payload_set_value payload = {
+ .value_descriptor = MALI_SET_VALUE_ZERO,
+ .immediate = JOB_NOT_STARTED
+ };
+ int i;
+ int size = ALIGN(sizeof(header) + sizeof(payload), 64);
+ uint32_t *bos;
+
+ submit = calloc(1, sizeof(*submit));
+
+ submit->submit_bo = igt_panfrost_gem_new(fd, size * 3);
+ igt_panfrost_bo_mmap(fd, submit->submit_bo);
+
+ for(i = 0; i < 3; i++) {
+ int offset = i * size;
+ int next_offset = (i == 2) ? 0 : (offset + size);
+ int prev_offset = (i == 0) ? size * 2 : (offset - size);
+
+ header.next_job_64 = submit->submit_bo->offset + next_offset;
+ payload.out = submit->submit_bo->offset + prev_offset;
+
+ memcpy(submit->submit_bo->map + offset, &header,
+ sizeof(header));
+ memcpy(submit->submit_bo->map + offset + sizeof(header),
+ &payload, sizeof(payload));
+ }
+
+ bos = malloc(sizeof(*bos));
+ bos[0] = submit->submit_bo->handle;
+
+ submit->args = calloc(1, sizeof(*submit->args));
+ submit->args->jc = submit->submit_bo->offset;
+ submit->args->requirements = 0;
+ submit->args->bo_handles = to_user_pointer(bos);
+ submit->args->bo_handle_count = 1;
+
+ igt_assert_eq(drmSyncobjCreate(fd, DRM_SYNCOBJ_CREATE_SIGNALED, &submit->args->out_sync), 0);
+
+ return submit;
+}
+
struct panfrost_submit *igt_panfrost_trivial_job(int fd, bool do_crash, int width, int height, uint32_t color)
{
struct panfrost_submit *submit;
diff --git a/lib/igt_panfrost.h b/lib/igt_panfrost.h
index cc7998dc..5e7c8c86 100644
--- a/lib/igt_panfrost.h
+++ b/lib/igt_panfrost.h
@@ -47,6 +47,7 @@ struct panfrost_submit {
struct panfrost_bo *igt_panfrost_gem_new(int fd, size_t size);
void igt_panfrost_free_bo(int fd, struct panfrost_bo *bo);

+struct panfrost_submit *igt_panfrost_infinite_job(int fd);
struct panfrost_submit *igt_panfrost_trivial_job(int fd, bool do_crash, int width, int height, uint32_t color);
void igt_panfrost_free_job(int fd, struct panfrost_submit *submit);

diff --git a/lib/panfrost-job.h b/lib/panfrost-job.h
index 85ef02d0..206f9d44 100644
--- a/lib/panfrost-job.h
+++ b/lib/panfrost-job.h
@@ -613,9 +613,13 @@ struct mali_job_descriptor_header {
};
} __attribute__((packed));

+#define MALI_SET_VALUE_ZERO 3
+
struct mali_payload_set_value {
u64 out;
- u64 unknown;
+ u32 value_descriptor;
+ u32 reserved;
+ u64 immediate;
} __attribute__((packed));

/* Special attributes have a fixed index */
diff --git a/tests/panfrost_submit.c b/tests/panfrost_submit.c
index 13ce85b7..b0b37fbd 100644
--- a/tests/panfrost_submit.c
+++ b/tests/panfrost_submit.c
@@ -175,6 +175,20 @@ igt_main
igt_panfrost_free_job(fd, submit);
}

+ igt_subtest("pan-infinite") {
+ struct panfrost_submit *submit;
+
+ submit = igt_panfrost_infinite_job(fd);
+ do_ioctl(fd, DRM_IOCTL_PANFROST_SUBMIT, submit->args);
+ /* Expect for this job to timeout */
+ igt_assert(!syncobj_wait(fd, &submit->args->out_sync, 1,
+ abs_timeout(SHORT_TIME_NSEC), 0, NULL));
+ /* This will unmap the job while it is still executing causing
+ * a page fault
+ */
+ igt_panfrost_free_job(fd, submit);
+ }
+
igt_fixture {
close(fd);
}
--
2.20.1