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