From: Jonathan Derrick <jonathan.derrick@linux.dev>
To: Vidya Sagar <vidyas@nvidia.com>
Cc: "Manivannan Sadhasivam" <mani@kernel.org>,
"Lorenzo Pieralisi" <lorenzo.pieralisi@arm.com>,
"Bjorn Helgaas" <helgaas@kernel.org>,
linux-pci@vger.kernel.org, "Lukas Wunner" <lukas@wunner.de>,
"Pali Rohár" <pali@kernel.org>,
"Jonathan Derrick" <jonathan.derrick@linux.dev>
Subject: [PATCH v2 4/7] PCI: Move pci_dev_str_match to search.c
Date: Thu, 10 Nov 2022 12:50:12 -0700 [thread overview]
Message-ID: <20221110195015.207-5-jonathan.derrick@linux.dev> (raw)
In-Reply-To: <20221110195015.207-1-jonathan.derrick@linux.dev>
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 --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)
--
2.30.2
next prev parent reply other threads:[~2022-11-10 19:53 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-10 19:50 [PATCH v2 0/7] PCIe Hotplug Slot Emulation driver Jonathan Derrick
2022-11-10 19:50 ` [PATCH v2 1/7] PCI: Allow for indirecting capability registers Jonathan Derrick
2022-11-10 21:10 ` Pali Rohár
2022-11-10 19:50 ` [PATCH v2 2/7] PCI: Add pcie_port_slot_emulated stub Jonathan Derrick
2022-11-10 19:50 ` [PATCH v2 3/7] PCI: pciehp: Expose the poll loop to other drivers Jonathan Derrick
2022-11-10 19:50 ` Jonathan Derrick [this message]
2022-11-10 19:50 ` [PATCH v2 5/7] PCI: pci-bridge-emul: Provide a helper to set behavior Jonathan Derrick
2022-11-10 21:02 ` Pali Rohár
2022-11-10 19:50 ` [PATCH v2 6/7] PCI: pciehp: Add hotplug slot emulation driver Jonathan Derrick
2022-11-10 19:50 ` [PATCH v2 7/7] PCI: pciehp: Wire up pcie_port_emulate_slot and Jonathan Derrick
2022-11-10 21:17 ` [PATCH v2 0/7] PCIe Hotplug Slot Emulation driver Pali Rohár
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221110195015.207-5-jonathan.derrick@linux.dev \
--to=jonathan.derrick@linux.dev \
--cc=helgaas@kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lorenzo.pieralisi@arm.com \
--cc=lukas@wunner.de \
--cc=mani@kernel.org \
--cc=pali@kernel.org \
--cc=vidyas@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).