From: Leon Romanovsky leonro@nvidia.com
A client that enables ATS for the whole device must never be handed a bus address, and Translation Blocking must reject its peer-to-peer traffic even when the caller asks for the default class. That has to hold for answers taken from the cache as well.
Cover those on the fabricated fabric, together with a client that declared per-mapping ATS keeping the per-class answers and a client without ATS answering for the Untranslated Requests it will issue.
Signed-off-by: Leon Romanovsky leonro@nvidia.com --- drivers/pci/p2pdma.c | 8 +-- drivers/pci/pci.h | 5 ++ drivers/pci/pci_acs_test.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index ca79af2e64c7..dfef69638782 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -1595,14 +1595,15 @@ void pcim_p2pdma_set_ats_per_mapping(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pcim_p2pdma_set_ats_per_mapping);
-static unsigned int pci_p2pdma_client_tlp_flags(struct pci_dev *client, - unsigned int tlp_flags) +VISIBLE_IF_KUNIT unsigned int +pci_p2pdma_client_tlp_flags(struct pci_dev *client, unsigned int tlp_flags) { if (client->ats_enabled) return tlp_flags | PCI_P2PDMA_TLP_TRANSLATED;
return tlp_flags & ~PCI_P2PDMA_TLP_TRANSLATED; } +EXPORT_SYMBOL_IF_KUNIT(pci_p2pdma_client_tlp_flags);
/* * A client that translates every address it is handed cannot be handed a bus @@ -1611,7 +1612,7 @@ static unsigned int pci_p2pdma_client_tlp_flags(struct pci_dev *client, * translation carries the IOVA to the host bridge, so that route has to work * as well. */ -static enum pci_p2pdma_map_type +VISIBLE_IF_KUNIT enum pci_p2pdma_map_type pci_p2pdma_client_map_type(struct pci_dev *provider, struct pci_dev *client, bool per_mapping, enum pci_p2pdma_map_type type) { @@ -1629,6 +1630,7 @@ pci_p2pdma_client_map_type(struct pci_dev *provider, struct pci_dev *client,
return PCI_P2PDMA_MAP_NOT_SUPPORTED; } +EXPORT_SYMBOL_IF_KUNIT(pci_p2pdma_client_map_type);
/** * pci_p2pdma_map_type_tlp - Determine the mapping type for P2PDMA transfers diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5bc703ff0c86..c705603b2145 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1117,6 +1117,11 @@ enum pci_p2pdma_map_type calc_map_type_and_dist(struct pci_dev *provider, int *dist, unsigned int tlp_flags, bool verbose); +unsigned int pci_p2pdma_client_tlp_flags(struct pci_dev *client, + unsigned int tlp_flags); +enum pci_p2pdma_map_type +pci_p2pdma_client_map_type(struct pci_dev *provider, struct pci_dev *client, + bool per_mapping, enum pci_p2pdma_map_type type); #endif
#ifdef CONFIG_PCI_QUIRKS diff --git a/drivers/pci/pci_acs_test.c b/drivers/pci/pci_acs_test.c index 6a2cd38d968f..a9a3b80ea0ac 100644 --- a/drivers/pci/pci_acs_test.c +++ b/drivers/pci/pci_acs_test.c @@ -698,6 +698,125 @@ static void acs_walk_unreadable_control_test(struct kunit *test) PCI_P2PDMA_MAP_NOT_SUPPORTED); }
+/* + * Route the way pci_p2pdma_map_type_tlp() does once it has a client: the + * client decides which flags apply, and the answer is then checked against + * what the client can be handed. + */ +static enum pci_p2pdma_map_type acs_client_map(struct acs_fabric *f, + bool per_mapping, + unsigned int tlp_flags) +{ + enum pci_p2pdma_map_type type; + int dist; + + if (!per_mapping) + tlp_flags = pci_p2pdma_client_tlp_flags(f->client, tlp_flags); + type = calc_map_type_and_dist(f->provider, f->client, &dist, tlp_flags, + false); + return pci_p2pdma_client_map_type(f->provider, f->client, per_mapping, + type); +} + +/* + * A client with ATS enabled for the whole device translates whatever it is + * handed, so it never gets bus addresses even on a direct route, and + * Translation Blocking rejects it even when its caller asks for the default + * class. + */ +static void acs_client_ats_test(struct kunit *test) +{ + struct acs_fabric f = {}; + + acs_build_fabric(test, &f); + f.client->ats_enabled = 1; + + KUNIT_EXPECT_EQ(test, acs_client_map(&f, false, 0), + PCI_P2PDMA_MAP_THRU_HOST_BRIDGE); + + f.client_leaf->acs_cap = 0x100; + f.client_leaf->acs_capabilities = PCI_ACS_TB; + f.client_leaf_cfg.ctrl = PCI_ACS_TB; + + KUNIT_EXPECT_EQ(test, acs_client_map(&f, false, 0), + PCI_P2PDMA_MAP_NOT_SUPPORTED); +} + +/* + * The cache holds what the topology allows, filled when the client first + * asked. A client whose ATS was enabled after that must still be kept off the + * bus addresses the cache recorded. + */ +static void acs_client_ats_cached_test(struct kunit *test) +{ + enum pci_p2pdma_map_type type[PCI_P2PDMA_TLP_CLASSES], hit; + struct acs_fabric f = {}; + unsigned long cached; + unsigned int flags; + int dist; + + acs_build_fabric(test, &f); + for (flags = 0; flags < PCI_P2PDMA_TLP_CLASSES; flags++) + type[flags] = calc_map_type_and_dist(f.provider, f.client, + &dist, flags, false); + cached = pci_p2pdma_map_types_pack(type); + + f.client->ats_enabled = 1; + flags = pci_p2pdma_client_tlp_flags(f.client, 0); + hit = pci_p2pdma_map_types_unpack(cached, flags); + + KUNIT_EXPECT_EQ(test, + pci_p2pdma_client_map_type(f.provider, f.client, false, + hit), + PCI_P2PDMA_MAP_THRU_HOST_BRIDGE); +} + +/* + * A client that declared per-mapping ATS picks the Address Type per mapping, + * so it keeps the per-class answers even with ATS enabled. + */ +static void acs_client_ats_per_mapping_test(struct kunit *test) +{ + struct acs_fabric f = {}; + + acs_build_fabric(test, &f); + f.client->ats_enabled = 1; + + KUNIT_EXPECT_EQ(test, acs_client_map(&f, true, 0), + PCI_P2PDMA_MAP_BUS_ADDR); + KUNIT_EXPECT_EQ(test, + acs_client_map(&f, true, PCI_P2PDMA_TLP_TRANSLATED), + PCI_P2PDMA_MAP_BUS_ADDR); + + f.client_leaf->acs_cap = 0x100; + f.client_leaf->acs_capabilities = PCI_ACS_TB; + f.client_leaf_cfg.ctrl = PCI_ACS_TB; + + KUNIT_EXPECT_EQ(test, acs_client_map(&f, true, 0), + PCI_P2PDMA_MAP_BUS_ADDR); + KUNIT_EXPECT_EQ(test, + acs_client_map(&f, true, PCI_P2PDMA_TLP_TRANSLATED), + PCI_P2PDMA_MAP_NOT_SUPPORTED); +} + +/* + * A client with ATS disabled cannot issue Translated Requests, so a caller + * asking about them gets the answer for the Untranslated ones it will issue. + */ +static void acs_client_no_ats_test(struct kunit *test) +{ + struct acs_fabric f = {}; + + acs_build_fabric(test, &f); + f.dn1->acs_cap = 0x100; + f.dn1->acs_capabilities = PCI_ACS_RR | PCI_ACS_DT; + f.dn1_cfg.ctrl = PCI_ACS_RR | PCI_ACS_DT; + + KUNIT_EXPECT_EQ(test, + acs_client_map(&f, false, PCI_P2PDMA_TLP_TRANSLATED), + PCI_P2PDMA_MAP_THRU_HOST_BRIDGE); +} + static struct kunit_case pci_acs_test_cases[] = { KUNIT_CASE_PARAM(pci_acs_p2pdma_request_test, acs_request_gen_params), @@ -723,6 +842,10 @@ static struct kunit_case pci_acs_test_cases[] = { KUNIT_CASE(acs_walk_tb_above_divergence_test), KUNIT_CASE(acs_walk_unreadable_control_test), KUNIT_CASE(acs_map_types_pack_test), + KUNIT_CASE(acs_client_ats_test), + KUNIT_CASE(acs_client_ats_cached_test), + KUNIT_CASE(acs_client_ats_per_mapping_test), + KUNIT_CASE(acs_client_no_ats_test), {} };