source: trunk/kernel/mm/ppm.c @ 631

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

Fix a bug in the vmm_remove_vseg() function: the physical pages
associated to an user DATA vseg were released to the kernel when
the target process descriptor was in the reference cluster.
This physical pages release should be done only when the page
forks counter value is zero.
All other modifications are cosmetic.

File size: 17.4 KB
RevLine 
[1]1/*
2 * ppm.c - Per-cluster Physical Pages Manager implementation
3 *
4 * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
[567]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>
28#include <printk.h>
29#include <list.h>
30#include <bits.h>
31#include <page.h>
[585]32#include <dqdt.h>
[567]33#include <busylock.h>
34#include <queuelock.h>
[1]35#include <thread.h>
36#include <cluster.h>
37#include <kmem.h>
38#include <process.h>
[567]39#include <mapper.h>
[1]40#include <ppm.h>
[606]41#include <vfs.h>
[1]42
[567]43////////////////////////////////////////////////////////////////////////////////////////
44//     functions to  translate [ page <-> base <-> ppn ]
45////////////////////////////////////////////////////////////////////////////////////////
46
[1]47////////////////////////////////////////////////
48inline bool_t ppm_page_is_valid( page_t * page )
49{
[160]50        ppm_t    * ppm  = &LOCAL_CLUSTER->ppm;
[1]51        uint32_t   pgnr = (uint32_t)( page - ppm->pages_tbl );
52        return (pgnr <= ppm->pages_nr);
53}
54
[50]55/////////////////////////////////////////////
[315]56inline xptr_t ppm_page2base( xptr_t page_xp )
[1]57{
[315]58        ppm_t  * ppm      = &LOCAL_CLUSTER->ppm;
[1]59
[315]60    cxy_t    page_cxy = GET_CXY( page_xp );
[437]61    page_t * page_ptr = GET_PTR( page_xp );
[315]62
[406]63   void   * base_ptr = ppm->vaddr_base + 
64                       ((page_ptr - ppm->pages_tbl)<<CONFIG_PPM_PAGE_SHIFT);
65
[315]66        return XPTR( page_cxy , base_ptr );
67
68} // end ppm_page2base()
69
70/////////////////////////////////////////////
71inline xptr_t ppm_base2page( xptr_t base_xp )
[1]72{
[315]73        ppm_t  * ppm = &LOCAL_CLUSTER->ppm;
[1]74
[315]75    cxy_t    base_cxy = GET_CXY( base_xp );
[437]76    void   * base_ptr = GET_PTR( base_xp );
[315]77
78        page_t * page_ptr = ppm->pages_tbl + 
79                        ((base_ptr - ppm->vaddr_base)>>CONFIG_PPM_PAGE_SHIFT);
80
81        return XPTR( base_cxy , page_ptr );
82
83}  // end ppm_base2page()
84
85
86
[50]87///////////////////////////////////////////
[315]88inline ppn_t ppm_page2ppn( xptr_t page_xp )
89{
90        ppm_t  * ppm      = &LOCAL_CLUSTER->ppm;
91
92    cxy_t    page_cxy = GET_CXY( page_xp );
[437]93    page_t * page_ptr = GET_PTR( page_xp );
[315]94
95    paddr_t  paddr    = PADDR( page_cxy , (page_ptr - ppm->pages_tbl)<<CONFIG_PPM_PAGE_SHIFT );
96
[437]97    return (ppn_t)(paddr >> CONFIG_PPM_PAGE_SHIFT);
[315]98
99}  // end hal_page2ppn()
100
101///////////////////////////////////////
102inline xptr_t ppm_ppn2page( ppn_t ppn )
103{
[437]104        ppm_t   * ppm  = &LOCAL_CLUSTER->ppm;
[315]105
[437]106    paddr_t  paddr = ((paddr_t)ppn) << CONFIG_PPM_PAGE_SHIFT;
[315]107
[437]108    cxy_t    cxy   = CXY_FROM_PADDR( paddr );
109    lpa_t    lpa   = LPA_FROM_PADDR( paddr );
[315]110
[437]111    return XPTR( cxy , &ppm->pages_tbl[lpa>>CONFIG_PPM_PAGE_SHIFT] );
[315]112
113}  // end hal_ppn2page
114
115
116
117///////////////////////////////////////
118inline xptr_t ppm_ppn2base( ppn_t ppn )
119{
[437]120        ppm_t  * ppm   = &LOCAL_CLUSTER->ppm;
[315]121   
[437]122    paddr_t  paddr = ((paddr_t)ppn) << CONFIG_PPM_PAGE_SHIFT;
[315]123
[437]124    cxy_t    cxy   = CXY_FROM_PADDR( paddr );
125    lpa_t    lpa   = LPA_FROM_PADDR( paddr );
[315]126
[437]127        return XPTR( cxy , (void *)ppm->vaddr_base + lpa );
[315]128
129}  // end ppm_ppn2base()
130
131///////////////////////////////////////////
132inline ppn_t ppm_base2ppn( xptr_t base_xp )
133{
134        ppm_t  * ppm      = &LOCAL_CLUSTER->ppm;
135
136    cxy_t    base_cxy = GET_CXY( base_xp );
[437]137    void   * base_ptr = GET_PTR( base_xp );
[315]138
139    paddr_t  paddr    = PADDR( base_cxy , (base_ptr - ppm->vaddr_base) );
140
[437]141    return (ppn_t)(paddr >> CONFIG_PPM_PAGE_SHIFT);
[315]142
143}  // end ppm_base2ppn()
144
145
[567]146////////////////////////////////////////////////////////////////////////////////////////
147//     functions to  allocate / release  physical pages
148////////////////////////////////////////////////////////////////////////////////////////
[315]149
150///////////////////////////////////////////
[50]151void ppm_free_pages_nolock( page_t * page )
[1]152{
[7]153        page_t   * buddy;            // searched buddy page descriptor
154        uint32_t   buddy_index;      // buddy page index
155        page_t   * current;          // current (merged) page descriptor
156        uint32_t   current_index;    // current (merged) page index
[50]157        uint32_t   current_order;    // current (merged) page order
[7]158
[160]159        ppm_t    * ppm         = &LOCAL_CLUSTER->ppm;
160        page_t   * pages_tbl   = ppm->pages_tbl;
[1]161
[492]162        assert( !page_is_flag( page , PG_FREE ) ,
[407]163    "page already released : ppn = %x\n" , ppm_page2ppn(XPTR(local_cxy,page)) );
[177]164
[492]165        assert( !page_is_flag( page , PG_RESERVED ) ,
[407]166    "reserved page : ppn = %x\n" , ppm_page2ppn(XPTR(local_cxy,page)) );
167
[160]168        // update released page descriptor flags
[1]169        page_set_flag( page , PG_FREE );
170
[160]171        // search the buddy page descriptor
172        // - merge with current page descriptor if found
173        // - exit to release the current page descriptor if not found
174        current       = page ,
175        current_index = (uint32_t)(page - ppm->pages_tbl);
[18]176        for( current_order = page->order ;
[160]177             current_order < CONFIG_PPM_MAX_ORDER ;
178             current_order++ )
179        {
[7]180                buddy_index = current_index ^ (1 << current_order);
181                buddy       = pages_tbl + buddy_index;
[18]182
[7]183                if( !page_is_flag( buddy , PG_FREE ) || (buddy->order != current_order) ) break;
[1]184
[160]185                // remove buddy from free list
[7]186                list_unlink( &buddy->list );
[1]187                ppm->free_pages_nr[current_order] --;
[18]188
[160]189                // merge buddy with current
[7]190                buddy->order = 0;
191                current_index &= buddy_index;
[1]192        }
[18]193
[160]194        // update merged page descriptor order
[7]195        current        = pages_tbl + current_index;
196        current->order = current_order;
[1]197
[160]198        // insert current in free list
[7]199        list_add_first( &ppm->free_pages_root[current_order] , &current->list );
[1]200        ppm->free_pages_nr[current_order] ++;
201
[433]202}  // end ppm_free_pages_nolock()
203
[1]204////////////////////////////////////////////
205page_t * ppm_alloc_pages( uint32_t   order )
206{
[160]207        uint32_t   current_order;
[1]208        page_t   * remaining_block;
209        uint32_t   current_size;
[551]210
[438]211#if DEBUG_PPM_ALLOC_PAGES
[611]212thread_t * this = CURRENT_THREAD;
[433]213uint32_t cycle = (uint32_t)hal_get_cycles();
[438]214if( DEBUG_PPM_ALLOC_PAGES < cycle )
[611]215printk("\n[%s] thread[%x,%x] enter for %d page(s) / cycle %d\n",
216__FUNCTION__, this->process->pid, this->trdid, 1<<order, cycle );
[433]217#endif
[1]218
[438]219#if(DEBUG_PPM_ALLOC_PAGES & 0x1)
220if( DEBUG_PPM_ALLOC_PAGES < cycle )
[611]221ppm_print("enter ppm_alloc_pages");
[433]222#endif
223
[160]224        ppm_t    * ppm = &LOCAL_CLUSTER->ppm;
[1]225
[611]226// check order
227assert( (order < CONFIG_PPM_MAX_ORDER) , "illegal order argument = %d\n" , order );
[1]228
[406]229        page_t * block = NULL; 
[1]230
[160]231        // take lock protecting free lists
[567]232        busylock_acquire( &ppm->free_lock );
[1]233
[160]234        // find a free block equal or larger to requested size
[1]235        for( current_order = order ; current_order < CONFIG_PPM_MAX_ORDER ; current_order ++ )
236        {
237                if( !list_is_empty( &ppm->free_pages_root[current_order] ) )
238                {
239                        block = LIST_FIRST( &ppm->free_pages_root[current_order] , page_t , list );
240                        list_unlink( &block->list );
241                        break;
242                }
243        }
244
245        if( block == NULL ) // return failure
246        {
[160]247                // release lock protecting free lists
[567]248                busylock_release( &ppm->free_lock );
[1]249
[438]250#if DEBUG_PPM_ALLOC_PAGES
[433]251cycle = (uint32_t)hal_get_cycles();
[438]252if( DEBUG_PPM_ALLOC_PAGES < cycle )
[611]253printk("\n[%s] thread[%x,%x] cannot allocate %d page(s) / cycle %d\n",
254__FUNCTION__, this->process->pid, this->trdid, 1<<order, cycle );
[433]255#endif
256
[160]257                return NULL;
258        }
[18]259
[160]260        // update free-lists after removing a block
[18]261        ppm->free_pages_nr[current_order] --;
[1]262        current_size = (1 << current_order);
263
[160]264        // split the removed block in smaller sub-blocks if required
265        // and update the free-lists accordingly
[1]266        while( current_order > order )
267        {
268                current_order --;
269                current_size >>= 1;
[18]270
[1]271                remaining_block = block + current_size;
272                remaining_block->order = current_order;
273
274                list_add_first( &ppm->free_pages_root[current_order] , &remaining_block->list );
275                ppm->free_pages_nr[current_order] ++;
276        }
[18]277
[160]278        // update page descriptor
279        page_clear_flag( block , PG_FREE );
[1]280        page_refcount_up( block );
281        block->order = order;
282
[160]283        // release lock protecting free lists
[567]284        busylock_release( &ppm->free_lock );
[18]285
[585]286    // update DQDT
287    dqdt_increment_pages( order );
288
[438]289#if DEBUG_PPM_ALLOC_PAGES
[433]290cycle = (uint32_t)hal_get_cycles();
[438]291if( DEBUG_PPM_ALLOC_PAGES < cycle )
[611]292printk("\n[%s] thread[%x,%x] exit for %d page(s) / ppn = %x / cycle %d\n",
293__FUNCTION__, this->process->pid, this->trdid, 
[567]2941<<order, ppm_page2ppn(XPTR( local_cxy , block )), cycle );
[433]295#endif
[7]296
[611]297#if(DEBUG_PPM_ALLOC_PAGES & 0x1)
298if( DEBUG_PPM_ALLOC_PAGES < cycle )
299ppm_print("exit ppm_alloc_pages");
300#endif
301
[1]302        return block;
303
[433]304}  // end ppm_alloc_pages()
[1]305
[433]306
[1]307////////////////////////////////////
308void ppm_free_pages( page_t * page )
309{
310        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
[18]311
[438]312#if DEBUG_PPM_FREE_PAGES
[433]313uint32_t cycle = (uint32_t)hal_get_cycles();
[438]314if( DEBUG_PPM_FREE_PAGES < cycle )
[611]315printk("\n[%s] thread[%x,%x] enter for %d page(s) / ppn %x / cycle %d\n",
316__FUNCTION__, this->process->pid, this->trdid, 
[567]3171<<page->order, ppm_page2ppn(XPTR(local_cxy , page)), cycle );
[433]318#endif
319
[438]320#if(DEBUG_PPM_FREE_PAGES & 0x1)
321if( DEBUG_PPM_FREE_PAGES < cycle )
[611]322ppm_print("enter ppm_free_pages");
[433]323#endif
324
[160]325        // get lock protecting free_pages[] array
[567]326        busylock_acquire( &ppm->free_lock );
[1]327
[18]328        ppm_free_pages_nolock( page );
[1]329
[160]330        // release lock protecting free_pages[] array
[567]331        busylock_release( &ppm->free_lock );
[433]332
[585]333    // update DQDT
334    dqdt_decrement_pages( page->order );
335
[438]336#if DEBUG_PPM_FREE_PAGES
[433]337cycle = (uint32_t)hal_get_cycles();
[438]338if( DEBUG_PPM_FREE_PAGES < cycle )
[611]339printk("\n[%s] thread[%x,%x] exit for %d page(s) / ppn %x / cycle %d\n",
340__FUNCTION__, this->process->pid, this->trdid, 
[567]3411<<page->order, ppm_page2ppn(XPTR(local_cxy , page)), cycle );
[433]342#endif
343
[611]344#if(DEBUG_PPM_FREE_PAGES & 0x1)
345if( DEBUG_PPM_FREE_PAGES < cycle )
346ppm_print("exit ppm_free_pages");
347#endif
348
[567]349}  // end ppm_free_pages()
[1]350
[625]351////////////////////////
352void ppm_display( void )
[1]353{
354        uint32_t       order;
355        list_entry_t * iter;
356        page_t       * page;
357
[433]358    ppm_t * ppm = &LOCAL_CLUSTER->ppm;
359
[160]360        // get lock protecting free lists
[567]361        busylock_acquire( &ppm->free_lock );
[1]362
[625]363        printk("\n***** PPM in cluster %x / %d pages\n", local_cxy , ppm->pages_nr );
[18]364
[1]365        for( order = 0 ; order < CONFIG_PPM_MAX_ORDER ; order++ )
366        {
[433]367                printk("- order = %d / free_pages = %d\t: ",
[160]368                       order , ppm->free_pages_nr[order] );
[18]369
[1]370                LIST_FOREACH( &ppm->free_pages_root[order] , iter )
371                {
372                        page = LIST_ELEMENT( iter , page_t , list );
[433]373                        printk("%x," , page - ppm->pages_tbl );
[1]374                }
[18]375
[433]376                printk("\n");
[1]377        }
378
[160]379        // release lock protecting free lists
[567]380        busylock_release( &ppm->free_lock );
[160]381}
[1]382
[53]383///////////////////////////////////////
384error_t ppm_assert_order( ppm_t * ppm )
[1]385{
386        uint32_t       order;
387        list_entry_t * iter;
388        page_t       * page;
[18]389
[407]390        for( order=0 ; order < CONFIG_PPM_MAX_ORDER ; order++ )
[1]391        {
392                if( list_is_empty( &ppm->free_pages_root[order] ) ) continue;
[18]393
[1]394                LIST_FOREACH( &ppm->free_pages_root[order] , iter )
395                {
396                        page = LIST_ELEMENT( iter , page_t , list );
[160]397                        if( page->order != order )  return -1;
[1]398                }
399        }
400
[160]401        return 0;
402}
[53]403
[567]404
405//////////////////////////////////////////////////////////////////////////////////////
406//     functions to handle  dirty physical pages
407//////////////////////////////////////////////////////////////////////////////////////
408
[606]409//////////////////////////////////////////
410bool_t ppm_page_do_dirty( xptr_t page_xp )
[567]411{
412        bool_t done = false;
413
[606]414    // get page cluster and local pointer
415    page_t * page_ptr = GET_PTR( page_xp );
416    cxy_t    page_cxy = GET_CXY( page_xp );
417
418    // get local pointer on PPM (same in all clusters)
[567]419        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
420
[606]421    // build extended pointers on page lock, page flags, and PPM dirty list lock
422    xptr_t page_lock_xp  = XPTR( page_cxy , &page_ptr->lock  );   
423    xptr_t page_flags_xp = XPTR( page_cxy , &page_ptr->flags );
424    xptr_t dirty_lock_xp = XPTR( page_cxy , &ppm->dirty_lock );
425           
426        // lock the remote PPM dirty_list
427        remote_queuelock_acquire( dirty_lock_xp );
[567]428
[606]429    // lock the remote page
430    remote_busylock_acquire( page_lock_xp );
431
432    // get remote page flags
433    uint32_t flags = hal_remote_l32( page_flags_xp );
434
435        if( (flags & PG_DIRTY) == 0 )
[567]436        {
437                // set dirty flag in page descriptor
[606]438        hal_remote_s32( page_flags_xp , flags | PG_DIRTY );
[567]439
[606]440                // The PPM dirty list is a LOCAL list !!!
441        // We must update 4 pointers to insert a new page in this list.
442        // We can use the standard LIST API when the page is local,
443        // but we cannot use the standard API if the page is remote...
444
445        if( page_cxy == local_cxy )         // locally update the PPM dirty list
446        {
447            list_add_first( &ppm->dirty_root , &page_ptr->list );
448        } 
449        else                                // remotely update the PPM dirty list
450        {
451            // get local and remote pointers on "root" list entry
452            list_entry_t * root    = &ppm->dirty_root;
453            xptr_t         root_xp = XPTR( page_cxy , root );
454
455            // get local and remote pointers on "page" list entry
456            list_entry_t * list    = &page_ptr->list;
457            xptr_t         list_xp = XPTR( page_cxy , list );
458
459            // get local and remote pointers on first dirty page
460            list_entry_t * dirt    = hal_remote_lpt( XPTR( page_cxy, &root->next ) );
461            xptr_t         dirt_xp = XPTR( page_cxy , dirt );
462
463            // set root.next, list.next, list pred, curr.pred in remote cluster
464            hal_remote_spt( root_xp                    , list );
465            hal_remote_spt( list_xp                    , dirt );
466            hal_remote_spt( list_xp + sizeof(intptr_t) , root );
467            hal_remote_spt( dirt_xp + sizeof(intptr_t) , list );
468        }
469
[567]470                done = true;
471        }
472
[606]473    // unlock the remote page
474    remote_busylock_release( page_lock_xp );
[567]475
[606]476        // unlock the remote PPM dirty_list
477        remote_queuelock_release( dirty_lock_xp );
478
[567]479        return done;
480
[606]481} // end ppm_page_do_dirty()
482
483////////////////////////////////////////////
484bool_t ppm_page_undo_dirty( xptr_t page_xp )
[567]485{
486        bool_t done = false;
487
[606]488    // get page cluster and local pointer
489    page_t * page_ptr = GET_PTR( page_xp );
490    cxy_t    page_cxy = GET_CXY( page_xp );
491
492    // get local pointer on PPM (same in all clusters)
[567]493        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
494
[606]495    // build extended pointers on page lock, page flags, and PPM dirty list lock
496    xptr_t page_lock_xp  = XPTR( page_cxy , &page_ptr->lock  );
497    xptr_t page_flags_xp = XPTR( page_cxy , &page_ptr->flags );
498    xptr_t dirty_lock_xp = XPTR( page_cxy , &ppm->dirty_lock );
499           
500        // lock the remote PPM dirty_list
501        remote_queuelock_acquire( XPTR( page_cxy , &ppm->dirty_lock ) );
[567]502
[606]503    // lock the remote page
504    remote_busylock_acquire( page_lock_xp );
505
506    // get remote page flags
507    uint32_t flags = hal_remote_l32( page_flags_xp );
508
509        if( (flags & PG_DIRTY) )  // page is dirty
[567]510        {
[606]511                // reset dirty flag in page descriptor
512        hal_remote_s32( page_flags_xp , flags & (~PG_DIRTY) );
[567]513
[606]514                // The PPM dirty list is a LOCAL list !!!
515        // We must update 4 pointers to remove a page from this list.
516        // we can use the standard LIST API when the page is local,
517        // but we cannot use the standard API if the page is remote...
518
519        if( page_cxy == local_cxy )         // locally update the PPM dirty list
520        {
521            list_unlink( &page_ptr->list );
522        } 
523        else                                // remotely update the PPM dirty list
524        {
525            // get local and remote pointers on "page" list entry
526            list_entry_t * list    = &page_ptr->list;
527            xptr_t         list_xp = XPTR( page_cxy , list );
528
529            // get local and remote pointers on "next" page list entry
530            list_entry_t * next    = hal_remote_lpt( list_xp );
531            xptr_t         next_xp = XPTR( page_cxy , next );
532
533            // get local and remote pointers on "pred" page list entry
534            list_entry_t * pred    = hal_remote_lpt( list_xp + sizeof(intptr_t) );
535            xptr_t         pred_xp = XPTR( page_cxy , pred );
536
537            // set root.next, list.next, list pred, curr.pred in remote cluster
538            hal_remote_spt( pred_xp                    , next );
539            hal_remote_spt( list_xp                    , NULL );
540            hal_remote_spt( list_xp + sizeof(intptr_t) , NULL );
541            hal_remote_spt( next_xp + sizeof(intptr_t) , pred );
542        }
543
[567]544                done = true;
545        }
546
[606]547    // unlock the remote page
548    remote_busylock_release( page_lock_xp );
[567]549
[606]550        // unlock the remote PPM dirty_list
551        remote_queuelock_release( dirty_lock_xp );
552
[567]553        return done;
554
[606]555}  // end ppm_page_undo_dirty()
556
557/////////////////////////////////
558void ppm_sync_dirty_pages( void )
[567]559{
[606]560        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
[567]561
[606]562    // get local pointer on PPM dirty_root
563    list_entry_t * dirty_root = &ppm->dirty_root;
564
565    // build extended pointer on PPM dirty_lock
566    xptr_t dirty_lock_xp = XPTR( local_cxy , &ppm->dirty_lock );
567
[567]568        // get the PPM dirty_list lock
[606]569        remote_queuelock_acquire( dirty_lock_xp );
[567]570
571        while( !list_is_empty( &ppm->dirty_root ) )
572        {
[606]573                page_t * page = LIST_FIRST( dirty_root ,  page_t , list );
574        xptr_t   page_xp = XPTR( local_cxy , page );
[567]575
[606]576        // build extended pointer on page lock
577        xptr_t page_lock_xp = XPTR( local_cxy , &page->lock );
578
[567]579                // get the page lock
[606]580                remote_busylock_acquire( page_lock_xp );
[567]581
582                // sync the page
[606]583                vfs_fs_move_page( page_xp , false );  // from mapper to device
[567]584
585                // release the page lock
[606]586                remote_busylock_release( page_lock_xp );
[567]587        }
588
589        // release the PPM dirty_list lock
[606]590        remote_queuelock_release( dirty_lock_xp );
[567]591
[606]592}  // end ppm_sync_dirty_pages()
593
Note: See TracBrowser for help on using the repository browser.