mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
PCI/P2PDMA: Simplify distance calculation
Merge __calc_map_type_and_dist() and calc_map_type_and_dist_warn() into calc_map_type_and_dist() to simplify the code a bit. This now means we add the devfn strings to the acs_buf unconditionally even if the buffer is not printed, but that is not a lot of overhead and keeps the code much simpler. Link: https://lore.kernel.org/r/20210614055310.3960791-1-hch@lst.de Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
This commit is contained in:
parent
3ec0c3ec2d
commit
d1b8dc09dd
|
|
@ -388,79 +388,6 @@ static bool host_bridge_whitelist(struct pci_dev *a, struct pci_dev *b,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum pci_p2pdma_map_type
|
|
||||||
__calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
|
||||||
int *dist, bool *acs_redirects, struct seq_buf *acs_list)
|
|
||||||
{
|
|
||||||
struct pci_dev *a = provider, *b = client, *bb;
|
|
||||||
int dist_a = 0;
|
|
||||||
int dist_b = 0;
|
|
||||||
int acs_cnt = 0;
|
|
||||||
|
|
||||||
if (acs_redirects)
|
|
||||||
*acs_redirects = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note, we don't need to take references to devices returned by
|
|
||||||
* pci_upstream_bridge() seeing we hold a reference to a child
|
|
||||||
* device which will already hold a reference to the upstream bridge.
|
|
||||||
*/
|
|
||||||
|
|
||||||
while (a) {
|
|
||||||
dist_b = 0;
|
|
||||||
|
|
||||||
if (pci_bridge_has_acs_redir(a)) {
|
|
||||||
seq_buf_print_bus_devfn(acs_list, a);
|
|
||||||
acs_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb = b;
|
|
||||||
|
|
||||||
while (bb) {
|
|
||||||
if (a == bb)
|
|
||||||
goto check_b_path_acs;
|
|
||||||
|
|
||||||
bb = pci_upstream_bridge(bb);
|
|
||||||
dist_b++;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = pci_upstream_bridge(a);
|
|
||||||
dist_a++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist)
|
|
||||||
*dist = dist_a + dist_b;
|
|
||||||
|
|
||||||
return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
|
||||||
|
|
||||||
check_b_path_acs:
|
|
||||||
bb = b;
|
|
||||||
|
|
||||||
while (bb) {
|
|
||||||
if (a == bb)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (pci_bridge_has_acs_redir(bb)) {
|
|
||||||
seq_buf_print_bus_devfn(acs_list, bb);
|
|
||||||
acs_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb = pci_upstream_bridge(bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist)
|
|
||||||
*dist = dist_a + dist_b;
|
|
||||||
|
|
||||||
if (acs_cnt) {
|
|
||||||
if (acs_redirects)
|
|
||||||
*acs_redirects = true;
|
|
||||||
|
|
||||||
return PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PCI_P2PDMA_MAP_BUS_ADDR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long map_types_idx(struct pci_dev *client)
|
static unsigned long map_types_idx(struct pci_dev *client)
|
||||||
{
|
{
|
||||||
return (pci_domain_nr(client->bus) << 16) |
|
return (pci_domain_nr(client->bus) << 16) |
|
||||||
|
|
@ -502,63 +429,96 @@ static unsigned long map_types_idx(struct pci_dev *client)
|
||||||
* PCI_P2PDMA_MAP_THRU_HOST_BRIDGE with the distance set to the number of
|
* PCI_P2PDMA_MAP_THRU_HOST_BRIDGE with the distance set to the number of
|
||||||
* ports per above. If the device is not in the whitelist, return
|
* ports per above. If the device is not in the whitelist, return
|
||||||
* PCI_P2PDMA_MAP_NOT_SUPPORTED.
|
* PCI_P2PDMA_MAP_NOT_SUPPORTED.
|
||||||
*
|
|
||||||
* If any ACS redirect bits are set, then acs_redirects boolean will be set
|
|
||||||
* to true and their PCI device names will be appended to the acs_list
|
|
||||||
* seq_buf. This seq_buf is used to print a warning informing the user how
|
|
||||||
* to disable ACS using a command line parameter. (See
|
|
||||||
* calc_map_type_and_dist_warn() below)
|
|
||||||
*/
|
*/
|
||||||
static enum pci_p2pdma_map_type
|
static enum pci_p2pdma_map_type
|
||||||
calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
||||||
int *dist, bool *acs_redirects, struct seq_buf *acs_list)
|
int *dist, bool verbose)
|
||||||
{
|
|
||||||
enum pci_p2pdma_map_type map_type;
|
|
||||||
|
|
||||||
map_type = __calc_map_type_and_dist(provider, client, dist,
|
|
||||||
acs_redirects, acs_list);
|
|
||||||
|
|
||||||
if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE) {
|
|
||||||
if (!cpu_supports_p2pdma() &&
|
|
||||||
!host_bridge_whitelist(provider, client, acs_redirects))
|
|
||||||
map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (provider->p2pdma)
|
|
||||||
xa_store(&provider->p2pdma->map_types, map_types_idx(client),
|
|
||||||
xa_mk_value(map_type), GFP_KERNEL);
|
|
||||||
|
|
||||||
return map_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum pci_p2pdma_map_type
|
|
||||||
calc_map_type_and_dist_warn(struct pci_dev *provider, struct pci_dev *client,
|
|
||||||
int *dist)
|
|
||||||
{
|
{
|
||||||
|
enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
||||||
|
struct pci_dev *a = provider, *b = client, *bb;
|
||||||
|
bool acs_redirects = false;
|
||||||
struct seq_buf acs_list;
|
struct seq_buf acs_list;
|
||||||
bool acs_redirects;
|
int acs_cnt = 0;
|
||||||
|
int dist_a = 0;
|
||||||
|
int dist_b = 0;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int ret;
|
|
||||||
|
|
||||||
seq_buf_init(&acs_list, buf, sizeof(buf));
|
seq_buf_init(&acs_list, buf, sizeof(buf));
|
||||||
|
|
||||||
ret = calc_map_type_and_dist(provider, client, dist, &acs_redirects,
|
/*
|
||||||
&acs_list);
|
* Note, we don't need to take references to devices returned by
|
||||||
if (acs_redirects) {
|
* pci_upstream_bridge() seeing we hold a reference to a child
|
||||||
|
* device which will already hold a reference to the upstream bridge.
|
||||||
|
*/
|
||||||
|
while (a) {
|
||||||
|
dist_b = 0;
|
||||||
|
|
||||||
|
if (pci_bridge_has_acs_redir(a)) {
|
||||||
|
seq_buf_print_bus_devfn(&acs_list, a);
|
||||||
|
acs_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb = b;
|
||||||
|
|
||||||
|
while (bb) {
|
||||||
|
if (a == bb)
|
||||||
|
goto check_b_path_acs;
|
||||||
|
|
||||||
|
bb = pci_upstream_bridge(bb);
|
||||||
|
dist_b++;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = pci_upstream_bridge(a);
|
||||||
|
dist_a++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dist = dist_a + dist_b;
|
||||||
|
goto map_through_host_bridge;
|
||||||
|
|
||||||
|
check_b_path_acs:
|
||||||
|
bb = b;
|
||||||
|
|
||||||
|
while (bb) {
|
||||||
|
if (a == bb)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (pci_bridge_has_acs_redir(bb)) {
|
||||||
|
seq_buf_print_bus_devfn(&acs_list, bb);
|
||||||
|
acs_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb = pci_upstream_bridge(bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dist = dist_a + dist_b;
|
||||||
|
|
||||||
|
if (!acs_cnt) {
|
||||||
|
map_type = PCI_P2PDMA_MAP_BUS_ADDR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
acs_list.buffer[acs_list.len-1] = 0; /* drop final semicolon */
|
||||||
pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
|
pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
|
||||||
pci_name(provider));
|
pci_name(provider));
|
||||||
/* Drop final semicolon */
|
|
||||||
acs_list.buffer[acs_list.len-1] = 0;
|
|
||||||
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
|
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
|
||||||
acs_list.buffer);
|
acs_list.buffer);
|
||||||
}
|
}
|
||||||
|
acs_redirects = true;
|
||||||
|
|
||||||
if (ret == PCI_P2PDMA_MAP_NOT_SUPPORTED) {
|
map_through_host_bridge:
|
||||||
|
if (!cpu_supports_p2pdma() &&
|
||||||
|
!host_bridge_whitelist(provider, client, acs_redirects)) {
|
||||||
|
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_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));
|
pci_name(provider));
|
||||||
|
map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
done:
|
||||||
return ret;
|
if (provider->p2pdma)
|
||||||
|
xa_store(&provider->p2pdma->map_types, map_types_idx(client),
|
||||||
|
xa_mk_value(map_type), GFP_KERNEL);
|
||||||
|
return map_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -599,12 +559,8 @@ int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
map = calc_map_type_and_dist(provider, pci_client, &distance,
|
||||||
map = calc_map_type_and_dist_warn(provider, pci_client,
|
verbose);
|
||||||
&distance);
|
|
||||||
else
|
|
||||||
map = calc_map_type_and_dist(provider, pci_client,
|
|
||||||
&distance, NULL, NULL);
|
|
||||||
|
|
||||||
pci_dev_put(pci_client);
|
pci_dev_put(pci_client);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user