117 lines
2.7 KiB
C
117 lines
2.7 KiB
C
#include "alloc.h"
|
|
#include "kprintf.h"
|
|
#include "memory.h"
|
|
#include <stdint.h>
|
|
|
|
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");
|
|
}
|