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

Last change on this file since 619 was 619, checked in by alain, 5 years ago

1) Fix a bug in KSH : after the "load" command,

the [ksh] prompt is now printed after completion
of the loaded application.

2) Fix a bug in vmm_handle_cow() : the copy-on-write

use now a hal_remote_memcpy() to replicate the page content.


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