From: Paolo Bonzini <pbonzini@redhat.com> Date: Mon, 9 Aug 2010 19:23:51 -0400 Subject: [virt] xen: fix passthrough of SR-IOV VF Message-id: <1281381831-15207-1-git-send-email-pbonzini@redhat.com> Patchwork-id: 27480 O-Subject: [RHEL5.5 PATCH] xen: fix passthrough of SR-IOV VF Bugzilla: 582886 RH-Acked-by: Andrew Jones <drjones@redhat.com> Bugzilla: 582886 Upstream status: backport of changesets 103 + 998 + 999 + 1003 Brew build: https://brewweb.devel.redhat.com/taskinfo?taskID=2651766 This patch virtualizes vendor, device and BAR fields in the PCI config space, because they're dummy for a VF's host-level PCI config space. pciback can do so by always extracting the values installed in dom0 kernel's own PCI structures, rather than interrogating the underlying PCI config space directly. It's a backport of upstream changesets 103, 998, 999, and 1003. It is considerably easier to review all of them at the same time. The patch has already been tested by QE. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> diff --git a/drivers/xen/pciback/conf_space_header.c b/drivers/xen/pciback/conf_space_header.c index 61089c6..a417a19 100644 --- a/drivers/xen/pciback/conf_space_header.c +++ b/drivers/xen/pciback/conf_space_header.c @@ -18,6 +18,25 @@ struct pci_bar_info { #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) +static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) +{ + int i; + int ret; + + ret = pciback_read_config_word(dev, offset, value, data); + if (!dev->is_enabled) + return ret; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + if (dev->resource[i].flags & IORESOURCE_IO) + *value |= PCI_COMMAND_IO; + if (dev->resource[i].flags & IORESOURCE_MEM) + *value |= PCI_COMMAND_MEMORY; + } + + return ret; +} + static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) { if (!dev->is_enabled && is_enable_cmd(value)) { @@ -65,8 +84,15 @@ static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data) */ if (value == ~PCI_ROM_ADDRESS_ENABLE) bar->which = 1; - else + else { + u32 tmpval; + pci_read_config_dword(dev, offset, &tmpval); + if (tmpval != bar->val && value == bar->val) { + /* Allow restoration of bar value. */ + pci_write_config_dword(dev, offset, bar->val); + } bar->which = 0; + } /* Do we need to support enabling/disabling the rom address here? */ @@ -92,8 +118,15 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) */ if (value == ~0) bar->which = 1; - else + else { + u32 tmpval; + pci_read_config_dword(dev, offset, &tmpval); + if (tmpval != bar->val && value == bar->val) { + /* Allow restoration of bar value. */ + pci_write_config_dword(dev, offset, bar->val); + } bar->which = 0; + } return 0; } @@ -117,10 +150,26 @@ static inline void read_dev_bar(struct pci_dev *dev, struct pci_bar_info *bar_info, int offset, u32 len_mask) { - pci_read_config_dword(dev, offset, &bar_info->val); - pci_write_config_dword(dev, offset, len_mask); - pci_read_config_dword(dev, offset, &bar_info->len_val); - pci_write_config_dword(dev, offset, bar_info->val); + int pos; + struct resource *res = dev->resource; + + if (offset == PCI_ROM_ADDRESS || offset == PCI_ROM_ADDRESS1) + pos = PCI_ROM_RESOURCE; + else { + pos = (offset - PCI_BASE_ADDRESS_0) / 4; + if (pos && ((res[pos - 1].flags & (PCI_BASE_ADDRESS_SPACE | + PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == + (PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64))) { + bar_info->val = res[pos - 1].start >> 32; + bar_info->len_val = res[pos - 1].end >> 32; + return; + } + } + + bar_info->val = res[pos].start | + (res[pos].flags & PCI_REGION_FLAG_MASK); + bar_info->len_val = res[pos].end - res[pos].start + 1; } static void *bar_init(struct pci_dev *dev, int offset) @@ -161,6 +210,22 @@ static void bar_release(struct pci_dev *dev, int offset, void *data) kfree(data); } +static int pciback_read_vendor(struct pci_dev *dev, int offset, + u16 *value, void *data) +{ + *value = dev->vendor; + + return 0; +} + +static int pciback_read_device(struct pci_dev *dev, int offset, + u16 *value, void *data) +{ + *value = dev->device; + + return 0; +} + static int interrupt_read(struct pci_dev *dev, int offset, u8 * value, void *data) { @@ -188,9 +253,19 @@ static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data) static struct config_field header_common[] = { { + .offset = PCI_VENDOR_ID, + .size = 2, + .u.w.read = pciback_read_vendor, + }, + { + .offset = PCI_DEVICE_ID, + .size = 2, + .u.w.read = pciback_read_device, + }, + { .offset = PCI_COMMAND, .size = 2, - .u.w.read = pciback_read_config_word, + .u.w.read = command_read, .u.w.write = command_write, }, {