Add comments
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/* === BIT MAGIC ========================================================
|
||||||
* Bit stuff required to understand the code:
|
* Bit stuff required to understand the code:
|
||||||
*
|
*
|
||||||
* 1. Division and multiplication using shifts
|
* 1. Division and multiplication using shifts
|
||||||
@@ -149,6 +149,119 @@
|
|||||||
* using a bitwise and:
|
* using a bitwise and:
|
||||||
*
|
*
|
||||||
* delta_from_next_boundart = -x & (boundary - 1)
|
* delta_from_next_boundart = -x & (boundary - 1)
|
||||||
|
*
|
||||||
|
* === THE BIT TREE =====================================
|
||||||
|
* The state of each block is tracked by one bit. If the
|
||||||
|
* bit is set, the block is allocated else it is free.
|
||||||
|
* This is necessary to catch any invalid free operations
|
||||||
|
* the user may perform.
|
||||||
|
*
|
||||||
|
* Blocks are caracterized by their address and length,
|
||||||
|
* so for instance if we consider the block at address P
|
||||||
|
* of size N and the block at the same address P but size
|
||||||
|
* 2N, these two are considered different and therefore
|
||||||
|
* each has its own state bit.
|
||||||
|
*
|
||||||
|
* It is possible to organize blocks in a binary tree
|
||||||
|
* structure. Since each bit is associated to one and only
|
||||||
|
* one block, the same goes for the bits. This allocator
|
||||||
|
* stores the tree of bits breadth first in the page_info
|
||||||
|
* structures. Each page_info structure holds all the bits
|
||||||
|
* necessary to keep track of the splits of one block with
|
||||||
|
* the maximum size.
|
||||||
|
*
|
||||||
|
* If this tree thing isn't clear, here is an example:
|
||||||
|
*
|
||||||
|
* Lets say the allocator is configured to handle 2 blocks
|
||||||
|
* of 1024 that can be split up to 2 times:
|
||||||
|
*
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* | 1024 | 1024 |
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
*
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* | 512 | 512 | 512 | 512 |
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
*
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
* | 256 | 256 | 256 | 256 | 256 | 256 | 256 | 256 |
|
||||||
|
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
|
*
|
||||||
|
* Don't see the tree yet?
|
||||||
|
*
|
||||||
|
* 1024 1024
|
||||||
|
* / \ / \
|
||||||
|
* 512 512 512 512
|
||||||
|
* / \ / \ / \ / \
|
||||||
|
* 256 256 256 256 256 256 256 256
|
||||||
|
*
|
||||||
|
* Now about the tree of bits..
|
||||||
|
*
|
||||||
|
* The bits are serialized this way:
|
||||||
|
*
|
||||||
|
* index size
|
||||||
|
*
|
||||||
|
* 1 1K
|
||||||
|
* 2 512
|
||||||
|
* 3 512
|
||||||
|
* 4 256
|
||||||
|
* 5 256
|
||||||
|
* 6 256
|
||||||
|
* 7 256
|
||||||
|
*
|
||||||
|
* 1 1K
|
||||||
|
* 2 512
|
||||||
|
* 3 512
|
||||||
|
* 4 256
|
||||||
|
* 5 256
|
||||||
|
* 6 256
|
||||||
|
* 7 256
|
||||||
|
*
|
||||||
|
* The group of bits of a block are stored breadth first,
|
||||||
|
* while the groups themselves are stored linearly.
|
||||||
|
*
|
||||||
|
* I added 1-based indices within each group to show how
|
||||||
|
* the first chunk of its size class is always located at
|
||||||
|
* an index that's a power of 2:
|
||||||
|
*
|
||||||
|
* 1K -> 2^0
|
||||||
|
* 512 -> 2^1
|
||||||
|
* 256 -> 2^2
|
||||||
|
*
|
||||||
|
* But the block sizes are also powers of 2:
|
||||||
|
*
|
||||||
|
* 2^10 -> 2^0
|
||||||
|
* 2^(10 - 1) -> 2^1
|
||||||
|
* 2^(10 - 2) -> 2^2
|
||||||
|
*
|
||||||
|
* In general the first block of size 2^(10-i) is associated
|
||||||
|
* to the bit at index 2^i of its group. The 10 is there
|
||||||
|
* because its the maximum block size log2. By generalizing
|
||||||
|
* the maximum block size we get this:
|
||||||
|
*
|
||||||
|
* 2^(max_block_size_log2 - N) -> 2^N
|
||||||
|
*
|
||||||
|
* But this only brings us half way, because it gets us the
|
||||||
|
* bit of the first block of the given size, but not the one
|
||||||
|
* we need!
|
||||||
|
*
|
||||||
|
* The bits of a size class are stored linearly so we just
|
||||||
|
* need to add the index of the block relative to the start
|
||||||
|
* of the memory pool. If we are looking for the bit for the
|
||||||
|
* block at address P of size 2^(max_block_size_log2 - N),
|
||||||
|
* and the pool starts at address B, then the block index is:
|
||||||
|
*
|
||||||
|
* (P - B) / 2^(max_block_size_log2 - N)
|
||||||
|
*
|
||||||
|
* So the index of the bit within the group is:
|
||||||
|
*
|
||||||
|
* 2^(max_block_size_log2 - N) -> 2^N + (P - B) / 2^(max_block_size_log2 - N)
|
||||||
|
*
|
||||||
|
* Since every value here is a power of 2, all divisions,
|
||||||
|
* logarithms and powers can be evaluated as shifts:
|
||||||
|
*
|
||||||
|
* 1 << (max_block_size_log2 - N) -> (1 << N) + ((P - B) >> (max_block_size_log2 - N))
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -158,7 +271,7 @@
|
|||||||
#include "buddy.h"
|
#include "buddy.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are just for convenience
|
* Just for convenience
|
||||||
*/
|
*/
|
||||||
#define MAX_BLOCK_LOG2 BUDDY_ALLOC_MAX_BLOCK_LOG2
|
#define MAX_BLOCK_LOG2 BUDDY_ALLOC_MAX_BLOCK_LOG2
|
||||||
#define MIN_BLOCK_LOG2 BUDDY_ALLOC_MIN_BLOCK_LOG2
|
#define MIN_BLOCK_LOG2 BUDDY_ALLOC_MIN_BLOCK_LOG2
|
||||||
@@ -407,6 +520,12 @@ static size_t block_info_index(void *ptr, size_t len)
|
|||||||
return (1U << (MAX_BLOCK_LOG2 - len_log2)) + (reloff >> len_log2);
|
return (1U << (MAX_BLOCK_LOG2 - len_log2)) + (reloff >> len_log2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function checks wether the block (ptr, len)
|
||||||
|
* was marked as allocated.
|
||||||
|
*
|
||||||
|
* See the set_allocated function
|
||||||
|
*/
|
||||||
static bool is_allocated(struct buddy_alloc *alloc,
|
static bool is_allocated(struct buddy_alloc *alloc,
|
||||||
void *ptr, size_t len)
|
void *ptr, size_t len)
|
||||||
{
|
{
|
||||||
@@ -426,6 +545,16 @@ static bool is_allocated(struct buddy_alloc *alloc,
|
|||||||
return (alloc->info[i].bits[u] & mask) == mask;
|
return (alloc->info[i].bits[u] & mask) == mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function marks the block (ptr, len) as allocated.
|
||||||
|
* Note that a block is considered to be different from
|
||||||
|
* its splits. For instance when the block (ptr, len/2)
|
||||||
|
* is marked as allocated, the block (ptr, len) isn't.
|
||||||
|
*
|
||||||
|
* For more info about how allocation state is tracked,
|
||||||
|
* refer to the explanation THE BIT TREE at the start of
|
||||||
|
* the file.
|
||||||
|
*/
|
||||||
static void set_allocated(struct buddy_alloc *alloc,
|
static void set_allocated(struct buddy_alloc *alloc,
|
||||||
void *ptr, size_t len, bool value)
|
void *ptr, size_t len, bool value)
|
||||||
{
|
{
|
||||||
@@ -450,6 +579,10 @@ static void set_allocated(struct buddy_alloc *alloc,
|
|||||||
alloc->info[i].bits[u] &= ~mask;
|
alloc->info[i].bits[u] &= ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function returns true if the block (ptr, len)
|
||||||
|
* or any of its splits are marked as allocated.
|
||||||
|
*/
|
||||||
static bool
|
static bool
|
||||||
is_allocated_considering_splits(struct buddy_alloc *alloc,
|
is_allocated_considering_splits(struct buddy_alloc *alloc,
|
||||||
void *ptr, size_t len)
|
void *ptr, size_t len)
|
||||||
|
|||||||
@@ -115,6 +115,15 @@ void *buddy_malloc(struct buddy_alloc *alloc, size_t len);
|
|||||||
*/
|
*/
|
||||||
void buddy_free(struct buddy_alloc *alloc, size_t len, void *ptr);
|
void buddy_free(struct buddy_alloc *alloc, size_t len, void *ptr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if and only if ptr points inside of the memory
|
||||||
|
* generally available for allocation (even if currently marked
|
||||||
|
* as allocated).
|
||||||
|
*/
|
||||||
bool buddy_owned(struct buddy_alloc *alloc, void *ptr);
|
bool buddy_owned(struct buddy_alloc *alloc, void *ptr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if and only if the block at address ptr of size
|
||||||
|
* len is owned by the allocator and marked as allocated.
|
||||||
|
*/
|
||||||
bool buddy_allocated(struct buddy_alloc *alloc, void *ptr, size_t len);
|
bool buddy_allocated(struct buddy_alloc *alloc, void *ptr, size_t len);
|
||||||
Reference in New Issue
Block a user