|
@@ -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,
|
|
|
};
|
|
|
|