[PATCH v2 11/12] drm/xe/tests: Add KUnit tests for VF control state machines

Michal Wajdeczko michal.wajdeczko at intel.com
Fri Aug 9 17:23:15 UTC 2024


Add KUnit tests (~200) for all VF control state machines (FLR,
PAUSE, STOP and RESUME) to make sure they work as expected and
will not be broken while extending them with new functionality.

Signed-off-by: Michal Wajdeczko <michal.wajdeczko at intel.com>
Cc: Lucas De Marchi <lucas.demarchi at intel.com>
---
Test file named according to the new best practices [1]
[1] https://lore.kernel.org/linux-hardening/20240724201354.make.730-kees@kernel.org/
---
v2: fix kunit build break due last-minute change in .c
---
 .../xe/tests/xe_gt_sriov_pf_control_kunit.c   | 1360 +++++++++++++++++
 drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c   |    7 +
 2 files changed, 1367 insertions(+)
 create mode 100644 drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_control_kunit.c

diff --git a/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_control_kunit.c b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_control_kunit.c
new file mode 100644
index 000000000000..3061099ad35a
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_control_kunit.c
@@ -0,0 +1,1360 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+
+#include "tests/xe_kunit_helpers.h"
+#include "tests/xe_pci_test.h"
+
+#include "xe_gt_sriov_pf.h"
+
+static const unsigned int DUT_NUM_VFS = 2;
+static const unsigned int VFUT1 = VFID(1);
+static const unsigned int VFUT2 = VFID(2);
+
+static void dump_state(void *arg)
+{
+	struct xe_gt *gt = arg;
+
+	pf_dump_vf_state(gt, VFUT1);
+}
+
+static unsigned long replacement_timeout(enum xe_gt_sriov_control_bits bit)
+{
+	return HZ;
+}
+
+static int pf_control_test_init(struct kunit *test)
+{
+	struct xe_pci_fake_data fake = {
+		.sriov_mode = XE_SRIOV_MODE_PF,
+		.platform = XE_TIGERLAKE, /* some random platform */
+		.subplatform = XE_SUBPLATFORM_NONE,
+	};
+	struct xe_device *xe;
+	struct xe_gt *gt;
+
+	test->priv = &fake;
+	xe_kunit_helper_xe_device_test_init(test);
+
+	xe = test->priv;
+	KUNIT_ASSERT_EQ(test, xe_sriov_init(xe), 0);
+
+	xe->sriov.pf.driver_max_vfs = DUT_NUM_VFS;
+	KUNIT_EXPECT_NE(test, xe_sriov_pf_get_totalvfs(xe), 0);
+
+	gt = xe_device_get_gt(xe, 0);
+	KUNIT_ASSERT_EQ(test, xe_gt_sriov_pf_init_early(gt), 0);
+
+	KUNIT_ASSERT_EQ(test, 0ul, *pf_peek_vf_state(gt, VFUT1));
+	KUNIT_ASSERT_EQ(test, 0ul, *pf_peek_vf_state(gt, VFUT2));
+
+	KUNIT_EXPECT_EQ(test, 0, kunit_add_action_or_reset(test, dump_state, gt));
+
+	kunit_activate_static_stub(test, pf_get_default_timeout, replacement_timeout);
+
+	test->priv = gt;
+	return 0;
+}
+
+static int sanitize_vf_resources_fail(struct xe_gt *gt, u32 vfid, long timeout)
+{
+	return -ETIMEDOUT;
+}
+
+static int send_vf_control_cmd_reject(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return -EIO;
+}
+
+static int send_vf_control_cmd_fail(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return -ENODEV;
+}
+
+static int send_vf_control_cmd_pass_no_reply(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return 0;
+}
+
+static int send_vf_control_cmd_pass_and_reply(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	switch (cmd) {
+	case GUC_PF_TRIGGER_VF_PAUSE:
+		pf_handle_vf_event(gt, vfid, GUC_PF_NOTIFY_VF_PAUSE_DONE);
+		break;
+	case GUC_PF_TRIGGER_VF_FLR_START:
+		pf_handle_vf_event(gt, vfid, GUC_PF_NOTIFY_VF_FLR_DONE);
+		break;
+	case GUC_PF_TRIGGER_VF_RESUME:
+	case GUC_PF_TRIGGER_VF_STOP:
+	case GUC_PF_TRIGGER_VF_FLR_FINISH:
+		break;
+	default:
+		return -EPROTO;
+	}
+	return 0;
+}
+
+static int send_vf_control_cmd_busy_wait(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	schedule_timeout_interruptible(HZ / 20);
+	return -EBUSY;
+}
+
+static int send_vf_control_cmd_pass_but_reply_flr_only(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return cmd == GUC_PF_TRIGGER_VF_FLR_START ?
+		send_vf_control_cmd_pass_and_reply(gt, vfid, cmd) :
+		send_vf_control_cmd_pass_no_reply(gt, vfid, cmd);
+}
+
+static int send_vf_control_cmd_busy_except_flr(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return cmd == GUC_PF_TRIGGER_VF_FLR_START || cmd == GUC_PF_TRIGGER_VF_FLR_FINISH ?
+		send_vf_control_cmd_pass_and_reply(gt, vfid, cmd) :
+		send_vf_control_cmd_busy_wait(gt, vfid, cmd);
+}
+
+static int send_vf_control_cmd_busy_except_stop(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	return cmd == GUC_PF_TRIGGER_VF_STOP ?
+		send_vf_control_cmd_pass_and_reply(gt, vfid, cmd) :
+		send_vf_control_cmd_busy_wait(gt, vfid, cmd);
+}
+
+static int BUSY_MAGIC = 3;
+
+static int send_vf_control_cmd_busy_no_reply(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	static int counter;
+
+	return ++counter % BUSY_MAGIC ? -EBUSY : 0;
+}
+
+static int send_vf_control_cmd_busy_and_reply(struct xe_gt *gt, unsigned int vfid, u32 cmd)
+{
+	static int counter;
+
+	return ++counter % BUSY_MAGIC ? -EBUSY :
+		send_vf_control_cmd_pass_and_reply(gt, vfid, cmd);
+}
+
+static const enum xe_gt_sriov_control_bits ready[] = {
+};
+
+static const enum xe_gt_sriov_control_bits flr_starting[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_SEND_START,
+};
+
+static const enum xe_gt_sriov_control_bits flr_starting_paused[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_SEND_START,
+	XE_GT_SRIOV_STATE_PAUSED,
+};
+
+static const enum xe_gt_sriov_control_bits flr_starting_stopped[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_SEND_START,
+	XE_GT_SRIOV_STATE_STOPPED,
+};
+
+static const enum xe_gt_sriov_control_bits flr_waiting[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_WAIT_GUC,
+};
+
+static const enum xe_gt_sriov_control_bits flr_guc_done[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_GUC_DONE,
+};
+
+static const enum xe_gt_sriov_control_bits flr_resetting[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_RESET_CONFIG,
+};
+
+static const enum xe_gt_sriov_control_bits flr_finishing[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_FLR_WIP,
+	XE_GT_SRIOV_STATE_FLR_SEND_FINISH,
+};
+
+static const enum xe_gt_sriov_control_bits flr_failed[] = {
+	XE_GT_SRIOV_STATE_FLR_FAILED,
+};
+
+static const enum xe_gt_sriov_control_bits stopping[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_STOP_WIP,
+	XE_GT_SRIOV_STATE_STOP_SEND_STOP,
+};
+
+static const enum xe_gt_sriov_control_bits stopping_paused[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_STOP_WIP,
+	XE_GT_SRIOV_STATE_STOP_SEND_STOP,
+	XE_GT_SRIOV_STATE_PAUSED,
+};
+
+static const enum xe_gt_sriov_control_bits stop_failed[] = {
+	XE_GT_SRIOV_STATE_STOP_FAILED,
+};
+
+static const enum xe_gt_sriov_control_bits stop_rejected[] = {
+	XE_GT_SRIOV_STATE_STOP_FAILED,
+	XE_GT_SRIOV_STATE_MISMATCH,
+};
+
+static const enum xe_gt_sriov_control_bits stopped[] = {
+	XE_GT_SRIOV_STATE_STOPPED,
+};
+
+static const enum xe_gt_sriov_control_bits pausing[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_SEND_PAUSE,
+};
+
+static const enum xe_gt_sriov_control_bits pausing_wait_guc[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC,
+};
+
+static const enum xe_gt_sriov_control_bits pausing_guc_done[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_WIP,
+	XE_GT_SRIOV_STATE_PAUSE_GUC_DONE,
+};
+
+static const enum xe_gt_sriov_control_bits pause_failed[] = {
+	XE_GT_SRIOV_STATE_PAUSE_FAILED,
+};
+
+static const enum xe_gt_sriov_control_bits pause_rejected[] = {
+	XE_GT_SRIOV_STATE_PAUSE_FAILED,
+	XE_GT_SRIOV_STATE_MISMATCH,
+};
+
+static const enum xe_gt_sriov_control_bits paused[] = {
+	XE_GT_SRIOV_STATE_PAUSED,
+};
+
+static const enum xe_gt_sriov_control_bits resuming[] = {
+	XE_GT_SRIOV_STATE_WIP,
+	XE_GT_SRIOV_STATE_PAUSED,
+	XE_GT_SRIOV_STATE_RESUME_WIP,
+	XE_GT_SRIOV_STATE_RESUME_SEND_RESUME,
+};
+
+static const enum xe_gt_sriov_control_bits resume_failed[] = {
+	XE_GT_SRIOV_STATE_PAUSED,
+	XE_GT_SRIOV_STATE_RESUME_FAILED,
+};
+
+static const enum xe_gt_sriov_control_bits resume_rejected[] = {
+	XE_GT_SRIOV_STATE_PAUSED,
+	XE_GT_SRIOV_STATE_RESUME_FAILED,
+	XE_GT_SRIOV_STATE_MISMATCH,
+};
+
+static const enum xe_gt_sriov_control_bits resumed[] = {
+	XE_GT_SRIOV_STATE_RESUMED,
+};
+
+static const enum xe_gt_sriov_control_bits mismatch[] = {
+	XE_GT_SRIOV_STATE_MISMATCH,
+};
+
+struct state_param {
+	const char *name;
+	const enum xe_gt_sriov_control_bits *bits;
+	size_t num_bits;
+};
+
+static void state_param_get_desc(struct state_param *p, char *desc)
+{
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", p->name);
+}
+
+#define MAKE_STATE_PARAM(X) { .name = #X, .bits = X, .num_bits = ARRAY_SIZE(X) }
+
+/*
+ * Due to the test case logic the all "must_pass" params include "_rejected" states
+ * since our "GuC" calls by default will not complain again about the INVALID_STATE
+ * thus our state machine is able to fully recover.
+ */
+
+static struct state_param flr_must_pass_from[] = {
+	MAKE_STATE_PARAM(ready),
+	MAKE_STATE_PARAM(flr_starting),
+	MAKE_STATE_PARAM(flr_starting_paused),
+	MAKE_STATE_PARAM(flr_starting_stopped),
+	MAKE_STATE_PARAM(flr_waiting),
+	MAKE_STATE_PARAM(flr_guc_done),
+	MAKE_STATE_PARAM(flr_resetting),
+	MAKE_STATE_PARAM(flr_finishing),
+	MAKE_STATE_PARAM(flr_failed),
+	MAKE_STATE_PARAM(stopping),
+	MAKE_STATE_PARAM(stop_failed),
+	MAKE_STATE_PARAM(stop_rejected),
+	MAKE_STATE_PARAM(stopped),
+	MAKE_STATE_PARAM(pausing),
+	MAKE_STATE_PARAM(pausing_wait_guc),
+	MAKE_STATE_PARAM(pausing_guc_done),
+	MAKE_STATE_PARAM(pause_failed),
+	MAKE_STATE_PARAM(pause_rejected),
+	MAKE_STATE_PARAM(paused),
+	MAKE_STATE_PARAM(resuming),
+	MAKE_STATE_PARAM(resume_failed),
+	MAKE_STATE_PARAM(resume_rejected),
+	MAKE_STATE_PARAM(resumed),
+	MAKE_STATE_PARAM(mismatch),
+};
+
+static struct state_param stop_must_pass_from[] = {
+	MAKE_STATE_PARAM(ready),
+	MAKE_STATE_PARAM(stop_failed),
+	MAKE_STATE_PARAM(stop_rejected),
+	MAKE_STATE_PARAM(pausing),
+	MAKE_STATE_PARAM(pausing_wait_guc),
+	MAKE_STATE_PARAM(pausing_guc_done),
+	MAKE_STATE_PARAM(pause_failed),
+	MAKE_STATE_PARAM(pause_rejected),
+	MAKE_STATE_PARAM(paused),
+	MAKE_STATE_PARAM(resuming),
+	MAKE_STATE_PARAM(resume_failed),
+	MAKE_STATE_PARAM(resume_rejected),
+	MAKE_STATE_PARAM(resumed),
+	MAKE_STATE_PARAM(mismatch),
+	MAKE_STATE_PARAM(flr_failed),
+};
+
+static struct state_param stop_must_cancel_from[] = {
+	MAKE_STATE_PARAM(flr_starting),
+	MAKE_STATE_PARAM(flr_starting_paused),
+	MAKE_STATE_PARAM(flr_waiting),
+	MAKE_STATE_PARAM(flr_guc_done),
+	MAKE_STATE_PARAM(flr_resetting),
+	MAKE_STATE_PARAM(flr_finishing),
+};
+
+static struct state_param stop_must_fail_from[] = {
+	MAKE_STATE_PARAM(stopping),
+	MAKE_STATE_PARAM(stopping_paused),
+	MAKE_STATE_PARAM(stopped),
+	MAKE_STATE_PARAM(flr_starting_stopped),
+};
+
+static struct state_param pause_must_pass_from[] = {
+	MAKE_STATE_PARAM(ready),
+	MAKE_STATE_PARAM(pause_failed),
+	MAKE_STATE_PARAM(pause_rejected),
+	MAKE_STATE_PARAM(resumed),
+	MAKE_STATE_PARAM(mismatch),
+	MAKE_STATE_PARAM(stop_failed),
+	MAKE_STATE_PARAM(stop_rejected),
+	MAKE_STATE_PARAM(flr_failed),
+};
+
+static struct state_param pause_must_cancel_from[] = {
+	MAKE_STATE_PARAM(flr_starting),
+	MAKE_STATE_PARAM(flr_waiting),
+	MAKE_STATE_PARAM(flr_guc_done),
+	MAKE_STATE_PARAM(flr_resetting),
+	MAKE_STATE_PARAM(flr_finishing),
+	MAKE_STATE_PARAM(stopping),
+};
+
+static struct state_param pause_must_fail_from[] = {
+	MAKE_STATE_PARAM(pausing),
+	MAKE_STATE_PARAM(pausing_wait_guc),
+	MAKE_STATE_PARAM(pausing_guc_done),
+	MAKE_STATE_PARAM(paused),
+	MAKE_STATE_PARAM(resuming),
+	MAKE_STATE_PARAM(resume_failed),
+	MAKE_STATE_PARAM(resume_rejected),
+	MAKE_STATE_PARAM(stopped),
+};
+
+static struct state_param resume_must_pass_from[] = {
+	MAKE_STATE_PARAM(paused),
+	MAKE_STATE_PARAM(resume_failed),
+	MAKE_STATE_PARAM(resume_rejected),
+};
+
+static struct state_param resume_must_cancel_from[] = {
+	MAKE_STATE_PARAM(flr_starting_paused),
+	MAKE_STATE_PARAM(stopping_paused),
+};
+
+static struct state_param resume_must_fail_from[] = {
+	MAKE_STATE_PARAM(ready),
+	MAKE_STATE_PARAM(pausing),
+	MAKE_STATE_PARAM(pausing_wait_guc),
+	MAKE_STATE_PARAM(pausing_guc_done),
+	MAKE_STATE_PARAM(pause_failed),
+	MAKE_STATE_PARAM(pause_rejected),
+	MAKE_STATE_PARAM(resuming),
+	MAKE_STATE_PARAM(resumed),
+	MAKE_STATE_PARAM(stopping),
+	MAKE_STATE_PARAM(stop_failed),
+	MAKE_STATE_PARAM(stop_rejected),
+	MAKE_STATE_PARAM(stopped),
+	MAKE_STATE_PARAM(flr_starting),
+	MAKE_STATE_PARAM(flr_waiting),
+	MAKE_STATE_PARAM(flr_guc_done),
+	MAKE_STATE_PARAM(flr_resetting),
+	MAKE_STATE_PARAM(flr_finishing),
+	MAKE_STATE_PARAM(flr_failed),
+	MAKE_STATE_PARAM(mismatch),
+};
+
+KUNIT_ARRAY_PARAM(flr_must_pass_from, flr_must_pass_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(stop_must_pass_from, stop_must_pass_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(stop_must_fail_from, stop_must_fail_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(stop_must_cancel_from, stop_must_cancel_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(pause_must_pass_from, pause_must_pass_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(pause_must_fail_from, pause_must_fail_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(pause_must_cancel_from, pause_must_cancel_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(resume_must_pass_from, resume_must_pass_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(resume_must_fail_from, resume_must_fail_from, state_param_get_desc);
+KUNIT_ARRAY_PARAM(resume_must_cancel_from, resume_must_cancel_from, state_param_get_desc);
+
+static int mimic_pf_handle_vf_flr_done(struct xe_gt *gt, unsigned int vfid)
+{
+	pf_handle_vf_flr_done(gt, vfid);
+	return 0;
+}
+
+static int mimic_pf_handle_vf_pause_done(struct xe_gt *gt, unsigned int vfid)
+{
+	pf_handle_vf_pause_done(gt, vfid);
+	return 0;
+}
+
+static void prepare_state(struct kunit *test, unsigned int vfid,
+			  const enum xe_gt_sriov_control_bits *bits, size_t num_bits)
+{
+	struct xe_gt *gt = test->priv;
+	size_t n;
+
+	for (n = 0; n < num_bits; n++) {
+		enum xe_gt_sriov_control_bits bit = bits[n];
+
+		KUNIT_ASSERT_TRUE(test, pf_enter_vf_state(gt, vfid, bit));
+
+		if (bit == XE_GT_SRIOV_STATE_WIP) {
+			pf_queue_vf(gt, vfid);
+		} else if (bit == XE_GT_SRIOV_STATE_FLR_WAIT_GUC) {
+			xe_kunit_helper_delayed_call(test, HZ / 100,
+						     mimic_pf_handle_vf_flr_done, gt, vfid);
+		} else if (bit == XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC) {
+			xe_kunit_helper_delayed_call(test, HZ / 100,
+						     mimic_pf_handle_vf_pause_done, gt, vfid);
+		}
+	}
+}
+
+static void prepare_state_from_param(struct kunit *test)
+{
+	const struct state_param *p = test->param_value;
+
+	prepare_state(test, VFUT1, p->bits, p->num_bits);
+}
+
+static void expect_not_pausing(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WIP));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_SEND_PAUSE));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_GUC_DONE));
+}
+
+static void expect_not_in_pause(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	expect_not_pausing(test);
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_FAILED));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+}
+
+static void expect_not_resuming(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_WIP));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_SEND_RESUME));
+}
+
+static void expect_not_in_resume(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	expect_not_resuming(test);
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_FAILED));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+}
+
+static void expect_not_stopping(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_WIP));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_SEND_STOP));
+}
+
+static void expect_not_in_stop(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	expect_not_stopping(test);
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_FAILED));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOPPED));
+}
+
+static void expect_not_in_flr(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_SEND_START));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WAIT_GUC));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_GUC_DONE));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_SEND_FINISH));
+	KUNIT_EXPECT_TRUE(test,
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+}
+
+static void expect_not_in_wip(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void expect_not_in_mismatch(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+}
+
+static void try_flr_vf(struct kunit *test, bool mimic_busy, bool late_reply)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      mimic_busy ? late_reply ? send_vf_control_cmd_busy_no_reply :
+			      send_vf_control_cmd_busy_and_reply :
+			      late_reply ? send_vf_control_cmd_pass_no_reply :
+			      send_vf_control_cmd_pass_and_reply);
+
+	if (late_reply)
+		xe_kunit_helper_delayed_call(test, HZ / 10,
+					     mimic_pf_handle_vf_flr_done, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+
+	expect_not_in_flr(test);
+	expect_not_in_pause(test);
+	expect_not_in_resume(test);
+	expect_not_in_stop(test);
+	expect_not_in_mismatch(test);
+	expect_not_in_wip(test);
+}
+
+static void flr_vf_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_flr_vf(test, false, false);
+}
+
+static void flr_vf_needs_retry_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_flr_vf(test, true, false);
+}
+
+static void flr_vf_needs_retry_late_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_flr_vf(test, true, true);
+}
+
+static void flr_vf_fails_on_send(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_fail);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ETIMEDOUT);
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void flr_vf_fails_on_reset(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.vfs[VFUT1].config.sanitize,
+			      sanitize_vf_resources_fail);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ETIMEDOUT);
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void flr_vf_rejected_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_reject);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ETIMEDOUT);
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void flr_vf_unconfirmed_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, err, -ETIMEDOUT);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+}
+
+static void flr_vf_confirmed_early_continue_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, flr_starting, ARRAY_SIZE(flr_starting));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	pf_handle_vf_flr_done(gt, VFUT1);
+
+	/*
+	 * make sure SEND_START completes;
+	 * successful reply from cmd_pass_no_reply should exit mismatch state
+	 */
+	flush_work(&gt->sriov.pf.control.worker);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+}
+
+static void flr_vf_confirmed_early_reject_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, flr_starting, ARRAY_SIZE(flr_starting));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_reject);
+
+	pf_handle_vf_flr_done(gt, VFUT1);
+
+	/*
+	 * make sure SEND_START completes;
+	 * error from send_vf_control_cmd_reject should keep mismatch state
+	 */
+	flush_work(&gt->sriov.pf.control.worker);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void flr_vf_confirmed_twice_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, flr_guc_done, ARRAY_SIZE(flr_guc_done));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	pf_handle_vf_flr_done(gt, VFUT1);
+
+	/* this is fully recoverable */
+	KUNIT_EXPECT_EQ(test, 0, pf_wait_vf_wip_done(gt, VFUT1, HZ));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+}
+
+static void flr_vf_confirmed_too_late_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, flr_failed, ARRAY_SIZE(flr_failed));
+
+	pf_handle_vf_flr_done(gt, VFUT1);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void flr_vf_unsolicited_confirmation_from_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+
+	pf_handle_vf_flr_done(gt, VFUT1);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_FLR_FAILED));
+}
+
+static void flr_vf_wrong_confirmation_from_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, flr_waiting, ARRAY_SIZE(flr_waiting));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	pf_handle_vf_pause_done(gt, VFUT1);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+}
+
+static void flr_vf_canceled_by_restart(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_wait);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10, xe_gt_sriov_pf_control_restart, gt);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	expect_not_in_flr(test);
+}
+
+static void try_stop_vf(struct kunit *test, bool mimic_busy)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      mimic_busy ? send_vf_control_cmd_busy_and_reply :
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOPPED));
+	expect_not_stopping(test);
+	expect_not_in_pause(test);
+	expect_not_in_resume(test);
+	expect_not_in_mismatch(test);
+}
+
+static void stop_vf_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_stop_vf(test, false);
+}
+
+static void stop_vf_needs_retry_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_stop_vf(test, true);
+}
+
+static void stop_vf_refused_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, err == -EALREADY ||
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_FAILED));
+}
+
+static void stop_vf_fails_on_send(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_fail);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ETIMEDOUT);
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_FAILED));
+	expect_not_stopping(test);
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOPPED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void stop_vf_rejected_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_reject);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ETIMEDOUT);
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	expect_not_stopping(test);
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOPPED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void stop_vf_canceled_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOP_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_STOPPED));
+}
+
+static void stop_vf_canceled_by_restart(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_except_flr);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10, xe_gt_sriov_pf_control_restart, gt);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	expect_not_in_stop(test);
+}
+
+static void stop_vf_canceled_by_flr(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_except_flr);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10,
+				     xe_gt_sriov_pf_control_trigger_flr, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	expect_not_in_stop(test);
+}
+
+static void try_pause_vf(struct kunit *test, bool mimic_busy)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      mimic_busy ? send_vf_control_cmd_busy_and_reply :
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	expect_not_pausing(test);
+	expect_not_in_mismatch(test);
+}
+
+static void pause_vf_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_pause_vf(test, false);
+}
+
+static void pause_vf_needs_retry_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_pause_vf(test, true);
+}
+
+static void pause_vf_canceled_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	expect_not_in_pause(test);
+}
+
+static void pause_vf_refused_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -ECANCELED);
+
+	KUNIT_EXPECT_TRUE(test, err == -EALREADY ||
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_FAILED));
+}
+
+static void pause_vf_rejected_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_reject);
+
+	KUNIT_EXPECT_NE(test, 0, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void pause_vf_fails_on_send(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_fail);
+
+	KUNIT_EXPECT_NE(test, 0, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_FAILED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_WIP));
+}
+
+static void pause_vf_unconfirmed_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, err, -ETIMEDOUT);
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSE_FAILED));
+}
+
+static void pause_vf_canceled_by_restart(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10, xe_gt_sriov_pf_control_restart, gt);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	expect_not_in_pause(test);
+}
+
+static void pause_vf_canceled_by_flr(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_but_reply_flr_only);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10,
+				     xe_gt_sriov_pf_control_trigger_flr, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	expect_not_in_pause(test);
+}
+
+static void pause_vf_canceled_by_stop(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_no_reply);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10,
+				     xe_gt_sriov_pf_control_stop_vf, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	expect_not_in_pause(test);
+}
+
+static void try_resume_vf(struct kunit *test, bool mimic_busy)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      mimic_busy ? send_vf_control_cmd_busy_and_reply :
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+	expect_not_resuming(test);
+	expect_not_in_pause(test);
+	expect_not_in_mismatch(test);
+	expect_not_in_wip(test);
+}
+
+static void resume_vf_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_resume_vf(test, false);
+}
+
+static void resume_vf_needs_retry_from(struct kunit *test)
+{
+	prepare_state_from_param(test);
+	try_resume_vf(test, true);
+}
+
+static void resume_vf_fails_on_send(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, paused, ARRAY_SIZE(paused));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_fail);
+
+	KUNIT_EXPECT_NE(test, 0, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_FAILED));
+
+	expect_not_resuming(test);
+	expect_not_in_mismatch(test);
+	expect_not_in_wip(test);
+}
+
+static void resume_vf_rejected_by_guc(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, paused, ARRAY_SIZE(paused));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_reject);
+
+	KUNIT_EXPECT_NE(test, 0, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_PAUSED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_MISMATCH));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_FAILED));
+
+	expect_not_resuming(test);
+	expect_not_in_wip(test);
+}
+
+static void resume_vf_canceled_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_FAILED));
+	expect_not_resuming(test);
+}
+
+static void resume_vf_refused_from(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+	int err;
+
+	prepare_state_from_param(test);
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_NE(test, 0, err = xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+	KUNIT_EXPECT_NE(test, err, -EIO);
+
+	KUNIT_EXPECT_TRUE(test, err == -EALREADY ||
+			  pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_WIP));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_FAILED));
+}
+
+static void resume_vf_canceled_by_restart(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, paused, ARRAY_SIZE(paused));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_except_flr);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10, xe_gt_sriov_pf_control_restart, gt);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_WIP));
+}
+
+static void resume_vf_canceled_by_flr(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, paused, ARRAY_SIZE(paused));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_except_flr);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10,
+				     xe_gt_sriov_pf_control_trigger_flr, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_WIP));
+}
+
+static void resume_vf_canceled_by_stop(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	prepare_state(test, VFUT1, paused, ARRAY_SIZE(paused));
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_busy_except_stop);
+
+	xe_kunit_helper_delayed_call(test, HZ / 10,
+				     xe_gt_sriov_pf_control_stop_vf, gt, VFUT1);
+
+	KUNIT_EXPECT_EQ(test, -ECANCELED, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUMED));
+	KUNIT_EXPECT_TRUE(test, pf_expect_vf_not_state(gt, VFUT1, XE_GT_SRIOV_STATE_RESUME_WIP));
+}
+
+static void basic_pause_and_resume_vf(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_ASSERT_EQ(test, 0, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_resume_vf(gt, VFUT1));
+}
+
+static void basic_pause_and_stop_vf(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_ASSERT_EQ(test, 0, xe_gt_sriov_pf_control_pause_vf(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+}
+
+static void basic_stop_and_flr_vf(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_stop_vf(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+}
+
+static void basic_flr_and_flr_vf(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+}
+
+static void basic_flr_vfs(struct kunit *test)
+{
+	struct xe_gt *gt = test->priv;
+
+	XE_TEST_ACTIVATE_STUB(test, gt->sriov.pf.control.send_vf_control_cmd,
+			      send_vf_control_cmd_pass_and_reply);
+
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT1));
+	KUNIT_EXPECT_EQ(test, 0, xe_gt_sriov_pf_control_trigger_flr(gt, VFUT2));
+}
+
+static struct kunit_case pf_control_test_cases[] = {
+	KUNIT_CASE(basic_pause_and_resume_vf),
+	KUNIT_CASE(basic_pause_and_stop_vf),
+	KUNIT_CASE(basic_stop_and_flr_vf),
+	KUNIT_CASE(basic_flr_and_flr_vf),
+	KUNIT_CASE(basic_flr_vfs),
+
+	KUNIT_CASE_PARAM(flr_vf_from, flr_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(flr_vf_needs_retry_from, flr_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(flr_vf_needs_retry_late_from, flr_must_pass_from_gen_params),
+	KUNIT_CASE(flr_vf_fails_on_send),
+	KUNIT_CASE(flr_vf_fails_on_reset),
+	KUNIT_CASE(flr_vf_rejected_by_guc),
+	KUNIT_CASE_SLOW(flr_vf_unconfirmed_by_guc),
+	KUNIT_CASE(flr_vf_confirmed_early_continue_by_guc),
+	KUNIT_CASE(flr_vf_confirmed_early_reject_by_guc),
+	KUNIT_CASE(flr_vf_confirmed_twice_by_guc),
+	KUNIT_CASE(flr_vf_confirmed_too_late_by_guc),
+	KUNIT_CASE(flr_vf_wrong_confirmation_from_guc),
+	KUNIT_CASE(flr_vf_unsolicited_confirmation_from_guc),
+	KUNIT_CASE(flr_vf_canceled_by_restart),
+
+	KUNIT_CASE_PARAM(stop_vf_from, stop_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(stop_vf_needs_retry_from, stop_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(stop_vf_refused_from, stop_must_fail_from_gen_params),
+	KUNIT_CASE_PARAM(stop_vf_canceled_from, stop_must_cancel_from_gen_params),
+	KUNIT_CASE(stop_vf_fails_on_send),
+	KUNIT_CASE(stop_vf_rejected_by_guc),
+	KUNIT_CASE(stop_vf_canceled_by_flr),
+	KUNIT_CASE(stop_vf_canceled_by_restart),
+
+	KUNIT_CASE_PARAM(pause_vf_from, pause_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(pause_vf_needs_retry_from, pause_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(pause_vf_refused_from, pause_must_fail_from_gen_params),
+	KUNIT_CASE_PARAM(pause_vf_canceled_from, pause_must_cancel_from_gen_params),
+	KUNIT_CASE(pause_vf_fails_on_send),
+	KUNIT_CASE(pause_vf_rejected_by_guc),
+	KUNIT_CASE_SLOW(pause_vf_unconfirmed_by_guc),
+	KUNIT_CASE(pause_vf_canceled_by_flr),
+	KUNIT_CASE(pause_vf_canceled_by_stop),
+	KUNIT_CASE(pause_vf_canceled_by_restart),
+
+	KUNIT_CASE_PARAM(resume_vf_from, resume_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(resume_vf_needs_retry_from, resume_must_pass_from_gen_params),
+	KUNIT_CASE_PARAM(resume_vf_refused_from, resume_must_fail_from_gen_params),
+	KUNIT_CASE_PARAM(resume_vf_canceled_from, resume_must_cancel_from_gen_params),
+	KUNIT_CASE(resume_vf_fails_on_send),
+	KUNIT_CASE(resume_vf_rejected_by_guc),
+	KUNIT_CASE(resume_vf_canceled_by_flr),
+	KUNIT_CASE(resume_vf_canceled_by_stop),
+	KUNIT_CASE(resume_vf_canceled_by_restart),
+
+	{}
+};
+
+static struct kunit_suite pf_control_suite = {
+	.name = "pf_control",
+	.test_cases = pf_control_test_cases,
+	.init = pf_control_test_init,
+};
+
+kunit_test_suite(pf_control_suite);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
index e91c71d768ff..4863d79f72e0 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
@@ -4,6 +4,7 @@
  */
 
 #include <drm/drm_managed.h>
+#include <kunit/static_stub.h>
 
 #include "abi/guc_actions_sriov_abi.h"
 
@@ -196,6 +197,8 @@ static const char *control_bit_to_string(enum xe_gt_sriov_control_bits bit)
 
 static unsigned long pf_get_default_timeout(enum xe_gt_sriov_control_bits bit)
 {
+	KUNIT_STATIC_STUB_REDIRECT(pf_get_default_timeout, bit);
+
 	switch (bit) {
 	case XE_GT_SRIOV_STATE_FLR_WAIT_GUC:
 	case XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC:
@@ -1458,3 +1461,7 @@ void xe_gt_sriov_pf_control_restart(struct xe_gt *gt)
 	for (n = 1; n <= totalvfs; n++)
 		pf_enter_vf_ready(gt, n);
 }
+
+#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_gt_sriov_pf_control_kunit.c"
+#endif
-- 
2.43.0



More information about the Intel-xe mailing list