PCI: Linux kernel SR-IOV support
Subsystems (3)
v12 cover PCI: Linux kernel SR-IOV support
Greetings,
Following patches are intended to support SR-IOV capability in the Linux kernel. With these patches, people can turn a PCI device with the capability into multiple ones from software perspective, which will benefit KVM and achieve other purposes such as QoS, security, and etc.
SR-IOV specification can be found at:
http://www.pcisig.com/members/downloads/specifications/iov/sr-iov1.0_11Sep07.pdf
(it requires membership.)
Devices that support SR-IOV are available from following vendors:
http://download.intel.com/design/network/ProdBrf/320025.pdf
http://www.myri.com/vlsi/Lanai_Z8ES_Datasheet.pdf
http://www.neterion.com/products/pdfs/X3100ProductBrief.pdf
The patches to enable the SR-IOV capability of Intel 82576 NIC are available at (a.k.a Physical Function driver):
http://patchwork.kernel.org/patch/8063/
http://patchwork.kernel.org/patch/8064/
http://patchwork.kernel.org/patch/8065/
http://patchwork.kernel.org/patch/8066/
And the driver for Intel 82576 Virtual Function are available at:
http://patchwork.kernel.org/patch/11029/
http://patchwork.kernel.org/patch/11028/
Major changes from v11 to v12:
1, fix using garbage entry pointer after the list_for_each (Matthew Wilcox) 2, use #ifdef around SR-IOV structure in the pci_dev (Matthew Wilcox) 3, enhance the Kconfig help text for the SR-IOV (Matthew Wilcox)
v10 to v11:
1, use pci_setup_device() to setup Virtual Function (Matthew Wilcox) 2, various coding style fixes (Matthew Wilcox)
3, wording and grammar fixes (Randy Dunlap)
v9 -> v10:
1, minor fix in pci_restore_iov_state().
2, respin against the latest tree.
v8 -> v9:
1, put a might_sleep() into SR-IOV API which sleeps (Andi Kleen) 2, block user config accesses before clearing VF Enable bit (Matthew Wilcox)
Yu Zhao (8):
PCI: initialize and release SR-IOV capability
PCI: restore saved SR-IOV state
PCI: reserve bus range for SR-IOV device
PCI: centralize device setup code
PCI: add SR-IOV API for Physical Function driver
PCI: handle SR-IOV Virtual Function Migration
PCI: document SR-IOV sysfs entries
PCI: manual for SR-IOV user and driver developer
Documentation/ABI/testing/sysfs-bus-pci | 27 ++ Documentation/DocBook/kernel-api.tmpl | 1 + Documentation/PCI/pci-iov-howto.txt | 99 +++++ drivers/pci/Kconfig | 10 + drivers/pci/Makefile | 2 + drivers/pci/iov.c | 680 +++++++++++++++++++++++++++++++ drivers/pci/pci.c | 8 + drivers/pci/pci.h | 53 +++ drivers/pci/probe.c | 86 +++-- include/linux/pci.h | 34 ++ include/linux/pci_regs.h | 33 ++ 11 files changed, 994 insertions(+), 39 deletions(-) create mode 100644 Documentation/PCI/pci-iov-howto.txt create mode 100644 drivers/pci/iov.c
PATCH v12 1/8 PCI: initialize and release SR-IOV capability
If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF.
--- drivers/pci/Kconfig | 10 +++ drivers/pci/Makefile | 2 + drivers/pci/iov.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 7 ++ drivers/pci/pci.h | 37 +++++++++ drivers/pci/probe.c | 4 + include/linux/pci.h | 11 +++ include/linux/pci_regs.h | 33 ++++++++ 8 files changed, 286 insertions(+), 0 deletions(-) create mode 100644 drivers/pci/iov.c
drivers/pci/Kconfig +10 -0
@@ -59,3 +59,13 @@ config HT_IRQ This allows native hypertransport devices to use interrupts. If unsure say Y. + +config PCI_IOV + bool "PCI IOV support" + depends on PCI + help + I/O Virtualization is a PCI feature supported by some devices + which allows them to create virtual devices which share their + physical resources. + + If unsure, say N.
drivers/pci/Makefile +2 -0
@@ -29,6 +29,8 @@ obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o +obj-$(CONFIG_PCI_IOV) += iov.o + # # Some architectures use the generic PCI setup functions #
drivers/pci/iov.c new file +182 -0
182 lines not on this page · raw message
drivers/pci/pci.c +7 -0
@@ -2346,12 +2346,19 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)*/ int pci_resource_bar (struct pci_dev *dev,int resno,enum pci_bar_type *type) { +int reg; +if (resno <PCI_ROM_RESOURCE ) { *type = pci_bar_unknown;return PCI_BASE_ADDRESS_0 +4 * resno; }else if (resno ==PCI_ROM_RESOURCE ) { *type = pci_bar_mem32;return dev->rom_base_reg; + }else if (resno <PCI_BRIDGE_RESOURCES ) { +/* device specific resource */ + reg =pci_iov_resource_bar (dev, resno, type); +if (reg) +return reg; }dev_err (&dev->dev,"BAR: invalid resource #%d\n" , resno);
drivers/pci/pci.h +37 -0
@@ -195,4 +195,41 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } +/* Single Root I/O Virtualization */ +struct pci_sriov { +int pos;/* capability position */ +int nres;/* number of resources */ +u32 cap;/* SR-IOV Capabilities */ +u16 ctrl;/* SR-IOV Control */ +u16 total;/* total VFs associated with the PF */ +u16 offset;/* first VF Routing ID offset */ +u16 stride;/* following VF stride */ +u32 pgsz;/* page size for BAR alignment */ +u8 link;/* Function Dependency Link */ +struct pci_dev *dev;/* lowest numbered PF */ +struct pci_dev *self;/* this PF */ +struct mutex lock;/* lock for VF bus */ +}; + +#ifdef CONFIG_PCI_IOV +extern int pci_iov_init (struct pci_dev *dev); +extern void pci_iov_release (struct pci_dev *dev); +extern int pci_iov_resource_bar (struct pci_dev *dev,int resno, +enum pci_bar_type *type); +#else +static inline int pci_iov_init (struct pci_dev *dev) +{ +return -ENODEV ; +} +static inline void pci_iov_release (struct pci_dev *dev) + +{ +} +static inline int pci_iov_resource_bar (struct pci_dev *dev,int resno, +enum pci_bar_type *type) +{ +return 0 ; +} +#endif /* CONFIG_PCI_IOV */ +#endif /* DRIVERS_PCI_H */
drivers/pci/probe.c +4 -0
@@ -785,6 +785,7 @@ static int pci_setup_device(struct pci_dev * dev)static void pci_release_capabilities (struct pci_dev *dev) {pci_vpd_release (dev); +pci_iov_release (dev); }/**
@@ -972,6 +973,9 @@ static void pci_init_capabilities(struct pci_dev *dev)/* Alternative Routing-ID Forwarding */ pci_enable_ari (dev); + +/* Single Root I/O Virtualization */ +pci_iov_init (dev); }void pci_device_add (struct pci_dev *dev,struct pci_bus *bus)
include/linux/pci.h +11 -0
@@ -93,6 +93,12 @@ enum {/* #6: expansion ROM resource */ PCI_ROM_RESOURCE, +/* device specific resources */ +#ifdef CONFIG_PCI_IOV + PCI_IOV_RESOURCES, + PCI_IOV_RESOURCE_END = PCI_IOV_RESOURCES + PCI_SRIOV_NUM_BARS - 1, +#endif +/* resources assigned to buses behind the bridge */ #define PCI_BRIDGE_RESOURCE_NUM 4
@@ -180,6 +186,7 @@ struct pci_cap_saved_state {struct pcie_link_state ;struct pci_vpd ; +struct pci_sriov ;/* * The pci_dev structure is used to describe PCI devices.
@@ -257,6 +264,7 @@ struct pci_dev { unsigned int is_managed:1; unsigned int is_pcie:1; unsigned int state_saved:1; + unsigned int is_physfn:1;pci_dev_flags_t dev_flags;atomic_t enable_cnt;/* pci_enable_device has been called */
@@ -270,6 +278,9 @@ struct pci_dev {struct list_head msi_list;#endif struct pci_vpd *vpd; +#ifdef CONFIG_PCI_IOV +struct pci_sriov *sriov;/* SR-IOV capability related */ +#endif };extern struct pci_dev *alloc_pci_dev (void );
include/linux/pci_regs.h +33 -0
@@ -375,6 +375,7 @@#define PCI_EXP_TYPE_UPSTREAM 0x5/* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6/* Downstream Port */ #define PCI_EXP_TYPE_PCI_BRIDGE 0x7/* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_RC_END 0x9/* Root Complex Integrated Endpoint */ #define PCI_EXP_FLAGS_SLOT 0x0100/* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00/* Interrupt message number */ #define PCI_EXP_DEVCAP 4/* Device capabilities */
@@ -498,6 +499,7 @@#define PCI_EXT_CAP_ID_DSN 3#define PCI_EXT_CAP_ID_PWR 4#define PCI_EXT_CAP_ID_ARI 14 +#define PCI_EXT_CAP_ID_SRIOV 16/* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4/* Uncorrectable Error Status */
@@ -615,4 +617,35 @@#define PCI_ARI_CTRL_ACS 0x0002/* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG (x) (((x) >> 4) & 7)/* Function Group */ +/* Single Root I/O Virtualization */ +#define PCI_SRIOV_CAP 0x04/* SR-IOV Capabilities */ +#define PCI_SRIOV_CAP_VFM 0x01/* VF Migration Capable */ +#define PCI_SRIOV_CAP_INTR (x) ((x) >> 21)/* Interrupt Message Number */ +#define PCI_SRIOV_CTRL 0x08/* SR-IOV Control */ +#define PCI_SRIOV_CTRL_VFE 0x01/* VF Enable */ +#define PCI_SRIOV_CTRL_VFM 0x02/* VF Migration Enable */ +#define PCI_SRIOV_CTRL_INTR 0x04/* VF Migration Interrupt Enable */ +#define PCI_SRIOV_CTRL_MSE 0x08/* VF Memory Space Enable */ +#define PCI_SRIOV_CTRL_ARI 0x10/* ARI Capable Hierarchy */ +#define PCI_SRIOV_STATUS 0x0a/* SR-IOV Status */ +#define PCI_SRIOV_STATUS_VFM 0x01/* VF Migration Status */ +#define PCI_SRIOV_INITIAL_VF 0x0c/* Initial VFs */ +#define PCI_SRIOV_TOTAL_VF 0x0e/* Total VFs */ +#define PCI_SRIOV_NUM_VF 0x10/* Number of VFs */ +#define PCI_SRIOV_FUNC_LINK 0x12/* Function Dependency Link */ +#define PCI_SRIOV_VF_OFFSET 0x14/* First VF Offset */ +#define PCI_SRIOV_VF_STRIDE 0x16/* Following VF Stride */ +#define PCI_SRIOV_VF_DID 0x1a/* VF Device ID */ +#define PCI_SRIOV_SUP_PGSIZE 0x1c/* Supported Page Sizes */ +#define PCI_SRIOV_SYS_PGSIZE 0x20/* System Page Size */ +#define PCI_SRIOV_BAR 0x24/* VF BAR0 */ +#define PCI_SRIOV_NUM_BARS 6/* Number of VF BARs */ +#define PCI_SRIOV_VFM 0x3c/* VF Migration State Array Offset*/ +#define PCI_SRIOV_VFM_BIR (x) ((x) & 7)/* State BIR */ +#define PCI_SRIOV_VFM_OFFSET (x) ((x) & ~7)/* State Offset */ +#define PCI_SRIOV_VFM_UA 0x0/* Inactive.Unavailable */ +#define PCI_SRIOV_VFM_MI 0x1/* Dormant.MigrateIn */ +#define PCI_SRIOV_VFM_MO 0x2/* Active.MigrateOut */ +#define PCI_SRIOV_VFM_AV 0x3/* Active.Available */ +#endif /* LINUX_PCI_REGS_H */
Signature · 1 line
1.5.6.4
Re: [PATCH v12 1/8] PCI: initialize and release SR-IOV capability
Quoted 8 lines · On Fri, 20 Mar 2009 11:25:11 +0800 Yu Zhao <[email protected]> wrote:
If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF.
Reviewed-by Matthew Wilcox <[email protected]>Signed-off-by Yu Zhao <[email protected]>
I applied this series to my linux-next branch, but there were a few conflicts here and there, so please check it out. Looks like from start to finish this took about 6 months to get banged into shape, thanks for staying on it, Yu!
Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
Re: [PATCH v12 1/8] PCI: initialize and release SR-IOV capability
Quoted 9 lines · Jesse Barnes
Quoted 6 lines · On Fri, 20 Mar 2009 11:25:11 +0800 Yu Zhao <[email protected]> wrote:
If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF.
Quoted 7 lines
Quoted 2 lines
Reviewed-by Matthew Wilcox <[email protected]>Signed-off-by Yu Zhao <[email protected]>I applied this series to my linux-next branch, but there were a few conflicts here and there, so please check it out. Looks like from start to finish this took about 6 months to get banged into shape, thanks for staying on it, Yu!
Yes, I checked them and found there is conflict between the SR-IOV changes and Yinghai's 'PCI/x86: detect host bridge config space size w/o using quirks'. Following is the fix, thanks!
New pci_cfg_space_size() needs invalid pdev->class, put it in the right place in the pci_setup_device().
--- drivers/pci/probe.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)
drivers/pci/probe.c +3 -4
@@ -713,7 +713,6 @@ int pci_setup_device(struct pci_dev *dev) dev->dev.bus = &pci_bus_type; dev->hdr_type = hdr_type &0x7f ; dev->multifunction = !!(hdr_type &0x80 ); - dev->cfg_size =pci_cfg_space_size (dev); dev->error_state = pci_channel_io_normal;set_pcie_port_type (dev);
@@ -738,6 +737,9 @@ int pci_setup_device(struct pci_dev *dev)dev_dbg (&dev->dev,"found [%04x:%04x] class %06x header type %02x\n" , dev->vendor, dev->device, class, dev->hdr_type); +/* need to have dev->class ready */ + dev->cfg_size =pci_cfg_space_size (dev); +/* "Unknown power state" */ dev->current_state =PCI_UNKNOWN ;
@@ -959,9 +961,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)return NULL ; } -/* need to have dev->class ready */ - dev->cfg_size =pci_cfg_space_size (dev); -return dev; }
Signature · 1 line
1.5.6.4
Jesse Barnes
AppliedRe: [PATCH v12 1/8] PCI: initialize and release SR-IOV capability
Quoted 23 lines · On Sat, 21 Mar 2009 22:05:11 +0800 Yu Zhao <[email protected]> wrote:
Quoted 9 lines · Jesse Barnes
Quoted 6 lines · On Fri, 20 Mar 2009 11:25:11 +0800 Yu Zhao <[email protected]> wrote:
If a device has the SR-IOV capability, initialize it (set the ARI Capable Hierarchy in the lowest numbered PF if necessary; calculate the System Page Size for the VF MMIO, probe the VF Offset, Stride and BARs). A lock for the VF bus allocation is also initialized if a PF is the lowest numbered PF.
Quoted 7 lines
Quoted 2 lines
Reviewed-by Matthew Wilcox <[email protected]>Signed-off-by Yu Zhao <[email protected]>I applied this series to my linux-next branch, but there were a few conflicts here and there, so please check it out. Looks like from start to finish this took about 6 months to get banged into shape, thanks for staying on it, Yu!
Yes, I checked them and found there is conflict between the SR-IOV changes and Yinghai's 'PCI/x86: detect host bridge config space size w/o using quirks'. Following is the fix, thanks!
Quoted 4 lines
New
pci_cfg_space_size()needs invalidpdev->class, put it in the right place in thepci_setup_device().Signed-off-by Yu Zhao <[email protected]>
Applied, thanks Yu.
Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
PATCH v12 2/8 PCI: restore saved SR-IOV state
In reply to Yu ZhaoRestore the volatile registers in the SR-IOV capability after the D3->D0 transition.
--- drivers/pci/iov.c | 29 +++++++++++++++++++++++++++++ drivers/pci/pci.c | 1 + drivers/pci/pci.h | 4 ++++ 3 files changed, 34 insertions(+), 0 deletions(-)
drivers/pci/iov.c +29 -0
@@ -129,6 +129,25 @@ static void sriov_release(struct pci_dev *dev) dev->sriov =NULL ; } +static void sriov_restore_state (struct pci_dev *dev) +{ +int i; +u16 ctrl; +struct pci_sriov *iov = dev->sriov; + +pci_read_config_word (dev, iov->pos +PCI_SRIOV_CTRL , &ctrl); +if (ctrl &PCI_SRIOV_CTRL_VFE ) +return ; + +for (i =PCI_IOV_RESOURCES ; i <=PCI_IOV_RESOURCE_END ; i++) +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_CTRL , iov->ctrl); +if (iov->ctrl &PCI_SRIOV_CTRL_VFE ) +msleep (100 ); +} +/** * pci_iov_init - initialize the IOV capability * @dev: the PCI device
@@ -180,3 +199,13 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno,return dev->sriov->pos +PCI_SRIOV_BAR +4 * (resno -PCI_IOV_RESOURCES ); } + +/** +* pci_restore_iov_state - restore the state of the IOV capability +* @dev: the PCI device +*/ +void pci_restore_iov_state (struct pci_dev *dev) +{ +if (dev->is_physfn) +sriov_restore_state (dev); +}
drivers/pci/pci.c +1 -0
@@ -773,6 +773,7 @@ pci_restore_state(struct pci_dev *dev) }pci_restore_pcix_state (dev);pci_restore_msi_state (dev); +pci_restore_iov_state (dev);return 0 ; }
drivers/pci/pci.h +4 -0
@@ -216,6 +216,7 @@ extern int pci_iov_init(struct pci_dev *dev);extern void pci_iov_release (struct pci_dev *dev);extern int pci_iov_resource_bar (struct pci_dev *dev,int resno,enum pci_bar_type *type); +extern void pci_restore_iov_state (struct pci_dev *dev);#else static inline int pci_iov_init (struct pci_dev *dev) {
@@ -230,6 +231,9 @@ static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno, {return 0 ; } +static inline void pci_restore_iov_state (struct pci_dev *dev) +{ +}#endif /* CONFIG_PCI_IOV */ #endif /* DRIVERS_PCI_H */
Signature · 1 line
1.5.6.4
PATCH v12 3/8 PCI: reserve bus range for SR-IOV device
In reply to Yu ZhaoReserve the bus number range used by the Virtual Function when pcibios_assign_all_busses() returns true.
--- drivers/pci/iov.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 5 +++++ drivers/pci/probe.c | 3 +++ 3 files changed, 44 insertions(+), 0 deletions(-)
drivers/pci/iov.c +36 -0
@@ -14,6 +14,18 @@#include "pci.h" +static inline u8 virtfn_bus (struct pci_dev *dev,int id) +{ +return dev->bus->number + ((dev->devfn + dev->sriov->offset + + dev->sriov->stride * id) >>8 ); +} + +static inline u8 virtfn_devfn (struct pci_dev *dev,int id) +{ +return (dev->devfn + dev->sriov->offset + + dev->sriov->stride * id) &0xff ; +} +static int sriov_init (struct pci_dev *dev,int pos) {int i;
@@ -209,3 +221,27 @@ void pci_restore_iov_state(struct pci_dev *dev)if (dev->is_physfn)sriov_restore_state (dev); } + +/** +* pci_iov_bus_range - find bus range used by Virtual Function +* @bus: the PCI bus +* +* Returns max number of buses (exclude current one) used by Virtual +* Functions. +*/ +int pci_iov_bus_range (struct pci_bus *bus) +{ +int max =0 ; +u8 busnr; +struct pci_dev *dev; + +list_for_each_entry (dev, &bus->devices, bus_list) { +if (!dev->is_physfn) +continue ; + busnr =virtfn_bus (dev, dev->sriov->total -1 ); +if (busnr > max) + max = busnr; + } + +return max ? max - bus->number :0 ; +}
drivers/pci/pci.h +5 -0
@@ -217,6 +217,7 @@ extern void pci_iov_release(struct pci_dev *dev);extern int pci_iov_resource_bar (struct pci_dev *dev,int resno,enum pci_bar_type *type);extern void pci_restore_iov_state (struct pci_dev *dev); +extern int pci_iov_bus_range (struct pci_bus *bus);#else static inline int pci_iov_init (struct pci_dev *dev) {
@@ -234,6 +235,10 @@ static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno,static inline void pci_restore_iov_state (struct pci_dev *dev) { } +static inline int pci_iov_bus_range (struct pci_bus *bus) +{ +return 0 ; +}#endif /* CONFIG_PCI_IOV */ #endif /* DRIVERS_PCI_H */
drivers/pci/probe.c +3 -0
@@ -1078,6 +1078,9 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)for (devfn =0 ; devfn <0x100 ; devfn +=8 )pci_scan_slot (bus, devfn); +/* Reserve buses for SR-IOV capability. */ + max +=pci_iov_bus_range (bus); +/* * After performing arch-dependent fixup of the bus, look behind * all PCI-to-PCI bridges on this bus.
Signature · 1 line
1.5.6.4
PATCH v12 4/8 PCI: centralize device setup code
In reply to Yu ZhaoMove the device setup stuff into pci_setup_device() which will be used to setup the Virtual Function later.
--- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 79 ++++++++++++++++++++++++++------------------------- 2 files changed, 41 insertions(+), 39 deletions(-)
drivers/pci/pci.h +1 -0
@@ -178,6 +178,7 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +extern int pci_setup_device (struct pci_dev *dev);extern int __pci_read_base (struct pci_dev *dev,enum pci_bar_type type,struct resource *res,unsigned int reg); extern int pci_resource_bar(struct pci_dev *dev, int resno,
drivers/pci/probe.c +40 -39
134 lines not on this page · raw message
Signature · 1 line
1.5.6.4
PATCH v12 5/8 PCI: add SR-IOV API for Physical Function driver
In reply to Yu ZhaoAdd 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.
--- drivers/pci/iov.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 2 + include/linux/pci.h | 19 +++- 3 files changed, 334 insertions(+), 1 deletions(-)
drivers/pci/iov.c +314 -0
@@ -13,6 +13,7 @@#include <linux/delay.h> #include "pci.h" +#define VIRTFN_ID_LEN 16static inline u8 virtfn_bus (struct pci_dev *dev,int id) {
@@ -26,6 +27,284 @@ static inline u8 virtfn_devfn(struct pci_dev *dev, int id) dev->sriov->stride * id) & 0xff; } +static struct pci_bus *virtfn_add_bus (struct pci_bus *bus,int busnr) +{ +int rc; +struct pci_bus *child; + +if (bus->number == busnr) +return bus; + + child =pci_find_bus (pci_domain_nr (bus), busnr); +if (child) +return child; + + child =pci_add_new_bus (bus,NULL , busnr); +if (!child) +return NULL ; + + child->subordinate = busnr; + child->dev.parent = bus->bridge; + rc =pci_bus_add_child (child); +if (rc) { +pci_remove_bus (child); +return NULL ; + } + +return child; +} + +static void virtfn_remove_bus (struct pci_bus *bus,int busnr) +{ +struct pci_bus *child; + +if (bus->number == busnr) +return ; + + child =pci_find_bus (pci_domain_nr (bus), busnr); +BUG_ON (!child); + +if (list_empty (&child->devices)) +pci_remove_bus (child); +} + +static int virtfn_add (struct pci_dev *dev,int id,int reset) +{ +int i; +int rc; +u64 size; +char buf[VIRTFN_ID_LEN ]; +struct pci_dev *virtfn; +struct resource *res; +struct pci_sriov *iov = dev->sriov; + + virtfn =alloc_pci_dev (); +if (!virtfn) +return -ENOMEM ; + +mutex_lock (&iov->dev->sriov->lock); + virtfn->bus =virtfn_add_bus (dev->bus,virtfn_bus (dev, id)); +if (!virtfn->bus) { +kfree (virtfn); +mutex_unlock (&iov->dev->sriov->lock); +return -ENOMEM ; + } + virtfn->devfn =virtfn_devfn (dev, id); + virtfn->vendor = dev->vendor; +pci_read_config_word (dev, iov->pos +PCI_SRIOV_VF_DID , &virtfn->device); +pci_setup_device (virtfn); + virtfn->dev.parent = dev->dev.parent; + +for (i =0 ; i <PCI_SRIOV_NUM_BARS ; i++) { + res = dev->resource +PCI_IOV_RESOURCES + i; +if (!res->parent) +continue ; + virtfn->resource[i].name =pci_name (virtfn); + virtfn->resource[i].flags = res->flags; + size =resource_size (res); +do_div (size, iov->total); + 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]); +BUG_ON (rc); + } + +if (reset) +pci_execute_reset_function (virtfn); + +pci_device_add (virtfn, virtfn->bus); +mutex_unlock (&iov->dev->sriov->lock); + + virtfn->physfn =pci_dev_get (dev); + virtfn->is_virtfn =1 ; + + rc =pci_bus_add_device (virtfn); +if (rc) + goto failed1; +sprintf (buf,"virtfn%u" , id); + rc =sysfs_create_link (&dev->dev.kobj, &virtfn->dev.kobj, buf); +if (rc) + goto failed1; + rc =sysfs_create_link (&virtfn->dev.kobj, &dev->dev.kobj,"physfn" ); +if (rc) + goto failed2; + +kobject_uevent (&virtfn->dev.kobj,KOBJ_CHANGE ); + +return 0 ; + +failed2: +sysfs_remove_link (&dev->dev.kobj, buf); +failed1: +pci_dev_put (dev); +mutex_lock (&iov->dev->sriov->lock); +pci_remove_bus_device (virtfn); +virtfn_remove_bus (dev->bus,virtfn_bus (dev, id)); +mutex_unlock (&iov->dev->sriov->lock); + +return rc; +} + +static void virtfn_remove (struct pci_dev *dev,int id,int reset) +{ +char buf[VIRTFN_ID_LEN ]; +struct pci_bus *bus; +struct pci_dev *virtfn; +struct pci_sriov *iov = dev->sriov; + + bus =pci_find_bus (pci_domain_nr (dev->bus),virtfn_bus (dev, id)); +if (!bus) +return ; + + virtfn =pci_get_slot (bus,virtfn_devfn (dev, id)); +if (!virtfn) +return ; + +pci_dev_put (virtfn); + +if (reset) { +device_release_driver (&virtfn->dev); +pci_execute_reset_function (virtfn); + } + +sprintf (buf,"virtfn%u" , id); +sysfs_remove_link (&dev->dev.kobj, buf); +sysfs_remove_link (&virtfn->dev.kobj,"physfn" ); + +mutex_lock (&iov->dev->sriov->lock); +pci_remove_bus_device (virtfn); +virtfn_remove_bus (dev->bus,virtfn_bus (dev, id)); +mutex_unlock (&iov->dev->sriov->lock); + +pci_dev_put (dev); +} + +static int sriov_enable (struct pci_dev *dev,int nr_virtfn) +{ +int rc; +int i, j; +int nres; +u16 offset, stride, initial; +struct resource *res; +struct pci_dev *pdev; +struct pci_sriov *iov = dev->sriov; + +if (!nr_virtfn) +return 0 ; + +if (iov->nr_virtfn) +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))) +return -EIO ; + +if (nr_virtfn <0 || nr_virtfn > iov->total || + (!(iov->cap &PCI_SRIOV_CAP_VFM ) && (nr_virtfn > initial))) +return -EINVAL ; + +pci_write_config_word (dev, iov->pos +PCI_SRIOV_NUM_VF , nr_virtfn); +pci_read_config_word (dev, iov->pos +PCI_SRIOV_VF_OFFSET , &offset); +pci_read_config_word (dev, iov->pos +PCI_SRIOV_VF_STRIDE , &stride); +if (!offset || (nr_virtfn >1 && !stride)) +return -EIO ; + + nres =0 ; +for (i =0 ; i <PCI_SRIOV_NUM_BARS ; i++) { + res = dev->resource +PCI_IOV_RESOURCES + i; +if (res->parent) + nres++; + } +if (nres != iov->nres) { +dev_err (&dev->dev,"not enough MMIO resources for SR-IOV\n" ); +return -ENOMEM ; + } + + iov->offset = offset; + iov->stride = stride; + +if (virtfn_bus (dev, nr_virtfn -1 ) > dev->bus->subordinate) { +dev_err (&dev->dev,"SR-IOV: bus number out of range\n" ); +return -ENOMEM ; + } + +if (iov->link != dev->devfn) { + pdev =pci_get_slot (dev->bus, iov->link); +if (!pdev) +return -ENODEV ; + +pci_dev_put (pdev); + +if (!pdev->is_physfn) +return -ENODEV ; + + rc =sysfs_create_link (&dev->dev.kobj, + &pdev->dev.kobj,"dep_link" ); +if (rc) +return rc; + } + + iov->ctrl |=PCI_SRIOV_CTRL_VFE |PCI_SRIOV_CTRL_MSE ; +pci_block_user_cfg_access (dev); +pci_write_config_word (dev, iov->pos +PCI_SRIOV_CTRL , iov->ctrl); +msleep (100 ); +pci_unblock_user_cfg_access (dev); + + iov->initial = initial; +if (nr_virtfn < initial) + initial = nr_virtfn; + +for (i =0 ; i < initial; i++) { + rc =virtfn_add (dev, i,0 ); +if (rc) + goto failed; + } + +kobject_uevent (&dev->dev.kobj,KOBJ_CHANGE ); + iov->nr_virtfn = nr_virtfn; + +return 0 ; + +failed: +for (j =0 ; j < i; j++) +virtfn_remove (dev, j,0 ); + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE |PCI_SRIOV_CTRL_MSE ); +pci_block_user_cfg_access (dev); +pci_write_config_word (dev, iov->pos +PCI_SRIOV_CTRL , iov->ctrl); +ssleep (1 ); +pci_unblock_user_cfg_access (dev); + +if (iov->link != dev->devfn) +sysfs_remove_link (&dev->dev.kobj,"dep_link" ); + +return rc; +} + +static void sriov_disable (struct pci_dev *dev) +{ +int i; +struct pci_sriov *iov = dev->sriov; + +if (!iov->nr_virtfn) +return ; + +for (i =0 ; i < iov->nr_virtfn; i++) +virtfn_remove (dev, i,0 ); + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE |PCI_SRIOV_CTRL_MSE ); +pci_block_user_cfg_access (dev); +pci_write_config_word (dev, iov->pos +PCI_SRIOV_CTRL , iov->ctrl); +ssleep (1 ); +pci_unblock_user_cfg_access (dev); + +if (iov->link != dev->devfn) +sysfs_remove_link (&dev->dev.kobj,"dep_link" ); + + iov->nr_virtfn =0 ; +} +static int sriov_init (struct pci_dev *dev,int pos) {int i;
@@ -132,6 +411,8 @@ failed:static void sriov_release (struct pci_dev *dev) { +BUG_ON (dev->sriov->nr_virtfn); +if (dev == dev->sriov->dev)mutex_destroy (&dev->sriov->lock);else
@@ -155,6 +436,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_CTRL , iov->ctrl);if (iov->ctrl &PCI_SRIOV_CTRL_VFE )msleep (100 );
@@ -245,3 +527,35 @@ int pci_iov_bus_range(struct pci_bus *bus)return max ? max - bus->number :0 ; } + +/** +* pci_enable_sriov - enable the SR-IOV capability +* @dev: the PCI device +* +* Returns 0 on success, or negative on failure. +*/ +int pci_enable_sriov (struct pci_dev *dev,int nr_virtfn) +{ +might_sleep (); + +if (!dev->is_physfn) +return -ENODEV ; + +return sriov_enable (dev, nr_virtfn); +} +EXPORT_SYMBOL_GPL (pci_enable_sriov); + +/** +* pci_disable_sriov - disable the SR-IOV capability +* @dev: the PCI device +*/ +void pci_disable_sriov (struct pci_dev *dev) +{ +might_sleep (); + +if (!dev->is_physfn) +return ; + +sriov_disable (dev); +} +EXPORT_SYMBOL_GPL (pci_disable_sriov);
drivers/pci/pci.h +2 -0
@@ -203,6 +203,8 @@ struct pci_sriov {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 offset;/* first VF Routing ID offset */ u16 stride;/* following VF stride */ u32 pgsz;/* page size for BAR alignment */
include/linux/pci.h +18 -1
@@ -265,6 +265,7 @@ struct pci_dev { unsigned int is_pcie:1; unsigned int state_saved:1; unsigned int is_physfn:1; + unsigned int is_virtfn:1;pci_dev_flags_t dev_flags;atomic_t enable_cnt;/* pci_enable_device has been called */
@@ -279,7 +280,10 @@ struct pci_dev {#endif struct pci_vpd *vpd;#ifdef CONFIG_PCI_IOV -struct pci_sriov *sriov;/* SR-IOV capability related */ +union { +struct pci_sriov *sriov;/* SR-IOV capability related */ +struct pci_dev *physfn;/* the PF this VF is associated with */ + };#endif };
@@ -1205,5 +1209,18 @@ int pci_ext_cfg_avail(struct pci_dev *dev);void __iomem *pci_ioremap_bar (struct pci_dev *pdev,int bar); +#ifdef CONFIG_PCI_IOV +extern int pci_enable_sriov (struct pci_dev *dev,int nr_virtfn); +extern void pci_disable_sriov (struct pci_dev *dev); +#else +static inline int pci_enable_sriov (struct pci_dev *dev,int nr_virtfn) +{ +return -ENODEV ; +} +static inline void pci_disable_sriov (struct pci_dev *dev) +{ +} +#endif +#endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */
Signature · 1 line
1.5.6.4
PATCH v12 6/8 PCI: handle SR-IOV Virtual Function Migration
In reply to Yu ZhaoAdd or remove a Virtual Function after receiving a Migrate In or Out Request.
--- drivers/pci/iov.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 4 ++ include/linux/pci.h | 6 +++ 3 files changed, 129 insertions(+), 0 deletions(-)
drivers/pci/iov.c +119 -0
@@ -179,6 +179,97 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)pci_dev_put (dev); } +static int sriov_migration (struct pci_dev *dev) +{ +u16 status; +struct pci_sriov *iov = dev->sriov; + +if (!iov->nr_virtfn) +return 0 ; + +if (!(iov->cap &PCI_SRIOV_CAP_VFM )) +return 0 ; + +pci_read_config_word (dev, iov->pos +PCI_SRIOV_STATUS , &status); +if (!(status &PCI_SRIOV_STATUS_VFM )) +return 0 ; + +schedule_work (&iov->mtask); + +return 1 ; +} + +static void sriov_migration_task (struct work_struct *work) +{ +int i; +u8 state; +u16 status; + struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); + +for (i = iov->initial; i < iov->nr_virtfn; i++) { + state =readb (iov->mstate + i); +if (state ==PCI_SRIOV_VFM_MI ) { +writeb (PCI_SRIOV_VFM_AV , iov->mstate + i); + state =readb (iov->mstate + i); +if (state ==PCI_SRIOV_VFM_AV ) +virtfn_add (iov->self, i,1 ); + }else if (state ==PCI_SRIOV_VFM_MO ) { +virtfn_remove (iov->self, i,1 ); +writeb (PCI_SRIOV_VFM_UA , iov->mstate + i); + state =readb (iov->mstate + i); +if (state ==PCI_SRIOV_VFM_AV ) +virtfn_add (iov->self, i,0 ); + } + } + +pci_read_config_word (iov->self, iov->pos +PCI_SRIOV_STATUS , &status); + status &= ~PCI_SRIOV_STATUS_VFM ; +pci_write_config_word (iov->self, iov->pos +PCI_SRIOV_STATUS , status); +} + +static int sriov_enable_migration (struct pci_dev *dev,int nr_virtfn) +{ +int bir; +u32 table; +resource_size_t pa; +struct pci_sriov *iov = dev->sriov; + +if (nr_virtfn <= iov->initial) +return 0 ; + +pci_read_config_dword (dev, iov->pos +PCI_SRIOV_VFM , &table); + bir =PCI_SRIOV_VFM_BIR (table); +if (bir >PCI_STD_RESOURCE_END ) +return -EIO ; + + table =PCI_SRIOV_VFM_OFFSET (table); +if (table + nr_virtfn >pci_resource_len (dev, bir)) +return -EIO ; + + pa =pci_resource_start (dev, bir) + table; + iov->mstate =ioremap (pa, nr_virtfn); +if (!iov->mstate) +return -ENOMEM ; + +INIT_WORK (&iov->mtask, sriov_migration_task); + + iov->ctrl |=PCI_SRIOV_CTRL_VFM |PCI_SRIOV_CTRL_INTR ; +pci_write_config_word (dev, iov->pos +PCI_SRIOV_CTRL , iov->ctrl); + +return 0 ; +} + +static void sriov_disable_migration (struct pci_dev *dev) +{ +struct pci_sriov *iov = dev->sriov; + + iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM |PCI_SRIOV_CTRL_INTR ); +pci_write_config_word (dev, iov->pos +PCI_SRIOV_CTRL , iov->ctrl); + +cancel_work_sync (&iov->mtask); +iounmap (iov->mstate); +} +static int sriov_enable (struct pci_dev *dev,int nr_virtfn) {int rc;
@@ -261,6 +352,12 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto failed; } +if (iov->cap &PCI_SRIOV_CAP_VFM ) { + rc =sriov_enable_migration (dev, nr_virtfn); +if (rc) + goto failed; + } +kobject_uevent (&dev->dev.kobj,KOBJ_CHANGE ); iov->nr_virtfn = nr_virtfn;
@@ -290,6 +387,9 @@ static void sriov_disable(struct pci_dev *dev)if (!iov->nr_virtfn)return ; +if (iov->cap &PCI_SRIOV_CAP_VFM ) +sriov_disable_migration (dev); +for (i =0 ; i < iov->nr_virtfn; i++)virtfn_remove (dev, i,0 );
@@ -559,3 +659,22 @@ void pci_disable_sriov(struct pci_dev *dev)sriov_disable (dev); }EXPORT_SYMBOL_GPL (pci_disable_sriov); + +/** +* pci_sriov_migration - notify SR-IOV core of Virtual Function Migration +* @dev: the PCI device +* +* Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. +* +* Physical Function driver is responsible to register IRQ handler using +* VF Migration Interrupt Message Number, and call this function when the +* interrupt is generated by the hardware. +*/ +irqreturn_t pci_sriov_migration (struct pci_dev *dev) +{ +if (!dev->is_physfn) +return IRQ_NONE ; + +return sriov_migration (dev) ?IRQ_HANDLED :IRQ_NONE ; +} +EXPORT_SYMBOL_GPL (pci_sriov_migration);
drivers/pci/pci.h +4 -0
@@ -1,6 +1,8 @@#ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H +#include <linux/workqueue.h> +#define PCI_CFG_SPACE_SIZE 256#define PCI_CFG_SPACE_EXP_SIZE 4096
@@ -212,6 +214,8 @@ struct pci_sriov {struct pci_dev *dev;/* lowest numbered PF */ struct pci_dev *self;/* this PF */ struct mutex lock;/* lock for VF bus */ +struct work_struct mtask;/* VF Migration task */ +u8 __iomem *mstate;/* VF Migration State Array */ };#ifdef CONFIG_PCI_IOV
include/linux/pci.h +6 -0
@@ -52,6 +52,7 @@#include <asm/atomic.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/irqreturn.h> /* Include the ID list */ #include <linux/pci_ids.h>
@@ -1212,6 +1213,7 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar);#ifdef CONFIG_PCI_IOV 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);#else static inline int pci_enable_sriov (struct pci_dev *dev,int nr_virtfn) {
@@ -1220,6 +1222,10 @@ static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)static inline void pci_disable_sriov (struct pci_dev *dev) { } +static inline irqreturn_t pci_sriov_migration (struct pci_dev *dev) +{ +return IRQ_NONE ; +}#endif #endif /* __KERNEL__ */
Signature · 1 line
1.5.6.4
PATCH v12 7/8 PCI: document SR-IOV sysfs entries
In reply to Yu Zhao--- Documentation/ABI/testing/sysfs-bus-pci | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-)
Documentation/ABI/testing/sysfs-bus-pci +27 -0
@@ -52,3 +52,30 @@ Description: that some devices may have malformatted data. If the underlying VPD has a writable section then the corresponding section of this file will be writable. + +What: /sys/bus/pci/devices/.../virtfnN +Date: March 2009 +Contact: Yu Zhao <[email protected]> +Description: + This symbolic link appears when hardware supports the SR-IOV + capability and the Physical Function driver has enabled it. + The symbolic link points to the PCI device sysfs entry of the + Virtual Function whose index is N (0...MaxVFs-1). + +What: /sys/bus/pci/devices/.../dep_link +Date: March 2009 +Contact: Yu Zhao <[email protected]> +Description: + This symbolic link appears when hardware supports the SR-IOV + capability and the Physical Function driver has enabled it, + and this device has vendor specific dependencies with others. + The symbolic link points to the PCI device sysfs entry of + Physical Function this device depends on. + +What: /sys/bus/pci/devices/.../physfn +Date: March 2009 +Contact: Yu Zhao <[email protected]> +Description: + This symbolic link appears when a device is a Virtual Function. + The symbolic link points to the PCI device sysfs entry of the + Physical Function this device associates with.
Signature · 1 line
1.5.6.4
RE: [PATCH v12 7/8] PCI: document SR-IOV sysfs entries
Documentation/ABI/testing/sysfs-bus-pci
Quoted 13 lines · [email protected] wrote:
Reviewed-by Randy Dunlap <[email protected]>Reviewed-by Matthew Wilcox <[email protected]>Signed-off-by Yu Zhao <[email protected]>--- Documentation/ABI/testing/sysfs-bus-pci | 27 +++++++++++++++++++++++++++ 1 files changed, 27 insertions(+), 0 deletions(-)Documentation/ABI/testing/sysfs-bus-pci +0
-0
A very handy feature would be the ability to send Resets(BME etc) via a sysfs-entry to all the VF's attached to a PF. This way one can test the drivers/firmware really easily.
BME
|
|->PF
|->VF0
|->VF1
..
|->VFnThanks
Chetan
PATCH v12 8/8 PCI: manual for SR-IOV user and driver developer
In reply to Yu Zhao--- Documentation/DocBook/kernel-api.tmpl | 1 + Documentation/PCI/pci-iov-howto.txt | 99 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 0 deletions(-) create mode 100644 Documentation/PCI/pci-iov-howto.txt
Documentation/DocBook/kernel-api.tmpl +1 -0
@@ -199,6 +199,7 @@ X!Edrivers/pci/hotplug.c --> !Edrivers/pci/probe.c !Edrivers/pci/rom.c +!Edrivers/pci/iov.c </sect1> <sect1><title>PCI Hotplug Support Library</title> !Edrivers/pci/hotplug/pci_hotplug_core.c
Documentation/PCI/pci-iov-howto.txt new file +99 -0
99 lines not on this page · raw message
Signature · 1 line
1.5.6.4