/* * fy-allocator-auto.c - automatic allocator * * Copyright (c) 2023 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "fy-utils.h" #include "fy-allocator-mremap.h" #include "fy-allocator-dedup.h" #include "fy-allocator-linear.h" #include "fy-allocator-auto.h" /* fixed parameters */ #ifndef AUTO_ALLOCATOR_BIG_ALLOC_THRESHOLD #define AUTO_ALLOCATOR_BIG_ALLOC_THRESHOLD SIZE_MAX #endif #ifndef AUTO_ALLOCATOR_EMPTY_THRESHOLD #define AUTO_ALLOCATOR_EMPTY_THRESHOLD 64 #endif #ifndef AUTO_ALLOCATOR_GROW_RATIO #define AUTO_ALLOCATOR_GROW_RATIO 1.5 #endif #ifndef AUTO_ALOCATOR_BALLOON_RATIO #define AUTO_ALOCATOR_BALLOON_RATIO 8.0 #endif #ifndef AUTO_ALLOCATOR_ARENA_TYPE #define AUTO_ALLOCATOR_ARENA_TYPE FYMRAT_MMAP #endif #ifndef AUTO_ALLOCATOR_MINIMUM_ARENA_SIZE #define AUTO_ALLOCATOR_MINIMUM_ARENA_SIZE ((size_t)16 << 20) /* 16 MB */ #endif #ifndef AUTO_ALLOCATOR_DEFAULT_ESTIMATED_MAX_SIZE #define AUTO_ALLOCATOR_DEFAULT_ESTIMATED_MAX_SIZE ((size_t)1 << 20) /* 1MB */ #endif #ifndef AUTO_ALLOCATOR_DEFAULT_BLOOM_FILTER_BITS #define AUTO_ALLOCATOR_DEFAULT_BLOOM_FILTER_BITS 0 #endif #ifndef AUTO_ALLOCATOR_DEFAULT_BUCKET_COUNT_BITS #define AUTO_ALLOCATOR_DEFAULT_BUCKET_COUNT_BITS 0 #endif #ifndef AUTO_ALLOCATOR_DEFAULT_DEDUP_THRESHOLD #define AUTO_ALLOCATOR_DEFAULT_DEDUP_THRESHOLD 0 /* dedup everything */ #endif #ifndef AUTO_ALLOCATOR_DEFAULT_CHAIN_LENGTH_GROW_TRIGGER #define AUTO_ALLOCATOR_DEFAULT_CHAIN_LENGTH_GROW_TRIGGER 0 /* let dedup decide */ #endif static const struct fy_auto_allocator_cfg default_cfg = { .scenario = FYAST_PER_TAG_FREE_DEDUP, .estimated_max_size = AUTO_ALLOCATOR_DEFAULT_ESTIMATED_MAX_SIZE, }; static void fy_auto_cleanup(struct fy_allocator *a); static int fy_auto_setup(struct fy_allocator *a, struct fy_allocator *parent, int parent_tag, const void *cfg_data) { size_t pagesz, size; struct fy_auto_allocator *aa; const struct fy_auto_allocator_cfg *cfg; struct fy_mremap_allocator_cfg mrcfg; struct fy_dedup_allocator_cfg dcfg; struct fy_linear_allocator_cfg lcfg; struct fy_allocator *mra = NULL, *da = NULL, *ma = NULL, *la = NULL; struct fy_allocator *topa = NULL, *suba = NULL; cfg = cfg_data ? cfg_data : &default_cfg; aa = container_of(a, struct fy_auto_allocator, a); memset(aa, 0, sizeof(*aa)); aa->a.name = "auto"; aa->a.ops = &fy_auto_allocator_ops; aa->a.parent = parent; aa->a.parent_tag = parent_tag; aa->cfg = *cfg; pagesz = sysconf(_SC_PAGESIZE); size = cfg->estimated_max_size && cfg->estimated_max_size != SIZE_MAX ? cfg->estimated_max_size : AUTO_ALLOCATOR_MINIMUM_ARENA_SIZE; /* first allocator */ switch (cfg->scenario) { case FYAST_PER_TAG_FREE: case FYAST_PER_TAG_FREE_DEDUP: memset(&mrcfg, 0, sizeof(mrcfg)); mrcfg.big_alloc_threshold = AUTO_ALLOCATOR_BIG_ALLOC_THRESHOLD; mrcfg.empty_threshold = AUTO_ALLOCATOR_EMPTY_THRESHOLD; mrcfg.grow_ratio = AUTO_ALLOCATOR_GROW_RATIO; mrcfg.balloon_ratio = AUTO_ALOCATOR_BALLOON_RATIO; mrcfg.arena_type = AUTO_ALLOCATOR_ARENA_TYPE; mrcfg.minimum_arena_size = fy_size_t_align(size, pagesz); mra = fy_allocator_create("mremap", &mrcfg); if (!mra) goto err_out; topa = mra; break; case FYAST_PER_OBJ_FREE: case FYAST_PER_OBJ_FREE_DEDUP: ma = fy_allocator_create("malloc", NULL); if (!ma) goto err_out; topa = ma; break; case FYAST_SINGLE_LINEAR_RANGE: case FYAST_SINGLE_LINEAR_RANGE_DEDUP: memset(&lcfg, 0, sizeof(lcfg)); lcfg.size = fy_size_t_align(size, pagesz); la = fy_allocator_create("linear", &lcfg); if (!la) goto err_out; topa = la; break; default: goto err_out; } if (!topa) goto err_out; /* stack the dedup */ switch (cfg->scenario) { case FYAST_PER_TAG_FREE_DEDUP: case FYAST_PER_OBJ_FREE_DEDUP: case FYAST_SINGLE_LINEAR_RANGE_DEDUP: memset(&dcfg, 0, sizeof(dcfg)); dcfg.parent_allocator = topa; dcfg.bloom_filter_bits = AUTO_ALLOCATOR_DEFAULT_BLOOM_FILTER_BITS; dcfg.bucket_count_bits = AUTO_ALLOCATOR_DEFAULT_BUCKET_COUNT_BITS; dcfg.dedup_threshold = AUTO_ALLOCATOR_DEFAULT_DEDUP_THRESHOLD; dcfg.chain_length_grow_trigger = AUTO_ALLOCATOR_DEFAULT_CHAIN_LENGTH_GROW_TRIGGER; dcfg.estimated_content_size = size; dcfg.minimum_bucket_occupancy = 0.5; da = fy_allocator_create("dedup", &dcfg); if (!da) goto err_out; /* the top is sub now */ suba = topa; topa = da; break; /* nothing to stack for these */ case FYAST_PER_TAG_FREE: case FYAST_PER_OBJ_FREE: case FYAST_SINGLE_LINEAR_RANGE: break; default: goto err_out; } /* OK, assign the allocators now */ aa->parent_allocator = topa; aa->sub_parent_allocator = suba; return 0; err_out: fy_allocator_destroy(suba); fy_allocator_destroy(topa); fy_auto_cleanup(a); return -1; } static void fy_auto_cleanup(struct fy_allocator *a) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); /* order is important! */ if (aa->parent_allocator) { fy_allocator_destroy(aa->parent_allocator); aa->parent_allocator = NULL; } if (aa->sub_parent_allocator) { fy_allocator_destroy(aa->sub_parent_allocator); aa->sub_parent_allocator = NULL; } } struct fy_allocator *fy_auto_create(struct fy_allocator *parent, int parent_tag, const void *cfg) { struct fy_auto_allocator *aa = NULL; int rc; aa = fy_early_parent_allocator_alloc(parent, parent_tag, sizeof(*aa), _Alignof(struct fy_auto_allocator)); rc = fy_auto_setup(&aa->a, parent, parent_tag, cfg); if (rc) goto err_out; return &aa->a; err_out: fy_early_parent_allocator_free(parent, parent_tag, aa); return NULL; } void fy_auto_destroy(struct fy_allocator *a) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_auto_cleanup(a); fy_parent_allocator_free(a, aa); } void fy_auto_dump(struct fy_allocator *a) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_dump_nocheck(aa->parent_allocator); } static void *fy_auto_alloc(struct fy_allocator *a, int tag, size_t size, size_t align) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_alloc_nocheck(aa->parent_allocator, tag, size, align); } static void fy_auto_free(struct fy_allocator *a, int tag, void *data) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_free_nocheck(aa->parent_allocator, tag, data); } static int fy_auto_update_stats(struct fy_allocator *a, int tag, struct fy_allocator_stats *stats) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_update_stats_nocheck(aa->parent_allocator, tag, stats); } static const void *fy_auto_storev(struct fy_allocator *a, int tag, const struct iovec *iov, int iovcnt, size_t align, uint64_t hash) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_storev_hash_nocheck(aa->parent_allocator, tag, iov, iovcnt, align, hash); } static const void *fy_auto_lookupv(struct fy_allocator *a, int tag, const struct iovec *iov, int iovcnt, size_t align, uint64_t hash) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_lookupv_nocheck(aa->parent_allocator, tag, iov, iovcnt, align, hash); } static void fy_auto_release(struct fy_allocator *a, int tag, const void *data, size_t size) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_release_nocheck(aa->parent_allocator, tag, data, size); } static int fy_auto_get_tag(struct fy_allocator *a) { struct fy_auto_allocator *aa; /* TODO, convert tag config? */ aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_get_tag_nocheck(aa->parent_allocator); } static void fy_auto_release_tag(struct fy_allocator *a, int tag) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_release_tag_nocheck(aa->parent_allocator, tag); } static int fy_auto_get_tag_count(struct fy_allocator *a) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_get_tag_count_nocheck(aa->parent_allocator); } static int fy_auto_set_tag_count(struct fy_allocator *a, unsigned int count) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_set_tag_count_nocheck(aa->parent_allocator, count); } static void fy_auto_trim_tag(struct fy_allocator *a, int tag) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_trim_tag_nocheck(aa->parent_allocator, tag); } static void fy_auto_reset_tag(struct fy_allocator *a, int tag) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); fy_allocator_reset_tag_nocheck(aa->parent_allocator, tag); } static struct fy_allocator_info * fy_auto_get_info(struct fy_allocator *a, int tag) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_get_info_nocheck(aa->parent_allocator, tag); } static enum fy_allocator_cap_flags fy_auto_get_caps(struct fy_allocator *a) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_get_caps_nocheck(aa->parent_allocator); } static bool fy_auto_contains(struct fy_allocator *a, int tag, const void *ptr) { struct fy_auto_allocator *aa; aa = container_of(a, struct fy_auto_allocator, a); return fy_allocator_contains_nocheck(aa->parent_allocator, tag, ptr); } const struct fy_allocator_ops fy_auto_allocator_ops = { .setup = fy_auto_setup, .cleanup = fy_auto_cleanup, .create = fy_auto_create, .destroy = fy_auto_destroy, .dump = fy_auto_dump, .alloc = fy_auto_alloc, .free = fy_auto_free, .update_stats = fy_auto_update_stats, .storev = fy_auto_storev, .lookupv = fy_auto_lookupv, .release = fy_auto_release, .get_tag = fy_auto_get_tag, .release_tag = fy_auto_release_tag, .get_tag_count = fy_auto_get_tag_count, .set_tag_count = fy_auto_set_tag_count, .trim_tag = fy_auto_trim_tag, .reset_tag = fy_auto_reset_tag, .get_info = fy_auto_get_info, .get_caps = fy_auto_get_caps, .contains = fy_auto_contains, };