64 lines
1.8 KiB
C
64 lines
1.8 KiB
C
// SPDX-License-Identifier: ISC
|
|
|
|
#include <zinnia/alloc.h>
|
|
#include <zinnia/atomic.h>
|
|
#include <zinnia/park.h>
|
|
#include <zinnia/virt.h>
|
|
|
|
struct page* vm_page_from_addr(phys_t addr) {
|
|
return &vm_pfndb[addr / arch_mem_page_size()];
|
|
}
|
|
|
|
errno_t page_alloc(enum alloc_flags flags, struct page** out) {
|
|
phys_t addr;
|
|
errno_t status = mem_phys_alloc(1, flags, &addr);
|
|
if (status)
|
|
return status;
|
|
|
|
struct page* page = vm_page_from_addr(addr);
|
|
page->type = PAGE_PHYS;
|
|
page->obj.owner = nullptr;
|
|
page->obj.index = 0;
|
|
atomic_store(&page->flags, 0, ATOMIC_RELAXED);
|
|
atomic_store(&page->holds, 1, ATOMIC_RELEASE);
|
|
*out = page;
|
|
return 0;
|
|
}
|
|
|
|
void page_hold(struct page* page) {
|
|
atomic_fetch_add(&page->holds, 1, ATOMIC_RELAXED);
|
|
}
|
|
|
|
void page_unhold(struct page* page) {
|
|
if (atomic_fetch_sub(&page->holds, 1, ATOMIC_RELEASE) != 1)
|
|
return;
|
|
atomic_fence(ATOMIC_ACQUIRE);
|
|
atomic_store(&page->flags, 0, ATOMIC_RELAXED);
|
|
page->obj.owner = nullptr;
|
|
mem_phys_free(vm_page_addr(page), 1);
|
|
}
|
|
|
|
bool page_try_busy(struct page* page) {
|
|
return !(atomic_fetch_or(&page->flags, PAGE_BUSY, ATOMIC_ACQUIRE) & PAGE_BUSY);
|
|
}
|
|
|
|
void page_unbusy(struct page* page) {
|
|
const uint32_t old = atomic_fetch_and(&page->flags, ~(PAGE_BUSY | PAGE_WANTED), ATOMIC_RELEASE);
|
|
if (old & PAGE_WANTED)
|
|
unpark_all(&page->flags, 0);
|
|
}
|
|
|
|
static bool still_busy(void* ctx) {
|
|
struct page* page = ctx;
|
|
uint32_t flags = atomic_load(&page->flags, ATOMIC_RELAXED);
|
|
while (flags & PAGE_BUSY) {
|
|
if (atomic_compare_exchange_weak(&page->flags, &flags, flags | PAGE_WANTED, ATOMIC_RELAXED, ATOMIC_RELAXED))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void page_wait(struct page* page) {
|
|
park(&page->flags, still_busy, page, 0);
|
|
}
|