Skip to content

Commit 8346b35

Browse files
committed
[UR][L0] Memory resident limit to enabled peers
Previously, USM memory was made resident on all peer devices regardless of whether peer access had been explicitly enabled by the user. This change limits memory residency to only those peer devices for which peer access has been enabled via urUsmP2PEnablePeerAccessExp. To support this, a per-device `peers` vector is introduced (ur_device_handle_t_::PeerStatus) that tracks the peer-access state (ENABLED, DISABLED, NO_CONNECTION) for every device on the platform. The vector is populated at platform initialisation time using zeDeviceGetP2PProperties and updated atomically when urUsmP2PEnablePeerAccessExp / urUsmP2PDisablePeerAccessExp are called. Key changes: - device.hpp / device.cpp: introduce the PeerStatus enum (ENABLED, DISABLED, NO_CONNECTION) and peers vector on ur_device_handle_t_; add stream operators for logging device id and PeerStatus values. - platform.cpp: populate `peers` for every device during device-cache initialisation. - v2/context.cpp: add changeResidentDevice(), getDevicesWhoseAllocationsCanBeAccessedFrom(), and getDevicesWhichCanAccessAllocationsPresentOn() to update memory residency in existing USM pools when peer-access state changes. Fix urContextRelease to only remove the context from Platform->Contexts and delete it when the reference count actually reaches zero, preventing live contexts from being missed during subsequent peer-access updates. Replace UR_ASSERT with a conditional erase so that contexts created via urContextCreateWithNativeHandle (which are not tracked in Platform->Contexts) are released without error. Fix changeResidentDevice to hold a shared lock on the context Mutex while iterating usmPoolHandles, preventing a data race with concurrent addUsmPool/removeUsmPool calls. Fix a data race in getDevicesWhichCanAccessAllocationsPresentOn where peerCandidateDevice->peers.size() was read in UR_FASSERT before the scoped_lock was acquired; the lock is now moved before the assert, consistent with getDevicesWhoseAllocationsCanBeAccessedFrom. - v2/usm.cpp: build the resident-devices list from the source device plus only the explicitly enabled peer devices (via getDevicesWhichCanAccessAllocationsPresentOn), instead of passing all platform devices to umfLevelZeroMemoryProviderParamsSetResidentDevices. The previous code computed the enabled-peer set but discarded it, making memory resident on all devices unconditionally. - v2/usm_p2p.cpp: new file implementing urUsmP2PEnablePeerAccessExp, urUsmP2PDisablePeerAccessExp, and urUsmP2PPeerAccessGetInfoExp for the v2 adapter. Fix a data race where peers[] was written under a shared (read) lock; changed to an exclusive scoped_lock. Copy Platform->Contexts under ContextsMutex and iterate outside the critical section to avoid holding the mutex during heavy changeResidentDevice calls, reducing deadlock risk. - CMakeLists.txt: move usm_p2p.cpp from the v1 build into v2/usm_p2p.cpp in the v2 build so the v2 adapter uses its own P2P implementation. - usm_p2p.cpp (v1): add diagnostic log messages to urUsmP2PEnablePeerAccessExp and urUsmP2PDisablePeerAccessExp clarifying that the operations are no-ops in v1 because P2P is always enabled there. - sycl/source/device.cpp: add a cross-platform check in ext_oneapi_enable_peer_access / ext_oneapi_disable_peer_access and throw sycl::exception(errc::invalid) when the two devices belong to different platforms. Fix exception messages: "Can not" -> "Cannot". - ur_pool_manager.hpp: propagate resident-devices information through pool descriptors. - backtrace.hpp / backtrace_lin.cpp: replace #define MAX_BACKTRACE_FRAMES in backtrace.hpp with a static constexpr int; remove the now-redundant redeclaration from backtrace_lin.cpp. This avoids macro leakage and improves type safety. - test/memory_residency.cpp: fix memory leak in allocationExistsOnPeerWithEnabledAccess by freeing the USM allocation. Co-authored-by: Łukasz Ślusarczyk <lukasz.slusarczyk@intel.com> Co-authored-by: Lukasz Dorau <lukasz.dorau@intel.com> Signed-off-by: Lukasz Dorau <lukasz.dorau@intel.com>
1 parent 929fd90 commit 8346b35

17 files changed

Lines changed: 457 additions & 112 deletions

File tree

sycl/source/device.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,19 +212,33 @@ bool device::has(aspect Aspect) const { return impl->has(Aspect); }
212212
void device::ext_oneapi_enable_peer_access(const device &peer) {
213213
ur_device_handle_t Device = impl->getHandleRef();
214214
ur_device_handle_t Peer = peer.impl->getHandleRef();
215-
if (Device != Peer) {
216-
detail::adapter_impl &Adapter = impl->getAdapter();
217-
Adapter.call<detail::UrApiKind::urUsmP2PEnablePeerAccessExp>(Device, Peer);
215+
216+
if (Device == Peer)
217+
return;
218+
219+
if (peer.get_platform() != get_platform()) {
220+
throw exception(errc::invalid,
221+
"Cannot enable peer access between different platforms");
218222
}
223+
224+
impl->getAdapter().call<detail::UrApiKind::urUsmP2PEnablePeerAccessExp>(
225+
Device, Peer);
219226
}
220227

221228
void device::ext_oneapi_disable_peer_access(const device &peer) {
222229
ur_device_handle_t Device = impl->getHandleRef();
223230
ur_device_handle_t Peer = peer.impl->getHandleRef();
224-
if (Device != Peer) {
225-
detail::adapter_impl &Adapter = impl->getAdapter();
226-
Adapter.call<detail::UrApiKind::urUsmP2PDisablePeerAccessExp>(Device, Peer);
231+
232+
if (Device == Peer)
233+
return;
234+
235+
if (peer.get_platform() != get_platform()) {
236+
throw exception(errc::invalid,
237+
"Cannot disable peer access between different platforms");
227238
}
239+
240+
impl->getAdapter().call<detail::UrApiKind::urUsmP2PDisablePeerAccessExp>(
241+
Device, Peer);
228242
}
229243

230244
bool device::ext_oneapi_can_access_peer(const device &peer,

unified-runtime/source/adapters/level_zero/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ if(UR_BUILD_ADAPTER_L0_V2)
150150
${CMAKE_CURRENT_SOURCE_DIR}/helpers/kernel_helpers.cpp
151151
${CMAKE_CURRENT_SOURCE_DIR}/helpers/memory_helpers.cpp
152152
${CMAKE_CURRENT_SOURCE_DIR}/helpers/mutable_helpers.cpp
153-
${CMAKE_CURRENT_SOURCE_DIR}/usm_p2p.cpp
154153
${CMAKE_CURRENT_SOURCE_DIR}/virtual_mem.cpp
155154
${CMAKE_CURRENT_SOURCE_DIR}/../../ur/ur.cpp
156155
${CMAKE_CURRENT_SOURCE_DIR}/sampler.hpp
@@ -194,6 +193,7 @@ if(UR_BUILD_ADAPTER_L0_V2)
194193
${CMAKE_CURRENT_SOURCE_DIR}/v2/queue_immediate_in_order.cpp
195194
${CMAKE_CURRENT_SOURCE_DIR}/v2/queue_immediate_out_of_order.cpp
196195
${CMAKE_CURRENT_SOURCE_DIR}/v2/usm.cpp
196+
${CMAKE_CURRENT_SOURCE_DIR}/v2/usm_p2p.cpp
197197
)
198198
install_ur_library(ur_adapter_level_zero_v2)
199199

unified-runtime/source/adapters/level_zero/context.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ ur_result_t urContextCreate(
4141

4242
Context->initialize();
4343
*RetContext = reinterpret_cast<ur_context_handle_t>(Context);
44+
// TODO: delete below 'if' when memory isolation in the context is
45+
// implemented in the driver
4446
if (IndirectAccessTrackingEnabled) {
4547
std::scoped_lock<ur_shared_mutex> Lock(Platform->ContextsMutex);
4648
Platform->Contexts.push_back(*RetContext);

unified-runtime/source/adapters/level_zero/device.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,3 +2353,24 @@ void ZeUSMImportExtension::doZeUSMRelease(ze_driver_handle_t DriverHandle,
23532353
void *HostPtr) {
23542354
ZE_CALL_NOCHECK(zexDriverReleaseImportedPointer, (DriverHandle, HostPtr));
23552355
}
2356+
2357+
std::ostream &operator<<(std::ostream &os,
2358+
ur_device_handle_t_ const &device_handle) {
2359+
if (device_handle.Id.has_value()) {
2360+
return os << device_handle.Id.value();
2361+
}
2362+
return os << "NONE";
2363+
}
2364+
2365+
std::ostream &operator<<(std::ostream &os,
2366+
ur_device_handle_t_::PeerStatus peer_status) {
2367+
switch (peer_status) {
2368+
case ur_device_handle_t_::PeerStatus::DISABLED:
2369+
return os << "DISABLED";
2370+
case ur_device_handle_t_::PeerStatus::ENABLED:
2371+
return os << "ENABLED";
2372+
case ur_device_handle_t_::PeerStatus::NO_CONNECTION:
2373+
return os << "NO_CONNECTION";
2374+
}
2375+
return os << "UNKNOWN";
2376+
}

unified-runtime/source/adapters/level_zero/device.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,29 @@ struct ur_device_handle_t_ : ur_object {
261261
std::unordered_map<ur_exp_image_native_handle_t, ze_image_handle_t>
262262
ZeOffsetToImageHandleMap;
263263

264-
// unique ephemeral identifer of the device in the adapter
264+
// Devices which user enabled p2p access by
265+
// urUsmP2P(Enable|Disable)PeerAccessExp. Devices are indexed by device id.
266+
enum class PeerStatus : char { ENABLED, DISABLED, NO_CONNECTION };
267+
std::vector<PeerStatus>
268+
peers; // info if our device can access given peer device allocations
269+
270+
// unique ephemeral identifier of the device in the adapter
265271
std::optional<DeviceId> Id;
266272

267273
ur::RefCount RefCount;
268274
};
269275

276+
std::ostream &operator<<(std::ostream &os,
277+
ur_device_handle_t_ const &device_handle);
278+
std::ostream &operator<<(std::ostream &os,
279+
ur_device_handle_t_::PeerStatus peer_status);
280+
270281
// Collects a flat vector of unique devices for USM memory pool creation.
271282
// Traverses the input devices and their sub-devices, ensuring each Level Zero
272283
// device handle appears only once in the result.
273284
inline std::vector<ur_device_handle_t> CollectDevicesForUsmPoolCreation(
274285
const std::vector<ur_device_handle_t> &Devices) {
286+
275287
std::vector<ur_device_handle_t> DevicesAndSubDevices;
276288
std::unordered_set<ze_device_handle_t> Seen;
277289

unified-runtime/source/adapters/level_zero/platform.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,9 @@ ur_platform_handle_t_::getDeviceFromNativeHandle(ze_device_handle_t ZeDevice) {
746746
std::shared_lock<ur_shared_mutex> Lock(URDevicesCacheMutex);
747747
auto it = std::find_if(URDevicesCache.begin(), URDevicesCache.end(),
748748
[&](std::unique_ptr<ur_device_handle_t_> &D) {
749-
return D.get()->ZeDevice == ZeDevice &&
750-
(D.get()->RootDevice == nullptr ||
751-
D.get()->RootDevice->RootDevice == nullptr);
749+
return D->ZeDevice == ZeDevice &&
750+
(D->RootDevice == nullptr ||
751+
D->RootDevice->RootDevice == nullptr);
752752
});
753753
if (it != URDevicesCache.end()) {
754754
return (*it).get();
@@ -914,6 +914,43 @@ ur_result_t ur_platform_handle_t_::populateDeviceCacheIfNeeded() {
914914
ZeDeviceSynchronizeSupported = Supported;
915915
}
916916

917+
for (auto &dev : URDevicesCache) {
918+
dev->peers = std::vector<ur_device_handle_t_::PeerStatus>(
919+
URDevicesCache.size(), ur_device_handle_t_::PeerStatus::NO_CONNECTION);
920+
921+
for (size_t peerId = 0; peerId < URDevicesCache.size(); ++peerId) {
922+
if (peerId == dev->Id.value())
923+
continue;
924+
925+
ZeStruct<ze_device_p2p_properties_t> p2pProperties;
926+
ZE2UR_CALL(zeDeviceGetP2PProperties,
927+
(dev->ZeDevice, URDevicesCache[peerId]->ZeDevice,
928+
&p2pProperties));
929+
if (!(p2pProperties.flags & ZE_DEVICE_P2P_PROPERTY_FLAG_ACCESS)) {
930+
UR_LOG(INFO,
931+
"p2p access to memory of dev:{} from dev:{} not possible due to "
932+
"lack of p2p property",
933+
peerId, dev->Id.value());
934+
continue;
935+
}
936+
937+
ze_bool_t p2p;
938+
ZE2UR_CALL(zeDeviceCanAccessPeer,
939+
(dev->ZeDevice, URDevicesCache[peerId]->ZeDevice, &p2p));
940+
if (!p2p) {
941+
UR_LOG(INFO,
942+
"p2p access to memory of dev:{} from dev:{} not possible due to "
943+
"no connection",
944+
peerId, dev->Id.value());
945+
continue;
946+
}
947+
948+
UR_LOG(INFO, "p2p access to memory of dev:{} from dev:{} can be enabled",
949+
peerId, dev->Id.value());
950+
dev->peers[peerId] = ur_device_handle_t_::PeerStatus::DISABLED;
951+
}
952+
}
953+
917954
return UR_RESULT_SUCCESS;
918955
}
919956

unified-runtime/source/adapters/level_zero/platform.hpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,10 @@ struct ur_platform_handle_t_ : ur::handle_base<ur::level_zero::ddi_getter>,
9797
uint32_t VersionMinor,
9898
uint32_t VersionBuild);
9999

100-
// Keep track of all contexts in the platform. This is needed to manage
101-
// a lifetime of memory allocations in each context when there are kernels
102-
// with indirect access.
103-
// TODO: should be deleted when memory isolation in the context is implemented
104-
// in the driver.
100+
// Keep track of all contexts in the platform. In v1 L0 this is needed to
101+
// manage a lifetime of memory allocations in each context when there are
102+
// kernels with indirect access. In v2 it is used during
103+
// ext_oneapi_enable_peer_access and ext_oneapi_disable_peer_access calls.
105104
std::list<ur_context_handle_t> Contexts;
106105
ur_shared_mutex ContextsMutex;
107106

unified-runtime/source/adapters/level_zero/usm_p2p.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@
1212

1313
namespace ur::level_zero {
1414

15-
ur_result_t urUsmP2PEnablePeerAccessExp(ur_device_handle_t /*commandDevice*/,
16-
ur_device_handle_t /*peerDevice*/) {
15+
ur_result_t urUsmP2PEnablePeerAccessExp(ur_device_handle_t commandDevice,
16+
ur_device_handle_t peerDevice) {
1717

18-
// L0 has peer devices enabled by default
18+
UR_LOG(INFO,
19+
"user enables peer access to memory of {} from {}, ignored, in V1 P2P "
20+
"is always enabled",
21+
*peerDevice, *commandDevice);
1922
return UR_RESULT_SUCCESS;
2023
}
2124

22-
ur_result_t urUsmP2PDisablePeerAccessExp(ur_device_handle_t /*commandDevice*/,
23-
ur_device_handle_t /*peerDevice*/) {
25+
ur_result_t urUsmP2PDisablePeerAccessExp(ur_device_handle_t commandDevice,
26+
ur_device_handle_t peerDevice) {
2427

25-
// L0 has peer devices enabled by default
28+
UR_LOG(INFO,
29+
"user disables peer access to memory of {} from {}, ignored, in V1 "
30+
"P2P is always enabled",
31+
*peerDevice, *commandDevice);
2632
return UR_RESULT_SUCCESS;
2733
}
2834

0 commit comments

Comments
 (0)