[XPU] Honor device_ids for worker device placement (#56015)

Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com> Co-authored-by: Kunshang Ji <kunshang.ji@intel.com>

Chaojun Zhang committed Sep 16, 2026 at 11:53 UTC 622934e61fb27aceea346b95d82edaa350d985d1
3 files changed +44 -19
.buildkite/intel_jobs/test-intel.yaml
+1 -1
@@ -97,7 +97,7 @@ steps:
97 commands:
98 - >-
99 bash .buildkite/scripts/hardware_ci/run-intel-test.sh
100 - 'cd tests &&
100 + 'cd tests && pip install dpctl &&
101 pytest -v -s v1/core --ignore=v1/core/test_reset_prefix_cache_e2e.py --ignore=v1/core/test_scheduler_e2e.py &&
102 pytest -v -s v1/engine --ignore=v1/engine/test_output_processor.py &&
103 pytest -v -s v1/sample --ignore=v1/sample/test_logprobs.py --ignore=v1/sample/test_logprobs_e2e.py -k "not test_topk_only and not test_topp_only and not test_topk_and_topp" &&
vllm/v1/engine/core.py
+4 -7
@@ -2452,13 +2452,10 @@ class EngineCoreActorMixin:
2452 def _set_visible_devices(self, vllm_config: VllmConfig, local_dp_rank: int):
2453 from vllm.platforms import current_platform
2454
2455 - if current_platform.is_xpu():
2456 - pass
2457 - else:
2458 - device_control_env_var = current_platform.device_control_env_var
2459 - self._set_assigned_physical_gpu_ids(
2460 - vllm_config, local_dp_rank, device_control_env_var
2461 - )
2455 + device_control_env_var = current_platform.device_control_env_var
2456 + self._set_assigned_physical_gpu_ids(
2457 + vllm_config, local_dp_rank, device_control_env_var
2458 + )
2459
2460 def _set_assigned_physical_gpu_ids(
2461 self,
vllm/v1/worker/xpu_worker.py
+39 -11
@@ -115,27 +115,55 @@ class XPUWorker(Worker):
115
116 self.local_rank += dp_local_rank * replica_world_size
117
118 - assert self.local_rank < visible_device_count, (
119 - f"DP adjusted local rank {self.local_rank} is out of bounds. "
120 - )
121 - assert parallel_config.local_world_size <= visible_device_count, (
122 - f"local_world_size ({parallel_config.local_world_size}) must "
123 - f"be less than or equal to the number of visible devices "
124 - f"({visible_device_count})."
125 - )
126 -
118 device = self.device_config.device
119 if (
120 isinstance(device, torch.device)
121 and device.type == "xpu"
122 and current_platform.is_xpu()
123 ):
133 - self.device = torch.device(f"xpu:{self.local_rank}")
124 + assigned_physical_gpu_ids = self.parallel_config.assigned_physical_gpu_ids
125 + if assigned_physical_gpu_ids is not None:
126 + from vllm.platforms.interface import set_assigned_physical_gpu_ids
127 +
128 + set_assigned_physical_gpu_ids(assigned_physical_gpu_ids)
129 + assert self.local_rank < len(assigned_physical_gpu_ids), (
130 + f"local_rank {self.local_rank} is out of bounds for "
131 + f"assigned_physical_gpu_ids {assigned_physical_gpu_ids}"
132 + )
133 + # NOTE: local_world_size is derived from parallel_config.nnodes,
134 + # which is only set for the "mp" multi-node backend. With the
135 + # "ray"/"external_launcher" backends nnodes stays 1, so
136 + # local_world_size collapses to the full world_size and this
137 + # check wrongly fires on cross-node deployments.
138 + # assigned_physical_gpu_ids is already per-node and the
139 + # local_rank bound above fully validates the mapping for
140 + # these backends, so skip the check for them.
141 + if parallel_config.distributed_executor_backend not in (
142 + "ray",
143 + "external_launcher",
144 + ):
145 + assert parallel_config.local_world_size <= len(
146 + assigned_physical_gpu_ids
147 + ), (
148 + f"local_world_size ({parallel_config.local_world_size})"
149 + " exceeds assigned_physical_gpu_ids count "
150 + f"({len(assigned_physical_gpu_ids)})"
151 + )
152 + else:
153 + assert self.local_rank < torch.accelerator.device_count(), (
154 + f"DP adjusted local rank {self.local_rank} is out of "
155 + f"bounds for {torch.accelerator.device_count()} devices."
156 + )
157 +
158 + visible_device_index = (
159 + current_platform.logical_device_id_to_visible_device_id(self.local_rank)
160 + )
161 + self.device = torch.device(f"xpu:{visible_device_index}")
162 torch.accelerator.set_device_index(self.device)
163 current_platform.check_if_supports_dtype(self.model_config.dtype)
164 torch.accelerator.empty_cache()
165 self.init_gpu_memory = torch.xpu.get_device_properties(
138 - self.local_rank
166 + visible_device_index
167 ).total_memory
168 else:
169 raise RuntimeError(f"Unsupported device type: {self.device_config.device}")