Files
MentOS/mentos/inc/mem/zone_allocator.h
T
Enrico Fraccaroli (Galfurian) 38ce67f426 Improve Zone Allocator comments.
2022-07-06 11:03:26 -04:00

217 lines
8.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// @file zone_allocator.h
/// @brief Implementation of the Zone Allocator
/// @copyright (c) 2014-2022 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#pragma once
#include "mem/gfp.h"
#include "math.h"
#include "stdint.h"
#include "klib/list_head.h"
#include "sys/bitops.h"
#include "klib/stdatomic.h"
#include "boot.h"
#include "mem/buddysystem.h"
#include "mem/slab.h"
#define page_count(p) atomic_read(&(p)->count) ///< Reads the page count.
#define set_page_count(p, v) atomic_set(&(p)->count, v) ///< Sets the page count.
#define page_inc(p) atomic_inc(&(p)->count) ///< Increments the counter for the given page.
#define page_dec(p) atomic_dec(&(p)->count) ///< Decrements the counter for the given page.
/// @brief Page descriptor. Use as a bitmap to understand the order of the block
/// and if it is free or allocated.
typedef struct page_t {
/// Array of flags encoding also the zone number to which the page frame
/// belongs.
unsigned long flags;
/// Page frames reference counter. 0 free, 1 used, 2+ copy on write
atomic_t count;
/// Buddy system page definition
bb_page_t bbpage;
/// Contains pointers to the slabs doubly linked list of pages.
list_head slabs;
// Slab allocator variables / Contains the total number of objects in this
//page, 0 if not managed by the slub
unsigned int slab_objcnt;
/// Tracks the number of free objects in the current page
unsigned int slab_objfree;
/// Holds the first free object (if slab_objfree is > 0)
list_head slab_freelist;
/// @brief This union can either contain the pointer to the slab main page
/// that handles this page, or the cache that contains it.
union {
/// Holds the slab page used to handle this memory region (root page)
struct page_t *slab_main_page;
/// Holds the slab cache pointer on the main page
kmem_cache_t *slab_cache;
} container;
} page_t;
/// @brief Enumeration for zone_t.
enum zone_type {
/// @brief Direct mapping. Used by the kernel.
/// @details
/// Normal addressable memory is in **ZONE_NORMAL**. DMA operations can be
/// performed on pages in **ZONE_NORMAL** if the DMA devices support
/// transfers to all addressable memory.
ZONE_NORMAL,
/// @brief Page tables mapping. Used by user processes.
/// @details
/// A memory area that is only addressable by the kernel through mapping
/// portions into its own address space. This is for example used by i386 to
/// allow the kernel to address the memory beyond 900MB. The kernel will set
/// up special mappings (page table entries on i386) for each page that the
/// kernel needs to access.
ZONE_HIGHMEM,
/// The maximum number of zones.
__MAX_NR_ZONES
};
/// @brief Data structure to differentiate memory zone.
typedef struct zone_t {
/// Number of free pages in the zone.
unsigned long free_pages;
/// Buddy system managing this zone
bb_instance_t buddy_system;
/// Pointer to first page descriptor of the zone.
page_t *zone_mem_map;
/// Index of the first page frame of the zone.
uint32_t zone_start_pfn;
/// Zone's name.
char *name;
/// Zone's size in number of pages.
unsigned long size;
} zone_t;
/// @brief Data structure to rapresent a memory node. In Uniform memory access
/// (UMA) architectures there is only one node called contig_page_data.
typedef struct pg_data_t {
/// Zones of the node.
zone_t node_zones[__MAX_NR_ZONES];
/// Number of zones in the node.
int nr_zones;
/// Array of pages of the node.
page_t *node_mem_map;
/// Physical address of the first page of the node.
unsigned long node_start_paddr;
/// Index on global mem_map for node_mem_map.
unsigned long node_start_mapnr;
/// Node's size in number of pages.
unsigned long node_size;
/// NID.
int node_id;
/// Next item in the memory node list.
struct pg_data_t *node_next;
} pg_data_t;
extern page_t *mem_map;
extern pg_data_t *contig_page_data;
/// @brief Find the nearest block's order of size greater than the amount of
/// byte.
/// @param base_addr The start address, used to handle extra page calculation in
/// case of not page aligned addresses.
/// @param amount The amount of byte which we want to calculate order.
/// @return The block's order greater and nearest than amount.
uint32_t find_nearest_order_greater(uint32_t base_addr, uint32_t amount);
/// @brief Physical memory manager initialization.
/// @param boot_info Information coming from the booloader.
/// @return Outcome of the operation.
int pmmngr_init(boot_info_t *boot_info);
/// @brief Alloc a single cached page.
/// @param gfp_mask The GetFreePage mask.
/// @return Pointer to the page.
page_t *alloc_page_cached(gfp_t gfp_mask);
/// @brief Free a page allocated with alloc_page_cached.
/// @param page Pointer to the page to free.
void free_page_cached(page_t *page);
/// @brief Find the first free page frame, set it allocated and return the
/// memory address of the page frame.
/// @param gfp_mask GFP_FLAGS to decide the zone allocation.
/// @return Memory address of the first free block.
uint32_t __alloc_page_lowmem(gfp_t gfp_mask);
/// @brief Frees the given page frame address.
/// @param addr The block address.
void free_page_lowmem(uint32_t addr);
/// @brief Find the first free 2^order amount of page frames, set it allocated
/// and return the memory address of the first page frame allocated.
/// @param gfp_mask GFP_FLAGS to decide the zone allocation.
/// @param order The logarithm of the size of the page frame.
/// @return Memory address of the first free page frame allocated.
uint32_t __alloc_pages_lowmem(gfp_t gfp_mask, uint32_t order);
/// @brief Find the first free 2^order amount of page frames, set it allocated
/// and return the memory address of the first page frame allocated.
/// @param gfp_mask GFP_FLAGS to decide the zone allocation.
/// @param order The logarithm of the size of the page frame.
/// @return Memory address of the first free page frame allocated.
page_t *_alloc_pages(gfp_t gfp_mask, uint32_t order);
/// @brief Get the start address of the corresponding page.
/// @param page A page structure.
/// @return The address that corresponds to the page.
uint32_t get_lowmem_address_from_page(page_t *page);
/// @brief Get the start physical address of the corresponding page.
/// @param page A page structure
/// @return The physical address that corresponds to the page.
uint32_t get_physical_address_from_page(page_t *page);
/// @brief Get the page from it's physical address.
/// @param phy_addr The physical address
/// @return The page that corresponds to the physical address.
page_t *get_page_from_physical_address(uint32_t phy_addr);
/// @brief Get the page that contains the specified address.
/// @param addr A phisical address.
/// @return The page that corresponds to the address.
page_t *get_lowmem_page_from_address(uint32_t addr);
/// @brief Frees from the given page frame address up to 2^order amount of page
/// frames.
/// @param addr The page frame address.
void free_pages_lowmem(uint32_t addr);
/// @brief Frees from the given page frame address up to 2^order amount of page
/// frames.
/// @param page The page.
void __free_pages(page_t *page);
/// @brief Returns the total space for the given zone.
/// @param gfp_mask GFP_FLAGS to decide the zone.
/// @return Total space of the given zone.
unsigned long get_zone_total_space(gfp_t gfp_mask);
/// @brief Returns the total free space for the given zone.
/// @param gfp_mask GFP_FLAGS to decide the zone.
/// @return Total free space of the given zone.
unsigned long get_zone_free_space(gfp_t gfp_mask);
/// @brief Returns the total cached space for the given zone.
/// @param gfp_mask GFP_FLAGS to decide the zone.
/// @return Total cached space of the given zone.
unsigned long get_zone_cached_space(gfp_t gfp_mask);
/// @brief Checks if the specified address points to a page_t (or field) that
/// belongs to lowmem.
/// @param addr The address to check.
/// @return 1 if it belongs to lowmem, 0 otherwise.
static inline int is_lowmem_page_struct(void *addr)
{
uint32_t start_lowm_map = (uint32_t)contig_page_data->node_zones[ZONE_NORMAL].zone_mem_map;
uint32_t lowmem_map_size = sizeof(page_t) * contig_page_data->node_zones[ZONE_NORMAL].size;
uint32_t map_index = (uint32_t)addr - start_lowm_map;
return map_index < lowmem_map_size;
}