From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 3697C45845; Thu, 22 Aug 2024 12:38:37 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id D13F242EA2; Thu, 22 Aug 2024 12:38:35 +0200 (CEST) Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.11]) by mails.dpdk.org (Postfix) with ESMTP id 48BCC42E66 for ; Thu, 22 Aug 2024 12:38:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1724323111; x=1755859111; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=yfstov9CWntVD39YYLvMEt5dvf76qqvJO/dEMn6L1vo=; b=l5TK5itCaW8t01JFu+1Sf6c8Gd0/+sIGg9UQGQ/9FgjXYIFnzIqR/qCR AMWxj44xhxlxI9Uz2ZCMsu6tGeaFJp90L+bqlvYzWaZyLWhob/n5VSV50 zxMFpM12lGo3ibllYYvzQ6uYJ+auE+Bcgl/8xVGSmyyTxYadnxqsGrlHW KXQJVevPrzHqSSgjalABVsxaRsCbkVOPpNgUrJdEznVDlqAkxUm1X5bJG S2av/HoyWX0DjB80SvXvE7zfm9LCJ5MWimsqUYQYZxZp8OkvOf842iAr/ 3OguWgdqgCA7AHmv9K2hF210oPJZ7RtUqoHanxVlgg7Tk5tdfvfxfTVTm Q==; X-CSE-ConnectionGUID: BosgJB4kRySiQ3AeppO7Rw== X-CSE-MsgGUID: oKNq0P70TA+M5pG3q/cDLA== X-IronPort-AV: E=McAfee;i="6700,10204,11171"; a="33345948" X-IronPort-AV: E=Sophos;i="6.10,166,1719903600"; d="scan'208";a="33345948" Received: from orviesa007.jf.intel.com ([10.64.159.147]) by fmvoesa105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 22 Aug 2024 03:38:30 -0700 X-CSE-ConnectionGUID: cuAvwW+dRVSU9b51/8aO/w== X-CSE-MsgGUID: 3qTwVNWXRKCNDx+reoUb2g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,166,1719903600"; d="scan'208";a="61933344" Received: from silpixa00401119.ir.intel.com ([10.55.129.167]) by orviesa007.jf.intel.com with ESMTP; 22 Aug 2024 03:38:31 -0700 From: Anatoly Burakov To: dev@dpdk.org, Robin Jarry Cc: bruce.richardson@intel.com Subject: [PATCH v6 2/4] usertools/cpu_layout: print out NUMA nodes Date: Thu, 22 Aug 2024 11:38:24 +0100 Message-ID: <40cf4ee32f15952457ac5526cfce64728bd13d32.1724323106.git.anatoly.burakov@intel.com> X-Mailer: git-send-email 2.43.5 In-Reply-To: <3dd16eeb15638b8429c38fa77aa83101bf375143.1724323106.git.anatoly.burakov@intel.com> References: 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-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 --- Notes: v5 -> v6: - Track NUMA changes per socket to avoid issues with missing cores v2 -> v3: - Sort imports alphabetically 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 e133fb8ad3..5972cfecdb 100755 --- a/usertools/cpu_layout.py +++ b/usertools/cpu_layout.py @@ -5,6 +5,7 @@ """Display CPU topology information.""" +import glob import typing as T @@ -29,12 +30,21 @@ def read_sysfs(path: str) -> str: return fd.read().strip() +def read_numa_node(base: str) -> int: + """Read the NUMA node of a CPU.""" + 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: T.Tuple[str, ...], col_widths: T.List[int]) -> None: """Print a row of a table with the given column widths.""" 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): @@ -56,6 +66,7 @@ def main() -> None: sockets_s: T.Set[int] = set() cores_s: T.Set[int] = set() core_map: T.Dict[T.Tuple[int, int], T.List[int]] = {} + numa_map: T.Dict[int, int] = {} base_path = "/sys/devices/system/cpu" cpus = range_expand(read_sysfs(f"{base_path}/online")) @@ -64,12 +75,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) @@ -78,24 +91,36 @@ 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: T.List[T.Tuple[str, ...]] = [] + # track NUMA changes per socket + prev_numa: T.Dict[int, T.Optional[int]] = {socket: None for socket in sockets} for c in cores: # Core, row: T.Tuple[str, ...] = (f"Core {c}",) - # [lcores, lcores, ...] + # [NUMA, lcores, NUMA, lcores, ...] for s in sockets: try: lcores = core_map[(s, c)] + + numa = numa_map[lcores[0]] + numa_changed = prev_numa[s] != numa + prev_numa[s] = numa + + 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