1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
/*
* Copyright (C) 2023-2025 Colin Ian King.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "stress-ng.h"
#include "core-lock.h"
#include "core-madvise.h"
#include "core-stressors.h"
#include "core-shared-heap.h"
/*
* The max heap size needs to be larger if we can't determine if strings
* being dup'd are literal strings. Literal string dups just use the
* literal string, non-literals (e.g. stack based) need to be dup'd from
* a shared memory heap.
*/
#if defined(HAVE_BUILTIN_CONSTANT_P)
#define STRESS_MAX_SHARED_HEAP_SIZE (64 * KB)
#else
#define STRESS_MAX_SHARED_HEAP_SIZE (256 * KB)
#endif
/* Used just to determine number of stressors via STRESS_MAX */
enum {
STRESSORS(STRESSOR_ENUM)
STRESS_MAX
};
typedef struct stress_shared_heap_str {
struct stress_shared_heap_str *next;
char str[];
} stress_shared_heap_str_t;
/*
* stress_shared_heap_init()
* initialized shared heap
*/
void *stress_shared_heap_init(void)
{
const size_t page_size = stress_get_page_size();
/* Allocate enough heap for all stressor descriptions with 100% metrics allocated */
size_t size = (STRESS_MISC_METRICS_MAX * (32 + sizeof(void *)) * STRESS_MAX);
size = STRESS_MINIMUM(size, STRESS_MAX_SHARED_HEAP_SIZE);
g_shared->shared_heap.out_of_memory = false;
g_shared->shared_heap.heap_size = (size + page_size - 1) & ~(page_size - 1);
g_shared->shared_heap.str_list_head = NULL;
g_shared->shared_heap.heap = stress_mmap_populate(NULL, size,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (UNLIKELY(g_shared->shared_heap.heap == MAP_FAILED)) {
g_shared->shared_heap.lock = NULL;
return NULL;
}
stress_set_vma_anon_name(g_shared->shared_heap.heap, size, "shared-heap");
(void)stress_madvise_mergeable(g_shared->shared_heap.heap, size);
g_shared->shared_heap.lock = stress_lock_create("shared-heap");
if (UNLIKELY(!g_shared->shared_heap.lock)) {
(void)munmap((void *)g_shared->shared_heap.heap, g_shared->shared_heap.heap_size);
g_shared->shared_heap.heap = NULL;
return NULL;
}
return g_shared->shared_heap.lock;
}
/*
* stress_shared_heap_free()
* free shared heap
*/
void stress_shared_heap_free(void)
{
if (g_shared->shared_heap.out_of_memory) {
pr_inf("shared heap: out of memory duplicating some strings, increase STRESS_MAX_SHARED_HEAP_SIZE to fix this\n");
}
#if defined(STRESS_SHARED_HEAD_DEBUG)
if (g_shared->shared_heap.offset > 0) {
pr_dbg("shared heap: used %zd of %zd bytes of heap\n", g_shared->shared_heap.offset, g_shared->shared_heap.heap_size);
}
#endif
if (g_shared->shared_heap.heap) {
(void)munmap((void *)g_shared->shared_heap.heap, g_shared->shared_heap.heap_size);
g_shared->shared_heap.heap = NULL;
}
if (g_shared->shared_heap.lock) {
(void)stress_lock_destroy(g_shared->shared_heap.lock);
g_shared->shared_heap.lock = NULL;
}
g_shared->shared_heap.out_of_memory = false;
}
/*
* stress_shared_heap_malloc()
* Primitive non-free'ing heap allocator. Just return next allocated chunk from
* the shared memory heap. We don't use need a per-object free'ing, so no need
* to keep track of holes or do hole coalescing. Keep it simple for now.
*/
void *stress_shared_heap_malloc(const size_t size)
{
ssize_t heap_free;
void *ptr;
if (UNLIKELY(stress_lock_acquire(g_shared->shared_heap.lock) < 0))
return NULL;
heap_free = g_shared->shared_heap.heap_size - g_shared->shared_heap.offset;
if (heap_free < (ssize_t)size) {
g_shared->shared_heap.out_of_memory = true;
(void)stress_lock_release(g_shared->shared_heap.lock);
return NULL;
}
ptr = (void *)((uintptr_t)g_shared->shared_heap.heap + g_shared->shared_heap.offset);
g_shared->shared_heap.offset += (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
(void)stress_lock_release(g_shared->shared_heap.lock);
return ptr;
}
/*
* stress_shared_heap_dup_const()
* Do a strdup of string using the shared heap. String must never be
* modified as this dup operation re-used existing identical strings
* allocated on the shared heap. This is designed for storing metric
* descriptions that get allocated per stressor and we want to reduce
* duplicated allocations where possible.
*/
char *stress_shared_heap_dup_const(const char *str)
{
size_t len, str_len;
stress_shared_heap_str_t *heap_str;
if (UNLIKELY(stress_lock_acquire(g_shared->shared_heap.lock) < 0))
return NULL;
for (heap_str = (stress_shared_heap_str_t *)g_shared->shared_heap.str_list_head; heap_str; heap_str = heap_str->next) {
if (strcmp(str, heap_str->str) == 0) {
(void)stress_lock_release(g_shared->shared_heap.lock);
return heap_str->str;
}
}
(void)stress_lock_release(g_shared->shared_heap.lock);
str_len = strlen(str) + 1;
len = str_len + sizeof(void *);
heap_str = (stress_shared_heap_str_t *)stress_shared_heap_malloc(len);
if (UNLIKELY(!heap_str))
return NULL;
(void)shim_strscpy(heap_str->str, str, str_len);
heap_str->next = NULL;
/*
* We failed to acquire so we can't add to list, return dup'd string
* and skip adding it to the list, at least the dup worked!
*/
if (UNLIKELY(stress_lock_acquire(g_shared->shared_heap.lock) < 0))
return heap_str->str;
/*
* Save a copy so it can be re-used
*/
heap_str->next = (stress_shared_heap_str_t *)g_shared->shared_heap.str_list_head;
g_shared->shared_heap.str_list_head = (void *)heap_str;
(void)stress_lock_release(g_shared->shared_heap.lock);
return heap_str->str;
}
|