libkernaux/src/pfa.c

143 lines
3.6 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <kernaux/pfa.h>
#include <kernaux/stdlib.h>
static void KernAux_PFA_mark(
KernAux_PFA pfa,
bool status,
size_t start,
size_t end
)
__attribute__((nonnull));
void KernAux_PFA_initialize(const KernAux_PFA pfa)
{
kernaux_memset(pfa->flags, 0, sizeof(pfa->flags));
}
bool KernAux_PFA_is_available(const KernAux_PFA pfa, const size_t page_addr)
{
if (page_addr % KERNAUX_PFA_PAGE_SIZE != 0) return false;
const size_t page_index = page_addr / KERNAUX_PFA_PAGE_SIZE;
const size_t flag_index = page_index / 8;
const unsigned char flag_mask = 1 << (page_index % 8);
return pfa->flags[flag_index] & flag_mask;
}
void KernAux_PFA_mark_available(
const KernAux_PFA pfa,
const size_t start,
const size_t end
) {
KernAux_PFA_mark(pfa, true, start, end);
}
void KernAux_PFA_mark_unavailable(
const KernAux_PFA pfa,
const size_t start,
const size_t end
) {
KernAux_PFA_mark(pfa, false, start, end);
}
void KernAux_PFA_mark(
const KernAux_PFA pfa,
const bool status,
size_t start,
size_t end
) {
if (start >= end) return;
const size_t start_rem = start % KERNAUX_PFA_PAGE_SIZE;
const size_t end_rem = (end + 1) % KERNAUX_PFA_PAGE_SIZE;
if (start_rem != 0) {
start = start - start_rem + KERNAUX_PFA_PAGE_SIZE;
}
if (end_rem != 0) {
end = end - end_rem;
}
const size_t start_index = start / KERNAUX_PFA_PAGE_SIZE;
const size_t end_index = end / KERNAUX_PFA_PAGE_SIZE;
for (size_t index = start_index; index <= end_index; ++index) {
const size_t flag_index = index / 8;
const unsigned char flag_mask = 1 << (index % 8);
if (status) {
pfa->flags[flag_index] |= flag_mask;
} else {
pfa->flags[flag_index] &= ~flag_mask;
}
}
}
size_t KernAux_PFA_alloc_pages(const KernAux_PFA pfa, size_t mem_size)
{
const size_t mem_rem = mem_size % KERNAUX_PFA_PAGE_SIZE;
if (mem_rem != 0) {
mem_size = mem_size - mem_rem + KERNAUX_PFA_PAGE_SIZE;
}
const size_t pages_count = mem_size / KERNAUX_PFA_PAGE_SIZE;
// We start from 1 because 0 indicates failure.
// It is not very useful to alloc page at address 0;
for (size_t index = 1, start = 0;
index < KERNAUX_PFA_PAGES_COUNT_MAX;
++index)
{
const size_t flag_index = index / 8;
const unsigned char flag_mask = 1 << (index % 8);
if (!(pfa->flags[flag_index] & flag_mask)) {
start = 0;
continue;
}
if (start == 0) start = index;
if (index - start + 1 == pages_count) {
for (; index >= start; --index) {
const size_t flag_index = index / 8;
const unsigned char flag_mask = 1 << (index % 8);
pfa->flags[flag_index] &= ~flag_mask;
}
return start * KERNAUX_PFA_PAGE_SIZE;
}
}
return 0;
}
void KernAux_PFA_free_pages(
const KernAux_PFA pfa,
const size_t page_addr,
size_t mem_size
) {
if (page_addr % KERNAUX_PFA_PAGE_SIZE != 0) return;
const size_t mem_rem = mem_size % KERNAUX_PFA_PAGE_SIZE;
if (mem_rem != 0) {
mem_size = mem_size - mem_rem + KERNAUX_PFA_PAGE_SIZE;
}
const size_t start_index = page_addr / KERNAUX_PFA_PAGE_SIZE;
const size_t pages_count = mem_size / KERNAUX_PFA_PAGE_SIZE;
for (size_t index = 0; index < pages_count; ++index) {
const size_t flag_index = (start_index + index) / 8;
const unsigned char flag_mask = 1 << ((start_index + index) % 8);
pfa->flags[flag_index] |= flag_mask;
}
}