// SPDX-License-Identifier: ISC #include #include #include #include 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); }