1 /* Definition of available OpenCL devices.
2 
3    Copyright (c) 2011 Universidad Rey Juan Carlos and
4                  2012-2018 Pekka Jääskeläinen
5 
6    Permission is hereby granted, free of charge, to any person obtaining a copy
7    of this software and associated documentation files (the "Software"), to deal
8    in the Software without restriction, including without limitation the rights
9    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10    copies of the Software, and to permit persons to whom the Software is
11    furnished to do so, subject to the following conditions:
12 
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15 
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22    THE SOFTWARE.
23 */
24 
25 #define _GNU_SOURCE
26 
27 #include <string.h>
28 #include <ctype.h>
29 
30 #ifdef __linux__
31 #include <limits.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <ucontext.h>
37 #endif
38 
39 #ifndef _WIN32
40 #  include <unistd.h>
41 #else
42 #  include "vccompat.hpp"
43 #endif
44 
45 #include "common.h"
46 #include "devices.h"
47 #include "pocl_cache.h"
48 #include "pocl_debug.h"
49 #include "pocl_runtime_config.h"
50 #include "pocl_shared.h"
51 #include "pocl_tracing.h"
52 #include "pocl_util.h"
53 
54 #ifdef ENABLE_LLVM
55 #include "pocl_llvm.h"
56 #endif
57 
58 #ifdef BUILD_BASIC
59 #include "basic/basic.h"
60 #endif
61 #ifdef BUILD_PTHREAD
62 #include "pthread/pocl-pthread.h"
63 #endif
64 
65 #ifdef TCE_AVAILABLE
66 #include "tce/ttasim/ttasim.h"
67 #endif
68 
69 #ifdef BUILD_HSA
70 #include "hsa/pocl-hsa.h"
71 #endif
72 
73 #ifdef BUILD_CUDA
74 #include "cuda/pocl-cuda.h"
75 #endif
76 
77 #if defined(BUILD_ACCEL)
78 #include "accel/accel.h"
79 #endif
80 
81 #define MAX_DEV_NAME_LEN 64
82 
83 #ifndef PATH_MAX
84 #define PATH_MAX 4096
85 #endif
86 
87 #ifdef HAVE_DLFCN_H
88 #if defined(__APPLE__)
89 #define _DARWIN_C_SOURCE
90 #endif
91 #include <dlfcn.h>
92 #endif
93 
94 /* the enabled devices */
95 static struct _cl_device_id* pocl_devices = NULL;
96 unsigned int pocl_num_devices = 0;
97 
98 #ifdef ENABLE_LOADABLE_DRIVERS
99 #define INIT_DEV(ARG) NULL
100 #else
101 #define INIT_DEV(ARG) pocl_##ARG##_init_device_ops
102 #endif
103 
104 /* Init function prototype */
105 typedef void (*init_device_ops)(struct pocl_device_ops*);
106 
107 /* All init function for device operations available to pocl */
108 static init_device_ops pocl_devices_init_ops[] = {
109 #ifdef BUILD_BASIC
110   INIT_DEV (basic),
111 #endif
112 #ifdef BUILD_PTHREAD
113   INIT_DEV (pthread),
114 #endif
115 #ifdef TCE_AVAILABLE
116   INIT_DEV (ttasim),
117 #endif
118 #ifdef BUILD_HSA
119   INIT_DEV (hsa),
120 #endif
121 #ifdef BUILD_CUDA
122   INIT_DEV (cuda),
123 #endif
124 #ifdef BUILD_ACCEL
125   INIT_DEV (accel),
126 #endif
127 };
128 
129 #define POCL_NUM_DEVICE_TYPES (sizeof(pocl_devices_init_ops) / sizeof((pocl_devices_init_ops)[0]))
130 
131 char pocl_device_types[POCL_NUM_DEVICE_TYPES][30] = {
132 #ifdef BUILD_BASIC
133   "basic",
134 #endif
135 #ifdef BUILD_PTHREAD
136   "pthread",
137 #endif
138 #ifdef TCE_AVAILABLE
139   "ttasim",
140 #endif
141 #ifdef BUILD_HSA
142   "hsa",
143 #endif
144 #ifdef BUILD_CUDA
145   "cuda",
146 #endif
147 #ifdef BUILD_ACCEL
148   "accel",
149 #endif
150 };
151 
152 static struct pocl_device_ops pocl_device_ops[POCL_NUM_DEVICE_TYPES];
153 
154 extern pocl_lock_t pocl_runtime_config_lock;
155 extern pocl_lock_t pocl_context_handling_lock;
156 
157 int pocl_offline_compile = 0;
158 
159 // first setup
160 static unsigned first_init_done = 0;
161 static unsigned init_in_progress = 0;
162 static unsigned device_count[POCL_NUM_DEVICE_TYPES];
163 
164 // after calling drivers uninit, we may have to re-init the devices.
165 static unsigned devices_active = 0;
166 
167 static pocl_lock_t pocl_init_lock = POCL_LOCK_INITIALIZER;
168 
169 #ifdef ENABLE_LOADABLE_DRIVERS
170 
171 static void *pocl_device_handles[POCL_NUM_DEVICE_TYPES];
172 
173 #ifndef _WIN32
174 #define POCL_PATH_SEPARATOR "/"
175 #else
176 #define POCL_PATH_SEPARATOR "\\"
177 #endif
178 
179 static void
get_pocl_device_lib_path(char * result,char * device_name,int absolute_path)180 get_pocl_device_lib_path (char *result, char *device_name, int absolute_path)
181 {
182   Dl_info info;
183   if (absolute_path && dladdr ((void *)get_pocl_device_lib_path, &info))
184     {
185       char const *soname = info.dli_fname;
186       strcpy (result, soname);
187       char *last_slash = strrchr (result, POCL_PATH_SEPARATOR[0]);
188       *(++last_slash) = '\0';
189       if (strlen (result) > 0)
190         {
191 #ifdef ENABLE_POCL_BUILDING
192           if (pocl_get_bool_option ("POCL_BUILDING", 0))
193             {
194               strcat (result, "devices");
195               strcat (result, POCL_PATH_SEPARATOR);
196               if (strncmp(device_name, "ttasim", 6) == 0)
197                 {
198                   strcat (result, "tce");
199                 }
200               else
201                 {
202                   strcat (result, device_name);
203                 }
204               strcat (result, POCL_PATH_SEPARATOR);
205             }
206           else
207 #endif
208             {
209               strcat (result, POCL_INSTALL_PRIVATE_LIBDIR_REL);
210             }
211           strcat (result, POCL_PATH_SEPARATOR);
212           strcat (result, "libpocl-devices-");
213           strcat (result, device_name);
214           strcat (result, ".so");
215         }
216     }
217   else
218     {
219       strcat (result, "libpocl-devices-");
220       strcat (result, device_name);
221       strcat (result, ".so");
222     }
223 }
224 #endif
225 
226 /**
227  * Get the number of specified devices from environment
228  */
pocl_device_get_env_count(const char * dev_type)229 int pocl_device_get_env_count(const char *dev_type)
230 {
231   const char *dev_env = getenv(POCL_DEVICES_ENV);
232   char *ptr, *saveptr = NULL, *tofree, *token;
233   unsigned int dev_count = 0;
234   if (dev_env == NULL)
235     {
236       return -1;
237     }
238   ptr = tofree = strdup(dev_env);
239   while ((token = strtok_r (ptr, " ", &saveptr)) != NULL)
240     {
241       if(strcmp(token, dev_type) == 0)
242         dev_count++;
243       ptr = NULL;
244     }
245   POCL_MEM_FREE(tofree);
246 
247   return dev_count;
248 }
249 
250 unsigned int
pocl_get_devices(cl_device_type device_type,cl_device_id * devices,unsigned int num_devices)251 pocl_get_devices (cl_device_type device_type, cl_device_id *devices,
252                   unsigned int num_devices)
253 {
254   unsigned int i, dev_added = 0;
255 
256   for (i = 0; i < pocl_num_devices; ++i)
257     {
258       if (!pocl_offline_compile && (pocl_devices[i].available == CL_FALSE))
259         continue;
260 
261       if (device_type == CL_DEVICE_TYPE_DEFAULT)
262         {
263           devices[dev_added] = &pocl_devices[i];
264           ++dev_added;
265           break;
266         }
267 
268       if (pocl_devices[i].type & device_type)
269         {
270             if (dev_added < num_devices)
271               {
272                 devices[dev_added] = &pocl_devices[i];
273                 ++dev_added;
274               }
275             else
276               {
277                 break;
278               }
279         }
280     }
281   return dev_added;
282 }
283 
284 unsigned int
pocl_get_device_type_count(cl_device_type device_type)285 pocl_get_device_type_count(cl_device_type device_type)
286 {
287   unsigned int count = 0;
288   unsigned int i;
289 
290   for (i = 0; i < pocl_num_devices; ++i)
291     {
292       if (!pocl_offline_compile && (pocl_devices[i].available == CL_FALSE))
293         continue;
294 
295       if (device_type == CL_DEVICE_TYPE_DEFAULT)
296         return 1;
297 
298       if (pocl_devices[i].type & device_type)
299         {
300            ++count;
301         }
302     }
303 
304   return count;
305 }
306 
307 
308 static inline void
str_toupper(char * out,const char * in)309 str_toupper(char *out, const char *in)
310 {
311   int i;
312 
313   for (i = 0; in[i] != '\0'; i++)
314     out[i] = toupper(in[i]);
315   out[i] = '\0';
316 }
317 
318 cl_int
pocl_uninit_devices()319 pocl_uninit_devices ()
320 {
321   cl_int retval = CL_SUCCESS;
322 
323   POCL_LOCK (pocl_init_lock);
324   if ((!devices_active) || (pocl_num_devices == 0))
325     goto FINISH;
326 
327   POCL_MSG_PRINT_GENERAL ("UNINIT all devices\n");
328 
329   unsigned i, j, dev_index;
330 
331   dev_index = 0;
332   cl_device_id d;
333   for (i = 0; i < POCL_NUM_DEVICE_TYPES; ++i)
334     {
335       if (pocl_devices_init_ops[i] == NULL)
336         continue;
337       assert (pocl_device_ops[i].init);
338       for (j = 0; j < device_count[i]; ++j)
339         {
340           d = &pocl_devices[dev_index];
341           if (d->available == 0)
342             continue;
343           if (d->ops->reinit == NULL || d->ops->uninit == NULL)
344             continue;
345           cl_int ret = d->ops->uninit (j, d);
346           if (ret != CL_SUCCESS)
347             {
348               retval = ret;
349               goto FINISH;
350             }
351 #ifdef ENABLE_LOADABLE_DRIVERS
352           if (pocl_device_handles[i] != NULL)
353             {
354               dlclose (pocl_device_handles[i]);
355             }
356 #endif
357           ++dev_index;
358         }
359     }
360 
361 FINISH:
362   devices_active = 0;
363   POCL_UNLOCK (pocl_init_lock);
364 
365   return retval;
366 }
367 
368 static cl_int
pocl_reinit_devices()369 pocl_reinit_devices ()
370 {
371   assert (first_init_done);
372   cl_int retval = CL_SUCCESS;
373 
374   if (devices_active)
375     return retval;
376 
377   if (pocl_num_devices == 0)
378     return CL_DEVICE_NOT_FOUND;
379 
380   POCL_MSG_WARN ("REINIT all devices\n");
381 
382   unsigned i, j, dev_index;
383 
384   dev_index = 0;
385   cl_device_id d;
386   /* Init infos for each probed devices */
387   for (i = 0; i < POCL_NUM_DEVICE_TYPES; ++i)
388     {
389       assert (pocl_device_ops[i].init);
390       for (j = 0; j < device_count[i]; ++j)
391         {
392           d = &pocl_devices[dev_index];
393           if (d->available == 0)
394             continue;
395           if (d->ops->reinit == NULL || d->ops->uninit == NULL)
396             continue;
397           cl_int ret = d->ops->reinit (j, d);
398           if (ret != CL_SUCCESS)
399             {
400               retval = ret;
401               goto FINISH;
402             }
403 
404           ++dev_index;
405         }
406     }
407 
408 FINISH:
409 
410   devices_active = 1;
411   return retval;
412 }
413 
414 cl_int
pocl_init_devices()415 pocl_init_devices ()
416 {
417   int errcode = CL_SUCCESS;
418 
419   /* This is a workaround to a nasty problem with libhwloc: When
420      initializing basic, it calls libhwloc to query device info.
421      In case libhwloc has the OpenCL plugin installed, it initializes
422      it and it leads to initializing pocl again which leads to an
423      infinite loop. This only protects against recursive calls of
424      pocl_init_devices(), so must be done without pocl_init_lock held. */
425   if (init_in_progress)
426     return CL_SUCCESS; /* debatable, but what else can we do ? */
427 
428   POCL_LOCK (pocl_init_lock);
429   init_in_progress = 1;
430 
431   if (first_init_done)
432     {
433       if (!devices_active)
434         {
435           POCL_MSG_PRINT_GENERAL ("FIRST INIT done; REINIT all devices\n");
436           pocl_reinit_devices (); // TODO err check
437         }
438       errcode = pocl_num_devices ? CL_SUCCESS : CL_DEVICE_NOT_FOUND;
439       goto ERROR;
440     }
441   else
442     {
443       POCL_INIT_LOCK (pocl_runtime_config_lock);
444       POCL_INIT_LOCK (pocl_context_handling_lock);
445     }
446 
447   /* first time initialization */
448   unsigned i, j, dev_index;
449   char env_name[1024];
450   char dev_name[MAX_DEV_NAME_LEN] = { 0 };
451 
452   /* Set a global debug flag, so we don't have to call pocl_get_bool_option
453    * everytime we use the debug macros */
454 #ifdef POCL_DEBUG_MESSAGES
455   const char* debug = pocl_get_string_option ("POCL_DEBUG", "0");
456   pocl_debug_messages_setup (debug);
457   pocl_stderr_is_a_tty = isatty(fileno(stderr));
458 #endif
459 
460   POCL_GOTO_ERROR_ON ((pocl_cache_init_topdir ()), CL_DEVICE_NOT_FOUND,
461                       "Cache directory initialization failed");
462 
463   pocl_event_tracing_init ();
464 
465 #ifdef HAVE_SLEEP
466   int delay = pocl_get_int_option ("POCL_STARTUP_DELAY", 0);
467   if (delay > 0)
468     sleep (delay);
469 #endif
470 
471 
472 #ifdef __linux__
473 
474 #ifdef ENABLE_HOST_CPU_DEVICES
475   if (pocl_get_bool_option ("POCL_SIGFPE_HANDLER", 1))
476     {
477       pocl_install_sigfpe_handler ();
478     }
479 #endif
480 
481   if (pocl_get_bool_option ("POCL_SIGUSR2_HANDLER", 0))
482     {
483       pocl_install_sigusr2_handler ();
484     }
485 #endif
486 
487   pocl_offline_compile = pocl_get_bool_option ("POCL_OFFLINE_COMPILE", 0);
488 
489   /* Init operations */
490   for (i = 0; i < POCL_NUM_DEVICE_TYPES; ++i)
491     {
492 #ifdef ENABLE_LOADABLE_DRIVERS
493       if (pocl_devices_init_ops[i] == NULL)
494         {
495           char device_library[PATH_MAX] = "";
496           char init_device_ops_name[MAX_DEV_NAME_LEN + 21] = "";
497           get_pocl_device_lib_path (device_library, pocl_device_types[i], 1);
498           pocl_device_handles[i] = dlopen (device_library, RTLD_LAZY);
499           if (pocl_device_handles[i] == NULL)
500             {
501               POCL_MSG_WARN ("Loading %s failed: %s\n", device_library,
502                              dlerror ());
503 
504               /* Try again with just the *.so filename */
505               device_library[0] = 0;
506               get_pocl_device_lib_path (device_library,
507                                         pocl_device_types[i], 0);
508               pocl_device_handles[i] = dlopen (device_library, RTLD_LAZY);
509               if (pocl_device_handles[i] == NULL)
510                 {
511                   POCL_MSG_WARN ("Loading %s failed: %s\n", device_library,
512                                  dlerror ());
513                   device_count[i] = 0;
514                   continue;
515                 }
516               else
517                 {
518                   POCL_MSG_WARN ("Fallback loading %s succeeded\n",
519                                  device_library);
520                 }
521             }
522           strcat (init_device_ops_name, "pocl_");
523           strcat (init_device_ops_name, pocl_device_types[i]);
524           strcat (init_device_ops_name, "_init_device_ops");
525           pocl_devices_init_ops[i] = (init_device_ops)dlsym (
526           pocl_device_handles[i], init_device_ops_name);
527           if (pocl_devices_init_ops[i] != NULL)
528             {
529               pocl_devices_init_ops[i](&pocl_device_ops[i]);
530             }
531           else
532             {
533               POCL_MSG_ERR ("Loading symbol %s from %s failed: %s\n",
534                              init_device_ops_name, device_library,
535                              dlerror ());
536               device_count[i] = 0;
537               continue;
538             }
539         }
540       else
541         {
542           pocl_device_handles[i] = NULL;
543         }
544 #else
545       assert (pocl_devices_init_ops[i] != NULL);
546 #endif
547       pocl_devices_init_ops[i](&pocl_device_ops[i]);
548       assert(pocl_device_ops[i].device_name != NULL);
549 
550       /* Probe and add the result to the number of probed devices */
551       assert(pocl_device_ops[i].probe);
552       device_count[i] = pocl_device_ops[i].probe(&pocl_device_ops[i]);
553       pocl_num_devices += device_count[i];
554     }
555 
556   const char *dev_env = pocl_get_string_option (POCL_DEVICES_ENV, NULL);
557   POCL_GOTO_ERROR_ON ((pocl_num_devices == 0), CL_DEVICE_NOT_FOUND,
558                       "no devices found. %s=%s\n", POCL_DEVICES_ENV, dev_env);
559 
560   pocl_devices = (struct _cl_device_id*) calloc(pocl_num_devices, sizeof(struct _cl_device_id));
561   POCL_GOTO_ERROR_ON ((pocl_devices == NULL), CL_OUT_OF_HOST_MEMORY,
562                       "Can not allocate memory for devices\n");
563 
564   dev_index = 0;
565   /* Init infos for each probed devices */
566   for (i = 0; i < POCL_NUM_DEVICE_TYPES; ++i)
567     {
568       if (pocl_devices_init_ops[i] == NULL)
569         continue;
570       str_toupper (dev_name, pocl_device_ops[i].device_name);
571       assert(pocl_device_ops[i].init);
572       for (j = 0; j < device_count[i]; ++j)
573         {
574           cl_device_id dev = &pocl_devices[dev_index];
575           dev->ops = &pocl_device_ops[i];
576           dev->dev_id = dev_index;
577           /* The default value for the global memory space identifier is
578              the same as the device id. The device instance can then override
579              it to point to some other device's global memory id in case of
580              a shared global memory. */
581           dev->global_mem_id = dev_index;
582           POCL_INIT_OBJECT (dev);
583           dev->driver_version = POCL_VERSION_FULL;
584           if (dev->version == NULL)
585             dev->version = "OpenCL 2.0 pocl";
586           dev->short_name = strdup (dev->ops->device_name);
587 
588           /* Check if there are device-specific parameters set in the
589              POCL_DEVICEn_PARAMETERS env. */
590           POCL_GOTO_ERROR_ON (
591               (snprintf (env_name, 1024, "POCL_%s%d_PARAMETERS", dev_name, j)
592                < 0),
593               CL_OUT_OF_HOST_MEMORY, "Unable to generate the env string.");
594           errcode = pocl_devices[dev_index].ops->init (
595               j, &pocl_devices[dev_index], getenv (env_name));
596           POCL_GOTO_ERROR_ON ((errcode != CL_SUCCESS), CL_DEVICE_NOT_AVAILABLE,
597                               "Device %i / %s initialization failed!", j,
598                               dev_name);
599 
600           ++dev_index;
601         }
602     }
603 
604   first_init_done = 1;
605   devices_active = 1;
606 ERROR:
607   init_in_progress = 0;
608   POCL_UNLOCK (pocl_init_lock);
609   return errcode;
610 }
611