diff --git a/CMakeLists.txt b/CMakeLists.txt index cc6ede76..d52c28d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/loader/icd_cmake_config.h.in set (OPENCL_ICD_LOADER_SOURCES loader/icd.c loader/icd.h + loader/icd_device_visible.h + loader/icd_device_visible.c loader/icd_dispatch.c loader/icd_dispatch.h loader/icd_dispatch_generated.c diff --git a/loader/icd.c b/loader/icd.c index aa677b06..9d261386 100644 --- a/loader/icd.c +++ b/loader/icd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 The Khronos Group Inc. + * Copyright (c) 2016-2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include "icd.h" #include "icd_dispatch.h" #include "icd_envvars.h" +#include "icd_device_visible.h" #if defined(CL_ENABLE_LAYERS) #include #endif // defined(CL_ENABLE_LAYERS) @@ -122,6 +123,12 @@ void khrIcdVendorAdd(const char *libraryName) { continue; } + + if (!khrIcdCheckPlatformVisible(libraryName, i)) + { + continue; + } + result = platforms[i]->dispatch->clGetPlatformInfo( platforms[i], CL_PLATFORM_ICD_SUFFIX_KHR, @@ -179,8 +186,9 @@ void khrIcdVendorAdd(const char *libraryName) *prevNextPointer = vendor; } - KHR_ICD_TRACE("successfully added vendor %s with suffix %s\n", libraryName, suffix); + khrIcdVisibilitySetPlatform(libraryName, i, platforms[i]); + KHR_ICD_TRACE("successfully added vendor %s with suffix %s\n", libraryName, suffix); } Done: diff --git a/loader/icd_device_visible.c b/loader/icd_device_visible.c new file mode 100644 index 00000000..651a2164 --- /dev/null +++ b/loader/icd_device_visible.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2021 The Khronos Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * OpenCL is a trademark of Apple Inc. used under license by Khronos. + */ + +#include "icd.h" +#include "icd_device_visible.h" +#include "icd_envvars.h" +#include "icd_dispatch.h" +#include +#include +#include + +static KHRVisibility *khrVisibilities = NULL; + +void khrIcdVisibilityAdd(char *library, unsigned platformIndex, cl_device_type deviceType, + unsigned numVisibleDevices, unsigned *deviceIndices) +{ + KHRVisibility *visibility = NULL; + + visibility = (KHRVisibility*)malloc(sizeof(*visibility)); + if (!visibility) + { + KHR_ICD_TRACE("Failed to allocate memory\n"); + return; + } + memset(visibility, 0, sizeof(*visibility)); + + visibility->library = library; + visibility->platformIndex = platformIndex; + visibility->deviceType = deviceType; + visibility->numVisibleDevices = numVisibleDevices; + visibility->deviceIndices = deviceIndices; + + if (numVisibleDevices > 0) + { + visibility->deviceIDs = (cl_device_id*)malloc(sizeof(cl_device_id) * numVisibleDevices); + if (!visibility->deviceIDs) + { + KHR_ICD_TRACE("Failed to allocate memory\n"); + free(visibility); + return; + } + memset(visibility->deviceIDs, 0, sizeof(cl_device_id) * numVisibleDevices); + } + + // add this entry at the tail of the list + { + KHRVisibility **prevNextPointer = NULL; + for (prevNextPointer = &khrVisibilities; *prevNextPointer; prevNextPointer = &((*prevNextPointer)->next)); + *prevNextPointer = visibility; + } +} + +void khrIcdVisibilityReplaceLibraryName(const char *oldName, const char *newName) +{ + KHRVisibility *iterator; + for (iterator = khrVisibilities; iterator; iterator = iterator->next) + { + if (strcmp(iterator->library, oldName) == 0) + { + char *oldBuffer = iterator->library; + char *buffer = (char*)malloc(strlen(newName) + 1); + if (!buffer) + { + KHR_ICD_TRACE("Failed to allocate memory\n"); + continue; + } + strcpy(buffer, newName); + + iterator->library = buffer; + free(oldBuffer); + } + } +} + +void khrIcdVisibilitySetPlatform(const char *libraryFile, unsigned index, cl_platform_id platform) +{ + KHRVisibility *iterator; + for (iterator = khrVisibilities; iterator; iterator = iterator->next) + { + if (khrIcdOsLibraryFileMatch(iterator->library, libraryFile) && + iterator->platformIndex == index && + iterator->platformID == NULL) + { + cl_uint numDevices = 0; + cl_device_id *devices = NULL; + cl_int result; + unsigned i; + + iterator->platformID = platform; + if (iterator->numVisibleDevices == 0) + { + continue; + } + + result = platform->dispatch->clGetDeviceIDs(platform, iterator->deviceType, 0, NULL, &numDevices); + if (CL_SUCCESS != result) + { + continue; + } + + devices = (cl_device_id*)malloc(sizeof(cl_device_id) * numDevices); + if (!devices) + { + continue; + } + result = platform->dispatch->clGetDeviceIDs(platform, iterator->deviceType, numDevices, devices, NULL); + if (CL_SUCCESS != result) + { + free(devices); + continue; + } + + for (i = 0; i < iterator->numVisibleDevices; ++i) + { + if (iterator->deviceIndices[i] < numDevices) + { + iterator->deviceIDs[i] = devices[iterator->deviceIndices[i]]; + } + else + { + iterator->deviceIDs[i] = NULL; + } + } + } + } +} + +int khrIcdCheckLibraryVisible(const char *libraryFile) +{ + KHRVisibility *iterator; + if (!khrVisibilities) return 1; + for (iterator = khrVisibilities; iterator; iterator = iterator->next) + { + if (khrIcdOsLibraryFileMatch(iterator->library, libraryFile)) + { + return 1; + } + } + return 0; +} + +int khrIcdCheckPlatformVisible(const char *libraryFile, unsigned index) +{ + KHRVisibility *iterator; + if (!khrVisibilities) return 1; + for (iterator = khrVisibilities; iterator; iterator = iterator->next) + { + if (khrIcdOsLibraryFileMatch(iterator->library, libraryFile) && + iterator->platformIndex == index) + { + return 1; + } + } + return 0; +} + +int khrIcdCheckDeviceVisible(cl_platform_id platform, cl_device_id device) +{ + KHRVisibility *iterator; + unsigned i; + if (!khrVisibilities) return 1; + + for (iterator = khrVisibilities; iterator; iterator = iterator->next) + { + if (iterator->platformID == platform) + { + if (iterator->numVisibleDevices == 0) + { + return 1; + } + for (i = 0; i < iterator->numVisibleDevices; ++i) + { + if (iterator->deviceIDs[i] == device) + { + return 1; + } + } + } + } + return 0; +} + +static char* ParseIntegerList(char* str, unsigned* numValuesRet, unsigned* valuesRet) +{ + unsigned numValues = 0; + unsigned value; + char* p = str; + + numValues = 0; + p = str; + while ((*p >= '0' && *p <= '9') || *p == ',') + { + if (*p >= '0' && *p <= '9') + { + value = 0; + for (; *p >= '0' && *p <= '9'; ++p) + { + value = value * 10 + (*p - '0'); + } + if (valuesRet) valuesRet[numValues] = value; + numValues++; + } + else + { + ++p; + } + } + if (numValuesRet) + { + *numValuesRet = numValues; + } + return p; +} + + +static char* ParseVisibilityEntry(char* str) +{ + // e.g., d:\\foo.dll,0,gpu,0,2 + // d:\\foo1.dll,0,cpu,0::d:\\foo2.dll,0,any,4,5 + // d:\\foo.dll,0,gpu (use all gpu devices in this platform) + // d:\\foo.dll,0 (use all devices in the platform) + size_t libraryLength = 0; + char* library = NULL; + unsigned platformIndex = 0; + cl_device_type deviceType = CL_DEVICE_TYPE_ALL; + unsigned numVisibleDevices = 0; + unsigned* deviceIndices = NULL; + char* p = str; + + if (str == NULL || *str == '\0') + { + return NULL; + } + // parse library / file name. + while (*p != '\0' && *p != ',') ++p; + libraryLength = p - str; + library = (char*)malloc(libraryLength + 1); + if (!library) + { + KHR_ICD_TRACE("Failed to allocate memory\n"); + goto Cleanup; + } + strncpy(library, str, libraryLength); + library[libraryLength] = '\0'; + + if (*p != ',') + { + KHR_ICD_TRACE("OPENCL_VISIBLE_DEVICES only provides library name. Failed to parse.\n"); + goto Cleanup; + } + ++p; + // Get platform index. + do + { + if (*p >= '0' && *p <= '9') + { + platformIndex = platformIndex * 10 + (*p - '0'); + } + else + { + KHR_ICD_TRACE("Failed to parse platform index in OPENCL_VISIBLE_DEVICES\n"); + goto Cleanup; + } + ++p; + } while (*p != '\0' && *p != ','); + + if (*p != ',' || *(p + 1) == ',') goto Done; + + ++p; + // Get device type. + if (strncmp(p, "gpu", 3) == 0) + { + deviceType = CL_DEVICE_TYPE_GPU; + p += 3; + } + else if (strncmp(p, "cpu", 3) == 0) + { + deviceType = CL_DEVICE_TYPE_CPU; + p += 3; + } + else if (strncmp(p, "accelerator", 11) == 0) + { + deviceType = CL_DEVICE_TYPE_ACCELERATOR; + p += 11; + } + else if (strncmp(p, "custom", 6) == 0) + { + deviceType = CL_DEVICE_TYPE_CUSTOM; + p += 6; + } + else if (strncmp(p, "any", 3) == 0) + { + deviceType = CL_DEVICE_TYPE_ALL; + p += 3; + } + else + { + KHR_ICD_TRACE("Failed to parse OPENCL_VISIBLE_DEVICES\n"); + goto Cleanup; + } + + if (*p != ',' || *(p + 1) == ',') goto Done; + ++p; + // Get device indexs. + ParseIntegerList(p, &numVisibleDevices, NULL); + if (numVisibleDevices > 0) + { + deviceIndices = (unsigned*)malloc(sizeof(unsigned) * numVisibleDevices); + if (!deviceIndices) + { + KHR_ICD_TRACE("Failed to allocate memory\n"); + goto Cleanup; + } + } + p = ParseIntegerList(p, NULL, deviceIndices); + +Done: + khrIcdVisibilityAdd(library, platformIndex, deviceType, numVisibleDevices, deviceIndices); + return p; + +Cleanup: + if (library) free(library); + if (deviceIndices) free(deviceIndices); + return NULL; +} + +void khrIcdOsGetOpenCLVisibleDevices(void) +{ + char* env = khrIcd_getenv("OPENCL_VISIBLE_DEVICES"); + char* envOrig = env; + + if (env == NULL || *env == '\0') + { + return; + } + + while (*env != '\0') + { + if (*env == ':' && *(env + 1) == ':') + { + env += 2; + continue; + } + + env = ParseVisibilityEntry(env); + if (!env) break; + + if (*env != '\0' && (*env != ':' && *(env + 1) != ':')) + { + KHR_ICD_TRACE("Failed to parse OPENCL_VISIBLE_DEVICES\n"); + break; + } + } + // env != null + khrIcd_free_getenv(envOrig); +} + +int khrIcdOsLibraryFileMatch(const char* name, const char* fileName) +{ + size_t nameLength = 0; + size_t fileNameLength = 0; + if (!name || !fileName) return 0; + + nameLength = strlen(name); + fileNameLength = strlen(fileName); + + if (nameLength == fileNameLength) + { + return strcmp(name, fileName) == 0; + } + else if (nameLength < fileNameLength) + { + return fileName[fileNameLength - nameLength - 1] == DIRECTORY_SYMBOL && + strcmp(name, fileName + (fileNameLength - nameLength)) == 0; + } + return 0; +} diff --git a/loader/icd_device_visible.h b/loader/icd_device_visible.h new file mode 100644 index 00000000..7ec951fb --- /dev/null +++ b/loader/icd_device_visible.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016-2021 The Khronos Group Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * OpenCL is a trademark of Apple Inc. used under license by Khronos. + */ + +#ifndef _ICD_DEVICE_VISIBLE_H_ +#define _ICD_DEVICE_VISIBLE_H_ + +#include + +typedef struct KHRVisibilityRec KHRVisibility; + +struct KHRVisibilityRec { + char *library; + unsigned platformIndex; + cl_device_type deviceType; + unsigned numVisibleDevices; + unsigned *deviceIndices; + + cl_platform_id platformID; + cl_device_id *deviceIDs; + + KHRVisibility *next; +}; + +int khrIcdOsLibraryFileMatch(const char *name, const char *fileName); + +void khrIcdVisibilityAdd(char *library, unsigned platformIndex, + cl_device_type deviceType, unsigned numVisibleDevices, + unsigned *deviceIndices); +void khrIcdVisibilityReplaceLibraryName(const char *oldName, + const char *newName); +void khrIcdVisibilitySetPlatform(const char *libraryFile, unsigned index, + cl_platform_id platform); + +int khrIcdCheckLibraryVisible(const char *libraryFile); +int khrIcdCheckPlatformVisible(const char *libraryFile, unsigned index); +int khrIcdCheckDeviceVisible(cl_platform_id platform, cl_device_id device); + +void khrIcdOsGetOpenCLVisibleDevices(void); +#endif diff --git a/loader/icd_dispatch_generated.c b/loader/icd_dispatch_generated.c index a27aac37..a569f289 100644 --- a/loader/icd_dispatch_generated.c +++ b/loader/icd_dispatch_generated.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2020 The Khronos Group Inc. + * Copyright (c) 2012-2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,9 @@ */ #include "icd_dispatch.h" +#include "icd_envvars.h" #include "icd.h" +#include "icd_device_visible.h" #ifdef __cplusplus extern "C" { @@ -96,12 +98,81 @@ CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDs( num_devices); #endif // defined(CL_ENABLE_LAYERS) KHR_ICD_VALIDATE_HANDLE_RETURN_ERROR(platform, CL_INVALID_PLATFORM); - return platform->dispatch->clGetDeviceIDs( + cl_uint num_all_devices = 0; + cl_device_id *all_devices = NULL; + cl_uint i; + cl_int result; + + if (!num_entries && devices) + { + return CL_INVALID_VALUE; + } + if (!devices && !num_devices) + { + return CL_INVALID_VALUE; + } + + if (num_devices) + { + *num_devices = 0; + } + for (i = 0; i < num_entries && devices; ++i) + { + devices[i] = NULL; + } + + + result = platform->dispatch->clGetDeviceIDs( platform, device_type, - num_entries, - devices, - num_devices); + 0, + NULL, + &num_all_devices); + + if (CL_SUCCESS != result) + { + return result; + } + if (!num_all_devices) + { + return CL_DEVICE_NOT_FOUND; + } + all_devices = (cl_device_id*)malloc(sizeof(cl_device_id) * num_all_devices); + if (!all_devices) + { + return CL_OUT_OF_HOST_MEMORY; + } + result = platform->dispatch->clGetDeviceIDs( + platform, + device_type, + num_all_devices, + all_devices, + NULL); + if (CL_SUCCESS != result) + { + free(all_devices); + return result; + } + + result = CL_DEVICE_NOT_FOUND; + for (i = 0; i < num_all_devices; ++i) + { + if (khrIcdCheckDeviceVisible(platform, all_devices[i])) + { + result = CL_SUCCESS; + if (num_entries && devices) + { + *(devices++) = all_devices[i]; + --num_entries; + } + if (num_devices) + { + ++(*num_devices); + } + } + } + free(all_devices); + return result; } /////////////////////////////////////////////////////////////////////////////// @@ -235,6 +306,12 @@ CL_API_ENTRY cl_context CL_API_CALL clCreateContextFromType( void* user_data, cl_int* errcode_ret) { + cl_platform_id platform = NULL; + cl_uint num_devices = 0; + cl_device_id *devices = NULL; + cl_context context = NULL; + cl_int result; + khrIcdInitialize(); #if defined(CL_ENABLE_LAYERS) if (khrFirstLayer) @@ -245,15 +322,56 @@ CL_API_ENTRY cl_context CL_API_CALL clCreateContextFromType( user_data, errcode_ret); #endif // defined(CL_ENABLE_LAYERS) - cl_platform_id platform = NULL; khrIcdContextPropertiesGetPlatform(properties, &platform); KHR_ICD_VALIDATE_HANDLE_RETURN_HANDLE(platform, CL_INVALID_PLATFORM); - return platform->dispatch->clCreateContextFromType( + + result = clGetDeviceIDs(platform, device_type, 0, NULL, &num_devices); + if (CL_SUCCESS != result) + { + if (*errcode_ret) + { + *errcode_ret = result; + } + return NULL; + } + if (!num_devices) + { + if (*errcode_ret) + { + *errcode_ret = CL_DEVICE_NOT_FOUND; + } + return NULL; + } + devices = (cl_device_id*)malloc(sizeof(cl_device_id) * num_devices); + if (!devices) + { + if (*errcode_ret) + { + *errcode_ret = CL_OUT_OF_HOST_MEMORY; + } + return NULL; + } + result = clGetDeviceIDs(platform, device_type, num_devices, devices, NULL); + if (CL_SUCCESS != result) + { + free(devices); + if (*errcode_ret) + { + *errcode_ret = result; + } + return NULL; + } + + context = platform->dispatch->clCreateContext( properties, - device_type, + num_devices, + devices, pfn_notify, user_data, errcode_ret); + free(devices); + return context; + } /////////////////////////////////////////////////////////////////////////////// diff --git a/loader/icd_envvars.h b/loader/icd_envvars.h index 0d34d3d6..cfcc469a 100644 --- a/loader/icd_envvars.h +++ b/loader/icd_envvars.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Khronos Group Inc. + * Copyright (c) 2016-2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ #ifndef _ICD_ENVVARS_H_ #define _ICD_ENVVARS_H_ +#include + char *khrIcd_getenv(const char *name); char *khrIcd_secure_getenv(const char *name); void khrIcd_free_getenv(char *val); diff --git a/loader/linux/icd_linux.c b/loader/linux/icd_linux.c index 4cc078b2..9d195d9e 100644 --- a/loader/linux/icd_linux.c +++ b/loader/linux/icd_linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 The Khronos Group Inc. + * Copyright (c) 2016-2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ #include "icd.h" #include "icd_envvars.h" +#include "icd_device_visible.h" #include #include @@ -35,13 +36,16 @@ static pthread_once_t initialized = PTHREAD_ONCE_INIT; * */ -// go through the list of vendors in the two configuration files +// go through the OPENCL_VISIBLE_DEVICES env variable and go through the list +// of vendors in the two configuration files void khrIcdOsVendorsEnumerate(void) { DIR *dir = NULL; struct dirent *dirEntry = NULL; char* vendorPath = ICD_VENDOR_PATH; char* envPath = NULL; + // go through the list in OPENCL_VISIBLE_DEVICES env variable + khrIcdOsGetOpenCLVisibleDevices(); khrIcdVendorsEnumerateEnv(); @@ -121,8 +125,14 @@ void khrIcdOsVendorsEnumerate(void) // ignore a newline at the end of the file if (buffer[bufferSize-1] == '\n') buffer[bufferSize-1] = '\0'; + khrIcdVisibilityReplaceLibraryName(dirEntry->d_name, buffer); + khrIcdVisibilityReplaceLibraryName(fileName, buffer); + // load the string read from the file - khrIcdVendorAdd(buffer); + if (khrIcdCheckLibraryVisible(buffer)) + { + khrIcdVendorAdd(buffer); + } free(fileName); free(buffer); diff --git a/loader/windows/icd_windows.c b/loader/windows/icd_windows.c index 852dd0e7..d5dace4b 100644 --- a/loader/windows/icd_windows.c +++ b/loader/windows/icd_windows.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 The Khronos Group Inc. + * Copyright (c) 2016-2021 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include #include "icd.h" +#include "icd_device_visible.h" #include "icd_windows.h" #include "icd_windows_hkr.h" #include "icd_windows_dxgk.h" @@ -103,8 +104,8 @@ void adapterFree(WinAdapter *pWinAdapter) * */ -// go through the list of vendors in the registry and call khrIcdVendorAdd -// for each vendor encountered +// go through the OPENCL_VISIBLE_DEVICES env variable and list of vendors in the +// registry and call khrIcdVendorAdd for each vendor encountered BOOL CALLBACK khrIcdOsVendorsEnumerate(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) { LONG result; @@ -112,6 +113,8 @@ BOOL CALLBACK khrIcdOsVendorsEnumerate(PINIT_ONCE InitOnce, PVOID Parameter, PVO const char* platformsName = "SOFTWARE\\Khronos\\OpenCL\\Vendors"; HKEY platformsKey = NULL; DWORD dwIndex; + // go through the list in OPENCL_VISIBLE_DEVICES env variable + khrIcdOsGetOpenCLVisibleDevices(); khrIcdVendorsEnumerateEnv();