source: rtems/cpukit/libcsupport/src/malloc.c @ 49f466c

Last change on this file since 49f466c was 49f466c, checked in by Joel Sherrill <joel.sherrill@…>, on Oct 22, 2003 at 4:54:15 PM

2003-10-22 Joel Sherrill <joel@…>

PR 511/filesystem

  • src/malloc.c: Add deferred free and protect against C Program Heap operations while in a dispatch disable critical section or ISR.
  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 *  RTEMS Malloc Family Implementation
3 *
4 *
5 *  COPYRIGHT (c) 1989-1999.
6 *  On-Line Applications Research Corporation (OAR).
7 *
8 *  The license and distribution terms for this file may be
9 *  found in the file LICENSE in this distribution or at
10 *  http://www.rtems.com/license/LICENSE.
11 *
12 *  $Id$
13 */
14
15#if HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
20#include <rtems.h>
21#include <rtems/libcsupport.h>
22#include <rtems/score/apimutex.h>
23#ifdef RTEMS_NEWLIB
24#include <sys/reent.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/types.h>
30#include <assert.h>
31#include <errno.h>
32#include <string.h>
33
34#include <unistd.h>    /* sbrk(2) */
35
36#include <chain.h>
37
38Chain_Control RTEMS_Malloc_GC_list;
39
40rtems_id RTEMS_Malloc_Heap;
41size_t RTEMS_Malloc_Sbrk_amount;
42
43#ifdef RTEMS_DEBUG
44#define MALLOC_STATS
45#define MALLOC_DIRTY
46#endif
47
48#ifdef MALLOC_STATS
49#define MSBUMP(f,n)    rtems_malloc_stats.f += (n)
50
51struct {
52    unsigned32  space_available;             /* current size of malloc area */
53    unsigned32  malloc_calls;                /* # calls to malloc */
54    unsigned32  free_calls;
55    unsigned32  realloc_calls;
56    unsigned32  calloc_calls;
57    unsigned32  max_depth;                   /* most ever malloc'd at 1 time */
58    unsigned64  lifetime_allocated;
59    unsigned64  lifetime_freed;
60} rtems_malloc_stats;
61
62#else                   /* No rtems_malloc_stats */
63#define MSBUMP(f,n)
64#endif
65
66void RTEMS_Malloc_Initialize(
67  void   *start,
68  size_t  length,
69  size_t  sbrk_amount
70)
71{
72  rtems_status_code   status;
73  void               *starting_address;
74  rtems_unsigned32    old_address;
75  rtems_unsigned32    u32_address;
76
77  /*
78   *  Initialize the garbage collection list to start with nothing on it.
79   */
80  Chain_Initialize_empty(&RTEMS_Malloc_GC_list);
81
82  /*
83   * If the starting address is 0 then we are to attempt to
84   * get length worth of memory using sbrk. Make sure we
85   * align the address that we get back.
86   */
87
88  starting_address = start;
89  RTEMS_Malloc_Sbrk_amount = sbrk_amount;
90
91  if (!starting_address) {
92    u32_address = (unsigned int)sbrk(length);
93
94    if (u32_address == (rtems_unsigned32) -1) {
95      rtems_fatal_error_occurred( RTEMS_NO_MEMORY );
96      /* DOES NOT RETURN!!! */
97    }
98
99    if (u32_address & (CPU_HEAP_ALIGNMENT-1)) {
100      old_address = u32_address;
101      u32_address = (u32_address + CPU_HEAP_ALIGNMENT) & ~(CPU_HEAP_ALIGNMENT-1);
102
103       /*
104        * adjust the length by whatever we aligned by
105        */
106
107      length -= u32_address - old_address;
108    }
109
110    starting_address = (void *)u32_address;
111  }
112
113  /*
114   *  If the BSP is not clearing out the workspace, then it is most likely
115   *  not clearing out the initial memory for the heap.  There is no
116   *  standard supporting zeroing out the heap memory.  But much code
117   *  with UNIX history seems to assume that memory malloc'ed during
118   *  initialization (before any free's) is zero'ed.  This is true most
119   *  of the time under UNIX because zero'ing memory when it is first
120   *  given to a process eliminates the chance of a process seeing data
121   *  left over from another process.  This would be a security violation.
122   */
123
124  if ( rtems_cpu_configuration_get_do_zero_of_workspace() )
125     memset( starting_address, 0, length );
126
127  /*
128   *  Unfortunately we cannot use assert if this fails because if this
129   *  has failed we do not have a heap and if we do not have a heap
130   *  STDIO cannot work because there will be no buffers.
131   */
132
133  status = rtems_region_create(
134    rtems_build_name( 'H', 'E', 'A', 'P' ),
135    starting_address,
136    length,
137    CPU_HEAP_ALIGNMENT,
138    RTEMS_DEFAULT_ATTRIBUTES,
139    &RTEMS_Malloc_Heap
140  );
141  if ( status != RTEMS_SUCCESSFUL )
142    rtems_fatal_error_occurred( status );
143
144#ifdef MALLOC_STATS
145  /* zero all the stats */
146  (void) memset( &rtems_malloc_stats, 0, sizeof(rtems_malloc_stats) );
147#endif
148 
149  MSBUMP(space_available, length);
150}
151
152#ifdef RTEMS_NEWLIB
153void *malloc(
154  size_t  size
155)
156{
157  void              *return_this;
158  void              *starting_address;
159  rtems_unsigned32   the_size;
160  rtems_unsigned32   sbrk_amount;
161  rtems_status_code  status;
162  Chain_Node        *to_be_freed;
163
164  MSBUMP(malloc_calls, 1);
165
166  if ( !size )
167    return (void *) 0;
168
169  /*
170   *  Do not attempt to allocate memory if in a critical section or ISR.
171   */
172
173  if (_Thread_Dispatch_disable_level > 0)
174    return (void *) 0;
175 
176  if (_ISR_Nest_level > 0)
177    return (void *) 0;
178 
179  /*
180   *  If some free's have been deferred, then do them now.
181   */
182  while ((to_be_freed = Chain_Get(&RTEMS_Malloc_GC_list)) != NULL)
183    free(to_be_freed);
184
185  /*
186   * Try to give a segment in the current region if there is not
187   * enough space then try to grow the region using rtems_region_extend().
188   * If this fails then return a NULL pointer.
189   */
190
191  status = rtems_region_get_segment(
192    RTEMS_Malloc_Heap,
193    size,
194    RTEMS_NO_WAIT,
195    RTEMS_NO_TIMEOUT,
196    &return_this
197  );
198
199  if ( status != RTEMS_SUCCESSFUL ) {
200    /*
201     *  Round to the "requested sbrk amount" so hopefully we won't have
202     *  to grow again for a while.  This effectively does sbrk() calls
203     *  in "page" amounts.
204     */
205
206    sbrk_amount = RTEMS_Malloc_Sbrk_amount;
207
208    if ( sbrk_amount == 0 )
209      return (void *) 0;
210
211    the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
212
213    if (((rtems_unsigned32)starting_address = (void *)sbrk(the_size)) 
214            == (rtems_unsigned32) -1)
215      return (void *) 0;
216
217    status = rtems_region_extend(
218      RTEMS_Malloc_Heap,
219      starting_address,
220      the_size
221    );
222    if ( status != RTEMS_SUCCESSFUL ) {
223      sbrk(-the_size);
224      errno = ENOMEM;
225      return (void *) 0;
226    }
227   
228    MSBUMP(space_available, the_size);
229
230    status = rtems_region_get_segment(
231      RTEMS_Malloc_Heap,
232       size,
233       RTEMS_NO_WAIT,
234       RTEMS_NO_TIMEOUT,
235       &return_this
236    );
237    if ( status != RTEMS_SUCCESSFUL ) {
238      errno = ENOMEM;
239      return (void *) 0;
240    }
241  }
242
243#ifdef MALLOC_STATS
244  if (return_this)
245  {
246      unsigned32 actual_size;
247      unsigned32 current_depth;
248      status = rtems_region_get_segment_size(
249                   RTEMS_Malloc_Heap, return_this, &actual_size);
250      MSBUMP(lifetime_allocated, actual_size);
251      current_depth = rtems_malloc_stats.lifetime_allocated -
252                   rtems_malloc_stats.lifetime_freed;
253      if (current_depth > rtems_malloc_stats.max_depth)
254          rtems_malloc_stats.max_depth = current_depth;
255  }
256#endif
257
258#ifdef MALLOC_DIRTY
259  (void) memset(return_this, 0xCF, size);
260#endif
261
262  return return_this;
263}
264
265void *calloc(
266  size_t nelem,
267  size_t elsize
268)
269{
270  register char *cptr;
271  int length;
272
273  MSBUMP(calloc_calls, 1);
274
275  length = nelem * elsize;
276  cptr = malloc( length );
277  if ( cptr )
278    memset( cptr, '\0', length );
279
280  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
281
282  return cptr;
283}
284
285void *realloc(
286  void *ptr,
287  size_t size
288)
289{
290  rtems_unsigned32  old_size;
291  rtems_status_code status;
292  char *new_area;
293
294  MSBUMP(realloc_calls, 1);
295
296  /*
297   *  Do not attempt to allocate memory if in a critical section or ISR.
298   */
299
300  if (_Thread_Dispatch_disable_level > 0)
301    return (void *) 0;
302 
303  if (_ISR_Nest_level > 0)
304    return (void *) 0;
305 
306  /*
307   * Continue with calloc().
308   */
309  if ( !ptr )
310    return malloc( size );
311
312  if ( !size ) {
313    free( ptr );
314    return (void *) 0;
315  }
316
317  new_area = malloc( size );
318 
319  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
320
321  /*
322   *  There used to be a free on this error case but it is wrong to
323   *  free the memory per OpenGroup Single UNIX Specification V2
324   *  and the C Standard.
325   */
326
327  if ( !new_area ) {
328    return (void *) 0;
329  }
330
331  status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &old_size );
332  if ( status != RTEMS_SUCCESSFUL ) {
333    errno = EINVAL;
334    return (void *) 0;
335  }
336
337  memcpy( new_area, ptr, (size < old_size) ? size : old_size );
338  free( ptr );
339
340  return new_area;
341
342}
343
344void free(
345  void *ptr
346)
347{
348  rtems_status_code status;
349
350  MSBUMP(free_calls, 1);
351
352  if ( !ptr )
353    return;
354
355  /*
356   *  Do not attempt to free memory if in a critical section or ISR.
357   */
358
359  if ((_Thread_Dispatch_disable_level > 0) || (_ISR_Nest_level > 0)) {
360    Chain_Append(&RTEMS_Malloc_GC_list, (Chain_Node *)ptr);
361    return;
362  }
363 
364#ifdef MALLOC_STATS
365  {
366      unsigned32        size;
367      status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &size );
368      if ( status == RTEMS_SUCCESSFUL ) {
369          MSBUMP(lifetime_freed, size);
370      }
371  }
372#endif
373 
374  status = rtems_region_return_segment( RTEMS_Malloc_Heap, ptr );
375  if ( status != RTEMS_SUCCESSFUL ) {
376    errno = EINVAL;
377    assert( 0 );
378  }
379}
380/* end if RTEMS_NEWLIB */
381#endif
382
383#ifdef MALLOC_STATS
384/*
385 * Dump the malloc statistics
386 * May be called via atexit()  (installable by our bsp) or
387 * at any time by user
388 */
389
390void malloc_dump(void)
391{
392    unsigned32 allocated = rtems_malloc_stats.lifetime_allocated -
393                     rtems_malloc_stats.lifetime_freed;
394
395    printf("Malloc stats\n");
396    printf("  avail:%uk  allocated:%uk (%d%%) "
397              "max:%uk (%d%%) lifetime:%Luk freed:%Luk\n",
398           (unsigned int) rtems_malloc_stats.space_available / 1024,
399           (unsigned int) allocated / 1024,
400           /* avoid float! */
401           (allocated * 100) / rtems_malloc_stats.space_available,
402           (unsigned int) rtems_malloc_stats.max_depth / 1024,
403           (rtems_malloc_stats.max_depth * 100) / rtems_malloc_stats.space_available,
404           (unsigned64) rtems_malloc_stats.lifetime_allocated / 1024,
405           (unsigned64) rtems_malloc_stats.lifetime_freed / 1024);
406    printf("  Call counts:   malloc:%d   free:%d   realloc:%d   calloc:%d\n",
407           rtems_malloc_stats.malloc_calls,
408           rtems_malloc_stats.free_calls,
409           rtems_malloc_stats.realloc_calls,
410           rtems_malloc_stats.calloc_calls);
411}
412
413
414void malloc_walk(size_t source, size_t printf_enabled)
415{
416   register Region_Control *the_region;
417   Objects_Locations        location;
418 
419  _RTEMS_Lock_allocator();                      /* to prevent deletion */
420   the_region = _Region_Get( RTEMS_Malloc_Heap, &location );
421   if ( location == OBJECTS_LOCAL )
422   {
423      _Heap_Walk( &the_region->Memory, source, printf_enabled );
424   }
425  _RTEMS_Unlock_allocator();
426}
427
428#else
429
430void malloc_dump(void)
431{
432   return;
433}
434 
435void malloc_walk(size_t source, size_t printf_enabled)
436{
437   return;
438}
439
440#endif
441
442/*
443 *  "Reentrant" versions of the above routines implemented above.
444 */
445
446#ifdef RTEMS_NEWLIB
447void *_malloc_r(
448  struct _reent *ignored,
449  size_t  size
450)
451{
452  return malloc( size );
453}
454
455void *_calloc_r(
456  struct _reent *ignored,
457  size_t nelem,
458  size_t elsize
459)
460{
461  return calloc( nelem, elsize );
462}
463
464void *_realloc_r(
465  struct _reent *ignored,
466  void *ptr,
467  size_t size
468)
469{
470  return realloc( ptr, size );
471}
472
473void _free_r(
474  struct _reent *ignored,
475  void *ptr
476)
477{
478  free( ptr );
479}
480
481#endif
Note: See TracBrowser for help on using the repository browser.