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

Last change on this file since 551 was 551, checked in by nicolas.van.phan@…, 3 years ago

Make locks before IDLE Init busy

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