source: trunk/kernel/mm/kcm.c @ 348

Last change on this file since 348 was 315, checked in by alain, 7 years ago

Redefine the fuctions ppm_base2page() / ppm_page2base() / ppm_page2ppn() / ppm_ppn2page() / ppm_base2ppn() / ppm_ppn2base(),
to use explicitely extended pointers.

File size: 10.3 KB
RevLine 
[1]1/*
2 * kcm.c - Per cluster & per type Kernel Cache Manager access functions
[18]3 *
[1]4 * Author  Ghassan Almaless (2008,2009,2010,2011,2012)
[50]5 *         Alain Greiner    (2016,2017)
[1]6 *
7 * Copyright (c) UPMC Sorbonne Universites
8 *
9 * This file is part of ALMOS-MKH.
10 *
11 * ALMOS-MKH is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.0 of the License.
14 *
15 * ALMOS-MKH is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
[14]25#include <kernel_config.h>
[1]26#include <hal_types.h>
27#include <hal_special.h>
28#include <list.h>
29#include <printk.h>
30#include <bits.h>
31#include <ppm.h>
32#include <thread.h>
33#include <page.h>
34#include <cluster.h>
[7]35#include <kmem.h>
[1]36#include <kcm.h>
37
38//////////////////////////////////////////////////////////////////////////////////////
[7]39// This static function returns pointer on an allocated block from an active page.
[1]40// It returns NULL if no block available in selected page.
41// It changes the page status if required.
42//////////////////////////////////////////////////////////////////////////////////////
[50]43// @ kcm      : pointer on kcm allocator.
44// @ kcm_page : pointer on active kcm page to use.
[7]45/////////////////////////////////////////////////////////////////////////////////////
46static void * kcm_get_block( kcm_t      * kcm,
[50]47                             kcm_page_t * kcm_page )
[1]48{
[50]49        kcm_dmsg("\n[INFO] %s : enters for %s / page %x / count = %d / active = %d\n",
50                 __FUNCTION__ , kmem_type_str( kcm->type ) ,
[161]51                 (intptr_t)kcm_page , kcm_page->count , kcm_page->active );
[1]52
[50]53        assert( kcm_page->active , __FUNCTION__ , "kcm_page should be active" );
54
[20]55        // get first block available
[50]56        int32_t index = bitmap_ffs( kcm_page->bitmap , kcm->blocks_nr );
[1]57
[50]58        assert( (index != -1) , __FUNCTION__ , "kcm_page should not be full" );
[18]59
[20]60        // allocate block
[50]61        bitmap_clear( kcm_page->bitmap , index );
[7]62
[50]63        // increase kcm_page count
64        kcm_page->count ++;
[1]65
[50]66        // change the kcm_page to busy if no more free block in page
67        if( kcm_page->count >= kcm->blocks_nr )
[20]68        {
[161]69                kcm_page->active = 0;
[50]70                list_unlink( &kcm_page->list);
[1]71                kcm->active_pages_nr --;
72
[50]73                list_add_first( &kcm->busy_root , &kcm_page->list);
[1]74                kcm->busy_pages_nr ++;
[50]75                kcm_page->busy = 1;
[20]76        }
[1]77
[161]78        // compute return pointer
79        void * ptr = (void *)((intptr_t)kcm_page + CONFIG_KCM_SLOT_SIZE
80                     + (index * kcm->block_size) );
[1]81
[65]82        kcm_dmsg("\n[INFO] %s : allocated one block  %s / ptr = %p / page = %x / count = %d\n",
83                 __FUNCTION__ , kmem_type_str( kcm->type ) , ptr ,
[161]84                 (intptr_t)kcm_page , kcm_page->count );
[50]85
86        return ptr;
[161]87}
[50]88
[1]89/////////////////////////////////////////////////////////////////////////////////////
90// This static function releases a previously allocated block.
[50]91// It changes the kcm_page status if required.
[1]92/////////////////////////////////////////////////////////////////////////////////////
[7]93// @ kcm   : pointer on kcm allocator.
94// @ ptr   : pointer on block to be released.
95/////////////////////////////////////////////////////////////////////////////////////
96static void kcm_put_block ( kcm_t * kcm,
97                            void  * ptr )
[1]98{
[50]99        kcm_page_t * kcm_page;
[20]100        uint32_t     index;
[18]101
[161]102        // compute pointer on kcm_page from block pointer
[50]103        kcm_page = (kcm_page_t*)((intptr_t)ptr & ~CONFIG_PPM_PAGE_MASK);
[18]104
[161]105        // compute block index from block pointer
[50]106        index = ((uint8_t *)ptr - (uint8_t *)kcm_page - CONFIG_KCM_SLOT_SIZE) / kcm->block_size;
[18]107
[176]108        assert( !bitmap_state( kcm_page->bitmap , index ) , __FUNCTION__ , "page already freed" );
109        assert( (kcm_page->count > 0) , __FUNCTION__ , "count already zero" );
110
[50]111        bitmap_set( kcm_page->bitmap , index );
112        kcm_page->count --;
113
[20]114        // change the page to active if it was busy
[50]115        if( kcm_page->busy )
[1]116        {
[50]117                kcm_page->busy = 0;
118                list_unlink( &kcm_page->list );
[1]119                kcm->busy_pages_nr --;
120
[50]121                list_add_last( &kcm->active_root, &kcm_page->list );
[1]122                kcm->active_pages_nr ++;
[50]123                kcm_page->active = 1;
[1]124        }
125
[50]126        // change the kcm_page to free if last block in active page
127        if( (kcm_page->active) && (kcm_page->count == 0) )
[1]128        {
[50]129                kcm_page->active = 0;
130                list_unlink( &kcm_page->list);
[1]131                kcm->active_pages_nr --;
132
[50]133                list_add_first( &kcm->free_root , &kcm_page->list);
[1]134                kcm->free_pages_nr ++;
135        }
[161]136}
[1]137
138/////////////////////////////////////////////////////////////////////////////////////
[7]139// This static function allocates one page from PPM. It initializes
[50]140// the kcm_page descriptor, and introduces the new kcm_page into freelist.
[1]141/////////////////////////////////////////////////////////////////////////////////////
142static error_t freelist_populate( kcm_t * kcm )
143{
144        page_t     * page;
[50]145        kcm_page_t * kcm_page;
[20]146        kmem_req_t   req;
[1]147
[20]148        // get one page from local PPM
149        req.type  = KMEM_PAGE;
150        req.size  = 0;
151        req.flags = AF_KERNEL;
152        page = kmem_alloc( &req );
[18]153
[7]154        if( page == NULL )
155        {
[18]156                printk("\n[ERROR] in %s : failed to allocate page in cluster %d\n",
[20]157                       __FUNCTION__ , local_cxy );
158                return ENOMEM;
[7]159        }
160
[20]161        // get page base address
[315]162        xptr_t base_xp = ppm_page2base( XPTR( local_cxy , page ) );
163        kcm_page = (kcm_page_t *)GET_PTR( base_xp );
[1]164
[20]165        // initialize KCM-page descriptor
[50]166        bitmap_set_range( kcm_page->bitmap , 0 , kcm->blocks_nr );
[1]167
[50]168        kcm_page->busy          = 0;
169        kcm_page->active        = 0;
170        kcm_page->count      = 0;
171        kcm_page->kcm           = kcm;
172        kcm_page->page          = page;
[1]173
[20]174        // introduce new page in free-list
[50]175        list_add_first( &kcm->free_root , &kcm_page->list );
[1]176        kcm->free_pages_nr ++;
[18]177
[1]178        return 0;
[161]179}
[1]180
181/////////////////////////////////////////////////////////////////////////////////////
[20]182// This private function gets one KCM page from the KCM freelist.
[1]183// It populates the freelist if required.
184/////////////////////////////////////////////////////////////////////////////////////
185static kcm_page_t * freelist_get( kcm_t * kcm )
186{
[7]187        error_t      error;
[50]188        kcm_page_t * kcm_page;
[1]189
[20]190        // get a new page from PPM if freelist empty
[1]191        if( kcm->free_pages_nr == 0 )
192        {
[20]193                error = freelist_populate( kcm );
194                if( error ) return NULL;
[1]195        }
196
[50]197        // get first KCM page from freelist and unlink it
198        kcm_page = LIST_FIRST( &kcm->free_root, kcm_page_t , list );
199        list_unlink( &kcm_page->list );
[1]200        kcm->free_pages_nr --;
201
[50]202        return kcm_page;
[161]203}
[1]204
[7]205//////////////////////////////
206void kcm_init( kcm_t    * kcm,
207                   uint32_t   type )
[1]208{
[161]209        // the kcm_page descriptor mut fit in the KCM slot
210        assert( (sizeof(kcm_page_t) <= CONFIG_KCM_SLOT_SIZE) ,
211                 __FUNCTION__ , "KCM slot too small\n" );
[1]212
[20]213        // initialize lock
[1]214        spinlock_init( &kcm->lock );
215
[20]216        // initialize KCM type
[1]217        kcm->type = type;
218
[20]219        // initialize KCM page lists
[1]220        kcm->free_pages_nr   = 0;
221        kcm->busy_pages_nr   = 0;
222        kcm->active_pages_nr = 0;
223        list_root_init( &kcm->free_root );
224        list_root_init( &kcm->busy_root );
225        list_root_init( &kcm->active_root );
226
[161]227        // initialize block size
[50]228        uint32_t block_size = ARROUND_UP( kmem_type_size( type ) , CONFIG_KCM_SLOT_SIZE );
[1]229        kcm->block_size = block_size;
[18]230
[50]231        // initialize number of blocks per page
232        uint32_t  blocks_nr = (CONFIG_PPM_PAGE_SIZE - CONFIG_KCM_SLOT_SIZE) / block_size;
[161]233        kcm->blocks_nr = blocks_nr;
[50]234
[20]235        kcm_dmsg("\n[INFO] %s : KCM %s initialised / block_size = %d / blocks_nr = %d\n",
[50]236                 __FUNCTION__ , kmem_type_str( type ) , kcm->block_size , kcm->blocks_nr );
[161]237}
[1]238
239///////////////////////////////
240void kcm_destroy( kcm_t * kcm )
241{
[50]242        kcm_page_t   * kcm_page;
[1]243        list_entry_t * iter;
[18]244
[20]245        // get KCM lock
[1]246        spinlock_lock( &kcm->lock );
247
[20]248        // release all free pages
[1]249        LIST_FOREACH( &kcm->free_root , iter )
250        {
[50]251                kcm_page = (kcm_page_t *)LIST_ELEMENT( iter , kcm_page_t , list );
[1]252                list_unlink( iter );
253                kcm->free_pages_nr --;
[50]254                ppm_free_pages( kcm_page->page );
[1]255        }
256
[20]257        // release all active pages
[1]258        LIST_FOREACH( &kcm->active_root , iter )
259        {
[50]260                kcm_page = (kcm_page_t *)LIST_ELEMENT( iter , kcm_page_t , list );
[1]261                list_unlink( iter );
262                kcm->free_pages_nr --;
[50]263                ppm_free_pages( kcm_page->page );
[1]264        }
265
[20]266        // release all busy pages
[1]267        LIST_FOREACH( &kcm->busy_root , iter )
268        {
[50]269                kcm_page = (kcm_page_t *)LIST_ELEMENT( iter , kcm_page_t , list );
[1]270                list_unlink( iter );
271                kcm->free_pages_nr --;
[50]272                ppm_free_pages( kcm_page->page );
[1]273        }
274
[20]275        // release KCM lock
276        spinlock_unlock( &kcm->lock );
[161]277}
[1]278
279///////////////////////////////
280void * kcm_alloc( kcm_t * kcm )
281{
[50]282        kcm_page_t * kcm_page;
[1]283        void       * ptr = NULL;   // pointer on block
284
[20]285        // get lock
[1]286        spinlock_lock( &kcm->lock );
[18]287
[20]288        // get an active page
289        if( list_is_empty( &kcm->active_root ) )  // no active page => get one
290        {
291                // get a page from free list
[50]292                kcm_page = freelist_get( kcm );
[7]293
[182]294                if( kcm_page == NULL )
295                {
296                        spinlock_unlock( &kcm->lock );
297                        return NULL;
298                }
[50]299
[20]300                // insert page in active list
[50]301                list_add_first( &kcm->active_root , &kcm_page->list );
[20]302                kcm->active_pages_nr ++;
[161]303                kcm_page->active = 1;
[50]304
[161]305                kcm_dmsg("\n[INFO] %s : enters for type %s at cycle %d / new page = %x / count = %d\n",
[101]306                         __FUNCTION__ , kmem_type_str( kcm->type ) , hal_get_cycles() ,
[161]307                         (intptr_t)kcm_page , kcm_page->count );
[50]308
[20]309        }
[50]310        else                                    // get first page from active list
[20]311        {
[50]312                // get page pointer from active list
313                kcm_page = (kcm_page_t *)LIST_FIRST( &kcm->active_root , kcm_page_t , list );
[7]314
[50]315                kcm_dmsg("\n[INFO] %s : enters for type %s at cycle %d / page = %x / count = %d\n",
[161]316                         __FUNCTION__ , kmem_type_str( kcm->type ) , hal_get_cycles() ,
317                         (intptr_t)kcm_page , kcm_page->count );
[20]318        }
[1]319
[20]320        // get a block from selected active page
321        // cannot fail, as an active page cannot be full...
[50]322        ptr  = kcm_get_block( kcm , kcm_page );
[7]323
[20]324        // release lock
[50]325        spinlock_unlock( &kcm->lock );
[1]326
327        return ptr;
[161]328}
[1]329
330///////////////////////////
331void kcm_free( void * ptr )
332{
[50]333        kcm_page_t * kcm_page;
[1]334        kcm_t      * kcm;
[18]335
[50]336        assert( (ptr != NULL) , __FUNCTION__ , "pointer cannot be NULL" );
[18]337
[50]338        kcm_page = (kcm_page_t *)((intptr_t)ptr & ~CONFIG_PPM_PAGE_MASK);
339        kcm      = kcm_page->kcm;
[1]340
[20]341        // get lock
[1]342        spinlock_lock( &kcm->lock );
343
[20]344        // release block
[7]345        kcm_put_block( kcm , ptr );
[1]346
[20]347        // release lock
[1]348        spinlock_unlock( &kcm->lock );
[161]349}
[1]350
351////////////////////////////
352void kcm_print (kcm_t * kcm)
353{
[7]354        printk("*** KCM type = %s / free_pages = %d / busy_pages = %d / active_pages = %d\n",
[20]355               kmem_type_str( kcm->type ) ,
356               kcm->free_pages_nr ,
357               kcm->busy_pages_nr ,
358               kcm->active_pages_nr );
[1]359}
Note: See TracBrowser for help on using the repository browser.