summaryrefslogtreecommitdiff
path: root/drivers/pci/iov.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r--drivers/pci/iov.c153
1 files changed, 142 insertions, 11 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 10693b5d7eb6..ac4375954c94 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -7,11 +7,16 @@
* Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
*/
+#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 */
@@ -150,7 +155,28 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
if (!dev->is_physfn)
return 0;
- return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
+ return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)];
+}
+
+void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
+ resource_size_t size)
+{
+ if (!pci_resource_is_iov(resno)) {
+ pci_warn(dev, "%s is not an IOV resource\n",
+ pci_resource_name(dev, resno));
+ return;
+ }
+
+ dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)] = size;
+}
+
+bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
+{
+ u16 cmd;
+
+ pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_CTRL, &cmd);
+
+ return cmd & PCI_SRIOV_CTRL_MSE;
}
static void pci_read_vf_config_common(struct pci_dev *virtfn)
@@ -341,12 +367,14 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
virtfn->multifunction = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
- res = &dev->resource[i + PCI_IOV_RESOURCES];
+ int idx = pci_resource_num_from_vf_bar(i);
+
+ res = &dev->resource[idx];
if (!res->parent)
continue;
virtfn->resource[i].name = pci_name(virtfn);
virtfn->resource[i].flags = res->flags;
- size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
+ size = pci_iov_resource_size(dev, idx);
resource_set_range(&virtfn->resource[i],
res->start + size * id, size);
rc = request_resource(res, &virtfn->resource[i]);
@@ -643,8 +671,13 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
nres = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
- bars |= (1 << (i + PCI_IOV_RESOURCES));
- res = &dev->resource[i + PCI_IOV_RESOURCES];
+ int idx = pci_resource_num_from_vf_bar(i);
+ resource_size_t vf_bar_sz = pci_iov_resource_size(dev, idx);
+
+ bars |= (1 << idx);
+ res = &dev->resource[idx];
+ if (vf_bar_sz * nr_virtfn > resource_size(res))
+ continue;
if (res->parent)
nres++;
}
@@ -810,8 +843,10 @@ found:
nres = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
- res = &dev->resource[i + PCI_IOV_RESOURCES];
- res_name = pci_resource_name(dev, i + PCI_IOV_RESOURCES);
+ int idx = pci_resource_num_from_vf_bar(i);
+
+ res = &dev->resource[idx];
+ res_name = pci_resource_name(dev, idx);
/*
* If it is already FIXED, don't change it, something
@@ -850,6 +885,7 @@ found:
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
+ iov->vf_rebar_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VF_REBAR);
if (pdev)
iov->dev = pci_dev_get(pdev);
@@ -869,7 +905,7 @@ fail_max_buses:
dev->is_physfn = 0;
failed:
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
- res = &dev->resource[i + PCI_IOV_RESOURCES];
+ res = &dev->resource[pci_resource_num_from_vf_bar(i)];
res->flags = 0;
}
@@ -888,6 +924,30 @@ static void sriov_release(struct pci_dev *dev)
dev->sriov = NULL;
}
+static void sriov_restore_vf_rebar_state(struct pci_dev *dev)
+{
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_iov_vf_rebar_cap(dev);
+ if (!pos)
+ return;
+
+ pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
+ nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl);
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ int bar_idx, size;
+
+ pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
+ bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl);
+ size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]);
+ ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE;
+ ctrl |= FIELD_PREP(PCI_VF_REBAR_CTRL_BAR_SIZE, size);
+ pci_write_config_dword(dev, pos + PCI_VF_REBAR_CTRL, ctrl);
+ }
+}
+
static void sriov_restore_state(struct pci_dev *dev)
{
int i;
@@ -907,7 +967,7 @@ static void sriov_restore_state(struct pci_dev *dev)
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl);
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++)
- pci_update_resource(dev, i + PCI_IOV_RESOURCES);
+ pci_update_resource(dev, pci_resource_num_from_vf_bar(i));
pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
pci_iov_set_numvfs(dev, iov->num_VFs);
@@ -973,7 +1033,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno)
{
struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL;
struct resource *res = pci_resource_n(dev, resno);
- int vf_bar = resno - PCI_IOV_RESOURCES;
+ int vf_bar = pci_resource_num_to_vf_bar(resno);
struct pci_bus_region region;
u16 cmd;
u32 new;
@@ -1047,8 +1107,10 @@ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
*/
void pci_restore_iov_state(struct pci_dev *dev)
{
- if (dev->is_physfn)
+ if (dev->is_physfn) {
+ sriov_restore_vf_rebar_state(dev);
sriov_restore_state(dev);
+ }
}
/**
@@ -1255,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)
+{
+ u64 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);