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

Last change on this file since 395 was 352, checked in by max@…, 7 years ago

Don't compute kcm_page twice.

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