[PATCH RFC 7/7] PCI/P2PDMA: Add KUnit tests for HMAT provider ranking
Leon Romanovsky <[email protected]> Wed, 12 Aug 2026 22:47:44 +0300
| Newsgroups | gmane.linux.acpi.devel,gmane.linux.kernel,gmane.linux.kernel.pci |
|---|---|
| Message-ID | <[email protected]> |
From: Leon Romanovsky <[email protected]> HMAT provider selection combines directional coordinates across every client. A regression in metric completeness, worst-path aggregation, or precedence could select a slower provider or change the distance fallback. Protect the ranking contract across complete, partial, direct, and platform-authorized paths, including its ordering and distance tie-break. Signed-off-by: Leon Romanovsky <[email protected]> --- drivers/pci/Kconfig | 3 +- drivers/pci/p2pdma.c | 22 +--- drivers/pci/p2pdma_test.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 21 ++++ 4 files changed, 294 insertions(+), 19 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 47b92b289faf..b46833ff10e1 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -247,7 +247,8 @@ config PCI_P2PDMA_HMAT_KUNIT_TEST default KUNIT_ALL_TESTS help Enable KUnit coverage for authorizing cross-host-bridge P2P DMA - through ordered HMAT paths. + through ordered HMAT paths and ranking providers by their + ordered-path performance. For more information on KUnit and unit tests in general, refer to the KUnit documentation in Documentation/dev-tools/kunit/. diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index fe3e7ea0776c..ca822f60e9d3 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -70,22 +70,6 @@ struct pci_p2pdma_pagemap { struct p2pdma_provider *mem; }; -/* Provider rank classes, ordered from most to least preferable. */ -enum pci_p2pdma_rank_type { - PCI_P2PDMA_RANK_DIRECT, - PCI_P2PDMA_RANK_HMAT_BANDWIDTH, - PCI_P2PDMA_RANK_HMAT_LATENCY, - PCI_P2PDMA_RANK_DISTANCE, -}; - -struct pci_p2pdma_rank { - enum pci_p2pdma_rank_type type; - u32 bandwidth; - u32 latency; - int distance; - bool latency_valid; -}; - static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap) { return container_of(pgmap, struct pci_p2pdma_pagemap, pgmap); @@ -1016,7 +1000,7 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client, } EXPORT_SYMBOL_IF_KUNIT(calc_map_type_and_dist); -static int +VISIBLE_IF_KUNIT int pci_p2pdma_rank_cmp(const struct pci_p2pdma_rank *a, const struct pci_p2pdma_rank *b) { @@ -1039,13 +1023,14 @@ pci_p2pdma_rank_cmp(const struct pci_p2pdma_rank *a, return 0; } +EXPORT_SYMBOL_IF_KUNIT(pci_p2pdma_rank_cmp); /* * P2P bandwidth is limited by the slowest direction and client path, while * the largest latency bounds the worst path. Only compare a metric when every * host-bridge path supplies both its read and write values. */ -static int +VISIBLE_IF_KUNIT int pci_p2pdma_rank_many(struct pci_dev *provider, struct device **clients, int num_clients, bool verbose, struct pci_p2pdma_rank *rank) @@ -1124,6 +1109,7 @@ pci_p2pdma_rank_many(struct pci_dev *provider, struct device **clients, return 0; } +EXPORT_SYMBOL_IF_KUNIT(pci_p2pdma_rank_many); /** * pci_p2pdma_distance_many - Determine the cumulative distance between diff --git a/drivers/pci/p2pdma_test.c b/drivers/pci/p2pdma_test.c index 1430185f69b7..8475c40c0eaf 100644 --- a/drivers/pci/p2pdma_test.c +++ b/drivers/pci/p2pdma_test.c @@ -92,6 +92,15 @@ struct hmat_route_ctx { bool unexpected_device; }; +static void hmat_test_device_release(struct device *dev) +{ +} + +static void hmat_test_put_device(void *data) +{ + put_device(data); +} + static struct pci_dev *hmat_add_root_device(struct kunit *test, u8 busnr) { struct pci_host_bridge *host; @@ -104,6 +113,13 @@ static struct pci_dev *hmat_add_root_device(struct kunit *test, u8 busnr) KUNIT_ASSERT_NOT_NULL(test, bus); pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, pdev); + device_initialize(&pdev->dev); + pdev->dev.bus = &pci_bus_type; + pdev->dev.release = hmat_test_device_release; + KUNIT_ASSERT_EQ(test, + kunit_add_action_or_reset(test, hmat_test_put_device, + &pdev->dev), + 0); bus->number = busnr; bus->bridge = &host->dev; @@ -194,8 +210,259 @@ static void pci_p2pdma_hmat_route_test(struct kunit *test) } } +struct hmat_rank_case { + const char *desc; + struct access_coordinate coord[2]; + enum pci_p2pdma_rank_type expected_type; + u32 expected_bandwidth; + u32 expected_latency; + bool expected_latency_valid; +}; + +static const struct hmat_rank_case hmat_rank_cases[] = { + { + .desc = "bandwidth_and_latency", + .coord = { + { + .read_bandwidth = 100, + .write_bandwidth = 90, + .read_latency = 7, + .write_latency = 8, + }, + { + .read_bandwidth = 75, + .write_bandwidth = 80, + .read_latency = 10, + .write_latency = 9, + }, + }, + .expected_type = PCI_P2PDMA_RANK_HMAT_BANDWIDTH, + .expected_bandwidth = 75, + .expected_latency = 10, + .expected_latency_valid = true, + }, + { + .desc = "bandwidth_only", + .coord = { + { + .read_bandwidth = 100, + .write_bandwidth = 90, + }, + { + .read_bandwidth = 75, + .write_bandwidth = 80, + }, + }, + .expected_type = PCI_P2PDMA_RANK_HMAT_BANDWIDTH, + .expected_bandwidth = 75, + }, + { + .desc = "latency_only", + .coord = { + { + .read_latency = 7, + .write_latency = 8, + }, + { + .read_latency = 10, + .write_latency = 9, + }, + }, + .expected_type = PCI_P2PDMA_RANK_HMAT_LATENCY, + .expected_latency = 10, + .expected_latency_valid = true, + }, + { + .desc = "incomplete_coordinates", + .coord = { + { + .read_bandwidth = 100, + .write_bandwidth = 90, + .read_latency = 7, + .write_latency = 8, + }, + { + .read_bandwidth = 75, + .read_latency = 10, + }, + }, + .expected_type = PCI_P2PDMA_RANK_DISTANCE, + }, +}; + +static void hmat_rank_case_desc(const struct hmat_rank_case *c, char *desc) +{ + strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(hmat_rank, hmat_rank_cases, hmat_rank_case_desc); + +struct hmat_rank_ctx { + const struct hmat_rank_case *test_case; + struct pci_dev *provider; + struct pci_dev *client[2]; + int lookup_calls; + bool unexpected_lookup; +}; + +static int pci_host_bridge_rank_pxm_stub(struct pci_dev *pdev) +{ + struct kunit *test = kunit_get_current_test(); + struct hmat_rank_ctx *ctx = test->priv; + unsigned int i; + + if (pdev == ctx->provider) + return 22; + for (i = 0; i < ARRAY_SIZE(ctx->client); i++) + if (pdev == ctx->client[i]) + return 11 + i; + + ctx->unexpected_lookup = true; + return -ENODEV; +} + +static int acpi_get_p2p_rank_coordinates_stub(int initiator, int target, + enum hmat_p2p_class class, + struct access_coordinate *coord) +{ + struct kunit *test = kunit_get_current_test(); + struct hmat_rank_ctx *ctx = test->priv; + int index = initiator - 11; + + ctx->lookup_calls++; + if (index < 0 || index >= (int)ARRAY_SIZE(ctx->client) || target != 22 || + class != HMAT_P2P_NON_UIO) { + ctx->unexpected_lookup = true; + return -ENOENT; + } + + *coord = ctx->test_case->coord[index]; + return 0; +} + +static void pci_p2pdma_hmat_rank_many_test(struct kunit *test) +{ + const struct hmat_rank_case *test_case = test->param_value; + struct hmat_rank_ctx ctx = { .test_case = test_case }; + struct device *clients[ARRAY_SIZE(ctx.client)]; + struct pci_p2pdma_rank rank; + unsigned int i; + + ctx.provider = hmat_add_root_device(test, 0); + for (i = 0; i < ARRAY_SIZE(ctx.client); i++) { + ctx.client[i] = hmat_add_root_device(test, i + 1); + clients[i] = &ctx.client[i]->dev; + } + test->priv = &ctx; + + kunit_activate_static_stub(test, cpu_supports_p2pdma, + cpu_supports_p2pdma_stub); + kunit_activate_static_stub(test, pci_host_bridge_pxm, + pci_host_bridge_rank_pxm_stub); + kunit_activate_static_stub(test, acpi_get_p2p_coordinates, + acpi_get_p2p_rank_coordinates_stub); + + KUNIT_ASSERT_EQ(test, + pci_p2pdma_rank_many(ctx.provider, clients, + ARRAY_SIZE(clients), false, &rank), + 0); + KUNIT_EXPECT_EQ(test, rank.type, test_case->expected_type); + KUNIT_EXPECT_EQ(test, rank.distance, 4); + KUNIT_EXPECT_EQ(test, ctx.lookup_calls, 2); + KUNIT_EXPECT_FALSE(test, ctx.unexpected_lookup); + if (rank.type == PCI_P2PDMA_RANK_HMAT_BANDWIDTH) + KUNIT_EXPECT_EQ(test, rank.bandwidth, + test_case->expected_bandwidth); + if (rank.latency_valid) + KUNIT_EXPECT_EQ(test, rank.latency, + test_case->expected_latency); + KUNIT_EXPECT_EQ(test, rank.latency_valid, + test_case->expected_latency_valid); +} + +static void pci_p2pdma_hmat_rank_compare_test(struct kunit *test) +{ + struct pci_p2pdma_rank direct = { + .type = PCI_P2PDMA_RANK_DIRECT, + .distance = 8, + }; + struct pci_p2pdma_rank bandwidth = { + .type = PCI_P2PDMA_RANK_HMAT_BANDWIDTH, + .bandwidth = 100, + .latency = 20, + .distance = 4, + .latency_valid = true, + }; + struct pci_p2pdma_rank other = bandwidth; + + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&direct, &bandwidth), 0); + + other.bandwidth = 90; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + other = bandwidth; + other.latency = 30; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + other = bandwidth; + other.latency_valid = false; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + other = bandwidth; + other.distance = 5; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + KUNIT_EXPECT_EQ(test, pci_p2pdma_rank_cmp(&bandwidth, &bandwidth), 0); + + bandwidth.type = PCI_P2PDMA_RANK_HMAT_LATENCY; + other = bandwidth; + other.type = PCI_P2PDMA_RANK_DISTANCE; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + other = bandwidth; + other.latency = 30; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); + + bandwidth.type = PCI_P2PDMA_RANK_DISTANCE; + bandwidth.distance = 4; + other = bandwidth; + other.distance = 5; + KUNIT_EXPECT_LT(test, pci_p2pdma_rank_cmp(&bandwidth, &other), 0); +} + +static void pci_p2pdma_direct_rank_test(struct kunit *test) +{ + struct pci_dev *provider = hmat_add_root_device(test, 0); + struct device *clients[] = { &provider->dev }; + struct pci_p2pdma_rank rank; + + KUNIT_ASSERT_EQ(test, + pci_p2pdma_rank_many(provider, clients, + ARRAY_SIZE(clients), false, &rank), + 0); + KUNIT_EXPECT_EQ(test, rank.type, PCI_P2PDMA_RANK_DIRECT); + KUNIT_EXPECT_EQ(test, rank.distance, 0); +} + +static void pci_p2pdma_distance_rank_fallback_test(struct kunit *test) +{ + struct pci_dev *provider = hmat_add_root_device(test, 0); + struct pci_dev *client = hmat_add_root_device(test, 1); + struct device *clients[] = { &client->dev }; + struct pci_p2pdma_rank rank; + + kunit_activate_static_stub(test, cpu_supports_p2pdma, + cpu_supports_p2pdma_true_stub); + + KUNIT_ASSERT_EQ(test, + pci_p2pdma_rank_many(provider, clients, + ARRAY_SIZE(clients), false, &rank), + 0); + KUNIT_EXPECT_EQ(test, rank.type, PCI_P2PDMA_RANK_DISTANCE); + KUNIT_EXPECT_EQ(test, rank.distance, 2); +} + static struct kunit_case pci_p2pdma_hmat_test_cases[] = { KUNIT_CASE_PARAM(pci_p2pdma_hmat_route_test, hmat_route_gen_params), + KUNIT_CASE_PARAM(pci_p2pdma_hmat_rank_many_test, hmat_rank_gen_params), + KUNIT_CASE(pci_p2pdma_hmat_rank_compare_test), + KUNIT_CASE(pci_p2pdma_direct_rank_test), + KUNIT_CASE(pci_p2pdma_distance_rank_fallback_test), {} }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 7a44158fdbd0..9872e081ec07 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1092,6 +1092,22 @@ enum pci_acs_p2pdma_state { PCI_ACS_P2PDMA_NOT_SUPPORTED, /* no usable peer-to-peer route */ }; +/* Provider rank classes, ordered from most to least preferable. */ +enum pci_p2pdma_rank_type { + PCI_P2PDMA_RANK_DIRECT, + PCI_P2PDMA_RANK_HMAT_BANDWIDTH, + PCI_P2PDMA_RANK_HMAT_LATENCY, + PCI_P2PDMA_RANK_DISTANCE, +}; + +struct pci_p2pdma_rank { + enum pci_p2pdma_rank_type type; + u32 bandwidth; + u32 latency; + int distance; + bool latency_valid; +}; + #if IS_ENABLED(CONFIG_KUNIT) bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags, enum pci_acs_scope scope); @@ -1103,6 +1119,11 @@ enum pci_p2pdma_map_type calc_map_type_and_dist(struct pci_dev *provider, int *dist, bool verbose); int pci_host_bridge_pxm(struct pci_dev *pdev); bool cpu_supports_p2pdma(void); +int pci_p2pdma_rank_many(struct pci_dev *provider, struct device **clients, + int num_clients, bool verbose, + struct pci_p2pdma_rank *rank); +int pci_p2pdma_rank_cmp(const struct pci_p2pdma_rank *a, + const struct pci_p2pdma_rank *b); #endif #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags, -- 2.55.0