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

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

Various bugs.

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