Add support for using alternate vendors configuration directory
[ocl-icd] / ocl_icd_loader.c
1 /**
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <dirent.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <dlfcn.h>
32 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS
33 #include <CL/opencl.h>
34
35 #pragma GCC visibility push(hidden)
36
37 #include "ocl_icd_loader.h"
38 #include "ocl_icd_loader_debug.h"
39
40 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors/"
41
42 int debug_ocl_icd_mask=0;
43
44 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
45 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
46
47
48 struct vendor_icd {
49   cl_uint       num_platforms;
50   cl_uint       first_platform;
51   void *        dl_handle;
52   clGetExtensionFunctionAddress_fn ext_fn_ptr;
53 };
54
55 struct platform_icd {
56   char *         extension_suffix;
57   char *         version;
58   struct vendor_icd *vicd;
59   cl_platform_id pid;
60 };
61
62 struct vendor_icd *_icds=NULL;
63 struct platform_icd *_picds=NULL;
64 static cl_uint _num_icds = 0;
65 static cl_uint _num_picds = 0;
66
67 static cl_uint _initialized = 0;
68
69 #if DEBUG_OCL_ICD
70 #  define _clS(x) [-x] = #x
71 #  define MAX_CL_ERRORS CL_INVALID_DEVICE_PARTITION_COUNT
72 static char const * const clErrorStr[-MAX_CL_ERRORS+1] = {
73   _clS(CL_SUCCESS),
74   _clS(CL_DEVICE_NOT_FOUND),
75   _clS(CL_DEVICE_NOT_AVAILABLE),
76   _clS(CL_COMPILER_NOT_AVAILABLE),
77   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
78   _clS(CL_OUT_OF_RESOURCES),
79   _clS(CL_OUT_OF_HOST_MEMORY),
80   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
81   _clS(CL_MEM_COPY_OVERLAP),
82   _clS(CL_IMAGE_FORMAT_MISMATCH),
83   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
84   _clS(CL_BUILD_PROGRAM_FAILURE),
85   _clS(CL_MAP_FAILURE),
86   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
87   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
88   _clS(CL_COMPILE_PROGRAM_FAILURE),
89   _clS(CL_LINKER_NOT_AVAILABLE),
90   _clS(CL_LINK_PROGRAM_FAILURE),
91   _clS(CL_DEVICE_PARTITION_FAILED),
92   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
93   _clS(CL_INVALID_VALUE),
94   _clS(CL_INVALID_DEVICE_TYPE),
95   _clS(CL_INVALID_PLATFORM),
96   _clS(CL_INVALID_DEVICE),
97   _clS(CL_INVALID_CONTEXT),
98   _clS(CL_INVALID_QUEUE_PROPERTIES),
99   _clS(CL_INVALID_COMMAND_QUEUE),
100   _clS(CL_INVALID_HOST_PTR),
101   _clS(CL_INVALID_MEM_OBJECT),
102   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
103   _clS(CL_INVALID_IMAGE_SIZE),
104   _clS(CL_INVALID_SAMPLER),
105   _clS(CL_INVALID_BINARY),
106   _clS(CL_INVALID_BUILD_OPTIONS),
107   _clS(CL_INVALID_PROGRAM),
108   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
109   _clS(CL_INVALID_KERNEL_NAME),
110   _clS(CL_INVALID_KERNEL_DEFINITION),
111   _clS(CL_INVALID_KERNEL),
112   _clS(CL_INVALID_ARG_INDEX),
113   _clS(CL_INVALID_ARG_VALUE),
114   _clS(CL_INVALID_ARG_SIZE),
115   _clS(CL_INVALID_KERNEL_ARGS),
116   _clS(CL_INVALID_WORK_DIMENSION),
117   _clS(CL_INVALID_WORK_GROUP_SIZE),
118   _clS(CL_INVALID_WORK_ITEM_SIZE),
119   _clS(CL_INVALID_GLOBAL_OFFSET),
120   _clS(CL_INVALID_EVENT_WAIT_LIST),
121   _clS(CL_INVALID_EVENT),
122   _clS(CL_INVALID_OPERATION),
123   _clS(CL_INVALID_GL_OBJECT),
124   _clS(CL_INVALID_BUFFER_SIZE),
125   _clS(CL_INVALID_MIP_LEVEL),
126   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
127   _clS(CL_INVALID_PROPERTY),
128   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
129   _clS(CL_INVALID_COMPILER_OPTIONS),
130   _clS(CL_INVALID_LINKER_OPTIONS),
131   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
132 };
133 #undef _clS
134 #endif
135
136 static char* _clerror2string (cl_int error) __attribute__((unused));
137 static char* _clerror2string (cl_int error) {
138 #if DEBUG_OCL_ICD
139   if (-error > MAX_CL_ERRORS || error > 0) {
140     debug(D_WARN, "Unknown error code %d", error);
141     RETURN_STR("OpenCL Error");
142   }
143   const char *ret=clErrorStr[-error];
144   if (ret == NULL) {
145     debug(D_WARN, "Unknown error code %d", error);
146     RETURN_STR("OpenCL Error");
147   }
148   RETURN_STR(ret);
149 #else
150   static char number[15];
151   if (error==0) {
152     RETURN_STR("CL_SUCCESS");
153   }
154   snprintf(number, 15, "%i", error);
155   RETURN_STR(number);
156 #endif
157 }
158
159 static inline cl_uint _find_num_icds(DIR *dir) {
160   cl_uint num_icds = 0;
161   struct dirent *ent;
162   while( (ent=readdir(dir)) != NULL ){
163     if( strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0 )
164       continue;
165     cl_uint d_name_len = strlen(ent->d_name);
166     if( d_name_len>4 && strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
167       continue;
168     num_icds++;
169   }
170   rewinddir(dir);
171   RETURN(num_icds);
172 }
173
174 static inline cl_uint _open_drivers(DIR *dir, const char* dir_path) {
175   cl_uint num_icds = 0;
176   struct dirent *ent;
177   while( (ent=readdir(dir)) != NULL ){
178     if( strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0 )
179       continue;
180     cl_uint d_name_len = strlen(ent->d_name);
181     if( d_name_len>4 && strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
182       continue;
183     char * lib_path;
184     char * err;
185     unsigned int lib_path_length = strlen(dir_path) + strlen(ent->d_name) + 1;
186     lib_path = malloc(lib_path_length*sizeof(char));
187     sprintf(lib_path,"%s%s", dir_path, ent->d_name);
188     FILE *f = fopen(lib_path,"r");
189     free(lib_path);
190
191     fseek(f, 0, SEEK_END);
192     lib_path_length = ftell(f)+1;
193     fseek(f, 0, SEEK_SET);
194     if(lib_path_length == 1) {
195       fclose(f);
196       continue;
197     }
198     lib_path = malloc(lib_path_length*sizeof(char));
199     err = fgets(lib_path, lib_path_length, f);
200     fclose(f);
201     if( err == NULL ) {
202       free(lib_path);
203       continue;
204     }
205
206     lib_path_length = strlen(lib_path);
207     
208     if( lib_path[lib_path_length-1] == '\n' )
209       lib_path[lib_path_length-1] = '\0';
210
211     _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
212     if(_icds[num_icds].dl_handle != NULL) {
213       debug(D_LOG, "Loading ICD[%i] -> '%s'", num_icds, lib_path);
214       num_icds++;
215     }
216     free(lib_path);
217   }
218   RETURN(num_icds);
219 }
220
221 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
222   void *addr1;
223   addr1=dlsym(dlh, name);
224   if (addr1 == NULL) {
225     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
226   }
227   void* addr2=NULL;
228   if (fn) {
229     addr2=(*fn)(name);
230     if (addr2 == NULL) {
231       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
232     }
233 #if DEBUG_OCL_ICD
234     if (addr1 && addr2 && addr1!=addr2) {
235       debug(D_WARN, "Function and symbol '%s' have different addresses!", name);
236     }
237 #endif
238   }
239   if (!addr2) addr2=addr1;
240   RETURN(addr2);
241 }
242
243 static int _allocate_platforms(int req) {
244   static cl_uint allocated=0;
245   debug(D_LOG,"Requesting allocation for %d platforms",req);
246   if (allocated - _num_picds < req) {
247     if (allocated==0) {
248       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
249     } else {
250       req = req - (allocated - _num_picds);
251       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
252     }
253     allocated += req;
254   }
255   RETURN(allocated - _num_picds);
256 }
257
258 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
259                  cl_platform_id pid, cl_platform_info cname, char* sname) {
260   cl_int error;
261   size_t param_value_size_ret;
262   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
263   if (error != CL_SUCCESS) {
264     debug(D_WARN, "Error %s while requesting %s in platform %p",
265           _clerror2string(error), sname, pid);
266     return NULL;
267   }
268   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
269   if (param_value == NULL) {
270     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
271           sname, pid);
272     return NULL;
273   }
274   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
275   if (error != CL_SUCCESS){
276     free(param_value);
277     debug(D_WARN, "Error %s while requesting %s in platform %p",
278           _clerror2string(error), sname, pid);
279     return NULL;
280   }
281   RETURN_STR(param_value);
282 }
283
284 static inline void _find_and_check_platforms(cl_uint num_icds) {
285   cl_uint i;
286   _num_icds = 0;
287   for( i=0; i<num_icds; i++){
288     debug(D_LOG, "Checking ICD %i", i);
289     struct vendor_icd *picd = &_icds[_num_icds];
290     void* dlh = _icds[i].dl_handle;
291     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
292     clIcdGetPlatformIDsKHR_fn plt_fn_ptr = 
293       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
294     clGetPlatformInfo_fn plt_info_ptr = 
295       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
296     if( picd->ext_fn_ptr == NULL
297         || plt_fn_ptr == NULL
298         || plt_info_ptr == NULL) {
299       debug(D_WARN, "Missing symbols in ICD, skipping it");
300       continue;
301     }
302     cl_uint num_platforms=0;
303     cl_int error;
304     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
305     if( error != CL_SUCCESS || num_platforms == 0) {
306       debug(D_LOG, "No platform in ICD, skipping it");
307       continue;
308     }
309     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
310     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
311     if( error != CL_SUCCESS ){
312       free(platforms);
313       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
314       continue;
315     }
316     cl_uint num_valid_platforms=0;
317     cl_uint j;
318     debug(D_LOG, "Try to load %d plateforms", num_platforms);
319     if (_allocate_platforms(num_platforms) < num_platforms) {
320       free(platforms);
321       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
322       continue;
323     }
324     for(j=0; j<num_platforms; j++) {
325       debug(D_LOG, "Checking platform %i", j);
326       struct platform_icd *p=&_picds[_num_picds];
327       p->extension_suffix=NULL;
328       p->vicd=&_icds[i];
329       p->pid=platforms[j];
330 #if DEBUG_OCL_ICD
331       if (debug_ocl_icd_mask & D_DUMP) {
332         dump_platform(p->pid);
333       }
334 #endif
335       char *param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
336       if (param_value == NULL){
337         debug(D_WARN, "Skipping platform %i", j);
338         continue;
339       }
340       debug(D_DUMP, "Supported extensions: %s", param_value);
341       if( strstr(param_value, "cl_khr_icd") == NULL){
342         free(param_value);
343         debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
344         continue;
345       }
346       free(param_value);
347       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
348       if (param_value == NULL){
349         debug(D_WARN, "Skipping platform %i", j);
350         continue;
351       }
352       p->extension_suffix = param_value;
353       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
354 #if DEBUG_OCL_ICD
355       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
356       if (param_value != NULL){
357         debug(D_DUMP, "Profile: %s", param_value);
358         free(param_value);
359       }
360 #endif
361       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
362       p->version = param_value;
363       if (param_value != NULL){
364         debug(D_DUMP, "Version: %s", param_value);
365         free(param_value);
366       }
367 #if DEBUG_OCL_ICD
368       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
369       if (param_value != NULL){
370         debug(D_DUMP, "Name: %s", param_value);
371         free(param_value);
372       }
373       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
374       if (param_value != NULL){
375         debug(D_DUMP, "Vendor: %s", param_value);
376         free(param_value);
377       }
378 #endif
379       num_valid_platforms++;
380       _num_picds++;
381     }
382     if( num_valid_platforms != 0 ) {
383       if ( _num_icds != i ) {
384         picd->dl_handle = dlh;
385       }
386       _num_icds++;
387       picd->num_platforms = num_valid_platforms;
388       _icds[i].first_platform = _num_picds - num_valid_platforms;
389     } else {
390       dlclose(dlh);
391     }
392     free(platforms);
393   }
394 }
395
396 static void _initClIcd( void ) {
397   if( _initialized )
398     return;
399 #if DEBUG_OCL_ICD
400   char *debug=getenv("OCL_ICD_DEBUG");
401   if (debug) {
402     debug_ocl_icd_mask=atoi(debug);
403     if (*debug == 0)
404       debug_ocl_icd_mask=1;
405   }
406 #endif
407   cl_uint num_icds = 0;
408   DIR *dir;
409   const char* dir_path=getenv("OCL_ICD_VENDORS");
410   if (! dir_path || dir_path[0]==0) {
411     dir_path=ETC_OPENCL_VENDORS;
412   }
413   debug(D_LOG,"Reading icd list from '%s'", dir_path);
414   dir = opendir(dir_path);
415   if(dir == NULL) {
416     goto abort;
417   }
418
419   num_icds = _find_num_icds(dir);
420   if(num_icds == 0) {
421     goto abort;
422   }
423
424   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
425   if (_icds == NULL) {
426     goto abort;
427   }
428   
429   num_icds = _open_drivers(dir, dir_path);
430   if(num_icds == 0) {
431     goto abort;
432   }
433
434   _find_and_check_platforms(num_icds);
435   if(_num_icds == 0){
436     goto abort;
437   }
438
439   if (_num_icds < num_icds) {
440     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
441   }
442   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
443   _initialized = 1;
444   return;
445 abort:
446   _num_icds = 0;
447   _initialized = 1;
448   if (_icds) {
449     free(_icds);
450     _icds = NULL;
451   }
452   return;
453 }
454
455 #pragma GCC visibility pop
456
457 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
458   if( !_initialized )
459     _initClIcd();
460   if( func_name == NULL )
461     return NULL;
462   cl_uint suffix_length;
463   cl_uint i;
464   void * return_value=NULL;
465   struct func_desc const * fn=&function_description[0];
466   while (fn->name != NULL) {
467     if (strcmp(func_name, fn->name)==0)
468       return fn->addr;
469     fn++;
470   }
471   for(i=0; i<_num_picds; i++) {
472     suffix_length = strlen(_picds[i].extension_suffix);
473     if( suffix_length > strlen(func_name) )
474       continue;
475     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
476       return (*_picds[i].vicd->ext_fn_ptr)(func_name);
477   }
478   return return_value;
479 }
480 typeof(clGetExtensionFunctionAddress) clGetExtensionFunctionAddress_hid __attribute__ ((alias ("clGetExtensionFunctionAddress"), visibility("hidden")));
481
482 CL_API_ENTRY cl_int CL_API_CALL
483 clGetPlatformIDs(cl_uint          num_entries,
484                  cl_platform_id * platforms,
485                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
486   if( !_initialized )
487     _initClIcd();
488   if( platforms == NULL && num_platforms == NULL )
489     return CL_INVALID_VALUE;
490   if( num_entries == 0 && platforms != NULL )
491     return CL_INVALID_VALUE;
492   if( _num_icds == 0)
493     return CL_PLATFORM_NOT_FOUND_KHR;
494
495   cl_uint i;
496   if( num_platforms != NULL ){
497     *num_platforms = _num_picds;
498   }
499   if( platforms != NULL ) {
500     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
501     for( i=0; i<n_platforms; i++) {
502       *(platforms++) = _picds[i].pid;
503     }
504   }
505   return CL_SUCCESS;
506 }
507 typeof(clGetPlatformIDs) clGetPlatformIDs_hid __attribute__ ((alias ("clGetPlatformIDs"), visibility("hidden")));
508