// return next meta, if any meta_t* _slab_free_erase(meta_t* meta) { meta_t *next, *prev; if (!meta) { return NULL; } // prev and next free link if (meta->free_prev > 0) { prev = META(meta, meta->free_prev); } else { prev = NULL; } if (meta->free_next > 0) { next = META(meta, meta->free_next); } else { next = NULL; } // relink if (next) { next->free_prev = prev ? META_SHIFT(prev) : -1; } if (prev) { prev->free_next = next ? META_SHIFT(next) : -1; } // erase self link meta->free_prev = -1; meta->free_next = -1; return next; }
void _slab_free_insert(meta_t* meta, meta_t* insert) { meta_t* next; if (!meta || !insert) return; if (meta->free_next > 0) { next = META(meta, meta->free_next); insert->free_next = META_SHIFT(next); next->free_prev = META_SHIFT(insert); } insert->free_prev = META_SHIFT(meta); meta->free_next = META_SHIFT(insert); }
void* _slab_alloc(page_t* page, list_head_t* head, size_t sz) { int16_t shift; meta_t* meta, *next, *split; // no free memory if (page->free <= 0 || page->remain < (int)sz + (int)META_SIZE) { return NULL; } // loop free memory shift = page->free; while (shift > 0) { meta = PAGE_META(page, shift); shift = meta->free_next; // not enough, ignore if (meta->size < (int)sz) { meta = NULL; continue; } // just fit, erase from free list else if (meta->size <= (int)(sz + META_SIZE)) { next = _slab_free_erase(meta); // erase free-link head if (META_SHIFT(meta) == page->free) { page->free = next ? META_SHIFT(next) : -1; } meta->color |= META_COLOR_ALLOC; break; } // do split else { split = _slab_free_split(meta, sz); _slab_free_replace(meta, split); if (META_SHIFT(meta) == page->free) { page->free = META_SHIFT(split); } meta->color |= META_COLOR_ALLOC; break; } } if (meta) { page->remain -= (META_SIZE + meta->size); _slab_page_erase(page, head); return META_MEM(meta); } return NULL; }
static void _slab_free_replace(meta_t* meta, meta_t* replace) { if (!meta || !replace) { return; } if (meta->free_prev > 0) { meta_t* prev = META(meta, meta->free_prev); prev->free_next = META_SHIFT(replace); replace->free_prev = META_SHIFT(prev); } else { replace->free_prev = -1; } if (meta->free_next > 0) { meta_t* next = META(meta, meta->free_next); next->free_prev = META_SHIFT(replace); replace->free_next = META_SHIFT(next); } else { replace->free_next = -1; } }
// return next meta, if any static meta_t* _slab_free_erase(meta_t* meta) { if (!meta) { return NULL; } // prev and next free link meta_t* prev = (meta->free_prev > 0) ? META(meta, meta->free_prev) : NULL; meta_t* next = (meta->free_next > 0) ? META(meta, meta->free_next) : NULL; // relink if (next) { next->free_prev = prev ? META_SHIFT(prev) : -1; } if (prev) { prev->free_next = next ? META_SHIFT(next) : -1; } // erase self link meta->free_prev = -1; meta->free_next = -1; return next; }
// check next meta whether free, if free, then do quick merge // replace_next == 0 means meta will replace next in free link // return 0 means do quick merge success int _slab_free_quick_merge(meta_t* meta, int replace_next) { meta_t* next, *prev; page_t* page = META_PAGE(meta); int shift = META_SHIFT(meta) + META_SIZE + meta->size; if (shift < getpagesize()) { next = META(meta, shift); if (!(next->color & META_COLOR_ALLOC)) { prev = next->free_prev > 0 ? PAGE_META(page, next->free_prev) : NULL; // quick merge with next meta->size += next->size + META_SIZE; if (replace_next == 0) { _slab_free_replace(next, meta); if (META_SHIFT(next) == page->free) { page->free = META_SHIFT(meta); } } // quick merge with prev if (prev && META_SHIFT(prev) + META_SIZE + prev->size == META_SHIFT(meta)) { prev->size += META_SIZE + meta->size; prev->free_next = meta->free_next; next = meta->free_next > 0 ? PAGE_META(page, meta->free_next) : NULL; if (next) { next->free_prev = META_SHIFT(prev); } } return 0; } } return -1; }