/* * ppm.c - Per-cluster Physical Pages Manager implementation * * Authors Ghassan Almaless (2008,2009,2010,2011,2012) * Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH.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-MKH.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-MKH.; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////// inline bool_t ppm_page_is_valid( page_t * page ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; uint32_t pgnr = (uint32_t)( page - ppm->pages_tbl ); return (pgnr <= ppm->pages_nr); } //////////////////////////////////////////// inline void * ppm_page2base( page_t * page ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; return (void*)((page - ppm->pages_tbl) << CONFIG_PPM_PAGE_SHIFT); } //////////////////////////////////////////// inline page_t * ppm_base2page( void * base ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; return (ppm->pages_tbl + (((uint32_t)base ) >> CONFIG_PPM_PAGE_SHIFT)); } ////////////////////////////////////////// inline ppn_t ppm_page2ppn( page_t * page ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; return (ppn_t)( page - ppm->pages_tbl ); } ///////////////////////////////////////// inline page_t * ppm_ppn2page( ppn_t ppn ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; return &ppm->pages_tbl[ppn]; } /////////////////////////////////////// inline void * ppm_ppn2base( ppn_t ppn ) { return (void*)( ppn << CONFIG_PPM_PAGE_SHIFT ); } //////////////////////////////////////// inline ppn_t ppm_base2ppn( void * base ) { return (ppn_t)( (uint32_t)base >> CONFIG_PPM_PAGE_SHIFT ); } ////////////////////////////////////////////////// static void ppm_free_pages_nolock( page_t * page ) { page_t * buddy; // searched buddy page descriptor uint32_t buddy_index; // buddy page index page_t * current; // current (merged) page descriptor uint32_t current_index; // current (merged) page index uint32_t current_order; // current (merget) page order ppm_t * ppm = &LOCAL_CLUSTER->ppm; page_t * pages_tbl = ppm->pages_tbl; // update released page descriptor flags page_set_flag( page , PG_FREE ); // search the buddy page descriptor // - merge with current page descriptor if found // - exit to release the current page descriptor if not found current = page , current_index = (uint32_t)(page - ppm->pages_tbl); for( current_order = page->order ; current_order < CONFIG_PPM_MAX_ORDER ; current_order++ ) { buddy_index = current_index ^ (1 << current_order); buddy = pages_tbl + buddy_index; if( !page_is_flag( buddy , PG_FREE ) || (buddy->order != current_order) ) break; // remove buddy from free list list_unlink( &buddy->list ); ppm->free_pages_nr[current_order] --; ppm->total_free_pages -= (1 << current_order); // merge buddy with current buddy->order = 0; current_index &= buddy_index; } // update merged page descriptor order current = pages_tbl + current_index; current->order = current_order; // insert current in free list list_add_first( &ppm->free_pages_root[current_order] , ¤t->list ); ppm->free_pages_nr[current_order] ++; ppm->total_free_pages += (1 << current_order); } // end ppm_free_pages_nolock() ////////////////////////////// void ppm_init( ppm_t * ppm, uint32_t pages_nr, // total pages number uint32_t pages_offset ) // occupied pages { uint32_t i; // set signature ppm->signature = PPM_SIGNATURE; // initialize lock protecting the free_pages[] array spinlock_init( &ppm->free_lock ); // initialize free_pages[] array as empty ppm->total_free_pages = 0; for( i = 0 ; i < CONFIG_PPM_MAX_ORDER ; i++ ) { list_root_init( &ppm->free_pages_root[i] ); ppm->free_pages_nr[i] = 0; } // initialize dirty_list as empty list_root_init( &ppm->dirty_root ); // initialize pointer on page descriptors array ppm->pages_tbl = (page_t*)( pages_offset << CONFIG_PPM_PAGE_SHIFT ); // compute size of pages descriptor array rounded to an integer number of pages uint32_t bytes = ARROUND_UP( pages_nr * sizeof(page_t), CONFIG_PPM_PAGE_SIZE ); // compute number of pages required to store page descriptor array uint32_t pages_array = bytes >> CONFIG_PPM_PAGE_SHIFT; // compute total number of reserved pages (kernel code & pages_tbl[]) uint32_t reserved_pages = pages_offset + pages_array; // set pages numbers ppm->pages_nr = pages_nr; ppm->pages_offset = reserved_pages; // initialises all page descriptors in pages_tbl[] for( i = 0 ; i < pages_nr ; i++ ) { page_init( &ppm->pages_tbl[i] ); // TODO optimisation : make only a partial init [AG] // complete the initialisation when page is allocated [AG] // ppm->pages_tbl[i].flags = 0; } // - set PG_RESERVED flag for reserved pages (kernel code & pages_tbl[]) // - release all other pages to populate the free lists for( i = 0 ; i < reserved_pages ; i++) { page_set_flag( &ppm->pages_tbl[i] , PG_RESERVED ); } for( i = reserved_pages ; i < pages_nr ; i++ ) { ppm_free_pages_nolock( &ppm->pages_tbl[i] ); // TODO optimisation : decompose this enormous set of small pages // to a set big pages with various order values } // check consistency ppm_assert_order( ppm ); } // end ppm_init() //////////////////////////////////////////// page_t * ppm_alloc_pages( uint32_t order ) { uint32_t current_order; page_t * remaining_block; uint32_t current_size; ppm_t * ppm = &LOCAL_CLUSTER->ppm; assert( (ppm->signature == PPM_SIGNATURE) , __FUNCTION__ , "PPM non initialised" ); assert( (order < CONFIG_PPM_MAX_ORDER) , __FUNCTION__ , "illegal order argument" ); page_t * block = NULL; ppm_dmsg("\n[INFO] %s : enters / order = %d\n", __FUNCTION__ , order ); #if( CONFIG_PPM_DEBUG ) ppm_print( ppm , "before allocation" ); #endif // take lock protecting free lists spinlock_lock( &ppm->free_lock ); // find a free block equal or larger to requested size for( current_order = order ; current_order < CONFIG_PPM_MAX_ORDER ; current_order ++ ) { if( !list_is_empty( &ppm->free_pages_root[current_order] ) ) { block = LIST_FIRST( &ppm->free_pages_root[current_order] , page_t , list ); list_unlink( &block->list ); break; } } if( block == NULL ) // return failure { // release lock protecting free lists spinlock_unlock( &ppm->free_lock ); return NULL; } // update free-lists after removing a block ppm->total_free_pages -= (1 << current_order); ppm->free_pages_nr[current_order] --; current_size = (1 << current_order); // split the removed block in smaller sub-blocks if required // and update the free-lists accordingly while( current_order > order ) { current_order --; current_size >>= 1; remaining_block = block + current_size; remaining_block->order = current_order; list_add_first( &ppm->free_pages_root[current_order] , &remaining_block->list ); ppm->free_pages_nr[current_order] ++; ppm->total_free_pages += (1 << current_order); } // update page descriptor page_clear_flag( block , PG_FREE ); page_refcount_up( block ); block->order = order; // release lock protecting free lists spinlock_unlock( &ppm->free_lock ); ppm_dmsg("\n[INFO] %s : base = %x / order = %d\n", __FUNCTION__ , (uint32_t)ppm_page2base( block ) , order ); #if CONFIG_PPM_DEBUG ppm_print( ppm , "after allocation" ); #endif return block; } // end pmm_alloc-pages() //////////////////////////////////// void ppm_free_pages( page_t * page ) { ppm_t * ppm = &LOCAL_CLUSTER->ppm; // get lock protecting free_pages[] array spinlock_lock( &ppm->free_lock ); ppm_free_pages_nolock( page ); // release lock protecting free_pages[] array spinlock_unlock( &ppm->free_lock ); } //////////////////////////// void ppm_print( ppm_t * ppm, char * string ) { uint32_t order; list_entry_t * iter; page_t * page; // get lock protecting free lists spinlock_lock( &ppm->free_lock ); printk("\n*** PPM state in cluster %x %s : pages = %d / offset = %d / free = %d ***\n", local_cxy , string , ppm->pages_nr , ppm->pages_offset , ppm->total_free_pages ); for( order = 0 ; order < CONFIG_PPM_MAX_ORDER ; order++ ) { printk("- order = %d / free_pages = %d [", order , ppm->free_pages_nr[order] ); LIST_FOREACH( &ppm->free_pages_root[order] , iter ) { page = LIST_ELEMENT( iter , page_t , list ); printk("%d," , page - ppm->pages_tbl ); } printk("]\n", NULL ); } // release lock protecting free lists spinlock_unlock( &ppm->free_lock ); } // end ppm_print() //////////////////////////u///////// void ppm_assert_order( ppm_t * ppm ) { uint32_t order; list_entry_t * iter; page_t * page; for(order=0; order < CONFIG_PPM_MAX_ORDER; order++) { if( list_is_empty( &ppm->free_pages_root[order] ) ) continue; LIST_FOREACH( &ppm->free_pages_root[order] , iter ) { page = LIST_ELEMENT( iter , page_t , list ); assert( (page->order == order) , __FUNCTION__ , "PPM inconsistency" ); } } } // end ppm_assert_order()