Add cross-platform NUMA library
[blender.git] / intern / numaapi / source / numaapi_linux.c
1 // Copyright (c) 2016, libnumaapi authors
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 // IN THE SOFTWARE.
20 //
21 // Author: Sergey Sharybin (sergey.vfx@gmail.com)
22
23 #include "build_config.h"
24
25 #if OS_LINUX
26
27 #include "numaapi.h"
28
29 #include <stdlib.h>
30
31 #ifndef WITH_DYNLOAD
32 #  include <numa.h>
33 #else
34 #  include <dlfcn.h>
35 #endif
36
37 #ifdef WITH_DYNLOAD
38
39 // Descriptor numa library.
40 static void* numa_lib;
41
42 // Types of all symbols which are read from the library.
43 struct bitmask;
44 typedef int tnuma_available(void);
45 typedef int tnuma_max_node(void);
46 typedef int tnuma_node_to_cpus(int node, struct bitmask* mask);
47 typedef long tnuma_node_size(int node, long* freep);
48 typedef int tnuma_run_on_node(int node);
49 typedef void* tnuma_alloc_onnode(size_t size, int node);
50 typedef void* tnuma_alloc_local(size_t size);
51 typedef void tnuma_free(void* start, size_t size);
52 typedef struct bitmask* tnuma_bitmask_clearall(struct bitmask *bitmask);
53 typedef int tnuma_bitmask_isbitset(const struct bitmask *bitmask,
54                                    unsigned int n);
55 typedef struct bitmask* tnuma_bitmask_setbit(struct bitmask *bitmask,
56                                              unsigned int n);
57 typedef unsigned int tnuma_bitmask_nbytes(struct bitmask *bitmask);
58 typedef void tnuma_bitmask_free(struct bitmask *bitmask);
59 typedef struct bitmask* tnuma_allocate_cpumask(void);
60 typedef struct bitmask* tnuma_allocate_nodemask(void);
61 typedef void tnuma_free_cpumask(struct bitmask* bitmask);
62 typedef void tnuma_free_nodemask(struct bitmask* bitmask);
63 typedef int tnuma_run_on_node_mask(struct bitmask *nodemask);
64 typedef void tnuma_set_interleave_mask(struct bitmask *nodemask);
65 typedef void tnuma_set_localalloc(void);
66
67 // Actual symbols.
68 static tnuma_available* numa_available;
69 static tnuma_max_node* numa_max_node;
70 static tnuma_node_to_cpus* numa_node_to_cpus;
71 static tnuma_node_size* numa_node_size;
72 static tnuma_run_on_node* numa_run_on_node;
73 static tnuma_alloc_onnode* numa_alloc_onnode;
74 static tnuma_alloc_local* numa_alloc_local;
75 static tnuma_free* numa_free;
76 static tnuma_bitmask_clearall* numa_bitmask_clearall;
77 static tnuma_bitmask_isbitset* numa_bitmask_isbitset;
78 static tnuma_bitmask_setbit* numa_bitmask_setbit;
79 static tnuma_bitmask_nbytes* numa_bitmask_nbytes;
80 static tnuma_bitmask_free* numa_bitmask_free;
81 static tnuma_allocate_cpumask* numa_allocate_cpumask;
82 static tnuma_allocate_nodemask* numa_allocate_nodemask;
83 static tnuma_free_nodemask* numa_free_nodemask;
84 static tnuma_free_cpumask* numa_free_cpumask;
85 static tnuma_run_on_node_mask* numa_run_on_node_mask;
86 static tnuma_set_interleave_mask* numa_set_interleave_mask;
87 static tnuma_set_localalloc* numa_set_localalloc;
88
89 static void* findLibrary(const char** paths) {
90   int i = 0;
91   while (paths[i] != NULL) {
92       void* lib = dlopen(paths[i], RTLD_LAZY);
93       if (lib != NULL) {
94         return lib;
95       }
96       ++i;
97   }
98   return NULL;
99 }
100
101 static void numaExit(void) {
102   if (numa_lib == NULL) {
103     return;
104   }
105   dlclose(numa_lib);
106   numa_lib = NULL;
107 }
108
109 static NUMAAPI_Result loadNumaSymbols(void) {
110   // Prevent multiple initializations.
111   static bool initialized = false;
112   static NUMAAPI_Result result = NUMAAPI_NOT_AVAILABLE;
113   if (initialized) {
114     return result;
115   }
116   initialized = true;
117   // Find appropriate .so library.
118   const char* numa_paths[] = {
119       "libnuma.so.1",
120       "libnuma.so",
121       NULL};
122   // Register de-initialization.
123   const int error = atexit(numaExit);
124   if (error) {
125     result = NUMAAPI_ERROR_ATEXIT;
126     return result;
127   }
128   // Load library.
129   numa_lib = findLibrary(numa_paths);
130   if (numa_lib == NULL) {
131     result = NUMAAPI_NOT_AVAILABLE;
132     return result;
133   }
134   // Load symbols.
135
136 #define _LIBRARY_FIND(lib, name)          \
137   do {                                    \
138     name = (t##name *)dlsym(lib, #name);  \
139   } while (0)
140 #define NUMA_LIBRARY_FIND(name) _LIBRARY_FIND(numa_lib, name)
141
142   NUMA_LIBRARY_FIND(numa_available);
143   NUMA_LIBRARY_FIND(numa_max_node);
144   NUMA_LIBRARY_FIND(numa_node_to_cpus);
145   NUMA_LIBRARY_FIND(numa_node_size);
146   NUMA_LIBRARY_FIND(numa_run_on_node);
147   NUMA_LIBRARY_FIND(numa_alloc_onnode);
148   NUMA_LIBRARY_FIND(numa_alloc_local);
149   NUMA_LIBRARY_FIND(numa_free);
150   NUMA_LIBRARY_FIND(numa_bitmask_clearall);
151   NUMA_LIBRARY_FIND(numa_bitmask_isbitset);
152   NUMA_LIBRARY_FIND(numa_bitmask_setbit);
153   NUMA_LIBRARY_FIND(numa_bitmask_nbytes);
154   NUMA_LIBRARY_FIND(numa_bitmask_free);
155   NUMA_LIBRARY_FIND(numa_allocate_cpumask);
156   NUMA_LIBRARY_FIND(numa_allocate_nodemask);
157   NUMA_LIBRARY_FIND(numa_free_cpumask);
158   NUMA_LIBRARY_FIND(numa_free_nodemask);
159   NUMA_LIBRARY_FIND(numa_run_on_node_mask);
160   NUMA_LIBRARY_FIND(numa_set_interleave_mask);
161   NUMA_LIBRARY_FIND(numa_set_localalloc);
162
163 #undef NUMA_LIBRARY_FIND
164 #undef _LIBRARY_FIND
165
166   result = NUMAAPI_SUCCESS;
167   return result;
168 }
169 #endif
170
171 ////////////////////////////////////////////////////////////////////////////////
172 // Initialization.
173
174 NUMAAPI_Result numaAPI_Initialize(void) {
175 #ifdef WITH_DYNLOAD
176   NUMAAPI_Result result = loadNumaSymbols();
177   if (result != NUMAAPI_SUCCESS) {
178     return result;
179   }
180 #endif
181   if (numa_available() < 0) {
182     return NUMAAPI_NOT_AVAILABLE;
183   }
184   return NUMAAPI_SUCCESS;
185 }
186
187 ////////////////////////////////////////////////////////////////////////////////
188 // Topology query.
189
190 int numaAPI_GetNumNodes(void) {
191   return numa_max_node() + 1;
192 }
193
194 bool numaAPI_IsNodeAvailable(int node) {
195   if (numa_node_size(node, NULL) > 0) {
196     return true;
197   }
198   return false;
199 }
200
201 int numaAPI_GetNumNodeProcessors(int node) {
202   struct bitmask* cpu_mask = numa_allocate_cpumask();
203   numa_node_to_cpus(node, cpu_mask);
204   const unsigned int num_bytes = numa_bitmask_nbytes(cpu_mask);
205   const unsigned int num_bits = num_bytes  *8;
206   // TODO(sergey): There might be faster way calculating number of set bits.
207   int num_processors = 0;
208   for (unsigned int bit = 0; bit < num_bits; ++bit) {
209     if (numa_bitmask_isbitset(cpu_mask, bit)) {
210       ++num_processors;
211     }
212   }
213 #ifdef WITH_DYNLOAD
214   if (numa_free_cpumask != NULL) {
215     numa_free_cpumask(cpu_mask);
216   } else {
217     numa_bitmask_free(cpu_mask);
218   }
219 #else
220   numa_free_cpumask(cpu_mask);
221 #endif
222   return num_processors;
223 }
224
225 ////////////////////////////////////////////////////////////////////////////////
226 // Affinities.
227
228 bool numaAPI_RunProcessOnNode(int node) {
229   numaAPI_RunThreadOnNode(node);
230   return true;
231 }
232
233 bool numaAPI_RunThreadOnNode(int node) {
234   // Construct bit mask from node index.
235   struct bitmask* node_mask = numa_allocate_nodemask();
236   numa_bitmask_clearall(node_mask);
237   numa_bitmask_setbit(node_mask, node);
238   numa_run_on_node_mask(node_mask);
239   // TODO(sergey): The following commands are based on x265 code, we might want
240   // to make those optional, or require to call those explicitly.
241   //
242   // Current assumption is that this is similar to SetThreadGroupAffinity().
243   numa_set_interleave_mask(node_mask);
244   numa_set_localalloc();
245 #ifdef WITH_DYNLOAD
246   if (numa_free_nodemask != NULL) {
247     numa_free_nodemask(node_mask);
248   } else {
249     numa_bitmask_free(node_mask);
250   }
251 #else
252   numa_free_nodemask(node_mask);
253 #endif
254   return true;
255 }
256
257 ////////////////////////////////////////////////////////////////////////////////
258 // Memory management.
259
260 void* numaAPI_AllocateOnNode(size_t size, int node) {
261   return numa_alloc_onnode(size, node);
262 }
263
264 void* numaAPI_AllocateLocal(size_t size) {
265   return numa_alloc_local(size);
266 }
267
268 void numaAPI_Free(void* start, size_t size) {
269   numa_free(start, size);
270 }
271
272 #endif  // OS_LINUX