mirror of https://github.com/trapexit/mergerfs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
6.5 KiB
380 lines
6.5 KiB
/*
|
|
ISC License
|
|
|
|
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "kvec.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
#define ROUND_UP(N,S) ((((N) + (S) - 1) / (S)) * (S))
|
|
|
|
typedef kvec_t(void*) slab_kvec_t;
|
|
|
|
typedef struct mem_stack_t mem_stack_t;
|
|
struct mem_stack_t
|
|
{
|
|
mem_stack_t *next;
|
|
};
|
|
|
|
typedef struct fmp_t fmp_t;
|
|
struct fmp_t
|
|
{
|
|
mem_stack_t *objs;
|
|
slab_kvec_t slabs;
|
|
uint64_t avail_objs;
|
|
uint64_t obj_size;
|
|
uint64_t page_size;
|
|
uint64_t slab_size;
|
|
};
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_page_size()
|
|
{
|
|
return sysconf(_SC_PAGESIZE);
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_init(fmp_t *fmp_,
|
|
const uint64_t obj_size_,
|
|
const uint64_t page_multiple_)
|
|
{
|
|
kv_init(fmp_->slabs);
|
|
fmp_->objs = NULL;
|
|
fmp_->avail_objs = 0;
|
|
fmp_->obj_size = ROUND_UP(obj_size_,sizeof(void*));
|
|
fmp_->page_size = fmp_page_size();
|
|
fmp_->slab_size = (fmp_->page_size * page_multiple_);
|
|
}
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_slab_count(fmp_t *fmp_)
|
|
{
|
|
return kv_size(fmp_->slabs);
|
|
}
|
|
|
|
static
|
|
inline
|
|
void*
|
|
fmp_slab_alloc_posix_memalign(fmp_t *fmp_)
|
|
{
|
|
int rv;
|
|
void *mem;
|
|
const size_t alignment = fmp_->page_size;
|
|
const size_t size = fmp_->slab_size;
|
|
|
|
rv = posix_memalign(&mem,alignment,size);
|
|
if(rv != 0)
|
|
return NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void*
|
|
fmp_slab_alloc_mmap(fmp_t *fmp_)
|
|
{
|
|
void *mem;
|
|
void *address = NULL;
|
|
const size_t length = fmp_->slab_size;
|
|
const int protect = PROT_READ|PROT_WRITE;
|
|
const int flags = MAP_PRIVATE|MAP_ANONYMOUS;
|
|
const int filedes = -1;
|
|
const off_t offset = 0;
|
|
|
|
mem = mmap(address,length,protect,flags,filedes,offset);
|
|
if(mem == MAP_FAILED)
|
|
return NULL;
|
|
|
|
return mem;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_slab_free_posix_memalign(fmp_t* fmp_,
|
|
void *mem_)
|
|
{
|
|
(void)fmp_;
|
|
free(mem_);
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_slab_free_mmap(fmp_t* fmp_,
|
|
void *mem_)
|
|
{
|
|
void *addr = mem_;
|
|
size_t length = fmp_->slab_size;
|
|
|
|
(void)munmap(addr,length);
|
|
}
|
|
|
|
static
|
|
inline
|
|
int
|
|
fmp_slab_alloc(fmp_t *fmp_)
|
|
{
|
|
char *i;
|
|
void *mem;
|
|
|
|
mem = fmp_slab_alloc_mmap(fmp_);
|
|
if(mem == NULL)
|
|
return -ENOMEM;
|
|
|
|
kv_push(void*,fmp_->slabs,mem);
|
|
|
|
i = ((char*)mem + fmp_->slab_size - fmp_->obj_size);
|
|
while(i >= (char*)mem)
|
|
{
|
|
mem_stack_t *obj = (mem_stack_t*)i;
|
|
|
|
obj->next = fmp_->objs;
|
|
fmp_->objs = obj;
|
|
fmp_->avail_objs++;
|
|
|
|
i -= fmp_->obj_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void*
|
|
fmp_alloc(fmp_t *fmp_)
|
|
{
|
|
void *rv;
|
|
|
|
if(fmp_->objs == NULL)
|
|
fmp_slab_alloc(fmp_);
|
|
if(fmp_->objs == NULL)
|
|
return NULL;
|
|
|
|
rv = fmp_->objs;
|
|
|
|
fmp_->objs = fmp_->objs->next;
|
|
fmp_->avail_objs--;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void*
|
|
fmp_calloc(fmp_t *fmp_)
|
|
{
|
|
void *obj;
|
|
|
|
obj = fmp_alloc(fmp_);
|
|
if(obj == NULL)
|
|
return NULL;
|
|
|
|
memset(obj,0,fmp_->obj_size);
|
|
|
|
return obj;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_free(fmp_t *fmp_,
|
|
void *obj_)
|
|
{
|
|
mem_stack_t *obj = (mem_stack_t*)obj_;
|
|
|
|
obj->next = fmp_->objs;
|
|
fmp_->objs = obj;
|
|
fmp_->avail_objs++;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_clear(fmp_t *fmp_)
|
|
{
|
|
while(kv_size(fmp_->slabs))
|
|
{
|
|
void *slab = kv_pop(fmp_->slabs);
|
|
|
|
fmp_slab_free_mmap(fmp_,slab);
|
|
}
|
|
|
|
fmp_->objs = NULL;
|
|
fmp_->avail_objs = 0;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_destroy(fmp_t *fmp_)
|
|
{
|
|
fmp_clear(fmp_);
|
|
kv_destroy(fmp_->slabs);
|
|
}
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_avail_objs(fmp_t *fmp_)
|
|
{
|
|
return fmp_->avail_objs;
|
|
}
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_objs_per_slab(fmp_t *fmp_)
|
|
{
|
|
return (fmp_->slab_size / fmp_->obj_size);
|
|
}
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_objs_in_slab(fmp_t *fmp_,
|
|
void *slab_)
|
|
{
|
|
char *slab;
|
|
uint64_t objs_per_slab;
|
|
uint64_t objs_in_slab;
|
|
|
|
slab = (char*)slab_;
|
|
objs_in_slab = 0;
|
|
objs_per_slab = fmp_objs_per_slab(fmp_);
|
|
for(mem_stack_t *stack = fmp_->objs; stack != NULL; stack = stack->next)
|
|
{
|
|
char *obj = (char*)stack;
|
|
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
|
objs_in_slab++;
|
|
if(objs_in_slab >= objs_per_slab)
|
|
break;
|
|
}
|
|
|
|
return objs_in_slab;
|
|
}
|
|
|
|
static
|
|
inline
|
|
void
|
|
fmp_remove_objs_in_slab(fmp_t *fmp_,
|
|
void *slab_)
|
|
{
|
|
char *slab;
|
|
uint64_t objs_per_slab;
|
|
uint64_t objs_in_slab;
|
|
mem_stack_t **p;
|
|
|
|
p = &fmp_->objs;
|
|
slab = (char*)slab_;
|
|
objs_in_slab = 0;
|
|
objs_per_slab = fmp_objs_per_slab(fmp_);
|
|
while((*p) != NULL)
|
|
{
|
|
char *obj = (char*)*p;
|
|
|
|
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
|
{
|
|
objs_in_slab++;
|
|
*p = (*p)->next;
|
|
fmp_->avail_objs--;
|
|
|
|
if(objs_in_slab >= objs_per_slab)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
p = &(*p)->next;
|
|
}
|
|
}
|
|
|
|
static
|
|
inline
|
|
int
|
|
fmp_gc_slab(fmp_t *fmp_,
|
|
uint64_t slab_idx_)
|
|
{
|
|
char *slab;
|
|
uint64_t objs_in_slab;
|
|
uint64_t objs_per_slab;
|
|
|
|
slab_idx_ = (slab_idx_ % kv_size(fmp_->slabs));
|
|
|
|
slab = kv_A(fmp_->slabs,slab_idx_);
|
|
|
|
objs_per_slab = fmp_objs_per_slab(fmp_);
|
|
objs_in_slab = fmp_objs_in_slab(fmp_,slab);
|
|
if(objs_in_slab != objs_per_slab)
|
|
return 0;
|
|
|
|
fmp_remove_objs_in_slab(fmp_,slab);
|
|
kv_delete(fmp_->slabs,slab_idx_);
|
|
fmp_slab_free_mmap(fmp_,slab);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static
|
|
inline
|
|
int
|
|
fmp_gc(fmp_t *fmp_)
|
|
{
|
|
uint64_t slab_idx;
|
|
|
|
slab_idx = rand();
|
|
|
|
return fmp_gc_slab(fmp_,slab_idx);
|
|
}
|
|
|
|
static
|
|
inline
|
|
double
|
|
fmp_slab_usage_ratio(fmp_t *fmp_)
|
|
{
|
|
double avail_objs;
|
|
double objs_per_slab;
|
|
double nums_of_slabs;
|
|
|
|
avail_objs = fmp_->avail_objs;
|
|
objs_per_slab = fmp_objs_per_slab(fmp_);
|
|
nums_of_slabs = kv_size(fmp_->slabs);
|
|
|
|
return (avail_objs / (objs_per_slab * nums_of_slabs));
|
|
}
|
|
|
|
static
|
|
inline
|
|
uint64_t
|
|
fmp_total_allocated_memory(fmp_t *fmp_)
|
|
{
|
|
return (fmp_->slab_size * kv_size(fmp_->slabs));
|
|
}
|