]> www.infradead.org Git - users/hch/misc.git/commitdiff
PCI: Refactor capability search into PCI_FIND_NEXT_CAP()
authorHans Zhang <18255117159@163.com>
Wed, 13 Aug 2025 14:45:25 +0000 (22:45 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 14 Aug 2025 20:03:40 +0000 (15:03 -0500)
The PCI Capability search functionality is duplicated across the PCI core
and several controller drivers. The core's current implementation requires
fully initialized PCI device and bus structures, which prevents controller
drivers from using it during early initialization phases before these
structures are available.

Move the Capability search logic into a PCI_FIND_NEXT_CAP() macro that
accepts a config space accessor function as an argument. This enables
controller drivers to perform Capability discovery using their early
access mechanisms prior to full device initialization while sharing the
Capability search code.

Convert the existing PCI core Capability search implementation to use
PCI_FIND_NEXT_CAP().  Controller drivers can later use this with their
early access mechanisms while maintaining the existing protection against
infinite loops through preserved TTL checks.

Signed-off-by: Hans Zhang <18255117159@163.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Niklas Schnelle <schnelle@linux.ibm.com>
Link: https://patch.msgid.link/20250813144529.303548-3-18255117159@163.com
drivers/pci/pci.c
drivers/pci/pci.h

index 40a5c87d9a6b6d35b677135dcf82e6744eaefd60..ac2658d946ea8bff6560b9e09f580450c14fab05 100644 (file)
@@ -9,7 +9,6 @@
  */
 
 #include <linux/acpi.h>
-#include <linux/align.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
@@ -424,36 +423,10 @@ found:
        return 1;
 }
 
-static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
-                                 u8 pos, int cap, int *ttl)
-{
-       u8 id;
-       u16 ent;
-
-       pci_bus_read_config_byte(bus, devfn, pos, &pos);
-
-       while ((*ttl)--) {
-               if (pos < PCI_STD_HEADER_SIZEOF)
-                       break;
-               pos = ALIGN_DOWN(pos, 4);
-               pci_bus_read_config_word(bus, devfn, pos, &ent);
-
-               id = FIELD_GET(PCI_CAP_ID_MASK, ent);
-               if (id == 0xff)
-                       break;
-               if (id == cap)
-                       return pos;
-               pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, ent);
-       }
-       return 0;
-}
-
 static u8 __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
                              u8 pos, int cap)
 {
-       int ttl = PCI_FIND_CAP_TTL;
-
-       return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);
+       return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, bus, devfn);
 }
 
 u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap)
@@ -649,7 +622,7 @@ EXPORT_SYMBOL_GPL(pci_get_dsn);
 
 static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
 {
-       int rc, ttl = PCI_FIND_CAP_TTL;
+       int rc;
        u8 cap, mask;
 
        if (ht_cap == HT_CAPTYPE_SLAVE || ht_cap == HT_CAPTYPE_HOST)
@@ -657,8 +630,8 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
        else
                mask = HT_5BIT_CAP_MASK;
 
-       pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos,
-                                     PCI_CAP_ID_HT, &ttl);
+       pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos,
+                               PCI_CAP_ID_HT, dev->bus, dev->devfn);
        while (pos) {
                rc = pci_read_config_byte(dev, pos + 3, &cap);
                if (rc != PCIBIOS_SUCCESSFUL)
@@ -667,9 +640,10 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
                if ((cap & mask) == ht_cap)
                        return pos;
 
-               pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn,
-                                             pos + PCI_CAP_LIST_NEXT,
-                                             PCI_CAP_ID_HT, &ttl);
+               pos = PCI_FIND_NEXT_CAP(pci_bus_read_config,
+                                       pos + PCI_CAP_LIST_NEXT,
+                                       PCI_CAP_ID_HT, dev->bus,
+                                       dev->devfn);
        }
 
        return 0;
index 34f65d69662e9f61f0c489ec58de2ce17d21c0c6..81580987509f2a2f432b3319af6537e7d93378d8 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef DRIVERS_PCI_H
 #define DRIVERS_PCI_H
 
+#include <linux/align.h>
+#include <linux/bitfield.h>
 #include <linux/pci.h>
 
 struct pcie_tlp_log;
@@ -88,6 +90,49 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
 bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
 bool pcie_cap_has_rtctl(const struct pci_dev *dev);
 
+/* Standard Capability finder */
+/**
+ * PCI_FIND_NEXT_CAP - Find a PCI standard capability
+ * @read_cfg: Function pointer for reading PCI config space
+ * @start: Starting position to begin search
+ * @cap: Capability ID to find
+ * @args: Arguments to pass to read_cfg function
+ *
+ * Search the capability list in PCI config space to find @cap.
+ * Implements TTL (time-to-live) protection against infinite loops.
+ *
+ * Return: Position of the capability if found, 0 otherwise.
+ */
+#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...)               \
+({                                                                     \
+       int __ttl = PCI_FIND_CAP_TTL;                                   \
+       u8 __id, __found_pos = 0;                                       \
+       u8 __pos = (start);                                             \
+       u16 __ent;                                                      \
+                                                                       \
+       read_cfg##_byte(args, __pos, &__pos);                           \
+                                                                       \
+       while (__ttl--) {                                               \
+               if (__pos < PCI_STD_HEADER_SIZEOF)                      \
+                       break;                                          \
+                                                                       \
+               __pos = ALIGN_DOWN(__pos, 4);                           \
+               read_cfg##_word(args, __pos, &__ent);                   \
+                                                                       \
+               __id = FIELD_GET(PCI_CAP_ID_MASK, __ent);               \
+               if (__id == 0xff)                                       \
+                       break;                                          \
+                                                                       \
+               if (__id == (cap)) {                                    \
+                       __found_pos = __pos;                            \
+                       break;                                          \
+               }                                                       \
+                                                                       \
+               __pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, __ent);       \
+       }                                                               \
+       __found_pos;                                                    \
+})
+
 /* Functions internal to the PCI core code */
 
 #ifdef CONFIG_DMI