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

Last change on this file since 567 was 567, checked in by alain, 6 years ago

Complete restructuration of kernel locks.

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