From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id E0D71457E4;
	Fri, 16 Aug 2024 14:17:09 +0200 (CEST)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 746BC42DCA;
	Fri, 16 Aug 2024 14:17:05 +0200 (CEST)
Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.14])
 by mails.dpdk.org (Postfix) with ESMTP id 87B41400EF
 for <dev@dpdk.org>; Fri, 16 Aug 2024 14:17:03 +0200 (CEST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
 d=intel.com; i=@intel.com; q=dns/txt; s=Intel;
 t=1723810624; x=1755346624;
 h=from:to:cc:subject:date:message-id:in-reply-to:
 references:mime-version:content-transfer-encoding;
 bh=cbIO9dwuOy6h1wts2aWL4CQIlHzIliPapthPPMy9XaE=;
 b=cI3HFMhQ8YDAYp45+lzM/p7jSf+fAPXihHw2mua2NkH06nT4IYysY6pS
 KGMLSP5zP8T7D1+ExO6jXfkN2Egm3Cf/IuFZ+W99IU3j/00Lq7IVgf8y5
 nhFQMyLG8yp8sk5A7/a8dEGwmjAtA32Rqxbh0qAfgstY6/Y5nNCenVedt
 RHGkiaUCghBMmgGU0RVAQFu+59JQsvCSGFX3rn7LOYEngJV1lBTqZxJNW
 RPRjwGuZI/xE8Q1uHSiYbyu8/Uku+bIoRuQeJI0C38pkV4feMF9zdjWHg
 B2PvK/fIiFx19rxlzDKL6F7qHDbyAaYeIP7MI6SRBQIqAjCnLGAL0xHkx A==;
X-CSE-ConnectionGUID: EKn/++kvQj+9IsV9Bp+ndg==
X-CSE-MsgGUID: O/13bWqWTN+XXD/Krjav3w==
X-IronPort-AV: E=McAfee;i="6700,10204,11166"; a="22276054"
X-IronPort-AV: E=Sophos;i="6.10,151,1719903600"; d="scan'208";a="22276054"
Received: from fmviesa010.fm.intel.com ([10.60.135.150])
 by fmvoesa108.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;
 16 Aug 2024 05:17:03 -0700
X-CSE-ConnectionGUID: NHuZ6EeJTpC8oIGv/Uz6aQ==
X-CSE-MsgGUID: BVQUmmMnQhGMCzamfdzvyw==
X-ExtLoop1: 1
X-IronPort-AV: E=Sophos;i="6.10,151,1719903600"; d="scan'208";a="59819879"
Received: from silpixa00401119.ir.intel.com ([10.55.129.167])
 by fmviesa010.fm.intel.com with ESMTP; 16 Aug 2024 05:17:02 -0700
From: Anatoly Burakov <anatoly.burakov@intel.com>
To: dev@dpdk.org,
	Robin Jarry <rjarry@redhat.com>
Cc: bruce.richardson@intel.com
Subject: [PATCH v2 2/4] usertools/cpu_layout: print out NUMA nodes
Date: Fri, 16 Aug 2024 13:16:56 +0100
Message-ID: <79418ee26f32ad7d850360fb2060b7a9d6d391f9.1723810613.git.anatoly.burakov@intel.com>
X-Mailer: git-send-email 2.43.5
In-Reply-To: <4844fc3a4604ffef789702752ff04699ab5118db.1723810613.git.anatoly.burakov@intel.com>
References: <e51d00fea7c535404dda4d63e483639b13c48b2d.1723634354.git.anatoly.burakov@intel.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

In traditional NUMA case, NUMA nodes and physical sockets were used
interchangeably, but there are cases where there can be multiple NUMA
nodes per socket, as well as all CPU's being assigned NUMA node 0 even in
cases of multiple sockets. Use sysfs to print out NUMA information.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 usertools/cpu_layout.py | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py
index be86f06938..e43bdbf343 100755
--- a/usertools/cpu_layout.py
+++ b/usertools/cpu_layout.py
@@ -4,6 +4,7 @@
 # Copyright(c) 2017 Cavium, Inc. All rights reserved.
 
 from typing import List, Set, Dict, Tuple
+import glob
 
 
 def _range_expand(rstr: str) -> List[int]:
@@ -26,11 +27,19 @@ def _read_sysfs(path: str) -> str:
         return fd.read().strip()
 
 
+def _read_numa_node(base: str) -> int:
+    node_glob = f"{base}/node*"
+    node_dirs = glob.glob(node_glob)
+    if not node_dirs:
+        return 0  # default to node 0
+    return int(node_dirs[0].split("node")[1])
+
+
 def _print_row(row: Tuple[str, ...], col_widths: List[int]) -> None:
     first, *rest = row
     w_first, *w_rest = col_widths
     first_end = " " * 4
-    rest_end = " " * 10
+    rest_end = " " * 4
 
     print(first.ljust(w_first), end=first_end)
     for cell, width in zip(rest, w_rest):
@@ -50,6 +59,7 @@ def _main() -> None:
     sockets_s: Set[int] = set()
     cores_s: Set[int] = set()
     core_map: Dict[Tuple[int, int], List[int]] = {}
+    numa_map: Dict[int, int] = {}
     base_path = "/sys/devices/system/cpu"
 
     cpus = _range_expand(_read_sysfs(f"{base_path}/online"))
@@ -58,12 +68,14 @@ def _main() -> None:
         lcore_base = f"{base_path}/cpu{cpu}"
         core = int(_read_sysfs(f"{lcore_base}/topology/core_id"))
         socket = int(_read_sysfs(f"{lcore_base}/topology/physical_package_id"))
+        node = _read_numa_node(lcore_base)
 
         cores_s.add(core)
         sockets_s.add(socket)
         key = (socket, core)
         core_map.setdefault(key, [])
         core_map[key].append(cpu)
+        numa_map[cpu] = node
 
     cores = sorted(cores_s)
     sockets = sorted(sockets_s)
@@ -73,24 +85,37 @@ def _main() -> None:
 
     print("cores = ", cores)
     print("sockets = ", sockets)
+    print("numa = ", sorted(set(numa_map.values())))
     print()
 
-    # Core, [Socket, Socket, ...]
-    heading_strs = "", *[f"Socket {s}" for s in sockets]
+    # Core, [NUMA, Socket, NUMA, Socket, ...]
+    heading_strs = "", *[v for s in sockets for v in ("", f"Socket {s}")]
     sep_strs = tuple("-" * len(hstr) for hstr in heading_strs)
     rows: List[Tuple[str, ...]] = []
 
+    prev_numa = None
     for c in cores:
         # Core,
         row: Tuple[str, ...] = (f"Core {c}",)
 
-        # [lcores, lcores, ...]
+        # assume NUMA changes symmetrically
+        first_lcore = core_map[(0, c)][0]
+        cur_numa = numa_map[first_lcore]
+        numa_changed = prev_numa != cur_numa
+        prev_numa = cur_numa
+
+        # [NUMA, lcores, NUMA, lcores, ...]
         for s in sockets:
             try:
                 lcores = core_map[(s, c)]
+                numa = numa_map[lcores[0]]
+                if numa_changed:
+                    row += (f"NUMA {numa}",)
+                else:
+                    row += ("",)
                 row += (str(lcores),)
             except KeyError:
-                row += ("",)
+                row += ("", "")
         rows += [row]
 
     # find max widths for each column, including header and rows
-- 
2.43.5