Use internal symbols to avoid conflict with loaded ICD
[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 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include "config.h"
37 #ifdef USE_PTHREAD
38 #  include <pthread.h>
39 #endif
40 #pragma GCC diagnostic push
41 #  pragma GCC diagnostic ignored "-Wcpp"
42 #  define CL_USE_DEPRECATED_OPENCL_1_1_APIS
43 #  include <CL/opencl.h>
44 #pragma GCC diagnostic pop
45
46 #pragma GCC visibility push(hidden)
47
48 #include "ocl_icd_loader.h"
49 #define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD
50 #include "ocl_icd_debug.h"
51
52 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors"
53
54 int debug_ocl_icd_mask=0;
55
56 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
57
58 inline void dump_vendor_icd(const char* info, const struct vendor_icd *v) {
59   debug(D_DUMP, "%s %p={ num=%i, handle=%p, f=%p}\n", info,
60         v, v->num_platforms, v->dl_handle, v->ext_fn_ptr);
61 }
62
63 struct vendor_icd *_icds=NULL;
64 struct platform_icd *_picds=NULL;
65 static cl_uint _num_icds = 0;
66 cl_uint _num_picds = 0;
67
68 #ifdef DEBUG_OCL_ICD
69 #  define _clS(x) [-x] = #x
70 #  define MAX_CL_ERRORS (-CL_INVALID_DEVICE_PARTITION_COUNT)
71 static char const * const clErrorStr[MAX_CL_ERRORS+1] = {
72   _clS(CL_SUCCESS),
73   _clS(CL_DEVICE_NOT_FOUND),
74   _clS(CL_DEVICE_NOT_AVAILABLE),
75   _clS(CL_COMPILER_NOT_AVAILABLE),
76   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
77   _clS(CL_OUT_OF_RESOURCES),
78   _clS(CL_OUT_OF_HOST_MEMORY),
79   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
80   _clS(CL_MEM_COPY_OVERLAP),
81   _clS(CL_IMAGE_FORMAT_MISMATCH),
82   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
83   _clS(CL_BUILD_PROGRAM_FAILURE),
84   _clS(CL_MAP_FAILURE),
85   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
86   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
87   _clS(CL_COMPILE_PROGRAM_FAILURE),
88   _clS(CL_LINKER_NOT_AVAILABLE),
89   _clS(CL_LINK_PROGRAM_FAILURE),
90   _clS(CL_DEVICE_PARTITION_FAILED),
91   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
92   _clS(CL_INVALID_VALUE),
93   _clS(CL_INVALID_DEVICE_TYPE),
94   _clS(CL_INVALID_PLATFORM),
95   _clS(CL_INVALID_DEVICE),
96   _clS(CL_INVALID_CONTEXT),
97   _clS(CL_INVALID_QUEUE_PROPERTIES),
98   _clS(CL_INVALID_COMMAND_QUEUE),
99   _clS(CL_INVALID_HOST_PTR),
100   _clS(CL_INVALID_MEM_OBJECT),
101   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
102   _clS(CL_INVALID_IMAGE_SIZE),
103   _clS(CL_INVALID_SAMPLER),
104   _clS(CL_INVALID_BINARY),
105   _clS(CL_INVALID_BUILD_OPTIONS),
106   _clS(CL_INVALID_PROGRAM),
107   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
108   _clS(CL_INVALID_KERNEL_NAME),
109   _clS(CL_INVALID_KERNEL_DEFINITION),
110   _clS(CL_INVALID_KERNEL),
111   _clS(CL_INVALID_ARG_INDEX),
112   _clS(CL_INVALID_ARG_VALUE),
113   _clS(CL_INVALID_ARG_SIZE),
114   _clS(CL_INVALID_KERNEL_ARGS),
115   _clS(CL_INVALID_WORK_DIMENSION),
116   _clS(CL_INVALID_WORK_GROUP_SIZE),
117   _clS(CL_INVALID_WORK_ITEM_SIZE),
118   _clS(CL_INVALID_GLOBAL_OFFSET),
119   _clS(CL_INVALID_EVENT_WAIT_LIST),
120   _clS(CL_INVALID_EVENT),
121   _clS(CL_INVALID_OPERATION),
122   _clS(CL_INVALID_GL_OBJECT),
123   _clS(CL_INVALID_BUFFER_SIZE),
124   _clS(CL_INVALID_MIP_LEVEL),
125   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
126   _clS(CL_INVALID_PROPERTY),
127   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
128   _clS(CL_INVALID_COMPILER_OPTIONS),
129   _clS(CL_INVALID_LINKER_OPTIONS),
130   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
131 };
132 #undef _clS
133 #endif
134
135 static char* _clerror2string (cl_int error) __attribute__((unused));
136 static char* _clerror2string (cl_int error) {
137 #ifdef DEBUG_OCL_ICD
138   if (-error > MAX_CL_ERRORS || error > 0) {
139     debug(D_WARN, "Unknown error code %d", error);
140     RETURN_STR("OpenCL Error");
141   }
142   const char *ret=clErrorStr[-error];
143   if (ret == NULL) {
144     debug(D_WARN, "Unknown error code %d", error);
145     RETURN_STR("OpenCL Error");
146   }
147   RETURN_STR(ret);
148 #else
149   static char number[15];
150   if (error==0) {
151     RETURN_STR("CL_SUCCESS");
152   }
153   snprintf(number, 15, "%i", error);
154   RETURN_STR(number);
155 #endif
156 }
157
158 static inline int _string_end_with_icd(const char* str) {
159   size_t len = strlen(str);
160   if( len<5 || strcmp(str + len - 4, ".icd" ) != 0 ) {
161     return 0;
162   }
163   return 1;
164 }
165
166 static inline int _string_with_slash(const char* str) {
167   return strchr(str, '/') != NULL;
168 }
169
170 static inline unsigned int _find_num_icds(DIR *dir) {
171   unsigned int num_icds = 0;
172   struct dirent *ent;
173   while( (ent=readdir(dir)) != NULL ){
174     if (_string_end_with_icd(ent->d_name)) {
175       num_icds++;
176     }
177   }
178   rewinddir(dir);
179   RETURN(num_icds);
180 }
181
182 static inline unsigned int _load_icd(int num_icds, const char* lib_path) {
183   unsigned int ret=0;
184   debug(D_LOG, "Loading ICD '%s'", lib_path);
185
186   _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
187   if(_icds[num_icds].dl_handle != NULL) {
188     debug(D_LOG, "ICD[%i] loaded", num_icds);
189     ret=1;
190   } else {
191     debug(D_WARN, "error while dlopening the IDL: '%s',\n  => skipping ICD", dlerror());
192   }
193   return ret;
194 }
195
196 static inline unsigned int _open_driver(unsigned int num_icds,
197                                         const char*dir_path, const char*file_path) {
198   char * lib_path;
199   char * err;
200   unsigned int lib_path_length;
201   if (dir_path != NULL) {
202     lib_path_length = strlen(dir_path) + strlen(file_path) + 2;
203     lib_path = malloc(lib_path_length*sizeof(char));
204     sprintf(lib_path,"%s/%s", dir_path, file_path);
205   } else {
206     lib_path_length = strlen(file_path) + 1;
207     lib_path = malloc(lib_path_length*sizeof(char));
208     sprintf(lib_path,"%s", file_path);
209   }
210   debug(D_LOG, "Considering file '%s'", lib_path);
211   FILE *f = fopen(lib_path,"r");
212   free(lib_path);
213   if (f==NULL) {
214     RETURN(num_icds);
215   }
216
217   fseek(f, 0, SEEK_END);
218   lib_path_length = ftell(f)+1;
219   fseek(f, 0, SEEK_SET);
220   if(lib_path_length == 1) {
221     debug(D_WARN, "File contents too short, skipping ICD");
222     fclose(f);
223     RETURN(num_icds);
224   }
225   lib_path = malloc(lib_path_length*sizeof(char));
226   err = fgets(lib_path, lib_path_length, f);
227   fclose(f);
228   if( err == NULL ) {
229     free(lib_path);
230     debug(D_WARN, "Error while loading file contents, skipping ICD");
231     RETURN(num_icds);
232   }
233
234   lib_path_length = strnlen(lib_path, lib_path_length);
235
236   if( lib_path[lib_path_length-1] == '\n' )
237     lib_path[lib_path_length-1] = '\0';
238
239   num_icds += _load_icd(num_icds, lib_path);
240
241   free(lib_path);
242   RETURN(num_icds);
243 }
244
245 static inline unsigned int _open_drivers(DIR *dir, const char* dir_path) {
246   unsigned int num_icds = 0;
247   struct dirent *ent;
248   while( (ent=readdir(dir)) != NULL ){
249     if(! _string_end_with_icd(ent->d_name)) {
250       continue;
251     }
252     num_icds = _open_driver(num_icds, dir_path, ent->d_name);
253
254   }
255   RETURN(num_icds);
256 }
257
258 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
259   void *addr1;
260   debug(D_LOG,"Looking for function %s",name);
261   addr1=dlsym(dlh, name);
262   if (addr1 == NULL) {
263     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
264   }
265   void* addr2=NULL;
266   if (fn) {
267     addr2=(*fn)(name);
268     if (addr2 == NULL) {
269       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
270     }
271 #ifdef DEBUG_OCL_ICD
272     if (addr1 && addr2 && addr1!=addr2) {
273       debug(D_WARN, "Function and symbol '%s' have different addresses (%p != %p)!", name, addr2, addr1);
274     }
275 #endif
276   }
277   if (!addr2) addr2=addr1;
278   RETURN(addr2);
279 }
280
281 static int _allocate_platforms(int req) {
282   static cl_uint allocated=0;
283   debug(D_LOG,"Requesting allocation for %d platforms",req);
284   if (allocated - _num_picds < req) {
285     if (allocated==0) {
286       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
287     } else {
288       req = req - (allocated - _num_picds);
289       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
290     }
291     allocated += req;
292   }
293   RETURN(allocated - _num_picds);
294 }
295
296 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
297                  cl_platform_id pid, cl_platform_info cname, char* sname) {
298   cl_int error;
299   size_t param_value_size_ret;
300   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
301   if (error != CL_SUCCESS) {
302     debug(D_WARN, "Error %s while requesting %s in platform %p",
303           _clerror2string(error), sname, pid);
304     return NULL;
305   }
306   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
307   if (param_value == NULL) {
308     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
309           sname, pid);
310     return NULL;
311   }
312   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
313   if (error != CL_SUCCESS){
314     free(param_value);
315     debug(D_WARN, "Error %s while requesting %s in platform %p",
316           _clerror2string(error), sname, pid);
317     return NULL;
318   }
319   RETURN_STR(param_value);
320 }
321
322 static void _count_devices(struct platform_icd *p) {
323   cl_int error;
324
325   /* Ensure they are 0 in case of errors */
326   p->ngpus = p->ncpus = p->ndevs = 0;
327
328   error = clGetDeviceIDs(p->pid, CL_DEVICE_TYPE_GPU, 0, NULL, &(p->ngpus));
329   if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND){
330     debug(D_WARN, "Error %s while counting GPU devices in platform %p",
331           _clerror2string(error), p->pid);
332   }
333
334   error = clGetDeviceIDs(p->pid, CL_DEVICE_TYPE_CPU, 0, NULL, &(p->ncpus));
335   if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND){
336     debug(D_WARN, "Error %s while counting CPU devices in platform %p",
337           _clerror2string(error), p->pid);
338   }
339
340   error = clGetDeviceIDs(p->pid, CL_DEVICE_TYPE_ALL, 0, NULL, &(p->ndevs));
341   if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND){
342     debug(D_WARN, "Error %s while counting ALL devices in platform %p",
343           _clerror2string(error), p->pid);
344   }
345
346 }
347
348 static int _cmp_platforms(const void *_a, const void *_b) {
349         const struct platform_icd *a=(const struct platform_icd *)_a;
350         const struct platform_icd *b=(const struct platform_icd *)_b;
351
352         /* sort first platforms handling max gpu */
353         if (a->ngpus > b->ngpus) return -1;
354         if (a->ngpus < b->ngpus) return 1;
355         /* sort next platforms handling max cpu */
356         if (a->ncpus > b->ncpus) return -1;
357         if (a->ncpus < b->ncpus) return 1;
358         /* sort then platforms handling max devices */
359         if (a->ndevs > b->ndevs) return -1;
360         if (a->ndevs < b->ndevs) return 1;
361         /* else consider platforms equal */
362         return 0;
363 }
364
365 static void _sort_platforms(struct platform_icd *picds, int npicds) {
366         debug(D_WARN, "Nb platefroms: %i", npicds);
367         if (npicds > 1) {
368                 char* ocl_sort=getenv("OCL_ICD_PLATFORM_SORT");
369                 if (ocl_sort!=NULL && !strcmp(ocl_sort, "none")) {
370                         debug(D_LOG, "Platform not sorted");
371                 } else {
372                         if (ocl_sort!=NULL && strcmp(ocl_sort, "devices")) {
373                                 debug(D_WARN, "Unknown platform sort algorithm requested: %s", ocl_sort);
374                                 debug(D_WARN, "Switching do the 'devices' algorithm");
375                         }
376                         int i;
377                         debug(D_LOG, "Platform sorted by GPU, CPU, DEV");
378                         for (i=0; i<npicds; i++) {
379                                 _count_devices(&picds[i]);
380                         }
381                         qsort(picds, npicds, sizeof(*picds),
382                               &_cmp_platforms);
383                 }
384         }
385 }
386
387 static inline void _find_and_check_platforms(cl_uint num_icds) {
388   cl_uint i;
389   _num_icds = 0;
390   for( i=0; i<num_icds; i++){
391     debug(D_LOG, "Checking ICD %i/%i", i, num_icds);
392     dump_vendor_icd("before looking for platforms", &_icds[i]);
393     struct vendor_icd *picd = &_icds[i];
394     void* dlh = _icds[i].dl_handle;
395     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
396     clIcdGetPlatformIDsKHR_fn plt_fn_ptr =
397       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
398     clGetPlatformInfo_fn plt_info_ptr =
399       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
400     if( picd->ext_fn_ptr == NULL
401         || plt_fn_ptr == NULL
402         || plt_info_ptr == NULL) {
403       debug(D_WARN, "Missing symbols in ICD, skipping it");
404       continue;
405     }
406     cl_uint num_platforms=0;
407     cl_int error;
408     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
409     if( error != CL_SUCCESS || num_platforms == 0) {
410       debug(D_LOG, "No platform in ICD, skipping it");
411       continue;
412     }
413     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
414     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
415     if( error != CL_SUCCESS ){
416       free(platforms);
417       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
418       continue;
419     }
420     cl_uint num_valid_platforms=0;
421     cl_uint j;
422     debug(D_LOG, "Try to load %d platforms", num_platforms);
423     if (_allocate_platforms(num_platforms) < num_platforms) {
424       free(platforms);
425       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
426       continue;
427     }
428     for(j=0; j<num_platforms; j++) {
429       debug(D_LOG, "Checking platform %i", j);
430       struct platform_icd *p=&_picds[_num_picds];
431       char *param_value=NULL;
432       p->extension_suffix=NULL;
433       p->vicd=&_icds[i];
434       p->pid=platforms[j];
435 #ifdef DEBUG_OCL_ICD
436       if (debug_ocl_icd_mask & D_DUMP) {
437         int log=debug_ocl_icd_mask & D_TRACE;
438         debug_ocl_icd_mask &= ~D_TRACE;
439         dump_platform(p->vicd->ext_fn_ptr, p->pid);
440         debug_ocl_icd_mask |= log;
441       }
442 #endif
443       {
444               /* Allow to workaround a bug in the Intel ICD used
445                * with optirun (search for NVidia Optimus for more info)
446                */
447               const char* str=getenv("OCL_ICD_ASSUME_ICD_EXTENSION");
448               if (! str || str[0]==0) {
449                       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
450                       if (param_value == NULL){
451                               debug(D_WARN, "Skipping platform %i", j);
452                               continue;
453                       }
454                       debug(D_DUMP, "Supported extensions: %s", param_value);
455                       if( strstr(param_value, "cl_khr_icd") == NULL){
456                               free(param_value);
457                               debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
458                               continue;
459                       }
460                       free(param_value);
461               }
462       }
463       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
464       if (param_value == NULL){
465         debug(D_WARN, "Skipping platform %i", j);
466         continue;
467       }
468       p->extension_suffix = param_value;
469       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
470 #ifdef DEBUG_OCL_ICD
471       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
472       if (param_value != NULL){
473         debug(D_DUMP, "Profile: %s", param_value);
474         free(param_value);
475       }
476       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
477       p->version = param_value;
478       if (param_value != NULL){
479         debug(D_DUMP, "Version: %s", param_value);
480         free(param_value);
481       }
482       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
483       if (param_value != NULL){
484         debug(D_DUMP, "Name: %s", param_value);
485         free(param_value);
486       }
487       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
488       if (param_value != NULL){
489         debug(D_DUMP, "Vendor: %s", param_value);
490         free(param_value);
491       }
492 #endif
493       num_valid_platforms++;
494       _num_picds++;
495     }
496     if( num_valid_platforms != 0 ) {
497       if ( _num_icds != i ) {
498         picd->dl_handle = dlh;
499       }
500       dump_vendor_icd("after looking for platforms", &_icds[_num_icds]);
501       _num_icds++;
502       picd->num_platforms = num_valid_platforms;
503     } else {
504       dlclose(dlh);
505     }
506     free(platforms);
507   }
508   _sort_platforms(&_picds[0], _num_picds);
509 }
510
511 static void __initClIcd( void ) {
512   debug_init();
513   cl_uint num_icds = 0;
514   int is_dir = 0;
515   DIR *dir = NULL;
516   const char* dir_path=getenv("OCL_ICD_VENDORS");
517   if (! dir_path || dir_path[0]==0) {
518     debug(D_DUMP, "OCL_ICD_VENDORS empty or not defined, using %s", ETC_OPENCL_VENDORS);
519     dir_path=ETC_OPENCL_VENDORS;
520     is_dir=1;
521   }
522   if (!is_dir) {
523     struct stat buf;
524     int ret=stat(dir_path, &buf);
525     if (ret != 0 && errno != ENOENT) {
526       debug(D_WARN, "Cannot stat '%s'. Aborting", dir_path);
527     }
528     if (ret == 0 && S_ISDIR(buf.st_mode)) {
529       is_dir=1;
530     }
531   }
532
533   if (!is_dir) {
534     debug(D_LOG,"Only loading '%s' as an ICD", dir_path);
535     num_icds = 1;
536     dir=NULL;
537   } else {
538     debug(D_LOG,"Reading icd list from '%s'", dir_path);
539     dir = opendir(dir_path);
540     if(dir == NULL) {
541       if (errno == ENOTDIR) {
542         debug(D_DUMP, "%s is not a directory, trying to use it as a ICD libname",
543           dir_path);
544       }
545       goto abort;
546     }
547
548     num_icds = _find_num_icds(dir);
549     if(num_icds == 0) {
550       goto abort;
551     }
552   }
553
554   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
555   if (_icds == NULL) {
556     goto abort;
557   }
558
559   if (!is_dir) {
560     if (_string_end_with_icd(dir_path)) {
561       num_icds = 0;
562       if (! _string_with_slash(dir_path)) {
563         num_icds = _open_driver(0, ETC_OPENCL_VENDORS, dir_path);
564       }
565       if (num_icds == 0) {
566         num_icds = _open_driver(0, NULL, dir_path);
567       }
568     } else {
569       num_icds = _load_icd(0, dir_path);
570     }
571   } else {
572     num_icds = _open_drivers(dir, dir_path);
573   }
574   if(num_icds == 0) {
575     goto abort;
576   }
577
578   _find_and_check_platforms(num_icds);
579   if(_num_icds == 0){
580     goto abort;
581   }
582
583   if (_num_icds < num_icds) {
584     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
585   }
586   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
587
588   if (dir != NULL){
589     closedir(dir);
590   }
591   return;
592  abort:
593   _num_icds = 0;
594   if (_icds) {
595     free(_icds);
596     _icds = NULL;
597   }
598   if (dir != NULL){
599     closedir(dir);
600   }
601   return;
602 }
603
604 #ifdef USE_PTHREAD
605 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
606 #else
607 static int gard=0;
608 #endif
609 volatile static __thread int in_init = 0;
610 volatile static cl_uint _initialized = 0;
611
612 static void _initClIcd_real( void ) {
613 #ifdef USE_PTHREAD
614   if (in_init) {
615     /* probably reentrency, in_init is a __thread variable */
616     debug(D_WARN, "Executing init while already in init!");
617   } else {
618     in_init=1;
619     __sync_synchronize();
620     pthread_once(&once_init, &__initClIcd);
621     __sync_synchronize();
622     in_init=0;
623   }
624 #else
625   if (__sync_bool_compare_and_swap(&gard, 0, 1)) {
626     in_init=1;
627     __sync_synchronize();
628     __initClIcd();
629     __sync_synchronize();
630     in_init=0;
631   } else {
632     if (in_init) {
633       /* probably reentrency (could also be preemptive user-level threads). */
634     } else {
635       /* someone else started __initClIcd(). We wait until it ends. */
636       debug(D_WARN, "Waiting end of init");
637       while (!_initialized) {
638         __sync_synchronize();
639       }
640       debug(D_WARN, "Wait done");
641    }
642   }
643 #endif
644   _initialized = 1;
645 }
646
647 static inline void _initClIcd( void ) {
648   if( __builtin_expect (_initialized, 1) )
649     return;
650   _initClIcd_real();
651 }
652
653 cl_platform_id __attribute__((visibility("internal")))
654 getDefaultPlatformID() {
655   static cl_platform_id defaultPlatformID=NULL;
656   static int defaultSet=0;
657   _initClIcd();
658   if (! defaultSet) {
659     do {
660       if(_num_picds == 0) {
661         break;
662       }
663       const char *default_platform = getenv("OCL_ICD_DEFAULT_PLATFORM");
664       int num_default_platform;
665       char *end_scan;
666       if (! default_platform) {
667         num_default_platform = 0;
668       } else {
669         num_default_platform = strtol(default_platform, &end_scan, 10);
670         if (*default_platform == '\0' || *end_scan != '\0') {
671           break;
672         }
673       }
674       if (num_default_platform < 0 || num_default_platform >= _num_picds) {
675         break;
676       }
677       defaultPlatformID=_picds[num_default_platform].pid;
678     } while(0);
679     defaultSet=1;
680   }
681   return defaultPlatformID;
682 }
683
684 #pragma GCC visibility pop
685 #define hidden_alias(name) \
686   typeof(name) name##_hid __attribute__ ((alias (#name), visibility("hidden")))
687
688 typedef enum {
689   CL_ICDL_OCL_VERSION=1,
690   CL_ICDL_VERSION=2,
691   CL_ICDL_NAME=3,
692   CL_ICDL_VENDOR=4,
693 } cl_icdl_info;
694
695 static cl_int clGetICDLoaderInfoOCLICD(
696   cl_icdl_info     param_name,
697   size_t           param_value_size,
698   void *           param_value,
699   size_t *         param_value_size_ret)
700 {
701   char cl_icdl_ocl_version[] = "OpenCL 1.2";
702   char cl_icdl_version[] = PACKAGE_VERSION;
703   char cl_icdl_name[] = PACKAGE_NAME;
704   char cl_icdl_vendor[] = "OCL Icd free software";
705
706   size_t size_string;
707   char * string_p;
708 #define oclcase(name, NAME) \
709   case CL_ICDL_##NAME: \
710     string_p = cl_icdl_##name; \
711     size_string = sizeof(cl_icdl_##name); \
712     break
713
714   switch ( param_name ) {
715     oclcase(ocl_version,OCL_VERSION);
716     oclcase(version,VERSION);
717     oclcase(name,NAME);
718     oclcase(vendor,VENDOR);
719     default:
720       return CL_INVALID_VALUE;
721       break;
722   }
723 #undef oclcase
724   if( param_value != NULL ) {
725     if( size_string > param_value_size )
726       return CL_INVALID_VALUE;
727     memcpy(param_value, string_p, size_string);
728   }
729   if( param_value_size_ret != NULL )
730     *param_value_size_ret = size_string;
731   return CL_SUCCESS;
732 }
733
734 typeof(clGetGLContextInfoKHR) clGetGLContextInfoKHR_hid;
735 typeof(clCreateSubDevicesEXT) clCreateSubDevicesEXT_hid;
736 typeof(clRetainDeviceEXT) clRetainDeviceEXT_hid;
737 typeof(clReleaseDeviceEXT) clReleaseDeviceEXT_hid;
738 typeof(clCreateEventFromGLsyncKHR) clCreateEventFromGLsyncKHR_hid;
739 typeof(clCreateFromEGLImageKHR) clCreateFromEGLImageKHR_hid;
740 typeof(clEnqueueAcquireEGLObjectsKHR) clEnqueueAcquireEGLObjectsKHR_hid;
741 typeof(clEnqueueReleaseEGLObjectsKHR) clEnqueueReleaseEGLObjectsKHR_hid;
742 typeof(clCreateEventFromEGLSyncKHR) clCreateEventFromEGLSyncKHR_hid;
743 typeof(clGetKernelSubGroupInfoKHR) clGetKernelSubGroupInfoKHR_hid;
744
745 CL_API_ENTRY void * CL_API_CALL
746 clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
747   debug_trace();
748   _initClIcd();
749   if( func_name == NULL )
750     return NULL;
751   if( !strcmp(func_name,"clGetGLContextInfoKHR") )
752     return (void *)clGetGLContextInfoKHR_hid;
753   if( !strcmp(func_name,"clCreateSubDevicesEXT") )
754     return (void *)clCreateSubDevicesEXT_hid;
755   if( !strcmp(func_name,"clRetainDeviceEXT") )
756     return (void *)clRetainDeviceEXT_hid;
757   if( !strcmp(func_name,"clReleaseDeviceEXT") )
758     return (void *)clReleaseDeviceEXT_hid;
759   if( !strcmp(func_name,"clCreateEventFromGLsyncKHR") )
760     return (void *)clCreateEventFromGLsyncKHR_hid;
761   if( !strcmp(func_name,"clCreateFromEGLImageKHR") )
762     return (void *)clCreateFromEGLImageKHR_hid;
763   if( !strcmp(func_name,"clEnqueueAcquireEGLObjectsKHR") )
764     return (void *)clEnqueueAcquireEGLObjectsKHR_hid;
765   if( !strcmp(func_name,"clEnqueueReleaseEGLObjectsKHR") )
766     return (void *)clEnqueueReleaseEGLObjectsKHR_hid;
767   if( !strcmp(func_name,"clCreateEventFromEGLSyncKHR") )
768     return (void *)clCreateEventFromEGLSyncKHR_hid;
769   if( !strcmp(func_name,"clGetKernelSubGroupInfoKHR") )
770     return (void *)clGetKernelSubGroupInfoKHR_hid;
771   cl_uint suffix_length;
772   cl_uint i;
773   void * return_value=NULL;
774   struct func_desc const * fn=&function_description[0];
775   int lenfn=strlen(func_name);
776   if (lenfn > 3 &&
777       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
778     while (fn->name != NULL) {
779       if (strcmp(func_name, fn->name)==0)
780         RETURN(fn->addr);
781       fn++;
782     }
783   }
784   for(i=0; i<_num_picds; i++) {
785     suffix_length = strlen(_picds[i].extension_suffix);
786     if( suffix_length > strlen(func_name) )
787       continue;
788     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
789       RETURN((*_picds[i].vicd->ext_fn_ptr)(func_name));
790   }
791   if(strcmp(func_name, "clGetICDLoaderInfoOCLICD") == 0) {
792     return (void*)(void*(*)(void))(&clGetICDLoaderInfoOCLICD);
793   }
794   RETURN(return_value);
795 }
796 hidden_alias(clGetExtensionFunctionAddress);
797
798 CL_API_ENTRY cl_int CL_API_CALL
799 clGetPlatformIDs(cl_uint          num_entries,
800                  cl_platform_id * platforms,
801                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
802   debug_trace();
803   _initClIcd();
804   if( platforms == NULL && num_platforms == NULL )
805     RETURN(CL_INVALID_VALUE);
806   if( num_entries == 0 && platforms != NULL )
807     RETURN(CL_INVALID_VALUE);
808   if( _num_icds == 0 || _num_picds == 0 ) {
809     if ( num_platforms != NULL )
810       *num_platforms = 0;
811     RETURN(CL_PLATFORM_NOT_FOUND_KHR);
812   }
813
814   cl_uint i;
815   if( num_platforms != NULL ){
816     *num_platforms = _num_picds;
817   }
818   if( platforms != NULL ) {
819     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
820     for( i=0; i<n_platforms; i++) {
821       *(platforms++) = _picds[i].pid;
822     }
823   }
824   return CL_SUCCESS;
825 }
826 hidden_alias(clGetPlatformIDs);
827
828 #define RETURN_WITH_ERRCODE(errvar, errvalue, retvalue) \
829   do { \
830     if(errvar) { \
831       *errvar=errvalue; \
832     } \
833     RETURN(NULL); \
834   } while(0)
835
836 #define CHECK_PLATFORM(__pid) \
837   ({ \
838     cl_platform_id _pid=(__pid); \
839     int good=0; \
840     cl_uint j; \
841     for( j=0; j<_num_picds; j++) { \
842       if( _picds[j].pid == _pid) { \
843         good=1; \
844         break; \
845       } \
846     } \
847     good; \
848   })
849
850 CL_API_ENTRY cl_context CL_API_CALL
851 clCreateContext(const cl_context_properties *  properties ,
852                 cl_uint                        num_devices ,
853                 const cl_device_id *           devices ,
854                 void (CL_CALLBACK *  pfn_notify )(const char *, const void *, size_t, void *),
855                 void *                         user_data ,
856                 cl_int *                       errcode_ret ){
857   debug_trace();
858   _initClIcd();
859   cl_uint i=0;
860   if( properties != NULL){
861     while( properties[i] != 0 ) {
862       if( properties[i] == CL_CONTEXT_PLATFORM ) {
863         if((struct _cl_platform_id *) properties[i+1] == NULL) {
864           if(errcode_ret) {
865             *errcode_ret = CL_INVALID_PLATFORM;
866           }
867           RETURN(NULL);
868         } else {
869           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
870             RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
871           }
872         }
873         RETURN(((struct _cl_platform_id *) properties[i+1])
874           ->dispatch->clCreateContext(properties, num_devices, devices,
875                         pfn_notify, user_data, errcode_ret));
876       }
877       i += 2;
878     }
879   }
880   if(devices == NULL || num_devices == 0) {
881     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_VALUE, NULL);
882   }
883   if((struct _cl_device_id *)devices[0] == NULL) {
884     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_DEVICE, NULL);
885   }
886   RETURN(((struct _cl_device_id *)devices[0])
887     ->dispatch->clCreateContext(properties, num_devices, devices,
888                   pfn_notify, user_data, errcode_ret));
889 }
890 hidden_alias(clCreateContext);
891
892 CL_API_ENTRY cl_context CL_API_CALL
893 clCreateContextFromType(const cl_context_properties *  properties ,
894                         cl_device_type                 device_type ,
895                         void (CL_CALLBACK *      pfn_notify )(const char *, const void *, size_t, void *),
896                         void *                         user_data ,
897                         cl_int *                       errcode_ret ){
898   debug_trace();
899   _initClIcd();
900   if(_num_picds == 0) {
901     goto out;
902   }
903   cl_uint i=0;
904   if( properties != NULL){
905     while( properties[i] != 0 ) {
906       if( properties[i] == CL_CONTEXT_PLATFORM ) {
907         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
908           goto out;
909         } else {
910           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
911             goto out;
912           }
913         }
914         return ((struct _cl_platform_id *) properties[i+1])
915           ->dispatch->clCreateContextFromType(properties, device_type,
916                         pfn_notify, user_data, errcode_ret);
917       }
918       i += 2;
919     }
920   } else {
921     cl_platform_id default_platform=getDefaultPlatformID();
922     RETURN(default_platform->dispatch->clCreateContextFromType
923         (properties, device_type, pfn_notify, user_data, errcode_ret));
924   }
925  out:
926   RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
927 }
928 hidden_alias(clCreateContextFromType);
929
930 CL_API_ENTRY cl_int CL_API_CALL
931 clGetGLContextInfoKHR(const cl_context_properties *  properties ,
932                       cl_gl_context_info             param_name ,
933                       size_t                         param_value_size ,
934                       void *                         param_value ,
935                       size_t *                       param_value_size_ret ){
936   debug_trace();
937   _initClIcd();
938   cl_uint i=0;
939   if( properties != NULL){
940     while( properties[i] != 0 ) {
941       if( properties[i] == CL_CONTEXT_PLATFORM ) {
942         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
943           RETURN(CL_INVALID_PLATFORM);
944         } else {
945           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
946             RETURN(CL_INVALID_PLATFORM);
947           }
948         }
949         RETURN(((struct _cl_platform_id *) properties[i+1])
950           ->dispatch->clGetGLContextInfoKHR(properties, param_name,
951                         param_value_size, param_value, param_value_size_ret));
952       }
953       i += 2;
954     }
955   }
956   RETURN(CL_INVALID_PLATFORM);
957 }
958 hidden_alias(clGetGLContextInfoKHR);
959
960 CL_API_ENTRY cl_int CL_API_CALL
961 clWaitForEvents(cl_uint              num_events ,
962                 const cl_event *     event_list ){
963   debug_trace();
964   if( num_events == 0 || event_list == NULL )
965     RETURN(CL_INVALID_VALUE);
966   if( (struct _cl_event *)event_list[0] == NULL )
967     RETURN(CL_INVALID_EVENT);
968   RETURN(((struct _cl_event *)event_list[0])
969     ->dispatch->clWaitForEvents(num_events, event_list));
970 }
971 hidden_alias(clWaitForEvents);
972
973 CL_API_ENTRY cl_int CL_API_CALL
974 clUnloadCompiler( void ){
975   debug_trace();
976   RETURN(CL_SUCCESS);
977 }
978 hidden_alias(clUnloadCompiler);