arrayalloc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #include "../libnetdata.h"
  2. #include "arrayalloc.h"
  3. #include "daemon/common.h"
  4. // max file size
  5. #define ARAL_MAX_PAGE_SIZE_MMAP (1*1024*1024*1024)
  6. // max malloc size
  7. #define ARAL_MAX_PAGE_SIZE_MALLOC (10*1024*1024)
  8. typedef struct arrayalloc_free {
  9. size_t size;
  10. struct arrayalloc_page *page;
  11. struct arrayalloc_free *next;
  12. } ARAL_FREE;
  13. typedef struct arrayalloc_page {
  14. const char *filename;
  15. size_t size; // the total size of the page
  16. size_t used_elements; // the total number of used elements on this page
  17. uint8_t *data;
  18. ARAL_FREE *free_list;
  19. struct arrayalloc_page *prev; // the prev page on the list
  20. struct arrayalloc_page *next; // the next page on the list
  21. } ARAL_PAGE;
  22. #define ARAL_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
  23. static inline size_t natural_alignment(size_t size, size_t alignment) {
  24. if(unlikely(size % alignment))
  25. size = size + alignment - (size % alignment);
  26. return size;
  27. }
  28. static void arrayalloc_init(ARAL *ar) {
  29. static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER;
  30. netdata_mutex_lock(&mutex);
  31. if(!ar->internal.initialized) {
  32. netdata_mutex_init(&ar->internal.mutex);
  33. long int page_size = sysconf(_SC_PAGE_SIZE);
  34. if (unlikely(page_size == -1))
  35. ar->internal.natural_page_size = 4096;
  36. else
  37. ar->internal.natural_page_size = page_size;
  38. // we need to add a page pointer after the element
  39. // so, first align the element size to the pointer size
  40. ar->internal.element_size = natural_alignment(ar->element_size, sizeof(uintptr_t));
  41. // then add the size of a pointer to it
  42. ar->internal.element_size += sizeof(uintptr_t);
  43. // make sure it is at least what we need for an ARAL_FREE slot
  44. if (ar->internal.element_size < sizeof(ARAL_FREE))
  45. ar->internal.element_size = sizeof(ARAL_FREE);
  46. // and finally align it to the natural alignment
  47. ar->internal.element_size = natural_alignment(ar->internal.element_size, ARAL_NATURAL_ALIGNMENT);
  48. // this is where we should write the pointer
  49. ar->internal.page_ptr_offset = ar->internal.element_size - sizeof(uintptr_t);
  50. if(ar->element_size + sizeof(uintptr_t) > ar->internal.element_size)
  51. fatal("ARRAYALLOC: failed to calculate properly page_ptr_offset: element size %zu, sizeof(uintptr_t) %zu, natural alignment %zu, final element size %zu, page_ptr_offset %zu",
  52. ar->element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset);
  53. //info("ARRAYALLOC: element size %zu, sizeof(uintptr_t) %zu, natural alignment %zu, final element size %zu, page_ptr_offset %zu",
  54. // ar->element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset);
  55. if (ar->elements < 10)
  56. ar->elements = 10;
  57. ar->internal.mmap = (ar->use_mmap && ar->cache_dir && *ar->cache_dir) ? true : false;
  58. ar->internal.max_alloc_size = ar->internal.mmap ? ARAL_MAX_PAGE_SIZE_MMAP : ARAL_MAX_PAGE_SIZE_MALLOC;
  59. if(ar->internal.max_alloc_size % ar->internal.natural_page_size)
  60. ar->internal.max_alloc_size += ar->internal.natural_page_size - (ar->internal.max_alloc_size % ar->internal.natural_page_size) ;
  61. if(ar->internal.max_alloc_size % ar->internal.element_size)
  62. ar->internal.max_alloc_size -= ar->internal.max_alloc_size % ar->internal.element_size;
  63. ar->internal.first_page = NULL;
  64. ar->internal.last_page = NULL;
  65. ar->internal.allocation_multiplier = 1;
  66. ar->internal.file_number = 0;
  67. if(ar->internal.mmap) {
  68. char filename[FILENAME_MAX + 1];
  69. snprintfz(filename, FILENAME_MAX, "%s/array_alloc.mmap", *ar->cache_dir);
  70. int r = mkdir(filename, 0775);
  71. if (r != 0 && errno != EEXIST)
  72. fatal("Cannot create directory '%s'", filename);
  73. }
  74. ar->internal.initialized = true;
  75. }
  76. netdata_mutex_unlock(&mutex);
  77. }
  78. #ifdef NETDATA_INTERNAL_CHECKS
  79. static inline void arrayalloc_free_checks(ARAL *ar, ARAL_FREE *fr) {
  80. if(fr->size < ar->internal.element_size)
  81. fatal("ARRAYALLOC: free item of size %zu, less than the expected element size %zu", fr->size, ar->internal.element_size);
  82. if(fr->size % ar->internal.element_size)
  83. fatal("ARRAYALLOC: free item of size %zu is not multiple to element size %zu", fr->size, ar->internal.element_size);
  84. }
  85. #else
  86. #define arrayalloc_free_checks(ar, fr) debug_dummy()
  87. #endif
  88. static inline void unlink_page(ARAL *ar, ARAL_PAGE *page) {
  89. if(unlikely(!page)) return;
  90. if(page->next)
  91. page->next->prev = page->prev;
  92. if(page->prev)
  93. page->prev->next = page->next;
  94. if(page == ar->internal.first_page)
  95. ar->internal.first_page = page->next;
  96. if(page == ar->internal.last_page)
  97. ar->internal.last_page = page->prev;
  98. }
  99. static inline void link_page_first(ARAL *ar, ARAL_PAGE *page) {
  100. page->prev = NULL;
  101. page->next = ar->internal.first_page;
  102. if(page->next) page->next->prev = page;
  103. ar->internal.first_page = page;
  104. if(!ar->internal.last_page)
  105. ar->internal.last_page = page;
  106. }
  107. static inline void link_page_last(ARAL *ar, ARAL_PAGE *page) {
  108. page->next = NULL;
  109. page->prev = ar->internal.last_page;
  110. if(page->prev) page->prev->next = page;
  111. ar->internal.last_page = page;
  112. if(!ar->internal.first_page)
  113. ar->internal.first_page = page;
  114. }
  115. static inline ARAL_PAGE *find_page_with_allocation(ARAL *ar, void *ptr) {
  116. uintptr_t seeking = (uintptr_t)ptr;
  117. ARAL_PAGE *page;
  118. for(page = ar->internal.first_page; page ; page = page->next) {
  119. if(unlikely(seeking >= (uintptr_t)page->data && seeking < (uintptr_t)page->data + page->size))
  120. break;
  121. }
  122. return page;
  123. }
  124. static void arrayalloc_increase(ARAL *ar) {
  125. if(unlikely(!ar->internal.initialized))
  126. arrayalloc_init(ar);
  127. ARAL_PAGE *page = callocz(1, sizeof(ARAL_PAGE));
  128. page->size = ar->elements * ar->internal.element_size * ar->internal.allocation_multiplier;
  129. if(page->size > ar->internal.max_alloc_size)
  130. page->size = ar->internal.max_alloc_size;
  131. else
  132. ar->internal.allocation_multiplier *= 2;
  133. if(ar->internal.mmap) {
  134. ar->internal.file_number++;
  135. char filename[FILENAME_MAX + 1];
  136. snprintfz(filename, FILENAME_MAX, "%s/array_alloc.mmap/%s.%zu", *ar->cache_dir, ar->filename, ar->internal.file_number);
  137. page->filename = strdupz(filename);
  138. page->data = netdata_mmap(page->filename, page->size, MAP_SHARED, 0);
  139. if (unlikely(!page->data))
  140. fatal("Cannot allocate arrayalloc buffer of size %zu on filename '%s'", page->size, page->filename);
  141. }
  142. else
  143. page->data = mallocz(page->size);
  144. // link the free space to its page
  145. ARAL_FREE *fr = (ARAL_FREE *)page->data;
  146. fr->size = page->size;
  147. fr->page = page;
  148. fr->next = NULL;
  149. page->free_list = fr;
  150. // link the new page at the front of the list of pages
  151. link_page_first(ar, page);
  152. arrayalloc_free_checks(ar, fr);
  153. }
  154. static void arrayalloc_lock(ARAL *ar) {
  155. if(!ar->internal.lockless)
  156. netdata_mutex_lock(&ar->internal.mutex);
  157. }
  158. static void arrayalloc_unlock(ARAL *ar) {
  159. if(!ar->internal.lockless)
  160. netdata_mutex_unlock(&ar->internal.mutex);
  161. }
  162. ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir) {
  163. ARAL *ar = callocz(1, sizeof(ARAL));
  164. ar->element_size = element_size;
  165. ar->elements = elements;
  166. ar->filename = filename;
  167. ar->cache_dir = cache_dir;
  168. return ar;
  169. }
  170. void *arrayalloc_mallocz(ARAL *ar) {
  171. arrayalloc_lock(ar);
  172. if(unlikely(!ar->internal.first_page || !ar->internal.first_page->free_list))
  173. arrayalloc_increase(ar);
  174. ARAL_PAGE *page = ar->internal.first_page;
  175. ARAL_FREE *fr = page->free_list;
  176. if(unlikely(!fr))
  177. fatal("ARRAYALLOC: free item cannot be NULL.");
  178. if(unlikely(fr->size < ar->internal.element_size))
  179. fatal("ARRAYALLOC: free item size %zu is smaller than %zu", fr->size, ar->internal.element_size);
  180. if(fr->size - ar->internal.element_size <= ar->internal.element_size) {
  181. // we are done with this page
  182. page->free_list = NULL;
  183. if(page != ar->internal.last_page) {
  184. unlink_page(ar, page);
  185. link_page_last(ar, page);
  186. }
  187. }
  188. else {
  189. uint8_t *data = (uint8_t *)fr;
  190. ARAL_FREE *fr2 = (ARAL_FREE *)&data[ar->internal.element_size];
  191. fr2->page = fr->page;
  192. fr2->size = fr->size - ar->internal.element_size;
  193. fr2->next = fr->next;
  194. page->free_list = fr2;
  195. arrayalloc_free_checks(ar, fr2);
  196. }
  197. fr->page->used_elements++;
  198. // put the page pointer after the element
  199. uint8_t *data = (uint8_t *)fr;
  200. ARAL_PAGE **page_ptr = (ARAL_PAGE **)&data[ar->internal.page_ptr_offset];
  201. *page_ptr = page;
  202. arrayalloc_unlock(ar);
  203. return (void *)fr;
  204. }
  205. void arrayalloc_freez(ARAL *ar, void *ptr) {
  206. if(!ptr) return;
  207. arrayalloc_lock(ar);
  208. // get the page pointer
  209. ARAL_PAGE *page;
  210. {
  211. uint8_t *data = (uint8_t *)ptr;
  212. ARAL_PAGE **page_ptr = (ARAL_PAGE **)&data[ar->internal.page_ptr_offset];
  213. page = *page_ptr;
  214. #ifdef NETDATA_INTERNAL_CHECKS
  215. // make it NULL so that we will fail on double free
  216. // do not enable this on production, because the MMAP file
  217. // will need to be saved again!
  218. *page_ptr = NULL;
  219. #endif
  220. }
  221. #ifdef NETDATA_INTERNAL_CHECKS
  222. {
  223. // find the page ptr belongs
  224. ARAL_PAGE *page2 = find_page_with_allocation(ar, ptr);
  225. if(unlikely(page != page2))
  226. fatal("ARRAYALLOC: page pointers do not match!");
  227. if (unlikely(!page2))
  228. fatal("ARRAYALLOC: free of pointer %p is not in arrayalloc address space.", ptr);
  229. }
  230. #endif
  231. if(unlikely(!page))
  232. fatal("ARRAYALLOC: possible corruption or double free of pointer %p", ptr);
  233. if (unlikely(!page->used_elements))
  234. fatal("ARRAYALLOC: free of pointer %p is inside a page without any active allocations.", ptr);
  235. page->used_elements--;
  236. // make this element available
  237. ARAL_FREE *fr = (ARAL_FREE *)ptr;
  238. fr->page = page;
  239. fr->size = ar->internal.element_size;
  240. fr->next = page->free_list;
  241. page->free_list = fr;
  242. // if the page is empty, release it
  243. if(!page->used_elements) {
  244. unlink_page(ar, page);
  245. // free it
  246. if(ar->internal.mmap) {
  247. munmap(page->data, page->size);
  248. if (unlikely(unlink(page->filename) == 1))
  249. error("Cannot delete file '%s'", page->filename);
  250. freez((void *)page->filename);
  251. }
  252. else
  253. freez(page->data);
  254. freez(page);
  255. }
  256. else if(page != ar->internal.first_page) {
  257. unlink_page(ar, page);
  258. link_page_first(ar, page);
  259. }
  260. arrayalloc_unlock(ar);
  261. }