[PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes
From: Lin Wang
Date: Tue Sep 01 2026 - 03:27:20 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 be used as an index into the dense DF node cache.
Translate the CPUID value through the CDDs sorted by socket and DFID.
The NodeId reported by CPUID in a guest may not describe the physical
DF topology. Return -ENODEV in guests or when the NodeId does not
identify an enumerated CDD.
Signed-off-by: Lin Wang <wanglin@xxxxxxxxxxxxxx>
---
arch/x86/include/asm/hygon/node.h | 21 ++++++++++++
arch/x86/kernel/hygon_node.c | 53 +++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+)
diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h
index 6e15c133b774..bc4e588ab405 100644
--- a/arch/x86/include/asm/hygon/node.h
+++ b/arch/x86/include/asm/hygon/node.h
@@ -82,6 +82,22 @@ 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 8000001E[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())
@@ -126,6 +142,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 85835938315b..e2aa7de180e9 100644
--- a/arch/x86/kernel/hygon_node.c
+++ b/arch/x86/kernel/hygon_node.c
@@ -16,6 +16,8 @@
#define pr_fmt(fmt) "hygon_node: " fmt
#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpufeature.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/pci.h>
@@ -23,6 +25,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>
@@ -554,6 +557,40 @@ static int __init hygon_sort_and_classify(struct hygon_node_cache *cache)
return 0;
}
+/*
+ * Translate a Hygon Fam18h phys_node_id (CPUID 8000001E ECX[7:0]) to a
+ * dense DF CDD index. On supported models, the node layer uses
+ * 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 = phys_nid >> 4;
+ unsigned int local = phys_nid & 0xf;
+ unsigned int ordinal = 0;
+ u16 i;
+
+ if (!hygon_cache.ready)
+ 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;
+}
+
/*
* Build the global DF node cache.
*
@@ -618,6 +655,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.ready)
--
2.43.0