From: Leon Romanovsky <[email protected]>

pci_acs_p2pdma_request() and pci_acs_p2pdma_completion() turn an ACS
Control register and a TLP class into a routing decision. Which bits apply
to which direction and which class is easy to get wrong, and hardware that
exposes a given combination may not be at hand.

Drive both from a table of register values and classes, covering the
redirect controls per direction and Translation Blocking, Direct Translated
P2P and Relaxed Ordering. Direct Translated P2P gets a case with and
without a redirect to override, since it changes nothing without one.
Exposing the two helpers moves their state enum and the TLP flags into
pci.h.

Tested-by: Tushar Dave <[email protected]>
Signed-off-by: Leon Romanovsky <[email protected]>
---
 drivers/pci/Kconfig        |  15 ++++++
 drivers/pci/Makefile       |   1 +
 drivers/pci/p2pdma.c       |  40 ++-------------
 drivers/pci/pci.h          |  43 ++++++++++++++++
 drivers/pci/pci_acs_test.c | 121 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 184 insertions(+), 36 deletions(-)

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 0c7408509ba2..7a3eb5beb328 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -226,6 +226,21 @@ config PCI_P2PDMA
 
          If unsure, say N.
 
+config PCI_ACS_KUNIT_TEST
+       tristate "KUnit tests for PCI ACS P2P routing" if !KUNIT_ALL_TESTS
+       depends on PCI_P2PDMA && KUNIT
+       default KUNIT_ALL_TESTS
+       help
+         Enable KUnit tests for the PCI ACS peer-to-peer routing decision
+         logic, including direction-specific Request and Completion
+         controls that cannot all be exercised on typical peer-to-peer
+         hardware.
+
+         For more information on KUnit and unit tests in general, refer to
+         the KUnit documentation in Documentation/dev-tools/kunit/.
+
+         If unsure, say N.
+
 config PCI_LABEL
        def_bool y if (DMI || ACPI)
        select NLS
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a518..6305d128d3df 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_PCI_STUB)                += pci-stub.o
 obj-$(CONFIG_PCI_PF_STUB)      += pci-pf-stub.o
 obj-$(CONFIG_PCI_ECAM)         += ecam.o
 obj-$(CONFIG_PCI_P2PDMA)       += p2pdma.o
+obj-$(CONFIG_PCI_ACS_KUNIT_TEST) += pci_acs_test.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 obj-$(CONFIG_VGA_ARB)          += vgaarb.o
 obj-$(CONFIG_PCI_DOE)          += doe.o
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index cef486e22f97..913d1a32a836 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -492,40 +492,6 @@ static struct pci_dev *find_parent_pci_dev(struct device 
*dev)
        return NULL;
 }
 
-/**
- * enum pci_p2pdma_tlp_flags - Properties of the TLPs a client will issue
- *
- * These describe the traffic rather than the topology, and select which ACS
- * controls apply along the peer-to-peer path. A value of 0 means strictly
- * ordered Requests carrying an Untranslated address.
- *
- * @PCI_P2PDMA_TLP_TRANSLATED: Requests carry an ATS Translated address. PCIe
- *     r7.0 sec 6.12.3 routes those to the peer regardless of ACS P2P Request
- *     Redirect and ACS P2P Egress Control wherever ACS Direct Translated P2P
- *     is enabled.
- * @PCI_P2PDMA_TLP_RELAXED_CPL: The provider returns Completions with the
- *     Relaxed Ordering attribute set. PCIe r7.0 sec 6.12.1.1 never redirects
- *     those, so ACS P2P Completion Redirect does not gate the path. The
- *     Completer chooses this attribute and the specification does not require
- *     it to copy Relaxed Ordering from the Request into the Completion, so a
- *     caller passing this flag asserts that its provider does.
- */
-enum pci_p2pdma_tlp_flags {
-       PCI_P2PDMA_TLP_TRANSLATED       = 1 << 0,
-       PCI_P2PDMA_TLP_RELAXED_CPL      = 1 << 1,
-};
-
-/* Every combination of the flags above selects one routing class. */
-#define PCI_P2PDMA_TLP_CLASSES \
-       ((PCI_P2PDMA_TLP_TRANSLATED | PCI_P2PDMA_TLP_RELAXED_CPL) + 1)
-
-enum pci_acs_p2pdma_state {
-       PCI_ACS_P2PDMA_NOT_SUPPORTED,
-       PCI_ACS_P2PDMA_DIRECT,
-       PCI_ACS_P2PDMA_REDIRECT,
-       PCI_ACS_P2PDMA_BLOCKED,
-};
-
 /*
  * Decide how a peer-to-peer Request at an ACS-capable ingress port routes,
  * from that port's ACS Control register and the Request's Address Type.
@@ -535,7 +501,7 @@ enum pci_acs_p2pdma_state {
  * selects are a direct route and an ACS Violation, and neither one lets peer
  * bus addressing be assumed.
  */
-static enum pci_acs_p2pdma_state
+VISIBLE_IF_KUNIT enum pci_acs_p2pdma_state
 pci_acs_p2pdma_request(u16 ctrl, unsigned int tlp_flags)
 {
        if (tlp_flags & PCI_P2PDMA_TLP_TRANSLATED) {
@@ -562,6 +528,7 @@ pci_acs_p2pdma_request(u16 ctrl, unsigned int tlp_flags)
        return ctrl & (PCI_ACS_RR | PCI_ACS_EC) ?
                PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT;
 }
+EXPORT_SYMBOL_IF_KUNIT(pci_acs_p2pdma_request);
 
 /*
  * Decide how a peer-to-peer Completion at an ACS-capable ingress port routes.
@@ -569,7 +536,7 @@ pci_acs_p2pdma_request(u16 ctrl, unsigned int tlp_flags)
  * affects a Completion, and that one leaves Completions carrying the Relaxed
  * Ordering attribute alone.
  */
-static enum pci_acs_p2pdma_state
+VISIBLE_IF_KUNIT enum pci_acs_p2pdma_state
 pci_acs_p2pdma_completion(u16 ctrl, unsigned int tlp_flags)
 {
        if (tlp_flags & PCI_P2PDMA_TLP_RELAXED_CPL)
@@ -578,6 +545,7 @@ pci_acs_p2pdma_completion(u16 ctrl, unsigned int tlp_flags)
        return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT :
                                   PCI_ACS_P2PDMA_DIRECT;
 }
+EXPORT_SYMBOL_IF_KUNIT(pci_acs_p2pdma_completion);
 
 static const char *pci_acs_p2pdma_state_name(enum pci_acs_p2pdma_state state)
 {
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc2..04c4042dd9b8 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -2,6 +2,7 @@
 #ifndef DRIVERS_PCI_H
 #define DRIVERS_PCI_H
 
+#include <kunit/visibility.h>
 #include <linux/bug.h>
 #include <linux/align.h>
 #include <linux/bitfield.h>
@@ -1093,6 +1094,48 @@ resource_size_t pci_min_window_alignment(struct pci_bus 
*bus,
 
 void pci_acs_init(struct pci_dev *dev);
 void pci_enable_acs(struct pci_dev *dev);
+
+/**
+ * enum pci_p2pdma_tlp_flags - Properties of the TLPs a client will issue
+ *
+ * These describe the traffic rather than the topology, and select which ACS
+ * controls apply along the peer-to-peer path. A value of 0 means strictly
+ * ordered Requests carrying an Untranslated address.
+ *
+ * @PCI_P2PDMA_TLP_TRANSLATED: Requests carry an ATS Translated address. PCIe
+ *     r7.0 sec 6.12.3 routes those to the peer regardless of ACS P2P Request
+ *     Redirect and ACS P2P Egress Control wherever ACS Direct Translated P2P
+ *     is enabled.
+ * @PCI_P2PDMA_TLP_RELAXED_CPL: The provider returns Completions with the
+ *     Relaxed Ordering attribute set. PCIe r7.0 sec 6.12.1.1 never redirects
+ *     those, so ACS P2P Completion Redirect does not gate the path. The
+ *     Completer chooses this attribute and the specification does not require
+ *     it to copy Relaxed Ordering from the Request into the Completion, so a
+ *     caller passing this flag asserts that its provider does.
+ */
+enum pci_p2pdma_tlp_flags {
+       PCI_P2PDMA_TLP_TRANSLATED       = 1 << 0,
+       PCI_P2PDMA_TLP_RELAXED_CPL      = 1 << 1,
+};
+
+/* Every combination of the flags above selects one routing class. */
+#define PCI_P2PDMA_TLP_CLASSES \
+       ((PCI_P2PDMA_TLP_TRANSLATED | PCI_P2PDMA_TLP_RELAXED_CPL) + 1)
+
+enum pci_acs_p2pdma_state {
+       PCI_ACS_P2PDMA_NOT_SUPPORTED,
+       PCI_ACS_P2PDMA_DIRECT,
+       PCI_ACS_P2PDMA_REDIRECT,
+       PCI_ACS_P2PDMA_BLOCKED,
+};
+
+#if IS_ENABLED(CONFIG_KUNIT)
+enum pci_acs_p2pdma_state pci_acs_p2pdma_request(u16 ctrl,
+                                               unsigned int tlp_flags);
+enum pci_acs_p2pdma_state pci_acs_p2pdma_completion(u16 ctrl,
+                                                   unsigned int tlp_flags);
+#endif
+
 #ifdef CONFIG_PCI_QUIRKS
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
 int pci_dev_specific_enable_acs(struct pci_dev *dev);
diff --git a/drivers/pci/pci_acs_test.c b/drivers/pci/pci_acs_test.c
new file mode 100644
index 000000000000..ce6b9375da36
--- /dev/null
+++ b/drivers/pci/pci_acs_test.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for PCI ACS peer-to-peer routing decisions.
+ *
+ * These exercise Request and Completion routing independently of the ACS
+ * settings exposed by available PCIe hardware.
+ */
+#include <kunit/test.h>
+
+#include <linux/pci.h>
+#include <linux/pci-p2pdma.h>
+#include <linux/pci_regs.h>
+
+#include "pci.h"
+
+struct acs_decision_case {
+       const char *desc;
+       u16 ctrl;
+       unsigned int tlp_flags;
+       enum pci_acs_p2pdma_state expect;
+};
+
+/* Shorthands to keep the tables below readable. */
+#define ACS_DIRECT     PCI_ACS_P2PDMA_DIRECT
+#define ACS_REDIR      PCI_ACS_P2PDMA_REDIRECT
+#define ACS_RO         PCI_P2PDMA_TLP_RELAXED_CPL
+#define ACS_AT         PCI_P2PDMA_TLP_TRANSLATED
+#define ACS_BLOCK      PCI_ACS_P2PDMA_BLOCKED
+
+/* Request routing ignores Completion Redirect. */
+static const struct acs_decision_case acs_request_cases[] = {
+       { "req/none", 0, 0, ACS_DIRECT },
+       { "req/rr", PCI_ACS_RR, 0, ACS_REDIR },
+       { "req/cr", PCI_ACS_CR, 0, ACS_DIRECT },
+       { "req/rr_cr", PCI_ACS_RR | PCI_ACS_CR, 0, ACS_REDIR },
+       { "req/ec", PCI_ACS_EC, 0, ACS_REDIR },
+       { "req/ec_cr", PCI_ACS_EC | PCI_ACS_CR, 0, ACS_REDIR },
+
+       /*
+        * Direct Translated P2P overrides the redirect controls, but only for
+        * a Request that actually carries a Translated address.
+        */
+       { "req/dt", PCI_ACS_DT, 0, ACS_DIRECT },
+       { "req/dt_rr", PCI_ACS_DT | PCI_ACS_RR, 0, ACS_REDIR },
+       { "req/at", 0, ACS_AT, ACS_DIRECT },
+       { "req/at_rr", PCI_ACS_RR, ACS_AT, ACS_REDIR },
+       { "req/at_dt_rr", PCI_ACS_DT | PCI_ACS_RR, ACS_AT, ACS_DIRECT },
+       { "req/at_dt_ec", PCI_ACS_DT | PCI_ACS_EC, ACS_AT, ACS_DIRECT },
+
+       /*
+        * Translation Blocking rejects a Translated address outright, and
+        * makes the port ignore Direct Translated P2P.
+        */
+       { "req/tb", PCI_ACS_TB, 0, ACS_DIRECT },
+       { "req/tb_rr", PCI_ACS_TB | PCI_ACS_RR, 0, ACS_REDIR },
+       { "req/at_tb", PCI_ACS_TB, ACS_AT, ACS_BLOCK },
+       { "req/at_tb_dt", PCI_ACS_TB | PCI_ACS_DT, ACS_AT, ACS_BLOCK },
+};
+
+/* Completion routing depends only on Completion Redirect. */
+static const struct acs_decision_case acs_completion_cases[] = {
+       { "cpl/none", 0, 0, ACS_DIRECT },
+       { "cpl/rr", PCI_ACS_RR, 0, ACS_DIRECT },
+       { "cpl/cr", PCI_ACS_CR, 0, ACS_REDIR },
+       { "cpl/rr_cr", PCI_ACS_RR | PCI_ACS_CR, 0, ACS_REDIR },
+       { "cpl/ec", PCI_ACS_EC, 0, ACS_DIRECT },
+       { "cpl/ec_cr", PCI_ACS_EC | PCI_ACS_CR, 0, ACS_REDIR },
+
+       /* Relaxed Ordering Completions are never redirected. */
+       { "cpl/ro", 0, ACS_RO, ACS_DIRECT },
+       { "cpl/ro_cr", PCI_ACS_CR, ACS_RO, ACS_DIRECT },
+       { "cpl/ro_rr_cr", PCI_ACS_RR | PCI_ACS_CR, ACS_RO, ACS_DIRECT },
+};
+
+#undef ACS_DIRECT
+#undef ACS_REDIR
+#undef ACS_RO
+#undef ACS_AT
+#undef ACS_BLOCK
+
+static void acs_decision_desc(const struct acs_decision_case *c, char *desc)
+{
+       strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(acs_request, acs_request_cases, acs_decision_desc);
+KUNIT_ARRAY_PARAM(acs_completion, acs_completion_cases, acs_decision_desc);
+
+static void pci_acs_p2pdma_request_test(struct kunit *test)
+{
+       const struct acs_decision_case *c = test->param_value;
+
+       KUNIT_EXPECT_EQ(test, pci_acs_p2pdma_request(c->ctrl, c->tlp_flags),
+                       c->expect);
+}
+
+static void pci_acs_p2pdma_completion_test(struct kunit *test)
+{
+       const struct acs_decision_case *c = test->param_value;
+
+       KUNIT_EXPECT_EQ(test, pci_acs_p2pdma_completion(c->ctrl, c->tlp_flags),
+                       c->expect);
+}
+
+static struct kunit_case pci_acs_test_cases[] = {
+       KUNIT_CASE_PARAM(pci_acs_p2pdma_request_test,
+                        acs_request_gen_params),
+       KUNIT_CASE_PARAM(pci_acs_p2pdma_completion_test,
+                        acs_completion_gen_params),
+       {}
+};
+
+static struct kunit_suite pci_acs_test_suite = {
+       .name = "pci_acs",
+       .test_cases = pci_acs_test_cases,
+};
+kunit_test_suite(pci_acs_test_suite);
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for PCI ACS peer-to-peer routing decisions");

-- 
2.55.0

Reply via email to