mirror of
https://github.com/torvalds/linux.git
synced 2024-12-11 13:41:55 +00:00
Merge branch 'pci/enumeration'
- Fix pci_cfg_wait queue locking problem (Xiang Zheng, Bjorn Helgaas) - Keep device in system even if driver attach fails (Rajat Jain) - Cache ACS capability offset in device (Rajat Jain) - Treat "external-facing" devices themselves as internal, not external (Rajat Jain) - Announce device after early fixups (Tiezhu Yang) * pci/enumeration: PCI: Announce device after early fixups PCI: Treat "external-facing" devices themselves as internal PCI: Cache ACS capability offset in device PCI: Reorder pci_enable_acs() and dependencies PCI: Add device even if driver attach failed PCI: Fix pci_cfg_wait queue locking problem
This commit is contained in:
commit
3f906da760
@ -4730,12 +4730,12 @@ const struct attribute_group *intel_iommu_groups[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static inline bool has_untrusted_dev(void)
|
||||
static inline bool has_external_pci(void)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
for_each_pci_dev(pdev)
|
||||
if (pdev->untrusted)
|
||||
if (pdev->external_facing)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -4743,7 +4743,7 @@ static inline bool has_untrusted_dev(void)
|
||||
|
||||
static int __init platform_optin_force_iommu(void)
|
||||
{
|
||||
if (!dmar_platform_optin() || no_platform_optin || !has_untrusted_dev())
|
||||
if (!dmar_platform_optin() || no_platform_optin || !has_external_pci())
|
||||
return 0;
|
||||
|
||||
if (no_iommu || dmar_disabled)
|
||||
|
@ -204,17 +204,13 @@ EXPORT_SYMBOL(pci_bus_set_ops);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(pci_cfg_wait);
|
||||
|
||||
static noinline void pci_wait_cfg(struct pci_dev *dev)
|
||||
__must_hold(&pci_lock)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
__add_wait_queue(&pci_cfg_wait, &wait);
|
||||
do {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
raw_spin_unlock_irq(&pci_lock);
|
||||
schedule();
|
||||
wait_event(pci_cfg_wait, !dev->block_cfg_access);
|
||||
raw_spin_lock_irq(&pci_lock);
|
||||
} while (dev->block_cfg_access);
|
||||
__remove_wait_queue(&pci_cfg_wait, &wait);
|
||||
}
|
||||
|
||||
/* Returns 0 on success, negative values indicate error. */
|
||||
|
@ -322,12 +322,8 @@ void pci_bus_add_device(struct pci_dev *dev)
|
||||
|
||||
dev->match_driver = true;
|
||||
retval = device_attach(&dev->dev);
|
||||
if (retval < 0 && retval != -EPROBE_DEFER) {
|
||||
if (retval < 0 && retval != -EPROBE_DEFER)
|
||||
pci_warn(dev, "device attach failed (%d)\n", retval);
|
||||
pci_proc_detach_device(dev);
|
||||
pci_remove_sysfs_dev_files(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
pci_dev_assign_added(dev, true);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ void pci_set_bus_of_node(struct pci_bus *bus)
|
||||
} else {
|
||||
node = of_node_get(bus->self->dev.of_node);
|
||||
if (node && of_property_read_bool(node, "external-facing"))
|
||||
bus->self->untrusted = true;
|
||||
bus->self->external_facing = true;
|
||||
}
|
||||
|
||||
bus->dev.of_node = node;
|
||||
|
@ -253,7 +253,7 @@ static int pci_bridge_has_acs_redir(struct pci_dev *pdev)
|
||||
int pos;
|
||||
u16 ctrl;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
|
||||
pos = pdev->acs_cap;
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
|
@ -1213,7 +1213,7 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
|
||||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
static void pci_acpi_set_untrusted(struct pci_dev *dev)
|
||||
static void pci_acpi_set_external_facing(struct pci_dev *dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
@ -1224,11 +1224,10 @@ static void pci_acpi_set_untrusted(struct pci_dev *dev)
|
||||
|
||||
/*
|
||||
* These root ports expose PCIe (including DMA) outside of the
|
||||
* system so make sure we treat them and everything behind as
|
||||
* untrusted.
|
||||
* system. Everything downstream from them is external.
|
||||
*/
|
||||
if (val)
|
||||
dev->untrusted = 1;
|
||||
dev->external_facing = 1;
|
||||
}
|
||||
|
||||
static void pci_acpi_setup(struct device *dev)
|
||||
@ -1240,7 +1239,7 @@ static void pci_acpi_setup(struct device *dev)
|
||||
return;
|
||||
|
||||
pci_acpi_optimize_delay(pci_dev, adev->handle);
|
||||
pci_acpi_set_untrusted(pci_dev);
|
||||
pci_acpi_set_external_facing(pci_dev);
|
||||
pci_acpi_add_edr_notifier(pci_dev);
|
||||
|
||||
pci_acpi_add_pm_notifier(adev, pci_dev);
|
||||
|
@ -777,6 +777,133 @@ int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_acs_enable;
|
||||
|
||||
/**
|
||||
* pci_request_acs - ask for ACS to be enabled if supported
|
||||
*/
|
||||
void pci_request_acs(void)
|
||||
{
|
||||
pci_acs_enable = 1;
|
||||
}
|
||||
|
||||
static const char *disable_acs_redir_param;
|
||||
|
||||
/**
|
||||
* pci_disable_acs_redir - disable ACS redirect capabilities
|
||||
* @dev: the PCI device
|
||||
*
|
||||
* For only devices specified in the disable_acs_redir parameter.
|
||||
*/
|
||||
static void pci_disable_acs_redir(struct pci_dev *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *p;
|
||||
int pos;
|
||||
u16 ctrl;
|
||||
|
||||
if (!disable_acs_redir_param)
|
||||
return;
|
||||
|
||||
p = disable_acs_redir_param;
|
||||
while (*p) {
|
||||
ret = pci_dev_str_match(dev, p, &p);
|
||||
if (ret < 0) {
|
||||
pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n",
|
||||
disable_acs_redir_param);
|
||||
|
||||
break;
|
||||
} else if (ret == 1) {
|
||||
/* Found a match */
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p != ';' && *p != ',') {
|
||||
/* End of param or invalid format */
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (ret != 1)
|
||||
return;
|
||||
|
||||
if (!pci_dev_specific_disable_acs_redir(dev))
|
||||
return;
|
||||
|
||||
pos = dev->acs_cap;
|
||||
if (!pos) {
|
||||
pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||
|
||||
/* P2P Request & Completion Redirect */
|
||||
ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||
|
||||
pci_info(dev, "disabled ACS redirect\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilities
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
static void pci_std_enable_acs(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u16 cap;
|
||||
u16 ctrl;
|
||||
|
||||
pos = dev->acs_cap;
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||
|
||||
/* Source Validation */
|
||||
ctrl |= (cap & PCI_ACS_SV);
|
||||
|
||||
/* P2P Request Redirect */
|
||||
ctrl |= (cap & PCI_ACS_RR);
|
||||
|
||||
/* P2P Completion Redirect */
|
||||
ctrl |= (cap & PCI_ACS_CR);
|
||||
|
||||
/* Upstream Forwarding */
|
||||
ctrl |= (cap & PCI_ACS_UF);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_enable_acs - enable ACS if hardware support it
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
static void pci_enable_acs(struct pci_dev *dev)
|
||||
{
|
||||
if (!pci_acs_enable)
|
||||
goto disable_acs_redir;
|
||||
|
||||
if (!pci_dev_specific_enable_acs(dev))
|
||||
goto disable_acs_redir;
|
||||
|
||||
pci_std_enable_acs(dev);
|
||||
|
||||
disable_acs_redir:
|
||||
/*
|
||||
* Note: pci_disable_acs_redir() must be called even if ACS was not
|
||||
* enabled by the kernel because it may have been enabled by
|
||||
* platform firmware. So if we are told to disable it, we should
|
||||
* always disable it after setting the kernel's default
|
||||
* preferences.
|
||||
*/
|
||||
pci_disable_acs_redir(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_restore_bars - restore a device's BAR values (e.g. after wake-up)
|
||||
* @dev: PCI device to have its BARs restored
|
||||
@ -3230,139 +3357,12 @@ void pci_configure_ari(struct pci_dev *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_acs_enable;
|
||||
|
||||
/**
|
||||
* pci_request_acs - ask for ACS to be enabled if supported
|
||||
*/
|
||||
void pci_request_acs(void)
|
||||
{
|
||||
pci_acs_enable = 1;
|
||||
}
|
||||
|
||||
static const char *disable_acs_redir_param;
|
||||
|
||||
/**
|
||||
* pci_disable_acs_redir - disable ACS redirect capabilities
|
||||
* @dev: the PCI device
|
||||
*
|
||||
* For only devices specified in the disable_acs_redir parameter.
|
||||
*/
|
||||
static void pci_disable_acs_redir(struct pci_dev *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *p;
|
||||
int pos;
|
||||
u16 ctrl;
|
||||
|
||||
if (!disable_acs_redir_param)
|
||||
return;
|
||||
|
||||
p = disable_acs_redir_param;
|
||||
while (*p) {
|
||||
ret = pci_dev_str_match(dev, p, &p);
|
||||
if (ret < 0) {
|
||||
pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n",
|
||||
disable_acs_redir_param);
|
||||
|
||||
break;
|
||||
} else if (ret == 1) {
|
||||
/* Found a match */
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p != ';' && *p != ',') {
|
||||
/* End of param or invalid format */
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (ret != 1)
|
||||
return;
|
||||
|
||||
if (!pci_dev_specific_disable_acs_redir(dev))
|
||||
return;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
if (!pos) {
|
||||
pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||
|
||||
/* P2P Request & Completion Redirect */
|
||||
ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||
|
||||
pci_info(dev, "disabled ACS redirect\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilities
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
static void pci_std_enable_acs(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u16 cap;
|
||||
u16 ctrl;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||
|
||||
/* Source Validation */
|
||||
ctrl |= (cap & PCI_ACS_SV);
|
||||
|
||||
/* P2P Request Redirect */
|
||||
ctrl |= (cap & PCI_ACS_RR);
|
||||
|
||||
/* P2P Completion Redirect */
|
||||
ctrl |= (cap & PCI_ACS_CR);
|
||||
|
||||
/* Upstream Forwarding */
|
||||
ctrl |= (cap & PCI_ACS_UF);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_enable_acs - enable ACS if hardware support it
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
void pci_enable_acs(struct pci_dev *dev)
|
||||
{
|
||||
if (!pci_acs_enable)
|
||||
goto disable_acs_redir;
|
||||
|
||||
if (!pci_dev_specific_enable_acs(dev))
|
||||
goto disable_acs_redir;
|
||||
|
||||
pci_std_enable_acs(dev);
|
||||
|
||||
disable_acs_redir:
|
||||
/*
|
||||
* Note: pci_disable_acs_redir() must be called even if ACS was not
|
||||
* enabled by the kernel because it may have been enabled by
|
||||
* platform firmware. So if we are told to disable it, we should
|
||||
* always disable it after setting the kernel's default
|
||||
* preferences.
|
||||
*/
|
||||
pci_disable_acs_redir(dev);
|
||||
}
|
||||
|
||||
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
|
||||
{
|
||||
int pos;
|
||||
u16 cap, ctrl;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
|
||||
pos = pdev->acs_cap;
|
||||
if (!pos)
|
||||
return false;
|
||||
|
||||
@ -3487,6 +3487,18 @@ bool pci_acs_path_enabled(struct pci_dev *start,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_acs_init - Initialize ACS if hardware supports it
|
||||
* @dev: the PCI device
|
||||
*/
|
||||
void pci_acs_init(struct pci_dev *dev)
|
||||
{
|
||||
dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
|
||||
if (dev->acs_cap)
|
||||
pci_enable_acs(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_rebar_find_pos - find position of resize ctrl reg for BAR
|
||||
* @pdev: PCI device
|
||||
|
@ -532,7 +532,7 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
||||
return resource_alignment(res);
|
||||
}
|
||||
|
||||
void pci_enable_acs(struct pci_dev *dev);
|
||||
void pci_acs_init(struct pci_dev *dev);
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
|
||||
int pci_dev_specific_enable_acs(struct pci_dev *dev);
|
||||
|
@ -1552,7 +1552,7 @@ static void set_pcie_untrusted(struct pci_dev *dev)
|
||||
* untrusted as well.
|
||||
*/
|
||||
parent = pci_upstream_bridge(dev);
|
||||
if (parent && parent->untrusted)
|
||||
if (parent && (parent->untrusted || parent->external_facing))
|
||||
dev->untrusted = true;
|
||||
}
|
||||
|
||||
@ -1802,9 +1802,6 @@ int pci_setup_device(struct pci_dev *dev)
|
||||
dev->revision = class & 0xff;
|
||||
dev->class = class >> 8; /* upper 3 bytes */
|
||||
|
||||
pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
|
||||
dev->vendor, dev->device, dev->hdr_type, dev->class);
|
||||
|
||||
if (pci_early_dump)
|
||||
early_dump_pci_device(dev);
|
||||
|
||||
@ -1822,6 +1819,9 @@ int pci_setup_device(struct pci_dev *dev)
|
||||
/* Early fixups, before probing the BARs */
|
||||
pci_fixup_device(pci_fixup_early, dev);
|
||||
|
||||
pci_info(dev, "[%04x:%04x] type %02x class %#08x\n",
|
||||
dev->vendor, dev->device, dev->hdr_type, dev->class);
|
||||
|
||||
/* Device class may be changed after fixup */
|
||||
class = dev->class >> 8;
|
||||
|
||||
@ -2390,7 +2390,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
|
||||
pci_ats_init(dev); /* Address Translation Services */
|
||||
pci_pri_init(dev); /* Page Request Interface */
|
||||
pci_pasid_init(dev); /* Process Address Space ID */
|
||||
pci_enable_acs(dev); /* Enable ACS P2P upstream forwarding */
|
||||
pci_acs_init(dev); /* Access Control Services */
|
||||
pci_ptm_init(dev); /* Precision Time Measurement */
|
||||
pci_aer_init(dev); /* Advanced Error Reporting */
|
||||
pci_dpc_init(dev); /* Downstream Port Containment */
|
||||
|
@ -4653,7 +4653,7 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags)
|
||||
if (!pci_quirk_intel_spt_pch_acs_match(dev))
|
||||
return -ENOTTY;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
pos = dev->acs_cap;
|
||||
if (!pos)
|
||||
return -ENOTTY;
|
||||
|
||||
@ -4961,7 +4961,7 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
|
||||
if (!pci_quirk_intel_spt_pch_acs_match(dev))
|
||||
return -ENOTTY;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
pos = dev->acs_cap;
|
||||
if (!pos)
|
||||
return -ENOTTY;
|
||||
|
||||
@ -4988,7 +4988,7 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
|
||||
if (!pci_quirk_intel_spt_pch_acs_match(dev))
|
||||
return -ENOTTY;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||
pos = dev->acs_cap;
|
||||
if (!pos)
|
||||
return -ENOTTY;
|
||||
|
||||
@ -5355,7 +5355,7 @@ int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout)
|
||||
bool found;
|
||||
struct pci_dev *bridge = bus->self;
|
||||
|
||||
pos = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ACS);
|
||||
pos = bridge->acs_cap;
|
||||
|
||||
/* Disable ACS SV before initial config reads */
|
||||
if (pos) {
|
||||
|
@ -432,6 +432,12 @@ struct pci_dev {
|
||||
* mappings to make sure they cannot access arbitrary memory.
|
||||
*/
|
||||
unsigned int untrusted:1;
|
||||
/*
|
||||
* Info from the platform, e.g., ACPI or device tree, may mark a
|
||||
* device as "external-facing". An external-facing device is
|
||||
* itself internal but devices downstream from it are external.
|
||||
*/
|
||||
unsigned int external_facing:1;
|
||||
unsigned int broken_intx_masking:1; /* INTx masking can't be used */
|
||||
unsigned int io_window_1k:1; /* Intel bridge 1K I/O windows */
|
||||
unsigned int irq_managed:1;
|
||||
@ -486,6 +492,7 @@ struct pci_dev {
|
||||
#ifdef CONFIG_PCI_P2PDMA
|
||||
struct pci_p2pdma *p2pdma;
|
||||
#endif
|
||||
u16 acs_cap; /* ACS Capability offset */
|
||||
phys_addr_t rom; /* Physical address if not from BAR */
|
||||
size_t romlen; /* Length if not from BAR */
|
||||
char *driver_override; /* Driver name to force a match */
|
||||
|
Loading…
Reference in New Issue
Block a user