Merge branches 'pci/dpc', 'pci/resource' and 'pci/thunderbolt' into next
* pci/dpc: PCI: Add Downstream Port Containment driver PCI: Add Downstream Port Containment portdrv service type PCI: Widen portdrv service type from 4 bits to 8 bits * pci/resource: alpha/PCI: Call iomem_is_exclusive() for IORESOURCE_MEM, but not IORESOURCE_IO PCI: Supply CPU physical address (not bus address) to iomem_is_exclusive() * pci/thunderbolt: thunderbolt: Fix double free of drom buffer
This commit is contained in:
Коммит
d9322d226f
|
@ -77,10 +77,10 @@ static int pci_mmap_resource(struct kobject *kobj,
|
|||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
|
||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||
return -EINVAL;
|
||||
|
||||
if (iomem_is_exclusive(res->start))
|
||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||
return -EINVAL;
|
||||
|
||||
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
||||
|
|
|
@ -1008,6 +1008,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
|
||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||
return -EINVAL;
|
||||
|
||||
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
||||
WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
|
||||
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
|
||||
|
@ -1024,10 +1027,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||
pci_resource_to_user(pdev, i, res, &start, &end);
|
||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
||||
|
||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
|
||||
return -EINVAL;
|
||||
|
||||
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,3 +81,17 @@ endchoice
|
|||
config PCIE_PME
|
||||
def_bool y
|
||||
depends on PCIEPORTBUS && PM
|
||||
|
||||
config PCIE_DPC
|
||||
tristate "PCIe Downstream Port Containment support"
|
||||
depends on PCIEPORTBUS
|
||||
default n
|
||||
help
|
||||
This enables PCI Express Downstream Port Containment (DPC)
|
||||
driver support. DPC events from Root and Downstream ports
|
||||
will be handled by the DPC driver. If your system doesn't
|
||||
have this capability or you do not want to use this feature,
|
||||
it is safe to answer N.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pcie-dpc.
|
||||
|
|
|
@ -14,3 +14,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
|
|||
obj-$(CONFIG_PCIEAER) += aer/
|
||||
|
||||
obj-$(CONFIG_PCIE_PME) += pme.o
|
||||
|
||||
obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* PCI Express Downstream Port Containment services driver
|
||||
* Copyright (C) 2016 Intel Corp.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pcieport_if.h>
|
||||
|
||||
struct dpc_dev {
|
||||
struct pcie_device *dev;
|
||||
struct work_struct work;
|
||||
int cap_pos;
|
||||
};
|
||||
|
||||
static void dpc_wait_link_inactive(struct pci_dev *pdev)
|
||||
{
|
||||
unsigned long timeout = jiffies + HZ;
|
||||
u16 lnk_status;
|
||||
|
||||
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||
while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
|
||||
!time_after(jiffies, timeout)) {
|
||||
msleep(10);
|
||||
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||
}
|
||||
if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
|
||||
dev_warn(&pdev->dev, "Link state not disabled for DPC event");
|
||||
}
|
||||
|
||||
static void interrupt_event_handler(struct work_struct *work)
|
||||
{
|
||||
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
|
||||
struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
|
||||
struct pci_bus *parent = pdev->subordinate;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
||||
bus_list) {
|
||||
pci_dev_get(dev);
|
||||
pci_stop_and_remove_bus_device(dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
dpc_wait_link_inactive(pdev);
|
||||
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
|
||||
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
|
||||
}
|
||||
|
||||
static irqreturn_t dpc_irq(int irq, void *context)
|
||||
{
|
||||
struct dpc_dev *dpc = (struct dpc_dev *)context;
|
||||
struct pci_dev *pdev = dpc->dev->port;
|
||||
u16 status, source;
|
||||
|
||||
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
|
||||
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID,
|
||||
&source);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
dev_info(&dpc->dev->device, "DPC containment event, status:%#06x source:%#06x\n",
|
||||
status, source);
|
||||
|
||||
if (status & PCI_EXP_DPC_STATUS_TRIGGER) {
|
||||
u16 reason = (status >> 1) & 0x3;
|
||||
|
||||
dev_warn(&dpc->dev->device, "DPC %s triggered, remove downstream devices\n",
|
||||
(reason == 0) ? "unmasked uncorrectable error" :
|
||||
(reason == 1) ? "ERR_NONFATAL" :
|
||||
(reason == 2) ? "ERR_FATAL" : "extended error");
|
||||
schedule_work(&dpc->work);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
|
||||
static int dpc_probe(struct pcie_device *dev)
|
||||
{
|
||||
struct dpc_dev *dpc;
|
||||
struct pci_dev *pdev = dev->port;
|
||||
int status;
|
||||
u16 ctl, cap;
|
||||
|
||||
dpc = kzalloc(sizeof(*dpc), GFP_KERNEL);
|
||||
if (!dpc)
|
||||
return -ENOMEM;
|
||||
|
||||
dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
|
||||
dpc->dev = dev;
|
||||
INIT_WORK(&dpc->work, interrupt_event_handler);
|
||||
set_service_data(dev, dpc);
|
||||
|
||||
status = request_irq(dev->irq, dpc_irq, IRQF_SHARED, "pcie-dpc", dpc);
|
||||
if (status) {
|
||||
dev_warn(&dev->device, "request IRQ%d failed: %d\n", dev->irq,
|
||||
status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
|
||||
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||
|
||||
ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||
|
||||
dev_info(&dev->device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
|
||||
cap & 0xf, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
|
||||
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||
FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), (cap >> 8) & 0xf,
|
||||
FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
|
||||
return status;
|
||||
out:
|
||||
kfree(dpc);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dpc_remove(struct pcie_device *dev)
|
||||
{
|
||||
struct dpc_dev *dpc = get_service_data(dev);
|
||||
struct pci_dev *pdev = dev->port;
|
||||
u16 ctl;
|
||||
|
||||
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||
ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
|
||||
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||
|
||||
free_irq(dev->irq, dpc);
|
||||
kfree(dpc);
|
||||
}
|
||||
|
||||
static struct pcie_port_service_driver dpcdriver = {
|
||||
.name = "dpc",
|
||||
.port_type = PCI_EXP_TYPE_ROOT_PORT | PCI_EXP_TYPE_DOWNSTREAM,
|
||||
.service = PCIE_PORT_SERVICE_DPC,
|
||||
.probe = dpc_probe,
|
||||
.remove = dpc_remove,
|
||||
};
|
||||
|
||||
static int __init dpc_service_init(void)
|
||||
{
|
||||
return pcie_port_service_register(&dpcdriver);
|
||||
}
|
||||
|
||||
static void __exit dpc_service_exit(void)
|
||||
{
|
||||
pcie_port_service_unregister(&dpcdriver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("PCI Express Downstream Port Containment driver");
|
||||
MODULE_AUTHOR("Keith Busch <keith.busch@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
module_init(dpc_service_init);
|
||||
module_exit(dpc_service_exit);
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||
/*
|
||||
* According to the PCI Express Base Specification 2.0, the indices of
|
||||
* the MSI-X table entries used by port services must not exceed 31
|
||||
*/
|
||||
#define PCIE_PORT_MAX_MSIX_ENTRIES 32
|
||||
|
||||
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
|
||||
#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
|
||||
|
||||
extern struct bus_type pcie_port_bus_type;
|
||||
int pcie_port_device_register(struct pci_dev *dev);
|
||||
|
|
|
@ -51,7 +51,7 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
|
|||
|
||||
flags = root->osc_control_set;
|
||||
|
||||
*srv_mask = PCIE_PORT_SERVICE_VC;
|
||||
*srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
||||
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
||||
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
||||
|
|
|
@ -261,7 +261,7 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||
return 0;
|
||||
|
||||
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
||||
| PCIE_PORT_SERVICE_VC;
|
||||
| PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||
if (pci_aer_available())
|
||||
cap_mask |= PCIE_PORT_SERVICE_AER;
|
||||
|
||||
|
@ -307,6 +307,8 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||
*/
|
||||
pcie_pme_interrupt_enable(dev, false);
|
||||
}
|
||||
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
|
||||
services |= PCIE_PORT_SERVICE_DPC;
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -334,7 +336,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
|
|||
device = &pcie->device;
|
||||
device->bus = &pcie_port_bus_type;
|
||||
device->release = release_pcie_device; /* callback to free pcie dev */
|
||||
dev_set_name(device, "%s:pcie%02x",
|
||||
dev_set_name(device, "%s:pcie%03x",
|
||||
pci_name(pdev),
|
||||
get_descriptor_id(pci_pcie_type(pdev), service));
|
||||
device->parent = &pdev->dev;
|
||||
|
|
|
@ -449,6 +449,7 @@ int tb_drom_read(struct tb_switch *sw)
|
|||
return tb_drom_parse_entries(sw);
|
||||
err:
|
||||
kfree(sw->drom);
|
||||
sw->drom = NULL;
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
||||
#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */
|
||||
#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT)
|
||||
#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */
|
||||
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
|
||||
|
||||
struct pcie_device {
|
||||
int irq; /* Service IRQ/MSI/MSI-X Vector */
|
||||
|
|
|
@ -670,7 +670,8 @@
|
|||
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
|
||||
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
||||
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID
|
||||
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
|
||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DPC
|
||||
|
||||
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
||||
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
||||
|
@ -946,4 +947,21 @@
|
|||
#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
|
||||
#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
|
||||
|
||||
/* Downstream Port Containment */
|
||||
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
|
||||
#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
|
||||
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
|
||||
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
|
||||
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
|
||||
|
||||
#define PCI_EXP_DPC_CTL 6 /* DPC control */
|
||||
#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */
|
||||
#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */
|
||||
|
||||
#define PCI_EXP_DPC_STATUS 8 /* DPC Status */
|
||||
#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */
|
||||
#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */
|
||||
|
||||
#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
|
||||
|
||||
#endif /* LINUX_PCI_REGS_H */
|
||||
|
|
Загрузка…
Ссылка в новой задаче