|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC 11/12] PCI: Initialize and release SIOV capability
From: Dimitri Daskalakis <daskald@xxxxxxxx>
Modify pci_init_capabilities() to discover the SIOV extended capability
(cap ID 0x38). When present, allocate struct pci_siov that records the
capability position, total SDI count, routing ID offset and stride, and
the maximum bus range the SDIs can span.
The init path mirrors sriov_init(): read the capability registers,
compute the worst-case bus consumption from total_SDIs, and stash the
result in the PF's pci_dev. Release frees the structure on teardown.
If is_physfn was already set (by sriov_init), it will not be cleared if
siov_init() fails. This prevents clobbering the flag for devices that
enable both virtualization types.
The SR-IOV code does not unset the is_physfn bit of a pci device
when disabled, and the SIOV code follows that pattern.
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Dimitri Daskalakis <daskald@xxxxxxxx>
---
drivers/pci/Makefile | 1 +
drivers/pci/pci.h | 16 ++++++
drivers/pci/probe.c | 2 +
drivers/pci/siov.c | 113 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 132 insertions(+)
create mode 100644 drivers/pci/siov.c
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a518..a584cd1bf08a 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_PCI_QUIRKS) += quirks.o
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
obj-$(CONFIG_PCI_ATS) += ats.o
obj-$(CONFIG_PCI_IOV) += iov.o
+obj-$(CONFIG_PCI_SIOV) += siov.o
obj-$(CONFIG_PCI_BRIDGE_EMUL) += pci-bridge-emul.o
obj-$(CONFIG_PCI_LABEL) += pci-label.o
obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fd7c04e26c16..a516db996aab 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1042,6 +1042,22 @@ static inline u16 pci_virtfn_routing_id(struct pci_dev
*pf, u16 offset,
}
#endif
+#ifdef CONFIG_PCI_SIOV
+int pci_siov_init(struct pci_dev *dev);
+void pci_siov_release(struct pci_dev *dev);
+int pci_siov_bus_range(struct pci_bus *bus);
+#else
+static inline int pci_siov_init(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+static inline void pci_siov_release(struct pci_dev *dev) { }
+static inline int pci_siov_bus_range(struct pci_bus *bus)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_PCIE_TPH
void pci_restore_tph_state(struct pci_dev *dev);
void pci_save_tph_state(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index b63cd0c310bc..bebc32c8d374 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2473,6 +2473,7 @@ static void pci_release_capabilities(struct pci_dev *dev)
pci_aer_exit(dev);
pci_rcec_exit(dev);
pci_iov_release(dev);
+ pci_siov_release(dev);
pci_free_cap_save_buffers(dev);
}
@@ -2666,6 +2667,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_vpd_init(dev); /* Vital Product Data */
pci_configure_ari(dev); /* Alternative Routing-ID Forwarding */
pci_iov_init(dev); /* Single Root I/O Virtualization */
+ pci_siov_init(dev); /* Scalable I/O Virtualization */
pci_ats_init(dev); /* Address Translation Services */
pci_pri_init(dev); /* Page Request Interface */
pci_pasid_init(dev); /* Process Address Space ID */
diff --git a/drivers/pci/siov.c b/drivers/pci/siov.c
new file mode 100644
index 000000000000..7372ce95714b
--- /dev/null
+++ b/drivers/pci/siov.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Express Scalable I/O Virtualization (SIOV) support
+ */
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include "pci.h"
+
+static int pci_siov_sdi_bus(struct pci_dev *dev, int sdi_id)
+{
+ if (!dev->siov)
+ return -EINVAL;
+ return pci_virtfn_routing_id(dev, dev->siov->offset,
+ dev->siov->stride, sdi_id) >> 8;
+}
+
+static int compute_max_sdi_buses(struct pci_dev *dev)
+{
+ struct pci_siov *siov = dev->siov;
+
+ if (!siov->offset || (siov->total_SDIs > 1 && !siov->stride))
+ return -EIO;
+
+ siov->max_SDI_buses = pci_siov_sdi_bus(dev, siov->total_SDIs - 1);
+ return 0;
+}
+
+static int siov_init(struct pci_dev *dev, int pos)
+{
+ struct pci_siov *siov;
+ bool was_physfn;
+ u16 total;
+ u8 status;
+ int rc;
+
+ pci_read_config_byte(dev, pos + PCI_SIOV_STATUS, &status);
+ if (status & PCI_SIOV_STATUS_ENABLED)
+ pci_warn(dev, "SIOV: SDIs active at init, FLR may be
required\n");
+
+ pci_read_config_word(dev, pos + PCI_SIOV_TOTAL_SDI, &total);
+ if (!total)
+ return 0;
+
+ siov = kzalloc_obj(*siov);
+ if (!siov)
+ return -ENOMEM;
+
+ siov->pos = pos;
+ siov->total_SDIs = total;
+ siov->driver_max_SDIs = total;
+ siov->self = dev;
+ pci_read_config_dword(dev, pos + PCI_SIOV_CAP, &siov->cap);
+ pci_read_config_word(dev, pos + PCI_SIOV_SDI_OFFSET, &siov->offset);
+ pci_read_config_word(dev, pos + PCI_SIOV_SDI_STRIDE, &siov->stride);
+
+ was_physfn = dev->is_physfn;
+
+ dev->siov = siov;
+ dev->is_physfn = 1;
+ dev->is_siov = 1;
+ rc = compute_max_sdi_buses(dev);
+ if (rc) {
+ dev->siov = NULL;
+ dev->is_siov = 0;
+ if (!was_physfn)
+ dev->is_physfn = 0;
+ kfree(siov);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void siov_release(struct pci_dev *dev)
+{
+ WARN_ON_ONCE(dev->siov->num_SDIs);
+
+ kfree(dev->siov);
+ dev->siov = NULL;
+ dev->is_siov = 0;
+}
+
+/**
+ * pci_siov_init - initialize the Scalable IOV capability
+ * @dev: the PCI device
+ *
+ * Returns 0 on success, or negative on failure.
+ */
+int pci_siov_init(struct pci_dev *dev)
+{
+ int pos;
+
+ if (!pci_is_pcie(dev))
+ return -ENODEV;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SIOV);
+ if (pos)
+ return siov_init(dev, pos);
+
+ return -ENODEV;
+}
+
+/**
+ * pci_siov_release - release resources used by the SIOV capability
+ * @dev: the PCI device
+ */
+void pci_siov_release(struct pci_dev *dev)
+{
+ if (dev->siov)
+ siov_release(dev);
+}
--
2.52.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |