Made lib/numa compile on x86_32
[barrelfish] / lib / numa / alloc.c
1 /**
2  * \file
3  * \brief General Numa functions
4  *
5  */
6
7 /*
8  * Copyright (c) 2014, ETH Zurich.
9  * All rights reserved.
10  *
11  * This file is distributed under the terms in the attached LICENSE file.
12  * If you do not find this file, copies can be found by writing to:
13  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
14  */
15
16 #include <stdio.h>
17 #include <string.h>
18
19 #include <barrelfish/barrelfish.h>
20
21 #include <numa.h>
22 #include <bitmap.h>
23 #include "numa_internal.h"
24
25 ///< numa interleave mask for allocations
26 struct bitmap *numa_alloc_interleave_mask;
27
28 ///< numa bind mask for allocations
29 struct bitmap *numa_alloc_bind_mask;
30
31 static void validate_page_size(size_t *pagesize, vregion_flags_t *flags)
32 {
33 // XXX - Something is seriously wrong here.
34 #if 0 // !defined(__x86_64__)
35     if (*pagesize < LARGE_PAGE_SIZE) {
36         *pagesize = BASE_PAGE_SIZE;
37         *flags = VREGION_FLAGS_READ_WRITE;
38     } else if (*pagesize < HUGE_PAGE_SIZE) {
39         *pagesize = LARGE_PAGE_SIZE;
40         *flags = VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE;
41     } else {
42         *pagesize = HUGE_PAGE_SIZE;
43         *flags = VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_HUGE;
44     }
45 #else
46     *pagesize = BASE_PAGE_SIZE;
47     *flags = VREGION_FLAGS_READ_WRITE;
48 #endif
49
50
51 }
52
53
54 /** \brief   returns the current interleave mask
55  *
56  * \returns bitmask representing the current interleave state
57  *
58  * returns the current interleave mask if the task's memory allocation policy is
59  * page interleaved. Otherwise, this function returns an empty mask.
60  */
61 struct bitmap *numa_get_interleave_mask(void)
62 {
63     assert(numa_alloc_interleave_mask);
64     struct bitmap *im = numa_allocate_nodemask();
65     if (im == NULL) {
66         return NULL;
67     }
68     bitmap_copy(im, numa_alloc_interleave_mask);
69     return im;
70 }
71
72
73 /**
74  * \brief sets the memory interleave mask for the current task to nodemask
75  *
76  * \param nodemask bitmask representing the nodes
77  *
78  * All new memory allocations are page interleaved over all nodes in the interleave
79  * mask. Interleaving can be turned off again by passing an empty mask.
80  *
81  * This bitmask is considered to be a hint. Fallback to other nodes may be possible
82  */
83 void numa_set_interleave_mask(struct bitmap *nodemask)
84 {
85     assert(numa_alloc_interleave_mask);
86
87     if (!nodemask) {
88         bitmap_clear_all(numa_alloc_interleave_mask);
89         return;
90     }
91
92     if (bitmap_get_nbits(nodemask) < NUMA_MAX_NUMNODES) {
93         NUMA_WARNING("supplied interleave mask (%p) has to less bits!", nodemask);
94         return;
95     }
96     bitmap_copy(numa_alloc_interleave_mask, nodemask);
97
98     /* clear out the invalid nodes */
99     bitmap_clear_range(numa_alloc_interleave_mask, numa_num_configured_nodes(),
100                        bitmap_get_nbits(numa_alloc_interleave_mask));
101
102     /* clear the bind mask as we are using interleaving mode now */
103     bitmap_clear_all(numa_alloc_bind_mask);
104 }
105
106
107 /**
108  * \brief binds the current task and its children to the nodes specified in nodemask.
109  *
110  * \param nodemask  bitmap representing the nodes
111  */
112 void numa_bind(struct bitmap *nodemask)
113 {
114     USER_PANIC("Not yet implemented");
115 }
116
117
118 /**
119  * \brief sets the memory allocation policy for the calling task to local allocation.
120  */
121 void numa_set_localalloc(void)
122 {
123     assert(numa_alloc_bind_mask);
124     assert(numa_alloc_interleave_mask);
125
126     /* clear interleave mode */
127     bitmap_clear_all(numa_alloc_interleave_mask);
128
129     bitmap_clear_all(numa_alloc_bind_mask);
130     bitmap_set_bit(numa_alloc_bind_mask, numa_current_node());
131 }
132
133 /**
134  * \brief sets the memory allocation mask.
135  *
136  * \param nodemask  bitmap representing the nodes
137  *
138  * The task will only allocate memory from the nodes set in nodemask.
139  *
140  * an empty mask or not allowed nodes in the mask will result in an error
141  */
142 errval_t numa_set_membind(struct bitmap *nodemask)
143 {
144     assert(numa_alloc_bind_mask);
145     assert(numa_alloc_interleave_mask);
146
147     if (!nodemask) {
148         return NUMA_ERR_BITMAP_PARSE;
149     }
150
151     if (bitmap_get_nbits(nodemask) < NUMA_MAX_NUMNODES) {
152         NUMA_WARNING("supplied interleave mask (%p) has to less bits!", nodemask);
153         return NUMA_ERR_BITMAP_RANGE;
154     }
155
156     /* copy new membind mask and clear out invalid bits */
157     bitmap_copy(numa_alloc_bind_mask, nodemask);
158     bitmap_clear_range(numa_alloc_bind_mask, numa_num_configured_nodes(),
159                        bitmap_get_nbits(numa_alloc_bind_mask));
160
161     if (bitmap_get_weight(numa_alloc_bind_mask) == 0) {
162         /* cannot bind to no node, restore with all nodes pointer*/
163         bitmap_copy(numa_alloc_bind_mask, numa_all_nodes_ptr);
164         return NUMA_ERR_NUMA_MEMBIND;
165     }
166
167     /* disable interleaving mode */
168     bitmap_clear_all(numa_alloc_interleave_mask);
169
170     return SYS_ERR_OK;
171 }
172
173
174 /**
175  * \brief returns the mask of nodes from which memory can currently be allocated.
176  *
177  * \return bitmap of nodes from which can be allocated
178  */
179 struct bitmap *numa_get_membind(void)
180 {
181     assert(numa_alloc_bind_mask);
182     struct bitmap *im = numa_allocate_nodemask();
183     if (im == NULL) {
184         return NULL;
185     }
186     bitmap_copy(im, numa_alloc_bind_mask);
187     return im;
188 }
189
190
191 /**
192  * \brief allocates memory on a specific node.
193  *
194  * \param size      size of the region in bytes
195  * \param node      ID of the node to allocate from
196  * \param pagesize  page size to be used for the mapping
197  *
198  * \returns pointer to memory region
199  *
200  * The size argument will be rounded up to a multiple of the system page size.
201  * if the specified node is externally denied to this process, this call will fail.
202  * The memory must be freed with numa_free(). On errors NULL is returned.
203  */
204 void *numa_alloc_onnode(size_t size, nodeid_t node, size_t pagesize)
205 {
206     errval_t err;
207
208     /*
209      * TODO: keep track of the allocated numa frames
210      */
211
212     NUMA_DEBUG_ALLOC("allocate on node %" PRIuNODEID "\n", node);
213
214     /* validate page size and round up size */
215     vregion_flags_t flags;
216     validate_page_size(&pagesize, &flags);
217     size = (size + pagesize - 1) & ~(pagesize - 1);
218
219     /* allocate frame */
220     struct capref frame;
221     size_t ret_size;
222     err = numa_frame_alloc_on_node(&frame, size, node, &ret_size);
223     if (err_is_fail(err)) {
224         return NULL;
225     }
226
227     NUMA_DEBUG_ALLOC("mapping allocated frame\n");
228
229     void *addr;
230     err = vspace_map_one_frame_attr_aligned(&addr, size, frame, flags,
231                                             pagesize, NULL, NULL);
232     if (err_is_fail(err)) {
233         USER_PANIC_ERR(err, "vspace_map_one_frame_attr_aligned");
234         err = numa_frame_free(frame);
235         if (err_is_fail(err)) {
236             USER_PANIC_ERR(err, "nested error while freeing frame");
237         }
238         return NULL;
239     }
240
241     NUMA_DEBUG_ALLOC("frame mapped @ %p\n", addr);
242
243     return addr;
244 }
245
246
247 /**
248  * \brief allocates size bytes of memory on the local node
249  *
250  * \param size  size of the memory region in bytes
251  * \param pagesize  page size to be used for the mapping
252  *
253  * \returns pointer to memory region
254  *
255  * The memory must be freed with numa_free(). On errors NULL is returned.
256  */
257 void *numa_alloc_local(size_t size, size_t pagesize)
258 {
259     nodeid_t node = numa_current_node();
260
261     NUMA_DEBUG_ALLOC("allocate on local node %" PRIuNODEID "\n", node);
262
263     return numa_alloc_onnode(size, node, pagesize);
264 }
265
266
267 /**
268  * \brief allocates size bytes of memory page interleaved on all nodes.
269  *
270  * \param size      size of the memory region in bytes
271  * \param pagesize  preferred page size to be used
272  *
273  * \returns pointer to the mapped memory region
274  *
275  * should only be used for large areas consisting of multiple pages.
276  * The memory must be freed with numa_free(). On errors NULL is returned.
277  */
278 void *numa_alloc_interleaved(size_t size, size_t pagesize)
279 {
280     return numa_alloc_interleaved_subset(size, pagesize, numa_all_nodes_ptr);
281 }
282
283
284 /**
285  * \brief allocates size bytes of memory page interleaved the nodes specified in
286  *        the nodemask.
287  *
288  * \param size     size of the memory region in bytes
289  * \param nodemask subset of nodes to consider for allocation
290  * \param pagesize  preferred page size to be used
291  *
292  * \returns pointer to the mapped memory region
293  *
294  * should only be used for large areas consisting of multiple pages.
295  * The memory must be freed with numa_free(). On errors NULL is returned.
296  */
297 void *numa_alloc_interleaved_subset(size_t size, size_t pagesize,
298                                     struct bitmap *nodemask)
299 {
300     errval_t err;
301
302     /* clear out invalid bits */
303     bitmap_clear_range(nodemask, numa_num_configured_nodes(),
304                        bitmap_get_nbits(nodemask));
305
306     /* get the number of nodes */
307     nodeid_t nodes = bitmap_get_weight(nodemask);
308     if (nodes == 0) {
309         return NULL;
310     }
311
312     NUMA_DEBUG_ALLOC("allocating interleaved using %" PRIuNODEID " nodes\n", nodes);
313
314     assert(nodes <= numa_num_configured_nodes());
315
316     vregion_flags_t flags;
317     validate_page_size(&pagesize, &flags);
318     size_t stride = pagesize;
319
320     size_t node_size = size / nodes;
321     node_size = (node_size + pagesize - 1) & ~(pagesize - 1);
322
323     /* update total size as this may change due to rounding of node sizes*/
324     size = nodes * node_size;
325
326     /*
327      * XXX: we may want to keep track of numa alloced frames
328      */
329
330     struct memobj_numa *memobj = calloc(1, sizeof(struct memobj_numa));
331     err = memobj_create_numa(memobj, size, 0, numa_num_configured_nodes(), stride);
332     if (err_is_fail(err)) {
333         return NULL;
334     }
335
336     bitmap_bit_t node = bitmap_get_first(nodemask);
337     nodeid_t node_idx=0;
338     while(node != BITMAP_BIT_NONE) {
339         struct capref frame;
340         err = numa_frame_alloc_on_node(&frame, node_size, (nodeid_t)node, NULL);
341         if (err_is_fail(err)) {
342             DEBUG_ERR(err, "numa_frame_alloc_on_node");
343             goto out_err;
344         }
345         memobj->m.f.fill(&memobj->m, node_idx, frame, 0);
346         ++node_idx;
347         node = bitmap_get_next(nodemask, node);
348     }
349
350     struct vregion *vreg = calloc(1, sizeof(struct vregion));
351     if (vreg == NULL) {
352         goto out_err;
353     }
354     err = vregion_map_aligned(vreg, get_current_vspace(), &memobj->m, 0, size,
355                         flags, pagesize);
356     if (err_is_fail(err)) {
357         DEBUG_ERR(err, "vregion_map_aligned");
358         goto out_err;
359     }
360
361     err = memobj->m.f.pagefault(&memobj->m, vreg, 0, 0);
362     if (err_is_fail(err)) {
363         vregion_destroy(vreg);
364         free(vreg);
365         DEBUG_ERR(err, "memobj.m.f.pagefault");
366         goto out_err;
367     }
368
369     // XXX - Is this right?
370     return (void *)(uintptr_t)vregion_get_base_addr(vreg);
371
372     out_err:
373     for (int i = 0; i < node_idx; ++i) {
374         struct capref frame;
375         memobj->m.f.unfill(&memobj->m, node_idx, &frame, NULL);
376         cap_delete(frame);
377     }
378     return NULL;
379
380 }
381
382
383 /**
384  * \brief allocates size bytes of memory with the current NUMA policy.
385  *
386  * \param size      size of the memory region in bytes
387  * \param pagesize  preferred page size to be used
388  * \returns pointer to the mapped memory region
389  *
390  * The memory must be freed with numa_free(). On errors NULL is returned.
391  */
392 void *numa_alloc(size_t size, size_t pagesize)
393 {
394     NUMA_DEBUG_ALLOC("allocate according to policy\n");
395
396     /* check if we use interleaved mode */
397     if (bitmap_get_weight(numa_alloc_interleave_mask)) {
398         return numa_alloc_interleaved_subset(size, pagesize,
399                                              numa_alloc_interleave_mask);
400     }
401
402     /* check membind */
403     if (bitmap_get_weight(numa_alloc_bind_mask) == 1) {
404         nodeid_t node = (nodeid_t) bitmap_get_first(numa_alloc_bind_mask);
405         return numa_alloc_onnode(size, node, pagesize);
406     }
407
408     /* TODO:
409      * - handle the case where multiple nodes are set in membind
410      */
411
412     /* just return some memory */
413     return malloc(size);
414
415 }
416
417
418 /**
419  * \brief changes the size of the memory area.
420  *
421  * \param old_addr  pointer ot the old memory region
422  * \param old_size  size of the old memory region
423  * \param new_size  new size to allocate
424  */
425 void *numa_realloc(void *old_addr, size_t old_size, size_t new_size)
426 {
427     assert(!"NYI");
428     return 0;
429 }
430
431
432 /**
433  * \brief frees size bytes of memory starting at start
434  *
435  * \param start start of the memory region
436  * \param size  number of bytes to free
437  *
438  * the memory must be previously allocated by one of the numa_alloc* functions
439  */
440 void numa_free(void *start, size_t size)
441 {
442     assert(!"NYI");
443 }
444
445
446
447 /**
448  * \brief allocates a frame on a specific node
449  *
450  * \param dest      capref to store the frame
451  * \param size      size of the frame to allocated
452  * \param node      node on which the frame should be allocated
453  * \param ret_size  returned size of the frame capability
454  *
455  * \returns SYS_ERR_OK on SUCCESS
456  *          errval on FAILURE
457  */
458 errval_t numa_frame_alloc_on_node(struct capref *dest,
459                                   size_t size,
460                                   nodeid_t node,
461                                   size_t *ret_size)
462 {
463     errval_t err;
464
465     NUMA_DEBUG_ALLOC("allocating frame on node %" PRIuNODEID "\n", node);
466
467     uint64_t min_base, max_limit;
468     ram_get_affinity(&min_base, &max_limit);
469
470     if (node >= numa_topology.num_nodes) {
471         return NUMA_ERR_NODEID_INVALID;
472     }
473
474     uint64_t node_base = numa_node_base(node);
475     uint64_t node_limit = node_base + numa_node_size(node, NULL);
476
477     NUMA_DEBUG_ALLOC("setting affinity to 0x%" PRIx64 "..0x%" PRIx64 "\n",
478                      node_base, node_limit);
479
480     ram_set_affinity(node_base, node_limit);
481
482     err = frame_alloc(dest, size, ret_size);
483
484     ram_set_affinity(min_base, max_limit);
485
486     NUMA_DEBUG_ALLOC("restore affinity to 0x%" PRIx64 "..0x%" PRIx64 "\n",
487                      min_base, max_limit);
488
489     return err;
490 }
491
492
493 /**
494  * \brief frees a previously allocated frame
495  *
496  * \param frame capability to free
497  */
498 errval_t numa_frame_free(struct capref frame)
499 {
500     assert(!"NYI");
501     return 0;
502 }
503
504
505 /**
506  * \brief  moves a list of pages in the address space of the current domain
507  *
508  * \param did    the domain ID
509  * \param count  number of pages to move
510  * \param pages  list of pages
511  * \param nodes  list of nodes to which the pages can be moved
512  * \param status returns the outcome for each page
513  * \param flags  flags for moving the pages
514  *
515  * \returns SYS_ERR_OK on SUCCESS
516  */
517 errval_t numa_move_pages(domainid_t did,
518                          size_t count,
519                          void **pages,
520                          const nodeid_t *nodes,
521                          errval_t *status,
522                          int flags)
523 {
524     assert(!"NYI");
525     return 0;
526 }
527
528
529 /**
530  * \brief migrate a domain from one set of nodes to another
531  *
532  * \param did        the domain ID
533  * \param fromnodes  bitmap representing the current nodes
534  * \param tonodes    bitmap representing the
535  *
536  * \returns SYS_ERR_OK on SUCCESS
537  */
538 errval_t numa_migrate_pages(domainid_t did,
539                             struct bitmap *fromnodes,
540                             struct bitmap *tonodes)
541 {
542     assert(!"NYI");
543     return 0;
544 }