Re: [PATCH v5 3/3] PCI: Introduce the disable_acs_redir parameter

From: Alex Williamson
Date: Fri Jul 06 2018 - 18:57:07 EST


On Wed, 27 Jun 2018 11:46:50 -0600
Logan Gunthorpe <logang@xxxxxxxxxxxx> wrote:

> In order to support P2P traffic on a segment of the PCI hierarchy,
> we must be able to disable the ACS redirect bits for select
> PCI bridges. The bridges must be selected before the devices are
> discovered by the kernel and the IOMMU groups created. Therefore,
> a kernel command line parameter is created to specify devices
> which must have their ACS bits disabled.
>
> The new parameter takes a list of devices separated by a semicolon.
> Each device specified will have it's ACS redirect bits disabled.
> This is similar to the existing 'resource_alignment' parameter.
>
> The ACS Request P2P Request Redirect, P2P Completion Redirect and P2P
> Egress Control bits are disabled which is sufficient to always allow
> passing P2P traffic uninterrupted. The bits are set after the kernel
> (optionally) enables the ACS bits itself. It is also done regardless of
> whether the kernel sets the bits or not seeing some BIOS firmware is known
> to set the bits on boot.
>
> Signed-off-by: Logan Gunthorpe <logang@xxxxxxxxxxxx>
> Reviewed-by: Stephen Bates <sbates@xxxxxxxxxxxx>
> Acked-by: Christian KÃnig <christian.koenig@xxxxxxx>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 9 ++++
> drivers/pci/pci.c | 71 ++++++++++++++++++++++++-
> 2 files changed, 78 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index a69947d9e14e..02634c4ab181 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -3192,6 +3192,15 @@
> Adding the window is slightly risky (it may
> conflict with unreported devices), so this
> taints the kernel.
> + disable_acs_redir=<pci_dev>[; ...]
> + Specify one or more PCI devices (in the format
> + specified above) separated by semicolons.
> + Each device specified will have the PCI ACS
> + redirect capabilities forced off which will
> + allow P2P traffic between devices through
> + bridges without forcing it upstream. Note:
> + this removes isolation between devices and
> + will make the IOMMU groups less granular.
>
> pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power
> Management.
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 59638075b4df..079f7c911e09 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -2983,6 +2983,61 @@ 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;
> +
> + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
> + if (!pos)
> + 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 capabilites
> * @dev: the PCI device
> @@ -3022,12 +3077,22 @@ static void pci_std_enable_acs(struct pci_dev *dev)
> void pci_enable_acs(struct pci_dev *dev)
> {
> if (!pci_acs_enable)
> - return;
> + goto disable_acs_redir;
>
> if (!pci_dev_specific_enable_acs(dev))
> - return;
> + goto disable_acs_redir;
>
> pci_std_enable_acs(dev);
> +
> +disable_acs_redir:
> + /*
> + * Note: pci_disable_acs_redir() must be called even if
> + * ACS is not enabled by the kernel because the firmware
> + * may have unexpectedly set the flags. 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)
> @@ -5967,6 +6032,8 @@ static int __init pci_setup(char *str)
> pcie_bus_config = PCIE_BUS_PEER2PEER;
> } else if (!strncmp(str, "pcie_scan_all", 13)) {
> pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
> + } else if (!strncmp(str, "disable_acs_redir=", 18)) {
> + disable_acs_redir_param = str + 18;
> } else {
> printk(KERN_ERR "PCI: Unknown option `%s'\n",
> str);

If we lived in a perfect world where vendors actually implemented ACS
correctly, this looks fine, I approve. But take a look at the quirks
we call out to via pci_dev_specific_enable_acs(). Both of these are
for Intel root ports, in one case we're twiddling ACS comparable
features in the device, in the other Intel has decided specs don't
matter and used dwords rather than words for the ACS capability and
control registers. Is disabling redirect via device specific means,
then attempting to re-enable via standard mechanisms perhaps not the
right approach here? Maybe if we determine a device is slated to
enable p2p we shouldn't call the device specific enabler. The
non-standard ACS implementation on Intel root ports is still
troublesome though, I wonder if we need a more generic approach for
those...

Maybe we track if we enabled ACS via a device specific quirk and
minimally print an incompatibility error if it's also specified for
disable_acs_redir? Thanks,

Alex