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 | 73 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 6 ++++
2 files changed, 79 insertions(+)
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 2fafbd6a998f0..3e24d84a1ee10 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -8,11 +8,15 @@
*/
#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/log2.h>
#include <linux/pci.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/string.h>
#include <linux/delay.h>
+#include <asm/div64.h>
#include "pci.h"
#define VIRTFN_ID_LEN 17 /* "virtfn%u\0" for 2^32 - 1 */
@@ -1313,3 +1317,72 @@ 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, 31=128TB)
+ *
+ * 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.
+ *
+ * Return: 0 on success, or negative on failure.
+ */
+int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
+{
+ u32 sizes;
+ int ret;
+
+ 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 allowing 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 accommodate @num_vfs within
+ * the currently assigned size of the resource @resno.
+ *
+ * Return: A bitmask of sizes in format defined in the spec (bit 0=1MB,
+ * bit 31=128TB).
+ */
+u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs)
+{
+ resource_size_t vf_len = pci_resource_len(dev, resno);
+ u32 sizes;
+
+ if (!num_vfs)
+ return 0;
+
+ do_div(vf_len, num_vfs);
+ sizes = (roundup_pow_of_two(vf_len + 1) - 1) >> ilog2(SZ_1M);
+
+ return sizes & pci_rebar_get_possible_sizes(dev, resno);
+}
+EXPORT_SYMBOL_GPL(pci_iov_vf_bar_get_sizes);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ab62bcb5f99c6..cc633b1a13d51 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2437,6 +2437,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) */
@@ -2489,6 +2491,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.49.0
Hi Michał,
kernel test robot noticed the following build errors:
[auto build test ERROR on pci/next]
[also build test ERROR on pci/for-linus drm-xe/drm-xe-next drm-exynos/exynos-drm-next linus/master v6.15 next-20250526]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Micha-Winiarski/PCI-IOV-Restore-VF-resizable-BAR-state-after-reset/20250527-054652
base: https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link: https://lore.kernel.org/r/20250526214257.3481760-6-michal.winiarski%40intel.com
patch subject: [PATCH v8 5/6] PCI: Allow drivers to control VF BAR size
config: csky-randconfig-002-20250527 (https://download.01.org/0day-ci/archive/20250527/202505270842.rZMzTQh6-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 12.4.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250527/202505270842.rZMzTQh6-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505270842.rZMzTQh6-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from ./arch/csky/include/generated/asm/div64.h:1,
from include/linux/math.h:6,
from include/linux/kernel.h:27,
from include/linux/cpumask.h:11,
from include/linux/smp.h:13,
from include/linux/lockdep.h:14,
from include/linux/mutex.h:17,
from include/linux/kernfs.h:11,
from include/linux/sysfs.h:16,
from include/linux/kobject.h:20,
from include/linux/pci.h:35,
from drivers/pci/iov.c:13:
drivers/pci/iov.c: In function 'pci_iov_vf_bar_get_sizes':
include/asm-generic/div64.h:183:35: warning: comparison of distinct pointer types lacks a cast
183 | (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
| ^~
drivers/pci/iov.c:1383:9: note: in expansion of macro 'do_div'
1383 | do_div(vf_len, num_vfs);
| ^~~~~~
In file included from include/linux/build_bug.h:5,
from include/linux/bitfield.h:10,
from drivers/pci/iov.c:10:
include/asm-generic/div64.h:195:32: warning: right shift count >= width of type [-Wshift-count-overflow]
195 | } else if (likely(((n) >> 32) == 0)) { \
| ^~
include/linux/compiler.h:76:45: note: in definition of macro 'likely'
76 | # define likely(x) __builtin_expect(!!(x), 1)
| ^
drivers/pci/iov.c:1383:9: note: in expansion of macro 'do_div'
1383 | do_div(vf_len, num_vfs);
| ^~~~~~
>> include/asm-generic/div64.h:199:36: error: passing argument 1 of '__div64_32' from incompatible pointer type [-Werror=incompatible-pointer-types]
199 | __rem = __div64_32(&(n), __base); \
| ^~~~
| |
| resource_size_t * {aka unsigned int *}
drivers/pci/iov.c:1383:9: note: in expansion of macro 'do_div'
1383 | do_div(vf_len, num_vfs);
| ^~~~~~
include/asm-generic/div64.h:174:38: note: expected 'uint64_t *' {aka 'long long unsigned int *'} but argument is of type 'resource_size_t *' {aka 'unsigned int *'}
174 | extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
| ~~~~~~~~~~^~~~~~~~
cc1: some warnings being treated as errors
vim +/__div64_32 +199 include/asm-generic/div64.h
^1da177e4c3f41 Linus Torvalds 2005-04-16 176
^1da177e4c3f41 Linus Torvalds 2005-04-16 177 /* The unnecessary pointer compare is there
^1da177e4c3f41 Linus Torvalds 2005-04-16 178 * to check for type safety (n must be 64bit)
^1da177e4c3f41 Linus Torvalds 2005-04-16 179 */
^1da177e4c3f41 Linus Torvalds 2005-04-16 180 # define do_div(n,base) ({ \
^1da177e4c3f41 Linus Torvalds 2005-04-16 181 uint32_t __base = (base); \
^1da177e4c3f41 Linus Torvalds 2005-04-16 182 uint32_t __rem; \
^1da177e4c3f41 Linus Torvalds 2005-04-16 183 (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
911918aa7ef6f8 Nicolas Pitre 2015-11-02 184 if (__builtin_constant_p(__base) && \
911918aa7ef6f8 Nicolas Pitre 2015-11-02 185 is_power_of_2(__base)) { \
911918aa7ef6f8 Nicolas Pitre 2015-11-02 186 __rem = (n) & (__base - 1); \
911918aa7ef6f8 Nicolas Pitre 2015-11-02 187 (n) >>= ilog2(__base); \
c747ce4706190e Geert Uytterhoeven 2021-08-11 188 } else if (__builtin_constant_p(__base) && \
461a5e51060c93 Nicolas Pitre 2015-10-30 189 __base != 0) { \
461a5e51060c93 Nicolas Pitre 2015-10-30 190 uint32_t __res_lo, __n_lo = (n); \
461a5e51060c93 Nicolas Pitre 2015-10-30 191 (n) = __div64_const32(n, __base); \
461a5e51060c93 Nicolas Pitre 2015-10-30 192 /* the remainder can be computed with 32-bit regs */ \
461a5e51060c93 Nicolas Pitre 2015-10-30 193 __res_lo = (n); \
461a5e51060c93 Nicolas Pitre 2015-10-30 194 __rem = __n_lo - __res_lo * __base; \
911918aa7ef6f8 Nicolas Pitre 2015-11-02 195 } else if (likely(((n) >> 32) == 0)) { \
^1da177e4c3f41 Linus Torvalds 2005-04-16 196 __rem = (uint32_t)(n) % __base; \
^1da177e4c3f41 Linus Torvalds 2005-04-16 197 (n) = (uint32_t)(n) / __base; \
c747ce4706190e Geert Uytterhoeven 2021-08-11 198 } else { \
^1da177e4c3f41 Linus Torvalds 2005-04-16 @199 __rem = __div64_32(&(n), __base); \
c747ce4706190e Geert Uytterhoeven 2021-08-11 200 } \
^1da177e4c3f41 Linus Torvalds 2005-04-16 201 __rem; \
^1da177e4c3f41 Linus Torvalds 2005-04-16 202 })
^1da177e4c3f41 Linus Torvalds 2005-04-16 203
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.