diff --git a/setup.py b/setup.py index c05280e40e..7c226a7242 100644 --- a/setup.py +++ b/setup.py @@ -927,7 +927,9 @@ def get_vllm_version() -> str: elif _is_tpu(): version += f"{sep}tpu" elif _is_cpu(): - if envs.VLLM_TARGET_DEVICE == "cpu": + # Check the local VLLM_TARGET_DEVICE (may be set by auto-detect above), + # not envs.VLLM_TARGET_DEVICE, so CPU-only hosts still get `+cpu`. + if VLLM_TARGET_DEVICE == "cpu": version += f"{sep}cpu" elif _is_xpu(): version += f"{sep}xpu" diff --git a/vllm/platforms/__init__.py b/vllm/platforms/__init__.py index af344acfcb..645da0a1fe 100644 --- a/vllm/platforms/__init__.py +++ b/vllm/platforms/__init__.py @@ -177,7 +177,6 @@ def cpu_platform_plugin() -> str | None: logger.debug( "Confirmed CPU platform is available because the machine is MacOS." ) - except Exception as e: logger.debug("CPU platform is not available because: %s", str(e)) diff --git a/vllm/utils/cpu_resource_utils.py b/vllm/utils/cpu_resource_utils.py index 4a56e7f643..bbf554d0cc 100644 --- a/vllm/utils/cpu_resource_utils.py +++ b/vllm/utils/cpu_resource_utils.py @@ -87,7 +87,13 @@ def get_memory_node_info(node_id: int = 0) -> MemoryNodeInfo: meminfo_path = f"/sys/devices/system/node/node{node_id}/meminfo" if not os.path.exists(meminfo_path): - raise RuntimeError(f"{meminfo_path} doesn't exit.") + # Non-NUMA systems (e.g. many RISC-V boards) don't expose per-node + # meminfo. Fall back to system-wide numbers from psutil. + vm = psutil.virtual_memory() + return MemoryNodeInfo( + total_memory=vm.total, + available_memory=vm.available, + ) meminfo = {} with open(meminfo_path) as f: @@ -147,19 +153,36 @@ def get_visible_memory_node() -> list[int]: @cache +def _synthesize_cpu_list() -> list[LogicalCPUInfo]: + """Synthesize a flat CPU list: each logical CPU is its own core on + NUMA node 0. Used when lscpu output is unavailable or unparsable + (e.g. macOS, RISC-V).""" + cpu_count = os.cpu_count() + assert cpu_count + return [LogicalCPUInfo(i, i, 0) for i in range(cpu_count)] + + def _get_cpu_list() -> list[LogicalCPUInfo]: if platform.system() == "Darwin": # For MacOS, no user-level CPU affinity and SMT, return all CPUs - cpu_count = os.cpu_count() - assert cpu_count - return [LogicalCPUInfo(i, i, 0) for i in range(cpu_count)] + return _synthesize_cpu_list() lscpu_output = subprocess.check_output( "lscpu --json --extended=CPU,CORE,NODE --online", shell=True, text=True ) - # For platform without NUMA, replace '-' to '0' - lscpu_output = re.sub(r'"node":\s*-\s*(,|\n)', r'"node": 0\1', lscpu_output) + # For platforms without NUMA, map bare `-` node to 0 so non-NUMA + # systems keep the existing behavior from #39781. + lscpu_output = re.sub(r'"node":\s*-\s*(,|\n|\})', r'"node": 0\1', lscpu_output) + + # On some architectures (notably RISC-V), lscpu also emits bare `-` + # for cpu/core. Quote them so the JSON parses; they will decode to + # -1 and be filtered out below, triggering the synthesized fallback. + lscpu_output = re.sub( + r'("(?:cpu|core)":\s*)-\s*(,|\n|\})', + r'\1"-"\2', + lscpu_output, + ) logical_cpu_list: list[LogicalCPUInfo] = json.loads( lscpu_output, object_hook=LogicalCPUInfo.json_decoder @@ -170,4 +193,9 @@ def _get_cpu_list() -> list[LogicalCPUInfo]: x for x in logical_cpu_list if -1 not in (x.id, x.physical_core, x.numa_node) ] + # If lscpu returned no valid entries (e.g. RISC-V where all fields + # are bare `-`), fall back to synthesized topology. + if not logical_cpu_list: + logical_cpu_list = _synthesize_cpu_list() + return logical_cpu_list