[PATCH v5 6/6] drm/xe/tests: Add KUnit tests for VF provisioning error handling
Satyanarayana K V P <[email protected]>
| Newsgroups | org.freedesktop.lists.intel-xe |
|---|---|
| Message-ID | <[email protected]> |
VF relies on the PF to provide a valid hardware configuration via GuC KLV responses. In unlikely event of PF malfunction or misconfiguration, a VF may receive incomplete, zero, or out-of-range values for its submission contexts, doorbells, VRAM or GGTT assignment. Add KUnit test cases that use the xe_guc_mmio_send_recv() stub to inject bad KLV responses and verify that VF can survive without crashing for the invalid configuration data received. Signed-off-by: Satyanarayana K V P <[email protected]> Cc: Michal Wajdeczko <[email protected]> --- V4 -> V5: - Added alignment tests for vram, GGTT base and size (Michal W). - Separated stubs for ctx, db, vram and GGTT (Michal W). - Added new test cases for xe_guc_mmio_send_recv() (Michal W). V3 -> V4: - Changed stub function from guc_action_query_single_klv32() and guc_action_query_single_klv64() to xe_guc_mmio_send_recv() (Michal W). - Fixed review comments (Michal W). V2 -> V3: - Renamed the test names. (Michal W). - Fixed review comments (Michal W). V1 -> V2: - Renamed the test file (Michal W). - Fixed review comments (Michal W). --- .../gpu/drm/xe/tests/xe_gt_sriov_vf_kunit.c | 322 ++++++++++++++++++ drivers/gpu/drm/xe/xe_gt_sriov_vf.c | 4 + 2 files changed, 326 insertions(+) create mode 100644 drivers/gpu/drm/xe/tests/xe_gt_sriov_vf_kunit.c diff --git a/drivers/gpu/drm/xe/tests/xe_gt_sriov_vf_kunit.c b/drivers/gpu/drm/xe/tests/xe_gt_sriov_vf_kunit.c new file mode 100644 index 000000000000..9756c069fbc6 --- /dev/null +++ b/drivers/gpu/drm/xe/tests/xe_gt_sriov_vf_kunit.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 AND MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <kunit/static_stub.h> +#include <kunit/test.h> + +#include "regs/xe_guc_regs.h" +#include "xe_device.h" +#include "xe_kunit_helpers.h" +#include "xe_pci_test.h" +#include "xe_guc.h" + +#define TEST_VRAM SZ_8G +#define TEST_GGTT_SIZE SZ_2G +#define TEST_CTXS 1024 +#define TEST_DBS 128 +#define TEST_GGTT_START 0xa0a0a000ull + +struct config { + u32 orig; + u32 query; + int expected_ret; + const char *name; +} vf_ctx_testcase[] = { + {TEST_CTXS, TEST_CTXS, 0, "same", }, + {0, 0, -ENODATA, "none", }, + {0, GUC_ID_MAX + 1, -EPROTO, "overflow", }, + {TEST_CTXS, 0, -EREMCHG, "lost", }, + {TEST_CTXS, TEST_CTXS / 2, -EREMCHG, "reduced", }, + {TEST_CTXS, TEST_CTXS * 2, -EREMCHG, "increased", }, +}, vf_db_testcase[] = { + {TEST_DBS, TEST_DBS, 0, "same", }, + {0, 0, 0, "none", }, + {0, GUC_NUM_DOORBELLS + 1, -EPROTO, "overflow", }, + {TEST_DBS, 0, -EREMCHG, "lost", }, + {TEST_DBS, TEST_DBS / 2, -EREMCHG, "reduced", }, + {TEST_DBS, GUC_NUM_DOORBELLS - 1, -EREMCHG, "increased", }, +}; + +struct config_vram { + u64 orig; + u64 query; + int expected_ret; + const char *name; +} vf_vram_testcase[] = { + {TEST_VRAM, TEST_VRAM, 0, "same", }, + {0, 0, -ENODATA, "none", }, + {TEST_VRAM / 2, TEST_VRAM + SZ_1G, -EREMCHG, "overflow", }, + {TEST_VRAM / 2, 0, -EREMCHG, "lost", }, + {TEST_VRAM / 2, TEST_VRAM / 4, -EREMCHG, "reduced", }, + {TEST_VRAM / 2, TEST_VRAM, -EREMCHG, "increased", }, + {TEST_VRAM - SZ_1M, TEST_VRAM - SZ_1M, -EREMCHG, "unaligned", }, +}; + +struct config_ggtt { + u64 start_orig; + u64 start_query; + u64 size_orig; + u64 size_query; + int expected_ret; + const char *name; +} vf_ggtt_testcase[] = { + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE, + TEST_GGTT_SIZE, 0, "same", }, + {0, 0, 0, + 0, -ENODATA, "none", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE, + TEST_GGTT_SIZE + SZ_1G, -EREMCHG, "overflow", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE, + 0, -ENODATA, "lost", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE, + TEST_GGTT_SIZE - SZ_1M, -EREMCHG, "reduced", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE, + TEST_GGTT_SIZE + SZ_1M, -EREMCHG, "increased", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE - SZ_2K, + TEST_GGTT_SIZE - SZ_2K, -EREMCHG, "unaligned_size_4K", }, + {TEST_GGTT_START, TEST_GGTT_START, TEST_GGTT_SIZE - SZ_8K, + TEST_GGTT_SIZE - SZ_8K, -EREMCHG, "unaligned_size_64K",}, + {TEST_GGTT_START - SZ_2K, TEST_GGTT_START - SZ_2K, TEST_GGTT_SIZE, + TEST_GGTT_SIZE, -EREMCHG, "unaligned_base_4k", }, + {TEST_GGTT_START - SZ_8K, TEST_GGTT_START - SZ_8K, TEST_GGTT_SIZE, + TEST_GGTT_SIZE, -EREMCHG, "unaligned_base_64k", }, +}; + +struct config_mmio { + int expected_ret; + const char *name; +} guc_mmio_resp_testcase[] = { + {0, "same", }, + {-ENODATA, "none", }, + {-EOVERFLOW, "overflow", }, + {-EREMCHG, "lost", }, + {-EOVERFLOW, "invalid", }, +}; + +KUNIT_ARRAY_PARAM_DESC(ctx_testcase, vf_ctx_testcase, name); +KUNIT_ARRAY_PARAM_DESC(db_testcase, vf_db_testcase, name); +KUNIT_ARRAY_PARAM_DESC(vram_testcase, vf_vram_testcase, name); +KUNIT_ARRAY_PARAM_DESC(ggtt_testcase, vf_ggtt_testcase, name); +KUNIT_ARRAY_PARAM_DESC(guc_mmio_resp, guc_mmio_resp_testcase, name); + +static int xe_guc_mmio_send_recv_stub_ctx(struct xe_guc *guc, const u32 *request, + u32 len, u32 *response_buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct config *c = test->param_value; + + if ((request[0] & 0xffff) != GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV) + return -EINVAL; + + if (request[1] != GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY) + return -EINVAL; + + response_buf[0] = sizeof(u32) / sizeof(u32); + response_buf[1] = c->query; + + return 0; +} + +static int xe_guc_mmio_send_recv_stub_db(struct xe_guc *guc, const u32 *request, + u32 len, u32 *response_buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct config *c = test->param_value; + + if ((request[0] & 0xffff) != GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV) + return -EINVAL; + + if (request[1] != GUC_KLV_VF_CFG_NUM_DOORBELLS_KEY) + return -EINVAL; + + response_buf[0] = sizeof(u32) / sizeof(u32); + response_buf[1] = c->query; + + return 0; +} + +static int xe_guc_mmio_send_recv_stub_vram(struct xe_guc *guc, const u32 *request, + u32 len, u32 *response_buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct config_vram *c = test->param_value; + + if ((request[0] & 0xffff) != GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV) + return -EINVAL; + + if (request[1] != GUC_KLV_VF_CFG_LMEM_SIZE_KEY) + return -EINVAL; + + response_buf[0] = sizeof(u64) / sizeof(u32); + response_buf[1] = c->query & 0xffffffff; + response_buf[2] = (c->query >> 32) & 0xffffffff; + + return 0; +} + +static int xe_guc_mmio_send_recv_stub_ggtt(struct xe_guc *guc, const u32 *request, + u32 len, u32 *response_buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct config_ggtt *c = test->param_value; + + if ((request[0] & 0xffff) != GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV) + return -EINVAL; + + if (request[1] != GUC_KLV_VF_CFG_GGTT_START_KEY && + request[1] != GUC_KLV_VF_CFG_GGTT_SIZE_KEY) + return -EINVAL; + + if (request[1] == GUC_KLV_VF_CFG_GGTT_START_KEY) { + response_buf[0] = sizeof(u64) / sizeof(u32); + response_buf[1] = c->start_query & 0xffffffff; + response_buf[2] = (c->start_query >> 32) & 0xffffffff; + } else { + response_buf[0] = sizeof(u64) / sizeof(u32); + response_buf[1] = c->size_query & 0xffffffff; + response_buf[2] = (c->size_query >> 32) & 0xffffffff; + } + + return 0; +} + +static int xe_guc_mmio_send_recv_stub(struct xe_guc *guc, const u32 *request, + u32 len, u32 *response_buf) +{ + struct kunit *test = kunit_get_current_test(); + const struct config_mmio *c = test->param_value; + + if ((request[0] & 0xffff) != GUC_ACTION_VF2GUC_QUERY_SINGLE_KLV) + return -EINVAL; + /** + * Let us take help of vf_get_ctxs_cfg() function to test responses + * from Guc. + */ + if (request[1] != GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY) + return -EINVAL; + + if (!strcmp(c->name, "same")) { + response_buf[0] = sizeof(u32) / sizeof(u32); + response_buf[1] = TEST_CTXS; + } else if (!strcmp(c->name, "none")) { + response_buf[0] = 0; + response_buf[1] = 0; + } else if (!strcmp(c->name, "overflow")) { + response_buf[0] = sizeof(u64) / sizeof(u32); + response_buf[1] = TEST_CTXS; + } else if (!strcmp(c->name, "lost")) { + response_buf[0] = sizeof(u32) / sizeof(u32); + response_buf[1] = 0; + } else if (!strcmp(c->name, "invalid")) { + response_buf[0] = 0xffff; + response_buf[1] = 0xffff; + } + + return 0; +} + +static void test_ctxs(struct kunit *test) +{ + struct xe_gt *gt = test->priv; + const struct config *c = test->param_value; + + gt->sriov.vf.self_config.num_ctxs = c->orig; + kunit_activate_static_stub(test, xe_guc_mmio_send_recv, + xe_guc_mmio_send_recv_stub_ctx); + KUNIT_EXPECT_EQ(test, vf_get_ctxs_cfg(gt), c->expected_ret); +} + +static void test_dbs(struct kunit *test) +{ + struct xe_gt *gt = test->priv; + const struct config *c = test->param_value; + + gt->sriov.vf.self_config.num_dbs = c->orig; + kunit_activate_static_stub(test, xe_guc_mmio_send_recv, + xe_guc_mmio_send_recv_stub_db); + KUNIT_EXPECT_EQ(test, vf_get_dbs_cfg(gt), c->expected_ret); +} + +static void test_vram(struct kunit *test) +{ + struct xe_gt *gt = test->priv; + const struct config_vram *c = test->param_value; + + gt->tile->sriov.vf.self_config.lmem_size = c->orig; + kunit_activate_static_stub(test, xe_guc_mmio_send_recv, + xe_guc_mmio_send_recv_stub_vram); + KUNIT_EXPECT_EQ(test, vf_get_lmem_info(gt), c->expected_ret); +} + +static void test_ggtt_size(struct kunit *test) +{ + const struct config_ggtt *c = test->param_value; + struct xe_gt *gt = test->priv; + struct xe_device *xe = gt_to_xe(gt); + + gt->tile->sriov.vf.self_config.ggtt_base = c->start_orig; + gt->tile->sriov.vf.self_config.ggtt_size = c->size_orig; + + if (!strcmp(c->name, "unaligned_size_64K") || + !strcmp(c->name, "unaligned_base_64k")) + xe->info.vram_flags = XE_VRAM_FLAGS_NEED64K; + + kunit_activate_static_stub(test, xe_guc_mmio_send_recv, + xe_guc_mmio_send_recv_stub_ggtt); + KUNIT_EXPECT_EQ(test, vf_get_ggtt_info(gt), c->expected_ret); +} + +static void test_guc_mmio(struct kunit *test) +{ + const struct config_mmio *c = test->param_value; + struct xe_gt *gt = test->priv; + + gt->sriov.vf.self_config.num_ctxs = TEST_CTXS; + kunit_activate_static_stub(test, xe_guc_mmio_send_recv, + xe_guc_mmio_send_recv_stub); + KUNIT_EXPECT_EQ(test, vf_get_ctxs_cfg(gt), c->expected_ret); +} + +static int vf_gt_config_test_init(struct kunit *test) +{ + struct xe_pci_fake_data fake = { + .sriov_mode = XE_SRIOV_MODE_VF, + .platform = XE_BATTLEMAGE, /* any random DGFX platform with SR-IOV */ + .subplatform = XE_SUBPLATFORM_NONE, + .graphics_verx100 = 2001, + }; + struct xe_device *xe; + struct xe_gt *gt; + + test->priv = &fake; + xe_kunit_helper_xe_device_test_init(test); + + xe = test->priv; + KUNIT_ASSERT_TRUE(test, IS_SRIOV_VF(xe)); + KUNIT_ASSERT_TRUE(test, IS_DGFX(xe)); + + gt = xe_root_mmio_gt(xe); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gt); + test->priv = gt; + + return 0; +} + +static struct kunit_case vf_gt_config_test_cases[] = { + KUNIT_CASE_PARAM(test_ctxs, ctx_testcase_gen_params), + KUNIT_CASE_PARAM(test_dbs, db_testcase_gen_params), + KUNIT_CASE_PARAM(test_vram, vram_testcase_gen_params), + KUNIT_CASE_PARAM(test_ggtt_size, ggtt_testcase_gen_params), + KUNIT_CASE_PARAM(test_guc_mmio, guc_mmio_resp_gen_params), + {} +}; + +static struct kunit_suite vf_gt_config_suite = { + .name = "vf_gt_config", + .test_cases = vf_gt_config_test_cases, + .init = vf_gt_config_test_init, +}; + +kunit_test_suite(vf_gt_config_suite); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c index 6c1878e4b1b2..05a6ad3348be 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c @@ -1685,3 +1685,7 @@ int xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt) return atomic_read(>->sriov.vf.migration.fixups_complete_count); } + +#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST) +#include "tests/xe_gt_sriov_vf_kunit.c" +#endif -- 2.53.0