[PATCH v6 2/8] x86/hygon: Map CPU NodeIds to DF nodes
From: Lin Wang
Date: Mon Sep 21 2026 - 01:56:47 EST
On bare-metal Hygon systems, CPUID 0x8000001E ECX[7:0] encodes a
CPU's socket and local CDD ordinal. The value is sparse across sockets
and cannot directly index the dense DF node cache.
Resolve the encoded socket and local ordinal against CDD entries in the
cache, which are ordered by socket and DFID. The NodeId reported by
CPUID in a guest may not describe the physical DF topology, so return
-ENODEV in guests or when it does not identify an enumerated CDD.
Signed-off-by: Lin Wang <wanglin@xxxxxxxxxxxxxx>
---
arch/x86/include/asm/hygon/node.h | 22 +++++++++++++
arch/x86/kernel/hygon_node.c | 55 +++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+)
diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h
index b97eaee7c0d1..23af0d44a432 100644
--- a/arch/x86/include/asm/hygon/node.h
+++ b/arch/x86/include/asm/hygon/node.h
@@ -77,6 +77,23 @@ u16 hygon_cdd_num(void);
*/
int hygon_node_get_info(u16 node, struct hygon_node_info *info);
+/**
+ * hygon_cpu_to_df_node() - Map CPU to dense DF CDD index
+ * @cpu: CPU index
+ *
+ * Hygon Fam18h exposes sparse physical node IDs via CPUID 0x8000001E
+ * ECX[7:0].
+ * This function translates the per-CPU physical node ID into a dense
+ * DF CDD index in [0, hygon_cdd_num()). The NodeId reported by CPUID in
+ * a guest may not describe the physical DF topology, so the translation
+ * is unavailable in guests.
+ *
+ * Return: DF CDD index on success, -EINVAL if @cpu is out of range,
+ * -ENODEV if CPU-to-DF mapping is unavailable or the physical node ID
+ * does not map to a known DF node.
+ */
+int hygon_cpu_to_df_node(unsigned int cpu);
+
/**
* hygon_node_get_func() - Get DF function PCI device for a node
* @node: DF node index in [0, hygon_node_num())
@@ -118,6 +135,11 @@ static inline int hygon_node_get_info(u16 node, struct hygon_node_info *info)
return -ENODEV;
}
+static inline int hygon_cpu_to_df_node(unsigned int cpu)
+{
+ return -ENODEV;
+}
+
static inline struct pci_dev *hygon_node_get_func(u16 node, u8 func)
{
return NULL;
diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c
index e8346d86d8a6..8c21190a19c2 100644
--- a/arch/x86/kernel/hygon_node.c
+++ b/arch/x86/kernel/hygon_node.c
@@ -26,6 +26,7 @@
#include <linux/processor.h>
#include <linux/slab.h>
#include <linux/sort.h>
+#include <linux/topology.h>
#include <asm/cpu_device_id.h>
#include <asm/hygon/node.h>
@@ -489,6 +490,44 @@ static int __init hygon_sort_and_classify(struct hygon_node_cache *cache)
return 0;
}
+#define HYGON_NODE_ID_SOCKET GENMASK(7, 4)
+#define HYGON_NODE_ID_LOCAL GENMASK(3, 0)
+
+/*
+ * Translate a Hygon Fam18h phys_node_id (CPUID 8000001E ECX[7:0]) to a
+ * dense DF CDD index. On supported models, CPUID reports this
+ * encoding:
+ *
+ * phys_node_id = (socket_id << 4) | local_cdd_index_in_dfid_order
+ *
+ * The cache->nodes[] CDD region is sorted by (socket_id ASC, dfid ASC),
+ * so walk it and return the actual cache index of the CDD whose
+ * socket_id matches and whose socket-local ordinal is @local.
+ *
+ * Return -ENODEV if the cache is unavailable or no CDD matches.
+ */
+static int hygon_phys_nid_to_df_node(unsigned int phys_nid)
+{
+ unsigned int socket, local, ordinal = 0;
+ u16 i;
+
+ socket = FIELD_GET(HYGON_NODE_ID_SOCKET, phys_nid);
+ local = FIELD_GET(HYGON_NODE_ID_LOCAL, phys_nid);
+
+ if (!hygon_cache.nodes)
+ return -ENODEV;
+
+ for (i = 0; i < hygon_cache.num_cdd; i++) {
+ if (hygon_cache.nodes[i].socket_id != socket)
+ continue;
+
+ if (ordinal++ == local)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
static int __init hygon_build_cache(void)
{
struct hygon_node_cache cache = { };
@@ -544,6 +583,22 @@ int hygon_node_get_info(u16 node, struct hygon_node_info *info)
}
EXPORT_SYMBOL_GPL(hygon_node_get_info);
+int hygon_cpu_to_df_node(unsigned int cpu)
+{
+ if (cpu >= nr_cpu_ids)
+ return -EINVAL;
+
+ /*
+ * The NodeId reported by CPUID in a guest may not describe the
+ * physical DF topology.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
+ return -ENODEV;
+
+ return hygon_phys_nid_to_df_node(topology_amd_node_id(cpu));
+}
+EXPORT_SYMBOL_GPL(hygon_cpu_to_df_node);
+
struct pci_dev *hygon_node_get_func(u16 node, u8 func)
{
if (!hygon_cache.nodes)
--
2.43.0