Skip to content

Commit d10b8a8

Browse files
committed
Enable IPC cache for Open/Close functions
1 parent 51dc7a1 commit d10b8a8

File tree

8 files changed

+459
-75
lines changed

8 files changed

+459
-75
lines changed

src/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ set(UMF_SOURCES
104104
${BA_SOURCES}
105105
libumf.c
106106
ipc.c
107+
ipc_cache.c
107108
memory_pool.c
108109
memory_provider.c
109110
memory_provider_get_last_failed.c
@@ -247,6 +248,7 @@ target_include_directories(
247248
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/provider>
248249
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/memspaces>
249250
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/memtargets>
251+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/uthash>
250252
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
251253

252254
install(TARGETS umf EXPORT ${PROJECT_NAME}-targets)

src/ipc.c

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ umf_result_t umfGetIPCHandle(const void *ptr, umf_ipc_handle_t *umfIPCHandle,
9090
return ret;
9191
}
9292

93+
// ipcData->handle_id is filled by tracking provider
94+
ipcData->base = allocInfo.base;
9395
ipcData->pid = utils_getpid();
9496
ipcData->baseSize = allocInfo.baseSize;
9597
ipcData->offset = (uintptr_t)ptr - (uintptr_t)allocInfo.base;

src/ipc_cache.c

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
6+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
*
8+
*/
9+
10+
#include <stdbool.h>
11+
12+
#include "base_alloc_global.h"
13+
#include "ipc_cache.h"
14+
#include "uthash.h"
15+
#include "utils_common.h"
16+
#include "utils_concurrency.h"
17+
#include "utils_log.h"
18+
#include "utlist.h"
19+
20+
struct ipc_handle_cache_entry_t;
21+
22+
typedef struct ipc_handle_cache_entry_t *hash_map_t;
23+
typedef struct ipc_handle_cache_entry_t *lru_list_t;
24+
25+
typedef struct ipc_handle_cache_entry_t {
26+
UT_hash_handle hh;
27+
struct ipc_handle_cache_entry_t *next, *prev;
28+
ipc_mapped_handle_cache_key_t key;
29+
uint64_t ref_count;
30+
uint64_t handle_id;
31+
hash_map_t
32+
*hash_table; // pointer to the hash table to which the entry belongs
33+
ipc_mapped_handle_cache_value_t value;
34+
} ipc_handle_cache_entry_t;
35+
36+
typedef struct ipc_mapped_handle_cache_global_t {
37+
utils_mutex_t cache_lock;
38+
umf_ba_pool_t *cache_allocator;
39+
size_t max_size;
40+
size_t cur_size;
41+
lru_list_t lru_list;
42+
} ipc_mapped_handle_cache_global_t;
43+
44+
typedef struct ipc_mapped_handle_cache_t {
45+
ipc_mapped_handle_cache_global_t *global;
46+
hash_map_t hash_table;
47+
ipc_mapped_handle_cache_eviction_cb_t eviction_cb;
48+
} ipc_mapped_handle_cache_t;
49+
50+
ipc_mapped_handle_cache_global_t *IPC_MAPPED_CACHE_GLOBAL = NULL;
51+
52+
umf_result_t umfIpcCacheGlobalInit(void) {
53+
umf_result_t ret = UMF_RESULT_SUCCESS;
54+
ipc_mapped_handle_cache_global_t *cache_global =
55+
umf_ba_global_alloc(sizeof(*cache_global));
56+
if (!cache_global) {
57+
LOG_ERR("Failed to allocate memory for the IPC cache global data");
58+
ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
59+
goto err_exit;
60+
}
61+
62+
if (NULL == utils_mutex_init(&(cache_global->cache_lock))) {
63+
LOG_ERR("Failed to initialize mutex for the IPC global cache");
64+
ret = UMF_RESULT_ERROR_UNKNOWN;
65+
goto err_cache_global_free;
66+
}
67+
68+
cache_global->cache_allocator =
69+
umf_ba_create(sizeof(ipc_handle_cache_entry_t));
70+
if (!cache_global->cache_allocator) {
71+
LOG_ERR("Failed to create IPC cache allocator");
72+
ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
73+
goto err_mutex_destroy;
74+
}
75+
76+
// TODO: make max_size configurable via environment variable
77+
cache_global->max_size = 0;
78+
cache_global->cur_size = 0;
79+
cache_global->lru_list = NULL;
80+
81+
IPC_MAPPED_CACHE_GLOBAL = cache_global;
82+
goto err_exit;
83+
84+
err_mutex_destroy:
85+
utils_mutex_destroy_not_free(&(cache_global->cache_lock));
86+
err_cache_global_free:
87+
umf_ba_global_free(cache_global);
88+
err_exit:
89+
return ret;
90+
}
91+
92+
static size_t getGlobalLruListSize(lru_list_t lru_list) {
93+
size_t size = 0;
94+
ipc_handle_cache_entry_t *tmp;
95+
DL_COUNT(lru_list, tmp, size);
96+
return size;
97+
}
98+
99+
void umfIpcCacheGlobalTearDown(void) {
100+
ipc_mapped_handle_cache_global_t *cache_global = IPC_MAPPED_CACHE_GLOBAL;
101+
IPC_MAPPED_CACHE_GLOBAL = NULL;
102+
103+
if (!cache_global) {
104+
return;
105+
}
106+
107+
assert(cache_global->cur_size == 0);
108+
assert(getGlobalLruListSize(cache_global->lru_list) == 0);
109+
110+
umf_ba_destroy(cache_global->cache_allocator);
111+
utils_mutex_destroy_not_free(&(cache_global->cache_lock));
112+
umf_ba_global_free(cache_global);
113+
}
114+
115+
ipc_mapped_handle_cache_handle_t umfIpcHandleMappedCacheCreate(
116+
ipc_mapped_handle_cache_eviction_cb_t eviction_cb) {
117+
if (eviction_cb == NULL) {
118+
LOG_ERR("Eviction callback is NULL");
119+
return NULL;
120+
}
121+
122+
ipc_mapped_handle_cache_t *cache = umf_ba_global_alloc(sizeof(*cache));
123+
124+
if (!cache) {
125+
LOG_ERR("Failed to allocate memory for the IPC cache");
126+
return NULL;
127+
}
128+
129+
assert(IPC_MAPPED_CACHE_GLOBAL != NULL);
130+
131+
cache->global = IPC_MAPPED_CACHE_GLOBAL;
132+
cache->hash_table = NULL;
133+
cache->eviction_cb = eviction_cb;
134+
135+
return cache;
136+
}
137+
138+
void umfIpcHandleMappedCacheDestroy(ipc_mapped_handle_cache_handle_t cache) {
139+
ipc_handle_cache_entry_t *entry, *tmp;
140+
HASH_ITER(hh, cache->hash_table, entry, tmp) {
141+
DL_DELETE(cache->global->lru_list, entry);
142+
HASH_DEL(cache->hash_table, entry);
143+
cache->global->cur_size -= 1;
144+
cache->eviction_cb(&entry->key, &entry->value);
145+
utils_mutex_destroy_not_free(&(entry->value.mmap_lock));
146+
umf_ba_free(cache->global->cache_allocator, entry);
147+
}
148+
HASH_CLEAR(hh, cache->hash_table);
149+
150+
umf_ba_global_free(cache);
151+
}
152+
153+
umf_result_t
154+
umfIpcHandleMappedCacheGet(ipc_mapped_handle_cache_handle_t cache,
155+
const ipc_mapped_handle_cache_key_t *key,
156+
uint64_t handle_id,
157+
ipc_mapped_handle_cache_value_t **retEntry) {
158+
ipc_handle_cache_entry_t *entry = NULL;
159+
umf_result_t ret = UMF_RESULT_SUCCESS;
160+
bool evicted = false;
161+
ipc_mapped_handle_cache_value_t evicted_value;
162+
163+
if (!cache || !key || !retEntry) {
164+
LOG_ERR("Some arguments are NULL, cache=%p, key=%p, retEntry=%p",
165+
(void *)cache, (const void *)key, (void *)retEntry);
166+
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
167+
}
168+
169+
assert(cache->global != NULL);
170+
171+
utils_mutex_lock(&(cache->global->cache_lock));
172+
173+
HASH_FIND(hh, cache->hash_table, key, sizeof(*key), entry);
174+
if (entry && entry->handle_id == handle_id) { // cache hit
175+
// update frequency list
176+
// remove the entry from the current position
177+
DL_DELETE(cache->global->lru_list, entry);
178+
// add the entry to the head of the list
179+
DL_PREPEND(cache->global->lru_list, entry);
180+
} else { //cache miss
181+
// Look for eviction candidate
182+
if (entry == NULL && cache->global->max_size != 0 &&
183+
cache->global->cur_size >= cache->global->max_size) {
184+
// If max_size is set and the cache is full, evict the least recently used entry.
185+
entry = cache->global->lru_list->prev;
186+
}
187+
188+
if (entry) { // we have eviction candidate
189+
// remove the entry from the frequency list
190+
DL_DELETE(cache->global->lru_list, entry);
191+
// remove the entry from the hash table it belongs to
192+
HASH_DEL(*(entry->hash_table), entry);
193+
cache->global->cur_size -= 1;
194+
evicted_value.mapped_base_ptr = entry->value.mapped_base_ptr;
195+
evicted_value.mapped_size = entry->value.mapped_size;
196+
evicted = true;
197+
} else { // allocate the new entry
198+
entry = umf_ba_alloc(cache->global->cache_allocator);
199+
if (!entry) {
200+
ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
201+
LOG_ERR("Failed to allocate memory for a new IPC cache entry");
202+
goto exit;
203+
}
204+
if (NULL == utils_mutex_init(&(entry->value.mmap_lock))) {
205+
LOG_ERR("Failed to initialize mutex for the IPC cache entry");
206+
umf_ba_global_free(entry);
207+
ret = UMF_RESULT_ERROR_UNKNOWN;
208+
goto exit;
209+
}
210+
}
211+
212+
entry->key = *key;
213+
entry->ref_count = 0;
214+
entry->handle_id = handle_id;
215+
entry->hash_table = &cache->hash_table;
216+
entry->value.mapped_size = 0;
217+
entry->value.mapped_base_ptr = NULL;
218+
219+
HASH_ADD(hh, cache->hash_table, key, sizeof(entry->key), entry);
220+
DL_PREPEND(cache->global->lru_list, entry);
221+
cache->global->cur_size += 1;
222+
}
223+
224+
exit:
225+
if (ret == UMF_RESULT_SUCCESS) {
226+
utils_atomic_increment(&entry->ref_count);
227+
*retEntry = &entry->value;
228+
}
229+
230+
utils_mutex_unlock(&(cache->global->cache_lock));
231+
232+
if (evicted) {
233+
cache->eviction_cb(key, &evicted_value);
234+
}
235+
236+
return ret;
237+
}

src/ipc_cache.h

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
6+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
*
8+
*/
9+
10+
#ifndef UMF_IPC_CACHE_H
11+
#define UMF_IPC_CACHE_H 1
12+
13+
#include <umf/memory_provider.h>
14+
15+
#include "utils_concurrency.h"
16+
17+
typedef struct ipc_mapped_handle_cache_key_t {
18+
void *remote_base_ptr;
19+
umf_memory_provider_handle_t local_provider;
20+
int remote_pid;
21+
} ipc_mapped_handle_cache_key_t;
22+
23+
typedef struct ipc_mapped_handle_cache_value_t {
24+
void *mapped_base_ptr;
25+
size_t mapped_size;
26+
utils_mutex_t mmap_lock;
27+
} ipc_mapped_handle_cache_value_t;
28+
29+
struct ipc_mapped_handle_cache_t;
30+
31+
typedef struct ipc_mapped_handle_cache_t *ipc_mapped_handle_cache_handle_t;
32+
33+
umf_result_t umfIpcCacheGlobalInit(void);
34+
void umfIpcCacheGlobalTearDown(void);
35+
36+
// define pointer to the eviction callback function
37+
typedef void (*ipc_mapped_handle_cache_eviction_cb_t)(
38+
const ipc_mapped_handle_cache_key_t *key,
39+
const ipc_mapped_handle_cache_value_t *value);
40+
41+
ipc_mapped_handle_cache_handle_t umfIpcHandleMappedCacheCreate(
42+
ipc_mapped_handle_cache_eviction_cb_t eviction_cb);
43+
44+
void umfIpcHandleMappedCacheDestroy(ipc_mapped_handle_cache_handle_t cache);
45+
46+
umf_result_t
47+
umfIpcHandleMappedCacheGet(ipc_mapped_handle_cache_handle_t cache,
48+
const ipc_mapped_handle_cache_key_t *key,
49+
uint64_t handle_id,
50+
ipc_mapped_handle_cache_value_t **retEntry);
51+
52+
#endif /* UMF_IPC_CACHE_H */

src/ipc_internal.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ extern "C" {
2121
// providerIpcData is a Flexible Array Member because its size varies
2222
// depending on the provider.
2323
typedef struct umf_ipc_data_t {
24-
int pid; // process ID of the process that allocated the memory
25-
size_t baseSize; // size of base (coarse-grain) allocation
24+
uint64_t handle_id; // unique ID of this handle
25+
void *base; // base address of the memory
26+
int pid; // process ID of the process that allocated the memory
27+
size_t baseSize; // size of base (coarse-grain) allocation
2628
uint64_t offset;
2729
char providerIpcData[];
2830
} umf_ipc_data_t;

src/libumf.c

+12-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <stddef.h>
1111

1212
#include "base_alloc_global.h"
13+
#include "ipc_cache.h"
1314
#include "memspace_internal.h"
1415
#include "provider_tracking.h"
1516
#include "utils_log.h"
@@ -25,9 +26,18 @@ int umfInit(void) {
2526
if (utils_fetch_and_add64(&umfRefCount, 1) == 0) {
2627
utils_log_init();
2728
TRACKER = umfMemoryTrackerCreate();
29+
if (!TRACKER) {
30+
LOG_ERR("Failed to create memory tracker");
31+
return -1;
32+
}
33+
umf_result_t umf_result = umfIpcCacheGlobalInit();
34+
if (umf_result != UMF_RESULT_SUCCESS) {
35+
LOG_ERR("Failed to initialize IPC cache");
36+
return -1;
37+
}
2838
}
2939

30-
return (TRACKER) ? 0 : -1;
40+
return 0;
3141
}
3242

3343
void umfTearDown(void) {
@@ -39,6 +49,7 @@ void umfTearDown(void) {
3949
umfMemspaceLowestLatencyDestroy();
4050
umfDestroyTopology();
4151
#endif
52+
umfIpcCacheGlobalTearDown();
4253
// make sure TRACKER is not used after being destroyed
4354
umf_memory_tracker_handle_t t = TRACKER;
4455
TRACKER = NULL;

0 commit comments

Comments
 (0)