From: Jan Maslak <jan.maslak@intel.com>
To: igt-dev@lists.freedesktop.org
Cc: maciej.patelczyk@intel.com, Jan Maslak <jan.maslak@intel.com>
Subject: [PATCH 1/1] tests/intel/xe_eudebug: Add basic-vm-bind-ufence-no-ack test
Date: Fri, 27 Feb 2026 15:26:38 +0100 [thread overview]
Message-ID: <20260227142638.3264013-2-jan.maslak@intel.com> (raw)
In-Reply-To: <20260227142638.3264013-1-jan.maslak@intel.com>
Add a test that verifies the kernel correctly blocks unbind operations
on VMAs whose VM_BIND ufence has not yet been ACKed by the debugger.
The test performs N vm_bind operations with user fences attached, then
partially acknowledges them: the first (N-2) ufences are ACKed
immediately, while the remaining 2 are withheld. It checks that:
- the ACKed ufences complete successfully,
- the un-ACKed ufences time out as blocked,
- unbind attempts on the un-ACKed VMAs are denied with -EBUSY,
- after the remaining ACKs are delivered, the blocked ufences complete
and the unbinds succeed.
Signed-off-by: Jan Maslak <jan.maslak@intel.com>
---
tests/intel/xe_eudebug.c | 144 +++++++++++++++++++++++++++++++++------
1 file changed, 124 insertions(+), 20 deletions(-)
diff --git a/tests/intel/xe_eudebug.c b/tests/intel/xe_eudebug.c
index 134f31766..c899316fb 100644
--- a/tests/intel/xe_eudebug.c
+++ b/tests/intel/xe_eudebug.c
@@ -66,6 +66,7 @@ static void test_sysfs_toggle(int fd)
#define VM_BIND_DELAY_UFENCE_ACK (1 << 8)
#define VM_BIND_UFENCE_RECONNECT (1 << 9)
#define VM_BIND_UFENCE_SIGINT_CLIENT (1 << 10)
+#define VM_BIND_UFENCE_NO_ACK (1 << 11)
#define TEST_FAULTABLE (1 << 30)
#define TEST_DISCOVERY (1 << 31)
@@ -1924,6 +1925,7 @@ static void test_metadata_attach(int fd, unsigned int flags, int num_clients)
}
#define STAGE_CLIENT_WAIT_ON_UFENCE_DONE 1337
+#define STAGE_NO_ACK_READY_FOR_PARTIAL_ACK 1338
#define UFENCE_EVENT_COUNT_EXPECTED 4
#define UFENCE_EVENT_COUNT_MAX 100
@@ -1973,6 +1975,72 @@ static void client_wait_ufences(struct xe_eudebug_client *c,
}
}
+#define NO_ACK_HELD_COUNT 2
+
+static void client_wait_ufences_no_ack(struct xe_eudebug_client *c,
+ int fd, uint32_t vm,
+ struct ufence_bind *binds, int count)
+{
+ const int64_t short_timeout_ns = 500 * NSEC_PER_MSEC;
+ const int64_t long_timeout_ns = XE_EUDEBUG_DEFAULT_TIMEOUT_SEC * NSEC_PER_SEC;
+ int64_t timeout_ns;
+ int err;
+
+ igt_assert(count >= NO_ACK_HELD_COUNT);
+
+ /* Signal debugger that binds are done - it will ACK first (N-2) fences */
+ xe_eudebug_client_signal_stage(c, STAGE_NO_ACK_READY_FOR_PARTIAL_ACK);
+
+ /* First (count - NO_ACK_HELD_COUNT) fences should be ACKed already - expect success */
+ for (int i = 0; i < count - NO_ACK_HELD_COUNT; i++) {
+ struct ufence_bind *b = &binds[i];
+
+ timeout_ns = long_timeout_ns;
+ err = __xe_wait_ufence(fd, &b->fence_data->vm_sync, b->f.timeline_value,
+ 0, &timeout_ns);
+ igt_assert_eq(err, 0);
+ igt_assert_eq(b->fence_data->vm_sync, b->f.timeline_value);
+ igt_debug("no-ack: wait #%d completed (was ACKed)\n", i);
+ }
+
+ /* Last NO_ACK_HELD_COUNT fences should NOT be ACKed yet - expect timeout */
+ for (int i = count - NO_ACK_HELD_COUNT; i < count; i++) {
+ struct ufence_bind *b = &binds[i];
+
+ timeout_ns = short_timeout_ns;
+ err = __xe_wait_ufence(fd, &b->fence_data->vm_sync, b->f.timeline_value,
+ 0, &timeout_ns);
+ igt_assert_eq(err, -ETIME);
+ igt_assert_neq(b->fence_data->vm_sync, b->f.timeline_value);
+ igt_debug("no-ack: wait #%d blocked as expected (not ACKed)\n", i);
+ }
+
+ /* Try to unbind un-ACKed VMAs - kernel should deny with -EBUSY */
+ for (int i = count - NO_ACK_HELD_COUNT; i < count; i++) {
+ struct ufence_bind *b = &binds[i];
+
+ err = __xe_vm_bind(fd, vm, 0, 0, 0, b->addr, b->range,
+ DRM_XE_VM_BIND_OP_UNMAP, 0, NULL, 0, 0, 0, 0);
+ igt_assert_eq(err, -EBUSY);
+ igt_debug("no-ack: unbind #%d denied with -EBUSY as expected\n", i);
+ }
+
+ /* Signal debugger that we verified the blocked state - it will now send remaining ACKs */
+ xe_eudebug_client_signal_stage(c, STAGE_CLIENT_WAIT_ON_UFENCE_DONE);
+
+ /* Now last NO_ACK_HELD_COUNT fences should complete after debugger ACKs them */
+ for (int i = count - NO_ACK_HELD_COUNT; i < count; i++) {
+ struct ufence_bind *b = &binds[i];
+
+ timeout_ns = long_timeout_ns;
+ err = __xe_wait_ufence(fd, &b->fence_data->vm_sync, b->f.timeline_value,
+ 0, &timeout_ns);
+ igt_assert_eq(err, 0);
+ igt_assert_eq(b->fence_data->vm_sync, b->f.timeline_value);
+ igt_debug("no-ack: wait #%d completed after delayed ACK\n", i);
+ }
+}
+
static struct ufence_bind *create_binds_with_ufence(int fd, int count)
{
struct ufence_bind *binds;
@@ -2024,7 +2092,10 @@ static void basic_ufence_client(struct xe_eudebug_client *c)
&b->f, 1, 0);
}
- client_wait_ufences(c, fd, binds, n);
+ if (c->flags & VM_BIND_UFENCE_NO_ACK)
+ client_wait_ufences_no_ack(c, fd, vm, binds, n);
+ else
+ client_wait_ufences(c, fd, binds, n);
for (int i = 0; i < n; i++) {
struct ufence_bind *b = &binds[i];
@@ -2147,6 +2218,13 @@ static int wait_for_ufence_events(struct ufence_priv *priv, int timeout_ms)
* Functionality: SIGINT
* Description:
* Give user fence in application, hold it, send SIGINT to client and check if anything breaks.
+ *
+ * SUBTEST: basic-vm-bind-ufence-no-ack
+ * Functionality: VM bind event
+ * Description:
+ * Verify that missing debugger ACK for VM_BIND ufence blocks the application.
+ * ACKs only the first (N-2) ufences immediately, verifies last 2 are blocked,
+ * then delivers remaining ACKs and confirms client unblocks.
*/
static void test_basic_ufence(int fd, unsigned int flags)
{
@@ -2169,26 +2247,49 @@ static void test_basic_ufence(int fd, unsigned int flags)
xe_eudebug_debugger_start_worker(d);
xe_eudebug_client_start(c);
- xe_eudebug_debugger_wait_stage(s, STAGE_CLIENT_WAIT_ON_UFENCE_DONE);
- xe_eudebug_assert_f(d, wait_for_ufence_events(priv, XE_EUDEBUG_DEFAULT_TIMEOUT_SEC * MSEC_PER_SEC) == 0,
- "missing ufence events\n");
-
- if (flags & VM_BIND_DELAY_UFENCE_ACK)
- sleep(XE_EUDEBUG_DEFAULT_TIMEOUT_SEC * 4 / 5);
-
- if (flags & VM_BIND_UFENCE_SIGINT_CLIENT) {
- filter = XE_EUDEBUG_FILTER_ALL;
- kill(c->pid, SIGINT);
- c->pid = 0;
- c->done = 1;
- } else if (flags & VM_BIND_UFENCE_RECONNECT) {
- filter = XE_EUDEBUG_FILTER_EVENT_VM_BIND | XE_EUDEBUG_FILTER_EVENT_VM |
- XE_EUDEBUG_FILTER_EVENT_OPEN;
- xe_eudebug_debugger_detach(d);
- xe_eudebug_client_wait_done(c);
- igt_assert_eq(xe_eudebug_debugger_attach(d, c), 0);
+ if (flags & VM_BIND_UFENCE_NO_ACK) {
+ /* Wait for client to signal binds are done */
+ xe_eudebug_debugger_wait_stage(s, STAGE_NO_ACK_READY_FOR_PARTIAL_ACK);
+ xe_eudebug_assert_f(d,
+ wait_for_ufence_events(priv, XE_EUDEBUG_DEFAULT_TIMEOUT_SEC *
+ MSEC_PER_SEC) == 0,
+ "missing ufence events\n");
+
+ /* ACK only first (n-2) fences */
+ for (int i = 0; i < UFENCE_EVENT_COUNT_EXPECTED - NO_ACK_HELD_COUNT; i++)
+ xe_eudebug_ack_ufence(d->fd, &priv->ufence_events[i]);
+
+ /* Wait for client to verify blocked state and unbind attempts */
+ xe_eudebug_debugger_wait_stage(s, STAGE_CLIENT_WAIT_ON_UFENCE_DONE);
+
+ /* ACK remaining fences */
+ for (int i = UFENCE_EVENT_COUNT_EXPECTED - NO_ACK_HELD_COUNT;
+ i < UFENCE_EVENT_COUNT_EXPECTED; i++)
+ xe_eudebug_ack_ufence(d->fd, &priv->ufence_events[i]);
} else {
- ack_fences(d);
+ xe_eudebug_debugger_wait_stage(s, STAGE_CLIENT_WAIT_ON_UFENCE_DONE);
+ xe_eudebug_assert_f(d,
+ wait_for_ufence_events(priv, XE_EUDEBUG_DEFAULT_TIMEOUT_SEC *
+ MSEC_PER_SEC) == 0,
+ "missing ufence events\n");
+
+ if (flags & VM_BIND_DELAY_UFENCE_ACK)
+ sleep(XE_EUDEBUG_DEFAULT_TIMEOUT_SEC * 4 / 5);
+
+ if (flags & VM_BIND_UFENCE_SIGINT_CLIENT) {
+ filter = XE_EUDEBUG_FILTER_ALL;
+ kill(c->pid, SIGINT);
+ c->pid = 0;
+ c->done = 1;
+ } else if (flags & VM_BIND_UFENCE_RECONNECT) {
+ filter = XE_EUDEBUG_FILTER_EVENT_VM_BIND | XE_EUDEBUG_FILTER_EVENT_VM |
+ XE_EUDEBUG_FILTER_EVENT_OPEN;
+ xe_eudebug_debugger_detach(d);
+ xe_eudebug_client_wait_done(c);
+ igt_assert_eq(xe_eudebug_debugger_attach(d, c), 0);
+ } else {
+ ack_fences(d);
+ }
}
xe_eudebug_client_wait_done(c);
@@ -2882,6 +2983,9 @@ int igt_main()
igt_subtest("basic-vm-bind-ufence-sigint-client")
test_basic_ufence(fd, VM_BIND_UFENCE_SIGINT_CLIENT);
+ igt_subtest("basic-vm-bind-ufence-no-ack")
+ test_basic_ufence(fd, VM_BIND_UFENCE_NO_ACK);
+
igt_subtest("basic-vm-bind-discovery")
test_basic_discovery(fd, VM_BIND, true);
--
2.34.1
next prev parent reply other threads:[~2026-02-27 14:27 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-27 14:26 [PATCH 0/1] tests/intel/xe_eudebug: Add basic-vm-bind-ufence-no-ack test Jan Maslak
2026-02-27 14:26 ` Jan Maslak [this message]
2026-05-07 8:52 ` [PATCH 1/1] " Maciej Patelczyk
2026-02-27 20:55 ` ✓ Xe.CI.BAT: success for " Patchwork
2026-02-27 20:57 ` ✗ i915.CI.BAT: failure " Patchwork
2026-02-28 10:22 ` ✗ Xe.CI.FULL: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260227142638.3264013-2-jan.maslak@intel.com \
--to=jan.maslak@intel.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=maciej.patelczyk@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox