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