Fix and improve testsuite
[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         qsort(picds, npicds, sizeof(*picds),
367                 &_cmp_platforms);
368 }
369
370 static inline void _find_and_check_platforms(cl_uint num_icds) {
371   cl_uint i;
372   _num_icds = 0;
373   for( i=0; i<num_icds; i++){
374     debug(D_LOG, "Checking ICD %i/%i", i, num_icds);
375     dump_vendor_icd("before looking for platforms", &_icds[i]);
376     struct vendor_icd *picd = &_icds[i];
377     void* dlh = _icds[i].dl_handle;
378     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
379     clIcdGetPlatformIDsKHR_fn plt_fn_ptr =
380       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
381     clGetPlatformInfo_fn plt_info_ptr =
382       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
383     if( picd->ext_fn_ptr == NULL
384         || plt_fn_ptr == NULL
385         || plt_info_ptr == NULL) {
386       debug(D_WARN, "Missing symbols in ICD, skipping it");
387       continue;
388     }
389     cl_uint num_platforms=0;
390     cl_int error;
391     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
392     if( error != CL_SUCCESS || num_platforms == 0) {
393       debug(D_LOG, "No platform in ICD, skipping it");
394       continue;
395     }
396     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
397     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
398     if( error != CL_SUCCESS ){
399       free(platforms);
400       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
401       continue;
402     }
403     cl_uint num_valid_platforms=0;
404     cl_uint j;
405     debug(D_LOG, "Try to load %d platforms", num_platforms);
406     if (_allocate_platforms(num_platforms) < num_platforms) {
407       free(platforms);
408       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
409       continue;
410     }
411     for(j=0; j<num_platforms; j++) {
412       debug(D_LOG, "Checking platform %i", j);
413       struct platform_icd *p=&_picds[_num_picds];
414       char *param_value=NULL;
415       p->extension_suffix=NULL;
416       p->vicd=&_icds[i];
417       p->pid=platforms[j];
418 #ifdef DEBUG_OCL_ICD
419       if (debug_ocl_icd_mask & D_DUMP) {
420         int log=debug_ocl_icd_mask & D_TRACE;
421         debug_ocl_icd_mask &= ~D_TRACE;
422         dump_platform(p->vicd->ext_fn_ptr, p->pid);
423         debug_ocl_icd_mask |= log;
424       }
425 #endif
426       {
427               /* Allow to workaround a bug in the Intel ICD used
428                * with optirun (search for NVidia Optimus for more info)
429                */
430               const char* str=getenv("OCL_ICD_ASSUME_ICD_EXTENSION");
431               if (! str || str[0]==0) {
432                       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
433                       if (param_value == NULL){
434                               debug(D_WARN, "Skipping platform %i", j);
435                               continue;
436                       }
437                       debug(D_DUMP, "Supported extensions: %s", param_value);
438                       if( strstr(param_value, "cl_khr_icd") == NULL){
439                               free(param_value);
440                               debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
441                               continue;
442                       }
443                       free(param_value);
444               }
445       }
446       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
447       if (param_value == NULL){
448         debug(D_WARN, "Skipping platform %i", j);
449         continue;
450       }
451       p->extension_suffix = param_value;
452       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
453 #ifdef DEBUG_OCL_ICD
454       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
455       if (param_value != NULL){
456         debug(D_DUMP, "Profile: %s", param_value);
457         free(param_value);
458       }
459       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
460       p->version = param_value;
461       if (param_value != NULL){
462         debug(D_DUMP, "Version: %s", param_value);
463         free(param_value);
464       }
465       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
466       if (param_value != NULL){
467         debug(D_DUMP, "Name: %s", param_value);
468         free(param_value);
469       }
470       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
471       if (param_value != NULL){
472         debug(D_DUMP, "Vendor: %s", param_value);
473         free(param_value);
474       }
475 #endif
476       _count_devices(p);
477       num_valid_platforms++;
478       _num_picds++;
479     }
480     if( num_valid_platforms != 0 ) {
481       if ( _num_icds != i ) {
482         picd->dl_handle = dlh;
483       }
484       dump_vendor_icd("after looking for platforms", &_icds[_num_icds]);
485       _num_icds++;
486       picd->num_platforms = num_valid_platforms;
487     } else {
488       dlclose(dlh);
489     }
490     free(platforms);
491   }
492   _sort_platforms(&_picds[0], _num_picds);
493 }
494
495 static void __initClIcd( void ) {
496   debug_init();
497   cl_uint num_icds = 0;
498   int is_dir = 0;
499   DIR *dir = NULL;
500   const char* dir_path=getenv("OCL_ICD_VENDORS");
501   if (! dir_path || dir_path[0]==0) {
502     debug(D_DUMP, "OCL_ICD_VENDORS empty or not defined, using %s", ETC_OPENCL_VENDORS);
503     dir_path=ETC_OPENCL_VENDORS;
504     is_dir=1;
505   }
506   if (!is_dir) {
507     struct stat buf;
508     int ret=stat(dir_path, &buf);
509     if (ret != 0 && errno != ENOENT) {
510       debug(D_WARN, "Cannot stat '%s'. Aborting", dir_path);
511     }
512     if (ret == 0 && S_ISDIR(buf.st_mode)) {
513       is_dir=1;
514     }
515   }
516
517   if (!is_dir) {
518     debug(D_LOG,"Only loading '%s' as an ICD", dir_path);
519     num_icds = 1;
520     dir=NULL;
521   } else {
522     debug(D_LOG,"Reading icd list from '%s'", dir_path);
523     dir = opendir(dir_path);
524     if(dir == NULL) {
525       if (errno == ENOTDIR) {
526         debug(D_DUMP, "%s is not a directory, trying to use it as a ICD libname",
527           dir_path);
528       }
529       goto abort;
530     }
531
532     num_icds = _find_num_icds(dir);
533     if(num_icds == 0) {
534       goto abort;
535     }
536   }
537
538   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
539   if (_icds == NULL) {
540     goto abort;
541   }
542
543   if (!is_dir) {
544     if (_string_end_with_icd(dir_path)) {
545       num_icds = 0;
546       if (! _string_with_slash(dir_path)) {
547         num_icds = _open_driver(0, ETC_OPENCL_VENDORS, dir_path);
548       }
549       if (num_icds == 0) {
550         num_icds = _open_driver(0, NULL, dir_path);
551       }
552     } else {
553       num_icds = _load_icd(0, dir_path);
554     }
555   } else {
556     num_icds = _open_drivers(dir, dir_path);
557   }
558   if(num_icds == 0) {
559     goto abort;
560   }
561
562   _find_and_check_platforms(num_icds);
563   if(_num_icds == 0){
564     goto abort;
565   }
566
567   if (_num_icds < num_icds) {
568     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
569   }
570   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
571
572   if (dir != NULL){
573     closedir(dir);
574   }
575   return;
576  abort:
577   _num_icds = 0;
578   if (_icds) {
579     free(_icds);
580     _icds = NULL;
581   }
582   if (dir != NULL){
583     closedir(dir);
584   }
585   return;
586 }
587
588 #ifdef USE_PTHREAD
589 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
590 #else
591 static int gard=0;
592 #endif
593 volatile static __thread int in_init = 0;
594 volatile static cl_uint _initialized = 0;
595
596 static void _initClIcd_real( void ) {
597 #ifdef USE_PTHREAD
598   if (in_init) {
599     /* probably reentrency, in_init is a __thread variable */
600     debug(D_WARN, "Executing init while already in init!");
601   } else {
602     in_init=1;
603     __sync_synchronize();
604     pthread_once(&once_init, &__initClIcd);
605     __sync_synchronize();
606     in_init=0;
607   }
608 #else
609   if (__sync_bool_compare_and_swap(&gard, 0, 1)) {
610     in_init=1;
611     __sync_synchronize();
612     __initClIcd();
613     __sync_synchronize();
614     in_init=0;
615   } else {
616     if (in_init) {
617       /* probably reentrency (could also be preemptive user-level threads). */
618     } else {
619       /* someone else started __initClIcd(). We wait until it ends. */
620       debug(D_WARN, "Waiting end of init");
621       while (!_initialized) {
622         __sync_synchronize();
623       }
624       debug(D_WARN, "Wait done");
625    }
626   }
627 #endif
628   _initialized = 1;
629 }
630
631 static inline void _initClIcd( void ) {
632   if( __builtin_expect (_initialized, 1) )
633     return;
634   _initClIcd_real();
635 }
636
637 cl_platform_id __attribute__((visibility("internal")))
638 getDefaultPlatformID() {
639   static cl_platform_id defaultPlatformID=NULL;
640   static int defaultSet=0;
641   _initClIcd();
642   if (! defaultSet) {
643     do {
644       if(_num_picds == 0) {
645         break;
646       }
647       const char *default_platform = getenv("OPENCL_ICD_DEFAULT_PLATFORM");
648       int num_default_platform;
649       char *end_scan;
650       if (! default_platform) {
651         num_default_platform = 0;
652       } else {
653         num_default_platform = strtol(default_platform, &end_scan, 10);
654         if (*default_platform == '\0' || *end_scan != '\0') {
655           break;
656         }
657       }
658       if (num_default_platform < 0 || num_default_platform >= _num_picds) {
659         break;
660       }
661       defaultPlatformID=_picds[num_default_platform].pid;
662     } while(0);
663     defaultSet=1;
664   }
665   return defaultPlatformID;
666 }
667
668 #pragma GCC visibility pop
669 #define hidden_alias(name) \
670   typeof(name) name##_hid __attribute__ ((alias (#name), visibility("hidden")))
671
672 typedef enum {
673   CL_ICDL_OCL_VERSION=1,
674   CL_ICDL_VERSION=2,
675   CL_ICDL_NAME=3,
676   CL_ICDL_VENDOR=4,
677 } cl_icdl_info;
678
679 static cl_int clGetICDLoaderInfoOCLICD(
680   cl_icdl_info     param_name,
681   size_t           param_value_size,
682   void *           param_value,
683   size_t *         param_value_size_ret)
684 {
685   char cl_icdl_ocl_version[] = "OpenCL 1.2";
686   char cl_icdl_version[] = PACKAGE_VERSION;
687   char cl_icdl_name[] = PACKAGE_NAME;
688   char cl_icdl_vendor[] = "OCL Icd free software";
689
690   size_t size_string;
691   char * string_p;
692 #define oclcase(name, NAME) \
693   case CL_ICDL_##NAME: \
694     string_p = cl_icdl_##name; \
695     size_string = sizeof(cl_icdl_##name); \
696     break
697
698   switch ( param_name ) {
699     oclcase(ocl_version,OCL_VERSION);
700     oclcase(version,VERSION);
701     oclcase(name,NAME);
702     oclcase(vendor,VENDOR);
703     default:
704       return CL_INVALID_VALUE;
705       break;
706   }
707 #undef oclcase
708   if( param_value != NULL ) {
709     if( size_string > param_value_size )
710       return CL_INVALID_VALUE;
711     memcpy(param_value, string_p, size_string);
712   }
713   if( param_value_size_ret != NULL )
714     *param_value_size_ret = size_string;
715   return CL_SUCCESS;
716 }
717
718 CL_API_ENTRY void * CL_API_CALL
719 clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
720   debug_trace();
721   _initClIcd();
722   if( func_name == NULL )
723     return NULL;
724   cl_uint suffix_length;
725   cl_uint i;
726   void * return_value=NULL;
727   struct func_desc const * fn=&function_description[0];
728   int lenfn=strlen(func_name);
729   if (lenfn > 3 &&
730       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
731     while (fn->name != NULL) {
732       if (strcmp(func_name, fn->name)==0)
733         RETURN(fn->addr);
734       fn++;
735     }
736   }
737   for(i=0; i<_num_picds; i++) {
738     suffix_length = strlen(_picds[i].extension_suffix);
739     if( suffix_length > strlen(func_name) )
740       continue;
741     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
742       RETURN((*_picds[i].vicd->ext_fn_ptr)(func_name));
743   }
744   if(strcmp(func_name, "clGetICDLoaderInfoOCLICD") == 0) {
745     return (void*)(void*(*)(void))(&clGetICDLoaderInfoOCLICD);
746   }
747   RETURN(return_value);
748 }
749 hidden_alias(clGetExtensionFunctionAddress);
750
751 CL_API_ENTRY cl_int CL_API_CALL
752 clGetPlatformIDs(cl_uint          num_entries,
753                  cl_platform_id * platforms,
754                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
755   debug_trace();
756   _initClIcd();
757   if( platforms == NULL && num_platforms == NULL )
758     RETURN(CL_INVALID_VALUE);
759   if( num_entries == 0 && platforms != NULL )
760     RETURN(CL_INVALID_VALUE);
761   if( _num_icds == 0 || _num_picds == 0 ) {
762     if ( num_platforms != NULL )
763       *num_platforms = 0;
764     RETURN(CL_PLATFORM_NOT_FOUND_KHR);
765   }
766
767   cl_uint i;
768   if( num_platforms != NULL ){
769     *num_platforms = _num_picds;
770   }
771   if( platforms != NULL ) {
772     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
773     for( i=0; i<n_platforms; i++) {
774       *(platforms++) = _picds[i].pid;
775     }
776   }
777   return CL_SUCCESS;
778 }
779 hidden_alias(clGetPlatformIDs);
780
781 #define RETURN_WITH_ERRCODE(errvar, errvalue, retvalue) \
782   do { \
783     if(errvar) { \
784       *errvar=errvalue; \
785     } \
786     RETURN(NULL); \
787   } while(0)
788
789 #define CHECK_PLATFORM(__pid) \
790   ({ \
791     cl_platform_id _pid=(__pid); \
792     int good=0; \
793     cl_uint j; \
794     for( j=0; j<_num_picds; j++) { \
795       if( _picds[j].pid == _pid) { \
796         good=1; \
797         break; \
798       } \
799     } \
800     good; \
801   })
802
803 CL_API_ENTRY cl_context CL_API_CALL
804 clCreateContext(const cl_context_properties *  properties ,
805                 cl_uint                        num_devices ,
806                 const cl_device_id *           devices ,
807                 void (CL_CALLBACK *  pfn_notify )(const char *, const void *, size_t, void *),
808                 void *                         user_data ,
809                 cl_int *                       errcode_ret ){
810   debug_trace();
811   _initClIcd();
812   cl_uint i=0;
813   if( properties != NULL){
814     while( properties[i] != 0 ) {
815       if( properties[i] == CL_CONTEXT_PLATFORM ) {
816         if((struct _cl_platform_id *) properties[i+1] == NULL) {
817           if(errcode_ret) {
818             *errcode_ret = CL_INVALID_PLATFORM;
819           }
820           RETURN(NULL);
821         } else {
822           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
823             RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
824           }
825         }
826         RETURN(((struct _cl_platform_id *) properties[i+1])
827           ->dispatch->clCreateContext(properties, num_devices, devices,
828                         pfn_notify, user_data, errcode_ret));
829       }
830       i += 2;
831     }
832   }
833   if(devices == NULL || num_devices == 0) {
834     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_VALUE, NULL);
835   }
836   if((struct _cl_device_id *)devices[0] == NULL) {
837     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_DEVICE, NULL);
838   }
839   RETURN(((struct _cl_device_id *)devices[0])
840     ->dispatch->clCreateContext(properties, num_devices, devices,
841                   pfn_notify, user_data, errcode_ret));
842 }
843 hidden_alias(clCreateContext);
844
845 CL_API_ENTRY cl_context CL_API_CALL
846 clCreateContextFromType(const cl_context_properties *  properties ,
847                         cl_device_type                 device_type ,
848                         void (CL_CALLBACK *      pfn_notify )(const char *, const void *, size_t, void *),
849                         void *                         user_data ,
850                         cl_int *                       errcode_ret ){
851   debug_trace();
852   _initClIcd();
853   if(_num_picds == 0) {
854     goto out;
855   }
856   cl_uint i=0;
857   if( properties != NULL){
858     while( properties[i] != 0 ) {
859       if( properties[i] == CL_CONTEXT_PLATFORM ) {
860         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
861           goto out;
862         } else {
863           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
864             goto out;
865           }
866         }
867         return ((struct _cl_platform_id *) properties[i+1])
868           ->dispatch->clCreateContextFromType(properties, device_type,
869                         pfn_notify, user_data, errcode_ret);
870       }
871       i += 2;
872     }
873   } else {
874     const char *default_platform = getenv("OPENCL_ICD_DEFAULT_PLATFORM");
875     int num_default_platform;
876     char *end_scan;
877     if (! default_platform) {
878       num_default_platform = 0;
879     } else {
880       num_default_platform = strtol(default_platform, &end_scan, 10);
881       if (*default_platform == '\0' || *end_scan != '\0') {
882         goto out;
883       }
884     }
885     if (num_default_platform < 0 || num_default_platform >= _num_picds) {
886       goto out;
887     }
888     RETURN(_picds[num_default_platform].pid->dispatch->clCreateContextFromType
889         (properties, device_type, pfn_notify, user_data, errcode_ret));
890   }
891  out:
892   RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
893 }
894 hidden_alias(clCreateContextFromType);
895
896 CL_API_ENTRY cl_int CL_API_CALL
897 clGetGLContextInfoKHR(const cl_context_properties *  properties ,
898                       cl_gl_context_info             param_name ,
899                       size_t                         param_value_size ,
900                       void *                         param_value ,
901                       size_t *                       param_value_size_ret ){
902   debug_trace();
903   _initClIcd();
904   cl_uint i=0;
905   if( properties != NULL){
906     while( properties[i] != 0 ) {
907       if( properties[i] == CL_CONTEXT_PLATFORM ) {
908         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
909           RETURN(CL_INVALID_PLATFORM);
910         } else {
911           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
912             RETURN(CL_INVALID_PLATFORM);
913           }
914         }
915         RETURN(((struct _cl_platform_id *) properties[i+1])
916           ->dispatch->clGetGLContextInfoKHR(properties, param_name,
917                         param_value_size, param_value, param_value_size_ret));
918       }
919       i += 2;
920     }
921   }
922   RETURN(CL_INVALID_PLATFORM);
923 }
924 hidden_alias(clGetGLContextInfoKHR);
925
926 CL_API_ENTRY cl_int CL_API_CALL
927 clWaitForEvents(cl_uint              num_events ,
928                 const cl_event *     event_list ){
929   debug_trace();
930   if( num_events == 0 || event_list == NULL )
931     RETURN(CL_INVALID_VALUE);
932   if( (struct _cl_event *)event_list[0] == NULL )
933     RETURN(CL_INVALID_EVENT);
934   RETURN(((struct _cl_event *)event_list[0])
935     ->dispatch->clWaitForEvents(num_events, event_list));
936 }
937 hidden_alias(clWaitForEvents);
938
939 CL_API_ENTRY cl_int CL_API_CALL
940 clUnloadCompiler( void ){
941   debug_trace();
942   RETURN(CL_SUCCESS);
943 }
944 hidden_alias(clUnloadCompiler);