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

Last change on this file since 2 was 1, checked in by alain, 8 years ago

First import

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