On Thu, Oct 01, 2026 at 02:55:14PM +0300, Leon Romanovsky wrote:
> From: Leon Romanovsky <[email protected]>
> 
> calc_map_type_and_dist() reads each divergence port's ACS Control register
> and folds the result into running counters as it goes. Any routing property
> that depends on the kind of TLP being routed would have to be threaded
> through that code, so there is nowhere to put one without reading the
> registers again for each kind.

What is the "one" that there's nowhere to put?  I guess the routing
property?  So this is an optimization to avoid some config reads?

> Collect the two ports' ACS Control values into struct pci_p2pdma_acs_path
> first, then decide from it. pci_p2pdma_route() applies the same rule as
> before: a path routes directly only when both directions do.
> 
> Reviewed-by: Logan Gunthorpe <[email protected]>
> Tested-by: Tushar Dave <[email protected]>
> Signed-off-by: Leon Romanovsky <[email protected]>
> ---
>  drivers/pci/p2pdma.c | 148 
> +++++++++++++++++++++++++++++++++------------------
>  1 file changed, 96 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
> index 550e6c7346ef..841c86be31bb 100644
> --- a/drivers/pci/p2pdma.c
> +++ b/drivers/pci/p2pdma.c
> @@ -553,6 +553,80 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, 
> struct pci_dev *pdev)
>       seq_buf_printf(buf, "%s;", pci_name(pdev));
>  }
>  
> +/*
> + * What the topology walk found out about one provider/client path. Producing
> + * this costs a walk and one config read per divergence port, none of which
> + * depends on the TLP being routed.
> + *
> + * @req_ctrl:        ACS Control of the client-side divergence port. That is 
> the
> + *           first port at which a Request can route toward the peer rather
> + *           than upstream, so it is where the Request controls apply.
> + * @cpl_ctrl:        ACS Control of the provider-side divergence port, 
> likewise for
> + *           the Completions travelling back.
> + * @unreadable:      First port whose ACS Control could not be read, if any.
> + */
> +struct pci_p2pdma_acs_path {
> +     u16 req_ctrl;
> +     u16 cpl_ctrl;
> +     struct pci_dev *unreadable;
> +};
> +
> +/*
> + * Combine both directions into a mapping type. Only a path that routes the
> + * Request and the Completions it generates directly can be programmed with
> + * the peer's bus addresses.
> + */
> +static enum pci_p2pdma_map_type
> +pci_p2pdma_route(const struct pci_p2pdma_acs_path *path)
> +{
> +     if (path->unreadable)
> +             return PCI_P2PDMA_MAP_NOT_SUPPORTED;
> +
> +     if (pci_acs_p2pdma_request(path->req_ctrl) == PCI_ACS_P2PDMA_DIRECT &&
> +         pci_acs_p2pdma_completion(path->cpl_ctrl) == PCI_ACS_P2PDMA_DIRECT)
> +             return PCI_P2PDMA_MAP_BUS_ADDR;
> +
> +     return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
> +}
> +
> +/*
> + * Name the ports that keep this path off a direct route, so that the admin
> + * can hand them to pci=disable_acs_redir=.
> + */
> +static void pci_p2pdma_warn_path(struct pci_dev *client,
> +                              struct pci_dev *provider,
> +                              const struct pci_p2pdma_acs_path *path,
> +                              struct pci_dev *a_child,
> +                              struct pci_dev *b_child)
> +{
> +     struct seq_buf acs_list;
> +     char buf[128];
> +
> +     if (path->unreadable) {
> +             pci_warn(client,
> +                      "ACS Control is unreadable for provider %s at %s\n",
> +                      pci_name(provider), pci_name(path->unreadable));
> +             return;
> +     }
> +
> +     seq_buf_init(&acs_list, buf, sizeof(buf));
> +     if (pci_acs_p2pdma_completion(path->cpl_ctrl) != PCI_ACS_P2PDMA_DIRECT)
> +             seq_buf_print_bus_devfn(&acs_list, a_child);
> +     if (pci_acs_p2pdma_request(path->req_ctrl) != PCI_ACS_P2PDMA_DIRECT)
> +             seq_buf_print_bus_devfn(&acs_list, b_child);
> +
> +     /* Drop the final semicolon; the list is not empty here. */
> +     if (!seq_buf_has_overflowed(&acs_list))
> +             acs_list.buffer[acs_list.len - 1] = '\0';
> +
> +     pci_warn(client,
> +              "ACS redirect is set between the client and provider (%s)\n",
> +              pci_name(provider));
> +     pci_warn(client,
> +              "to disable ACS controls for this path, add the kernel 
> parameter: pci=disable_acs_redir=%s\n",
> +              seq_buf_str(&acs_list));
> +}
> +
>  static bool cpu_supports_p2pdma(void)
>  {
>  #ifdef CONFIG_X86
> @@ -751,19 +825,13 @@ calc_map_type_and_dist(struct pci_dev *provider, struct 
> pci_dev *client,
>               int *dist, bool verbose)
>  {
>       enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
> -     enum pci_acs_p2pdma_state state = PCI_ACS_P2PDMA_NOT_SUPPORTED;
>       struct pci_dev *a = provider, *b = client, *bb;
>       struct pci_dev *a_child = NULL, *b_child = NULL;
> -     struct pci_dev *acs_unreadable = NULL;
> +     struct pci_p2pdma_acs_path path = {};
>       struct pci_p2pdma *p2pdma;
> -     struct seq_buf acs_list;
> -     int acs_redirect_cnt = 0;
> +     bool cpu_p2pdma, host_whitelisted = false;
>       int dist_a = 0;
>       int dist_b = 0;
> -     char buf[128];
> -     u16 ctrl;
> -
> -     seq_buf_init(&acs_list, buf, sizeof(buf));
>  
>       /*
>        * Note, we don't need to take references to devices returned by
> @@ -806,61 +874,35 @@ calc_map_type_and_dist(struct pci_dev *provider, struct 
> pci_dev *client,
>        * branch is upstream, so redirect controls do not affect the path.
>        */
>       if (a_child && b_child) {
> -             if (pci_acs_p2pdma_ctrl(a_child, &ctrl))
> -                     state = pci_acs_p2pdma_completion(ctrl);
> -             if (state != PCI_ACS_P2PDMA_DIRECT) {
> -                     seq_buf_print_bus_devfn(&acs_list, a_child);
> -                     if (state == PCI_ACS_P2PDMA_REDIRECT)
> -                             acs_redirect_cnt++;
> -                     else if (!acs_unreadable)
> -                             acs_unreadable = a_child;
> -             }
> -
> -             state = PCI_ACS_P2PDMA_NOT_SUPPORTED;
> -             if (pci_acs_p2pdma_ctrl(b_child, &ctrl))
> -                     state = pci_acs_p2pdma_request(ctrl);
> -             if (state != PCI_ACS_P2PDMA_DIRECT) {
> -                     seq_buf_print_bus_devfn(&acs_list, b_child);
> -                     if (state == PCI_ACS_P2PDMA_REDIRECT)
> -                             acs_redirect_cnt++;
> -                     else if (!acs_unreadable)
> -                             acs_unreadable = b_child;
> -             }
> +             if (!pci_acs_p2pdma_ctrl(a_child, &path.cpl_ctrl))
> +                     path.unreadable = a_child;
> +             if (!pci_acs_p2pdma_ctrl(b_child, &path.req_ctrl) &&
> +                 !path.unreadable)
> +                     path.unreadable = b_child;
>       }
>  
>       /*
>        * Below a shared upstream bridge, a path whose divergence ports do not
>        * redirect routes the request directly.
>        */
> -     if (!acs_unreadable && !acs_redirect_cnt) {
> -             map_type = PCI_P2PDMA_MAP_BUS_ADDR;
> +     map_type = pci_p2pdma_route(&path);
> +     if (map_type == PCI_P2PDMA_MAP_BUS_ADDR)
>               goto done;
> -     }
>  
> -     if (verbose) {
> -             /* Drop the final semicolon; the list is not empty here. */
> -             if (!seq_buf_has_overflowed(&acs_list))
> -                     acs_list.buffer[acs_list.len - 1] = '\0';
> -             if (acs_unreadable)
> -                     pci_warn(client, "ACS Control is unreadable for 
> provider %s at %s\n",
> -                              pci_name(provider), pci_name(acs_unreadable));
> -             else {
> -                     pci_warn(client, "ACS redirect is set between the 
> client and provider (%s)\n",
> -                              pci_name(provider));
> -                     pci_warn(client, "to disable ACS controls for this 
> path, add the kernel parameter: pci=disable_acs_redir=%s\n",
> -                              seq_buf_str(&acs_list));
> -             }
> -     }
> +     if (verbose)
> +             pci_p2pdma_warn_path(client, provider, &path, a_child, b_child);
>  
>       /* An unreadable control does not establish an upstream redirect. */
> -     if (acs_unreadable) {
> -             map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
> +     if (path.unreadable)
>               goto done;
> -     }
>  
>  map_through_host_bridge:
> -     if (!cpu_supports_p2pdma() &&
> -         !host_bridge_whitelist(provider, client, verbose)) {
> +     cpu_p2pdma = cpu_supports_p2pdma();
> +     if (!cpu_p2pdma)
> +             host_whitelisted = host_bridge_whitelist(provider, client,
> +                                                       verbose);
> +
> +     if (!cpu_p2pdma && !host_whitelisted) {
>               if (verbose)
>                       pci_warn(client, "cannot be used for peer-to-peer DMA 
> as the client and provider (%s) do not share an upstream bridge or 
> whitelisted host bridge\n",
>                                pci_name(provider));
> @@ -1193,8 +1235,9 @@ enum pci_p2pdma_map_type pci_p2pdma_map_type(struct 
> p2pdma_provider *provider,
>  {
>       enum pci_p2pdma_map_type type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
>       struct pci_dev *pdev = to_pci_dev(provider->owner);
> -     struct pci_dev *client;
>       struct pci_p2pdma *p2pdma;
> +     unsigned long cache_index;
> +     struct pci_dev *client;
>       int dist;
>  
>       if (!pdev->p2pdma)
> @@ -1204,13 +1247,14 @@ enum pci_p2pdma_map_type pci_p2pdma_map_type(struct 
> p2pdma_provider *provider,
>               return PCI_P2PDMA_MAP_NOT_SUPPORTED;
>  
>       client = to_pci_dev(dev);
> +     cache_index = map_types_idx(client);
>  
>       rcu_read_lock();
>       p2pdma = rcu_dereference(pdev->p2pdma);
>  
>       if (p2pdma)
>               type = xa_to_value(xa_load(&p2pdma->map_types,
> -                                        map_types_idx(client)));
> +                                        cache_index));
>       rcu_read_unlock();
>  
>       if (type == PCI_P2PDMA_MAP_UNKNOWN)
> 
> -- 
> 2.55.0
> 

Reply via email to