Commit 29ccb7b9 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/resources'

- Restore VF resizable BAR state after reset (Michał Winiarski)

- Add pci_resource_num_to_vf_bar() and pci_resource_num_from_vf_bar() to
  convert between VF BAR number and the dev->resource[] index (Michał
  Winiarski)

- Allow IOV resources (VF BARs) to be resized (Michał Winiarski)

- Add pci_iov_vf_bar_set_size() so drivers can control VF BAR size (Michał
  Winiarski)

* pci/resources:
  PCI/IOV: Allow drivers to control VF BAR size
  PCI/IOV: Check that VF BAR fits within the reservation
  PCI/IOV: Allow IOV resources to be resized in pci_resize_resource()
  PCI/IOV: Add pci_resource_num_to_vf_bar() to convert VF BAR number to/from IOV resource
  PCI/IOV: Restore VF resizable BAR state after reset
parents 618c1ead 84f89041
Loading
Loading
Loading
Loading
+142 −11
Original line number Diff line number Diff line
@@ -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 @@ static int sriov_init(struct pci_dev *dev, int pos)

	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 @@ static int sriov_init(struct pci_dev *dev, int pos)
	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 @@ static int sriov_init(struct pci_dev *dev, int pos)
	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,9 +1107,11 @@ 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);
	}
}

/**
 * pci_vf_drivers_autoprobe - set PF property drivers_autoprobe for VFs
@@ -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);
+7 −1
Original line number Diff line number Diff line
@@ -3756,7 +3756,13 @@ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
	unsigned int pos, nbars, i;
	u32 ctrl;

	if (pci_resource_is_iov(bar)) {
		pos = pci_iov_vf_rebar_cap(pdev);
		bar = pci_resource_num_to_vf_bar(bar);
	} else {
		pos = pdev->rebar_cap;
	}

	if (!pos)
		return -ENOTSUPP;

+39 −0
Original line number Diff line number Diff line
@@ -492,6 +492,7 @@ struct pci_sriov {
	u16		subsystem_vendor; /* VF subsystem vendor */
	u16		subsystem_device; /* VF subsystem device */
	resource_size_t	barsz[PCI_SRIOV_NUM_BARS];	/* VF BAR size */
	u16		vf_rebar_cap;	/* VF Resizable BAR capability offset */
	bool		drivers_autoprobe; /* Auto probing of VFs by driver */
};

@@ -716,10 +717,28 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
void pci_restore_iov_state(struct pci_dev *dev);
int pci_iov_bus_range(struct pci_bus *bus);
void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
			       resource_size_t size);
bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
{
	if (!dev->is_physfn)
		return 0;

	return dev->sriov->vf_rebar_cap;
}
static inline bool pci_resource_is_iov(int resno)
{
	return resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END;
}
static inline int pci_resource_num_from_vf_bar(int resno)
{
	return resno + PCI_IOV_RESOURCES;
}
static inline int pci_resource_num_to_vf_bar(int resno)
{
	return resno - PCI_IOV_RESOURCES;
}
extern const struct attribute_group sriov_pf_dev_attr_group;
extern const struct attribute_group sriov_vf_dev_attr_group;
#else
@@ -740,10 +759,30 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
{
	return 0;
}
static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
					     resource_size_t size) { }
static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
{
	return false;
}
static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
{
	return 0;
}
static inline bool pci_resource_is_iov(int resno)
{
	return false;
}
static inline int pci_resource_num_from_vf_bar(int resno)
{
	WARN_ON_ONCE(1);
	return -ENODEV;
}
static inline int pci_resource_num_to_vf_bar(int resno)
{
	WARN_ON_ONCE(1);
	return -ENODEV;
}
#endif /* CONFIG_PCI_IOV */

#ifdef CONFIG_PCIE_TPH
+2 −1
Original line number Diff line number Diff line
@@ -1888,7 +1888,8 @@ static int iov_resources_unassigned(struct pci_dev *dev, void *data)
	bool *unassigned = data;

	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
		struct resource *r = &dev->resource[i + PCI_IOV_RESOURCES];
		int idx = pci_resource_num_from_vf_bar(i);
		struct resource *r = &dev->resource[idx];
		struct pci_bus_region region;

		/* Not assigned or rejected by kernel? */
+30 −5
Original line number Diff line number Diff line
@@ -423,13 +423,39 @@ void pci_release_resource(struct pci_dev *dev, int resno)
}
EXPORT_SYMBOL(pci_release_resource);

static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
						  int resno)
{
	u16 cmd;

	if (pci_resource_is_iov(resno))
		return pci_iov_is_memory_decoding_enabled(dev);

	pci_read_config_word(dev, PCI_COMMAND, &cmd);

	return cmd & PCI_COMMAND_MEMORY;
}

static void pci_resize_resource_set_size(struct pci_dev *dev, int resno,
					 int size)
{
	resource_size_t res_size = pci_rebar_size_to_bytes(size);
	struct resource *res = pci_resource_n(dev, resno);

	if (!pci_resource_is_iov(resno)) {
		resource_set_size(res, res_size);
	} else {
		resource_set_size(res, res_size * pci_sriov_get_totalvfs(dev));
		pci_iov_resource_set_size(dev, resno, res_size);
	}
}

int pci_resize_resource(struct pci_dev *dev, int resno, int size)
{
	struct resource *res = pci_resource_n(dev, resno);
	struct pci_host_bridge *host;
	int old, ret;
	u32 sizes;
	u16 cmd;

	/* Check if we must preserve the firmware's resource assignment */
	host = pci_find_host_bridge(dev->bus);
@@ -440,8 +466,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size)
	if (!(res->flags & IORESOURCE_UNSET))
		return -EBUSY;

	pci_read_config_word(dev, PCI_COMMAND, &cmd);
	if (cmd & PCI_COMMAND_MEMORY)
	if (pci_resize_is_memory_decoding_enabled(dev, resno))
		return -EBUSY;

	sizes = pci_rebar_get_possible_sizes(dev, resno);
@@ -459,7 +484,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size)
	if (ret)
		return ret;

	resource_set_size(res, pci_rebar_size_to_bytes(size));
	pci_resize_resource_set_size(dev, resno, size);

	/* Check if the new config works by trying to assign everything. */
	if (dev->bus->self) {
@@ -471,7 +496,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size)

error_resize:
	pci_rebar_set_size(dev, resno, old);
	resource_set_size(res, pci_rebar_size_to_bytes(old));
	pci_resize_resource_set_size(dev, resno, old);
	return ret;
}
EXPORT_SYMBOL(pci_resize_resource);
Loading