From ae43799529c480510dcc14bd0ed141a1d85b9259 Mon Sep 17 00:00:00 2001 From: Logan Odell Date: Thu, 10 Sep 2026 10:36:48 -0700 Subject: [PATCH] PCI/IOV: Read VF config values that need not match PF Per PCIe r7.0, sec 7.5.1.1.5 and 7.5.1.2.3, VF Revision IDs and Subsystem IDs may differ from the PF's. Read these values from VF config space instead of using cached values from VF 0. Per 7.5.1.1.6 and 7.5.1.2.3, VF Class Codes and Subsystem Vendor IDs must be the same as the PF's. Use the PF's value instead of reading from VF config space. Per 7.5.1.1.9, the Header Type for a VF must be 0, so hardcode that assignment. Fixes: cf0921bea66c ("PCI/IOV: Use VF0 cached config registers for other VFs") Signed-off-by: Logan Odell [bhelgaas: commit log, move "rev" decl inside block that uses it] Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260910173648.1304517-1-loganodell@google.com --- drivers/pci/iov.c | 25 ------------------------- drivers/pci/pci.h | 4 ---- drivers/pci/probe.c | 15 ++++++++++----- 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9d408fb8ac25..9185934a900f 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -179,29 +179,6 @@ bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev) return cmd & PCI_SRIOV_CTRL_MSE; } -static void pci_read_vf_config_common(struct pci_dev *virtfn) -{ - struct pci_dev *physfn = virtfn->physfn; - - /* - * Some config registers are the same across all associated VFs. - * Read them once from VF0 so we can skip reading them from the - * other VFs. - * - * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to - * have the same Revision ID and Subsystem ID, but we assume they - * do. - */ - pci_read_config_dword(virtfn, PCI_CLASS_REVISION, - &physfn->sriov->class); - pci_read_config_byte(virtfn, PCI_HEADER_TYPE, - &physfn->sriov->hdr_type); - pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID, - &physfn->sriov->subsystem_vendor); - pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID, - &physfn->sriov->subsystem_device); -} - int pci_iov_sysfs_link(struct pci_dev *dev, struct pci_dev *virtfn, int id) { @@ -329,8 +306,6 @@ static struct pci_dev *pci_iov_scan_device(struct pci_dev *dev, int id, virtfn->physfn = pci_dev_get(dev); virtfn->no_command_memory = 1; - if (id == 0) - pci_read_vf_config_common(virtfn); rc = pci_setup_device(virtfn); if (rc) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index ba3c3fddddc2..5b3dd1adbf35 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -711,10 +711,6 @@ struct pci_sriov { u16 driver_max_VFs; /* Max num VFs driver supports */ struct pci_dev *dev; /* Lowest numbered PF */ struct pci_dev *self; /* This PF */ - u32 class; /* VF device */ - u8 hdr_type; /* VF header type */ - u16 subsystem_vendor; /* VF subsystem vendor */ - u16 subsystem_device; /* VF subsystem device */ resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ u16 vf_rebar_cap; /* VF Resizable BAR capability offset */ bool drivers_autoprobe; /* Auto probing of VFs by driver */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 27008e2ea5af..721daf5c5184 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1890,8 +1890,13 @@ static u32 pci_class(struct pci_dev *dev) u32 class; #ifdef CONFIG_PCI_IOV - if (dev->is_virtfn) - return dev->physfn->sriov->class; + if (dev->is_virtfn) { + u8 rev; + + if (pci_read_config_byte(dev, PCI_REVISION_ID, &rev)) + rev = 0; + return (dev->physfn->class << 8) | rev; + } #endif pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); return class; @@ -1901,8 +1906,8 @@ static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device) { #ifdef CONFIG_PCI_IOV if (dev->is_virtfn) { - *vendor = dev->physfn->sriov->subsystem_vendor; - *device = dev->physfn->sriov->subsystem_device; + *vendor = dev->physfn->subsystem_vendor; + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device); return; } #endif @@ -1916,7 +1921,7 @@ static u8 pci_hdr_type(struct pci_dev *dev) #ifdef CONFIG_PCI_IOV if (dev->is_virtfn) - return dev->physfn->sriov->hdr_type; + return 0; #endif pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type); return hdr_type;