cleanup code and add additionnal tests
[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 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS
33 #include <CL/opencl.h>
34
35 #pragma GCC visibility push(hidden)
36
37 #include "ocl_icd_loader.h"
38 #include "ocl_icd_debug.h"
39
40 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors"
41
42 int debug_ocl_icd_mask=0;
43
44 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
45 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
46
47
48 struct vendor_icd {
49   cl_uint       num_platforms;
50   cl_uint       first_platform;
51   void *        dl_handle;
52   clGetExtensionFunctionAddress_fn ext_fn_ptr;
53 };
54
55 struct platform_icd {
56   char *         extension_suffix;
57   char *         version;
58   struct vendor_icd *vicd;
59   cl_platform_id pid;
60 };
61
62 struct vendor_icd *_icds=NULL;
63 struct platform_icd *_picds=NULL;
64 static cl_uint _num_icds = 0;
65 static cl_uint _num_picds = 0;
66
67 static cl_uint _initialized = 0;
68
69 #if DEBUG_OCL_ICD
70 #  define _clS(x) [-x] = #x
71 #  define MAX_CL_ERRORS CL_INVALID_DEVICE_PARTITION_COUNT
72 static char const * const clErrorStr[-MAX_CL_ERRORS+1] = {
73   _clS(CL_SUCCESS),
74   _clS(CL_DEVICE_NOT_FOUND),
75   _clS(CL_DEVICE_NOT_AVAILABLE),
76   _clS(CL_COMPILER_NOT_AVAILABLE),
77   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
78   _clS(CL_OUT_OF_RESOURCES),
79   _clS(CL_OUT_OF_HOST_MEMORY),
80   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
81   _clS(CL_MEM_COPY_OVERLAP),
82   _clS(CL_IMAGE_FORMAT_MISMATCH),
83   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
84   _clS(CL_BUILD_PROGRAM_FAILURE),
85   _clS(CL_MAP_FAILURE),
86   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
87   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
88   _clS(CL_COMPILE_PROGRAM_FAILURE),
89   _clS(CL_LINKER_NOT_AVAILABLE),
90   _clS(CL_LINK_PROGRAM_FAILURE),
91   _clS(CL_DEVICE_PARTITION_FAILED),
92   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
93   _clS(CL_INVALID_VALUE),
94   _clS(CL_INVALID_DEVICE_TYPE),
95   _clS(CL_INVALID_PLATFORM),
96   _clS(CL_INVALID_DEVICE),
97   _clS(CL_INVALID_CONTEXT),
98   _clS(CL_INVALID_QUEUE_PROPERTIES),
99   _clS(CL_INVALID_COMMAND_QUEUE),
100   _clS(CL_INVALID_HOST_PTR),
101   _clS(CL_INVALID_MEM_OBJECT),
102   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
103   _clS(CL_INVALID_IMAGE_SIZE),
104   _clS(CL_INVALID_SAMPLER),
105   _clS(CL_INVALID_BINARY),
106   _clS(CL_INVALID_BUILD_OPTIONS),
107   _clS(CL_INVALID_PROGRAM),
108   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
109   _clS(CL_INVALID_KERNEL_NAME),
110   _clS(CL_INVALID_KERNEL_DEFINITION),
111   _clS(CL_INVALID_KERNEL),
112   _clS(CL_INVALID_ARG_INDEX),
113   _clS(CL_INVALID_ARG_VALUE),
114   _clS(CL_INVALID_ARG_SIZE),
115   _clS(CL_INVALID_KERNEL_ARGS),
116   _clS(CL_INVALID_WORK_DIMENSION),
117   _clS(CL_INVALID_WORK_GROUP_SIZE),
118   _clS(CL_INVALID_WORK_ITEM_SIZE),
119   _clS(CL_INVALID_GLOBAL_OFFSET),
120   _clS(CL_INVALID_EVENT_WAIT_LIST),
121   _clS(CL_INVALID_EVENT),
122   _clS(CL_INVALID_OPERATION),
123   _clS(CL_INVALID_GL_OBJECT),
124   _clS(CL_INVALID_BUFFER_SIZE),
125   _clS(CL_INVALID_MIP_LEVEL),
126   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
127   _clS(CL_INVALID_PROPERTY),
128   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
129   _clS(CL_INVALID_COMPILER_OPTIONS),
130   _clS(CL_INVALID_LINKER_OPTIONS),
131   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
132 };
133 #undef _clS
134 #endif
135
136 static char* _clerror2string (cl_int error) __attribute__((unused));
137 static char* _clerror2string (cl_int error) {
138 #if DEBUG_OCL_ICD
139   if (-error > MAX_CL_ERRORS || error > 0) {
140     debug(D_WARN, "Unknown error code %d", error);
141     RETURN_STR("OpenCL Error");
142   }
143   const char *ret=clErrorStr[-error];
144   if (ret == NULL) {
145     debug(D_WARN, "Unknown error code %d", error);
146     RETURN_STR("OpenCL Error");
147   }
148   RETURN_STR(ret);
149 #else
150   static char number[15];
151   if (error==0) {
152     RETURN_STR("CL_SUCCESS");
153   }
154   snprintf(number, 15, "%i", error);
155   RETURN_STR(number);
156 #endif
157 }
158
159 static inline cl_uint _find_num_icds(DIR *dir) {
160   cl_uint num_icds = 0;
161   struct dirent *ent;
162   while( (ent=readdir(dir)) != NULL ){
163     cl_uint d_name_len = strlen(ent->d_name);
164     if( d_name_len<5 || strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
165       continue;
166     num_icds++;
167   }
168   rewinddir(dir);
169   RETURN(num_icds);
170 }
171
172 static inline cl_uint _open_drivers(DIR *dir, const char* dir_path) {
173   cl_uint num_icds = 0;
174   struct dirent *ent;
175   while( (ent=readdir(dir)) != NULL ){
176     cl_uint d_name_len = strlen(ent->d_name);
177     if( d_name_len<5 || strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
178       continue;
179     char * lib_path;
180     char * err;
181     unsigned int lib_path_length = strlen(dir_path) + strlen(ent->d_name) + 2;
182     lib_path = malloc(lib_path_length*sizeof(char));
183     sprintf(lib_path,"%s/%s", dir_path, ent->d_name);
184     debug(D_LOG, "Considering file '%s'", lib_path);
185     FILE *f = fopen(lib_path,"r");
186     free(lib_path);
187
188     fseek(f, 0, SEEK_END);
189     lib_path_length = ftell(f)+1;
190     fseek(f, 0, SEEK_SET);
191     if(lib_path_length == 1) {
192       debug(D_WARN, "File contents too short, skipping ICD");
193       fclose(f);
194       continue;
195     }
196     lib_path = malloc(lib_path_length*sizeof(char));
197     err = fgets(lib_path, lib_path_length, f);
198     fclose(f);
199     if( err == NULL ) {
200       free(lib_path);
201       debug(D_WARN, "Error while loading file contents, skipping ICD");
202       continue;
203     }
204
205     lib_path_length = strlen(lib_path);
206     
207     if( lib_path[lib_path_length-1] == '\n' )
208       lib_path[lib_path_length-1] = '\0';
209
210     debug(D_LOG, "Loading ICD '%s'", lib_path);
211
212     _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
213     if(_icds[num_icds].dl_handle != NULL) {
214       debug(D_LOG, "ICD[%i] loaded", num_icds);
215       num_icds++;
216     } else {
217       debug(D_WARN, "error while dlopening the IDL, skipping ICD");
218     }
219     free(lib_path);
220   }
221   RETURN(num_icds);
222 }
223
224 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
225   void *addr1;
226   addr1=dlsym(dlh, name);
227   if (addr1 == NULL) {
228     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
229   }
230   void* addr2=NULL;
231   if (fn) {
232     addr2=(*fn)(name);
233     if (addr2 == NULL) {
234       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
235     }
236 #if DEBUG_OCL_ICD
237     if (addr1 && addr2 && addr1!=addr2) {
238       debug(D_WARN, "Function and symbol '%s' have different addresses!", name);
239     }
240 #endif
241   }
242   if (!addr2) addr2=addr1;
243   RETURN(addr2);
244 }
245
246 static int _allocate_platforms(int req) {
247   static cl_uint allocated=0;
248   debug(D_LOG,"Requesting allocation for %d platforms",req);
249   if (allocated - _num_picds < req) {
250     if (allocated==0) {
251       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
252     } else {
253       req = req - (allocated - _num_picds);
254       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
255     }
256     allocated += req;
257   }
258   RETURN(allocated - _num_picds);
259 }
260
261 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
262                  cl_platform_id pid, cl_platform_info cname, char* sname) {
263   cl_int error;
264   size_t param_value_size_ret;
265   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
266   if (error != CL_SUCCESS) {
267     debug(D_WARN, "Error %s while requesting %s in platform %p",
268           _clerror2string(error), sname, pid);
269     return NULL;
270   }
271   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
272   if (param_value == NULL) {
273     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
274           sname, pid);
275     return NULL;
276   }
277   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
278   if (error != CL_SUCCESS){
279     free(param_value);
280     debug(D_WARN, "Error %s while requesting %s in platform %p",
281           _clerror2string(error), sname, pid);
282     return NULL;
283   }
284   RETURN_STR(param_value);
285 }
286
287 static inline void _find_and_check_platforms(cl_uint num_icds) {
288   cl_uint i;
289   _num_icds = 0;
290   for( i=0; i<num_icds; i++){
291     debug(D_LOG, "Checking ICD %i", i);
292     struct vendor_icd *picd = &_icds[_num_icds];
293     void* dlh = _icds[i].dl_handle;
294     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
295     clIcdGetPlatformIDsKHR_fn plt_fn_ptr = 
296       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
297     clGetPlatformInfo_fn plt_info_ptr = 
298       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
299     if( picd->ext_fn_ptr == NULL
300         || plt_fn_ptr == NULL
301         || plt_info_ptr == NULL) {
302       debug(D_WARN, "Missing symbols in ICD, skipping it");
303       continue;
304     }
305     cl_uint num_platforms=0;
306     cl_int error;
307     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
308     if( error != CL_SUCCESS || num_platforms == 0) {
309       debug(D_LOG, "No platform in ICD, skipping it");
310       continue;
311     }
312     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
313     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
314     if( error != CL_SUCCESS ){
315       free(platforms);
316       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
317       continue;
318     }
319     cl_uint num_valid_platforms=0;
320     cl_uint j;
321     debug(D_LOG, "Try to load %d plateforms", num_platforms);
322     if (_allocate_platforms(num_platforms) < num_platforms) {
323       free(platforms);
324       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
325       continue;
326     }
327     for(j=0; j<num_platforms; j++) {
328       debug(D_LOG, "Checking platform %i", j);
329       struct platform_icd *p=&_picds[_num_picds];
330       p->extension_suffix=NULL;
331       p->vicd=&_icds[i];
332       p->pid=platforms[j];
333 #if DEBUG_OCL_ICD
334       if (debug_ocl_icd_mask & D_DUMP) {
335         dump_platform(p->pid);
336       }
337 #endif
338       char *param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
339       if (param_value == NULL){
340         debug(D_WARN, "Skipping platform %i", j);
341         continue;
342       }
343       debug(D_DUMP, "Supported extensions: %s", param_value);
344       if( strstr(param_value, "cl_khr_icd") == NULL){
345         free(param_value);
346         debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
347         continue;
348       }
349       free(param_value);
350       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
351       if (param_value == NULL){
352         debug(D_WARN, "Skipping platform %i", j);
353         continue;
354       }
355       p->extension_suffix = param_value;
356       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
357 #if DEBUG_OCL_ICD
358       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
359       if (param_value != NULL){
360         debug(D_DUMP, "Profile: %s", param_value);
361         free(param_value);
362       }
363 #endif
364       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
365       p->version = param_value;
366       if (param_value != NULL){
367         debug(D_DUMP, "Version: %s", param_value);
368         free(param_value);
369       }
370 #if DEBUG_OCL_ICD
371       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
372       if (param_value != NULL){
373         debug(D_DUMP, "Name: %s", param_value);
374         free(param_value);
375       }
376       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
377       if (param_value != NULL){
378         debug(D_DUMP, "Vendor: %s", param_value);
379         free(param_value);
380       }
381 #endif
382       num_valid_platforms++;
383       _num_picds++;
384     }
385     if( num_valid_platforms != 0 ) {
386       if ( _num_icds != i ) {
387         picd->dl_handle = dlh;
388       }
389       _num_icds++;
390       picd->num_platforms = num_valid_platforms;
391       _icds[i].first_platform = _num_picds - num_valid_platforms;
392     } else {
393       dlclose(dlh);
394     }
395     free(platforms);
396   }
397 }
398
399 static void _initClIcd( void ) {
400   if( _initialized )
401     return;
402   debug_init();
403   cl_uint num_icds = 0;
404   DIR *dir;
405   const char* dir_path=getenv("OCL_ICD_VENDORS");
406   if (! dir_path || dir_path[0]==0) {
407     dir_path=ETC_OPENCL_VENDORS;
408   }
409   debug(D_LOG,"Reading icd list from '%s'", dir_path);
410   dir = opendir(dir_path);
411   if(dir == NULL) {
412     goto abort;
413   }
414
415   num_icds = _find_num_icds(dir);
416   if(num_icds == 0) {
417     goto abort;
418   }
419
420   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
421   if (_icds == NULL) {
422     goto abort;
423   }
424   
425   num_icds = _open_drivers(dir, dir_path);
426   if(num_icds == 0) {
427     goto abort;
428   }
429
430   _find_and_check_platforms(num_icds);
431   if(_num_icds == 0){
432     goto abort;
433   }
434
435   if (_num_icds < num_icds) {
436     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
437   }
438   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
439   _initialized = 1;
440   return;
441 abort:
442   _num_icds = 0;
443   _initialized = 1;
444   if (_icds) {
445     free(_icds);
446     _icds = NULL;
447   }
448   return;
449 }
450
451 #pragma GCC visibility pop
452
453 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
454   if( !_initialized )
455     _initClIcd();
456   if( func_name == NULL )
457     return NULL;
458   cl_uint suffix_length;
459   cl_uint i;
460   void * return_value=NULL;
461   struct func_desc const * fn=&function_description[0];
462   while (fn->name != NULL) {
463     if (strcmp(func_name, fn->name)==0)
464       return fn->addr;
465     fn++;
466   }
467   for(i=0; i<_num_picds; i++) {
468     suffix_length = strlen(_picds[i].extension_suffix);
469     if( suffix_length > strlen(func_name) )
470       continue;
471     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
472       return (*_picds[i].vicd->ext_fn_ptr)(func_name);
473   }
474   return return_value;
475 }
476 typeof(clGetExtensionFunctionAddress) clGetExtensionFunctionAddress_hid __attribute__ ((alias ("clGetExtensionFunctionAddress"), visibility("hidden")));
477
478 CL_API_ENTRY cl_int CL_API_CALL
479 clGetPlatformIDs(cl_uint          num_entries,
480                  cl_platform_id * platforms,
481                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
482   if( !_initialized )
483     _initClIcd();
484   if( platforms == NULL && num_platforms == NULL )
485     return CL_INVALID_VALUE;
486   if( num_entries == 0 && platforms != NULL )
487     return CL_INVALID_VALUE;
488   if( _num_icds == 0)
489     return CL_PLATFORM_NOT_FOUND_KHR;
490
491   cl_uint i;
492   if( num_platforms != NULL ){
493     *num_platforms = _num_picds;
494   }
495   if( platforms != NULL ) {
496     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
497     for( i=0; i<n_platforms; i++) {
498       *(platforms++) = _picds[i].pid;
499     }
500   }
501   return CL_SUCCESS;
502 }
503 typeof(clGetPlatformIDs) clGetPlatformIDs_hid __attribute__ ((alias ("clGetPlatformIDs"), visibility("hidden")));
504