static char *pci_devs_use_d3r = NULL;
module_param_named(d3r, pci_devs_use_d3r, charp, S_IRUGO);
+/* Device id list holding different device type listings
+ * for hiding devices and reset logic.
+ */
+#define PCIBACK_ID_TYPE_HIDE 1
+#define PCIBACK_ID_TYPE_SBR 2
+#define PCIBACK_ID_TYPE_D3R 3
+
struct pcistub_device_id {
struct list_head slot_list;
+ int type;
int domain;
unsigned char bus;
unsigned int devfn;
static LIST_HEAD(pcistub_device_ids);
static DEFINE_SPINLOCK(device_ids_lock);
-/* reset usage lists */
-static LIST_HEAD(pcistub_sbr_device_ids);
-static LIST_HEAD(pcistub_d3r_device_ids);
-static DEFINE_SPINLOCK(device_reset_ids_lock);
-
struct pcistub_device {
struct kref kref;
struct list_head dev_list;
return 0;
}
-static int __devinit pcistub_match(struct pci_dev *dev, struct list_head *list)
+static int __devinit pcistub_match(struct pci_dev *dev, int type)
{
struct pcistub_device_id *pdev_id;
unsigned long flags;
int found = 0;
- spinlock_t *lock;
- if (list == &pcistub_device_ids)
- lock = &device_ids_lock;
- else
- lock = &device_reset_ids_lock;
-
- spin_lock_irqsave(lock, flags);
- list_for_each_entry(pdev_id, list, slot_list) {
+ spin_lock_irqsave(&device_ids_lock, flags);
+ list_for_each_entry(pdev_id, &pcistub_device_ids, slot_list) {
+ if (pdev_id->type != type)
+ continue;
if (pcistub_match_one(dev, pdev_id)) {
found = 1;
break;
}
}
- spin_unlock_irqrestore(lock, flags);
+ spin_unlock_irqrestore(&device_ids_lock, flags);
return found;
}
* values if SBR/D3R reset logic was requested.
*/
pciback_classify_device(dev);
- dev_data->use_sbr = pcistub_match(dev, &pcistub_sbr_device_ids);
- dev_data->use_d3r = pcistub_match(dev, &pcistub_d3r_device_ids);
+ dev_data->use_sbr = pcistub_match(dev, PCIBACK_ID_TYPE_SBR);
+ dev_data->use_d3r = pcistub_match(dev, PCIBACK_ID_TYPE_D3R);
/* Store the config space here where the device is off and ready to be
* exported before any FLRs or other resets are done
dev_dbg(&dev->dev, "probing...\n");
- if (pcistub_match(dev, &pcistub_device_ids)) {
+ if (pcistub_match(dev, PCIBACK_ID_TYPE_HIDE)) {
if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL
&& dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
return -EINVAL;
}
-static int pcistub_device_id_add(int domain, int bus, int slot, int func)
+static int pcistub_device_id_add(int domain, int bus, int slot, int func, int type)
{
struct pcistub_device_id *pci_dev_id;
unsigned long flags;
if (!pci_dev_id)
return -ENOMEM;
+ pci_dev_id->type = type;
pci_dev_id->domain = domain;
pci_dev_id->bus = bus;
pci_dev_id->devfn = PCI_DEVFN(slot, func);
- pr_debug("pciback: wants to seize %04x:%02x:%02x.%01x\n",
- domain, bus, slot, func);
+ pr_debug("pciback: adding device ID type: %d for %04x:%02x:%02x.%01x\n",
+ type, domain, bus, slot, func);
spin_lock_irqsave(&device_ids_lock, flags);
list_add_tail(&pci_dev_id->slot_list, &pcistub_device_ids);
return err;
}
-static int pcistub_device_id_sbr_add(int domain, int bus, int slot, int func)
-{
- struct pcistub_device_id *pci_dev_id;
- unsigned long flags;
-
- pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
- if (!pci_dev_id)
- return -ENOMEM;
-
- pci_dev_id->domain = domain;
- pci_dev_id->bus = bus;
- pci_dev_id->devfn = PCI_DEVFN(slot, func);
-
- pr_debug("pciback: adding sbr device %04x:%02x:%02x.%01x\n",
- domain, bus, slot, func);
-
- spin_lock_irqsave(&device_reset_ids_lock, flags);
- list_add_tail(&pci_dev_id->slot_list, &pcistub_sbr_device_ids);
- spin_unlock_irqrestore(&device_reset_ids_lock, flags);
-
- return 0;
-}
-
-static int pcistub_device_id_d3r_add(int domain, int bus, int slot, int func)
-{
- struct pcistub_device_id *pci_dev_id;
- unsigned long flags;
-
- pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
- if (!pci_dev_id)
- return -ENOMEM;
-
- pci_dev_id->domain = domain;
- pci_dev_id->bus = bus;
- pci_dev_id->devfn = PCI_DEVFN(slot, func);
-
- pr_debug("pciback: adding d3r device %04x:%02x:%02x.%01x\n",
- domain, bus, slot, func);
-
- spin_lock_irqsave(&device_reset_ids_lock, flags);
- list_add_tail(&pci_dev_id->slot_list, &pcistub_d3r_device_ids);
- spin_unlock_irqrestore(&device_reset_ids_lock, flags);
-
- return 0;
-}
-
static ssize_t pcistub_slot_add(struct device_driver *drv, const char *buf,
size_t count)
{
if (err)
goto out;
- err = pcistub_device_id_add(domain, bus, slot, func);
+ err = pcistub_device_id_add(domain, bus, slot, func, PCIBACK_ID_TYPE_HIDE);
out:
if (!err)
size_t count)
{
int domain, bus, slot, func;
- int is_sbr, err;
+ int type, err;
/* string begins with reset type specifier sbr=|dr3= */
if (!strncmp(buf, "sbr=", 4)) {
- is_sbr = 1;
+ type = PCIBACK_ID_TYPE_SBR;
buf += 4;
} else if (!strncmp(buf, "d3r=", 4)) {
- is_sbr = 0;
+ type = PCIBACK_ID_TYPE_D3R;
buf += 4;
} else {
err = -EINVAL;
if (err)
goto out;
- if (is_sbr)
- err = pcistub_device_id_sbr_add(domain, bus, slot, func);
- else
- err = pcistub_device_id_d3r_add(domain, bus, slot, func);
+ err = pcistub_device_id_add(domain, bus, slot, func, type);
out:
if (!err)
int err = 0;
/* Parse device lists for hide, sbr, and d3r */
- err = pciback_parse_device_params(pci_devs_to_hide, pcistub_device_id_add);
+ err = pciback_parse_device_params(pci_devs_to_hide, PCIBACK_ID_TYPE_HIDE, pcistub_device_id_add);
if (err)
goto out;
- err = pciback_parse_device_params(pci_devs_use_sbr, pcistub_device_id_sbr_add);
+ err = pciback_parse_device_params(pci_devs_use_sbr, PCIBACK_ID_TYPE_SBR, pcistub_device_id_add);
if (err)
goto out;
- err = pciback_parse_device_params(pci_devs_use_d3r, pcistub_device_id_d3r_add);
+ err = pciback_parse_device_params(pci_devs_use_d3r, PCIBACK_ID_TYPE_D3R, pcistub_device_id_add);
if (err)
goto out;
goto cleanup;
failed:
- err = -ENXIO
+ err = -ENXIO;
dev_warn(&dev->dev,
"secondary bus reset failed for device - all functions need to be co-assigned!\n");
return err;
}
-/* This function is used to do a function level reset on a singe device/function.
- * FLRs must be done on devices before they are unassigned from one domain and
- * passed through to another. The preferred method is to do an actual FLR on the
- * device but the functionality may not be present or exposed. In the later case
- * we attempt to locate the capability even though it is not chained into the
- * capabilities list.
+/* This function is used to do a function level reset on a singe
+ * device/function. FLRs must be done on devices before they are
+ * unassigned from one domain and passed through to another. The
+ * preferred method is to do an actual FLR on the device but the
+ * functionality may not be present or exposed. In the later case
+ * we attempt to locate the capability even though it is not
+ * chained into the capabilities list.
*
- * In some cases, there are no TODO...
+ * In some cases, there is no way to perform the actual FLR so we
+ * fall back to some alternate methods (which are not as effective
+ * or useful).
*/
void pciback_flr_device(struct pci_dev *dev)
{
struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
int err = 0;
- if (dev_data->dev_type == PCIBACK_TYPE_PCIe_ENDPOINT) {
- if (dev_data->exp_flr_offset != 0) {
+ do {
+ /* First, always try to do an FLR */
+ if (dev_data->dev_type == PCIBACK_TYPE_PCIe_ENDPOINT &&
+ dev_data->exp_flr_offset != 0) {
pciback_do_pcie_flr(dev, dev_data->exp_flr_offset);
- goto out;
+ break;
}
-
- if (dev->bus->number == 0) {
- err = pciback_do_vendor_specific_reset(dev);
- if (err && dev_data->use_d3r)
- err = pciback_do_dstate_transition_reset(dev);
- if (err) {
- dev_warn(&dev->dev, "FLR functionality not supported; "
- "attempts to use vendor specific FLR and D-state transitions failed; "
- "FLR not performed for device\n");
- }
-
- }
- else if (dev_data->use_sbr) {
- err = pciback_do_dstate_transition_reset(dev);
- if (err) {
- dev_warn(&dev->dev, "FLR functionality not supported; "
- "attempts to use secondary bus reset failed; "
- "FLR not performed for device\n");
- }
- }
- }
- else if (dev_data->dev_type == PCIBACK_TYPE_PCI) {
- if (dev_data->af_flr_offset != 0) {
+ if (dev_data->dev_type == PCIBACK_TYPE_PCI &&
+ dev_data->af_flr_offset != 0) {
pciback_do_pci_flr(dev, dev_data->af_flr_offset);
- goto out;
+ break;
}
+
+ /* Next for integrated devices on the host bus 0, try some other methods */
if (dev->bus->number == 0) {
err = pciback_do_vendor_specific_reset(dev);
if (err && dev_data->use_d3r)
err = pciback_do_dstate_transition_reset(dev);
- if (err) {
+ if (err)
dev_warn(&dev->dev, "FLR functionality not supported; "
- "attempts to use vendor specific FLR and D-state transitions failed; "
- "FLR not performed for device\n");
- }
+ "attempts to use vendor FLR or D-states failed\n");
+ break;
}
- else {
- dev_warn(&dev->dev, "FLR functionality not supported; "
- "no secondary bus reset functionality available; "
- "FLR not performed for device\n");
+
+ /* Else for PCIe devices behind root port, attempt a secondary bus reset */
+ if (dev_data->dev_type == PCIBACK_TYPE_PCIe_ENDPOINT && dev_data->use_sbr) {
+ err = pciback_do_secondary_bus_reset(dev);
+ if (err)
+ dev_warn(&dev->dev, "FLR functionality not supported; "
+ "attempts to use secondary bus reset failed;\n");
+ break;
}
- }
- else {
- dev_warn(&dev->dev,
- "FLR not being used for bridge devices.\n");
- }
-
- /* no return, trace warning about activity */
-out:
+
+ err = -ENODEV;
+ } while (0);
+
+ if (err)
+ dev_warn(&dev->dev, "FLR not performed for device\n");
}
/* Helper used to location the FLR capabilities for a PCIe device.
return IRQ_HANDLED;
}
-int pciback_parse_device_params(const char *device_args, int (*add_func) (int domain, int bus, int slot, int func))
+int pciback_parse_device_params(const char *device_args, int type, int (*add_func) (int domain, int bus, int slot, int func, int type))
{
int pos = 0;
int err = 0;
goto parse_error;
}
- err = add_func(domain, bus, slot, func);
+ err = add_func(domain, bus, slot, func, type);
if (err)
goto out;