Skip to content

Commit c5a5577

Browse files
committed
Add support for MAP_SHARED to OS memory provider for Linux only
Signed-off-by: Lukasz Dorau <lukasz.dorau@intel.com>
1 parent 7df0dff commit c5a5577

File tree

7 files changed

+203
-13
lines changed

7 files changed

+203
-13
lines changed

README.md

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

139139
A memory provider that provides memory from an operating system.
140+
It supports two types of memory mappings
141+
1) private memory mapping (`UMF_MEM_MAP_PRIVATE`)
142+
2) shared memory mapping (`UMF_MEM_MAP_SHARED` - supported on Linux only yet)
140143

141144
##### Requirements
142145

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+
/* .flag */ UMF_MEM_MAP_PRIVATE,
110111

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

include/umf/providers/provider_os_memory.h

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

32+
/// @brief Memory flags
33+
typedef enum umf_memory_flags_t {
34+
UMF_MEM_NONE = 0, ///< No flag
35+
UMF_MEM_MAP_PRIVATE = (1 << 0), ///< private memory mapping
36+
UMF_MEM_MAP_SHARED =
37+
(1 << 1), ///< shared memory mapping (supported on Linux only)
38+
} umf_memory_flags_t;
39+
3240
/// @brief Memory binding mode
3341
/// Specifies how memory is bound to NUMA nodes on systems that support NUMA.
3442
/// Not every mode is supported on every system.
@@ -61,6 +69,8 @@ typedef enum umf_numa_mode_t {
6169
typedef struct umf_os_memory_provider_params_t {
6270
/// Combination of 'umf_mem_protection_flags_t' flags
6371
unsigned protection;
72+
/// 'umf_memory_flags_t' flag
73+
unsigned flag;
6474

6575
// NUMA config
6676
/// ordered list of numa nodes
@@ -91,6 +101,7 @@ static inline umf_os_memory_provider_params_t
91101
umfOsMemoryProviderParamsDefault(void) {
92102
umf_os_memory_provider_params_t params = {
93103
UMF_PROTECTION_READ | UMF_PROTECTION_WRITE, /* protection */
104+
UMF_MEM_MAP_PRIVATE, /* flag */
94105
NULL, /* numa_list */
95106
0, /* numa_list_len */
96107
UMF_NUMA_MODE_DEFAULT, /* numa_mode */

src/provider/provider_os_memory.c

Lines changed: 74 additions & 6 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

@@ -24,8 +25,15 @@
2425

2526
#define NODESET_STR_BUF_LEN 1024
2627

28+
// maximum size of file used for memory mapping
29+
#define MAX_SIZE_FD (1ULL << 40)
30+
2731
typedef struct os_memory_provider_t {
2832
unsigned protection; // combination of OS-specific protection flags
33+
unsigned flag; // memory mapping flag
34+
int fd; // file descriptor for memory mapping
35+
size_t size_fd; // size of file used for memory mapping
36+
critnib *ptr_off; // a critnib storing (ptr, fd_offset) pairs
2937

3038
// NUMA config
3139
hwloc_bitmap_t nodeset;
@@ -191,6 +199,25 @@ static umf_result_t translate_params(umf_os_memory_provider_params_t *in_params,
191199
return result;
192200
}
193201

202+
result = os_translate_memory_flag(in_params->flag, &provider->flag);
203+
if (result != UMF_RESULT_SUCCESS) {
204+
LOG_ERR("incorrect memory flag: %u", in_params->flag);
205+
return result;
206+
}
207+
208+
provider->fd = os_create_memory_fd(provider->flag);
209+
if (provider->fd == -1) {
210+
LOG_PERR("creating a file descriptor for memory mapping failed");
211+
return UMF_RESULT_ERROR_UNKNOWN;
212+
}
213+
214+
provider->size_fd = 0; // will be increased during each allocation
215+
216+
if (provider->fd > 0 && os_set_file_size(provider->fd, MAX_SIZE_FD)) {
217+
LOG_PDEBUG("growing file size failed");
218+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
219+
}
220+
194221
// NUMA config
195222
int emptyNodeset = in_params->numa_list_len == 0;
196223
result = translate_numa_mode(in_params->numa_mode, emptyNodeset,
@@ -258,10 +285,19 @@ static umf_result_t os_initialize(void *params, void **provider) {
258285
}
259286
}
260287

288+
os_provider->ptr_off = critnib_new();
289+
if (!os_provider->ptr_off) {
290+
LOG_ERR("creating IPC cache failed");
291+
ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
292+
goto err_free_nodeset_str_buf;
293+
}
294+
261295
*provider = os_provider;
262296

263297
return UMF_RESULT_SUCCESS;
264298

299+
err_free_nodeset_str_buf:
300+
umf_ba_global_free(os_provider->nodeset_str_buf);
265301
err_destroy_hwloc_topology:
266302
hwloc_topology_destroy(os_provider->topo);
267303
err_free_os_provider:
@@ -276,6 +312,9 @@ static void os_finalize(void *provider) {
276312
}
277313

278314
os_memory_provider_t *os_provider = provider;
315+
316+
critnib_delete(os_provider->ptr_off);
317+
279318
if (os_provider->nodeset_str_buf) {
280319
umf_ba_global_free(os_provider->nodeset_str_buf);
281320
}
@@ -331,7 +370,8 @@ static inline void assert_is_page_aligned(uintptr_t ptr, size_t page_size) {
331370
}
332371

333372
static int os_mmap_aligned(void *hint_addr, size_t length, size_t alignment,
334-
size_t page_size, int prot, void **out_addr) {
373+
size_t page_size, int prot, int flag, int fd,
374+
void **out_addr, size_t *fd_size) {
335375
assert(out_addr);
336376

337377
size_t extended_length = length;
@@ -344,8 +384,20 @@ static int os_mmap_aligned(void *hint_addr, size_t length, size_t alignment,
344384
extended_length += alignment;
345385
}
346386

347-
void *ptr = os_mmap(hint_addr, extended_length, prot);
387+
size_t fd_offset = 0;
388+
389+
if (fd > 0) {
390+
fd_offset = *fd_size;
391+
*fd_size += extended_length;
392+
if (*fd_size > MAX_SIZE_FD) {
393+
LOG_PDEBUG("growing file size failed");
394+
return -1;
395+
}
396+
}
397+
398+
void *ptr = os_mmap(hint_addr, extended_length, prot, flag, fd, fd_offset);
348399
if (ptr == NULL) {
400+
LOG_PDEBUG("memory mapping failed");
349401
return -1;
350402
}
351403

@@ -411,15 +463,16 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
411463
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
412464
}
413465

414-
int protection = os_provider->protection;
466+
size_t fd_offset = os_provider->size_fd; // needed for critnib_insert()
415467

416468
void *addr = NULL;
417469
errno = 0;
418-
ret = os_mmap_aligned(NULL, size, alignment, page_size, protection, &addr);
470+
ret = os_mmap_aligned(NULL, size, alignment, page_size,
471+
os_provider->protection, os_provider->flag,
472+
os_provider->fd, &addr, &os_provider->size_fd);
419473
if (ret) {
420474
os_store_last_native_error(UMF_OS_RESULT_ERROR_ALLOC_FAILED, errno);
421475
LOG_PERR("memory allocation failed");
422-
423476
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
424477
}
425478

@@ -429,7 +482,6 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
429482
LOG_ERR("allocated address 0x%llx is not aligned to %zu (0x%zx) "
430483
"bytes",
431484
(unsigned long long)addr, alignment, alignment);
432-
433485
goto err_unmap;
434486
}
435487

@@ -460,6 +512,16 @@ static umf_result_t os_alloc(void *provider, size_t size, size_t alignment,
460512
}
461513
}
462514

515+
if (os_provider->fd > 0) {
516+
ret = critnib_insert(os_provider->ptr_off, (uintptr_t)addr,
517+
(void *)(uintptr_t)fd_offset, 0 /* update */);
518+
if (ret) {
519+
LOG_DEBUG("inserting a value to the IPC cache failed (addr=%p, "
520+
"offset=%zu)",
521+
addr, fd_offset);
522+
}
523+
}
524+
463525
*resultPtr = addr;
464526

465527
return UMF_RESULT_SUCCESS;
@@ -474,6 +536,12 @@ static umf_result_t os_free(void *provider, void *ptr, size_t size) {
474536
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
475537
}
476538

539+
os_memory_provider_t *os_provider = (os_memory_provider_t *)provider;
540+
541+
if (os_provider->fd > 0) {
542+
critnib_remove(os_provider->ptr_off, (uintptr_t)ptr);
543+
}
544+
477545
errno = 0;
478546
int ret = os_munmap(ptr, size);
479547
// ignore error when size == 0

src/provider/provider_os_memory_internal.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@ 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_memory_flag(unsigned in_flag, unsigned *out_flag);
32+
33+
int os_create_memory_fd(unsigned translated_memory_flag);
34+
35+
int os_set_file_size(int fd, size_t size);
36+
37+
void *os_mmap(void *hint_addr, size_t length, int prot, int flag, int fd,
38+
size_t fd_offset);
3239

3340
int os_munmap(void *addr, size_t length);
3441

src/provider/provider_os_memory_linux.c

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
*/
77

88
#include <assert.h>
9+
#include <errno.h>
910
#include <string.h>
1011
#include <sys/mman.h>
12+
#include <sys/syscall.h>
1113
#include <unistd.h>
1214

13-
#include "provider_os_memory_internal.h"
1415
#include <umf/providers/provider_os_memory.h>
1516

17+
#include "provider_os_memory_internal.h"
18+
#include "utils_log.h"
19+
1620
umf_result_t os_translate_mem_protection_one_flag(unsigned in_protection,
1721
unsigned *out_protection) {
1822
switch (in_protection) {
@@ -32,6 +36,70 @@ umf_result_t os_translate_mem_protection_one_flag(unsigned in_protection,
3236
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
3337
}
3438

39+
umf_result_t os_translate_memory_flag(unsigned in_flag, unsigned *out_flag) {
40+
switch (in_flag) {
41+
case UMF_MEM_MAP_PRIVATE:
42+
*out_flag = MAP_PRIVATE;
43+
return UMF_RESULT_SUCCESS;
44+
case UMF_MEM_MAP_SHARED:
45+
#ifdef __APPLE__
46+
return UMF_RESULT_ERROR_NOT_SUPPORTED; // not supported on MacOSX
47+
#else
48+
*out_flag = MAP_SHARED;
49+
return UMF_RESULT_SUCCESS;
50+
#endif
51+
}
52+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
53+
}
54+
55+
int os_create_memory_fd(unsigned translated_memory_flag) {
56+
#ifdef __APPLE__
57+
(void)translated_memory_flag; // unused
58+
return 0; // ignored on MacOSX
59+
#else
60+
// fd is created only for MAP_SHARED
61+
if (translated_memory_flag != MAP_SHARED) {
62+
return 0;
63+
}
64+
65+
int fd = -1;
66+
67+
#ifdef __NR_memfd_secret
68+
// SYS_memfd_secret is supported since Linux 5.14
69+
fd = syscall(SYS_memfd_secret, 0);
70+
if (fd == -1) {
71+
LOG_PDEBUG("memfd_secret()");
72+
}
73+
if (fd > 0) {
74+
return fd;
75+
}
76+
// syscall(SYS_memfd_secret) can fail with errno == ENOTSYS (function not implemented).
77+
// We should try to call syscall(SYS_memfd_create) in this case.
78+
#endif /* __NR_memfd_secret */
79+
80+
#ifdef __NR_memfd_create
81+
// SYS_memfd_create is supported since Linux 3.17, glibc 2.27
82+
fd = syscall(SYS_memfd_create, "fd_name", 0);
83+
if (fd == -1) {
84+
LOG_PDEBUG("memfd_create()");
85+
}
86+
#endif /* __NR_memfd_create */
87+
88+
return fd;
89+
90+
#endif /* __APPLE__ */
91+
}
92+
93+
int os_set_file_size(int fd, size_t size) {
94+
#ifdef __APPLE__
95+
(void)fd; // unused
96+
(void)size; // unused
97+
return 0; // ignored on MacOSX
98+
#else
99+
return ftruncate(fd, size);
100+
#endif /* __APPLE__ */
101+
}
102+
35103
umf_result_t os_translate_mem_protection_flags(unsigned in_protection,
36104
unsigned *out_protection) {
37105
// translate protection - combination of 'umf_mem_protection_flags_t' flags
@@ -51,13 +119,19 @@ static int os_translate_purge_advise(umf_purge_advise_t advise) {
51119
return -1;
52120
}
53121

54-
void *os_mmap(void *hint_addr, size_t length, int prot) {
55-
// MAP_ANONYMOUS - the mapping is not backed by any file
56-
void *ptr =
57-
mmap(hint_addr, length, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
122+
void *os_mmap(void *hint_addr, size_t length, int prot, int flag, int fd,
123+
size_t fd_offset) {
124+
fd = (fd == 0) ? -1 : fd;
125+
if (fd == -1) {
126+
// MAP_ANONYMOUS - the mapping is not backed by any file
127+
flag |= MAP_ANONYMOUS;
128+
}
129+
130+
void *ptr = mmap(hint_addr, length, prot, flag, fd, fd_offset);
58131
if (ptr == MAP_FAILED) {
59132
return NULL;
60133
}
134+
61135
return ptr;
62136
}
63137

src/provider/provider_os_memory_windows.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,33 @@ umf_result_t os_translate_mem_protection_flags(unsigned in_protection,
5454
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
5555
}
5656

57-
void *os_mmap(void *hint_addr, size_t length, int prot) {
57+
umf_result_t os_translate_memory_flag(unsigned in_flag, unsigned *out_flag) {
58+
switch (in_flag) {
59+
case UMF_MEM_MAP_PRIVATE:
60+
*out_flag = 0; // ignored on Windows
61+
return UMF_RESULT_SUCCESS;
62+
case UMF_MEM_MAP_SHARED:
63+
return UMF_RESULT_ERROR_NOT_SUPPORTED; // not supported on Windows yet
64+
}
65+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
66+
}
67+
68+
int os_create_memory_fd(unsigned translated_memory_flag) {
69+
(void)translated_memory_flag; // unused
70+
return 0; // ignored on Windows
71+
}
72+
73+
int os_set_file_size(int fd, size_t size) {
74+
(void)fd; // unused
75+
(void)size; // unused
76+
return 0; // ignored on Windows
77+
}
78+
79+
void *os_mmap(void *hint_addr, size_t length, int prot, int flag, int fd,
80+
size_t fd_offset) {
81+
(void)flag; // ignored on Windows
82+
(void)fd; // ignored on Windows
83+
(void)fd_offset; // ignored on Windows
5884
return VirtualAlloc(hint_addr, length, MEM_RESERVE | MEM_COMMIT, prot);
5985
}
6086

0 commit comments

Comments
 (0)