Skip to content

Commit 4751c96

Browse files
authored
[UR] Fix OpenCL handles generated from sub-devices (#17931)
Sub-devices don't appear in the list of devices, and so the prior OpenCL implementation would assume that they are invalid. With this patch, creating a sub-device will add it to a "cache" stored in the platform. Multiple `DeviceCreateWithNativeHandle` calls with the same native handle will result in the same UR handle. A test has been added to verify this. This unfortunately required shuffling headers around to avoid circular imports. This fixes an issue tracked internally by Intel as URT-903
1 parent 18e36c5 commit 4751c96

File tree

6 files changed

+126
-42
lines changed

6 files changed

+126
-42
lines changed

unified-runtime/source/adapters/opencl/adapter.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
88
//
99
//===----------------------------------------------------------------------===//
10+
#include "device.hpp"
1011
#include "logger/ur_logger.hpp"
1112
#include "platform.hpp"
1213

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,12 @@ UR_APIEXPORT ur_result_t UR_APICALL urDeviceCreateWithNativeHandle(
15981598
ur_native_handle_t hNativeDevice, ur_adapter_handle_t,
15991599
const ur_device_native_properties_t *pProperties,
16001600
ur_device_handle_t *phDevice) {
1601+
1602+
auto SetDeviceProps = [&]() {
1603+
(*phDevice)->IsNativeHandleOwned =
1604+
pProperties ? pProperties->isNativeHandleOwned : false;
1605+
};
1606+
16011607
cl_device_id NativeHandle = reinterpret_cast<cl_device_id>(hNativeDevice);
16021608

16031609
uint32_t NumPlatforms = 0;
@@ -1617,12 +1623,43 @@ UR_APIEXPORT ur_result_t UR_APICALL urDeviceCreateWithNativeHandle(
16171623
for (auto &Device : Devices) {
16181624
if (Device->CLDevice == NativeHandle) {
16191625
*phDevice = Device;
1620-
(*phDevice)->IsNativeHandleOwned =
1621-
pProperties ? pProperties->isNativeHandleOwned : false;
1626+
SetDeviceProps();
16221627
return UR_RESULT_SUCCESS;
16231628
}
16241629
}
16251630
}
1631+
1632+
// Handle sub-devices by storing/querying a map stored in the platform
1633+
cl_device_id Parent = nullptr;
1634+
CL_RETURN_ON_FAILURE(clGetDeviceInfo(NativeHandle, CL_DEVICE_PARENT_DEVICE,
1635+
sizeof(Parent), &Parent, nullptr));
1636+
if (Parent != nullptr) {
1637+
ur_device_handle_t ParentUrHandle;
1638+
// This will either create a new device handle, or return an existing one
1639+
UR_RETURN_ON_FAILURE(urDeviceCreateWithNativeHandle(
1640+
reinterpret_cast<ur_native_handle_t>(Parent), nullptr, nullptr,
1641+
&ParentUrHandle));
1642+
1643+
ur_platform_handle_t PlatformHandle = ParentUrHandle->Platform;
1644+
assert(PlatformHandle);
1645+
1646+
{
1647+
std::lock_guard lock{PlatformHandle->SubDevicesLock};
1648+
1649+
if (PlatformHandle->SubDevices.count(NativeHandle)) {
1650+
*phDevice = PlatformHandle->SubDevices[NativeHandle];
1651+
} else {
1652+
*phDevice = std::make_unique<ur_device_handle_t_>(
1653+
NativeHandle, PlatformHandle, ParentUrHandle)
1654+
.release();
1655+
PlatformHandle->SubDevices[NativeHandle] = *phDevice;
1656+
}
1657+
}
1658+
1659+
SetDeviceProps();
1660+
return UR_RESULT_SUCCESS;
1661+
}
1662+
16261663
return UR_RESULT_ERROR_INVALID_DEVICE;
16271664
}
16281665

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#pragma once
1111

1212
#include "common.hpp"
13+
#include "device.hpp"
1314
#include "platform.hpp"
1415

1516
struct ur_device_handle_t_ {
@@ -35,6 +36,12 @@ struct ur_device_handle_t_ {
3536
}
3637

3738
~ur_device_handle_t_() {
39+
if (ParentDevice) {
40+
// This does not need protected by a lock; this destructor can only run
41+
// exactly once. However, to prevent issues with the OpenCL handle being
42+
// reused, CLDevice must still be alive here.
43+
Platform->SubDevices.erase(CLDevice);
44+
}
3845
if (ParentDevice && IsNativeHandleOwned) {
3946
clReleaseDevice(CLDevice);
4047
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "platform.hpp"
1212
#include "adapter.hpp"
13+
#include "device.hpp"
1314

1415
static cl_int mapURPlatformInfoToCL(ur_platform_info_t URPropName) {
1516

@@ -191,3 +192,43 @@ urPlatformGetBackendOption(ur_platform_handle_t, const char *pFrontendOption,
191192
}
192193
return UR_RESULT_ERROR_INVALID_VALUE;
193194
}
195+
196+
ur_result_t ur_platform_handle_t_::InitDevices() {
197+
if (Devices.empty()) {
198+
cl_uint DeviceNum = 0;
199+
cl_int Res =
200+
clGetDeviceIDs(CLPlatform, CL_DEVICE_TYPE_ALL, 0, nullptr, &DeviceNum);
201+
202+
// Absorb the CL_DEVICE_NOT_FOUND and just return 0 in num_devices
203+
if (Res == CL_DEVICE_NOT_FOUND) {
204+
return UR_RESULT_SUCCESS;
205+
}
206+
207+
CL_RETURN_ON_FAILURE(Res);
208+
209+
std::vector<cl_device_id> CLDevices(DeviceNum);
210+
Res = clGetDeviceIDs(CLPlatform, CL_DEVICE_TYPE_ALL, DeviceNum,
211+
CLDevices.data(), nullptr);
212+
213+
// Absorb the CL_DEVICE_NOT_FOUND and just return 0 in num_devices
214+
if (Res == CL_DEVICE_NOT_FOUND) {
215+
return UR_RESULT_SUCCESS;
216+
}
217+
218+
CL_RETURN_ON_FAILURE(Res);
219+
220+
try {
221+
Devices.resize(DeviceNum);
222+
for (size_t i = 0; i < DeviceNum; i++) {
223+
Devices[i] =
224+
std::make_unique<ur_device_handle_t_>(CLDevices[i], this, nullptr);
225+
}
226+
} catch (std::bad_alloc &) {
227+
return UR_RESULT_ERROR_OUT_OF_RESOURCES;
228+
} catch (...) {
229+
return UR_RESULT_ERROR_UNKNOWN;
230+
}
231+
}
232+
233+
return UR_RESULT_SUCCESS;
234+
}

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

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
#pragma once
1111

1212
#include "common.hpp"
13-
#include "device.hpp"
1413

1514
#include <vector>
1615

16+
struct ur_device_handle_t_;
17+
1718
struct ur_platform_handle_t_ {
1819
using native_type = cl_platform_id;
1920
native_type CLPlatform = nullptr;
2021
std::vector<std::unique_ptr<ur_device_handle_t_>> Devices;
22+
std::map<cl_device_id, ur_device_handle_t> SubDevices;
23+
std::mutex SubDevicesLock;
2124

2225
ur_platform_handle_t_(native_type Plat) : CLPlatform(Plat) {}
2326

@@ -42,45 +45,7 @@ struct ur_platform_handle_t_ {
4245
return UR_RESULT_SUCCESS;
4346
}
4447

45-
ur_result_t InitDevices() {
46-
if (Devices.empty()) {
47-
cl_uint DeviceNum = 0;
48-
cl_int Res = clGetDeviceIDs(CLPlatform, CL_DEVICE_TYPE_ALL, 0, nullptr,
49-
&DeviceNum);
50-
51-
// Absorb the CL_DEVICE_NOT_FOUND and just return 0 in num_devices
52-
if (Res == CL_DEVICE_NOT_FOUND) {
53-
return UR_RESULT_SUCCESS;
54-
}
55-
56-
CL_RETURN_ON_FAILURE(Res);
57-
58-
std::vector<cl_device_id> CLDevices(DeviceNum);
59-
Res = clGetDeviceIDs(CLPlatform, CL_DEVICE_TYPE_ALL, DeviceNum,
60-
CLDevices.data(), nullptr);
61-
62-
// Absorb the CL_DEVICE_NOT_FOUND and just return 0 in num_devices
63-
if (Res == CL_DEVICE_NOT_FOUND) {
64-
return UR_RESULT_SUCCESS;
65-
}
66-
67-
CL_RETURN_ON_FAILURE(Res);
68-
69-
try {
70-
Devices.resize(DeviceNum);
71-
for (size_t i = 0; i < DeviceNum; i++) {
72-
Devices[i] = std::make_unique<ur_device_handle_t_>(CLDevices[i], this,
73-
nullptr);
74-
}
75-
} catch (std::bad_alloc &) {
76-
return UR_RESULT_ERROR_OUT_OF_RESOURCES;
77-
} catch (...) {
78-
return UR_RESULT_ERROR_UNKNOWN;
79-
}
80-
}
81-
82-
return UR_RESULT_SUCCESS;
83-
}
48+
ur_result_t InitDevices();
8449

8550
ur_result_t getPlatformVersion(oclv::OpenCLVersion &Version) {
8651
size_t PlatVerSize = 0;

unified-runtime/test/conformance/device/urDeviceCreateWithNativeHandle.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,36 @@ TEST_P(urDeviceCreateWithNativeHandleTest, InvalidNullPointerDevice) {
6565
UR_RESULT_ERROR_INVALID_NULL_POINTER,
6666
urDeviceCreateWithNativeHandle(native_handle, adapter, nullptr, nullptr));
6767
}
68+
69+
TEST_P(urDeviceCreateWithNativeHandleTest, SubDeviceHandleEquality) {
70+
if (!uur::hasDevicePartitionSupport(device, UR_DEVICE_PARTITION_EQUALLY)) {
71+
GTEST_SKIP() << "Device: \'" << device
72+
<< "\' does not support partitioning equally.";
73+
}
74+
75+
ur_device_partition_property_t property = uur::makePartitionEquallyDesc(2);
76+
ur_device_partition_properties_t properties{
77+
UR_STRUCTURE_TYPE_DEVICE_PARTITION_PROPERTIES,
78+
nullptr,
79+
&property,
80+
1,
81+
};
82+
83+
std::vector<ur_device_handle_t> sub_devices(2);
84+
ASSERT_SUCCESS(
85+
urDevicePartition(device, &properties, 2, sub_devices.data(), nullptr));
86+
87+
ur_native_handle_t native;
88+
UUR_ASSERT_SUCCESS_OR_UNSUPPORTED(
89+
urDeviceGetNativeHandle(sub_devices[0], &native));
90+
91+
ur_device_handle_t handle_a;
92+
ASSERT_SUCCESS(
93+
urDeviceCreateWithNativeHandle(native, adapter, nullptr, &handle_a));
94+
ur_device_handle_t handle_b;
95+
ASSERT_SUCCESS(
96+
urDeviceCreateWithNativeHandle(native, adapter, nullptr, &handle_b));
97+
98+
ASSERT_EQ(handle_a, handle_b);
99+
ASSERT_NE(handle_a, nullptr);
100+
}

0 commit comments

Comments
 (0)