allows to avoid the platform sort and extends the 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         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 CL_API_ENTRY void * CL_API_CALL
735 clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
736   debug_trace();
737   _initClIcd();
738   if( func_name == NULL )
739     return NULL;
740   cl_uint suffix_length;
741   cl_uint i;
742   void * return_value=NULL;
743   struct func_desc const * fn=&function_description[0];
744   int lenfn=strlen(func_name);
745   if (lenfn > 3 &&
746       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
747     while (fn->name != NULL) {
748       if (strcmp(func_name, fn->name)==0)
749         RETURN(fn->addr);
750       fn++;
751     }
752   }
753   for(i=0; i<_num_picds; i++) {
754     suffix_length = strlen(_picds[i].extension_suffix);
755     if( suffix_length > strlen(func_name) )
756       continue;
757     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
758       RETURN((*_picds[i].vicd->ext_fn_ptr)(func_name));
759   }
760   if(strcmp(func_name, "clGetICDLoaderInfoOCLICD") == 0) {
761     return (void*)(void*(*)(void))(&clGetICDLoaderInfoOCLICD);
762   }
763   RETURN(return_value);
764 }
765 hidden_alias(clGetExtensionFunctionAddress);
766
767 CL_API_ENTRY cl_int CL_API_CALL
768 clGetPlatformIDs(cl_uint          num_entries,
769                  cl_platform_id * platforms,
770                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
771   debug_trace();
772   _initClIcd();
773   if( platforms == NULL && num_platforms == NULL )
774     RETURN(CL_INVALID_VALUE);
775   if( num_entries == 0 && platforms != NULL )
776     RETURN(CL_INVALID_VALUE);
777   if( _num_icds == 0 || _num_picds == 0 ) {
778     if ( num_platforms != NULL )
779       *num_platforms = 0;
780     RETURN(CL_PLATFORM_NOT_FOUND_KHR);
781   }
782
783   cl_uint i;
784   if( num_platforms != NULL ){
785     *num_platforms = _num_picds;
786   }
787   if( platforms != NULL ) {
788     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
789     for( i=0; i<n_platforms; i++) {
790       *(platforms++) = _picds[i].pid;
791     }
792   }
793   return CL_SUCCESS;
794 }
795 hidden_alias(clGetPlatformIDs);
796
797 #define RETURN_WITH_ERRCODE(errvar, errvalue, retvalue) \
798   do { \
799     if(errvar) { \
800       *errvar=errvalue; \
801     } \
802     RETURN(NULL); \
803   } while(0)
804
805 #define CHECK_PLATFORM(__pid) \
806   ({ \
807     cl_platform_id _pid=(__pid); \
808     int good=0; \
809     cl_uint j; \
810     for( j=0; j<_num_picds; j++) { \
811       if( _picds[j].pid == _pid) { \
812         good=1; \
813         break; \
814       } \
815     } \
816     good; \
817   })
818
819 CL_API_ENTRY cl_context CL_API_CALL
820 clCreateContext(const cl_context_properties *  properties ,
821                 cl_uint                        num_devices ,
822                 const cl_device_id *           devices ,
823                 void (CL_CALLBACK *  pfn_notify )(const char *, const void *, size_t, void *),
824                 void *                         user_data ,
825                 cl_int *                       errcode_ret ){
826   debug_trace();
827   _initClIcd();
828   cl_uint i=0;
829   if( properties != NULL){
830     while( properties[i] != 0 ) {
831       if( properties[i] == CL_CONTEXT_PLATFORM ) {
832         if((struct _cl_platform_id *) properties[i+1] == NULL) {
833           if(errcode_ret) {
834             *errcode_ret = CL_INVALID_PLATFORM;
835           }
836           RETURN(NULL);
837         } else {
838           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
839             RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
840           }
841         }
842         RETURN(((struct _cl_platform_id *) properties[i+1])
843           ->dispatch->clCreateContext(properties, num_devices, devices,
844                         pfn_notify, user_data, errcode_ret));
845       }
846       i += 2;
847     }
848   }
849   if(devices == NULL || num_devices == 0) {
850     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_VALUE, NULL);
851   }
852   if((struct _cl_device_id *)devices[0] == NULL) {
853     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_DEVICE, NULL);
854   }
855   RETURN(((struct _cl_device_id *)devices[0])
856     ->dispatch->clCreateContext(properties, num_devices, devices,
857                   pfn_notify, user_data, errcode_ret));
858 }
859 hidden_alias(clCreateContext);
860
861 CL_API_ENTRY cl_context CL_API_CALL
862 clCreateContextFromType(const cl_context_properties *  properties ,
863                         cl_device_type                 device_type ,
864                         void (CL_CALLBACK *      pfn_notify )(const char *, const void *, size_t, void *),
865                         void *                         user_data ,
866                         cl_int *                       errcode_ret ){
867   debug_trace();
868   _initClIcd();
869   if(_num_picds == 0) {
870     goto out;
871   }
872   cl_uint i=0;
873   if( properties != NULL){
874     while( properties[i] != 0 ) {
875       if( properties[i] == CL_CONTEXT_PLATFORM ) {
876         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
877           goto out;
878         } else {
879           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
880             goto out;
881           }
882         }
883         return ((struct _cl_platform_id *) properties[i+1])
884           ->dispatch->clCreateContextFromType(properties, device_type,
885                         pfn_notify, user_data, errcode_ret);
886       }
887       i += 2;
888     }
889   } else {
890     cl_platform_id default_platform=getDefaultPlatformID();
891     RETURN(default_platform->dispatch->clCreateContextFromType
892         (properties, device_type, pfn_notify, user_data, errcode_ret));
893   }
894  out:
895   RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
896 }
897 hidden_alias(clCreateContextFromType);
898
899 CL_API_ENTRY cl_int CL_API_CALL
900 clGetGLContextInfoKHR(const cl_context_properties *  properties ,
901                       cl_gl_context_info             param_name ,
902                       size_t                         param_value_size ,
903                       void *                         param_value ,
904                       size_t *                       param_value_size_ret ){
905   debug_trace();
906   _initClIcd();
907   cl_uint i=0;
908   if( properties != NULL){
909     while( properties[i] != 0 ) {
910       if( properties[i] == CL_CONTEXT_PLATFORM ) {
911         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
912           RETURN(CL_INVALID_PLATFORM);
913         } else {
914           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
915             RETURN(CL_INVALID_PLATFORM);
916           }
917         }
918         RETURN(((struct _cl_platform_id *) properties[i+1])
919           ->dispatch->clGetGLContextInfoKHR(properties, param_name,
920                         param_value_size, param_value, param_value_size_ret));
921       }
922       i += 2;
923     }
924   }
925   RETURN(CL_INVALID_PLATFORM);
926 }
927 hidden_alias(clGetGLContextInfoKHR);
928
929 CL_API_ENTRY cl_int CL_API_CALL
930 clWaitForEvents(cl_uint              num_events ,
931                 const cl_event *     event_list ){
932   debug_trace();
933   if( num_events == 0 || event_list == NULL )
934     RETURN(CL_INVALID_VALUE);
935   if( (struct _cl_event *)event_list[0] == NULL )
936     RETURN(CL_INVALID_EVENT);
937   RETURN(((struct _cl_event *)event_list[0])
938     ->dispatch->clWaitForEvents(num_events, event_list));
939 }
940 hidden_alias(clWaitForEvents);
941
942 CL_API_ENTRY cl_int CL_API_CALL
943 clUnloadCompiler( void ){
944   debug_trace();
945   RETURN(CL_SUCCESS);
946 }
947 hidden_alias(clUnloadCompiler);