Skip to content

Commit a4ec89b

Browse files
ldorauszadam
authored andcommitted
Add support for MAP_SHARED to OS memory provider for Linux only
Add support for MAP_SHARED to OS memory provider for Linux only and for the UMF_NUMA_MODE_DEFAULT only. Signed-off-by: Lukasz Dorau <[email protected]>
1 parent f988831 commit a4ec89b

File tree

8 files changed

+329
-20
lines changed

8 files changed

+329
-20
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ More detailed documentation is available here: https://oneapi-src.github.io/unif
138138
#### OS memory provider
139139

140140
A memory provider that provides memory from an operating system.
141+
It supports two types of memory mappings
142+
1) private memory mapping (`UMF_MEM_MAP_PRIVATE`)
143+
2) shared memory mapping (`UMF_MEM_MAP_SHARED` - supported on Linux only yet)
144+
145+
If the shared memory mapping is used then an anonymous file descriptor for memory mapping is created using:
146+
1) `memfd_secret()` syscall - (if it is implemented and) if the `UMF_MEM_FD_FUNC` environment variable does not contain the "memfd_create" string or
147+
2) `memfd_create()` syscall - otherwise (and if it is implemented).
141148

142149
##### Requirements
143150

benchmark/ubench.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ UBENCH_EX(simple, glibc_malloc) {
107107

108108
static umf_os_memory_provider_params_t UMF_OS_MEMORY_PROVIDER_PARAMS = {
109109
/* .protection = */ UMF_PROTECTION_READ | UMF_PROTECTION_WRITE,
110+
/* .visibility */ UMF_MEM_MAP_PRIVATE,
110111

111112
// NUMA config
112113
/* .nodemask = */ NULL,

include/umf/memory_provider_ops.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ typedef struct umf_memory_provider_ext_ops_t {
5050
/// @brief Merges two coarse grain allocations into a single allocation that
5151
/// can be managed (freed) as a whole.
5252
/// allocation_split and allocation_merge should be both set or both NULL.
53+
/// allocation_merge should NOT be called concurrently with allocation_split()
54+
/// with the same pointer.
5355
/// @param hProvider handle to the memory provider
5456
/// @param lowPtr pointer to the first allocation
5557
/// @param highPtr pointer to the second allocation (should be > lowPtr)
@@ -64,6 +66,8 @@ typedef struct umf_memory_provider_ext_ops_t {
6466
/// @brief Splits a coarse grain allocation into 2 adjacent allocations that
6567
/// can be managed (freed) separately.
6668
/// allocation_split and allocation_merge should be both set or both NULL.
69+
/// allocation_split should NOT be called concurrently with allocation_merge()
70+
/// with the same pointer.
6771
/// @param hProvider handle to the memory provider
6872
/// @param ptr pointer to the beginning of the allocation
6973
/// @param totalSize total size of the allocation to be split

include/umf/providers/provider_os_memory.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ typedef enum umf_mem_protection_flags_t {
2929
/// @endcond
3030
} umf_mem_protection_flags_t;
3131

32+
/// @brief Memory visibility mode
33+
typedef enum umf_memory_visibility_t {
34+
UMF_MEM_MAP_PRIVATE = 1, ///< private memory mapping
35+
UMF_MEM_MAP_SHARED, ///< shared memory mapping (supported on Linux only)
36+
} umf_memory_visibility_t;
37+
3238
/// @brief Memory binding mode
3339
/// Specifies how memory is bound to NUMA nodes on systems that support NUMA.
3440
/// Not every mode is supported on every system.
@@ -61,6 +67,8 @@ typedef enum umf_numa_mode_t {
6167
typedef struct umf_os_memory_provider_params_t {
6268
/// Combination of 'umf_mem_protection_flags_t' flags
6369
unsigned protection;
70+
/// memory visibility mode
71+
umf_memory_visibility_t visibility;
6472

6573
// NUMA config
6674
/// ordered list of numa nodes
@@ -91,6 +99,7 @@ static inline umf_os_memory_provider_params_t
9199
umfOsMemoryProviderParamsDefault(void) {
92100
umf_os_memory_provider_params_t params = {
93101
UMF_PROTECTION_READ | UMF_PROTECTION_WRITE, /* protection */
102+
UMF_MEM_MAP_PRIVATE, /* visibility mode */
94103
NULL, /* numa_list */
95104
0, /* numa_list_len */
96105
UMF_NUMA_MODE_DEFAULT, /* numa_mode */

src/provider/provider_os_memory.c

Lines changed: 142 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <string.h>
1616

1717
#include "base_alloc_global.h"
18+
#include "critnib.h"
1819
#include "provider_os_memory_internal.h"
1920
#include "utils_log.h"
2021

@@ -26,6 +27,16 @@
2627

2728
typedef struct os_memory_provider_t {
2829
unsigned protection; // combination of OS-specific protection flags
30+
unsigned visibility; // memory visibility mode
31+
int fd; // file descriptor for memory mapping
32+
size_t size_fd; // size of file used for memory mapping
33+
size_t max_size_fd; // maximum size of file used for memory mapping
34+
// A critnib map storing (ptr, fd_offset + 1) pairs. We add 1 to fd_offset
35+
// in order to be able to store fd_offset equal 0, because
36+
// critnib_get() returns value or NULL, so a value cannot equal 0.
37+
// It is needed mainly in the get_ipc_handle and open_ipc_handle hooks
38+
// to mmap a specific part of a file.
39+
critnib *fd_offset_map;
2940

3041
// NUMA config
3142
hwloc_bitmap_t nodeset;
@@ -191,6 +202,34 @@ static umf_result_t translate_params(umf_os_memory_provider_params_t *in_params,
191202
return result;
192203
}
193204

205+
result = os_translate_mem_visibility_flag(in_params->visibility,
206+
&provider->visibility);
207+
if (result != UMF_RESULT_SUCCESS) {
208+
LOG_ERR("incorrect memory visibility flag: %u", in_params->visibility);
209+
return result;
210+
}
211+
212+
provider->fd = os_create_anonymous_fd(provider->visibility);
213+
if (provider->fd == -1) {
214+
LOG_ERR(
215+
"creating an anonymous file descriptor for memory mapping failed");
216+
return UMF_RESULT_ERROR_UNKNOWN;
217+
}
218+
219+
provider->size_fd = 0; // will be increased during each allocation
220+
provider->max_size_fd = get_max_file_size();
221+
222+
if (provider->fd > 0) {
223+
int ret = os_set_file_size(provider->fd, provider->max_size_fd);
224+
if (ret) {
225+
LOG_ERR("setting file size %zu failed", provider->max_size_fd);
226+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
227+
}
228+
}
229+
230+
LOG_DEBUG("size of the memory mapped file set to %zu",
231+
provider->max_size_fd);
232+
194233
// NUMA config
195234
int emptyNodeset = in_params->numa_list_len == 0;
196235
result = translate_numa_mode(in_params->numa_mode, emptyNodeset,
@@ -218,6 +257,14 @@ static umf_result_t os_initialize(void *params, void **provider) {
218257
umf_os_memory_provider_params_t *in_params =
219258
(umf_os_memory_provider_params_t *)params;
220259

260+
if (in_params->visibility == UMF_MEM_MAP_SHARED &&
261+
in_params->numa_mode != UMF_NUMA_MODE_DEFAULT) {
262+
LOG_ERR("Unsupported NUMA mode for the UMF_MEM_MAP_SHARED memory "
263+
"visibility mode (only the UMF_NUMA_MODE_DEFAULT is supported "
264+
"for now)");
265+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
266+
}
267+
221268
os_memory_provider_t *os_provider =
222269
umf_ba_global_alloc(sizeof(os_memory_provider_t));
223270
if (!os_provider) {
@@ -261,10 +308,19 @@ static umf_result_t os_initialize(void *params, void **provider) {
261308
}
262309
}
263310

311+
os_provider->fd_offset_map = critnib_new();
312+
if (!os_provider->fd_offset_map) {
313+
LOG_ERR("creating file descriptor offset map failed");
314+
ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
315+
goto err_free_nodeset_str_buf;
316+
}
317+
264318
*provider = os_provider;
265319

266320
return UMF_RESULT_SUCCESS;
267321

322+
err_free_nodeset_str_buf:
323+
umf_ba_global_free(os_provider->nodeset_str_buf);
268324
err_destroy_hwloc_topology:
269325
hwloc_topology_destroy(os_provider->topo);
270326
err_free_os_provider:
@@ -279,6 +335,9 @@ static void os_finalize(void *provider) {
279335
}
280336

281337
os_memory_provider_t *os_provider = provider;
338+
339+
critnib_delete(os_provider->fd_offset_map);
340+
282341
if (os_provider->nodeset_str_buf) {
283342
umf_ba_global_free(os_provider->nodeset_str_buf);
284343
}
@@ -334,7 +393,9 @@ static inline void assert_is_page_aligned(uintptr_t ptr, size_t page_size) {
334393
}
335394

336395
static int os_mmap_aligned(void *hint_addr, size_t length, size_t alignment,
337-
size_t page_size, int prot, void **out_addr) {
396+
size_t page_size, int prot, int flag, int fd,
397+
size_t max_fd_size, void **out_addr,
398+
size_t *fd_size) {
338399
assert(out_addr);
339400

340401
size_t extended_length = length;
@@ -347,8 +408,21 @@ static int os_mmap_aligned(void *hint_addr, size_t length, size_t alignment,
347408
extended_length += alignment;
348409
}
349410

350-
void *ptr = os_mmap(hint_addr, extended_length, prot);
411+
size_t fd_offset = 0;
412+
413+
if (fd > 0) {
414+
if (*fd_size + extended_length > max_fd_size) {
415+
LOG_ERR("cannot grow a file size beyond %zu", max_fd_size);
416+
return -1;
417+
}
418+
419+
fd_offset = *fd_size;
420+
*fd_size += extended_length;
421+
}
422+
423+
void *ptr = os_mmap(hint_addr, extended_length, prot, flag, fd, fd_offset);
351424
if (ptr == NULL) {
425+
LOG_PDEBUG("memory mapping failed");
352426
return -1;
353427
}
354428

@@ -414,15 +488,17 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
414488
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
415489
}
416490

417-
int protection = os_provider->protection;
491+
size_t fd_offset = os_provider->size_fd; // needed for critnib_insert()
418492

419493
void *addr = NULL;
420494
errno = 0;
421-
ret = os_mmap_aligned(NULL, size, alignment, page_size, protection, &addr);
495+
ret = os_mmap_aligned(NULL, size, alignment, page_size,
496+
os_provider->protection, os_provider->visibility,
497+
os_provider->fd, os_provider->max_size_fd, &addr,
498+
&os_provider->size_fd);
422499
if (ret) {
423500
os_store_last_native_error(UMF_OS_RESULT_ERROR_ALLOC_FAILED, errno);
424501
LOG_PERR("memory allocation failed");
425-
426502
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
427503
}
428504

@@ -432,7 +508,6 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
432508
LOG_ERR("allocated address 0x%llx is not aligned to %zu (0x%zx) "
433509
"bytes",
434510
(unsigned long long)addr, alignment, alignment);
435-
436511
goto err_unmap;
437512
}
438513

@@ -463,6 +538,18 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
463538
}
464539
}
465540

541+
if (os_provider->fd > 0) {
542+
// store (fd_offset + 1) to be able to store fd_offset == 0
543+
ret =
544+
critnib_insert(os_provider->fd_offset_map, (uintptr_t)addr,
545+
(void *)(uintptr_t)(fd_offset + 1), 0 /* update */);
546+
if (ret) {
547+
LOG_ERR("os_alloc(): inserting a value to the file descriptor "
548+
"offset map failed (addr=%p, offset=%zu)",
549+
addr, fd_offset);
550+
}
551+
}
552+
466553
*resultPtr = addr;
467554

468555
return UMF_RESULT_SUCCESS;
@@ -477,6 +564,12 @@ static umf_result_t os_free(void *provider, void *ptr, size_t size) {
477564
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
478565
}
479566

567+
os_memory_provider_t *os_provider = (os_memory_provider_t *)provider;
568+
569+
if (os_provider->fd > 0) {
570+
critnib_remove(os_provider->fd_offset_map, (uintptr_t)ptr);
571+
}
572+
480573
errno = 0;
481574
int ret = os_munmap(ptr, size);
482575
// ignore error when size == 0
@@ -581,23 +674,59 @@ static const char *os_get_name(void *provider) {
581674
return "OS";
582675
}
583676

677+
// This function is supposed to be thread-safe, so it should NOT be called concurrently
678+
// with os_allocation_merge() with the same pointer.
584679
static umf_result_t os_allocation_split(void *provider, void *ptr,
585680
size_t totalSize, size_t firstSize) {
586-
(void)provider;
587-
(void)ptr;
588681
(void)totalSize;
589-
(void)firstSize;
590-
// nop
682+
683+
os_memory_provider_t *os_provider = (os_memory_provider_t *)provider;
684+
if (os_provider->fd <= 0) {
685+
return UMF_RESULT_SUCCESS;
686+
}
687+
688+
void *value = critnib_get(os_provider->fd_offset_map, (uintptr_t)ptr);
689+
if (value == NULL) {
690+
LOG_ERR("os_allocation_split(): getting a value from the file "
691+
"descriptor offset map failed (addr=%p)",
692+
ptr);
693+
return UMF_RESULT_ERROR_UNKNOWN;
694+
} else {
695+
uintptr_t new_key = (uintptr_t)ptr + firstSize;
696+
void *new_value = (void *)((uintptr_t)value + firstSize);
697+
int ret = critnib_insert(os_provider->fd_offset_map, new_key, new_value,
698+
0 /* update */);
699+
if (ret) {
700+
LOG_ERR("os_allocation_split(): inserting a value to the file "
701+
"descriptor offset map failed (addr=%p, offset=%zu)",
702+
(void *)new_key, (size_t)new_value - 1);
703+
return UMF_RESULT_ERROR_UNKNOWN;
704+
}
705+
}
706+
591707
return UMF_RESULT_SUCCESS;
592708
}
593709

710+
// It should NOT be called concurrently with os_allocation_split() with the same pointer.
594711
static umf_result_t os_allocation_merge(void *provider, void *lowPtr,
595712
void *highPtr, size_t totalSize) {
596-
(void)provider;
597713
(void)lowPtr;
598-
(void)highPtr;
599714
(void)totalSize;
600-
// nop
715+
716+
os_memory_provider_t *os_provider = (os_memory_provider_t *)provider;
717+
if (os_provider->fd <= 0) {
718+
return UMF_RESULT_SUCCESS;
719+
}
720+
721+
void *value =
722+
critnib_remove(os_provider->fd_offset_map, (uintptr_t)highPtr);
723+
if (value == NULL) {
724+
LOG_ERR("os_allocation_merge(): removing a value from the file "
725+
"descriptor offset map failed (addr=%p)",
726+
highPtr);
727+
return UMF_RESULT_ERROR_UNKNOWN;
728+
}
729+
601730
return UMF_RESULT_SUCCESS;
602731
}
603732

src/provider/provider_os_memory_internal.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,17 @@ umf_result_t os_translate_flags(unsigned in_flags, unsigned max,
2828
umf_result_t os_translate_mem_protection_flags(unsigned in_protection,
2929
unsigned *out_protection);
3030

31-
void *os_mmap(void *hint_addr, size_t length, int prot);
31+
umf_result_t os_translate_mem_visibility_flag(umf_memory_visibility_t in_flag,
32+
unsigned *out_flag);
33+
34+
int os_create_anonymous_fd(unsigned translated_memory_flag);
35+
36+
size_t get_max_file_size(void);
37+
38+
int os_set_file_size(int fd, size_t size);
39+
40+
void *os_mmap(void *hint_addr, size_t length, int prot, int flag, int fd,
41+
size_t fd_offset);
3242

3343
int os_munmap(void *addr, size_t length);
3444

0 commit comments

Comments
 (0)