#include "alloc.h" #include "kprintf.h" #include "memory.h" #include static void remove_used(Block *to_free) { Block *left = to_free->prev_used; Block *right = to_free->next_used; to_free->next_used = NULL; to_free->prev_used = NULL; if (!left && !right) { to_free->zone->used = NULL; return; } if (!left) to_free->zone->used = right; else left->next_used = right; if (right) right->prev_used = left; } /* * If all the blocks of the zone have been kfreed, * we can unmap the zone and delete it from the list of zones */ static int unmap_zone(Zone *zone) { int err = 0; block_type_t type = zone->type; Zone *left = zone->prev; Zone *right = zone->next; zone->prev = NULL; zone->next = NULL; if (!left && !right) { vzones[type] = NULL; goto unmap; } if (!left) vzones[type] = right; else left->next = right; if (right) right->prev = left; unmap: err = ufree_pages((void *)zone, zone->size); if (err) kprintf(KERN_ERR "error: munmap failed\n"); return (err); } /* * If the newly kfreed block is next to another previously * kfreed block, merge both of these and update the size */ static Block *merge_blocks(Block *left, Block *right) { if (right->next) right->next->prev = left; if (right->next_free) { right->next_free->prev_free = left; left->next_free = right->next_free; } left->next = right->next; left->size += right->size + sizeof(Block); return (left); } // Simply add the new block to the list of available blocks static int add_available(Block *available, Block *merged) { Zone *zone = available->zone; if (merged != zone->free && available != zone->free) available->next_free = zone->free; if (zone->free) zone->free->prev_free = available; zone->free = available; if (zone->type == LARGE) return (unmap_zone(zone)); return (0); } /* * ptr: pointer to kfree, if the pointer is invalid the kfree() * function will have an undefined behavior (most likely segfault) * * First, we remove the block from the list of in_use blocks * Then, we check if the block needs to be merged with another * neighboring block, if so we replace the previous block by the * newly merged block * Finally, we add the block to the list of available blocks */ void ufree(void *ptr) { if (ptr == NULL) return; Block *to_free = (Block *)((uint32_t)ptr - sizeof(Block)); Block *to_merge = NULL; to_free->in_use = false; remove_used(to_free); if (to_free->prev && !to_free->prev->in_use) { to_merge = to_free; to_free = merge_blocks(to_free->prev, to_free); } if (to_free->next && !to_free->next->in_use) { to_merge = to_free->next; to_free = merge_blocks(to_free, to_free->next); } int err = add_available(to_free, to_merge); if (err) kprintf(KERN_ERR "kfree: fatal error\n"); }