3
A brief file description
5
@section license License
7
Licensed to the Apache Software Foundation (ASF) under one
8
or more contributor license agreements. See the NOTICE file
9
distributed with this work for additional information
10
regarding copyright ownership. The ASF licenses this file
11
to you under the Apache License, Version 2.0 (the
12
"License"); you may not use this file except in compliance
13
with the License. You may obtain a copy of the License at
15
http://www.apache.org/licenses/LICENSE-2.0
17
Unless required by applicable law or agreed to in writing, software
18
distributed under the License is distributed on an "AS IS" BASIS,
19
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
See the License for the specific language governing permissions and
21
limitations under the License.
24
/***********************************************************************
26
Reclaimable freelist Implementation
28
***********************************************************************/
30
#include "ink_config.h"
35
#include <sys/types.h>
37
#include "ink_atomic.h"
38
#include "ink_queue.h"
39
#include "ink_memory.h"
40
#include "ink_error.h"
41
#include "ink_assert.h"
42
#include "ink_resource.h"
43
#include "ink_queue_ext.h"
45
#if TS_USE_RECLAIMABLE_FREELIST
47
#define CEIL(x,y) (((x) + (y) - 1L) / (y))
48
#define ROUND(x,l) (((x) + ((l) - 1L)) & ~((l) - 1L))
50
#define MAX_NUM_FREELIST 1024
53
* Configurable Variables
55
float cfg_reclaim_factor = 0.3;
56
int64_t cfg_max_overage = 10;
57
int64_t cfg_enable_reclaim = 0;
59
* Debug filter bit mask:
60
* bit 0: reclaim in ink_freelist_new
61
* bit 1: reclaim in ink_freelist_free
62
* bit 2: fetch memory from thread cache
64
int64_t cfg_debug_filter;
66
static uint32_t page_size;
67
static uint32_t nr_freelist;
68
static uint64_t total_mem_in_byte;
69
static __thread InkThreadCache *ThreadCaches[MAX_NUM_FREELIST];
71
static inline pthread_t thread_id(void)
73
static __thread pthread_t tid;
75
return tid?tid:(tid = pthread_self());
77
#define THREAD_ID thread_id()
78
#define MAX_CHUNK_BYTE_SIZE (page_size << 8)
84
#define show_info(tag, f, pCache) \
85
__show_info(stdout, __FILE__, __LINE__, tag, f, pCache)
87
#define show_info(tag, f, pCache) \
88
__silent(__FILE__, __LINE__, tag, f, pCache)
90
#define error_info(tag, f, pCache) \
91
__show_info(stderr, __FILE__, __LINE__, tag, f, pCache)
94
__silent(const char *file, int line,
95
const char *tag, InkFreeList *f, InkThreadCache *pCache) {}
98
__show_info(FILE *fp, const char *file, int line,
99
const char *tag, InkFreeList *f, InkThreadCache *pCache)
102
fprintf(fp, "[%lx:%02u][%s:%05d][%s] %6.2fM t:%-8uf:%-4u m:%-4u avg:%-6.1f"
103
" M:%-4u csbase:%-4u csize:%-4u tsize:%-6u cbsize:%u\n",
104
(long)THREAD_ID, f->thread_cache_idx, file, line, tag,
105
((double)total_mem_in_byte/1024/1024),
118
memory_alignment_init(InkFreeList *f, uint32_t type_size, uint32_t chunk_size,
121
uint32_t chunk_byte_size, user_alignment;
123
f->chunk_size_base = chunk_size;
124
user_alignment = alignment;
128
* limit the size of each chunk and resize alignment.
129
* 1) when size of chunk > MAX_CHUNK_BYTE_SIZE:
130
* alignment = page_size;
131
* 2) when size of chunk <= MAX_CHUNK_BYTE_SIZE:
132
* alignment = (2^N * page_size),
133
* alignment should not larger than MAX_CHUNK_BYTE_SIZE
135
alignment = page_size;
136
chunk_byte_size = ROUND(type_size + sizeof(InkChunkInfo), page_size);
137
if (chunk_byte_size <= MAX_CHUNK_BYTE_SIZE) {
139
chunk_byte_size = ROUND(type_size * f->chunk_size_base
140
+ sizeof(InkChunkInfo), page_size);
142
if (chunk_byte_size > MAX_CHUNK_BYTE_SIZE) {
143
chunk_size = (MAX_CHUNK_BYTE_SIZE - sizeof(InkChunkInfo)) / type_size;
144
chunk_byte_size = ROUND(type_size * chunk_size + sizeof(InkChunkInfo),
147
chunk_size = (chunk_byte_size - sizeof(InkChunkInfo)) / type_size;
149
if (chunk_size > 1) {
150
/* make alignment to be (2^N * page_size),
151
* but not larger than MAX_CHUNK_BYTE_SIZE */
152
while (alignment < chunk_byte_size)
157
if (user_alignment > alignment) {
158
alignment = page_size;
159
while (alignment < user_alignment)
162
ink_release_assert(alignment <= MAX_CHUNK_BYTE_SIZE);
164
f->alignment = alignment;
165
f->type_size = type_size;
166
f->chunk_size = chunk_size;
167
f->chunk_addr_mask = ~((uintptr_t)(alignment - 1));
168
f->chunk_byte_size = chunk_byte_size;
174
* mmap_align allocates _size_ bytes and returns a pointer to the
175
* allocated memory, which address will be a multiple of _alignment_.
176
* 1)the _size_ must be a multiple of page_size;
177
* 2)the _alignment_ must be a power of page_size;
180
mmap_align(size_t size, size_t alignment) {
182
size_t adjust, extra = 0;
184
ink_assert(size % page_size == 0);
186
/* ask for extra memory if alignment > page_size */
187
if (alignment > page_size) {
188
extra = alignment - page_size;
190
void* result = mmap(NULL, size + extra,
191
PROT_READ|PROT_WRITE,
192
MAP_PRIVATE|MAP_ANONYMOUS,
194
if (result == MAP_FAILED) {
196
ink_fatal(1, "Failed to mmap %zu bytes, %s", size, strerror(errno));
199
/* adjust the return memory so it is aligned */
201
ptr = (uintptr_t)result;
202
if ((ptr & (alignment - 1)) != 0) {
203
adjust = alignment - (ptr & (alignment - 1));
206
/* return the unused memory to the system */
208
munmap((void*)ptr, adjust);
210
if (adjust < extra) {
211
munmap((void*)(ptr + adjust + size), extra - adjust);
215
ink_assert((ptr & (alignment -1)) == 0);
219
static inline InkChunkInfo *
220
get_chunk_info_addr(InkFreeList *f, void *item)
222
uintptr_t chunk_addr;
224
if (f->chunk_size > 1)
225
chunk_addr = (uintptr_t)item & f->chunk_addr_mask;
227
chunk_addr = (uintptr_t)item;
229
return (InkChunkInfo *)(chunk_addr + f->type_size * f->chunk_size);
232
static inline InkChunkInfo *
233
ink_chunk_create(InkFreeList *f, InkThreadCache *pCache)
236
uint32_t type_size, chunk_size;
237
void *chunk_addr, *curr, *next;
238
InkChunkInfo *pChunk;
240
chunk_addr = mmap_align(f->chunk_byte_size, f->alignment);
241
pChunk = (InkChunkInfo *)((char *)chunk_addr + f->type_size * f->chunk_size);
243
type_size = f->type_size;
244
chunk_size = f->chunk_size;
246
pChunk->tid = THREAD_ID;
247
pChunk->head = chunk_addr;
248
pChunk->type_size = type_size;
249
pChunk->chunk_size = chunk_size;
250
pChunk->length = f->chunk_byte_size;
251
pChunk->allocated = 0;
252
pChunk->pThreadCache = pCache;
253
pChunk->link = Link<InkChunkInfo>();
256
pChunk->inner_free_list = curr;
257
for (i = 1; i < chunk_size; i++) {
258
next = (void *)((char *)curr + type_size);
259
*(void **)curr = next;
262
*(void **)curr = NULL;
264
ink_atomic_increment(&f->count, chunk_size);
265
ink_atomic_increment(&total_mem_in_byte, f->chunk_byte_size);
267
pCache->free_chunk_list.push(pChunk);
268
pCache->nr_free_chunks++;
273
ink_chunk_delete(InkFreeList *f, InkThreadCache *pCache, InkChunkInfo *pChunk)
275
void *chunk_addr = pChunk->head;
277
ink_assert(pChunk->allocated == 0);
279
pCache->free_chunk_list.remove(pChunk);
280
pCache->nr_free_chunks--;
282
if (unlikely(munmap(chunk_addr, f->chunk_byte_size))) {
284
ink_fatal(1, "Failed to munmap %u bytes, %s", f->chunk_byte_size,
288
ink_atomic_increment((int *)&f->count, -f->chunk_size);
291
* TODO: I had used ink_atomic_increment() here, but it would
292
* lead to incorrect value in linux OS, I don't know why:
293
* ink_atomic_decrement((int64_t *)&total_mem_in_byte, -f->chunk_byte_size);
295
* So I create a new wrap, ink_atomic_decrement(), in ink_atomic.h,
296
* it works well. But we should create the same wrap for other OS.
298
ink_atomic_decrement(&total_mem_in_byte, f->chunk_byte_size);
302
malloc_whole_chunk(InkFreeList *f, InkThreadCache *pCache, InkChunkInfo *pChunk)
305
uint32_t type_size, chunk_size;
308
ink_assert(pChunk->allocated == 0);
310
type_size = f->type_size;
311
chunk_size = f->chunk_size;
314
for (i = 1; i < chunk_size; i++) {
315
next = (void *)((char *)item + i * type_size);
316
ink_atomic_increment(&pCache->nr_free, 1);
317
ink_atomiclist_push(&pCache->outer_free_list, next);
320
pChunk->allocated += chunk_size;
321
pChunk->inner_free_list = NULL;
322
pCache->nr_total += chunk_size;
324
ink_atomic_increment(&f->allocated, chunk_size);
330
malloc_from_chunk(InkFreeList *f, InkThreadCache *pCache, InkChunkInfo *pChunk)
334
if ((item = pChunk->inner_free_list)) {
335
pChunk->inner_free_list = *(void **)item;
338
ink_atomic_increment(&f->allocated, 1);
345
free_to_chunk(InkFreeList *f, InkThreadCache *pCache, void *item)
347
InkChunkInfo *pChunk;
349
pChunk = get_chunk_info_addr(f, item);
351
ink_atomic_increment((int *)&f->allocated, -1);
354
*(void **)item = pChunk->inner_free_list;
355
pChunk->inner_free_list = item;
357
if (pChunk->allocated == 0)
358
ink_chunk_delete(f, pCache, pChunk);
362
malloc_from_cache(InkFreeList *f, InkThreadCache *pCache, uint32_t nr)
365
InkChunkInfo *pChunk;
367
pChunk = pCache->free_chunk_list.head;
369
while ((item = malloc_from_chunk(f, pCache, pChunk))) {
373
ink_atomic_increment(&pCache->nr_free, 1);
374
ink_atomiclist_push(&pCache->outer_free_list, item);
376
pChunk = pChunk->link.next;
379
pChunk = ink_chunk_create(f, pCache);
380
if (nr == f->chunk_size)
381
return malloc_whole_chunk(f, pCache, pChunk);
383
while ((item = malloc_from_chunk(f, pCache, pChunk))) {
387
ink_atomic_increment(&pCache->nr_free, 1);
388
ink_atomiclist_push(&pCache->outer_free_list, item);
396
free_to_cache(InkFreeList *f, InkThreadCache *pCache, void *item, uint32_t nr)
401
free_to_chunk(f, pCache, item);
403
while (n && (item = ink_atomiclist_pop(&pCache->outer_free_list))) {
404
free_to_chunk(f, pCache, item);
407
ink_atomic_increment((int *)&pCache->nr_free, -(nr - n));
411
refresh_average_info(InkThreadCache *pCache)
416
nr_free = pCache->nr_free;
417
nr_average = pCache->nr_average;
419
if (pCache->status == 1 || nr_free < pCache->nr_min)
420
pCache->nr_min = nr_free;
422
pCache->nr_average = (nr_average * (1 - cfg_reclaim_factor)) +
423
(nr_free * cfg_reclaim_factor);
427
need_to_reclaim(InkFreeList *f, InkThreadCache *pCache, pthread_t tid)
429
if (!cfg_enable_reclaim)
432
if(pCache->nr_free >= pCache->nr_average &&
433
pCache->nr_total > f->chunk_size_base) {
434
if (pCache->nr_overage++ >= cfg_max_overage) {
435
pCache->nr_overage = 0;
441
pCache->nr_overage = 0;
446
reclaimable_freelist_init(InkFreeList **fl, const char *name,
447
uint32_t type_size, uint32_t chunk_size,
451
ink_freelist_list *fll = freelists;
453
/* quick test for power of 2 */
454
ink_assert(!(alignment & (alignment - 1)));
457
page_size = sysconf(_SC_PAGESIZE);
459
/* NOTE: it's safe to operate on this global list because
460
* ink_freelist_init() is only called from single-threaded
461
* initialization code. */
463
/* Reuse InkFreeList if it has the same aligned type_size. */
464
if (fll->fl->type_size == type_size) {
472
f = (InkFreeList *)ats_memalign(alignment, sizeof(InkFreeList));
473
fll = (ink_freelist_list *)ats_memalign(alignment, sizeof(ink_freelist_list));
475
fll->next = freelists;
481
f->allocated_base = 0;
484
memory_alignment_init(f, type_size, chunk_size, alignment);
487
f->pThreadCache = NULL;
488
f->nr_thread_cache = 0;
489
f->thread_cache_idx = nr_freelist++;
490
ink_assert(f->thread_cache_idx < MAX_NUM_FREELIST);
491
ink_mutex_init(&f->lock, "InkFreeList Lock");
497
reclaimable_freelist_new(InkFreeList *f)
502
uint32_t num_to_move;
503
InkChunkInfo *pChunk = NULL;
504
InkThreadCache *pCache, *pNextCache;
506
/* no thread cache, create it */
507
if (unlikely((pCache = ThreadCaches[f->thread_cache_idx]) == NULL)) {
508
pCache = (InkThreadCache *) ats_calloc(1, sizeof(InkThreadCache));
511
pCache->free_chunk_list = DLL<InkChunkInfo>();
513
/* this lock will only be accessed when initializing
514
* thread cache, so it won't damage performance */
515
ink_mutex_acquire(&f->lock);
516
ink_atomiclist_init(&pCache->outer_free_list, f->name, 0);
518
nr = CEIL(f->chunk_size_base, f->chunk_size);
519
for (i = 0; i < nr; i++) {
520
pChunk = ink_chunk_create(f, pCache);
523
pCache->nr_malloc = 1;
525
ThreadCaches[f->thread_cache_idx] = pCache;
527
if (f->pThreadCache) {
528
/* we will loop pCache.next without lock, following
529
* statement's sequence is important for us. */
530
pCache->next = f->pThreadCache;
531
pCache->prev = f->pThreadCache->prev;
532
pCache->next->prev = pCache;
533
pCache->prev->next = pCache;
535
pCache->next = pCache;
536
pCache->prev = pCache;
539
f->pThreadCache = pCache;
540
f->nr_thread_cache++;
542
ink_mutex_release(&f->lock);
544
ptr = malloc_whole_chunk(f, pCache, pChunk);
550
/* priority to fetch memory from outer_free_list */
551
if ((ptr = ink_atomiclist_pop(&pCache->outer_free_list))) {
552
old_value = ink_atomic_increment((int *)&pCache->nr_free, -1);
553
ink_release_assert(old_value > 0);
554
ink_atomic_increment(&pCache->nr_malloc, 1);
558
/* try to steal memory from other thread's outer_free_list */
559
pNextCache = pCache->next;
560
while (pNextCache != pCache) {
561
if ((ptr = ink_atomiclist_pop(&pNextCache->outer_free_list))) {
562
old_value = ink_atomic_increment((int *)&pNextCache->nr_free, -1);
563
ink_release_assert(old_value > 0);
564
ink_atomic_increment(&pNextCache->nr_malloc, 1);
567
pNextCache = pNextCache->next;
570
/* try to reclaim memory from all caches in the same thread */
571
for (i = 0; i < nr_freelist; i++) {
572
if ((pNextCache = ThreadCaches[i]) == NULL)
575
if (need_to_reclaim(pNextCache->f, pNextCache, THREAD_ID)) {
576
if (cfg_debug_filter & 0x1)
577
show_info("F", pNextCache->f, pNextCache);
579
num_to_move = MIN(pNextCache->nr_average, pNextCache->nr_free);
581
free_to_cache(pNextCache->f, pNextCache, NULL, num_to_move);
583
if (cfg_debug_filter & 0x1)
584
show_info("-", pNextCache->f, pNextCache);
586
refresh_average_info(pNextCache);
590
/* finally, fetch from thread local cache */
591
if (cfg_debug_filter & 0x2)
592
show_info("M", f, pCache);
593
ptr = malloc_from_cache(f, pCache, f->chunk_size);
594
if (cfg_debug_filter & 0x2)
595
show_info("+", f, pCache);
597
refresh_average_info(pCache);
598
ink_atomic_increment(&pCache->nr_malloc, 1);
603
reclaimable_freelist_free(InkFreeList *f, void *item)
605
InkChunkInfo *pChunk;
606
InkThreadCache *pCache;
611
pChunk = get_chunk_info_addr(f, item);
612
pCache = pChunk->pThreadCache;
614
ink_atomic_increment((int *)&pCache->nr_malloc, -1);
615
if (ink_atomic_cas((int *)&pCache->status, 0, 1))
616
refresh_average_info(pCache);
618
ink_atomic_increment(&pCache->nr_free, 1);
619
ink_atomiclist_push(&pCache->outer_free_list, item);