Drivers could leverage the fact that the VF BAR MMIO reservation is
created for total number of VFs supported by the device by resizing the
BAR to larger size when smaller number of VFs is enabled.
Add a pci_iov_vf_bar_set_size() function to control the size and a
pci_iov_vf_bar_get_sizes() helper to get the VF BAR sizes that will
allow up to num_vfs to be successfully enabled with the current
underlying reservation size.
Signed-off-by: Michał Winiarski <michal.winiarski@intel.com>
---
drivers/pci/iov.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 6 ++++
2 files changed, 86 insertions(+)
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 5de828e5a26ea..de8d473459440 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -1281,3 +1281,83 @@ int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn)
return nr_virtfn;
}
EXPORT_SYMBOL_GPL(pci_sriov_configure_simple);
+
+/**
+ * pci_iov_vf_bar_set_size - set a new size for a VF BAR
+ * @dev: the PCI device
+ * @resno: the resource number
+ * @size: new size as defined in the spec (0=1MB, 19=512GB)
+ *
+ * Set the new size of a VF BAR that supports VF resizable BAR capability.
+ * Unlike pci_resize_resource(), this does not cause the resource that
+ * reserves the MMIO space (originally up to total_VFs) to be resized, which
+ * means that following calls to pci_enable_sriov() can fail if the resources
+ * no longer fit.
+ *
+ * Returns 0 on success, or negative on failure.
+ *
+ */
+int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
+{
+ int ret;
+ u32 sizes;
+
+ if (!pci_resource_is_iov(resno))
+ return -EINVAL;
+
+ if (pci_iov_is_memory_decoding_enabled(dev))
+ return -EBUSY;
+
+ sizes = pci_rebar_get_possible_sizes(dev, resno);
+ if (!sizes)
+ return -ENOTSUPP;
+
+ if (!(sizes & BIT(size)))
+ return -EINVAL;
+
+ ret = pci_rebar_set_size(dev, resno, size);
+ if (ret)
+ return ret;
+
+ pci_iov_resource_set_size(dev, resno, pci_rebar_size_to_bytes(size));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
+
+/**
+ * pci_iov_vf_bar_get_sizes - get VF BAR sizes that allow to create up to num_vfs
+ * @dev: the PCI device
+ * @resno: the resource number
+ * @num_vfs: number of VFs
+ *
+ * Get the sizes of a VF resizable BAR that can fit up to num_vfs within the
+ * resource that reserves the MMIO space (originally up to total_VFs) the as
+ * bitmask defined in the spec (bit 0=1MB, bit 19=512GB).
+ *
+ * Returns 0 if BAR isn't resizable.
+ *
+ */
+u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
+{
+ resource_size_t size;
+ u32 sizes;
+ int i;
+
+ sizes = pci_rebar_get_possible_sizes(dev, resno);
+ if (!sizes)
+ return 0;
+
+ while (sizes > 0) {
+ i = __fls(sizes);
+ size = pci_rebar_size_to_bytes(i);
+
+ if (size * num_vfs <= pci_resource_len(dev, resno))
+ break;
+
+ sizes &= ~BIT(i);
+ }
+
+ return sizes;
+}
+EXPORT_SYMBOL_GPL(pci_iov_vf_bar_get_sizes);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 573b4c4c2be61..1b9e7e3cab0ce 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2371,6 +2371,8 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs);
int pci_sriov_get_totalvfs(struct pci_dev *dev);
int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn);
resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno);
+int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size);
+u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs);
void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe);
/* Arch may override these (weak) */
@@ -2423,6 +2425,10 @@ static inline int pci_sriov_get_totalvfs(struct pci_dev *dev)
#define pci_sriov_configure_simple NULL
static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
{ return 0; }
+static inline int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
+{ return -ENODEV; }
+static inline u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
+{ return 0; }
static inline void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe) { }
#endif
--
2.47.0
On Fri, Oct 25, 2024 at 11:50:37PM +0200, Michał Winiarski wrote: > Drivers could leverage the fact that the VF BAR MMIO reservation is > created for total number of VFs supported by the device by resizing the > BAR to larger size when smaller number of VFs is enabled. > > Add a pci_iov_vf_bar_set_size() function to control the size and a > pci_iov_vf_bar_get_sizes() helper to get the VF BAR sizes that will > allow up to num_vfs to be successfully enabled with the current > underlying reservation size. > ... > + * pci_iov_vf_bar_get_sizes - get VF BAR sizes that allow to create up to num_vfs > + * @dev: the PCI device > + * @resno: the resource number > + * @num_vfs: number of VFs > + * > + * Get the sizes of a VF resizable BAR that can fit up to num_vfs within the > + * resource that reserves the MMIO space (originally up to total_VFs) the as > + * bitmask defined in the spec (bit 0=1MB, bit 19=512GB). This sentence doesn't quite parse; something is missing around "the as". I'm guessing you mean to say something about the return value being a bitmask of VF BAR sizes that can be accommodated if num_vfs are enabled? If so, maybe combine it with the following paragraph: > + * Returns 0 if BAR isn't resizable. > + * > + */ > +u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs) > +{ > + resource_size_t size; > + u32 sizes; > + int i; > + > + sizes = pci_rebar_get_possible_sizes(dev, resno); > + if (!sizes) > + return 0; > + > + while (sizes > 0) { > + i = __fls(sizes); > + size = pci_rebar_size_to_bytes(i); > + > + if (size * num_vfs <= pci_resource_len(dev, resno)) > + break; > + > + sizes &= ~BIT(i); > + } > + > + return sizes; > +} > +EXPORT_SYMBOL_GPL(pci_iov_vf_bar_get_sizes);
On Mon, Oct 28, 2024 at 11:50:31AM -0500, Bjorn Helgaas wrote: > On Fri, Oct 25, 2024 at 11:50:37PM +0200, Michał Winiarski wrote: > > Drivers could leverage the fact that the VF BAR MMIO reservation is > > created for total number of VFs supported by the device by resizing the > > BAR to larger size when smaller number of VFs is enabled. > > > > Add a pci_iov_vf_bar_set_size() function to control the size and a > > pci_iov_vf_bar_get_sizes() helper to get the VF BAR sizes that will > > allow up to num_vfs to be successfully enabled with the current > > underlying reservation size. > > ... > > > + * pci_iov_vf_bar_get_sizes - get VF BAR sizes that allow to create up to num_vfs > > + * @dev: the PCI device > > + * @resno: the resource number > > + * @num_vfs: number of VFs > > + * > > + * Get the sizes of a VF resizable BAR that can fit up to num_vfs within the > > + * resource that reserves the MMIO space (originally up to total_VFs) the as > > + * bitmask defined in the spec (bit 0=1MB, bit 19=512GB). > > This sentence doesn't quite parse; something is missing around "the as". Yeah, typo, "the" should be removed. > I'm guessing you mean to say something about the return value being a > bitmask of VF BAR sizes that can be accommodated if num_vfs are > enabled? If so, maybe combine it with the following paragraph: I'll change it to: "Get the sizes of a VF resizable BAR that can be accomodated within the resource that reserves the MMIO space if num_vfs are enabled. Returns 0 if BAR isn't resizable, otherwise returns a bitmask in format defined in the spec (bit 0=1MB, bit 19=512GB)." -Michał > > > + * Returns 0 if BAR isn't resizable. > > + * > > + */ > > +u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs) > > +{ > > + resource_size_t size; > > + u32 sizes; > > + int i; > > + > > + sizes = pci_rebar_get_possible_sizes(dev, resno); > > + if (!sizes) > > + return 0; > > + > > + while (sizes > 0) { > > + i = __fls(sizes); > > + size = pci_rebar_size_to_bytes(i); > > + > > + if (size * num_vfs <= pci_resource_len(dev, resno)) > > + break; > > + > > + sizes &= ~BIT(i); > > + } > > + > > + return sizes; > > +} > > +EXPORT_SYMBOL_GPL(pci_iov_vf_bar_get_sizes);
© 2016 - 2024 Red Hat, Inc.