/* * ppm.h - Per-cluster Physical Pages Manager definition. * * Authors Ghassan Almaless (2008,2009,2010,2011,2012) * Alain Greiner (2016,2017,2018,2019) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-kernel is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-kernel is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-kernel; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PPM_H_ #define _PPM_H_ #include #include #include #include #include #include /***************************************************************************************** * This structure defines the Physical Pages Manager in a cluster. * In each cluster, the physical memory bank starts at local physical address 0 and * contains an integer number of small pages, defined by the field in the * boot_info structure. It is split in three parts: * * - the "kernel_code" section contains the kernel code, loaded by the boot-loader. * It starts at local PPN = 0 and the size is defined by the field * in the boot_info structure. * - the local "pages_tbl" section contains the physical page descriptors array. * It starts at local PPN = pages_offset, and it contains one entry per small page. * It is created and initialized by the hal_ppm_create() function. * - The "kernel_heap" section contains all physical pages that are are not in the * "kernel_code" and "pages_tbl" sections, and that have not been reserved. * The reserved pages are defined in the boot_info structure. * * The main service provided by the PMM is the dynamic allocation of physical pages * from the "kernel_heap" section. This low-level allocator implements the "buddy" * algorithm: an allocated block is an integer number n of small pages, where n * is a power of 2, and ln(n) is called order. The free_pages_root[] array contains * the roots ot the local lists of free pages for different sizes, as required by * the "buddy" algorithm. * The local threads can access these free_lists by calling the ppm_alloc_pages() and * ppm_free_page() functions, but the remote threads can access the same free lists, * by calling the ppm_remote_alloc_pages() and ppm_remote_free_pages functions. * Therefore, these free lists are protected by a remote_busy_lock. * * Another service is to register the dirty pages in a specific local dirty_list, * also rooted in the PPM, in order to be able to synchronize all dirty pages on disk. * This dirty list is protected by a specific remote_queuelock, because it can be * modified by a remote thread. ****************************************************************************************/ typedef struct ppm_s { remote_busylock_t free_lock; /*! lock protecting free_pages[] lists */ list_entry_t free_pages_root[CONFIG_PPM_MAX_ORDER]; /*! roots of free lists */ uint32_t free_pages_nr[CONFIG_PPM_MAX_ORDER]; /*! free pages number */ page_t * pages_tbl; /*! pointer on page descriptors array */ uint32_t pages_nr; /*! total number of small physical page */ remote_queuelock_t dirty_lock; /*! lock protecting dirty pages list */ list_entry_t dirty_root; /*! root of dirty pages list */ void * vaddr_base; /*! pointer on local physical memory base */ } ppm_t; /************** functions to allocate / release physical pages *************************/ /***************************************************************************************** * This local allocator must be called by a thread running in local cluster. * It allocates n contiguous physical 4 Kbytes pages from the local cluster, where * n is a power of 2 defined by the argument. * In normal use, it should not be called directly, as the recommended way to allocate * physical pages is to call the generic allocator defined in kmem.h. ***************************************************************************************** * @ order : ln2( number of 4 Kbytes pages) * @ returns a local pointer on the page descriptor if success / NULL if error. ****************************************************************************************/ page_t * ppm_alloc_pages( uint32_t order ); /***************************************************************************************** * This function must be called by a thread running in local cluster to release * physical pages. It takes the lock protecting the free_lists before register the * released page in the relevant free_list. * In normal use, you do not need to call it directly, as the recommended way to free * physical pages is to call the generic allocator defined in kmem.h. ***************************************************************************************** * @ page : local pointer on the page descriptor to be released ****************************************************************************************/ void ppm_free_pages( page_t * page ); /***************************************************************************************** * This function does the same as the ppm_free_page() function, without taking the lock. * It is used by the hal_ppm_init() function to initialize the pages_tbl[] array, when * there is no concurrent access issue. ***************************************************************************************** * @ page : local pointer on the page descriptor to be released ****************************************************************************************/ void ppm_free_pages_nolock( page_t * page ); /***************************************************************************************** * This remote allocator can be called by any thread running in any cluster. * It allocates n contiguous physical 4 Kbytes pages from cluster identified * by the argument, where n is a power of 2 defined by the argument. * In normal use, it should not be called directly, as the recommended way to allocate * physical pages is to call the generic allocator defined in kmem.h. ***************************************************************************************** * @ cxy : remote cluster identifier. * @ order : ln2( number of 4 Kbytes pages) * @ returns a local pointer on remote page descriptor if success / XPTR_NULL if error. ****************************************************************************************/ void * ppm_remote_alloc_pages( cxy_t cxy, uint32_t order ); /***************************************************************************************** * This function can be called by any thread running in any cluster to release physical * pages to a remote cluster. It takes the lock protecting the free_list before register * the released page in the relevant free_list. * In normal use, you do not need to call it directly, as the recommended way to free * physical pages is to call the generic allocator defined in kmem.h. ***************************************************************************************** * @ cxy : remote cluster identifier. * @ page : local pointer on the page descriptor to be released in remote cluster. ****************************************************************************************/ void ppm_remote_free_pages( cxy_t cxy, page_t * page ); /***************************************************************************************** * This debug function can be called by any thread running in any cluster to display * the current PPM state of a remote cluster. ***************************************************************************************** * @ cxy : remote cluster identifier. ****************************************************************************************/ void ppm_remote_display( cxy_t cxy ); /************** functions to translate [ page <-> base <-> ppn ] ***********************/ /***************************************************************************************** * Get extended pointer on page base from extended pointer on page descriptor. ***************************************************************************************** * @ page_xp : extended pointer to page descriptor * @ returns extended pointer on page base. ****************************************************************************************/ inline xptr_t ppm_page2base( xptr_t page_xp ); /***************************************************************************************** * Get extended pointer on page descriptor from extended pointer on page base. ***************************************************************************************** * @ base_xp : extended pointer to page base. * @ returns extended pointer on page descriptor ****************************************************************************************/ inline xptr_t ppm_base2page( xptr_t base_xp ); /***************************************************************************************** * Get extended pointer on page base from global PPN. ***************************************************************************************** * @ ppn : global physical page number. * @ returns extended pointer on page base. ****************************************************************************************/ inline xptr_t ppm_ppn2base( ppn_t ppn ); /***************************************************************************************** * Get global PPN from extended pointer on page base. ***************************************************************************************** * @ base_xp : extended pointer to page base. * @ returns global physical page number. ****************************************************************************************/ inline ppn_t ppm_base2ppn( xptr_t base_xp ); /***************************************************************************************** * Get global PPN from extended pointer on page descriptor. ***************************************************************************************** * @ page_xp : pointer to page descriptor * @ returns global physical page number. ****************************************************************************************/ inline ppn_t ppm_page2ppn( xptr_t page_xp ); /***************************************************************************************** * Get extended pointer on page descriptor from global PPN. ***************************************************************************************** * @ ppn : global physical page number * @ returns extended pointer on page descriptor. ****************************************************************************************/ inline xptr_t ppm_ppn2page( ppn_t ppn ); /*********** debug functions **********************************************************/ /***************************************************************************************** * This function can be called by any thread running in any cluster. * It displays the PPM allocator status in cluster identified by the argument. ***************************************************************************************** * @ cxy : remote cluster ****************************************************************************************/ void ppm_remote_display( cxy_t cxy ); /***************************************************************************************** * This function must be called by a thread running in local cluster. * It checks the consistency of the local PPM allocator. ***************************************************************************************** * @ return 0 if PPM is OK / return -1 if PPM not consistent. ****************************************************************************************/ error_t ppm_assert_order( void ); /*********** functions to handle dirty pages *******************************************/ /***************************************************************************************** * This function registers a page identified by the argument as dirty. * It can be called by a thread running in any cluster. * - it takes the queuelock protecting the PPM dirty_list. * - it takes the busylock protecting the page flags. * - it test the PG_DIRTY flag in the page descriptor. * . if page already dirty => do nothing * . it page not dirty => set the PG_DIRTY flag and register page in PPM dirty list. * - it releases the busylock protecting the page flags. * - it releases the queuelock protecting the PPM dirty_list. ***************************************************************************************** * @ page_xp : extended pointer on page descriptor. * @ returns true if page was not dirty / returns false if page was dirty ****************************************************************************************/ bool_t ppm_page_do_dirty( xptr_t page_xp ); /***************************************************************************************** * This function unregisters a page identified by the argument as dirty. * It can be called by a thread running in any cluster. * - it takes the queuelock protecting the PPM dirty_list. * - it takes the busylock protecting the page flags. * - it test the PG_DIRTY flag in the page descriptor. * . if page not dirty => do nothing * . it page dirty => reset the PG_DIRTY flag and remove page from PPM dirty list. * - it releases the busylock protecting the page flags. * - it releases the queuelock protecting the PPM dirty_list. ***************************************************************************************** * @ page_xp : extended pointer on page descriptor. * @ returns true if page was dirty / returns false if page was not dirty ****************************************************************************************/ bool_t ppm_page_undo_dirty( xptr_t page_xp ); /***************************************************************************************** * This function synchronizes (i.e. update the IOC device) all dirty pages in a cluster. * - it takes the queuelock protecting the PPM dirty_list. * - it scans the PPM dirty list, and for each page: * . it takes the lock protecting the page. * . it removes the page from the PPM dirty_list. * . it reset the PG_DIRTY flag. * . it releases the lock protecting the page. * - it releases the queuelock protecting the PPM dirty_list. $ The PPM dirty_list is empty when the sync operation completes. ****************************************************************************************/ void ppm_sync_dirty_pages( void ); #endif /* _PPM_H_ */