File: mm.c

package info (click to toggle)
brickos 0.9.0-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,700 kB
  • ctags: 1,727
  • sloc: ansic: 9,139; cpp: 860; makefile: 717; asm: 693; sh: 123; perl: 61
file content (302 lines) | stat: -rw-r--r-- 7,241 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*! \file   mm.c
    \brief  Implementation: dynamic memory management
    \author Markus L. Noga <markus@noga.de>
*/
    
/*
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *  License for the specific language governing rights and limitations
 *  under the License.
 *
 *  The Original Code is legOS code, released October 17, 1999.
 *
 *  The Initial Developer of the Original Code is Markus L. Noga.
 *  Portions created by Markus L. Noga are Copyright (C) 1999
 *  Markus L. Noga. All Rights Reserved.
 *
 *  Contributor(s): Markus L. Noga <markus@noga.de>
 */

#include <sys/mm.h>

#ifdef CONF_MM

#include <stdlib.h>
#include <sys/tm.h>
#include <sys/critsec.h>
#include <string.h>

///////////////////////////////////////////////////////////////////////////////
//
// Variables
//
///////////////////////////////////////////////////////////////////////////////
      
size_t *mm_first_free;        //!< first free block

#ifndef CONF_TM
typedef size_t tid_t;                           //! dummy process ID type

//! current process ID
/*! we need a non-null, non-0xffff current pid even if there is no
    task management.
*/
const tid_t ctid=0x0001;
#endif

///////////////////////////////////////////////////////////////////////////////
//
// Functions
//
///////////////////////////////////////////////////////////////////////////////

//
// memory block structure:
// 0 1       : pid of owner (0=empty)
// 2 3       : size of data block >> 1
// 4 ... 4+2n: data
//

//! check for free blocks after this one and join them if possible
/* \param ptr pointer to size field of current block
   \return size of block
*/
size_t mm_try_join(size_t *ptr) {
  size_t *next=ptr+*ptr+1;
  size_t increase=0;
  
  while(*next==MM_FREE && next>=&mm_start) {
    increase+=*(next+1) + MM_HEADER_SIZE;
    next    +=*(next+1) + MM_HEADER_SIZE;
  }
  return (*ptr)+=increase;
}

//! defragment free blocks
/*! use mm_try_join on each free block of memory
*/
void mm_defrag() {
  size_t *ptr = &mm_start;
#ifdef CONF_TM
  ENTER_KERNEL_CRITICAL_SECTION();
#endif
  while(ptr >= &mm_start) {
    if(*ptr == MM_FREE)
      mm_try_join(ptr+1);
    ptr += *(ptr+1);
    ptr += MM_HEADER_SIZE;
  }
#ifdef CONF_TM
  LEAVE_KERNEL_CRITICAL_SECTION();
#endif
}

//! update first free block pointer
/*! \param start pointer to owner field of a memory block to start with.
*/
void mm_update_first_free(size_t *start) {
  size_t *ptr=start;
  
  while((*ptr!=MM_FREE) && (ptr>=&mm_start))
    ptr+=*(ptr+1)+MM_HEADER_SIZE;

  mm_first_free=ptr;
}


//! initialize memory management
/*!
*/
void mm_init() {
  size_t *current,*next;
  
  current=&mm_start;

  // memory layout
  //
  MM_BLOCK_FREE    (&mm_start);   // ram
  
                                  // something at 0xc000 ?
  
  MM_BLOCK_RESERVED(0xef30);      // lcddata
  MM_BLOCK_FREE    (0xef50);      // ram2
  MM_BLOCK_RESERVED(0xf000);      // motor
  MM_BLOCK_FREE    (0xfe00);      // ram4
  MM_BLOCK_RESERVED(0xff00);      // stack, onchip

  // expand last block to encompass all available memory
  *current=(int)(((-(int) current)-2)>>1);
  
  mm_update_first_free(&mm_start);
}


//! allocate a block of memory
/*! \param size requested block size
    \return 0 on error, else pointer to block.
*/
void *malloc(size_t size) {
  size_t *ptr,*next;
  
  size=(size+1)>>1;       // only multiples of 2
  
#ifdef CONF_TM
  ENTER_KERNEL_CRITICAL_SECTION();
#endif
  ptr=mm_first_free;
  
  while(ptr>=&mm_start) {
    if(*(ptr++)==MM_FREE) {     // free block?
#ifdef CONF_TM
      mm_try_join(ptr);   // unite with later blocks
#endif
      if(*ptr>=size) {    // big enough?
        *(ptr-1)=(size_t)ctid;  // set owner
              
              // split this block?
        if((*ptr-size)>=MM_SPLIT_THRESH) {
          next=ptr+size+1;
          *(next++)=MM_FREE;
          *(next)=*ptr-size-MM_HEADER_SIZE;
          mm_try_join(next);
          
          *ptr=size;
        }
          
              // was it the first free one?
        if(ptr==mm_first_free+1)
          mm_update_first_free(ptr+*ptr+1);
        
#ifdef CONF_TM
        LEAVE_KERNEL_CRITICAL_SECTION();
#endif    
        return (void*) (ptr+1); 
      }
    }   
      
    ptr+=(*ptr)+1;        // find next block.
  }
  
#ifdef CONF_TM
  LEAVE_KERNEL_CRITICAL_SECTION();
#endif    
  return NULL;
}


//! free a previously allocated block of memory.
/*! \param the_ptr pointer to block

    ever heard of free(software_paradigm)?
*/
void free(void *the_ptr) {
    size_t *ptr=the_ptr;
#ifndef CONF_TM
        size_t *p2,*next;
#endif  
  
  if(ptr==NULL || (((size_t)ptr)&1) )
    return;
  
  ptr-=MM_HEADER_SIZE;
  *((size_t*) ptr)=MM_FREE;     // mark as free

#ifdef CONF_TM
  // for task safe operations, free needs to be
  // atomic and nonblocking, because it may be
  // called by the scheduler.
        //
        // therefore, just update mm_first_free
        //
  if(ptr<mm_first_free || mm_first_free<&mm_start)
    mm_first_free=ptr;                // update mm_first_free
#else
        // without task management, we have the time to
        // unite neighboring memory blocks.
        //
  p2=&mm_start;
  while(p2!=ptr) {        // we could make free
    next=p2+*(p2+1)+MM_HEADER_SIZE;   // O(1) if we included
    if(*p2==MM_FREE && next==ptr)   // a pointer to the 
      break;        // previous block.
    p2=next;        // I don't want to.
  }
  mm_try_join(p2+1);        // defragment free areas

  if(ptr<mm_first_free || mm_first_free<&mm_start)
    mm_update_first_free(ptr);    // update mm_first_free
#endif
}


//! allocate adjacent blocks of memory
/*! \param nmemb number of blocks (must be > 0)
    \param size  individual block size (must be >0)
    \return 0 on error, else pointer to block
*/
void *calloc(size_t nmemb, size_t size) {
  void *ptr;
  size_t original_size = size;

  if (nmemb == 0 || size == 0)
    return 0;
 
  size*=nmemb;

  // if an overflow occurred, size/nmemb will not equal original_size
  if (size/nmemb != original_size)
    return 0;
  
  if((ptr=malloc(size))!=NULL)
    memset(ptr,0,size);
    
  return ptr;
}

//! free all blocks allocated by the current process.
/*! called by exit() and kmain().
*/
void mm_reaper() {
  size_t *ptr;
  
  // pass 1: mark as free 
  ptr=&mm_start;
  while(ptr>=&mm_start) {
    if(*ptr==(size_t)ctid)
      *ptr=MM_FREE;
    ptr+=*(ptr+1)+MM_HEADER_SIZE;
  }

  // pass 2: defragment free areas
  // this may alter free blocks
  mm_defrag();
} 

//! return the number of bytes of unallocated memory
int mm_free_mem(void) {
  int free = 0;
  size_t *ptr;
  
#ifdef CONF_TM
  ENTER_KERNEL_CRITICAL_SECTION();
#endif

  // Iterate through the free list
  for (ptr = mm_first_free; 
       ptr >= &mm_start; 
       ptr += *(ptr+1) + MM_HEADER_SIZE)
    free += *(ptr+1);

#ifdef CONF_TM
  LEAVE_KERNEL_CRITICAL_SECTION();
#endif    
  return free*2;
}

#endif