PCI: Allow per function PCI slots
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH v9 1/9 PCI: Allow per function PCI slots
On s390 systems, which use a machine level hypervisor, PCI devices are always accessed through a form of PCI pass-through which fundamentally operates on a per PCI function granularity. This is also reflected in the s390 PCI hotplug driver which creates hotplug slots for individual PCI functions. Its reset_slot() function, which is a wrapper for zpci_hot_reset_device(), thus also resets individual functions.
Currently, the kernel's PCI_SLOT() macro assigns the same pci_slot object to multifunction devices. This approach worked fine on s390 systems that only exposed virtual functions as individual PCI domains to the operating system. Since commit 44510d6fa0c0 ("s390/pci: Handling multifunctions") s390 supports exposing the topology of multifunction PCI devices by grouping them in a shared PCI domain. When attempting to reset a function through the hotplug driver, the shared slot assignment causes the wrong function to be reset instead of the intended one. It also leaks memory as we do create a pci_slot object for the function, but don't correctly free it in pci_slot_release().
Add a flag for struct pci_slot to allow per function PCI slots for functions managed through a hypervisor, which exposes individual PCI functions while retaining the topology.
--- drivers/pci/pci.c | 5 +++-- drivers/pci/slot.c | 25 ++++++++++++++++++++++--- include/linux/pci.h | 1 + 3 files changed, 26 insertions(+), 5 deletions(-)
drivers/pci/pci.c +3 -2
@@ -4869,8 +4869,9 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, bool probe)static int pci_dev_reset_slot_function (struct pci_dev *dev,bool probe) { -if (dev->multifunction || dev->subordinate || !dev->slot || - dev->dev_flags &PCI_DEV_FLAGS_NO_BUS_RESET ) +if (dev->subordinate || !dev->slot || + dev->dev_flags &PCI_DEV_FLAGS_NO_BUS_RESET || + (dev->multifunction && !dev->slot->per_func_slot))return -ENOTTY ;return pci_reset_hotplug_slot (dev->slot->hotplug, probe);
drivers/pci/slot.c +22 -3
@@ -63,6 +63,22 @@ static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf)return bus_speed_read (slot->bus->cur_bus_speed, buf); } +static bool pci_dev_matches_slot (struct pci_dev *dev,struct pci_slot *slot) +{ +if (slot->per_func_slot) +return dev->devfn == slot->number; + +return PCI_SLOT (dev->devfn) == slot->number; +} + +static bool pci_slot_enabled_per_func (void ) +{ +if (IS_ENABLED (CONFIG_S390 )) +return true; + +return false; +} +static void pci_slot_release (struct kobject *kobj) {struct pci_dev *dev;
@@ -73,7 +89,7 @@ static void pci_slot_release(struct kobject *kobj)down_read (&pci_bus_sem);list_for_each_entry (dev, &slot->bus->devices, bus_list) -if (PCI_SLOT (dev->devfn) == slot->number) +if (pci_dev_matches_slot (dev, slot)) dev->slot =NULL ;up_read (&pci_bus_sem);
@@ -166,7 +182,7 @@ void pci_dev_assign_slot(struct pci_dev *dev)mutex_lock (&pci_slot_mutex);list_for_each_entry (slot, &dev->bus->slots, list) -if (PCI_SLOT (dev->devfn) == slot->number) +if (pci_dev_matches_slot (dev, slot)) dev->slot = slot;mutex_unlock (&pci_slot_mutex); }
@@ -265,6 +281,9 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, slot->bus =pci_bus_get (parent); slot->number = slot_nr; +if (pci_slot_enabled_per_func ()) + slot->per_func_slot =1 ; + slot->kobj.kset = pci_slots_kset; slot_name =make_slot_name (name);
@@ -285,7 +304,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,down_read (&pci_bus_sem);list_for_each_entry (dev, &parent->devices, bus_list) -if (PCI_SLOT (dev->devfn) == slot_nr) +if (pci_dev_matches_slot (dev, slot)) dev->slot = slot;up_read (&pci_bus_sem);
include/linux/pci.h +1 -0
@@ -78,6 +78,7 @@ struct pci_slot {struct list_head list;/* Node in list of slots */ struct hotplug_slot *hotplug;/* Hotplug info (move here) */ unsigned char number;/* PCI_SLOT(pci_dev->devfn) */ + unsigned int per_func_slot:1; /* Allow per function slot */struct kobject kobj; };
Signature · 1 line
2.43.0
PATCH v9 4/9 PCI: Add additional checks for flr reset
In reply to a message not in the archiveIf a device is in an error state, then any reads of device registers can return error value. Add addtional checks to validate if a device is in an error state before doing an flr reset.
--- drivers/pci/pci.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/pci/pci.c +7 -0
@@ -4373,12 +4373,19 @@ EXPORT_SYMBOL_GPL(pcie_flr);*/ int pcie_reset_flr (struct pci_dev *dev,bool probe) { +u32 reg; +if (dev->dev_flags &PCI_DEV_FLAGS_NO_FLR_RESET )return -ENOTTY ;if (!(dev->devcap &PCI_EXP_DEVCAP_FLR ))return -ENOTTY ; +if (pcie_capability_read_dword (dev,PCI_EXP_DEVCAP , ®)) { +pci_warn (dev,"Device unable to do an FLR\n" ); +return -ENOTTY ; + } +if (probe)return 0 ;
Signature · 1 line
2.43.0