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