#include "config.h"
#include "util/alloc.h"
+/** prealloc some entries in the cache. To minimize contention.
+ * @param alloc: the structure to fill up.
+ */
+static void
+prealloc(struct alloc_cache* alloc)
+{
+ alloc_special_t* p;
+ int i;
+ for(i=0; i<ALLOC_SPECIAL_MAX; i++) {
+ if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
+ fatal_exit("prealloc: out of memory");
+ alloc_special_next(p) = alloc->quar;
+ alloc->quar = p;
+ alloc->num_quar++;
+ }
+}
+
+void
+alloc_init(struct alloc_cache* alloc, struct alloc_cache* super)
+{
+ memset(alloc, 0, sizeof(*alloc));
+ alloc->super = super;
+ lock_quick_init(&alloc->lock);
+}
+
+void
+alloc_delete(struct alloc_cache* alloc)
+{
+ alloc_special_t* p, *np;
+ if(!alloc)
+ return;
+ lock_quick_destroy(&alloc->lock);
+ if(alloc->super && alloc->quar) {
+ /* push entire list into super */
+ p = alloc->quar;
+ while(alloc_special_next(p)) /* find last */
+ p = alloc_special_next(p);
+ lock_quick_lock(&alloc->super->lock);
+ alloc_special_next(p) = alloc->super->quar;
+ alloc->super->quar = alloc->quar;
+ alloc->super->num_quar += alloc->num_quar;
+ lock_quick_unlock(&alloc->super->lock);
+ } else {
+ /* free */
+ p = alloc->quar;
+ while(p) {
+ np = alloc_special_next(p);
+ free(p);
+ p = np;
+ }
+ }
+ alloc->quar = 0;
+ alloc->num_quar = 0;
+}
+
+alloc_special_t*
+alloc_special_obtain(struct alloc_cache* alloc)
+{
+ alloc_special_t* p;
+ log_assert(alloc);
+ /* see if in local cache */
+ if(alloc->quar) {
+ p = alloc->quar;
+ alloc->quar = alloc_special_next(p);
+ alloc->num_quar--;
+ alloc->special_allocated++;
+ return p;
+ }
+ /* see if in global cache */
+ if(alloc->super) {
+ lock_quick_lock(&alloc->super->lock);
+ if((p = alloc->super->quar)) {
+ alloc->super->quar = alloc_special_next(p);
+ alloc->super->num_quar--;
+ }
+ lock_quick_unlock(&alloc->super->lock);
+ if(p) {
+ alloc->special_allocated++;
+ return p;
+ }
+ }
+ /* allocate new */
+ prealloc(alloc);
+ if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
+ fatal_exit("alloc_special_obtain: out of memory");
+ alloc->special_allocated++;
+ return p;
+}
+
+/** push mem and some more items to the super */
+static void
+pushintosuper(struct alloc_cache* alloc, alloc_special_t* mem)
+{
+ int i;
+ alloc_special_t *p = alloc->quar;
+ log_assert(p);
+ log_assert(alloc && alloc->super &&
+ alloc->num_quar >= ALLOC_SPECIAL_MAX);
+ /* push ALLOC_SPECIAL_MAX/2 after mem */
+ alloc_special_next(mem) = alloc->quar;
+ for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) {
+ p = alloc_special_next(p);
+ }
+ alloc->quar = alloc_special_next(p);
+ alloc->num_quar -= ALLOC_SPECIAL_MAX/2;
+
+ lock_quick_lock(&alloc->super->lock);
+ alloc_special_next(p) = alloc->super->quar;
+ alloc->super->quar = mem;
+ alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1;
+ lock_quick_unlock(&alloc->super->lock);
+}
+
+void
+alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
+{
+ log_assert(alloc);
+ if(!mem)
+ return;
+ if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
+ /* push it to the super structure */
+ alloc->special_allocated --;
+ pushintosuper(alloc, mem);
+ return;
+ }
+
+ alloc_special_next(mem) = alloc->quar;
+ alloc->quar = mem;
+ alloc->num_quar++;
+ alloc->special_allocated--;
+}
+
+void
+alloc_stats(struct alloc_cache* alloc)
+{
+ log_info("%salloc: %d allocated, %d in cache.", alloc->super?"":"sup",
+ (int)alloc->special_allocated, (int)alloc->num_quar);
+}
* o Avoid locking costs of getting global lock to call malloc().
* o The packed rrset type needs to be kept on special freelists,
* so that they are reused for other packet rrset allocations.
- * o This service is not there to improve speed of malloc.
- * Some caching is performed to help avoid locking costs.
- * o This service does not prevent fragmentation.
- * The caching will help somewhat for this.
*
* Design choices:
* o The global malloc/free is used to handle fragmentation, etc.
#include "util/locks.h"
-/** all allocations are in multiples of this size */
-#define ALLOC_ALIGN 32 /* bytes */
-
-/**
- * This size and smaller is kept in cached lists by size.
- * Must be a multiple of ALLOC_ALIGN.
- */
-#define ALLOC_LARGESIZE 1024 /* bytes */
-
-/** number of bins */
-#define ALLOC_BINS (ALLOC_LARGESIZE / ALLOC_ALIGN)
-
/** The special type, packed rrset. Not allowed to be used for other memory */
typedef uint64_t alloc_special_t;
/** clean the special type. Pass pointer. */
#define alloc_special_clean(x) memset(x, 0, sizeof(alloc_special_t))
/** access next pointer. (in available spot). Pass pointer. */
-#define alloc_special_next(x) ((alloc_special_t*)(*(x)))
+#define alloc_special_next(x) (*((alloc_special_t**)(x)))
-/** The number of cached items. */
-#define ALLOC_CACHENUM 10 /* memory blocks */
+/** how many blocks to cache locally. */
+#define ALLOC_SPECIAL_MAX 10
-/** Preallocated per thread. This number for every size. */
-#define ALLOC_PREALLOC 32 /* memory blocks */
-
-/** shorthand */
-typedef struct alloc_cache alloc_t;
/**
- * Structure that provides caching based on size. Used one per thread.
+ * Structure that provides allocation. Use one per thread.
+ * The one on top has a NULL super pointer.
*/
struct alloc_cache {
+ /** lock, only used for the super. */
+ lock_quick_t lock;
/** global allocator above this one. NULL for none (malloc/free) */
- struct alloc_super* super;
- /** singly linked lists per size: [0]32, [1]64, ... [n-1]LARGESIZE */
- void* bins[ALLOC_BINS];
- /** the number of items per bin. */
- size_t nums[ALLOC_BINS];
+ struct alloc_cache* super;
/** singly linked lists of special type. These are free for use. */
alloc_special_t* quar;
/** number of items in quarantine. */
size_t num_quar;
- /* some statistics */
- /** amount allocated. */
- size_t bytes_allocated;
- /** number of items allocated. */
- size_t items_allocated;
- /** wasted space due to assigning oversized aligned blocks. */
- size_t internal_frag;
- /** amount in cache. */
- size_t bytes_in_bins;
/** number of special type allocated */
size_t special_allocated;
};
-/**
- * Structure with lock to provide global cache of special items and allocs.
- */
-struct alloc_super {
- /** lock for single access. */
- lock_quick_t lock;
- /** singly linked lists of special type. These are free for use. */
- alloc_special_t* quar;
- /** number of items in quarantine. */
- size_t num_quar;
-};
-
-/**
- * Init super alloc. (zeroes the struct, inits the lock).
- * @param super: to init.
- */
-void asuper_init(struct alloc_super* super);
-
/**
* Init alloc (zeroes the struct).
* @param alloc: this parameter is allocated by the caller.
* @param super: super to use (init that before with super_init).
*/
-void alloc_init(alloc_t* alloc, struct alloc_super* super);
+void alloc_init(struct alloc_cache* alloc, struct alloc_cache* super);
/**
* Free the alloc. Pushes all the cached items into the super structure.
+ * Or deletes them if super is NULL.
+ * Does not free the alloc struct itself.
* @param alloc: is almost zeroed on exit (except some stats).
*/
-void alloc_delete(alloc_t* alloc);
-
-/**
- * Allocate memory
- * @param alloc: where to alloc it.
- * @param size: how much.
- * @return: memory block. Will not return NULL (instead fatal_exit).
- */
-void* alloc_alloc(alloc_t* alloc, size_t size);
-
-/**
- * Free memory.
- * @param alloc: where to alloc it.
- * @param mem: block to free.
- */
-void alloc_free(alloc_t* alloc, void* mem);
+void alloc_delete(struct alloc_cache* alloc);
/**
* Get a new special_t element.
* @param alloc: where to alloc it.
* @return: memory block. Will not return NULL (instead fatal_exit).
*/
-alloc_special_t* alloc_special_alloc(alloc_t* alloc);
+alloc_special_t* alloc_special_obtain(struct alloc_cache* alloc);
/**
* Return special_t back to pool.
* @param alloc: where to alloc it.
* @param mem: block to free.
*/
-void alloc_special_free(alloc_t* alloc, alloc_special_t* mem);
+void alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem);
+/**
+ * Print debug information (statistics).
+ * @param alloc: on what alloc.
+ */
+void alloc_stats(struct alloc_cache* alloc);
#endif /* UTIL_ALLOC_H */