drm/xe/kunit: Add GuC Relay kunit tests
authorMichal Wajdeczko <michal.wajdeczko@intel.com>
Thu, 4 Jan 2024 22:20:30 +0000 (23:20 +0100)
committerMichal Wajdeczko <michal.wajdeczko@intel.com>
Fri, 5 Jan 2024 15:25:54 +0000 (16:25 +0100)
Add few tests to make sure that some negative and normal use
scenarios of the GuC Relay are implemented correctly.

Reviewed-by: Piotr Piórkowski <piotr.piorkowski@intel.com>
Link: https://lore.kernel.org/r/20240104222031.277-10-michal.wajdeczko@intel.com
Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
drivers/gpu/drm/xe/tests/xe_guc_relay_test.c [new file with mode: 0644]
drivers/gpu/drm/xe/xe_guc_relay.c

diff --git a/drivers/gpu/drm/xe/tests/xe_guc_relay_test.c b/drivers/gpu/drm/xe/tests/xe_guc_relay_test.c
new file mode 100644 (file)
index 0000000..1370145
--- /dev/null
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#include <kunit/static_stub.h>
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+
+#include "xe_device.h"
+#include "xe_kunit_helpers.h"
+#include "xe_pci_test.h"
+
+#define TEST_RID       1234
+#define TEST_VFID      5
+#define TEST_LEN       6
+#define TEST_ACTION    0xa
+#define TEST_DATA(n)   (0xd0 + (n))
+
+static int replacement_relay_get_totalvfs(struct xe_guc_relay *relay)
+{
+       return TEST_VFID;
+}
+
+static int relay_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_guc_relay *relay;
+       struct xe_device *xe;
+
+       test->priv = &fake;
+       xe_kunit_helper_xe_device_test_init(test);
+
+       xe = test->priv;
+       KUNIT_ASSERT_EQ(test, xe_sriov_init(xe), 0);
+
+       relay = &xe_device_get_gt(xe, 0)->uc.guc.relay;
+       kunit_activate_static_stub(test, relay_get_totalvfs,
+                                  replacement_relay_get_totalvfs);
+
+       KUNIT_ASSERT_EQ(test, xe_guc_relay_init(relay), 0);
+       KUNIT_EXPECT_TRUE(test, relay_is_ready(relay));
+       relay->last_rid = TEST_RID - 1;
+
+       test->priv = relay;
+       return 0;
+}
+
+static const u32 TEST_MSG[TEST_LEN] = {
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_EVENT) |
+       FIELD_PREP_CONST(GUC_HXG_EVENT_MSG_0_ACTION, TEST_ACTION) |
+       FIELD_PREP_CONST(GUC_HXG_EVENT_MSG_0_DATA0, TEST_DATA(0)),
+       TEST_DATA(1), TEST_DATA(2), TEST_DATA(3), TEST_DATA(4),
+};
+
+static int replacement_xe_guc_ct_send_recv_always_fails(struct xe_guc_ct *ct,
+                                                       const u32 *msg, u32 len,
+                                                       u32 *response_buffer)
+{
+       struct kunit *test = kunit_get_current_test();
+
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ct);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, msg);
+       KUNIT_ASSERT_GE(test, len, GUC_HXG_MSG_MIN_LEN);
+
+       return -ECOMM;
+}
+
+static int replacement_xe_guc_ct_send_recv_expects_pf2guc_relay(struct xe_guc_ct *ct,
+                                                               const u32 *msg, u32 len,
+                                                               u32 *response_buffer)
+{
+       struct kunit *test = kunit_get_current_test();
+
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ct);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, msg);
+       KUNIT_ASSERT_GE(test, len, PF2GUC_RELAY_TO_VF_REQUEST_MSG_MIN_LEN);
+       KUNIT_ASSERT_EQ(test, len, PF2GUC_RELAY_TO_VF_REQUEST_MSG_MIN_LEN + TEST_LEN);
+       KUNIT_EXPECT_EQ(test, GUC_HXG_ORIGIN_HOST, FIELD_GET(GUC_HXG_MSG_0_ORIGIN, msg[0]));
+       KUNIT_EXPECT_EQ(test, GUC_HXG_TYPE_REQUEST, FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]));
+       KUNIT_EXPECT_EQ(test, XE_GUC_ACTION_PF2GUC_RELAY_TO_VF,
+                       FIELD_GET(GUC_HXG_REQUEST_MSG_0_ACTION, msg[0]));
+       KUNIT_EXPECT_EQ(test, TEST_VFID,
+                       FIELD_GET(PF2GUC_RELAY_TO_VF_REQUEST_MSG_1_VFID, msg[1]));
+       KUNIT_EXPECT_EQ(test, TEST_RID,
+                       FIELD_GET(PF2GUC_RELAY_TO_VF_REQUEST_MSG_2_RELAY_ID, msg[2]));
+       KUNIT_EXPECT_MEMEQ(test, TEST_MSG, msg + PF2GUC_RELAY_TO_VF_REQUEST_MSG_MIN_LEN,
+                          sizeof(u32) * TEST_LEN);
+       return 0;
+}
+
+static const u32 test_guc2pf[GUC2PF_RELAY_FROM_VF_EVENT_MSG_MAX_LEN] = {
+       /* transport */
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_GUC) |
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_EVENT) |
+       FIELD_PREP_CONST(GUC_HXG_EVENT_MSG_0_ACTION, XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF),
+       FIELD_PREP_CONST(GUC2PF_RELAY_FROM_VF_EVENT_MSG_1_VFID, TEST_VFID),
+       FIELD_PREP_CONST(GUC2PF_RELAY_FROM_VF_EVENT_MSG_2_RELAY_ID, TEST_RID),
+       /* payload */
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_RESPONSE_SUCCESS),
+};
+
+static const u32 test_guc2vf[GUC2VF_RELAY_FROM_PF_EVENT_MSG_MAX_LEN] = {
+       /* transport */
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_GUC) |
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_EVENT) |
+       FIELD_PREP_CONST(GUC_HXG_EVENT_MSG_0_ACTION, XE_GUC_ACTION_GUC2VF_RELAY_FROM_PF),
+       FIELD_PREP_CONST(GUC2VF_RELAY_FROM_PF_EVENT_MSG_1_RELAY_ID, TEST_RID),
+       /* payload */
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+       FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_RESPONSE_SUCCESS),
+};
+
+static void pf_rejects_guc2pf_too_short(struct kunit *test)
+{
+       const u32 len = GUC2PF_RELAY_FROM_VF_EVENT_MSG_MIN_LEN - 1;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2pf;
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, xe_guc_relay_process_guc2pf(relay, msg, len));
+}
+
+static void pf_rejects_guc2pf_too_long(struct kunit *test)
+{
+       const u32 len = GUC2PF_RELAY_FROM_VF_EVENT_MSG_MAX_LEN + 1;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2pf;
+
+       KUNIT_ASSERT_EQ(test, -EMSGSIZE, xe_guc_relay_process_guc2pf(relay, msg, len));
+}
+
+static void pf_rejects_guc2pf_no_payload(struct kunit *test)
+{
+       const u32 len = GUC2PF_RELAY_FROM_VF_EVENT_MSG_MIN_LEN;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2pf;
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, xe_guc_relay_process_guc2pf(relay, msg, len));
+}
+
+static void pf_fails_no_payload(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       const u32 msg = 0;
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, relay_process_msg(relay, TEST_VFID, TEST_RID, &msg, 0));
+}
+
+static void pf_fails_bad_origin(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       static const u32 msg[] = {
+               FIELD_PREP_CONST(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_GUC) |
+               FIELD_PREP_CONST(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_RESPONSE_SUCCESS),
+       };
+       u32 len = ARRAY_SIZE(msg);
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, relay_process_msg(relay, TEST_VFID, TEST_RID, msg, len));
+}
+
+static void pf_fails_bad_type(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       const u32 msg[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, 4), /* only 4 is undefined */
+       };
+       u32 len = ARRAY_SIZE(msg);
+
+       KUNIT_ASSERT_EQ(test, -EBADRQC, relay_process_msg(relay, TEST_VFID, TEST_RID, msg, len));
+}
+
+static void pf_txn_reports_error(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       struct relay_transaction *txn;
+
+       txn = __relay_get_transaction(relay, false, TEST_VFID, TEST_RID,
+                                     TEST_MSG, TEST_LEN, NULL, 0);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, txn);
+
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_always_fails);
+       KUNIT_EXPECT_EQ(test, -ECOMM, relay_send_transaction(relay, txn));
+
+       relay_release_transaction(relay, txn);
+}
+
+static void pf_txn_sends_pf2guc(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       struct relay_transaction *txn;
+
+       txn = __relay_get_transaction(relay, false, TEST_VFID, TEST_RID,
+                                     TEST_MSG, TEST_LEN, NULL, 0);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, txn);
+
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_expects_pf2guc_relay);
+       KUNIT_ASSERT_EQ(test, 0, relay_send_transaction(relay, txn));
+
+       relay_release_transaction(relay, txn);
+}
+
+static void pf_sends_pf2guc(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_expects_pf2guc_relay);
+       KUNIT_ASSERT_EQ(test, 0,
+                       xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                               TEST_MSG, TEST_LEN, NULL, 0));
+}
+
+static int replacement_xe_guc_ct_send_recv_loopback_relay(struct xe_guc_ct *ct,
+                                                         const u32 *msg, u32 len,
+                                                         u32 *response_buffer)
+{
+       struct kunit *test = kunit_get_current_test();
+       struct xe_guc_relay *relay = test->priv;
+       u32 *reply = kunit_kzalloc(test, len * sizeof(u32), GFP_KERNEL);
+       int (*guc2relay)(struct xe_guc_relay *, const u32 *, u32);
+       u32 action;
+       int err;
+
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ct);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, msg);
+       KUNIT_ASSERT_GE(test, len, GUC_HXG_MSG_MIN_LEN);
+       KUNIT_ASSERT_EQ(test, GUC_HXG_TYPE_REQUEST,
+                       FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]));
+       KUNIT_ASSERT_GE(test, len, GUC_HXG_REQUEST_MSG_MIN_LEN);
+       KUNIT_ASSERT_NOT_NULL(test, reply);
+
+       switch (FIELD_GET(GUC_HXG_REQUEST_MSG_0_ACTION, msg[0])) {
+       case XE_GUC_ACTION_PF2GUC_RELAY_TO_VF:
+               KUNIT_ASSERT_GE(test, len, PF2GUC_RELAY_TO_VF_REQUEST_MSG_MIN_LEN);
+               action = XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF;
+               guc2relay = xe_guc_relay_process_guc2pf;
+               break;
+       case XE_GUC_ACTION_VF2GUC_RELAY_TO_PF:
+               KUNIT_ASSERT_GE(test, len, VF2GUC_RELAY_TO_PF_REQUEST_MSG_MIN_LEN);
+               action = XE_GUC_ACTION_GUC2VF_RELAY_FROM_PF;
+               guc2relay = xe_guc_relay_process_guc2vf;
+               break;
+       default:
+               KUNIT_FAIL(test, "bad RELAY action %#x", msg[0]);
+               return -EINVAL;
+       }
+
+       reply[0] = FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_GUC) |
+                  FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_EVENT) |
+                  FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION, action);
+       memcpy(reply + 1, msg + 1, sizeof(u32) * (len - 1));
+
+       err = guc2relay(relay, reply, len);
+       KUNIT_EXPECT_EQ(test, err, 0);
+
+       return err;
+}
+
+static void test_requires_relay_testloop(struct kunit *test)
+{
+       /*
+        * The debug relay action GUC_RELAY_ACTION_VFXPF_TESTLOOP is available
+        * only on builds with CONFIG_DRM_XE_DEBUG_SRIOV enabled.
+        * See "kunit.py --kconfig_add" option if it's missing.
+        */
+       if (!IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV))
+               kunit_skip(test, "requires %s\n", __stringify(CONFIG_DRM_XE_DEBUG_SRIOV));
+}
+
+static void pf_loopback_nop(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       u32 request[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_RELAY_ACTION_VFXPF_TESTLOOP) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_DATA0, VFXPF_TESTLOOP_OPCODE_NOP),
+       };
+       u32 response[GUC_HXG_RESPONSE_MSG_MIN_LEN];
+       int ret;
+
+       test_requires_relay_testloop(test);
+
+       kunit_activate_static_stub(test, relay_kick_worker, relay_process_incoming_action);
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_loopback_relay);
+       ret = xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                     request, ARRAY_SIZE(request),
+                                     response, ARRAY_SIZE(response));
+       KUNIT_ASSERT_EQ(test, ret, GUC_HXG_RESPONSE_MSG_MIN_LEN);
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_MSG_0_ORIGIN, response[0]),
+                       GUC_HXG_ORIGIN_HOST);
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_MSG_0_TYPE, response[0]),
+                       GUC_HXG_TYPE_RESPONSE_SUCCESS);
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_RESPONSE_MSG_0_DATA0, response[0]), 0);
+}
+
+static void pf_loopback_echo(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       u32 request[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_RELAY_ACTION_VFXPF_TESTLOOP) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_DATA0, VFXPF_TESTLOOP_OPCODE_ECHO),
+               TEST_DATA(1), TEST_DATA(2), TEST_DATA(3), TEST_DATA(4),
+       };
+       u32 response[ARRAY_SIZE(request)];
+       unsigned int n;
+       int ret;
+
+       test_requires_relay_testloop(test);
+
+       kunit_activate_static_stub(test, relay_kick_worker, relay_process_incoming_action);
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_loopback_relay);
+       ret = xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                     request, ARRAY_SIZE(request),
+                                     response, ARRAY_SIZE(response));
+       KUNIT_ASSERT_EQ(test, ret, ARRAY_SIZE(response));
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_MSG_0_ORIGIN, response[0]),
+                       GUC_HXG_ORIGIN_HOST);
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_MSG_0_TYPE, response[0]),
+                       GUC_HXG_TYPE_RESPONSE_SUCCESS);
+       KUNIT_EXPECT_EQ(test, FIELD_GET(GUC_HXG_RESPONSE_MSG_0_DATA0, response[0]),
+                       ARRAY_SIZE(response));
+       for (n = GUC_HXG_RESPONSE_MSG_MIN_LEN; n < ret; n++)
+               KUNIT_EXPECT_EQ(test, request[n], response[n]);
+}
+
+static void pf_loopback_fail(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       u32 request[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_RELAY_ACTION_VFXPF_TESTLOOP) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_DATA0, VFXPF_TESTLOOP_OPCODE_FAIL),
+       };
+       u32 response[GUC_HXG_RESPONSE_MSG_MIN_LEN];
+       int ret;
+
+       test_requires_relay_testloop(test);
+
+       kunit_activate_static_stub(test, relay_kick_worker, relay_process_incoming_action);
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_loopback_relay);
+       ret = xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                     request, ARRAY_SIZE(request),
+                                     response, ARRAY_SIZE(response));
+       KUNIT_ASSERT_EQ(test, ret, -EREMOTEIO);
+}
+
+static void pf_loopback_busy(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       u32 request[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_RELAY_ACTION_VFXPF_TESTLOOP) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_DATA0, VFXPF_TESTLOOP_OPCODE_BUSY),
+               TEST_DATA(0xb),
+       };
+       u32 response[GUC_HXG_RESPONSE_MSG_MIN_LEN];
+       int ret;
+
+       test_requires_relay_testloop(test);
+
+       kunit_activate_static_stub(test, relay_testonly_nop, relay_process_incoming_action);
+       kunit_activate_static_stub(test, relay_kick_worker, relay_process_incoming_action);
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_loopback_relay);
+       ret = xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                     request, ARRAY_SIZE(request),
+                                     response, ARRAY_SIZE(response));
+       KUNIT_ASSERT_EQ(test, ret, GUC_HXG_RESPONSE_MSG_MIN_LEN);
+}
+
+static void pf_loopback_retry(struct kunit *test)
+{
+       struct xe_guc_relay *relay = test->priv;
+       u32 request[] = {
+               FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
+               FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_RELAY_ACTION_VFXPF_TESTLOOP) |
+               FIELD_PREP(GUC_HXG_REQUEST_MSG_0_DATA0, VFXPF_TESTLOOP_OPCODE_RETRY),
+               TEST_DATA(0xd), TEST_DATA(0xd),
+       };
+       u32 response[GUC_HXG_RESPONSE_MSG_MIN_LEN];
+       int ret;
+
+       test_requires_relay_testloop(test);
+
+       kunit_activate_static_stub(test, relay_kick_worker, relay_process_incoming_action);
+       kunit_activate_static_stub(test, xe_guc_ct_send_recv,
+                                  replacement_xe_guc_ct_send_recv_loopback_relay);
+       ret = xe_guc_relay_send_to_vf(relay, TEST_VFID,
+                                     request, ARRAY_SIZE(request),
+                                     response, ARRAY_SIZE(response));
+       KUNIT_ASSERT_EQ(test, ret, GUC_HXG_RESPONSE_MSG_MIN_LEN);
+}
+
+static struct kunit_case pf_relay_test_cases[] = {
+       KUNIT_CASE(pf_rejects_guc2pf_too_short),
+       KUNIT_CASE(pf_rejects_guc2pf_too_long),
+       KUNIT_CASE(pf_rejects_guc2pf_no_payload),
+       KUNIT_CASE(pf_fails_no_payload),
+       KUNIT_CASE(pf_fails_bad_origin),
+       KUNIT_CASE(pf_fails_bad_type),
+       KUNIT_CASE(pf_txn_reports_error),
+       KUNIT_CASE(pf_txn_sends_pf2guc),
+       KUNIT_CASE(pf_sends_pf2guc),
+       KUNIT_CASE(pf_loopback_nop),
+       KUNIT_CASE(pf_loopback_echo),
+       KUNIT_CASE(pf_loopback_fail),
+       KUNIT_CASE_SLOW(pf_loopback_busy),
+       KUNIT_CASE_SLOW(pf_loopback_retry),
+       {}
+};
+
+static struct kunit_suite pf_relay_suite = {
+       .name = "pf_relay",
+       .test_cases = pf_relay_test_cases,
+       .init = relay_test_init,
+};
+
+static void vf_rejects_guc2vf_too_short(struct kunit *test)
+{
+       const u32 len = GUC2VF_RELAY_FROM_PF_EVENT_MSG_MIN_LEN - 1;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2vf;
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, xe_guc_relay_process_guc2vf(relay, msg, len));
+}
+
+static void vf_rejects_guc2vf_too_long(struct kunit *test)
+{
+       const u32 len = GUC2VF_RELAY_FROM_PF_EVENT_MSG_MAX_LEN + 1;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2vf;
+
+       KUNIT_ASSERT_EQ(test, -EMSGSIZE, xe_guc_relay_process_guc2vf(relay, msg, len));
+}
+
+static void vf_rejects_guc2vf_no_payload(struct kunit *test)
+{
+       const u32 len = GUC2VF_RELAY_FROM_PF_EVENT_MSG_MIN_LEN;
+       struct xe_guc_relay *relay = test->priv;
+       const u32 *msg = test_guc2vf;
+
+       KUNIT_ASSERT_EQ(test, -EPROTO, xe_guc_relay_process_guc2vf(relay, msg, len));
+}
+
+static struct kunit_case vf_relay_test_cases[] = {
+       KUNIT_CASE(vf_rejects_guc2vf_too_short),
+       KUNIT_CASE(vf_rejects_guc2vf_too_long),
+       KUNIT_CASE(vf_rejects_guc2vf_no_payload),
+       {}
+};
+
+static struct kunit_suite vf_relay_suite = {
+       .name = "vf_relay",
+       .test_cases = vf_relay_test_cases,
+       .init = relay_test_init,
+};
+
+static void xe_drops_guc2pf_if_not_ready(struct kunit *test)
+{
+       struct xe_device *xe = test->priv;
+       struct xe_guc_relay *relay = &xe_device_get_gt(xe, 0)->uc.guc.relay;
+       const u32 *msg = test_guc2pf;
+       u32 len = GUC2PF_RELAY_FROM_VF_EVENT_MSG_MIN_LEN + GUC_RELAY_MSG_MIN_LEN;
+
+       KUNIT_ASSERT_EQ(test, -ENODEV, xe_guc_relay_process_guc2pf(relay, msg, len));
+}
+
+static void xe_drops_guc2vf_if_not_ready(struct kunit *test)
+{
+       struct xe_device *xe = test->priv;
+       struct xe_guc_relay *relay = &xe_device_get_gt(xe, 0)->uc.guc.relay;
+       const u32 *msg = test_guc2vf;
+       u32 len = GUC2VF_RELAY_FROM_PF_EVENT_MSG_MIN_LEN + GUC_RELAY_MSG_MIN_LEN;
+
+       KUNIT_ASSERT_EQ(test, -ENODEV, xe_guc_relay_process_guc2vf(relay, msg, len));
+}
+
+static void xe_rejects_send_if_not_ready(struct kunit *test)
+{
+       struct xe_device *xe = test->priv;
+       struct xe_guc_relay *relay = &xe_device_get_gt(xe, 0)->uc.guc.relay;
+       u32 msg[GUC_RELAY_MSG_MIN_LEN];
+       u32 len = ARRAY_SIZE(msg);
+
+       KUNIT_ASSERT_EQ(test, -ENODEV, xe_guc_relay_send_to_pf(relay, msg, len, NULL, 0));
+       KUNIT_ASSERT_EQ(test, -ENODEV, relay_send_to(relay, TEST_VFID, msg, len, NULL, 0));
+}
+
+static struct kunit_case no_relay_test_cases[] = {
+       KUNIT_CASE(xe_drops_guc2pf_if_not_ready),
+       KUNIT_CASE(xe_drops_guc2vf_if_not_ready),
+       KUNIT_CASE(xe_rejects_send_if_not_ready),
+       {}
+};
+
+static struct kunit_suite no_relay_suite = {
+       .name = "no_relay",
+       .test_cases = no_relay_test_cases,
+       .init = xe_kunit_helper_xe_device_test_init,
+};
+
+kunit_test_suites(&no_relay_suite,
+                 &pf_relay_suite,
+                 &vf_relay_suite);
index 0e7383256408225247865f561978b4ad9922cfca..b772088979bddc5a36e8d0c1a8297a33552c106c 100644 (file)
@@ -8,6 +8,8 @@
 
 #include <drm/drm_managed.h>
 
+#include <kunit/static_stub.h>
+
 #include "abi/guc_actions_sriov_abi.h"
 #include "abi/guc_relay_actions_abi.h"
 #include "abi/guc_relay_communication_abi.h"
@@ -60,6 +62,7 @@ static int relay_get_totalvfs(struct xe_guc_relay *relay)
        struct xe_device *xe = relay_to_xe(relay);
        struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
 
+       KUNIT_STATIC_STUB_REDIRECT(relay_get_totalvfs, relay);
        return IS_SRIOV_VF(xe) ? 0 : pci_sriov_get_totalvfs(pdev);
 }
 
@@ -392,6 +395,11 @@ static u32 prepare_error_reply(u32 *msg, u32 error, u32 hint)
        return GUC_HXG_FAILURE_MSG_LEN;
 }
 
+static void relay_testonly_nop(struct xe_guc_relay *relay)
+{
+       KUNIT_STATIC_STUB_REDIRECT(relay_testonly_nop, relay);
+}
+
 static int relay_send_message_and_wait(struct xe_guc_relay *relay,
                                       struct relay_transaction *txn,
                                       u32 *buf, u32 buf_size)
@@ -434,8 +442,10 @@ wait:
                reinit_completion(&txn->done);
                if (txn->reply == -EAGAIN)
                        goto resend;
-               if (txn->reply == -EBUSY)
+               if (txn->reply == -EBUSY) {
+                       relay_testonly_nop(relay);
                        goto wait;
+               }
                if (txn->reply > 0)
                        ret = from_relay_error(txn->reply);
                else
@@ -751,6 +761,7 @@ static bool relay_needs_worker(struct xe_guc_relay *relay)
 
 static void relay_kick_worker(struct xe_guc_relay *relay)
 {
+       KUNIT_STATIC_STUB_REDIRECT(relay_kick_worker, relay);
        queue_work(relay_to_xe(relay)->sriov.wq, &relay->worker);
 }
 
@@ -849,7 +860,7 @@ int xe_guc_relay_process_guc2vf(struct xe_guc_relay *relay, const u32 *msg, u32
        relay_assert(relay, FIELD_GET(GUC_HXG_EVENT_MSG_0_ACTION, msg[0]) ==
                     XE_GUC_ACTION_GUC2VF_RELAY_FROM_PF);
 
-       if (unlikely(!IS_SRIOV_VF(relay_to_xe(relay))))
+       if (unlikely(!IS_SRIOV_VF(relay_to_xe(relay)) && !kunit_get_current_test()))
                return -EPERM;
 
        if (unlikely(!relay_is_ready(relay)))
@@ -895,7 +906,7 @@ int xe_guc_relay_process_guc2pf(struct xe_guc_relay *relay, const u32 *msg, u32
        relay_assert(relay, FIELD_GET(GUC_HXG_EVENT_MSG_0_ACTION, msg[0]) ==
                     XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF);
 
-       if (unlikely(!IS_SRIOV_PF(relay_to_xe(relay))))
+       if (unlikely(!IS_SRIOV_PF(relay_to_xe(relay)) && !kunit_get_current_test()))
                return -EPERM;
 
        if (unlikely(!relay_is_ready(relay)))
@@ -923,3 +934,7 @@ int xe_guc_relay_process_guc2pf(struct xe_guc_relay *relay, const u32 *msg, u32
        return err;
 }
 #endif
+
+#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_guc_relay_test.c"
+#endif