ia64/linux-2.6.18-xen.hg

changeset 826:64c8f9fbf609

PCI: add SR-IOV API for Physical Function driver

Add or remove the Virtual Function when the SR-IOV is enabled or
disabled by the device driver. This can happen anytime rather than
only at the device probe stage.

Signed-off-by: Yu Zhao <yu.zhao@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 18 11:40:10 2009 +0000 (2009-03-18)
parents 002e044dc979
children 71aedaa9e21c
files drivers/pci/iov.c drivers/pci/pci.h include/linux/pci.h
line diff
     1.1 --- a/drivers/pci/iov.c	Wed Mar 18 11:39:56 2009 +0000
     1.2 +++ b/drivers/pci/iov.c	Wed Mar 18 11:40:10 2009 +0000
     1.3 @@ -13,6 +13,7 @@
     1.4  #include <linux/delay.h>
     1.5  #include "pci.h"
     1.6  
     1.7 +#define VIRTFN_ID_LEN	16
     1.8  
     1.9  static inline u8 virtfn_bus(struct pci_dev *dev, int id)
    1.10  {
    1.11 @@ -26,6 +27,267 @@ static inline u8 virtfn_devfn(struct pci
    1.12  		dev->sriov->stride * id) & 0xff;
    1.13  }
    1.14  
    1.15 +static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
    1.16 +{
    1.17 +	struct pci_bus *child;
    1.18 +
    1.19 +	if (bus->number == busnr)
    1.20 +		return bus;
    1.21 +
    1.22 +	child = pci_find_bus(pci_domain_nr(bus), busnr);
    1.23 +	if (child)
    1.24 +		return child;
    1.25 +
    1.26 +	child = pci_add_new_bus(bus, NULL, busnr);
    1.27 +	if (!child)
    1.28 +		return NULL;
    1.29 +
    1.30 +	child->subordinate = busnr;
    1.31 +
    1.32 +	return child;
    1.33 +}
    1.34 +
    1.35 +static void virtfn_remove_bus(struct pci_bus *bus, int busnr)
    1.36 +{
    1.37 +	struct pci_bus *child;
    1.38 +
    1.39 +	if (bus->number == busnr)
    1.40 +		return;
    1.41 +
    1.42 +	child = pci_find_bus(pci_domain_nr(bus), busnr);
    1.43 +	BUG_ON(!child);
    1.44 +
    1.45 +	if (list_empty(&child->devices))
    1.46 +		pci_remove_bus(child);
    1.47 +}
    1.48 +
    1.49 +static int virtfn_add(struct pci_dev *dev, int id)
    1.50 +{
    1.51 +	int i;
    1.52 +	int rc;
    1.53 +	u64 size;
    1.54 +	char buf[VIRTFN_ID_LEN];
    1.55 +	struct pci_dev *virtfn;
    1.56 +	struct resource *res;
    1.57 +	struct pci_sriov *iov = dev->sriov;
    1.58 +
    1.59 +	virtfn = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
    1.60 +	if (!virtfn)
    1.61 +		return -ENOMEM;
    1.62 +
    1.63 +	mutex_lock(&iov->dev->sriov->lock);
    1.64 +	virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
    1.65 +	if (!virtfn->bus) {
    1.66 +		kfree(virtfn);
    1.67 +		mutex_unlock(&iov->dev->sriov->lock);
    1.68 +		return -ENOMEM;
    1.69 +	}
    1.70 +	virtfn->devfn = virtfn_devfn(dev, id);
    1.71 +	virtfn->vendor = dev->vendor;
    1.72 +	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
    1.73 +	pci_setup_device(virtfn);
    1.74 +	virtfn->dev.parent = dev->dev.parent;
    1.75 +
    1.76 +	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
    1.77 +		res = dev->resource + PCI_IOV_RESOURCES + i;
    1.78 +		if (!res->parent)
    1.79 +			continue;
    1.80 +		virtfn->resource[i].name = pci_name(virtfn);
    1.81 +		virtfn->resource[i].flags = res->flags;
    1.82 +		size = res->end - res->start + 1;
    1.83 +		do_div(size, iov->total);
    1.84 +		virtfn->resource[i].start = res->start + size * id;
    1.85 +		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
    1.86 +		rc = request_resource(res, &virtfn->resource[i]);
    1.87 +		BUG_ON(rc);
    1.88 +	}
    1.89 +
    1.90 +	pci_device_add(virtfn, virtfn->bus);
    1.91 +	mutex_unlock(&iov->dev->sriov->lock);
    1.92 +
    1.93 +	virtfn->physfn = pci_dev_get(dev);
    1.94 +	virtfn->is_virtfn = 1;
    1.95 +
    1.96 +	pci_bus_add_device(virtfn);
    1.97 +	sprintf(buf, "virtfn%u", id);
    1.98 +	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
    1.99 +	if (rc)
   1.100 +		goto failed1;
   1.101 +	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
   1.102 +	if (rc)
   1.103 +		goto failed2;
   1.104 +
   1.105 +	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
   1.106 +
   1.107 +	return 0;
   1.108 +
   1.109 +failed2:
   1.110 +	sysfs_remove_link(&dev->dev.kobj, buf);
   1.111 +failed1:
   1.112 +	pci_dev_put(dev);
   1.113 +	mutex_lock(&iov->dev->sriov->lock);
   1.114 +	pci_remove_bus_device(virtfn);
   1.115 +	virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
   1.116 +	mutex_unlock(&iov->dev->sriov->lock);
   1.117 +
   1.118 +	return rc;
   1.119 +}
   1.120 +
   1.121 +static void virtfn_remove(struct pci_dev *dev, int id)
   1.122 +{
   1.123 +	char buf[VIRTFN_ID_LEN];
   1.124 +	struct pci_bus *bus;
   1.125 +	struct pci_dev *virtfn;
   1.126 +	struct pci_sriov *iov = dev->sriov;
   1.127 +
   1.128 +	bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id));
   1.129 +	if (!bus)
   1.130 +		return;
   1.131 +
   1.132 +	virtfn = pci_get_slot(bus, virtfn_devfn(dev, id));
   1.133 +	if (!virtfn)
   1.134 +		return;
   1.135 +
   1.136 +	pci_dev_put(virtfn);
   1.137 +
   1.138 +	sprintf(buf, "virtfn%u", id);
   1.139 +	sysfs_remove_link(&dev->dev.kobj, buf);
   1.140 +	sysfs_remove_link(&virtfn->dev.kobj, "physfn");
   1.141 +
   1.142 +	mutex_lock(&iov->dev->sriov->lock);
   1.143 +	pci_remove_bus_device(virtfn);
   1.144 +	virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
   1.145 +	mutex_unlock(&iov->dev->sriov->lock);
   1.146 +
   1.147 +	pci_dev_put(dev);
   1.148 +}
   1.149 +
   1.150 +static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
   1.151 +{
   1.152 +	int rc;
   1.153 +	int i, j;
   1.154 +	int nres;
   1.155 +	u16 offset, stride, initial;
   1.156 +	struct resource *res;
   1.157 +	struct pci_dev *pdev;
   1.158 +	struct pci_sriov *iov = dev->sriov;
   1.159 +
   1.160 +	if (!nr_virtfn)
   1.161 +		return 0;
   1.162 +
   1.163 +	if (iov->nr_virtfn)
   1.164 +		return -EINVAL;
   1.165 +
   1.166 +	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
   1.167 +	if (initial > iov->total ||
   1.168 +	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
   1.169 +		return -EIO;
   1.170 +
   1.171 +	if (nr_virtfn < 0 || nr_virtfn > iov->total ||
   1.172 +	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
   1.173 +		return -EINVAL;
   1.174 +
   1.175 +	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
   1.176 +	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
   1.177 +	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
   1.178 +	if (!offset || (nr_virtfn > 1 && !stride))
   1.179 +		return -EIO;
   1.180 +
   1.181 +	nres = 0;
   1.182 +	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
   1.183 +		res = dev->resource + PCI_IOV_RESOURCES + i;
   1.184 +		if (res->parent)
   1.185 +			nres++;
   1.186 +	}
   1.187 +	if (nres != iov->nres) {
   1.188 +		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
   1.189 +		return -ENOMEM;
   1.190 +	}
   1.191 +
   1.192 +	iov->offset = offset;
   1.193 +	iov->stride = stride;
   1.194 +
   1.195 +	if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) {
   1.196 +		dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
   1.197 +		return -ENOMEM;
   1.198 +	}
   1.199 +
   1.200 +	if (iov->link != dev->devfn) {
   1.201 +		pdev = pci_get_slot(dev->bus, iov->link);
   1.202 +		if (!pdev)
   1.203 +			return -ENODEV;
   1.204 +
   1.205 +		pci_dev_put(pdev);
   1.206 +
   1.207 +		if (!pdev->is_physfn)
   1.208 +			return -ENODEV;
   1.209 +
   1.210 +		rc = sysfs_create_link(&dev->dev.kobj,
   1.211 +					&pdev->dev.kobj, "dep_link");
   1.212 +		if (rc)
   1.213 +			return rc;
   1.214 +	}
   1.215 +
   1.216 +	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
   1.217 +	pci_block_user_cfg_access(dev);
   1.218 +	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
   1.219 +	msleep(100);
   1.220 +	pci_unblock_user_cfg_access(dev);
   1.221 +
   1.222 +	iov->initial = initial;
   1.223 +	if (nr_virtfn < initial)
   1.224 +		initial = nr_virtfn;
   1.225 +
   1.226 +	for (i = 0; i < initial; i++) {
   1.227 +		rc = virtfn_add(dev, i);
   1.228 +		if (rc)
   1.229 +			goto failed;
   1.230 +	}
   1.231 +
   1.232 +	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
   1.233 +	iov->nr_virtfn = nr_virtfn;
   1.234 +
   1.235 +	return 0;
   1.236 +
   1.237 +failed:
   1.238 +	for (j = 0; j < i; j++)
   1.239 +		virtfn_remove(dev, j);
   1.240 +
   1.241 +	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
   1.242 +	pci_block_user_cfg_access(dev);
   1.243 +	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
   1.244 +	ssleep(1);
   1.245 +	pci_unblock_user_cfg_access(dev);
   1.246 +
   1.247 +	if (iov->link != dev->devfn)
   1.248 +		sysfs_remove_link(&dev->dev.kobj, "dep_link");
   1.249 +
   1.250 +	return rc;
   1.251 +}
   1.252 +
   1.253 +static void sriov_disable(struct pci_dev *dev)
   1.254 +{
   1.255 +	int i;
   1.256 +	struct pci_sriov *iov = dev->sriov;
   1.257 +
   1.258 +	if (!iov->nr_virtfn)
   1.259 +		return;
   1.260 +
   1.261 +	for (i = 0; i < iov->nr_virtfn; i++)
   1.262 +		virtfn_remove(dev, i);
   1.263 +
   1.264 +	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
   1.265 +	pci_block_user_cfg_access(dev);
   1.266 +	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
   1.267 +	ssleep(1);
   1.268 +	pci_unblock_user_cfg_access(dev);
   1.269 +
   1.270 +	if (iov->link != dev->devfn)
   1.271 +		sysfs_remove_link(&dev->dev.kobj, "dep_link");
   1.272 +
   1.273 +	iov->nr_virtfn = 0;
   1.274 +}
   1.275 +
   1.276  static int sriov_init(struct pci_dev *dev, int pos)
   1.277  {
   1.278  	int i;
   1.279 @@ -128,6 +390,8 @@ failed:
   1.280  
   1.281  static void sriov_release(struct pci_dev *dev)
   1.282  {
   1.283 +	BUG_ON(dev->sriov->nr_virtfn);
   1.284 +
   1.285  	if (dev == dev->sriov->dev)
   1.286  		mutex_destroy(&dev->sriov->lock);
   1.287  	else
   1.288 @@ -151,6 +415,7 @@ static void sriov_restore_state(struct p
   1.289  		pci_update_resource(dev, i);
   1.290  
   1.291  	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
   1.292 +	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
   1.293  	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
   1.294  	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
   1.295  		msleep(100);
   1.296 @@ -238,3 +503,35 @@ int pci_iov_bus_range(struct pci_bus *bu
   1.297  
   1.298  	return max ? max - bus->number : 0;
   1.299  }
   1.300 +
   1.301 +/**
   1.302 + * pci_enable_sriov - enable the SR-IOV capability
   1.303 + * @dev: the PCI device
   1.304 + *
   1.305 + * Returns 0 on success, or negative on failure.
   1.306 + */
   1.307 +int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
   1.308 +{
   1.309 +	might_sleep();
   1.310 +
   1.311 +	if (!dev->is_physfn)
   1.312 +		return -ENODEV;
   1.313 +
   1.314 +	return sriov_enable(dev, nr_virtfn);
   1.315 +}
   1.316 +EXPORT_SYMBOL_GPL(pci_enable_sriov);
   1.317 +
   1.318 +/**
   1.319 + * pci_disable_sriov - disable the SR-IOV capability
   1.320 + * @dev: the PCI device
   1.321 + */
   1.322 +void pci_disable_sriov(struct pci_dev *dev)
   1.323 +{
   1.324 +	might_sleep();
   1.325 +
   1.326 +	if (!dev->is_physfn)
   1.327 +		return;
   1.328 +
   1.329 +	sriov_disable(dev);
   1.330 +}
   1.331 +EXPORT_SYMBOL_GPL(pci_disable_sriov);
     2.1 --- a/drivers/pci/pci.h	Wed Mar 18 11:39:56 2009 +0000
     2.2 +++ b/drivers/pci/pci.h	Wed Mar 18 11:40:10 2009 +0000
     2.3 @@ -141,6 +141,8 @@ struct pci_sriov {
     2.4  	u32 cap;		/* SR-IOV Capabilities */
     2.5  	u16 ctrl;		/* SR-IOV Control */
     2.6  	u16 total;		/* total VFs associated with the PF */
     2.7 +	u16 initial;		/* initial VFs associated with the PF */
     2.8 +	u16 nr_virtfn;		/* number of VFs available */
     2.9  	u16 offset;		/* first VF Routing ID offset */
    2.10  	u16 stride;		/* following VF stride */
    2.11  	u32 pgsz;		/* page size for BAR alignment */
     3.1 --- a/include/linux/pci.h	Wed Mar 18 11:39:56 2009 +0000
     3.2 +++ b/include/linux/pci.h	Wed Mar 18 11:40:10 2009 +0000
     3.3 @@ -199,6 +199,7 @@ struct pci_dev {
     3.4  	unsigned int	msix_enabled:1;
     3.5  	unsigned int	ari_enabled:1;	/* ARI forwarding */
     3.6  	unsigned int	is_physfn:1;
     3.7 +	unsigned int	is_virtfn:1;
     3.8  
     3.9  	u32		saved_config_space[16]; /* config space saved at suspend time */
    3.10  	struct hlist_head saved_cap_space;
    3.11 @@ -206,7 +207,10 @@ struct pci_dev {
    3.12  	int rom_attr_enabled;		/* has display of the rom attribute been enabled? */
    3.13  	struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
    3.14  #ifdef CONFIG_PCI_IOV
    3.15 -	struct pci_sriov *sriov;	/* SR-IOV capability related */
    3.16 +	union {
    3.17 +		struct pci_sriov *sriov;	/* SR-IOV capability related */
    3.18 +		struct pci_dev *physfn;	/* the PF this VF is associated with */
    3.19 +	};
    3.20  #endif
    3.21  };
    3.22  
    3.23 @@ -829,5 +833,18 @@ extern int pci_pci_problems;
    3.24  int pci_is_guestdev(struct pci_dev *dev);
    3.25  #endif /* CONFIG_PCI_GUESTDEV */
    3.26  
    3.27 +#ifdef CONFIG_PCI_IOV
    3.28 +extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
    3.29 +extern void pci_disable_sriov(struct pci_dev *dev);
    3.30 +#else
    3.31 +static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
    3.32 +{
    3.33 +	return -ENODEV;
    3.34 +}
    3.35 +static inline void pci_disable_sriov(struct pci_dev *dev)
    3.36 +{
    3.37 +}
    3.38 +#endif
    3.39 +
    3.40  #endif /* __KERNEL__ */
    3.41  #endif /* LINUX_PCI_H */