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

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

detect use-after-frees

File size: 7.5 KB
Line 
1/*
2 * ppm.c - Per-cluster Physical Pages Manager implementation
3 *
4 * Authors  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 <printk.h>
29#include <list.h>
30#include <bits.h>
31#include <page.h>
32#include <spinlock.h>
33#include <thread.h>
34#include <cluster.h>
35#include <kmem.h>
36#include <process.h>
37#include <dqdt.h>
38#include <ppm.h>
39
40////////////////////////////////////////////////
41inline bool_t ppm_page_is_valid( page_t * page )
42{
43        ppm_t    * ppm  = &LOCAL_CLUSTER->ppm;
44        uint32_t   pgnr = (uint32_t)( page - ppm->pages_tbl );
45        return (pgnr <= ppm->pages_nr);
46}
47
48
49/////////////////////////////////////////////
50inline void * ppm_page2vaddr( page_t * page )
51{
52        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
53        return ppm->vaddr_base + ((page - ppm->pages_tbl) << CONFIG_PPM_PAGE_SHIFT);
54}
55
56//////////////////////////////////////////////
57inline page_t * ppm_vaddr2page( void * vaddr )
58{
59        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
60        return ppm->pages_tbl + (vaddr - ppm->vaddr_base);
61}
62
63//////////////////////////////////////////
64inline ppn_t ppm_page2ppn( page_t * page )
65{
66        ppm_t  * ppm = &LOCAL_CLUSTER->ppm;
67        return (ppn_t)( page - ppm->pages_tbl );
68}
69
70/////////////////////////////////////////
71inline page_t * ppm_ppn2page( ppn_t ppn )
72{
73        ppm_t  * ppm = &LOCAL_CLUSTER->ppm;
74        return &ppm->pages_tbl[ppn];
75}
76
77///////////////////////////////////////
78inline void * ppm_ppn2vaddr( ppn_t ppn )
79{
80        ppm_t  * ppm  = &LOCAL_CLUSTER->ppm;
81        return ppm->vaddr_base + (ppn << CONFIG_PPM_PAGE_SHIFT);
82}
83
84//////////////////////////////////////////
85inline ppn_t ppm_vaddr2ppn( void * vaddr )
86{
87        ppm_t  * ppm  = &LOCAL_CLUSTER->ppm;
88        return ( (ppm->vaddr_base - vaddr) >> CONFIG_PPM_PAGE_SHIFT );
89}
90
91
92///////////////////////////////////////////
93void ppm_free_pages_nolock( page_t * page )
94{
95        page_t   * buddy;            // searched buddy page descriptor
96        uint32_t   buddy_index;      // buddy page index
97        page_t   * current;          // current (merged) page descriptor
98        uint32_t   current_index;    // current (merged) page index
99        uint32_t   current_order;    // current (merged) page order
100
101        ppm_t    * ppm         = &LOCAL_CLUSTER->ppm;
102        page_t   * pages_tbl   = ppm->pages_tbl;
103
104        assert( !page_is_flag( page , PG_FREE ) , __FUNCTION__ , "page already freed" );
105        assert( !page_is_flag( page , PG_RESERVED ) , __FUNCTION__ , "freeing reserved page" );
106
107        // update released page descriptor flags
108        page_set_flag( page , PG_FREE );
109
110        // search the buddy page descriptor
111        // - merge with current page descriptor if found
112        // - exit to release the current page descriptor if not found
113        current       = page ,
114        current_index = (uint32_t)(page - ppm->pages_tbl);
115        for( current_order = page->order ;
116             current_order < CONFIG_PPM_MAX_ORDER ;
117             current_order++ )
118        {
119                buddy_index = current_index ^ (1 << current_order);
120                buddy       = pages_tbl + buddy_index;
121
122                if( !page_is_flag( buddy , PG_FREE ) || (buddy->order != current_order) ) break;
123
124                // remove buddy from free list
125                list_unlink( &buddy->list );
126                ppm->free_pages_nr[current_order] --;
127
128                // merge buddy with current
129                buddy->order = 0;
130                current_index &= buddy_index;
131        }
132
133        // update merged page descriptor order
134        current        = pages_tbl + current_index;
135        current->order = current_order;
136
137        // insert current in free list
138        list_add_first( &ppm->free_pages_root[current_order] , &current->list );
139        ppm->free_pages_nr[current_order] ++;
140}
141
142////////////////////////////////////////////
143page_t * ppm_alloc_pages( uint32_t   order )
144{
145        uint32_t   current_order;
146        page_t   * remaining_block;
147        uint32_t   current_size;
148
149        ppm_t    * ppm = &LOCAL_CLUSTER->ppm;
150
151        assert( (order < CONFIG_PPM_MAX_ORDER) , __FUNCTION__ , "illegal order argument" );
152
153        page_t * block = NULL;
154
155        ppm_dmsg("\n[INFO] %s : enters / order = %d\n",
156                 __FUNCTION__ , order );
157
158        // take lock protecting free lists
159        spinlock_lock( &ppm->free_lock );
160
161        // find a free block equal or larger to requested size
162        for( current_order = order ; current_order < CONFIG_PPM_MAX_ORDER ; current_order ++ )
163        {
164                if( !list_is_empty( &ppm->free_pages_root[current_order] ) )
165                {
166                        block = LIST_FIRST( &ppm->free_pages_root[current_order] , page_t , list );
167                        list_unlink( &block->list );
168                        break;
169                }
170        }
171
172        if( block == NULL ) // return failure
173        {
174                // release lock protecting free lists
175                spinlock_unlock( &ppm->free_lock );
176
177                return NULL;
178        }
179
180        // update free-lists after removing a block
181        ppm->free_pages_nr[current_order] --;
182        current_size = (1 << current_order);
183
184        // split the removed block in smaller sub-blocks if required
185        // and update the free-lists accordingly
186        while( current_order > order )
187        {
188                current_order --;
189                current_size >>= 1;
190
191                remaining_block = block + current_size;
192                remaining_block->order = current_order;
193
194                list_add_first( &ppm->free_pages_root[current_order] , &remaining_block->list );
195                ppm->free_pages_nr[current_order] ++;
196        }
197
198        // update page descriptor
199        page_clear_flag( block , PG_FREE );
200        page_refcount_up( block );
201        block->order = order;
202
203        // release lock protecting free lists
204        spinlock_unlock( &ppm->free_lock );
205
206        ppm_dmsg("\n[INFO] %s : base = %x / order = %d\n",
207                 __FUNCTION__ , (uint32_t)ppm_page2base( block ) , order );
208
209        return block;
210}
211
212
213////////////////////////////////////
214void ppm_free_pages( page_t * page )
215{
216        ppm_t * ppm = &LOCAL_CLUSTER->ppm;
217
218        // get lock protecting free_pages[] array
219        spinlock_lock( &ppm->free_lock );
220
221        ppm_free_pages_nolock( page );
222
223        // release lock protecting free_pages[] array
224        spinlock_unlock( &ppm->free_lock );
225}
226
227////////////////////////////
228void ppm_print( ppm_t * ppm,
229                char  * string )
230{
231        uint32_t       order;
232        list_entry_t * iter;
233        page_t       * page;
234
235        // get lock protecting free lists
236        spinlock_lock( &ppm->free_lock );
237
238        printk("\n***  PPM in cluster %x : %d pages / &pages_tbl = %x / vaddr_base = %x ***\n",
239               local_cxy , ppm->pages_nr , (intptr_t)ppm->pages_tbl , (intptr_t)ppm->vaddr_base );
240
241        for( order = 0 ; order < CONFIG_PPM_MAX_ORDER ; order++ )
242        {
243                printk("- order = %d / free_pages = %d  [",
244                       order , ppm->free_pages_nr[order] );
245
246                LIST_FOREACH( &ppm->free_pages_root[order] , iter )
247                {
248                        page = LIST_ELEMENT( iter , page_t , list );
249                        printk("%d," , page - ppm->pages_tbl );
250                }
251
252                printk("]\n", NULL );
253        }
254
255        // release lock protecting free lists
256        spinlock_unlock( &ppm->free_lock );
257}
258
259///////////////////////////////////////
260error_t ppm_assert_order( ppm_t * ppm )
261{
262        uint32_t       order;
263        list_entry_t * iter;
264        page_t       * page;
265
266        for(order=0; order < CONFIG_PPM_MAX_ORDER; order++)
267        {
268                if( list_is_empty( &ppm->free_pages_root[order] ) ) continue;
269
270                LIST_FOREACH( &ppm->free_pages_root[order] , iter )
271                {
272                        page = LIST_ELEMENT( iter , page_t , list );
273
274                        if( page->order != order )  return -1;
275                }
276        }
277
278        return 0;
279}
280
Note: See TracBrowser for help on using the repository browser.