Merge branch 'pci/acs'
- Add ACS quirk for Broadcom BCM5760X NIC, which doesn't allow peer-to-peer transactions between functions, but doesn't advertise ACS support (Ajit Khaparde) - Add "pci=config_acs=" kernel command-line parameter to relax default ACS settings to enable peer-to-peer configurations. Requires expert knowledge of topology and ACS operation (Vidya Sagar) * pci/acs: PCI: Extend ACS configurability PCI: Add ACS quirk for Broadcom BCM5760X NIC
This commit is contained in:
commit
cb43487e5d
@ -4619,6 +4619,38 @@
|
||||
bridges without forcing it upstream. Note:
|
||||
this removes isolation between devices and
|
||||
may put more devices in an IOMMU group.
|
||||
config_acs=
|
||||
Format:
|
||||
<ACS flags>@<pci_dev>[; ...]
|
||||
Specify one or more PCI devices (in the format
|
||||
specified above) optionally prepended with flags
|
||||
and separated by semicolons. The respective
|
||||
capabilities will be enabled, disabled or
|
||||
unchanged based on what is specified in
|
||||
flags.
|
||||
|
||||
ACS Flags is defined as follows:
|
||||
bit-0 : ACS Source Validation
|
||||
bit-1 : ACS Translation Blocking
|
||||
bit-2 : ACS P2P Request Redirect
|
||||
bit-3 : ACS P2P Completion Redirect
|
||||
bit-4 : ACS Upstream Forwarding
|
||||
bit-5 : ACS P2P Egress Control
|
||||
bit-6 : ACS Direct Translated P2P
|
||||
Each bit can be marked as:
|
||||
'0' – force disabled
|
||||
'1' – force enabled
|
||||
'x' – unchanged
|
||||
For example,
|
||||
pci=config_acs=10x
|
||||
would configure all devices that support
|
||||
ACS to enable P2P Request Redirect, disable
|
||||
Translation Blocking, and leave Source
|
||||
Validation unchanged from whatever power-up
|
||||
or firmware set it to.
|
||||
|
||||
Note: this may remove isolation between devices
|
||||
and may put more devices in an IOMMU group.
|
||||
force_floating [S390] Force usage of floating interrupts.
|
||||
nomio [S390] Do not use MIO instructions.
|
||||
norid [S390] ignore the RID field and force use of
|
||||
|
@ -946,30 +946,67 @@ void pci_request_acs(void)
|
||||
}
|
||||
|
||||
static const char *disable_acs_redir_param;
|
||||
static const char *config_acs_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;
|
||||
struct pci_acs {
|
||||
u16 cap;
|
||||
u16 ctrl;
|
||||
u16 fw_ctrl;
|
||||
};
|
||||
|
||||
if (!disable_acs_redir_param)
|
||||
static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps,
|
||||
const char *p, u16 mask, u16 flags)
|
||||
{
|
||||
char *delimit;
|
||||
int ret = 0;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p = disable_acs_redir_param;
|
||||
while (*p) {
|
||||
if (!mask) {
|
||||
/* Check for ACS flags */
|
||||
delimit = strstr(p, "@");
|
||||
if (delimit) {
|
||||
int end;
|
||||
u32 shift = 0;
|
||||
|
||||
end = delimit - p - 1;
|
||||
|
||||
while (end > -1) {
|
||||
if (*(p + end) == '0') {
|
||||
mask |= 1 << shift;
|
||||
shift++;
|
||||
end--;
|
||||
} else if (*(p + end) == '1') {
|
||||
mask |= 1 << shift;
|
||||
flags |= 1 << shift;
|
||||
shift++;
|
||||
end--;
|
||||
} else if ((*(p + end) == 'x') || (*(p + end) == 'X')) {
|
||||
shift++;
|
||||
end--;
|
||||
} else {
|
||||
pci_err(dev, "Invalid ACS flags... Ignoring\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
p = delimit + 1;
|
||||
} else {
|
||||
pci_err(dev, "ACS Flags missing\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | PCI_ACS_CR |
|
||||
PCI_ACS_UF | PCI_ACS_EC | PCI_ACS_DT)) {
|
||||
pci_err(dev, "Invalid ACS flags specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
pr_info_once("PCI: Can't parse ACS command line parameter\n");
|
||||
break;
|
||||
} else if (ret == 1) {
|
||||
/* Found a match */
|
||||
@ -989,56 +1026,38 @@ static void pci_disable_acs_redir(struct pci_dev *dev)
|
||||
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_dbg(dev, "ACS mask = %#06x\n", mask);
|
||||
pci_dbg(dev, "ACS flags = %#06x\n", flags);
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||
/* If mask is 0 then we copy the bit from the firmware setting. */
|
||||
caps->ctrl = (caps->ctrl & ~mask) | (caps->fw_ctrl & mask);
|
||||
caps->ctrl |= flags;
|
||||
|
||||
/* 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_info(dev, "Configured ACS to %#06x\n", caps->ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilities
|
||||
* @dev: the PCI device
|
||||
* @caps: default ACS controls
|
||||
*/
|
||||
static void pci_std_enable_acs(struct pci_dev *dev)
|
||||
static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
|
||||
{
|
||||
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);
|
||||
caps->ctrl |= (caps->cap & PCI_ACS_SV);
|
||||
|
||||
/* P2P Request Redirect */
|
||||
ctrl |= (cap & PCI_ACS_RR);
|
||||
caps->ctrl |= (caps->cap & PCI_ACS_RR);
|
||||
|
||||
/* P2P Completion Redirect */
|
||||
ctrl |= (cap & PCI_ACS_CR);
|
||||
caps->ctrl |= (caps->cap & PCI_ACS_CR);
|
||||
|
||||
/* Upstream Forwarding */
|
||||
ctrl |= (cap & PCI_ACS_UF);
|
||||
caps->ctrl |= (caps->cap & PCI_ACS_UF);
|
||||
|
||||
/* Enable Translation Blocking for external devices and noats */
|
||||
if (pci_ats_disabled() || dev->external_facing || dev->untrusted)
|
||||
ctrl |= (cap & PCI_ACS_TB);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||
caps->ctrl |= (caps->cap & PCI_ACS_TB);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1047,23 +1066,33 @@ static void pci_std_enable_acs(struct pci_dev *dev)
|
||||
*/
|
||||
static void pci_enable_acs(struct pci_dev *dev)
|
||||
{
|
||||
if (!pci_acs_enable)
|
||||
goto disable_acs_redir;
|
||||
struct pci_acs caps;
|
||||
int pos;
|
||||
|
||||
if (!pci_dev_specific_enable_acs(dev))
|
||||
goto disable_acs_redir;
|
||||
pos = dev->acs_cap;
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_std_enable_acs(dev);
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CAP, &caps.cap);
|
||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &caps.ctrl);
|
||||
caps.fw_ctrl = caps.ctrl;
|
||||
|
||||
/* If an iommu is present we start with kernel default caps */
|
||||
if (pci_acs_enable) {
|
||||
if (pci_dev_specific_enable_acs(dev))
|
||||
pci_std_enable_acs(dev, &caps);
|
||||
}
|
||||
|
||||
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.
|
||||
* Always apply caps from the command line, even if there is no iommu.
|
||||
* Trust that the admin has a reason to change the ACS settings.
|
||||
*/
|
||||
pci_disable_acs_redir(dev);
|
||||
__pci_config_acs(dev, &caps, disable_acs_redir_param,
|
||||
PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC,
|
||||
~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC));
|
||||
__pci_config_acs(dev, &caps, config_acs_param, 0, 0);
|
||||
|
||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6840,6 +6869,8 @@ static int __init pci_setup(char *str)
|
||||
pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
|
||||
} else if (!strncmp(str, "disable_acs_redir=", 18)) {
|
||||
disable_acs_redir_param = str + 18;
|
||||
} else if (!strncmp(str, "config_acs=", 11)) {
|
||||
config_acs_param = str + 11;
|
||||
} else {
|
||||
pr_err("PCI: Unknown option `%s'\n", str);
|
||||
}
|
||||
@ -6864,6 +6895,7 @@ static int __init pci_realloc_setup_params(void)
|
||||
resource_alignment_param = kstrdup(resource_alignment_param,
|
||||
GFP_KERNEL);
|
||||
disable_acs_redir_param = kstrdup(disable_acs_redir_param, GFP_KERNEL);
|
||||
config_acs_param = kstrdup(config_acs_param, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5099,6 +5099,10 @@ static const struct pci_dev_acs_enabled {
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1750, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1751, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1752, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1760, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1761, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1762, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0x1763, pci_quirk_mf_endpoint_acs },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs },
|
||||
/* Amazon Annapurna Labs */
|
||||
{ PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, pci_quirk_al_acs },
|
||||
|
Loading…
x
Reference in New Issue
Block a user