123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- #include "onewayalloc.h"
- // https://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
- #define OWA_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
- typedef struct owa_page {
- size_t stats_pages;
- size_t stats_pages_size;
- size_t stats_mallocs_made;
- size_t stats_mallocs_size;
- size_t size; // the total size of the page
- size_t offset; // the first free byte of the page
- struct owa_page *next; // the next page on the list
- struct owa_page *last; // the last page on the list - we currently allocate on this
- } OWA_PAGE;
- static size_t onewayalloc_total_memory = 0;
- size_t onewayalloc_allocated_memory(void) {
- return __atomic_load_n(&onewayalloc_total_memory, __ATOMIC_RELAXED);
- }
- // allocations need to be aligned to CPU register width
- // https://en.wikipedia.org/wiki/Data_structure_alignment
- static inline size_t natural_alignment(size_t size) {
- if(unlikely(size % OWA_NATURAL_ALIGNMENT))
- size = size + OWA_NATURAL_ALIGNMENT - (size % OWA_NATURAL_ALIGNMENT);
- return size;
- }
- // Create an OWA
- // Once it is created, the called may call the onewayalloc_mallocz()
- // any number of times, for any amount of memory.
- static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
- static size_t OWA_NATURAL_PAGE_SIZE = 0;
- if(unlikely(!OWA_NATURAL_PAGE_SIZE)) {
- long int page_size = sysconf(_SC_PAGE_SIZE);
- if (unlikely(page_size == -1))
- OWA_NATURAL_PAGE_SIZE = 4096;
- else
- OWA_NATURAL_PAGE_SIZE = page_size;
- }
- // our default page size
- size_t size = OWA_NATURAL_PAGE_SIZE;
- // make sure the new page will fit both the requested size
- // and the OWA_PAGE structure at its beginning
- size_hint += natural_alignment(sizeof(OWA_PAGE));
- // prefer the user size if it is bigger than our size
- if(size_hint > size) size = size_hint;
- // try to allocate half of the total we have allocated already
- if(likely(head)) {
- size_t optimal_size = head->stats_pages_size / 2;
- if(optimal_size > size) size = optimal_size;
- }
- // Make sure our allocations are always a multiple of the hardware page size
- if(size % OWA_NATURAL_PAGE_SIZE) size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
- // OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0);
- // if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
- OWA_PAGE *page = (OWA_PAGE *)mallocz(size);
- __atomic_add_fetch(&onewayalloc_total_memory, size, __ATOMIC_RELAXED);
- page->size = size;
- page->offset = natural_alignment(sizeof(OWA_PAGE));
- page->next = page->last = NULL;
- if(unlikely(!head)) {
- // this is the first time we are called
- head = page;
- head->stats_pages = 0;
- head->stats_pages_size = 0;
- head->stats_mallocs_made = 0;
- head->stats_mallocs_size = 0;
- }
- else {
- // link this page into our existing linked list
- head->last->next = page;
- }
- head->last = page;
- head->stats_pages++;
- head->stats_pages_size += size;
- return page;
- }
- ONEWAYALLOC *onewayalloc_create(size_t size_hint) {
- return (ONEWAYALLOC *)onewayalloc_create_internal(NULL, size_hint);
- }
- void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size) {
- #ifdef FSANITIZE_ADDRESS
- return mallocz(size);
- #endif
- OWA_PAGE *head = (OWA_PAGE *)owa;
- OWA_PAGE *page = head->last;
- // update stats
- head->stats_mallocs_made++;
- head->stats_mallocs_size += size;
- // make sure the size is aligned
- size = natural_alignment(size);
- if(unlikely(page->size - page->offset < size)) {
- // we don't have enough space to fit the data
- // let's get another page
- page = onewayalloc_create_internal(head, (size > page->size)?size:page->size);
- }
- char *mem = (char *)page;
- mem = &mem[page->offset];
- page->offset += size;
- return (void *)mem;
- }
- void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size) {
- size_t total = nmemb * size;
- void *mem = onewayalloc_mallocz(owa, total);
- memset(mem, 0, total);
- return mem;
- }
- char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s) {
- size_t size = strlen(s) + 1;
- char *d = onewayalloc_mallocz((OWA_PAGE *)owa, size);
- memcpy(d, s, size);
- return d;
- }
- void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size) {
- void *mem = onewayalloc_mallocz((OWA_PAGE *)owa, size);
- // memcpy() is way faster than strcpy() since it does not check for '\0'
- memcpy(mem, src, size);
- return mem;
- }
- void onewayalloc_freez(ONEWAYALLOC *owa __maybe_unused, const void *ptr __maybe_unused) {
- #ifdef FSANITIZE_ADDRESS
- freez((void *)ptr);
- return;
- #endif
- #ifdef NETDATA_INTERNAL_CHECKS
- // allow the caller to call us for a mallocz() allocation
- // so try to find it in our memory and if it is not there
- // log an error
- if (unlikely(!ptr))
- return;
- OWA_PAGE *head = (OWA_PAGE *)owa;
- OWA_PAGE *page;
- uintptr_t seeking = (uintptr_t)ptr;
- for(page = head; page ;page = page->next) {
- uintptr_t start = (uintptr_t)page;
- uintptr_t end = start + page->size;
- if(seeking >= start && seeking <= end) {
- // found it - it is ours
- // just return to let the caller think we actually did something
- return;
- }
- }
- // not found - it is not ours
- // let's free it with the system allocator
- netdata_log_error("ONEWAYALLOC: request to free address 0x%p that is not allocated by this OWA", ptr);
- #endif
- return;
- }
- void *onewayalloc_doublesize(ONEWAYALLOC *owa, const void *src, size_t oldsize) {
- size_t newsize = oldsize * 2;
- void *dst = onewayalloc_mallocz(owa, newsize);
- memcpy(dst, src, oldsize);
- onewayalloc_freez(owa, src);
- return dst;
- }
- void onewayalloc_destroy(ONEWAYALLOC *owa) {
- if(!owa) return;
- OWA_PAGE *head = (OWA_PAGE *)owa;
- //netdata_log_info("OWA: %zu allocations of %zu total bytes, in %zu pages of %zu total bytes",
- // head->stats_mallocs_made, head->stats_mallocs_size,
- // head->stats_pages, head->stats_pages_size);
- size_t total_size = 0;
- OWA_PAGE *page = head;
- while(page) {
- total_size += page->size;
- OWA_PAGE *p = page;
- page = page->next;
- // munmap(p, p->size);
- freez(p);
- }
- __atomic_sub_fetch(&onewayalloc_total_memory, total_size, __ATOMIC_RELAXED);
- }
|