From a6ed1f4e94e1883742ac79f09f9754ffacfae456 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 19 Jun 2015 15:57:44 +0800 Subject: PCI: Use "slot" and "pci_slot" for struct hotplug_slot and struct pci_slot Now in pci_hotplug_core.c, we randomly name a struct hotplug_slot and a struct pci_slot. It's easy to confuse them, so let us use "slot" for a struct hotplug_slot and "pci_slot" for a struct pci_slot. No functional change. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 56d8486..d1fab97 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -83,12 +83,12 @@ GET_STATUS(attention_status, u8) GET_STATUS(latch_status, u8) GET_STATUS(adapter_status, u8) -static ssize_t power_read_file(struct pci_slot *slot, char *buf) +static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf) { int retval; u8 value; - retval = get_power_status(slot->hotplug, &value); + retval = get_power_status(pci_slot->hotplug, &value); if (retval) return retval; @@ -140,22 +140,22 @@ static struct pci_slot_attribute hotplug_slot_attr_power = { .store = power_write_file }; -static ssize_t attention_read_file(struct pci_slot *slot, char *buf) +static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf) { int retval; u8 value; - retval = get_attention_status(slot->hotplug, &value); + retval = get_attention_status(pci_slot->hotplug, &value); if (retval) return retval; return sprintf(buf, "%d\n", value); } -static ssize_t attention_write_file(struct pci_slot *slot, const char *buf, +static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { - struct hotplug_slot_ops *ops = slot->hotplug->ops; + struct hotplug_slot_ops *ops = pci_slot->hotplug->ops; unsigned long lattention; u8 attention; int retval = 0; @@ -169,7 +169,7 @@ static ssize_t attention_write_file(struct pci_slot *slot, const char *buf, goto exit; } if (ops->set_attention_status) - retval = ops->set_attention_status(slot->hotplug, attention); + retval = ops->set_attention_status(pci_slot->hotplug, attention); module_put(ops->owner); exit: @@ -184,12 +184,12 @@ static struct pci_slot_attribute hotplug_slot_attr_attention = { .store = attention_write_file }; -static ssize_t latch_read_file(struct pci_slot *slot, char *buf) +static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf) { int retval; u8 value; - retval = get_latch_status(slot->hotplug, &value); + retval = get_latch_status(pci_slot->hotplug, &value); if (retval) return retval; @@ -201,12 +201,12 @@ static struct pci_slot_attribute hotplug_slot_attr_latch = { .show = latch_read_file, }; -static ssize_t presence_read_file(struct pci_slot *slot, char *buf) +static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf) { int retval; u8 value; - retval = get_adapter_status(slot->hotplug, &value); + retval = get_adapter_status(pci_slot->hotplug, &value); if (retval) return retval; @@ -307,43 +307,43 @@ static bool has_test_file(struct pci_slot *pci_slot) return false; } -static int fs_add_slot(struct pci_slot *slot) +static int fs_add_slot(struct pci_slot *pci_slot) { int retval = 0; /* Create symbolic link to the hotplug driver module */ - pci_hp_create_module_link(slot); + pci_hp_create_module_link(pci_slot); - if (has_power_file(slot)) { - retval = sysfs_create_file(&slot->kobj, + if (has_power_file(pci_slot)) { + retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); if (retval) goto exit_power; } - if (has_attention_file(slot)) { - retval = sysfs_create_file(&slot->kobj, + if (has_attention_file(pci_slot)) { + retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); if (retval) goto exit_attention; } - if (has_latch_file(slot)) { - retval = sysfs_create_file(&slot->kobj, + if (has_latch_file(pci_slot)) { + retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); if (retval) goto exit_latch; } - if (has_adapter_file(slot)) { - retval = sysfs_create_file(&slot->kobj, + if (has_adapter_file(pci_slot)) { + retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); if (retval) goto exit_adapter; } - if (has_test_file(slot)) { - retval = sysfs_create_file(&slot->kobj, + if (has_test_file(pci_slot)) { + retval = sysfs_create_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); if (retval) goto exit_test; @@ -352,45 +352,45 @@ static int fs_add_slot(struct pci_slot *slot) goto exit; exit_test: - if (has_adapter_file(slot)) - sysfs_remove_file(&slot->kobj, + if (has_adapter_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); exit_adapter: - if (has_latch_file(slot)) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); + if (has_latch_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); exit_latch: - if (has_attention_file(slot)) - sysfs_remove_file(&slot->kobj, + if (has_attention_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); exit_attention: - if (has_power_file(slot)) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); + if (has_power_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); exit_power: - pci_hp_remove_module_link(slot); + pci_hp_remove_module_link(pci_slot); exit: return retval; } -static void fs_remove_slot(struct pci_slot *slot) +static void fs_remove_slot(struct pci_slot *pci_slot) { - if (has_power_file(slot)) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); + if (has_power_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); - if (has_attention_file(slot)) - sysfs_remove_file(&slot->kobj, + if (has_attention_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_attention.attr); - if (has_latch_file(slot)) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); + if (has_latch_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); - if (has_adapter_file(slot)) - sysfs_remove_file(&slot->kobj, + if (has_adapter_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_presence.attr); - if (has_test_file(slot)) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); + if (has_test_file(pci_slot)) + sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); - pci_hp_remove_module_link(slot); + pci_hp_remove_module_link(pci_slot); } static struct hotplug_slot *get_slot_from_name(const char *name) @@ -467,37 +467,37 @@ EXPORT_SYMBOL_GPL(__pci_hp_register); /** * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem - * @hotplug: pointer to the &struct hotplug_slot to deregister + * @slot: pointer to the &struct hotplug_slot to deregister * * The @slot must have been registered with the pci hotplug subsystem * previously with a call to pci_hp_register(). * * Returns 0 if successful, anything else for an error. */ -int pci_hp_deregister(struct hotplug_slot *hotplug) +int pci_hp_deregister(struct hotplug_slot *slot) { struct hotplug_slot *temp; - struct pci_slot *slot; + struct pci_slot *pci_slot; - if (!hotplug) + if (!slot) return -ENODEV; mutex_lock(&pci_hp_mutex); - temp = get_slot_from_name(hotplug_slot_name(hotplug)); - if (temp != hotplug) { + temp = get_slot_from_name(hotplug_slot_name(slot)); + if (temp != slot) { mutex_unlock(&pci_hp_mutex); return -ENODEV; } - list_del(&hotplug->slot_list); + list_del(&slot->slot_list); - slot = hotplug->pci_slot; - fs_remove_slot(slot); - dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug)); + pci_slot = slot->pci_slot; + fs_remove_slot(pci_slot); + dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); - hotplug->release(hotplug); - slot->hotplug = NULL; - pci_destroy_slot(slot); + slot->release(slot); + pci_slot->hotplug = NULL; + pci_destroy_slot(pci_slot); mutex_unlock(&pci_hp_mutex); return 0; @@ -506,7 +506,7 @@ EXPORT_SYMBOL_GPL(pci_hp_deregister); /** * pci_hp_change_slot_info - changes the slot's information structure in the core - * @hotplug: pointer to the slot whose info has changed + * @slot: pointer to the slot whose info has changed * @info: pointer to the info copy into the slot's info structure * * @slot must have been registered with the pci @@ -514,13 +514,13 @@ EXPORT_SYMBOL_GPL(pci_hp_deregister); * * Returns 0 if successful, anything else for an error. */ -int pci_hp_change_slot_info(struct hotplug_slot *hotplug, +int pci_hp_change_slot_info(struct hotplug_slot *slot, struct hotplug_slot_info *info) { - if (!hotplug || !info) + if (!slot || !info) return -ENODEV; - memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info)); + memcpy(slot->info, info, sizeof(struct hotplug_slot_info)); return 0; } -- cgit v0.10.2 From ac10836b681289f7e430e52b106a209bbdcaa75e Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 19 Jun 2015 15:57:45 +0800 Subject: PCI: pciehp: Simplify pcie_poll_cmd() Move first slot status read into while to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 2913f7e..daf54be 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -109,21 +109,17 @@ static int pcie_poll_cmd(struct controller *ctrl, int timeout) struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; - pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (slot_status & PCI_EXP_SLTSTA_CC) { - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_CC); - return 1; - } - while (timeout > 0) { - msleep(10); - timeout -= 10; + while (true) { pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (slot_status & PCI_EXP_SLTSTA_CC) { pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); return 1; } + if (timeout < 0) + break; + msleep(10); + timeout -= 10; } return 0; /* timeout */ } -- cgit v0.10.2 From fb0cc3aa552642631c3a4e83deae2b5c1a1ef4fa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 20 Jul 2015 09:10:36 -0500 Subject: iommu/vt-d: Cache PCI ATS state and Invalidate Queue Depth We check the ATS state (enabled/disabled) and fetch the PCI ATS Invalidate Queue Depth in performance-sensitive paths. It's easy to cache these, which removes dependencies on PCI. Remember the ATS enabled state. When enabling, read the queue depth once and cache it in the device_domain_info struct. This is similar to what amd_iommu.c does. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel Acked-by: Joerg Roedel diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index a98a7b2..c22a549 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -408,6 +408,10 @@ struct device_domain_info { struct list_head global; /* link to global list */ u8 bus; /* PCI bus number */ u8 devfn; /* PCI devfn number */ + struct { + u8 enabled:1; + u8 qdep; + } ats; /* ATS state */ struct device *dev; /* it's NULL for PCIe-to-PCI bridge */ struct intel_iommu *iommu; /* IOMMU used by this device */ struct dmar_domain *domain; /* pointer to domain */ @@ -1391,19 +1395,26 @@ iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu, static void iommu_enable_dev_iotlb(struct device_domain_info *info) { + struct pci_dev *pdev; + if (!info || !dev_is_pci(info->dev)) return; - pci_enable_ats(to_pci_dev(info->dev), VTD_PAGE_SHIFT); + pdev = to_pci_dev(info->dev); + if (pci_enable_ats(pdev, VTD_PAGE_SHIFT)) + return; + + info->ats.enabled = 1; + info->ats.qdep = pci_ats_queue_depth(pdev); } static void iommu_disable_dev_iotlb(struct device_domain_info *info) { - if (!info->dev || !dev_is_pci(info->dev) || - !pci_ats_enabled(to_pci_dev(info->dev))) + if (!info->ats.enabled) return; pci_disable_ats(to_pci_dev(info->dev)); + info->ats.enabled = 0; } static void iommu_flush_dev_iotlb(struct dmar_domain *domain, @@ -1415,16 +1426,11 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain, spin_lock_irqsave(&device_domain_lock, flags); list_for_each_entry(info, &domain->devices, link) { - struct pci_dev *pdev; - if (!info->dev || !dev_is_pci(info->dev)) - continue; - - pdev = to_pci_dev(info->dev); - if (!pci_ats_enabled(pdev)) + if (!info->ats.enabled) continue; sid = info->bus << 8 | info->devfn; - qdep = pci_ats_queue_depth(pdev); + qdep = info->ats.qdep; qi_flush_dev_iotlb(info->iommu, sid, qdep, addr, mask); } spin_unlock_irqrestore(&device_domain_lock, flags); @@ -2272,6 +2278,8 @@ static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu, info->bus = bus; info->devfn = devfn; + info->ats.enabled = 0; + info->ats.qdep = 0; info->dev = dev; info->domain = domain; info->iommu = iommu; -- cgit v0.10.2 From 67546762978f523749eac157903e0b01c18e083a Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 17 Jul 2015 17:16:31 +0800 Subject: PCI: Protect pci_bus->slots with pci_slot_mutex, not pci_bus_sem Rajat Jain reported a deadlock when PCIe hot-add and AER recovery happen at the same time: thread 1: pciehp_enable_slot pciehp_configure_device pci_bus_add_devices pci_bus_add_device device_attach device_lock(dev) # acquire device lock ... pciehp_probe init_slot pci_hp_register pci_create_slot down_write(pci_bus_sem) # deadlock here thread 2: aer_isr_one_error aer_process_err_device do_recovery broadcast_error_message(..., report_error_detected) pci_walk_bus(..., cb=report_error_detected, ...) down_read(&pci_bus_sem) # acquire pci_bus_sem report_error_detected(dev) # cb() device_lock(dev) # deadlock here Previously, the bus->devices and bus->slots list were protected by pci_bus_sem. In pci_create_slot(), we held it for writing so we could add to the bus->slots list. Add a new local pci_slot_mutex to protect bus->slots. Hold pci_bus_sem for reading while searching the bus->devices list. [bhelgaas: changelog] Link: http://lkml.kernel.org/r/CAA93t1qpPqbih+UB0McA_d_+2rVaNkXsinAUxYzK9+JXSS+L-g@mail.gmail.com Reported-by: Rajat Jain Tested-by: Guenter Roeck Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 396c200..4bd3fce 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -14,6 +14,7 @@ struct kset *pci_slots_kset; EXPORT_SYMBOL_GPL(pci_slots_kset); +static DEFINE_MUTEX(pci_slot_mutex); static ssize_t pci_slot_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) @@ -106,9 +107,11 @@ static void pci_slot_release(struct kobject *kobj) dev_dbg(&slot->bus->dev, "dev %02x, released physical slot %s\n", slot->number, pci_slot_name(slot)); + down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) if (PCI_SLOT(dev->devfn) == slot->number) dev->slot = NULL; + up_read(&pci_bus_sem); list_del(&slot->list); @@ -194,9 +197,8 @@ static int rename_slot(struct pci_slot *slot, const char *name) static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) { struct pci_slot *slot; - /* - * We already hold pci_bus_sem so don't worry - */ + + /* We already hold pci_slot_mutex */ list_for_each_entry(slot, &parent->slots, list) if (slot->number == slot_nr) { kobject_get(&slot->kobj); @@ -253,7 +255,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, int err = 0; char *slot_name = NULL; - down_write(&pci_bus_sem); + mutex_lock(&pci_slot_mutex); if (slot_nr == -1) goto placeholder; @@ -301,16 +303,18 @@ placeholder: INIT_LIST_HEAD(&slot->list); list_add(&slot->list, &parent->slots); + down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) if (PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; + up_read(&pci_bus_sem); dev_dbg(&parent->dev, "dev %02x, created physical slot %s\n", slot_nr, pci_slot_name(slot)); out: kfree(slot_name); - up_write(&pci_bus_sem); + mutex_unlock(&pci_slot_mutex); return slot; err: kfree(slot); @@ -332,9 +336,9 @@ void pci_destroy_slot(struct pci_slot *slot) dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n", slot->number, atomic_read(&slot->kobj.kref.refcount) - 1); - down_write(&pci_bus_sem); + mutex_lock(&pci_slot_mutex); kobject_put(&slot->kobj); - up_write(&pci_bus_sem); + mutex_unlock(&pci_slot_mutex); } EXPORT_SYMBOL_GPL(pci_destroy_slot); diff --git a/include/linux/pci.h b/include/linux/pci.h index 8a0321a..aaee493 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -446,7 +446,8 @@ struct pci_bus { struct list_head children; /* list of child buses */ struct list_head devices; /* list of devices on this bus */ struct pci_dev *self; /* bridge device as seen by parent */ - struct list_head slots; /* list of slots on this bus */ + struct list_head slots; /* list of slots on this bus; + protected by pci_slot_mutex */ struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; struct list_head resources; /* address space routed to this bus */ struct resource busn_res; /* bus numbers routed to this bus */ -- cgit v0.10.2 From 017ffe64e8b8c8db0f50433a71da41c6a4e12710 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 17 Jul 2015 17:16:32 +0800 Subject: PCI: Hold pci_slot_mutex while searching bus->slots list Previously, pci_setup_device() and similar functions searched the pci_bus->slots list without any locking. It was possible for another thread to update the list while we searched it. Add pci_dev_assign_slot() to search the list while holding pci_slot_mutex. [bhelgaas: changelog, fold in CONFIG_SYSFS fix] Tested-by: Guenter Roeck Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 42e02a2..5e2debf 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -126,7 +126,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, { struct pci_dev *dev; const char *type; - struct pci_slot *slot; dev = pci_alloc_dev(bus); if (!dev) @@ -145,10 +144,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, dev->needs_freset = 0; /* pcie fundamental reset required */ set_pcie_port_type(dev); - list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) - dev->slot = slot; - + pci_dev_assign_slot(dev); dev->vendor = get_int_prop(node, "vendor-id", 0xffff); dev->device = get_int_prop(node, "device-id", 0xffff); dev->subsystem_vendor = get_int_prop(node, "subsystem-vendor-id", 0); diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index c928bc6..3a0e1a9 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -249,7 +249,6 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, struct pci_bus *bus, int devfn) { struct dev_archdata *sd; - struct pci_slot *slot; struct platform_device *op; struct pci_dev *dev; const char *type; @@ -290,10 +289,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, dev->multifunction = 0; /* maybe a lie? */ set_pcie_port_type(dev); - list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) - dev->slot = slot; - + pci_dev_assign_slot(dev); dev->vendor = of_getintprop_default(node, "vendor-id", 0xffff); dev->device = of_getintprop_default(node, "device-id", 0xffff); dev->subsystem_vendor = diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cefd636..2a9ce16 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1133,7 +1133,6 @@ int pci_setup_device(struct pci_dev *dev) { u32 class; u8 hdr_type; - struct pci_slot *slot; int pos = 0; struct pci_bus_region region; struct resource *res; @@ -1149,10 +1148,7 @@ int pci_setup_device(struct pci_dev *dev) dev->error_state = pci_channel_io_normal; set_pcie_port_type(dev); - list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) - dev->slot = slot; - + pci_dev_assign_slot(dev); /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev->dma_mask = 0xffffffff; diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 4bd3fce..429d34c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -194,6 +194,17 @@ static int rename_slot(struct pci_slot *slot, const char *name) return result; } +void pci_dev_assign_slot(struct pci_dev *dev) +{ + struct pci_slot *slot; + + mutex_lock(&pci_slot_mutex); + list_for_each_entry(slot, &dev->bus->slots, list) + if (PCI_SLOT(dev->devfn) == slot->number) + dev->slot = slot; + mutex_unlock(&pci_slot_mutex); +} + static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) { struct pci_slot *slot; diff --git a/include/linux/pci.h b/include/linux/pci.h index aaee493..b3ba7fe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -798,6 +798,11 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, struct hotplug_slot *hotplug); void pci_destroy_slot(struct pci_slot *slot); +#ifdef CONFIG_SYSFS +void pci_dev_assign_slot(struct pci_dev *dev); +#else +static inline void pci_dev_assign_slot(struct pci_dev *dev) { } +#endif int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); -- cgit v0.10.2 From 1469d17dd341458267a08c8d8bb517cb986ec56d Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 21 Jul 2015 12:25:30 -0400 Subject: PCI: pciehp: Handle invalid data when reading from non-existent devices It's platform-dependent, but an MMIO read to a non-existent PCI device generally returns data with all bits set. This happens when the host bridge or Root Complex times out waiting for a response from the device and fabricates return data to complete the CPU's read. One example, reported in the bugzilla below, involved this hierarchy: pci 0000:00:1c.0: PCI bridge to [bus 02-3a] Root Port pci 0000:02:00.0: PCI bridge to [bus 03-0a] Upstream Port pci 0000:03:03.0: PCI bridge to [bus 05-07] Downstream Port pci 0000:05:00.0: PCI bridge to [bus 06-07] Thunderbolt Upstream Port pci 0000:06:00.0: PCI bridge to [bus 07] Thunderbolt Downstream Port pci 0000:07:00.0: BCM57762 NIC Unplugging the Thunderbolt switch and the NIC below it resulted in this: pciehp 0000:03:03.0: Surprise Removal tg3 0000:07:00.0: tg3_abort_hw timed out, TX_MODE_ENABLE will not clear MAC_TX_MODE=ffffffff pciehp 0000:06:00.0: unloading service driver pciehp pciehp 0000:06:00.0: pcie_isr: intr_loc 11f pciehp 0000:06:00.0: Switch interrupt received pciehp 0000:06:00.0: Latch open on Slot pciehp 0000:06:00.0: Attention button interrupt received pciehp 0000:06:00.0: Button pressed on Slot pciehp 0000:06:00.0: Presence/Notify input change pciehp 0000:06:00.0: Card present on Slot pciehp 0000:06:00.0: Power fault interrupt received pciehp 0000:06:00.0: Data Link Layer State change pciehp 0000:06:00.0: Link Up event The pciehp driver correctly noticed that the Thunderbolt switch (05:00.0 and 06:00.0) and NIC (07:00.0) had been removed, and it called their driver remove methods. Since the NIC was already gone, tg3 received 0xffffffff when it tried to read from the device. The resulting timeout is a tg3 issue and not of interest here. Similarly, since the 06:00.0 Thunderbolt switch was already gone, pcie_isr() received 0xffff when it tried to read PCI_EXP_SLTSTA, and pciehp thought that was valid status showing that many events had happened: the latch had been opened, the attention button had been pressed, a card was now present, and the link was now up. These are all wrong, of course, but pciehp went on to try to power up and enumerate devices below the non-existent bridge: pciehp 0000:06:00.0: PCI slot - powering on due to button press pciehp 0000:06:00.0: Surprise Insertion pci 0000:07:00.0 id reading try 50 times with interval 20 ms to get ffffffff [bhelgaas: changelog, also check in pcie_poll_cmd() & pcie_do_write_cmd()] Link: https://bugzilla.kernel.org/show_bug.cgi?id=99841 Suggested-by: Bjorn Helgaas Signed-off-by: Jarod Wilson Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index daf54be..8f3d3cf 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -111,6 +111,12 @@ static int pcie_poll_cmd(struct controller *ctrl, int timeout) while (true) { pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", + __func__); + return 0; + } + if (slot_status & PCI_EXP_SLTSTA_CC) { pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); @@ -186,6 +192,11 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, pcie_wait_cmd(ctrl); pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + if (slot_ctrl == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", __func__); + goto out; + } + slot_ctrl &= ~mask; slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; @@ -201,6 +212,7 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, if (wait) pcie_wait_cmd(ctrl); +out: mutex_unlock(&ctrl->ctrl_lock); } @@ -542,6 +554,11 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) intr_loc = 0; do { pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected); + if (detected == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", + __func__); + return IRQ_HANDLED; + } detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | -- cgit v0.10.2 From 58fa2405bd44805cb6166603100b0183ce26a0c8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 1 Jul 2015 17:01:03 -0500 Subject: PCI: pciehp: Remove unused interrupt events The list of interrupt events (INT_BUTTON_IGNORE, INT_PRESENCE_ON, etc.) was copied from other hotplug drivers, but pciehp doesn't use them all. Remove the interrupt events that aren't used by pciehp. Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 57cd132..aca84e1 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -101,18 +101,14 @@ struct controller { unsigned int power_fault_detected; }; -#define INT_BUTTON_IGNORE 0 #define INT_PRESENCE_ON 1 #define INT_PRESENCE_OFF 2 #define INT_SWITCH_CLOSE 3 #define INT_SWITCH_OPEN 4 #define INT_POWER_FAULT 5 -#define INT_POWER_FAULT_CLEAR 6 -#define INT_BUTTON_PRESS 7 -#define INT_BUTTON_RELEASE 8 -#define INT_BUTTON_CANCEL 9 -#define INT_LINK_UP 10 -#define INT_LINK_DOWN 11 +#define INT_BUTTON_PRESS 6 +#define INT_LINK_UP 7 +#define INT_LINK_DOWN 8 #define STATIC_STATE 0 #define BLINKINGON_STATE 1 -- cgit v0.10.2 From 2db0f71f56795f6b04d386b15c3b32b570558ebd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 1 Jul 2015 17:17:49 -0500 Subject: PCI: pciehp: Remove ignored MRL sensor interrupt events We queued interrupt events for the MRL being opened or closed, but the code in interrupt_event_handler() that handles these events ignored them. Stop enabling MRL interrupts and remove the ignored events. Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index aca84e1..62d6fe6 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -103,12 +103,10 @@ struct controller { #define INT_PRESENCE_ON 1 #define INT_PRESENCE_OFF 2 -#define INT_SWITCH_CLOSE 3 -#define INT_SWITCH_OPEN 4 -#define INT_POWER_FAULT 5 -#define INT_BUTTON_PRESS 6 -#define INT_LINK_UP 7 -#define INT_LINK_DOWN 8 +#define INT_POWER_FAULT 3 +#define INT_BUTTON_PRESS 4 +#define INT_LINK_UP 5 +#define INT_LINK_DOWN 6 #define STATIC_STATE 0 #define BLINKINGON_STATE 1 diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 8f3d3cf..5c24e93 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -543,7 +543,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) struct pci_dev *dev; struct slot *slot = ctrl->slot; u16 detected, intr_loc; - u8 open, present; + u8 present; bool link; /* @@ -561,7 +561,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) } detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | - PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); detected &= ~intr_loc; intr_loc |= detected; @@ -594,15 +594,6 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) return IRQ_HANDLED; - /* Check MRL Sensor Changed */ - if (intr_loc & PCI_EXP_SLTSTA_MRLSC) { - pciehp_get_latch_status(slot, &open); - ctrl_info(ctrl, "Latch %s on Slot(%s)\n", - open ? "open" : "close", slot_name(slot)); - pciehp_queue_interrupt_event(slot, open ? INT_SWITCH_OPEN : - INT_SWITCH_CLOSE); - } - /* Check Attention Button Pressed */ if (intr_loc & PCI_EXP_SLTSTA_ABP) { ctrl_info(ctrl, "Button pressed on Slot(%s)\n", @@ -662,13 +653,11 @@ void pcie_enable_notification(struct controller *ctrl) cmd |= PCI_EXP_SLTCTL_ABPE; else cmd |= PCI_EXP_SLTCTL_PDCE; - if (MRL_SENS(ctrl)) - cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | - PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_DLLSCE); -- cgit v0.10.2 From 3e14675335a15e1a91573837a15ff3bbb50bc14f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Jul 2015 15:55:21 -0500 Subject: PCI: Kill off set_irq_flags() usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit set_irq_flags is ARM-specific with custom flags which have genirq equivalents. Convert drivers to use the genirq interfaces directly, so we can kill off set_irq_flags. The translation of flags is as follows: IRQF_VALID -> !IRQ_NOREQUEST IRQF_PROBE -> !IRQ_NOPROBE IRQF_NOAUTOEN -> IRQ_NOAUTOEN For IRQs managed by an irqdomain, the irqdomain core code handles clearing and setting IRQ_NOREQUEST already, so there is no need to do this in .map() functions, and we can simply remove the set_irq_flags calls. Some users also modify IRQ_NOPROBE, and this has been maintained although it is not clear that is really needed. There appears to be a great deal of blind copy and paste of this code. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han CC: Kishon Vijay Abraham I CC: Murali Karicheri CC: Thierry Reding CC: Stephen Warren CC: Alexandre Courbot CC: Jingoo Han CC: Pratyush Anand CC: Simon Horman CC: Michal Simek CC: "Sören Brinkmann" diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c index 80db09e..1a0d124 100644 --- a/drivers/pci/host/pci-dra7xx.c +++ b/drivers/pci/host/pci-dra7xx.c @@ -155,7 +155,6 @@ static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index f34892e..f1d0749 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -196,7 +196,6 @@ static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip, handle_level_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } @@ -277,7 +276,6 @@ static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d, irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip, handle_level_irq); irq_set_chip_data(irq, d->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 10c0571..81df0c1 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -1248,7 +1248,6 @@ static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); tegra_cpuidle_pcie_irqs_in_use(); diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c index 2d31d4d..8e559d1 100644 --- a/drivers/pci/host/pci-xgene-msi.c +++ b/drivers/pci/host/pci-xgene-msi.c @@ -223,7 +223,6 @@ static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip, domain->host_data, handle_simple_irq, NULL, NULL); - set_irq_flags(virq, IRQF_VALID); return 0; } diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 69486be..5c6b562 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -350,7 +350,6 @@ static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index c086210..7678fe0 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -664,7 +664,6 @@ static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index f1a06a0..72015119 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -338,7 +338,6 @@ static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } @@ -377,7 +376,6 @@ static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); return 0; } -- cgit v0.10.2 From 95e16587ccd0f330e349360de048ebd4432d4b2c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Aug 2015 12:32:04 -0600 Subject: PCI: Add ACS quirks for Intel I219-LM/V The Intel 100-series chipset now includes the integrated Ethernet as part of a multifunction package. The Ethernet function does not include native ACS support, but Intel confirms that the device is not capable of peer-to- peer within the package. We can therefore quirk it to expose the isolation. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Acked-by: John Ronciak diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e9fd0e9..a914848 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3848,6 +3848,9 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_INTEL, 0x105F, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1060, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x10D9, pci_quirk_mf_endpoint_acs }, + /* I219 */ + { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ -- cgit v0.10.2 From edc90fee916b4f0d14af9c6b5c08666747488ef8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:05:46 -0500 Subject: PCI: Allocate ATS struct during enumeration Previously, we allocated pci_ats structures when an IOMMU driver called pci_enable_ats(). An SR-IOV VF shares the STU setting with its PF, so when enabling ATS on the VF, we allocated a pci_ats struct for the PF if it didn't already have one. We held the sriov->lock to serialize threads concurrently enabling ATS on several VFS so only one would allocate the PF pci_ats. Gregor reported a deadlock here: pci_enable_sriov sriov_enable virtfn_add mutex_lock(dev->sriov->lock) # acquire sriov->lock pci_device_add device_add BUS_NOTIFY_ADD_DEVICE notifier chain iommu_bus_notifier amd_iommu_add_device # iommu_ops.add_device init_iommu_group iommu_group_get_for_dev iommu_group_add_device __iommu_attach_device amd_iommu_attach_device # iommu_ops.attach_device attach_device pci_enable_ats mutex_lock(dev->sriov->lock) # deadlock There's no reason to delay allocating the pci_ats struct, and if we allocate it for each device at enumeration-time, there's no need for locking in pci_enable_ats(). Allocate pci_ats struct during enumeration, when we initialize other capabilities. Note that this implementation requires ATS to be enabled on the PF first, before on any of the VFs because the PF controls the STU for all the VFs. Link: http://permalink.gmane.org/gmane.linux.kernel.iommu/9433 Reported-by: Gregor Dick Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index a8099d4..2026f53 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -17,7 +17,7 @@ #include "pci.h" -static int ats_alloc_one(struct pci_dev *dev, int ps) +static void ats_alloc_one(struct pci_dev *dev) { int pos; u16 cap; @@ -25,20 +25,19 @@ static int ats_alloc_one(struct pci_dev *dev, int ps) pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); if (!pos) - return -ENODEV; + return; ats = kzalloc(sizeof(*ats), GFP_KERNEL); - if (!ats) - return -ENOMEM; + if (!ats) { + dev_warn(&dev->dev, "can't allocate space for ATS state\n"); + return; + } ats->pos = pos; - ats->stu = ps; pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP; dev->ats = ats; - - return 0; } static void ats_free_one(struct pci_dev *dev) @@ -47,6 +46,16 @@ static void ats_free_one(struct pci_dev *dev) dev->ats = NULL; } +void pci_ats_init(struct pci_dev *dev) +{ + ats_alloc_one(dev); +} + +void pci_ats_free(struct pci_dev *dev) +{ + ats_free_one(dev); +} + /** * pci_enable_ats - enable the ATS capability * @dev: the PCI device @@ -56,43 +65,35 @@ static void ats_free_one(struct pci_dev *dev) */ int pci_enable_ats(struct pci_dev *dev, int ps) { - int rc; u16 ctrl; BUG_ON(dev->ats && dev->ats->is_enabled); + if (!dev->ats) + return -EINVAL; + if (ps < PCI_ATS_MIN_STU) return -EINVAL; - if (dev->is_physfn || dev->is_virtfn) { - struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; + /* + * Note that enabling ATS on a VF fails unless it's already enabled + * with the same STU on the PF. + */ + ctrl = PCI_ATS_CTRL_ENABLE; + if (dev->is_virtfn) { + struct pci_dev *pdev = dev->physfn; - mutex_lock(&pdev->sriov->lock); - if (pdev->ats) - rc = pdev->ats->stu == ps ? 0 : -EINVAL; - else - rc = ats_alloc_one(pdev, ps); + if (pdev->ats->stu != ps) + return -EINVAL; - if (!rc) - pdev->ats->ref_cnt++; - mutex_unlock(&pdev->sriov->lock); - if (rc) - return rc; - } - - if (!dev->is_physfn) { - rc = ats_alloc_one(dev, ps); - if (rc) - return rc; + atomic_inc(&pdev->ats->ref_cnt); /* count enabled VFs */ + } else { + dev->ats->stu = ps; + ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU); } - - ctrl = PCI_ATS_CTRL_ENABLE; - if (!dev->is_virtfn) - ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); dev->ats->is_enabled = 1; - return 0; } EXPORT_SYMBOL_GPL(pci_enable_ats); @@ -107,24 +108,20 @@ void pci_disable_ats(struct pci_dev *dev) BUG_ON(!dev->ats || !dev->ats->is_enabled); + if (atomic_read(&dev->ats->ref_cnt)) + return; /* VFs still enabled */ + + if (dev->is_virtfn) { + struct pci_dev *pdev = dev->physfn; + + atomic_dec(&pdev->ats->ref_cnt); + } + pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); ctrl &= ~PCI_ATS_CTRL_ENABLE; pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); dev->ats->is_enabled = 0; - - if (dev->is_physfn || dev->is_virtfn) { - struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; - - mutex_lock(&pdev->sriov->lock); - pdev->ats->ref_cnt--; - if (!pdev->ats->ref_cnt) - ats_free_one(pdev); - mutex_unlock(&pdev->sriov->lock); - } - - if (!dev->is_physfn) - ats_free_one(dev); } EXPORT_SYMBOL_GPL(pci_disable_ats); @@ -140,7 +137,6 @@ void pci_restore_ats_state(struct pci_dev *dev) ctrl = PCI_ATS_CTRL_ENABLE; if (!dev->is_virtfn) ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU); - pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); } EXPORT_SYMBOL_GPL(pci_restore_ats_state); @@ -159,23 +155,13 @@ EXPORT_SYMBOL_GPL(pci_restore_ats_state); */ int pci_ats_queue_depth(struct pci_dev *dev) { - int pos; - u16 cap; - if (dev->is_virtfn) return 0; if (dev->ats) return dev->ats->qdep; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); - if (!pos) - return -ENODEV; - - pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); - - return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : - PCI_ATS_MAX_QDEP; + return -ENODEV; } EXPORT_SYMBOL_GPL(pci_ats_queue_depth); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cefd636..c206398 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1540,6 +1540,9 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Single Root I/O Virtualization */ pci_iov_init(dev); + /* Address Translation Services */ + pci_ats_init(dev); + /* Enable ACS P2P upstream forwarding */ pci_enable_acs(dev); } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 8a280e9..27617b8 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -26,6 +26,7 @@ static void pci_stop_dev(struct pci_dev *dev) dev->is_added = 0; } + pci_ats_free(dev); if (dev->bus->self) pcie_aspm_exit_link_state(dev); } diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 7203178..e2dcc2f 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -8,7 +8,7 @@ struct pci_ats { int pos; /* capability position */ int stu; /* Smallest Translation Unit */ int qdep; /* Invalidate Queue Depth */ - int ref_cnt; /* Physical Function reference count */ + atomic_t ref_cnt; /* number of VFs with ATS enabled */ unsigned int is_enabled:1; /* Enable bit is set */ }; diff --git a/include/linux/pci.h b/include/linux/pci.h index 8a0321a..1817819 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1294,6 +1294,15 @@ int ht_create_irq(struct pci_dev *dev, int idx); void ht_destroy_irq(unsigned int irq); #endif /* CONFIG_HT_IRQ */ +#ifdef CONFIG_PCI_ATS +/* Address Translation Service */ +void pci_ats_init(struct pci_dev *dev); +void pci_ats_free(struct pci_dev *dev); +#else +static inline void pci_ats_init(struct pci_dev *dev) { } +static inline void pci_ats_free(struct pci_dev *dev) { } +#endif + void pci_cfg_access_lock(struct pci_dev *dev); bool pci_cfg_access_trylock(struct pci_dev *dev); void pci_cfg_access_unlock(struct pci_dev *dev); -- cgit v0.10.2 From d544d75ac96aa1b0a8a378826626a0fbd8ce4380 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:15:19 -0500 Subject: PCI: Embed ATS info directly into struct pci_dev The pci_ats struct is small and will get smaller, so I don't think it's worth allocating it separately from the pci_dev struct. Embed the ATS fields directly into struct pci_dev. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 2026f53..690ae6e 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -21,29 +21,15 @@ static void ats_alloc_one(struct pci_dev *dev) { int pos; u16 cap; - struct pci_ats *ats; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); if (!pos) return; - ats = kzalloc(sizeof(*ats), GFP_KERNEL); - if (!ats) { - dev_warn(&dev->dev, "can't allocate space for ATS state\n"); - return; - } - - ats->pos = pos; - pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); - ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : + dev->ats_cap = pos; + pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap); + dev->ats_qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP; - dev->ats = ats; -} - -static void ats_free_one(struct pci_dev *dev) -{ - kfree(dev->ats); - dev->ats = NULL; } void pci_ats_init(struct pci_dev *dev) @@ -51,11 +37,6 @@ void pci_ats_init(struct pci_dev *dev) ats_alloc_one(dev); } -void pci_ats_free(struct pci_dev *dev) -{ - ats_free_one(dev); -} - /** * pci_enable_ats - enable the ATS capability * @dev: the PCI device @@ -67,9 +48,9 @@ int pci_enable_ats(struct pci_dev *dev, int ps) { u16 ctrl; - BUG_ON(dev->ats && dev->ats->is_enabled); + BUG_ON(dev->ats_cap && dev->ats_enabled); - if (!dev->ats) + if (!dev->ats_cap) return -EINVAL; if (ps < PCI_ATS_MIN_STU) @@ -83,17 +64,17 @@ int pci_enable_ats(struct pci_dev *dev, int ps) if (dev->is_virtfn) { struct pci_dev *pdev = dev->physfn; - if (pdev->ats->stu != ps) + if (pdev->ats_stu != ps) return -EINVAL; - atomic_inc(&pdev->ats->ref_cnt); /* count enabled VFs */ + atomic_inc(&pdev->ats_ref_cnt); /* count enabled VFs */ } else { - dev->ats->stu = ps; - ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU); + dev->ats_stu = ps; + ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU); } - pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); + pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); - dev->ats->is_enabled = 1; + dev->ats_enabled = 1; return 0; } EXPORT_SYMBOL_GPL(pci_enable_ats); @@ -106,22 +87,22 @@ void pci_disable_ats(struct pci_dev *dev) { u16 ctrl; - BUG_ON(!dev->ats || !dev->ats->is_enabled); + BUG_ON(!dev->ats_cap || !dev->ats_enabled); - if (atomic_read(&dev->ats->ref_cnt)) + if (atomic_read(&dev->ats_ref_cnt)) return; /* VFs still enabled */ if (dev->is_virtfn) { struct pci_dev *pdev = dev->physfn; - atomic_dec(&pdev->ats->ref_cnt); + atomic_dec(&pdev->ats_ref_cnt); } - pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); + pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, &ctrl); ctrl &= ~PCI_ATS_CTRL_ENABLE; - pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); + pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); - dev->ats->is_enabled = 0; + dev->ats_enabled = 0; } EXPORT_SYMBOL_GPL(pci_disable_ats); @@ -136,8 +117,8 @@ void pci_restore_ats_state(struct pci_dev *dev) ctrl = PCI_ATS_CTRL_ENABLE; if (!dev->is_virtfn) - ctrl |= PCI_ATS_CTRL_STU(dev->ats->stu - PCI_ATS_MIN_STU); - pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); + ctrl |= PCI_ATS_CTRL_STU(dev->ats_stu - PCI_ATS_MIN_STU); + pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl); } EXPORT_SYMBOL_GPL(pci_restore_ats_state); @@ -158,8 +139,8 @@ int pci_ats_queue_depth(struct pci_dev *dev) if (dev->is_virtfn) return 0; - if (dev->ats) - return dev->ats->qdep; + if (dev->ats_cap) + return dev->ats_qdep; return -ENODEV; } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 27617b8..8a280e9 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -26,7 +26,6 @@ static void pci_stop_dev(struct pci_dev *dev) dev->is_added = 0; } - pci_ats_free(dev); if (dev->bus->self) pcie_aspm_exit_link_state(dev); } diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index e2dcc2f..5d81d47 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -4,14 +4,6 @@ #include /* Address Translation Service */ -struct pci_ats { - int pos; /* capability position */ - int stu; /* Smallest Translation Unit */ - int qdep; /* Invalidate Queue Depth */ - atomic_t ref_cnt; /* number of VFs with ATS enabled */ - unsigned int is_enabled:1; /* Enable bit is set */ -}; - #ifdef CONFIG_PCI_ATS int pci_enable_ats(struct pci_dev *dev, int ps); @@ -26,7 +18,7 @@ int pci_ats_queue_depth(struct pci_dev *dev); */ static inline int pci_ats_enabled(struct pci_dev *dev) { - return dev->ats && dev->ats->is_enabled; + return dev->ats_cap && dev->ats_enabled; } #else /* CONFIG_PCI_ATS */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 1817819..8bc16b5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -343,6 +343,7 @@ struct pci_dev { unsigned int msi_enabled:1; unsigned int msix_enabled:1; unsigned int ari_enabled:1; /* ARI forwarding */ + unsigned int ats_enabled:1; /* Address Translation Service */ unsigned int is_managed:1; unsigned int needs_freset:1; /* Dev requires fundamental reset */ unsigned int state_saved:1; @@ -375,7 +376,10 @@ struct pci_dev { struct pci_sriov *sriov; /* SR-IOV capability related */ struct pci_dev *physfn; /* the PF this VF is associated with */ }; - struct pci_ats *ats; /* Address Translation Service */ + int ats_cap; /* ATS Capability offset */ + int ats_stu; /* ATS Smallest Translation Unit */ + int ats_qdep; /* ATS Invalidate Queue Depth */ + atomic_t ats_ref_cnt; /* number of VFs with ATS enabled */ #endif phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */ size_t romlen; /* Length of ROM if it's not from the BAR */ @@ -1297,10 +1301,8 @@ void ht_destroy_irq(unsigned int irq); #ifdef CONFIG_PCI_ATS /* Address Translation Service */ void pci_ats_init(struct pci_dev *dev); -void pci_ats_free(struct pci_dev *dev); #else static inline void pci_ats_init(struct pci_dev *dev) { } -static inline void pci_ats_free(struct pci_dev *dev) { } #endif void pci_cfg_access_lock(struct pci_dev *dev); -- cgit v0.10.2 From 67930995d7fb8ae7d2078822b563010b289ace2e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:27:34 -0500 Subject: PCI: Reduce size of ATS structure elements The extended capabilities list is linked with 12-bit pointers, and the ATS Smallest Translation Unit and Invalidate Queue Depth fields are both 5 bits. Use u16 and u8 to hold the extended capability address and the stu and qdep values. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/include/linux/pci.h b/include/linux/pci.h index 8bc16b5..238b77e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -376,9 +376,9 @@ struct pci_dev { struct pci_sriov *sriov; /* SR-IOV capability related */ struct pci_dev *physfn; /* the PF this VF is associated with */ }; - int ats_cap; /* ATS Capability offset */ - int ats_stu; /* ATS Smallest Translation Unit */ - int ats_qdep; /* ATS Invalidate Queue Depth */ + u16 ats_cap; /* ATS Capability offset */ + u8 ats_stu; /* ATS Smallest Translation Unit */ + u8 ats_qdep; /* ATS Invalidate Queue Depth */ atomic_t ats_ref_cnt; /* number of VFs with ATS enabled */ #endif phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */ -- cgit v0.10.2 From 3c765399524308ab36777a443ce77e19810a97d7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:30:26 -0500 Subject: PCI: Rationalize pci_ats_queue_depth() error checking We previously returned -ENODEV for devices that don't support ATS (except that we always returned 0 for VFs, whether or not they support ATS). For consistency, always return -EINVAL (not -ENODEV) if the device doesn't support ATS. Return zero for VFs that support ATS. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 690ae6e..9a98b3a 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -136,13 +136,13 @@ EXPORT_SYMBOL_GPL(pci_restore_ats_state); */ int pci_ats_queue_depth(struct pci_dev *dev) { + if (!dev->ats_cap) + return -EINVAL; + if (dev->is_virtfn) return 0; - if (dev->ats_cap) - return dev->ats_qdep; - - return -ENODEV; + return dev->ats_qdep; } EXPORT_SYMBOL_GPL(pci_ats_queue_depth); -- cgit v0.10.2 From afdd596c42c4540308eae9f4c685d9bccc525cfc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:35:18 -0500 Subject: PCI: Inline the ATS setup code into pci_ats_init() The ATS setup code in ats_alloc_one() is only used by pci_ats_init(), so inline it there. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 9a98b3a..95905f3 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -17,7 +17,7 @@ #include "pci.h" -static void ats_alloc_one(struct pci_dev *dev) +void pci_ats_init(struct pci_dev *dev) { int pos; u16 cap; @@ -32,11 +32,6 @@ static void ats_alloc_one(struct pci_dev *dev) PCI_ATS_MAX_QDEP; } -void pci_ats_init(struct pci_dev *dev) -{ - ats_alloc_one(dev); -} - /** * pci_enable_ats - enable the ATS capability * @dev: the PCI device -- cgit v0.10.2 From c39127dbaf6c267f18d1cac14e2b0ecbfaa52d63 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:38:13 -0500 Subject: PCI: Use pci_physfn() rather than looking up physfn by hand Use the pci_physfn() helper rather than looking up physfn by hand. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 95905f3..0b5b0ed 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -42,6 +42,7 @@ void pci_ats_init(struct pci_dev *dev) int pci_enable_ats(struct pci_dev *dev, int ps) { u16 ctrl; + struct pci_dev *pdev; BUG_ON(dev->ats_cap && dev->ats_enabled); @@ -57,8 +58,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps) */ ctrl = PCI_ATS_CTRL_ENABLE; if (dev->is_virtfn) { - struct pci_dev *pdev = dev->physfn; - + pdev = pci_physfn(dev); if (pdev->ats_stu != ps) return -EINVAL; @@ -80,6 +80,7 @@ EXPORT_SYMBOL_GPL(pci_enable_ats); */ void pci_disable_ats(struct pci_dev *dev) { + struct pci_dev *pdev; u16 ctrl; BUG_ON(!dev->ats_cap || !dev->ats_enabled); @@ -88,8 +89,7 @@ void pci_disable_ats(struct pci_dev *dev) return; /* VFs still enabled */ if (dev->is_virtfn) { - struct pci_dev *pdev = dev->physfn; - + pdev = pci_physfn(dev); atomic_dec(&pdev->ats_ref_cnt); } -- cgit v0.10.2 From a021f3019db7b40f0fe85b0a64c6be21e3665fbf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:43:27 -0500 Subject: PCI: Clean up ATS error handling There's no need to BUG() if we enable ATS when it's already enabled. We don't need to BUG() when disabling ATS on a device that doesn't support ATS or if it's already disabled. If ATS is enabled, certainly we found an ATS capability in the past, so it should still be there now. Clean up these error paths. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 0b5b0ed..9355f75 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -44,11 +44,12 @@ int pci_enable_ats(struct pci_dev *dev, int ps) u16 ctrl; struct pci_dev *pdev; - BUG_ON(dev->ats_cap && dev->ats_enabled); - if (!dev->ats_cap) return -EINVAL; + if (WARN_ON(pci_ats_enabled(dev))) + return -EBUSY; + if (ps < PCI_ATS_MIN_STU) return -EINVAL; @@ -83,7 +84,8 @@ void pci_disable_ats(struct pci_dev *dev) struct pci_dev *pdev; u16 ctrl; - BUG_ON(!dev->ats_cap || !dev->ats_enabled); + if (WARN_ON(!pci_ats_enabled(dev))) + return; if (atomic_read(&dev->ats_ref_cnt)) return; /* VFs still enabled */ @@ -107,8 +109,6 @@ void pci_restore_ats_state(struct pci_dev *dev) if (!pci_ats_enabled(dev)) return; - if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS)) - BUG(); ctrl = PCI_ATS_CTRL_ENABLE; if (!dev->is_virtfn) -- cgit v0.10.2 From ff9bee895c4d11a519a6b2c49451376025a6af4e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jul 2015 15:55:48 -0500 Subject: PCI: Move ATS declarations to linux/pci.h so they're all together Move ATS declarations to linux/pci.h so they're all in one place. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 5d81d47..57e0b82 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -3,47 +3,6 @@ #include -/* Address Translation Service */ -#ifdef CONFIG_PCI_ATS - -int pci_enable_ats(struct pci_dev *dev, int ps); -void pci_disable_ats(struct pci_dev *dev); -int pci_ats_queue_depth(struct pci_dev *dev); - -/** - * pci_ats_enabled - query the ATS status - * @dev: the PCI device - * - * Returns 1 if ATS capability is enabled, or 0 if not. - */ -static inline int pci_ats_enabled(struct pci_dev *dev) -{ - return dev->ats_cap && dev->ats_enabled; -} - -#else /* CONFIG_PCI_ATS */ - -static inline int pci_enable_ats(struct pci_dev *dev, int ps) -{ - return -ENODEV; -} - -static inline void pci_disable_ats(struct pci_dev *dev) -{ -} - -static inline int pci_ats_queue_depth(struct pci_dev *dev) -{ - return -ENODEV; -} - -static inline int pci_ats_enabled(struct pci_dev *dev) -{ - return 0; -} - -#endif /* CONFIG_PCI_ATS */ - #ifdef CONFIG_PCI_PRI int pci_enable_pri(struct pci_dev *pdev, u32 reqs); diff --git a/include/linux/pci.h b/include/linux/pci.h index 238b77e..307f96a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1301,8 +1301,16 @@ void ht_destroy_irq(unsigned int irq); #ifdef CONFIG_PCI_ATS /* Address Translation Service */ void pci_ats_init(struct pci_dev *dev); +int pci_enable_ats(struct pci_dev *dev, int ps); +void pci_disable_ats(struct pci_dev *dev); +int pci_ats_queue_depth(struct pci_dev *dev); +static inline int pci_ats_enabled(struct pci_dev *dev) { return dev->ats_cap && dev->ats_enabled; } #else -static inline void pci_ats_init(struct pci_dev *dev) { } +static inline void pci_ats_init(struct pci_dev *d) { } +static inline int pci_enable_ats(struct pci_dev *d, int ps) { return -ENODEV; } +static inline void pci_disable_ats(struct pci_dev *d) { } +static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } +static inline int pci_ats_enabled(struct pci_dev *d) { return 0; } #endif void pci_cfg_access_lock(struct pci_dev *dev); -- cgit v0.10.2 From a71f938f3a9a7bc879296cd34ecae9effe5edf3f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 20 Jul 2015 09:24:32 -0500 Subject: PCI: Stop caching ATS Invalidate Queue Depth Stop caching the Invalidate Queue Depth in struct pci_dev. pci_ats_queue_depth() is typically called only once per device, and it returns a fixed value per-device, so callers who need the value frequently can cache it themselves. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 9355f75..ceda7dc 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -20,16 +20,12 @@ void pci_ats_init(struct pci_dev *dev) { int pos; - u16 cap; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); if (!pos) return; dev->ats_cap = pos; - pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap); - dev->ats_qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : - PCI_ATS_MAX_QDEP; } /** @@ -131,13 +127,16 @@ EXPORT_SYMBOL_GPL(pci_restore_ats_state); */ int pci_ats_queue_depth(struct pci_dev *dev) { + u16 cap; + if (!dev->ats_cap) return -EINVAL; if (dev->is_virtfn) return 0; - return dev->ats_qdep; + pci_read_config_word(dev, dev->ats_cap + PCI_ATS_CAP, &cap); + return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : PCI_ATS_MAX_QDEP; } EXPORT_SYMBOL_GPL(pci_ats_queue_depth); diff --git a/include/linux/pci.h b/include/linux/pci.h index 307f96a..4b484fd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -378,7 +378,6 @@ struct pci_dev { }; u16 ats_cap; /* ATS Capability offset */ u8 ats_stu; /* ATS Smallest Translation Unit */ - u8 ats_qdep; /* ATS Invalidate Queue Depth */ atomic_t ats_ref_cnt; /* number of VFs with ATS enabled */ #endif phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */ -- cgit v0.10.2 From f7ef1340bb501717372a39f4807d0ad519ebd432 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 20 Jul 2015 09:23:37 -0500 Subject: PCI: Remove pci_ats_enabled() Remove pci_ats_enabled(). There are no callers outside the ATS code itself. We don't need to check ats_cap, because if we don't find an ATS capability, we'll never set ats_enabled. Signed-off-by: Bjorn Helgaas Reviewed-by: Joerg Roedel diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index ceda7dc..eeb9fb2 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -43,7 +43,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps) if (!dev->ats_cap) return -EINVAL; - if (WARN_ON(pci_ats_enabled(dev))) + if (WARN_ON(dev->ats_enabled)) return -EBUSY; if (ps < PCI_ATS_MIN_STU) @@ -80,7 +80,7 @@ void pci_disable_ats(struct pci_dev *dev) struct pci_dev *pdev; u16 ctrl; - if (WARN_ON(!pci_ats_enabled(dev))) + if (WARN_ON(!dev->ats_enabled)) return; if (atomic_read(&dev->ats_ref_cnt)) @@ -103,7 +103,7 @@ void pci_restore_ats_state(struct pci_dev *dev) { u16 ctrl; - if (!pci_ats_enabled(dev)) + if (!dev->ats_enabled) return; ctrl = PCI_ATS_CTRL_ENABLE; diff --git a/include/linux/pci.h b/include/linux/pci.h index 4b484fd..806da76 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1303,13 +1303,11 @@ void pci_ats_init(struct pci_dev *dev); int pci_enable_ats(struct pci_dev *dev, int ps); void pci_disable_ats(struct pci_dev *dev); int pci_ats_queue_depth(struct pci_dev *dev); -static inline int pci_ats_enabled(struct pci_dev *dev) { return dev->ats_cap && dev->ats_enabled; } #else static inline void pci_ats_init(struct pci_dev *d) { } static inline int pci_enable_ats(struct pci_dev *d, int ps) { return -ENODEV; } static inline void pci_disable_ats(struct pci_dev *d) { } static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } -static inline int pci_ats_enabled(struct pci_dev *d) { return 0; } #endif void pci_cfg_access_lock(struct pci_dev *dev); -- cgit v0.10.2