From: Leon Romanovsky <leon@kernel.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>,
Len Brown <lenb@kernel.org>,
Saket Dumbre <saket.dumbre@intel.com>,
Bjorn Helgaas <bhelgaas@google.com>,
Logan Gunthorpe <logang@deltatee.com>
Cc: linux-acpi@vger.kernel.org, acpica-devel@lists.linux.dev,
linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
Lukas Wunner <lukas@wunner.de>,
"Natu, Mahesh" <mahesh.natu@intel.com>
Subject: [PATCH RFC 2/7] ACPI: HMAT: Make PCIe P2P paths available to consumers
Date: Wed, 12 Aug 2026 22:47:39 +0300 [thread overview]
Message-ID: <20260812-hmat-p2p-v1-2-75ac41380585@nvidia.com> (raw)
In-Reply-To: <20260812-hmat-p2p-v1-0-75ac41380585@nvidia.com>
From: Leon Romanovsky <leonro@nvidia.com>
P2PDMA policy must preserve the direction, ordering requirements, and
reachability described by HMAT Type 3. A path may work in only one
direction or only with Unordered I/O, and a path the platform does not
describe must never authorize ordinary DMA.
Record each Generic Port pair and traffic class independently, reporting a
pair firmware never mentions separately from a pair it describes without a
usable value. Entries that normalize to zero, whether the platform encodes
no path or publishes no number, both mean no path, so consumers fail
closed.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
drivers/acpi/numa/hmat.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/acpi.h | 26 ++++++
2 files changed, 230 insertions(+)
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
index 9792dc394756..d06a62b38fff 100644
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -37,6 +37,7 @@ void __init disable_hmat(void)
static LIST_HEAD(targets);
static LIST_HEAD(initiators);
static LIST_HEAD(localities);
+static LIST_HEAD(p2p_localities);
static DEFINE_MUTEX(target_lock);
@@ -87,6 +88,21 @@ struct memory_locality {
struct acpi_hmat_locality *hmat_loc;
};
+/*
+ * PCIe peer-to-peer access coordinates between two generic ports (PCIe host
+ * bridges), keyed by their initiator and target proximity domains. A separate
+ * set of coordinates is kept for ordered (non-UIO) and Unordered I/O (UIO)
+ * traffic, as described by the HMAT PCIe P2P Latency and Bandwidth Information
+ * Structure.
+ */
+struct memory_p2p_locality {
+ struct list_head node;
+ int initiator;
+ int target;
+ unsigned int valid; /* bitmap of populated enum hmat_p2p_class */
+ struct access_coordinate coord[HMAT_P2P_MAX];
+};
+
static struct memory_initiator *find_mem_initiator(unsigned int cpu_pxm)
{
struct memory_initiator *initiator;
@@ -191,6 +207,81 @@ int acpi_get_genport_coordinates(u32 uid,
}
EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, "CXL");
+/**
+ * acpi_get_genport_proximity_domain - Retrieve the proximity domain of a
+ * generic port
+ * @uid: ACPI unique id of the generic port (typically a PCIe host bridge)
+ *
+ * The targets list is built while parsing SRAT during boot and is never
+ * modified afterwards, and both gen_port_device_handle and memory_pxm are
+ * written once by alloc_genport_target() and alloc_target(). So this
+ * takes no target_lock, unlike acpi_get_genport_coordinates() which reads
+ * target->coord[] that hmat_calculate_adistance() updates at runtime. PCI
+ * P2PDMA calls this from the DMA mapping path, which must not sleep.
+ *
+ * Return: the proximity domain on success, negative errno on failure.
+ */
+int acpi_get_genport_proximity_domain(u32 uid)
+{
+ struct memory_target *target;
+
+ target = acpi_find_genport_target(uid);
+ if (!target)
+ return -ENOENT;
+
+ return target->memory_pxm;
+}
+EXPORT_SYMBOL_GPL(acpi_get_genport_proximity_domain);
+
+/*
+ * The p2p_localities list is fully populated while parsing the HMAT during
+ * boot and is never modified afterwards, so runtime readers below walk it
+ * without holding target_lock.
+ */
+static struct memory_p2p_locality *find_p2p_locality(int initiator, int target)
+{
+ struct memory_p2p_locality *loc;
+
+ list_for_each_entry(loc, &p2p_localities, node)
+ if (loc->initiator == initiator && loc->target == target)
+ return loc;
+ return NULL;
+}
+
+static bool p2p_coord_populated(const struct access_coordinate *coord)
+{
+ return coord->read_bandwidth || coord->write_bandwidth ||
+ coord->read_latency || coord->write_latency;
+}
+
+/**
+ * acpi_get_p2p_coordinates - Retrieve the P2P access coordinates between two
+ * generic ports (PCIe host bridges)
+ * @initiator_pxm: proximity domain of the initiating generic port
+ * @target_pxm: proximity domain of the target generic port
+ * @class: traffic class (ordered/non-UIO or UIO)
+ * @coord: the access coordinates written back on success
+ *
+ * Return: 0 on success, -ENOENT if the firmware describes no entry for the
+ * pair, or -ENODATA if the described path is unreachable for @class.
+ */
+int acpi_get_p2p_coordinates(int initiator_pxm, int target_pxm,
+ enum hmat_p2p_class class,
+ struct access_coordinate *coord)
+{
+ struct memory_p2p_locality *loc;
+
+ loc = find_p2p_locality(initiator_pxm, target_pxm);
+ if (!loc)
+ return -ENOENT;
+ if (!(loc->valid & BIT(class)) || !p2p_coord_populated(&loc->coord[class]))
+ return -ENODATA;
+
+ *coord = loc->coord[class];
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_get_p2p_coordinates);
+
static __init void alloc_memory_initiator(unsigned int cpu_pxm)
{
struct memory_initiator *initiator;
@@ -479,6 +570,111 @@ static __init int hmat_parse_locality(union acpi_subtable_headers *header,
return 0;
}
+static void hmat_update_p2p_access(struct access_coordinate *coord,
+ u8 type, u32 value)
+{
+ switch (type) {
+ case ACPI_HMAT_ACCESS_LATENCY:
+ coord->read_latency = value;
+ coord->write_latency = value;
+ break;
+ case ACPI_HMAT_READ_LATENCY:
+ coord->read_latency = value;
+ break;
+ case ACPI_HMAT_WRITE_LATENCY:
+ coord->write_latency = value;
+ break;
+ case ACPI_HMAT_ACCESS_BANDWIDTH:
+ coord->read_bandwidth = value;
+ coord->write_bandwidth = value;
+ break;
+ case ACPI_HMAT_READ_BANDWIDTH:
+ coord->read_bandwidth = value;
+ break;
+ case ACPI_HMAT_WRITE_BANDWIDTH:
+ coord->write_bandwidth = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static __init void hmat_update_p2p(int initiator, int target, u8 flags,
+ u8 type, u32 value)
+{
+ struct memory_p2p_locality *loc;
+ enum hmat_p2p_class class;
+
+ loc = find_p2p_locality(initiator, target);
+ if (!loc) {
+ loc = kzalloc_obj(*loc);
+ if (!loc)
+ return;
+ loc->initiator = initiator;
+ loc->target = target;
+ list_add_tail(&loc->node, &p2p_localities);
+ }
+
+ for (class = 0; class < HMAT_P2P_MAX; class++) {
+ if (class == HMAT_P2P_NON_UIO && !(flags & ACPI_HMAT_P2P_NON_UIO))
+ continue;
+ if (class == HMAT_P2P_UIO && !(flags & ACPI_HMAT_P2P_UIO))
+ continue;
+ hmat_update_p2p_access(&loc->coord[class], type, value);
+ loc->valid |= BIT(class);
+ }
+}
+
+static __init int hmat_parse_p2p_latency(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_hmat_p2p_latency *p2p = (void *)header;
+ unsigned int init, targ, total_size, ipds, tpds;
+ u32 *inits, *targs, value;
+ u16 *entries;
+ u8 type, flags;
+
+ if (p2p->header.length < sizeof(*p2p)) {
+ pr_notice("Unexpected P2P header length: %u\n",
+ p2p->header.length);
+ return -EINVAL;
+ }
+
+ type = p2p->data_type;
+ flags = p2p->flags;
+ ipds = p2p->number_of_initiator_Pds;
+ tpds = p2p->number_of_target_Pds;
+ total_size = sizeof(*p2p) + sizeof(*entries) * ipds * tpds +
+ sizeof(*inits) * ipds + sizeof(*targs) * tpds;
+ if (p2p->header.length < total_size) {
+ pr_notice("Unexpected P2P header length:%u, minimum required:%u\n",
+ p2p->header.length, total_size);
+ return -EINVAL;
+ }
+
+ pr_debug("P2P: Flags:%02x Type:%s Initiator Ports:%u Target Ports:%u Base:%lld\n",
+ p2p->flags, hmat_data_type(type), ipds, tpds,
+ p2p->entry_base_unit);
+
+ inits = (u32 *)(p2p + 1);
+ targs = inits + ipds;
+ entries = (u16 *)(targs + tpds);
+ for (init = 0; init < ipds; init++) {
+ for (targ = 0; targ < tpds; targ++) {
+ value = hmat_normalize(entries[init * tpds + targ],
+ p2p->entry_base_unit, type);
+ pr_debug(" Initiator-Target[%u-%u]:%u%s\n",
+ inits[init], targs[targ], value,
+ hmat_data_type_suffix(type));
+
+ hmat_update_p2p(inits[init], targs[targ], flags,
+ type, value);
+ }
+ }
+
+ return 0;
+}
+
static __init int hmat_parse_cache(union acpi_subtable_headers *header,
const unsigned long end)
{
@@ -603,6 +799,8 @@ static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
return hmat_parse_locality(header, end);
case ACPI_HMAT_TYPE_CACHE:
return hmat_parse_cache(header, end);
+ case ACPI_HMAT_TYPE_P2P_LATENCY:
+ return hmat_parse_p2p_latency(header, end);
default:
return -EINVAL;
}
@@ -1004,6 +1202,7 @@ static __init void hmat_free_structures(void)
{
struct memory_target *target, *tnext;
struct memory_locality *loc, *lnext;
+ struct memory_p2p_locality *ploc, *pnext;
struct memory_initiator *initiator, *inext;
struct target_cache *tcache, *cnext;
@@ -1035,6 +1234,11 @@ static __init void hmat_free_structures(void)
list_del(&loc->node);
kfree(loc);
}
+
+ list_for_each_entry_safe(ploc, pnext, &p2p_localities, node) {
+ list_del(&ploc->node);
+ kfree(ploc);
+ }
}
static __init int hmat_init(void)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 60ab50cb8930..3d1f564213f1 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1152,15 +1152,41 @@ static inline int acpi_mrrm_max_mem_region(void)
#endif /* !CONFIG_ACPI */
+/*
+ * PCIe peer-to-peer traffic classes described by an HMAT PCIe P2P Latency
+ * and Bandwidth Information Structure (ACPI_HMAT_TYPE_P2P_LATENCY).
+ */
+enum hmat_p2p_class {
+ HMAT_P2P_NON_UIO, /* Ordered (non-UIO) P2P traffic */
+ HMAT_P2P_UIO, /* PCIe Unordered I/O (UIO) P2P traffic */
+ HMAT_P2P_MAX,
+};
+
#ifdef CONFIG_ACPI_HMAT
int hmat_get_extended_linear_cache_size(struct resource *backing_res, int nid,
resource_size_t *size);
+int acpi_get_genport_proximity_domain(u32 uid);
+int acpi_get_p2p_coordinates(int initiator_pxm, int target_pxm,
+ enum hmat_p2p_class class,
+ struct access_coordinate *coord);
#else
static inline int hmat_get_extended_linear_cache_size(struct resource *backing_res,
int nid, resource_size_t *size)
{
return -EOPNOTSUPP;
}
+
+static inline int acpi_get_genport_proximity_domain(u32 uid)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int acpi_get_p2p_coordinates(int initiator_pxm, int target_pxm,
+ enum hmat_p2p_class class,
+ struct access_coordinate *coord)
+{
+ return -EOPNOTSUPP;
+}
#endif
extern void arch_post_acpi_subsys_init(void);
--
2.55.0
next prev parent reply other threads:[~2026-08-12 19:48 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 19:47 [PATCH RFC 0/7] PCI/HMAT: Describe P2PDMA reachability and performance Leon Romanovsky
2026-08-12 19:47 ` [PATCH RFC 1/7] ACPICA: Define PCIe P2P latency and bandwidth information Leon Romanovsky
2026-08-13 10:13 ` Rafael J. Wysocki (Intel)
2026-08-13 11:01 ` Leon Romanovsky
2026-08-12 19:47 ` Leon Romanovsky [this message]
2026-08-12 19:47 ` [PATCH RFC 3/7] PCI/P2PDMA: Authorize HMAT-described cross-host-bridge P2P Leon Romanovsky
2026-08-12 19:47 ` [PATCH RFC 4/7] PCI/P2PDMA: Prefer providers with better HMAT performance Leon Romanovsky
2026-08-14 3:50 ` Logan Gunthorpe
2026-08-16 12:38 ` Leon Romanovsky
2026-08-17 16:09 ` Logan Gunthorpe
2026-08-17 18:27 ` Leon Romanovsky
2026-08-12 19:47 ` [PATCH RFC 5/7] ACPI: HMAT: Add KUnit tests for PCIe P2P matrix semantics Leon Romanovsky
2026-08-12 19:47 ` [PATCH RFC 6/7] PCI/P2PDMA: Add KUnit tests for HMAT policy boundaries Leon Romanovsky
2026-08-12 19:47 ` [PATCH RFC 7/7] PCI/P2PDMA: Add KUnit tests for HMAT provider ranking Leon Romanovsky
2026-08-24 20:15 ` [PATCH RFC 0/7] PCI/HMAT: Describe P2PDMA reachability and performance Alison Schofield
2026-08-24 20:24 ` Natu, Mahesh
2026-08-26 14:08 ` Leon Romanovsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260812-hmat-p2p-v1-2-75ac41380585@nvidia.com \
--to=leon@kernel.org \
--cc=acpica-devel@lists.linux.dev \
--cc=bhelgaas@google.com \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=logang@deltatee.com \
--cc=lukas@wunner.de \
--cc=mahesh.natu@intel.com \
--cc=rafael@kernel.org \
--cc=saket.dumbre@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®