diff mbox series

[v2,4/7] PCI: Move pci_dev_str_match to search.c

Message ID 20221110195015.207-5-jonathan.derrick@linux.dev (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show
Series PCIe Hotplug Slot Emulation driver | expand

Commit Message

Jonathan Derrick Nov. 10, 2022, 7:50 p.m. UTC
The method which extracts a string descriptor of one or more PCI devices
and matches to a struct pci_dev is useful in general to other subsystems
needing to match parameters or sysfs strings. Move this function to
search.c for general use.

Signed-off-by: Jonathan Derrick <jonathan.derrick@linux.dev>
---
 drivers/pci/pci.c    | 163 -------------------------------------------
 drivers/pci/search.c | 162 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h  |   5 ++
 3 files changed, 167 insertions(+), 163 deletions(-)
diff mbox series

Patch

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 2127aba3550b..b58a8f5a7654 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -250,169 +250,6 @@  void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar)
 EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
 #endif
 
-/**
- * pci_dev_str_match_path - test if a path string matches a device
- * @dev: the PCI device to test
- * @path: string to match the device against
- * @endptr: pointer to the string after the match
- *
- * Test if a string (typically from a kernel parameter) formatted as a
- * path of device/function addresses matches a PCI device. The string must
- * be of the form:
- *
- *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
- *
- * A path for a device can be obtained using 'lspci -t'.  Using a path
- * is more robust against bus renumbering than using only a single bus,
- * device and function address.
- *
- * Returns 1 if the string matches the device, 0 if it does not and
- * a negative error code if it fails to parse the string.
- */
-static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
-				  const char **endptr)
-{
-	int ret;
-	unsigned int seg, bus, slot, func;
-	char *wpath, *p;
-	char end;
-
-	*endptr = strchrnul(path, ';');
-
-	wpath = kmemdup_nul(path, *endptr - path, GFP_ATOMIC);
-	if (!wpath)
-		return -ENOMEM;
-
-	while (1) {
-		p = strrchr(wpath, '/');
-		if (!p)
-			break;
-		ret = sscanf(p, "/%x.%x%c", &slot, &func, &end);
-		if (ret != 2) {
-			ret = -EINVAL;
-			goto free_and_exit;
-		}
-
-		if (dev->devfn != PCI_DEVFN(slot, func)) {
-			ret = 0;
-			goto free_and_exit;
-		}
-
-		/*
-		 * Note: we don't need to get a reference to the upstream
-		 * bridge because we hold a reference to the top level
-		 * device which should hold a reference to the bridge,
-		 * and so on.
-		 */
-		dev = pci_upstream_bridge(dev);
-		if (!dev) {
-			ret = 0;
-			goto free_and_exit;
-		}
-
-		*p = 0;
-	}
-
-	ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot,
-		     &func, &end);
-	if (ret != 4) {
-		seg = 0;
-		ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end);
-		if (ret != 3) {
-			ret = -EINVAL;
-			goto free_and_exit;
-		}
-	}
-
-	ret = (seg == pci_domain_nr(dev->bus) &&
-	       bus == dev->bus->number &&
-	       dev->devfn == PCI_DEVFN(slot, func));
-
-free_and_exit:
-	kfree(wpath);
-	return ret;
-}
-
-/**
- * pci_dev_str_match - test if a string matches a device
- * @dev: the PCI device to test
- * @p: string to match the device against
- * @endptr: pointer to the string after the match
- *
- * Test if a string (typically from a kernel parameter) matches a specified
- * PCI device. The string may be of one of the following formats:
- *
- *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
- *   pci:<vendor>:<device>[:<subvendor>:<subdevice>]
- *
- * The first format specifies a PCI bus/device/function address which
- * may change if new hardware is inserted, if motherboard firmware changes,
- * or due to changes caused in kernel parameters. If the domain is
- * left unspecified, it is taken to be 0.  In order to be robust against
- * bus renumbering issues, a path of PCI device/function numbers may be used
- * to address the specific device.  The path for a device can be determined
- * through the use of 'lspci -t'.
- *
- * The second format matches devices using IDs in the configuration
- * space which may match multiple devices in the system. A value of 0
- * for any field will match all devices. (Note: this differs from
- * in-kernel code that uses PCI_ANY_ID which is ~0; this is for
- * legacy reasons and convenience so users don't have to specify
- * FFFFFFFFs on the command line.)
- *
- * Returns 1 if the string matches the device, 0 if it does not and
- * a negative error code if the string cannot be parsed.
- */
-static int pci_dev_str_match(struct pci_dev *dev, const char *p,
-			     const char **endptr)
-{
-	int ret;
-	int count;
-	unsigned short vendor, device, subsystem_vendor, subsystem_device;
-
-	if (strncmp(p, "pci:", 4) == 0) {
-		/* PCI vendor/device (subvendor/subdevice) IDs are specified */
-		p += 4;
-		ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device,
-			     &subsystem_vendor, &subsystem_device, &count);
-		if (ret != 4) {
-			ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count);
-			if (ret != 2)
-				return -EINVAL;
-
-			subsystem_vendor = 0;
-			subsystem_device = 0;
-		}
-
-		p += count;
-
-		if ((!vendor || vendor == dev->vendor) &&
-		    (!device || device == dev->device) &&
-		    (!subsystem_vendor ||
-			    subsystem_vendor == dev->subsystem_vendor) &&
-		    (!subsystem_device ||
-			    subsystem_device == dev->subsystem_device))
-			goto found;
-	} else {
-		/*
-		 * PCI Bus, Device, Function IDs are specified
-		 * (optionally, may include a path of devfns following it)
-		 */
-		ret = pci_dev_str_match_path(dev, p, &p);
-		if (ret < 0)
-			return ret;
-		else if (ret)
-			goto found;
-	}
-
-	*endptr = p;
-	return 0;
-
-found:
-	*endptr = p;
-	return 1;
-}
-
 static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
 				  u8 pos, int cap, int *ttl)
 {
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index b4c138a6ec02..059fc5b9db4c 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -390,3 +390,165 @@  int pci_dev_present(const struct pci_device_id *ids)
 	return 0;
 }
 EXPORT_SYMBOL(pci_dev_present);
+
+/**
+ * pci_dev_str_match_path - test if a path string matches a device
+ * @dev: the PCI device to test
+ * @path: string to match the device against
+ * @endptr: pointer to the string after the match
+ *
+ * Test if a string (typically from a kernel parameter) formatted as a
+ * path of device/function addresses matches a PCI device. The string must
+ * be of the form:
+ *
+ *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+ *
+ * A path for a device can be obtained using 'lspci -t'.  Using a path
+ * is more robust against bus renumbering than using only a single bus,
+ * device and function address.
+ *
+ * Returns 1 if the string matches the device, 0 if it does not and
+ * a negative error code if it fails to parse the string.
+ */
+static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
+				  const char **endptr)
+{
+	int ret;
+	unsigned int seg, bus, slot, func;
+	char *wpath, *p;
+	char end;
+
+	*endptr = strchrnul(path, ';');
+
+	wpath = kmemdup_nul(path, *endptr - path, GFP_ATOMIC);
+	if (!wpath)
+		return -ENOMEM;
+
+	while (1) {
+		p = strrchr(wpath, '/');
+		if (!p)
+			break;
+		ret = sscanf(p, "/%x.%x%c", &slot, &func, &end);
+		if (ret != 2) {
+			ret = -EINVAL;
+			goto free_and_exit;
+		}
+
+		if (dev->devfn != PCI_DEVFN(slot, func)) {
+			ret = 0;
+			goto free_and_exit;
+		}
+
+		/*
+		 * Note: we don't need to get a reference to the upstream
+		 * bridge because we hold a reference to the top level
+		 * device which should hold a reference to the bridge,
+		 * and so on.
+		 */
+		dev = pci_upstream_bridge(dev);
+		if (!dev) {
+			ret = 0;
+			goto free_and_exit;
+		}
+
+		*p = 0;
+	}
+
+	ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot,
+		     &func, &end);
+	if (ret != 4) {
+		seg = 0;
+		ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end);
+		if (ret != 3) {
+			ret = -EINVAL;
+			goto free_and_exit;
+		}
+	}
+
+	ret = (seg == pci_domain_nr(dev->bus) &&
+	       bus == dev->bus->number &&
+	       dev->devfn == PCI_DEVFN(slot, func));
+
+free_and_exit:
+	kfree(wpath);
+	return ret;
+}
+
+/**
+ * pci_dev_str_match - test if a string matches a device
+ * @dev: the PCI device to test
+ * @p: string to match the device against
+ * @endptr: pointer to the string after the match
+ *
+ * Test if a string (typically from a kernel parameter) matches a specified
+ * PCI device. The string may be of one of the following formats:
+ *
+ *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+ *   pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+ *
+ * The first format specifies a PCI bus/device/function address which
+ * may change if new hardware is inserted, if motherboard firmware changes,
+ * or due to changes caused in kernel parameters. If the domain is
+ * left unspecified, it is taken to be 0.  In order to be robust against
+ * bus renumbering issues, a path of PCI device/function numbers may be used
+ * to address the specific device.  The path for a device can be determined
+ * through the use of 'lspci -t'.
+ *
+ * The second format matches devices using IDs in the configuration
+ * space which may match multiple devices in the system. A value of 0
+ * for any field will match all devices. (Note: this differs from
+ * in-kernel code that uses PCI_ANY_ID which is ~0; this is for
+ * legacy reasons and convenience so users don't have to specify
+ * FFFFFFFFs on the command line.)
+ *
+ * Returns 1 if the string matches the device, 0 if it does not and
+ * a negative error code if the string cannot be parsed.
+ */
+int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr)
+{
+	int ret;
+	int count;
+	unsigned short vendor, device, subsystem_vendor, subsystem_device;
+
+	if (strncmp(p, "pci:", 4) == 0) {
+		/* PCI vendor/device (subvendor/subdevice) IDs are specified */
+		p += 4;
+		ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device,
+			     &subsystem_vendor, &subsystem_device, &count);
+		if (ret != 4) {
+			ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count);
+			if (ret != 2)
+				return -EINVAL;
+
+			subsystem_vendor = 0;
+			subsystem_device = 0;
+		}
+
+		p += count;
+
+		if ((!vendor || vendor == dev->vendor) &&
+		    (!device || device == dev->device) &&
+		    (!subsystem_vendor ||
+			    subsystem_vendor == dev->subsystem_vendor) &&
+		    (!subsystem_device ||
+			    subsystem_device == dev->subsystem_device))
+			goto found;
+	} else {
+		/*
+		 * PCI Bus, Device, Function IDs are specified
+		 * (optionally, may include a path of devfns following it)
+		 */
+		ret = pci_dev_str_match_path(dev, p, &p);
+		if (ret < 0)
+			return ret;
+		else if (ret)
+			goto found;
+	}
+
+	*endptr = p;
+	return 0;
+
+found:
+	*endptr = p;
+	return 1;
+}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 09f704337955..0c907f94bb61 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1182,6 +1182,7 @@  struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
 					    unsigned int devfn);
 struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from);
 int pci_dev_present(const struct pci_device_id *ids);
+int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr);
 
 int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn,
 			     int where, u8 *val);
@@ -1816,6 +1817,10 @@  static inline struct pci_dev *pci_get_class(unsigned int class,
 static inline int pci_dev_present(const struct pci_device_id *ids)
 { return 0; }
 
+static inline int pci_dev_str_match(struct pci_dev *dev, const char *p,
+				    const char **endptr)
+{ return 0; }
+
 #define no_pci_devices()	(1)
 #define pci_dev_put(dev)	do { } while (0)