Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9c15b42 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/don-sriov' into next

* pci/don-sriov:
  PCI: Remove useless "!dev" tests
  PCI: Use spec names for SR-IOV capability fields
  PCI: Provide method to reduce the number of total VFs supported
  PCI: SRIOV control and status via sysfs
  PCI: Use is_visible() with boot_vga attribute for pci_dev
  PCI: Add pci_device_type to pdev's device struct
parents 0dcccc5c 1452cd76
Loading
Loading
Loading
Loading
+67 −20
Original line number Diff line number Diff line
@@ -106,7 +106,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
		virtfn->resource[i].name = pci_name(virtfn);
		virtfn->resource[i].flags = res->flags;
		size = resource_size(res);
		do_div(size, iov->total);
		do_div(size, iov->total_VFs);
		virtfn->resource[i].start = res->start + size * id;
		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
		rc = request_resource(res, &virtfn->resource[i]);
@@ -194,7 +194,7 @@ static int sriov_migration(struct pci_dev *dev)
	u16 status;
	struct pci_sriov *iov = dev->sriov;

	if (!iov->nr_virtfn)
	if (!iov->num_VFs)
		return 0;

	if (!(iov->cap & PCI_SRIOV_CAP_VFM))
@@ -216,7 +216,7 @@ static void sriov_migration_task(struct work_struct *work)
	u16 status;
	struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask);

	for (i = iov->initial; i < iov->nr_virtfn; i++) {
	for (i = iov->initial_VFs; i < iov->num_VFs; i++) {
		state = readb(iov->mstate + i);
		if (state == PCI_SRIOV_VFM_MI) {
			writeb(PCI_SRIOV_VFM_AV, iov->mstate + i);
@@ -244,7 +244,7 @@ static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn)
	resource_size_t pa;
	struct pci_sriov *iov = dev->sriov;

	if (nr_virtfn <= iov->initial)
	if (nr_virtfn <= iov->initial_VFs)
		return 0;

	pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table);
@@ -294,15 +294,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
	if (!nr_virtfn)
		return 0;

	if (iov->nr_virtfn)
	if (iov->num_VFs)
		return -EINVAL;

	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
	if (initial > iov->total ||
	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
	if (initial > iov->total_VFs ||
	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
		return -EIO;

	if (nr_virtfn < 0 || nr_virtfn > iov->total ||
	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
		return -EINVAL;

@@ -359,7 +359,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
	msleep(100);
	pci_cfg_access_unlock(dev);

	iov->initial = initial;
	iov->initial_VFs = initial;
	if (nr_virtfn < initial)
		initial = nr_virtfn;

@@ -376,7 +376,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
	}

	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
	iov->nr_virtfn = nr_virtfn;
	iov->num_VFs = nr_virtfn;

	return 0;

@@ -401,13 +401,13 @@ static void sriov_disable(struct pci_dev *dev)
	int i;
	struct pci_sriov *iov = dev->sriov;

	if (!iov->nr_virtfn)
	if (!iov->num_VFs)
		return;

	if (iov->cap & PCI_SRIOV_CAP_VFM)
		sriov_disable_migration(dev);

	for (i = 0; i < iov->nr_virtfn; i++)
	for (i = 0; i < iov->num_VFs; i++)
		virtfn_remove(dev, i, 0);

	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
@@ -419,7 +419,7 @@ static void sriov_disable(struct pci_dev *dev)
	if (iov->link != dev->devfn)
		sysfs_remove_link(&dev->dev.kobj, "dep_link");

	iov->nr_virtfn = 0;
	iov->num_VFs = 0;
}

static int sriov_init(struct pci_dev *dev, int pos)
@@ -496,7 +496,7 @@ found:
	iov->pos = pos;
	iov->nres = nres;
	iov->ctrl = ctrl;
	iov->total = total;
	iov->total_VFs = total;
	iov->offset = offset;
	iov->stride = stride;
	iov->pgsz = pgsz;
@@ -529,7 +529,7 @@ failed:

static void sriov_release(struct pci_dev *dev)
{
	BUG_ON(dev->sriov->nr_virtfn);
	BUG_ON(dev->sriov->num_VFs);

	if (dev != dev->sriov->dev)
		pci_dev_put(dev->sriov->dev);
@@ -554,7 +554,7 @@ static void sriov_restore_state(struct pci_dev *dev)
		pci_update_resource(dev, i);

	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
		msleep(100);
@@ -661,7 +661,7 @@ int pci_iov_bus_range(struct pci_bus *bus)
	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (!dev->is_physfn)
			continue;
		busnr = virtfn_bus(dev, dev->sriov->total - 1);
		busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
		if (busnr > max)
			max = busnr;
	}
@@ -729,9 +729,56 @@ EXPORT_SYMBOL_GPL(pci_sriov_migration);
 */
int pci_num_vf(struct pci_dev *dev)
{
	if (!dev || !dev->is_physfn)
	if (!dev->is_physfn)
		return 0;
	else
		return dev->sriov->nr_virtfn;

	return dev->sriov->num_VFs;
}
EXPORT_SYMBOL_GPL(pci_num_vf);

/**
 * pci_sriov_set_totalvfs -- reduce the TotalVFs available
 * @dev: the PCI PF device
 * numvfs: number that should be used for TotalVFs supported
 *
 * Should be called from PF driver's probe routine with
 * device's mutex held.
 *
 * Returns 0 if PF is an SRIOV-capable device and
 * value of numvfs valid. If not a PF with VFS, return -EINVAL;
 * if VFs already enabled, return -EBUSY.
 */
int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
{
	if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs))
		return -EINVAL;

	/* Shouldn't change if VFs already enabled */
	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
		return -EBUSY;
	else
		dev->sriov->driver_max_VFs = numvfs;

	return 0;
}
EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);

/**
 * pci_sriov_get_totalvfs -- get total VFs supported on this devic3
 * @dev: the PCI PF device
 *
 * For a PCIe device with SRIOV support, return the PCIe
 * SRIOV capability value of TotalVFs or the value of driver_max_VFs
 * if the driver reduced it.  Otherwise, -EINVAL.
 */
int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
	if (!dev->is_physfn)
		return -EINVAL;

	if (dev->sriov->driver_max_VFs)
		return dev->sriov->driver_max_VFs;

	return dev->sriov->total_VFs;
}
EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
+161 −11
Original line number Diff line number Diff line
@@ -404,6 +404,106 @@ static ssize_t d3cold_allowed_show(struct device *dev,
}
#endif

#ifdef CONFIG_PCI_IOV
static ssize_t sriov_totalvfs_show(struct device *dev,
				   struct device_attribute *attr,
				   char *buf)
{
	struct pci_dev *pdev = to_pci_dev(dev);

	return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
}


static ssize_t sriov_numvfs_show(struct device *dev,
				 struct device_attribute *attr,
				 char *buf)
{
	struct pci_dev *pdev = to_pci_dev(dev);

	return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
}

/*
 * num_vfs > 0; number of vfs to enable
 * num_vfs = 0; disable all vfs
 *
 * Note: SRIOV spec doesn't allow partial VF
 *       disable, so its all or none.
 */
static ssize_t sriov_numvfs_store(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t count)
{
	struct pci_dev *pdev = to_pci_dev(dev);
	int num_vfs_enabled = 0;
	int num_vfs;
	int ret = 0;
	u16 total;

	if (kstrtoint(buf, 0, &num_vfs) < 0)
		return -EINVAL;

	/* is PF driver loaded w/callback */
	if (!pdev->driver || !pdev->driver->sriov_configure) {
		dev_info(&pdev->dev,
			 "Driver doesn't support SRIOV configuration via sysfs\n");
		return -ENOSYS;
	}

	/* if enabling vf's ... */
	total = pci_sriov_get_totalvfs(pdev);
	/* Requested VFs to enable < totalvfs and none enabled already */
	if ((num_vfs > 0) && (num_vfs <= total)) {
		if (pdev->sriov->num_VFs == 0) {
			num_vfs_enabled =
				pdev->driver->sriov_configure(pdev, num_vfs);
			if ((num_vfs_enabled >= 0) &&
			    (num_vfs_enabled != num_vfs)) {
				dev_warn(&pdev->dev,
					 "Only %d VFs enabled\n",
					 num_vfs_enabled);
				return count;
			} else if (num_vfs_enabled < 0)
				/* error code from driver callback */
				return num_vfs_enabled;
		} else if (num_vfs == pdev->sriov->num_VFs) {
			dev_warn(&pdev->dev,
				 "%d VFs already enabled; no enable action taken\n",
				 num_vfs);
			return count;
		} else {
			dev_warn(&pdev->dev,
				 "%d VFs already enabled. Disable before enabling %d VFs\n",
				 pdev->sriov->num_VFs, num_vfs);
			return -EINVAL;
		}
	}

	/* disable vfs */
	if (num_vfs == 0) {
		if (pdev->sriov->num_VFs != 0) {
			ret = pdev->driver->sriov_configure(pdev, 0);
			return ret ? ret : count;
		} else {
			dev_warn(&pdev->dev,
				 "All VFs disabled; no disable action taken\n");
			return count;
		}
	}

	dev_err(&pdev->dev,
		"Invalid value for number of VFs to enable: %d\n", num_vfs);

	return -EINVAL;
}

static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs);
static struct device_attribute sriov_numvfs_attr =
		__ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP),
		       sriov_numvfs_show, sriov_numvfs_store);
#endif /* CONFIG_PCI_IOV */

struct device_attribute pci_dev_attrs[] = {
	__ATTR_RO(resource),
	__ATTR_RO(vendor),
@@ -1303,29 +1403,20 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
		pdev->rom_attr = attr;
	}

	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
		retval = device_create_file(&pdev->dev, &vga_attr);
		if (retval)
			goto err_rom_file;
	}

	/* add platform-specific attributes */
	retval = pcibios_add_platform_entries(pdev);
	if (retval)
		goto err_vga_file;
		goto err_rom_file;

	/* add sysfs entries for various capabilities */
	retval = pci_create_capabilities_sysfs(pdev);
	if (retval)
		goto err_vga_file;
		goto err_rom_file;

	pci_create_firmware_label_files(pdev);

	return 0;

err_vga_file:
	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
		device_remove_file(&pdev->dev, &vga_attr);
err_rom_file:
	if (rom_size) {
		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
@@ -1411,3 +1502,62 @@ static int __init pci_sysfs_init(void)
}

late_initcall(pci_sysfs_init);

static struct attribute *pci_dev_dev_attrs[] = {
	&vga_attr.attr,
	NULL,
};

static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
						struct attribute *a, int n)
{
	struct device *dev = container_of(kobj, struct device, kobj);
	struct pci_dev *pdev = to_pci_dev(dev);

	if (a == &vga_attr.attr)
		if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
			return 0;

	return a->mode;
}

#ifdef CONFIG_PCI_IOV
static struct attribute *sriov_dev_attrs[] = {
	&sriov_totalvfs_attr.attr,
	&sriov_numvfs_attr.attr,
	NULL,
};

static umode_t sriov_attrs_are_visible(struct kobject *kobj,
					 struct attribute *a, int n)
{
	struct device *dev = container_of(kobj, struct device, kobj);

	if (!dev_is_pf(dev))
		return 0;

	return a->mode;
}

static struct attribute_group sriov_dev_attr_group = {
	.attrs = sriov_dev_attrs,
	.is_visible = sriov_attrs_are_visible,
};
#endif /* CONFIG_PCI_IOV */

static struct attribute_group pci_dev_attr_group = {
	.attrs = pci_dev_dev_attrs,
	.is_visible = pci_dev_attrs_are_visible,
};

static const struct attribute_group *pci_dev_attr_groups[] = {
	&pci_dev_attr_group,
#ifdef CONFIG_PCI_IOV
	&sriov_dev_attr_group,
#endif
	NULL,
};

struct device_type pci_dev_type = {
	.groups = pci_dev_attr_groups,
};
+5 −3
Original line number Diff line number Diff line
@@ -157,6 +157,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
}
extern struct device_attribute pci_dev_attrs[];
extern struct device_attribute pcibus_dev_attrs[];
extern struct device_type pci_dev_type;
#ifdef CONFIG_HOTPLUG
extern struct bus_attribute pci_bus_attrs[];
#else
@@ -232,13 +233,14 @@ struct pci_sriov {
	int nres;		/* number of resources */
	u32 cap;		/* SR-IOV Capabilities */
	u16 ctrl;		/* SR-IOV Control */
	u16 total;		/* total VFs associated with the PF */
	u16 initial;		/* initial VFs associated with the PF */
	u16 nr_virtfn;		/* number of VFs available */
	u16 total_VFs;		/* total VFs associated with the PF */
	u16 initial_VFs;	/* initial VFs associated with the PF */
	u16 num_VFs;		/* number of VFs available */
	u16 offset;		/* first VF Routing ID offset */
	u16 stride;		/* following VF stride */
	u32 pgsz;		/* page size for BAR alignment */
	u8 link;		/* Function Dependency Link */
	u16 driver_max_VFs;	/* max num VFs driver supports */
	struct pci_dev *dev;	/* lowest numbered PF */
	struct pci_dev *self;	/* this PF */
	struct mutex lock;	/* lock for VF bus */
+1 −0
Original line number Diff line number Diff line
@@ -975,6 +975,7 @@ int pci_setup_device(struct pci_dev *dev)
	dev->sysdata = dev->bus->sysdata;
	dev->dev.parent = dev->bus->bridge;
	dev->dev.bus = &pci_bus_type;
	dev->dev.type = &pci_dev_type;
	dev->hdr_type = hdr_type & 0x7f;
	dev->multifunction = !!(hdr_type & 0x80);
	dev->error_state = pci_channel_io_normal;
+11 −0
Original line number Diff line number Diff line
@@ -573,6 +573,7 @@ struct pci_driver {
	int  (*resume_early) (struct pci_dev *dev);
	int  (*resume) (struct pci_dev *dev);	                /* Device woken up */
	void (*shutdown) (struct pci_dev *dev);
	int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
	const struct pci_error_handlers *err_handler;
	struct device_driver	driver;
	struct pci_dynids dynids;
@@ -1613,6 +1614,8 @@ extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
extern void pci_disable_sriov(struct pci_dev *dev);
extern irqreturn_t pci_sriov_migration(struct pci_dev *dev);
extern int pci_num_vf(struct pci_dev *dev);
extern int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs);
extern int pci_sriov_get_totalvfs(struct pci_dev *dev);
#else
static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
{
@@ -1629,6 +1632,14 @@ static inline int pci_num_vf(struct pci_dev *dev)
{
	return 0;
}
static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
{
	return 0;
}
static inline int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
	return 0;
}
#endif

#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE)