Commit dc525311 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/enumeration'

- Split out ARI "next function" handling from the traditional one (Niklas
  Schnelle)

- Move jailhouse "isolated function" (non-zero functions where function 0
  doesn't exist) handling to pci_scan_slot() to avoid duplicating
  multi-function scanning in pci_scan_child_bus_extend() (Niklas Schnelle)

- Extend "isolated function" probing to s390 (Niklas Schnelle).

- Allow s390 zPCI zbus without a function 0 (Niklas Schnelle)

* pci/enumeration:
  s390/pci: allow zPCI zbus without a function zero
  PCI: Extend isolated function probing to s390
  PCI: Move jailhouse's isolated function handling to pci_scan_slot()
  PCI: Split out next_ari_fn() from next_fn()
  PCI: Clean up pci_scan_slot()
parents 49210431 960ac362
...@@ -145,9 +145,6 @@ int zpci_bus_scan_bus(struct zpci_bus *zbus) ...@@ -145,9 +145,6 @@ int zpci_bus_scan_bus(struct zpci_bus *zbus)
struct zpci_dev *zdev; struct zpci_dev *zdev;
int devfn, rc, ret = 0; int devfn, rc, ret = 0;
if (!zbus->function[0])
return 0;
for (devfn = 0; devfn < ZPCI_FUNCTIONS_PER_BUS; devfn++) { for (devfn = 0; devfn < ZPCI_FUNCTIONS_PER_BUS; devfn++) {
zdev = zbus->function[devfn]; zdev = zbus->function[devfn];
if (zdev && zdev->state == ZPCI_FN_STATE_CONFIGURED) { if (zdev && zdev->state == ZPCI_FN_STATE_CONFIGURED) {
...@@ -184,26 +181,26 @@ void zpci_bus_scan_busses(void) ...@@ -184,26 +181,26 @@ void zpci_bus_scan_busses(void)
/* zpci_bus_create_pci_bus - Create the PCI bus associated with this zbus /* zpci_bus_create_pci_bus - Create the PCI bus associated with this zbus
* @zbus: the zbus holding the zdevices * @zbus: the zbus holding the zdevices
* @f0: function 0 of the bus * @fr: PCI root function that will determine the bus's domain, and bus speeed
* @ops: the pci operations * @ops: the pci operations
* *
* Function zero is taken as a parameter as this is used to determine the * The PCI function @fr determines the domain (its UID), multifunction property
* domain, multifunction property and maximum bus speed of the entire bus. * and maximum bus speed of the entire bus.
* *
* Return: 0 on success, an error code otherwise * Return: 0 on success, an error code otherwise
*/ */
static int zpci_bus_create_pci_bus(struct zpci_bus *zbus, struct zpci_dev *f0, struct pci_ops *ops) static int zpci_bus_create_pci_bus(struct zpci_bus *zbus, struct zpci_dev *fr, struct pci_ops *ops)
{ {
struct pci_bus *bus; struct pci_bus *bus;
int domain; int domain;
domain = zpci_alloc_domain((u16)f0->uid); domain = zpci_alloc_domain((u16)fr->uid);
if (domain < 0) if (domain < 0)
return domain; return domain;
zbus->domain_nr = domain; zbus->domain_nr = domain;
zbus->multifunction = f0->rid_available; zbus->multifunction = fr->rid_available;
zbus->max_bus_speed = f0->max_bus_speed; zbus->max_bus_speed = fr->max_bus_speed;
/* /*
* Note that the zbus->resources are taken over and zbus->resources * Note that the zbus->resources are taken over and zbus->resources
...@@ -303,47 +300,6 @@ void pcibios_bus_add_device(struct pci_dev *pdev) ...@@ -303,47 +300,6 @@ void pcibios_bus_add_device(struct pci_dev *pdev)
} }
} }
/* zpci_bus_create_hotplug_slots - Add hotplug slot(s) for device added to bus
* @zdev: the zPCI device that was newly added
*
* Add the hotplug slot(s) for the newly added PCI function. Normally this is
* simply the slot for the function itself. If however we are adding the
* function 0 on a zbus, it might be that we already registered functions on
* that zbus but could not create their hotplug slots yet so add those now too.
*
* Return: 0 on success, an error code otherwise
*/
static int zpci_bus_create_hotplug_slots(struct zpci_dev *zdev)
{
struct zpci_bus *zbus = zdev->zbus;
int devfn, rc = 0;
rc = zpci_init_slot(zdev);
if (rc)
return rc;
zdev->has_hp_slot = 1;
if (zdev->devfn == 0 && zbus->multifunction) {
/* Now that function 0 is there we can finally create the
* hotplug slots for those functions with devfn != 0 that have
* been parked in zbus->function[] waiting for us to be able to
* create the PCI bus.
*/
for (devfn = 1; devfn < ZPCI_FUNCTIONS_PER_BUS; devfn++) {
zdev = zbus->function[devfn];
if (zdev && !zdev->has_hp_slot) {
rc = zpci_init_slot(zdev);
if (rc)
return rc;
zdev->has_hp_slot = 1;
}
}
}
return rc;
}
static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev)
{ {
int rc = -EINVAL; int rc = -EINVAL;
...@@ -352,21 +308,19 @@ static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev) ...@@ -352,21 +308,19 @@ static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev)
pr_err("devfn %04x is already assigned\n", zdev->devfn); pr_err("devfn %04x is already assigned\n", zdev->devfn);
return rc; return rc;
} }
zdev->zbus = zbus; zdev->zbus = zbus;
zbus->function[zdev->devfn] = zdev; zbus->function[zdev->devfn] = zdev;
zpci_nb_devices++; zpci_nb_devices++;
if (zbus->bus) { if (zbus->multifunction && !zdev->rid_available) {
if (zbus->multifunction && !zdev->rid_available) { WARN_ONCE(1, "rid_available not set for multifunction\n");
WARN_ONCE(1, "rid_available not set for multifunction\n"); goto error;
goto error;
}
zpci_bus_create_hotplug_slots(zdev);
} else {
/* Hotplug slot will be created once function 0 appears */
zbus->multifunction = 1;
} }
rc = zpci_init_slot(zdev);
if (rc)
goto error;
zdev->has_hp_slot = 1;
return 0; return 0;
...@@ -400,7 +354,11 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops) ...@@ -400,7 +354,11 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops)
return -ENOMEM; return -ENOMEM;
} }
if (zdev->devfn == 0) { if (!zbus->bus) {
/* The UID of the first PCI function registered with a zpci_bus
* is used as the domain number for that bus. Currently there
* is exactly one zpci_bus per domain.
*/
rc = zpci_bus_create_pci_bus(zbus, zdev, ops); rc = zpci_bus_create_pci_bus(zbus, zdev, ops);
if (rc) if (rc)
goto error; goto error;
......
...@@ -2579,33 +2579,39 @@ struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) ...@@ -2579,33 +2579,39 @@ struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
} }
EXPORT_SYMBOL(pci_scan_single_device); EXPORT_SYMBOL(pci_scan_single_device);
static unsigned int next_fn(struct pci_bus *bus, struct pci_dev *dev, static int next_ari_fn(struct pci_bus *bus, struct pci_dev *dev, int fn)
unsigned int fn)
{ {
int pos; int pos;
u16 cap = 0; u16 cap = 0;
unsigned int next_fn; unsigned int next_fn;
if (pci_ari_enabled(bus)) { if (!dev)
if (!dev) return -ENODEV;
return 0;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
if (!pos)
return 0;
pci_read_config_word(dev, pos + PCI_ARI_CAP, &cap); pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
next_fn = PCI_ARI_CAP_NFN(cap); if (!pos)
if (next_fn <= fn) return -ENODEV;
return 0; /* protect against malformed list */
return next_fn; pci_read_config_word(dev, pos + PCI_ARI_CAP, &cap);
} next_fn = PCI_ARI_CAP_NFN(cap);
if (next_fn <= fn)
return -ENODEV; /* protect against malformed list */
/* dev may be NULL for non-contiguous multifunction devices */ return next_fn;
if (!dev || dev->multifunction) }
return (fn + 1) % 8;
return 0; static int next_fn(struct pci_bus *bus, struct pci_dev *dev, int fn)
{
if (pci_ari_enabled(bus))
return next_ari_fn(bus, dev, fn);
if (fn >= 7)
return -ENODEV;
/* only multifunction devices may have more functions */
if (dev && !dev->multifunction)
return -ENODEV;
return fn + 1;
} }
static int only_one_child(struct pci_bus *bus) static int only_one_child(struct pci_bus *bus)
...@@ -2643,26 +2649,30 @@ static int only_one_child(struct pci_bus *bus) ...@@ -2643,26 +2649,30 @@ static int only_one_child(struct pci_bus *bus)
*/ */
int pci_scan_slot(struct pci_bus *bus, int devfn) int pci_scan_slot(struct pci_bus *bus, int devfn)
{ {
unsigned int fn, nr = 0;
struct pci_dev *dev; struct pci_dev *dev;
int fn = 0, nr = 0;
if (only_one_child(bus) && (devfn > 0)) if (only_one_child(bus) && (devfn > 0))
return 0; /* Already scanned the entire slot */ return 0; /* Already scanned the entire slot */
dev = pci_scan_single_device(bus, devfn); do {
if (!dev)
return 0;
if (!pci_dev_is_added(dev))
nr++;
for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
dev = pci_scan_single_device(bus, devfn + fn); dev = pci_scan_single_device(bus, devfn + fn);
if (dev) { if (dev) {
if (!pci_dev_is_added(dev)) if (!pci_dev_is_added(dev))
nr++; nr++;
dev->multifunction = 1; if (fn > 0)
dev->multifunction = 1;
} else if (fn == 0) {
/*
* Function 0 is required unless we are running on
* a hypervisor that passes through individual PCI
* functions.
*/
if (!hypervisor_isolated_pci_functions())
break;
} }
} fn = next_fn(bus, dev, fn);
} while (fn >= 0);
/* Only one slot has PCIe device */ /* Only one slot has PCIe device */
if (bus->self && nr) if (bus->self && nr)
...@@ -2858,29 +2868,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, ...@@ -2858,29 +2868,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
{ {
unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0; unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0;
unsigned int start = bus->busn_res.start; unsigned int start = bus->busn_res.start;
unsigned int devfn, fn, cmax, max = start; unsigned int devfn, cmax, max = start;
struct pci_dev *dev; struct pci_dev *dev;
int nr_devs;
dev_dbg(&bus->dev, "scanning bus\n"); dev_dbg(&bus->dev, "scanning bus\n");
/* Go find them, Rover! */ /* Go find them, Rover! */
for (devfn = 0; devfn < 256; devfn += 8) { for (devfn = 0; devfn < 256; devfn += 8)
nr_devs = pci_scan_slot(bus, devfn); pci_scan_slot(bus, devfn);
/*
* The Jailhouse hypervisor may pass individual functions of a
* multi-function device to a guest without passing function 0.
* Look for them as well.
*/
if (jailhouse_paravirt() && nr_devs == 0) {
for (fn = 1; fn < 8; fn++) {
dev = pci_scan_single_device(bus, devfn + fn);
if (dev)
dev->multifunction = 1;
}
}
}
/* Reserve buses for SR-IOV capability */ /* Reserve buses for SR-IOV capability */
used_buses = pci_iov_bus_range(bus); used_buses = pci_iov_bus_range(bus);
......
...@@ -32,4 +32,12 @@ static inline bool jailhouse_paravirt(void) ...@@ -32,4 +32,12 @@ static inline bool jailhouse_paravirt(void)
#endif /* !CONFIG_X86 */ #endif /* !CONFIG_X86 */
static inline bool hypervisor_isolated_pci_functions(void)
{
if (IS_ENABLED(CONFIG_S390))
return true;
return jailhouse_paravirt();
}
#endif /* __LINUX_HYPEVISOR_H */ #endif /* __LINUX_HYPEVISOR_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment