summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci-sysfs.c127
-rw-r--r--include/linux/pci.h1
2 files changed, 128 insertions, 0 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index fbbb97f28259..74508c63bd47 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -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", pdev->sriov->total);
+}
+
+
+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->nr_virtfn);
+}
+
+/*
+ * 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 = pdev->sriov->total;
+ /* Requested VFs to enable < totalvfs and none enabled already */
+ if ((num_vfs > 0) && (num_vfs <= total)) {
+ if (pdev->sriov->nr_virtfn == 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->nr_virtfn) {
+ 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->nr_virtfn, num_vfs);
+ return -EINVAL;
+ }
+ }
+
+ /* disable vfs */
+ if (num_vfs == 0) {
+ if (pdev->sriov->nr_virtfn != 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),
@@ -1421,6 +1521,30 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
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,
@@ -1428,6 +1552,9 @@ static struct attribute_group pci_dev_attr_group = {
static const struct attribute_group *pci_dev_attr_groups[] = {
&pci_dev_attr_group,
+#ifdef CONFIG_PCI_IOV
+ &sriov_dev_attr_group,
+#endif
NULL,
};
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ee2179546c63..7ef8fba319da 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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;