Merge branch 'slot' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci into v7.3/vfio/s390x-pci-error-recovery

PCI dependencies from shared branch supporting vfio-pci error recovery
on s390x.

Signed-off-by: Alex Williamson <alex@shazbot.org>
This commit is contained in:
Alex Williamson
2026-08-13 10:41:05 -06:00
6 changed files with 83 additions and 22 deletions

View File

@@ -808,7 +808,7 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
if (dn->child && PCI_DN(dn->child))
php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn);
else
php_slot->slot_no = -1; /* Placeholder slot */
php_slot->slot_no = PCI_SLOT_PLACEHOLDER; /* Placeholder slot */
kref_init(&php_slot->kref);
php_slot->state = PNV_PHP_STATE_INITIALIZED;

View File

@@ -84,7 +84,7 @@ int rpaphp_register_slot(struct slot *slot)
struct hotplug_slot *php_slot = &slot->hotplug_slot;
u32 my_index;
int retval;
int slotno = -1;
int slotno = PCI_SLOT_PLACEHOLDER;
dbg("%s registering slot:path[%pOF] index[%x], name[%s] pdomain[%x] type[%d]\n",
__func__, slot->dn, slot->index, slot->name,

View File

@@ -870,6 +870,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
{
struct msi_desc *entry;
bool write_msg;
u16 cmd;
if (!dev->msix_enabled)
return;
@@ -879,6 +880,14 @@ void __pci_restore_msix_state(struct pci_dev *dev)
pci_msix_clear_and_set_ctrl(dev, 0,
PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
/*
* The restored device state may not have Memory Space enabled.
* Since the MSI-X Table and PBA are in Memory Space, enable it
* while restoring them.
*/
pci_read_config_word(dev, PCI_COMMAND, &cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
write_msg = arch_restore_msi_irqs(dev);
scoped_guard (msi_descs_lock, &dev->dev) {
@@ -889,6 +898,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
}
}
pci_write_config_word(dev, PCI_COMMAND, cmd);
pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
}

View File

@@ -713,6 +713,28 @@ u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec)
}
EXPORT_SYMBOL_GPL(pci_find_dvsec_capability);
static bool pci_dev_config_accessible(struct pci_dev *dev, char *msg)
{
u32 val;
/*
* If a device's config space is inaccessible, reads typically
* return ~0. Since Device and Vendor ID are always ~0 for VFs,
* check the Command and Status registers instead.
*
* N.B. This is racy because the device may become inaccessible
* before the next access.
*/
pci_read_config_dword(dev, PCI_COMMAND, &val);
if (PCI_POSSIBLE_ERROR(val)) {
pci_warn(dev, "Device config space inaccessible; unable to %s\n",
msg);
return false;
}
return true;
}
/**
* pci_find_parent_resource - return resource region of parent bus of given
* region
@@ -4363,6 +4385,9 @@ int pcie_flr(struct pci_dev *dev)
{
int ret;
if (!pci_dev_config_accessible(dev, "FLR"))
return -ENOTTY;
if (!pci_wait_for_pending_transaction(dev))
pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n");
@@ -4897,8 +4922,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);
@@ -5058,6 +5084,9 @@ static void pci_dev_save_and_disable(struct pci_dev *dev)
*/
pci_set_power_state(dev, PCI_D0);
if (!pci_dev_config_accessible(dev, "save state"))
return;
pci_save_state(dev);
/*
* Disable the device by clearing the Command register, except for

View File

@@ -37,7 +37,7 @@ static const struct sysfs_ops pci_slot_sysfs_ops = {
static ssize_t address_read_file(struct pci_slot *slot, char *buf)
{
if (slot->number == 0xff)
if (slot->number == PCI_SLOT_PLACEHOLDER)
return sysfs_emit(buf, "%04x:%02x\n",
pci_domain_nr(slot->bus),
slot->bus->number);
@@ -72,6 +72,23 @@ 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 slot->number == PCI_SLOT_ALL_DEVICES ||
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;
@@ -82,8 +99,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 (slot->number == PCI_SLOT_ALL_DEVICES ||
PCI_SLOT(dev->devfn) == slot->number)
if (pci_dev_matches_slot(dev, slot))
dev->slot = NULL;
up_read(&pci_bus_sem);
@@ -187,8 +203,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 (slot->number == PCI_SLOT_ALL_DEVICES ||
PCI_SLOT(dev->devfn) == slot->number)
if (pci_dev_matches_slot(dev, slot))
dev->slot = slot;
mutex_unlock(&pci_slot_mutex);
}
@@ -210,7 +225,7 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
/**
* pci_create_slot - create or increment refcount for physical PCI slot
* @parent: struct pci_bus of parent bridge
* @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or
* @slot_nr: PCI_SLOT(pci_dev->devfn), PCI_SLOT_PLACEHOLDER for placeholder, or
* PCI_SLOT_ALL_DEVICES
* @name: user visible string presented in /sys/bus/pci/slots/<name>
* @hotplug: set if caller is hotplug driver, NULL otherwise
@@ -236,15 +251,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr)
* In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
* a slot. There is one notable exception - pSeries (rpaphp), where the
* @slot_nr cannot be determined until a device is actually inserted into
* the slot. In this scenario, the caller may pass -1 for @slot_nr.
* the slot. In this scenario, the caller may pass PCI_SLOT_PLACEHOLDER for @slot_nr.
*
* The following semantics are imposed when the caller passes @slot_nr ==
* -1. First, we no longer check for an existing %struct pci_slot, as there
* may be many slots with @slot_nr of -1. The other change in semantics is
* user-visible, which is the 'address' parameter presented in sysfs will
* consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
* %struct pci_bus and bb is the bus number. In other words, the devfn of
* the 'placeholder' slot will not be displayed.
* PCI_SLOT_PLACEHOLDER. First, we no longer check for an existing %struct
* pci_slot, as there may be many slots with @slot_nr of
* PCI_SLOT_PLACEHOLDER. The other change in semantics is user-visible,
* which is the 'address' parameter presented in sysfs will consist solely
* of a dddd:bb tuple, where dddd is the PCI domain of the %struct pci_bus
* and bb is the bus number. In other words, the devfn of the 'placeholder'
* slot will not be displayed.
*
* Bus-wide slots:
* For PCIe hotplug, the physical slot encompasses the entire secondary
@@ -267,7 +283,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
mutex_lock(&pci_slot_mutex);
if (slot_nr == -1)
if (slot_nr == PCI_SLOT_PLACEHOLDER)
goto placeholder;
/*
@@ -298,6 +314,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);
@@ -318,8 +337,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 (slot_nr == PCI_SLOT_ALL_DEVICES ||
PCI_SLOT(dev->devfn) == slot_nr)
if (pci_dev_matches_slot(dev, slot))
dev->slot = slot;
up_read(&pci_bus_sem);

View File

@@ -79,14 +79,18 @@
* and, if ARI Forwarding is enabled, functions may appear to be on multiple
* devices.
*/
#define PCI_SLOT_ALL_DEVICES 0xfe
#define PCI_SLOT_ALL_DEVICES 0xfeff
/* Used to identify a slot as a placeholder */
#define PCI_SLOT_PLACEHOLDER 0xffff
/* pci_slot represents a physical slot */
struct pci_slot {
struct pci_bus *bus; /* Bus this slot is on */
struct list_head list; /* Node in list of slots */
struct hotplug_slot *hotplug; /* Hotplug info (move here) */
unsigned char number; /* Device nr, or PCI_SLOT_ALL_DEVICES */
u16 number; /* Device nr, or PCI_SLOT_ALL_DEVICES */
unsigned int per_func_slot:1; /* Allow per function slot */
struct kobject kobj;
};