source: rtems/cpukit/libcsupport/src/malloc.c @ c11ca814

Last change on this file since c11ca814 was c11ca814, checked in by Joel Sherrill <joel.sherrill@…>, on Nov 26, 2003 at 5:51:00 PM

2003-11-26 Joel Sherrill <joel@…>

PR 523/filesystem

  • src/malloc.c: Make malloc family safer for use from ISRs and dispatching critical sections. If in a critical section while doing a free(), then the free is deferred until the next malloc() attempt.
  • Property mode set to 100644
File size: 10.8 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 (_System_state_Is_up(_System_state_Get())) {
174    if (_Thread_Dispatch_disable_level > 0)
175      return (void *) 0;
176 
177    if (_ISR_Nest_level > 0)
178      return (void *) 0;
179  }
180 
181  /*
182   *  If some free's have been deferred, then do them now.
183   */
184  while ((to_be_freed = Chain_Get(&RTEMS_Malloc_GC_list)) != NULL)
185    free(to_be_freed);
186
187  /*
188   * Try to give a segment in the current region if there is not
189   * enough space then try to grow the region using rtems_region_extend().
190   * If this fails then return a NULL pointer.
191   */
192
193  status = rtems_region_get_segment(
194    RTEMS_Malloc_Heap,
195    size,
196    RTEMS_NO_WAIT,
197    RTEMS_NO_TIMEOUT,
198    &return_this
199  );
200
201  if ( status != RTEMS_SUCCESSFUL ) {
202    /*
203     *  Round to the "requested sbrk amount" so hopefully we won't have
204     *  to grow again for a while.  This effectively does sbrk() calls
205     *  in "page" amounts.
206     */
207
208    sbrk_amount = RTEMS_Malloc_Sbrk_amount;
209
210    if ( sbrk_amount == 0 )
211      return (void *) 0;
212
213    the_size = ((size + sbrk_amount) / sbrk_amount * sbrk_amount);
214
215    if (((rtems_unsigned32)starting_address = (void *)sbrk(the_size)) 
216            == (rtems_unsigned32) -1)
217      return (void *) 0;
218
219    status = rtems_region_extend(
220      RTEMS_Malloc_Heap,
221      starting_address,
222      the_size
223    );
224    if ( status != RTEMS_SUCCESSFUL ) {
225      sbrk(-the_size);
226      errno = ENOMEM;
227      return (void *) 0;
228    }
229   
230    MSBUMP(space_available, the_size);
231
232    status = rtems_region_get_segment(
233      RTEMS_Malloc_Heap,
234       size,
235       RTEMS_NO_WAIT,
236       RTEMS_NO_TIMEOUT,
237       &return_this
238    );
239    if ( status != RTEMS_SUCCESSFUL ) {
240      errno = ENOMEM;
241      return (void *) 0;
242    }
243  }
244
245#ifdef MALLOC_STATS
246  if (return_this)
247  {
248      unsigned32 actual_size;
249      unsigned32 current_depth;
250      status = rtems_region_get_segment_size(
251                   RTEMS_Malloc_Heap, return_this, &actual_size);
252      MSBUMP(lifetime_allocated, actual_size);
253      current_depth = rtems_malloc_stats.lifetime_allocated -
254                   rtems_malloc_stats.lifetime_freed;
255      if (current_depth > rtems_malloc_stats.max_depth)
256          rtems_malloc_stats.max_depth = current_depth;
257  }
258#endif
259
260#ifdef MALLOC_DIRTY
261  (void) memset(return_this, 0xCF, size);
262#endif
263
264  return return_this;
265}
266
267void *calloc(
268  size_t nelem,
269  size_t elsize
270)
271{
272  register char *cptr;
273  int length;
274
275  MSBUMP(calloc_calls, 1);
276
277  length = nelem * elsize;
278  cptr = malloc( length );
279  if ( cptr )
280    memset( cptr, '\0', length );
281
282  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
283
284  return cptr;
285}
286
287void *realloc(
288  void *ptr,
289  size_t size
290)
291{
292  rtems_unsigned32  old_size;
293  rtems_status_code status;
294  char *new_area;
295
296  MSBUMP(realloc_calls, 1);
297
298  /*
299   *  Do not attempt to allocate memory if in a critical section or ISR.
300   */
301
302  if (_System_state_Is_up(_System_state_Get())) {
303    if (_Thread_Dispatch_disable_level > 0)
304      return (void *) 0;
305 
306    if (_ISR_Nest_level > 0)
307      return (void *) 0;
308  }
309 
310  /*
311   * Continue with calloc().
312   */
313  if ( !ptr )
314    return malloc( size );
315
316  if ( !size ) {
317    free( ptr );
318    return (void *) 0;
319  }
320
321  new_area = malloc( size );
322 
323  MSBUMP(malloc_calls, -1);   /* subtract off the malloc */
324
325  /*
326   *  There used to be a free on this error case but it is wrong to
327   *  free the memory per OpenGroup Single UNIX Specification V2
328   *  and the C Standard.
329   */
330
331  if ( !new_area ) {
332    return (void *) 0;
333  }
334
335  status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &old_size );
336  if ( status != RTEMS_SUCCESSFUL ) {
337    errno = EINVAL;
338    return (void *) 0;
339  }
340
341  memcpy( new_area, ptr, (size < old_size) ? size : old_size );
342  free( ptr );
343
344  return new_area;
345
346}
347
348void free(
349  void *ptr
350)
351{
352  rtems_status_code status;
353
354  MSBUMP(free_calls, 1);
355
356  if ( !ptr )
357    return;
358
359  /*
360   *  Do not attempt to free memory if in a critical section or ISR.
361   */
362
363  if (_System_state_Is_up(_System_state_Get())) {
364    if ((_Thread_Dispatch_disable_level > 0) || (_ISR_Nest_level > 0)) {
365      Chain_Append(&RTEMS_Malloc_GC_list, (Chain_Node *)ptr);
366      return;
367    }
368  }
369 
370#ifdef MALLOC_STATS
371  {
372      unsigned32        size;
373      status = rtems_region_get_segment_size( RTEMS_Malloc_Heap, ptr, &size );
374      if ( status == RTEMS_SUCCESSFUL ) {
375          MSBUMP(lifetime_freed, size);
376      }
377  }
378#endif
379 
380  status = rtems_region_return_segment( RTEMS_Malloc_Heap, ptr );
381  if ( status != RTEMS_SUCCESSFUL ) {
382    errno = EINVAL;
383    assert( 0 );
384  }
385}
386/* end if RTEMS_NEWLIB */
387#endif
388
389#ifdef MALLOC_STATS
390/*
391 * Dump the malloc statistics
392 * May be called via atexit()  (installable by our bsp) or
393 * at any time by user
394 */
395
396void malloc_dump(void)
397{
398    unsigned32 allocated = rtems_malloc_stats.lifetime_allocated -
399                     rtems_malloc_stats.lifetime_freed;
400
401    printf("Malloc stats\n");
402    printf("  avail:%uk  allocated:%uk (%d%%) "
403              "max:%uk (%d%%) lifetime:%Luk freed:%Luk\n",
404           (unsigned int) rtems_malloc_stats.space_available / 1024,
405           (unsigned int) allocated / 1024,
406           /* avoid float! */
407           (allocated * 100) / rtems_malloc_stats.space_available,
408           (unsigned int) rtems_malloc_stats.max_depth / 1024,
409           (rtems_malloc_stats.max_depth * 100) / rtems_malloc_stats.space_available,
410           (unsigned64) rtems_malloc_stats.lifetime_allocated / 1024,
411           (unsigned64) rtems_malloc_stats.lifetime_freed / 1024);
412    printf("  Call counts:   malloc:%d   free:%d   realloc:%d   calloc:%d\n",
413           rtems_malloc_stats.malloc_calls,
414           rtems_malloc_stats.free_calls,
415           rtems_malloc_stats.realloc_calls,
416           rtems_malloc_stats.calloc_calls);
417}
418
419
420void malloc_walk(size_t source, size_t printf_enabled)
421{
422   register Region_Control *the_region;
423   Objects_Locations        location;
424 
425  _RTEMS_Lock_allocator();                      /* to prevent deletion */
426   the_region = _Region_Get( RTEMS_Malloc_Heap, &location );
427   if ( location == OBJECTS_LOCAL )
428   {
429      _Heap_Walk( &the_region->Memory, source, printf_enabled );
430   }
431  _RTEMS_Unlock_allocator();
432}
433
434#else
435
436void malloc_dump(void)
437{
438   return;
439}
440 
441void malloc_walk(size_t source, size_t printf_enabled)
442{
443   return;
444}
445
446#endif
447
448/*
449 *  "Reentrant" versions of the above routines implemented above.
450 */
451
452#ifdef RTEMS_NEWLIB
453void *_malloc_r(
454  struct _reent *ignored,
455  size_t  size
456)
457{
458  return malloc( size );
459}
460
461void *_calloc_r(
462  struct _reent *ignored,
463  size_t nelem,
464  size_t elsize
465)
466{
467  return calloc( nelem, elsize );
468}
469
470void *_realloc_r(
471  struct _reent *ignored,
472  void *ptr,
473  size_t size
474)
475{
476  return realloc( ptr, size );
477}
478
479void _free_r(
480  struct _reent *ignored,
481  void *ptr
482)
483{
484  free( ptr );
485}
486
487#endif
Note: See TracBrowser for help on using the repository browser.