Skip to content

UCP/DEVICE: Add multi-lane support#11268

Merged
ofirfarjun7 merged 14 commits into
openucx:masterfrom
Artemy-Mellanox:multilane
May 6, 2026
Merged

UCP/DEVICE: Add multi-lane support#11268
ofirfarjun7 merged 14 commits into
openucx:masterfrom
Artemy-Mellanox:multilane

Conversation

@Artemy-Mellanox

@Artemy-Mellanox Artemy-Mellanox commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@Artemy-Mellanox Artemy-Mellanox force-pushed the multilane branch 2 times, most recently from ca9ba6b to af04b9e Compare March 16, 2026 21:42
Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment thread src/ucp/core/ucp_device.c
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated

static ucp_lane_index_t ucp_device_ep_find_lane(const ucp_ep_h ep, ucp_rsc_index_t tl_id)
{
ucp_ep_config_t *ep_config = ucp_ep_config(ep);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it better to use ucp_ep_num_lanes to get num lanes here, as you don't need ep_config var in this function?

Comment thread src/uct/ib/mlx5/gdaki/gdaki.c Outdated
Comment on lines +515 to +516
iface_attr->ctl_device = uct_cuda_get_cuda_device(iface->cuda_dev);
iface_attr->dev_num_paths = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
iface_attr->ctl_device = uct_cuda_get_cuda_device(iface->cuda_dev);
iface_attr->dev_num_paths = 1;
iface_attr->ctl_device = uct_cuda_get_cuda_device(iface->cuda_dev);
iface_attr->dev_num_paths = 1;

Comment thread src/ucp/core/ucp_device.c Outdated
(remote_sys_dev != lane_key->dst_sys_dev)) {
ucs_trace("lane[%u] wrong destination sys_dev: dst_sys_dev=%u",
lane, lane_key->dst_sys_dev);
if (wiface->attr.ctl_device != local_sys_dev) {

This comment was marked as resolved.

Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c Outdated
Comment thread src/ucp/core/ucp_device.c
void *local_addr;

ucp_device_get_tl_bitmap(worker, tl_bitmap, local_sys_dev);
num_lanes = UCS_STATIC_BITMAP_POPCOUNT(tl_bitmap[tl_type]);

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need to pack anything lane specific for no-lkey lanes, so we don't allocate data for it

Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
unsigned mem_list_index, void **addr_p)
{
const size_t elem_size = sizeof(uct_device_remote_mem_list_elem_t);
const size_t elem_size = sizeof(uct_device_remote_mem_elem_t);

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds multi-lane support for UCP device memory lists and updates UCT device-element packing/types so device-side operations can select among multiple device-EP lanes.

Changes:

  • Introduce new device memory element/layout types (uct_device_mem_elem_t, per-lane TL arrays) and propagate signature updates across UCT/UCP and CUDA test kernels.
  • Extend UCT iface attributes/capabilities with ctl_device and UCT_IFACE_FLAG_DEVICE_LKEY to support correct device endpoint selection and packing behavior.
  • Rework UCP device mem-list creation/packing to build multi-lane descriptors and adjust wireup path selection behavior for device lanes.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/gtest/uct/test_device.cc Updates CUDA context handling and switches tests to the new packed device mem element type.
test/gtest/uct/cuda/test_kernels_uct.h Updates kernel launcher prototypes to use uct_device_mem_elem_t.
test/gtest/uct/cuda/test_kernels_uct.cu Updates CUDA kernels/launchers to accept uct_device_mem_elem_t.
test/gtest/uct/cuda/test_kernels.h Updates test kernel APIs to use uct_device_mem_elem_t.
test/gtest/uct/cuda/test_kernels.cu Updates CUDA kernels to use uct_device_mem_elem_t.
test/gtest/uct/cuda/test_cuda_ipc_device.cc Updates CUDA IPC device tests to the new packed mem element type.
test/gtest/ucp/test_ucp_device.cc Adjusts kernel test expectations/usage to match updated device structures.
test/gtest/ucp/cuda/test_kernels.h Removes per-kernel state fields from the device-kernel result struct.
test/gtest/ucp/cuda/test_kernels.cu Removes device-side state parsing logic tied to the old lane/layout assumptions.
src/uct/ib/mlx5/gdaki/gdaki.cuh Updates GDA device EP APIs to accept uct_device_mem_elem_t and new source element semantics.
src/uct/ib/mlx5/gdaki/gdaki.c Exposes DEVICE_LKEY capability and sets ctl_device for GDA iface.
src/uct/ib/mlx5/dv/ib_mlx5dv_md.c Updates MD mem element pack signature to the new type.
src/uct/cuda/cuda_ipc/cuda_ipc_md.c Updates CUDA IPC MD mem element pack signature to the new type.
src/uct/cuda/cuda_ipc/cuda_ipc_iface.c Initializes ctl_device for CUDA IPC iface attributes.
src/uct/cuda/cuda_ipc/cuda_ipc.cuh Updates CUDA IPC device EP functions to take uct_device_mem_elem_t.
src/uct/base/uct_md.h Updates MD ops callback typedef to use uct_device_mem_elem_t.
src/uct/base/uct_md.c Updates uct_md_mem_elem_pack() signature to use uct_device_mem_elem_t.
src/uct/api/v2/uct_v2.h Updates the public v2 API signature for uct_md_mem_elem_pack().
src/uct/api/uct_def.h Renames/introduces device element typedefs for multi-lane descriptors.
src/uct/api/uct.h Adds UCT_IFACE_FLAG_DEVICE_LKEY and uct_iface_attr::ctl_device.
src/uct/api/device/uct_device_types.h Redefines device memory list element layouts for multi-lane support.
src/uct/api/device/uct_device_impl.h Updates device EP put/atomic/get_ptr APIs to use uct_device_mem_elem_t.
src/ucp/wireup/select.c Adjusts path-count logic for DEVICE lane selection.
src/ucp/wireup/address.c Forces num_paths=1 for device endpoints when gathering devices.
src/ucp/core/ucp_device.c Reworks host-side packing/allocation of local/remote device mem list handles for multi-lane descriptors.
src/ucp/api/device/ucp_device_types.h Adds num_lanes to device mem list handle headers and updates element types.
src/ucp/api/device/ucp_device_impl.h Adds lane selection macros and updates device-side send/put/counter paths for multi-lane descriptors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ucp/core/ucp_device.c
Comment on lines +520 to +532
for (i = 0; i < num_lanes;) {
UCS_STATIC_BITMAP_FOR_EACH_BIT(tl_id, tl_bitmap) {
status = ucp_device_remote_mem_list_element_pack(ucp_element, tl_id,
tl_element);
if (status != UCS_OK) {
return status;
}

tl_element = UCS_PTR_TYPE_OFFSET(tl_element, *tl_element);
i++;
}
}

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ucp_device_remote_mem_list_fill() repeats packing the TL bitmap until i == num_lanes. If UCS_STATIC_BITMAP_POPCOUNT(*tl_bitmap) differs from num_lanes, this will either duplicate TL entries (popcount < num_lanes) or overflow the allocated tl[] array (popcount > num_lanes). It would be safer to iterate the bitmap once and assert/verify that the number of packed entries equals num_lanes.

Suggested change
for (i = 0; i < num_lanes;) {
UCS_STATIC_BITMAP_FOR_EACH_BIT(tl_id, tl_bitmap) {
status = ucp_device_remote_mem_list_element_pack(ucp_element, tl_id,
tl_element);
if (status != UCS_OK) {
return status;
}
tl_element = UCS_PTR_TYPE_OFFSET(tl_element, *tl_element);
i++;
}
}
i = 0;
UCS_STATIC_BITMAP_FOR_EACH_BIT(tl_id, tl_bitmap) {
if (i >= num_lanes) {
return UCS_ERR_INVALID_PARAM;
}
status = ucp_device_remote_mem_list_element_pack(ucp_element, tl_id,
tl_element);
if (status != UCS_OK) {
return status;
}
tl_element = UCS_PTR_TYPE_OFFSET(tl_element, *tl_element);
i++;
}
if (i != num_lanes) {
return UCS_ERR_INVALID_PARAM;
}

Copilot uses AI. Check for mistakes.
Comment thread src/ucp/core/ucp_device.c Outdated
}
}

ucs_assert(tl_type < UCP_DEVICE_TL_TYPE_LAST);

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no tl_type bitmap matches an element EP, this ucs_assert() will abort the process. This can be triggered by runtime configuration mismatches and should return an error status (propagated to the caller) rather than asserting, so applications can handle it gracefully.

Suggested change
ucs_assert(tl_type < UCP_DEVICE_TL_TYPE_LAST);
if (tl_type == UCP_DEVICE_TL_TYPE_LAST) {
ucs_error("failed to find matching tl_type for remote mem "
"list element");
status = UCS_ERR_INVALID_PARAM;
goto out;
}

Copilot uses AI. Check for mistakes.
Comment thread src/uct/api/uct.h Outdated
/* Interface capability */
#define UCT_IFACE_FLAG_INTER_NODE UCS_BIT(54) /**< Interface is inter-node capable */
#define UCT_IFACE_FLAG_DEVICE_EP UCS_BIT(55) /**< Interface supports device endpoint */
#define UCT_IFACE_FLAG_DEVICE_LKEY UCS_BIT(56) /**< Interface require lkey for device operations */

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar in the flag description should be singular: "Interface requires lkey for device operations".

Suggested change
#define UCT_IFACE_FLAG_DEVICE_LKEY UCS_BIT(56) /**< Interface require lkey for device operations */
#define UCT_IFACE_FLAG_DEVICE_LKEY UCS_BIT(56) /**< Interface requires lkey for device operations */

Copilot uses AI. Check for mistakes.
Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment on lines +337 to +341
const auto mem_element = static_cast<uct_device_remote_mem_elem_t*>(
UCS_PTR_BYTE_OFFSET(mem_list_h->mem_elements,
mem_list_index * elem_size));

return uct_device_ep_get_ptr(mem_element->device_ep,
&mem_element->uct_mem_element,
return uct_device_ep_get_ptr(mem_element->tl[0].ep, &mem_element->tl[0].uct,

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ucp_device_get_ptr() indexes mem_elements with a fixed sizeof(uct_device_remote_mem_elem_t) stride, but remote mem list elements are now variable-sized (depend on mem_list_h->num_lanes). This will compute the wrong element address and can read out-of-bounds when num_lanes > 1. Use the same stride calculation as UCP_DEVICE_GET_ELEM(...) (or reuse the macro) to locate the mem_list_index element.

Copilot uses AI. Check for mistakes.
Comment thread src/ucp/core/ucp_device.c
Comment on lines +248 to +249
int tl_type = UCP_DEVICE_TL_TYPE_LKEY;
ucp_tl_bitmap_t tl_bitmap[UCP_DEVICE_TL_TYPE_LAST] = {};

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ucp_device_local_mem_list_create_handle() initializes tl_type to UCP_DEVICE_TL_TYPE_LKEY and never switches to UCP_DEVICE_TL_TYPE_NOLKEY if there are no DEVICE_LKEY transports. This can lead to exporting a handle with num_lanes == 0, which later causes division/modulo by zero in UCP_DEVICE_GET_LANE() and leaves no packed TL elements. Add a fallback/selection based on which bitmap is non-empty (or return an error) to guarantee num_lanes >= 1.

Copilot uses AI. Check for mistakes.
Comment thread src/ucp/core/ucp_device.c
Comment on lines +563 to +565
ucp_device_get_tl_bitmap(ep->worker, tl_bitmap, local_sys_dev);
num_lanes = UCS_STATIC_BITMAP_POPCOUNT(tl_bitmap[UCP_DEVICE_TL_TYPE_LKEY]);
if (!num_lanes) {

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

num_lanes is derived only from the LKEY bitmap here, but later tl_type may be selected per-element (LKEY vs NOLKEY). If an element ends up using the NOLKEY bitmap (different popcount), the code will pack a different number of TL entries than num_lanes implies, risking duplicate entries or out-of-bounds writes. Consider selecting a single tl_type/num_lanes for the whole handle or validating that all elements match the chosen bitmap size.

Copilot uses AI. Check for mistakes.
Comment thread src/ucp/core/ucp_device.c
uct_element = UCS_PTR_BYTE_OFFSET(uct_element, uct_elem_size);
}

handle->version = UCP_DEVICE_MEM_LIST_VERSION_V1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider UCP_DEVICE_MEM_LIST_MULTI_LANE as the handle structure changed?

Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment on lines +108 to +110
#define UCP_DEVICE_GET_LANE(_handle, _channel_id, _lane, _uct_channel_id) \
_lane = _channel_id % _handle->num_lanes; \
_uct_channel_id = _channel_id / _handle->num_lanes;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ucs_assertv(_handle != NULL, "dest memlist can't be null")
before deref, clearer failure for users

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in other APIs we not validate objects for NULL

Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
Comment on lines +113 to +118
#define UCP_DEVICE_GET_ELEM(_handle, _index, _lane) \
static_cast<ucs_typeof(_handle->mem_elements[0])*>(UCS_PTR_BYTE_OFFSET( \
_handle->mem_elements, \
(sizeof(_handle->mem_elements[0]) + \
(sizeof(_handle->mem_elements[0].tl[0]) * _handle->num_lanes)) * \
_index))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same idea here

Comment thread src/ucp/api/device/ucp_device_impl.h Outdated
_uct_channel_id = _channel_id / _handle->num_lanes;


#define UCP_DEVICE_GET_ELEM(_handle, _index, _lane) \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define UCP_DEVICE_GET_ELEM(_handle, _index, _lane) \
#define UCP_DEVICE_GET_ELEM(_handle, _index) \

Comment thread src/ucp/core/ucp_device.c
return status;
}

static ucp_lane_index_t ucp_device_ep_find_lane(const ucp_ep_h ep, ucp_rsc_index_t tl_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to assume tl_id is a device lane?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

device API working with device lanes, but this function is not specific

Comment thread src/ucp/core/ucp_device.c
Comment thread src/uct/cuda/cuda_ipc/cuda_ipc_iface.c
Comment on lines +113 to +114
#define UCP_DEVICE_GET_ELEM(_handle, _index) \
static_cast<ucs_typeof(_handle->mem_elements[0])*>(UCS_PTR_BYTE_OFFSET( \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it be inline function?

static_cast<ucs_typeof(_handle->mem_elements[0])*>(UCS_PTR_BYTE_OFFSET( \
_handle->mem_elements, \
(sizeof(_handle->mem_elements[0]) + \
(sizeof(_handle->mem_elements[0].tl[0]) * _handle->num_lanes)) * \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how much it affects performance?

/**
* Number of lanes in each memory descriptor.
*/
uint16_t num_lanes;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe uint8_t?

/**
* Number of lanes in each memory descriptor.
*/
uint16_t num_lanes;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider optimizing this to be something like MAX_LANES or MAX_MDS

Comment thread src/ucp/core/ucp_device.c
Comment on lines +248 to +249
int tl_type = UCP_DEVICE_TL_TYPE_LKEY;
ucp_tl_bitmap_t tl_bitmap[UCP_DEVICE_TL_TYPE_LAST] = {};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialized vars should be first

Comment thread src/ucp/core/ucp_device.c
uct_element->addr = ucp_element->remote_addr;
tl_element = uct_element->tl;
for (i = 0; i < num_lanes;) {
UCS_STATIC_BITMAP_FOR_EACH_BIT(tl_id, tl_bitmap) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it mean we could pack tl which does not have a lane on the current endpoint? because tl_bitmap was not created according to ep lanes

Comment thread src/ucp/wireup/address.c
Comment on lines +459 to +460
if (!(iface_attr->cap.flags & UCT_IFACE_FLAG_DEVICE_EP)) {
dev->num_paths = ucs_min(max_num_paths, iface_attr->dev_num_paths);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Comment thread src/ucp/wireup/select.c
local_num_paths = iface_attr->dev_num_paths;
remote_num_paths = ae->dev_num_paths;

if (bw_info->criteria.lane_type == UCP_LANE_TYPE_DEVICE) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks weird..
what is the value of iface_attr->dev_num_paths ?

Comment thread src/uct/api/uct.h
/* Interface capability */
#define UCT_IFACE_FLAG_INTER_NODE UCS_BIT(54) /**< Interface is inter-node capable */
#define UCT_IFACE_FLAG_DEVICE_EP UCS_BIT(55) /**< Interface supports device endpoint */
#define UCT_IFACE_FLAG_DEVICE_LKEY UCS_BIT(56) /**< Interface requires lkey for device operations */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why needed?
and can we add it to query v2?

Comment thread src/uct/api/uct.h
compared to using only a single
endpoint. */

ucs_sys_device_t ctl_device; /**< System device controlling this iface,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add it to query v2?

@openucx openucx deleted a comment from svc-nixl May 4, 2026

@ofirfarjun7 ofirfarjun7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix comments in next PR

@ofirfarjun7 ofirfarjun7 merged commit b788849 into openucx:master May 6, 2026
153 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants