forked from xuos/xiuos
modify nuttx bug of lack of files
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
############################################################################
|
||||
# mm/mm_heap/Make.defs
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
# Core heap allocator logic
|
||||
|
||||
ifeq ($(CONFIG_MM_DEFAULT_MANAGER),y)
|
||||
|
||||
CSRCS += mm_initialize.c mm_sem.c mm_addfreechunk.c mm_size2ndx.c
|
||||
CSRCS += mm_malloc_usable_size.c mm_shrinkchunk.c
|
||||
CSRCS += mm_brkaddr.c mm_calloc.c mm_extend.c mm_free.c mm_mallinfo.c
|
||||
CSRCS += mm_malloc.c mm_memalign.c mm_realloc.c mm_zalloc.c mm_heapmember.c
|
||||
|
||||
ifeq ($(CONFIG_BUILD_KERNEL),y)
|
||||
CSRCS += mm_sbrk.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DEBUG_MM),y)
|
||||
CSRCS += mm_checkcorruption.c
|
||||
endif
|
||||
|
||||
|
||||
# Add the core heap directory to the build
|
||||
|
||||
DEPPATH += --dep-path mm_heap
|
||||
VPATH += :mm_heap
|
||||
|
||||
endif # CONFIG_MM_DEFAULT_MANAGER
|
||||
@@ -0,0 +1,229 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __MM_MM_HEAP_MM_H
|
||||
#define __MM_MM_HEAP_MM_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Configuration ************************************************************/
|
||||
|
||||
/* Chunk Header Definitions *************************************************/
|
||||
|
||||
/* These definitions define the characteristics of allocator
|
||||
*
|
||||
* MM_MIN_SHIFT is used to define MM_MIN_CHUNK.
|
||||
* MM_MIN_CHUNK - is the smallest physical chunk that can be allocated. It
|
||||
* must be at least a large as sizeof(struct mm_freenode_s). Larger values
|
||||
* may improve performance slightly, but will waste memory due to
|
||||
* quantization losses.
|
||||
*
|
||||
* MM_MAX_SHIFT is used to define MM_MAX_CHUNK
|
||||
* MM_MAX_CHUNK is the largest, contiguous chunk of memory that can be
|
||||
* allocated. It can range from 16-bytes to 4Gb. Larger values of
|
||||
* MM_MAX_SHIFT can cause larger data structure sizes and, perhaps,
|
||||
* minor performance losses.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_MM_SMALL) && UINTPTR_MAX <= UINT32_MAX
|
||||
/* Two byte offsets; Pointers may be 2 or 4 bytes;
|
||||
* sizeof(struct mm_freenode_s) is 8 or 12 bytes.
|
||||
* REVISIT: We could do better on machines with 16-bit addressing.
|
||||
*/
|
||||
|
||||
# define MM_MIN_SHIFT B2C_SHIFT( 4) /* 16 bytes */
|
||||
# define MM_MAX_SHIFT B2C_SHIFT(15) /* 32 Kb */
|
||||
|
||||
#elif defined(CONFIG_HAVE_LONG_LONG)
|
||||
/* Four byte offsets; Pointers may be 4 or 8 bytes
|
||||
* sizeof(struct mm_freenode_s) is 16 or 24 bytes.
|
||||
*/
|
||||
|
||||
# if UINTPTR_MAX <= UINT32_MAX
|
||||
# define MM_MIN_SHIFT B2C_SHIFT( 4) /* 16 bytes */
|
||||
# elif UINTPTR_MAX <= UINT64_MAX
|
||||
# define MM_MIN_SHIFT B2C_SHIFT( 5) /* 32 bytes */
|
||||
# endif
|
||||
# define MM_MAX_SHIFT B2C_SHIFT(22) /* 4 Mb */
|
||||
|
||||
#else
|
||||
/* Four byte offsets; Pointers must be 4 bytes.
|
||||
* sizeof(struct mm_freenode_s) is 16 bytes.
|
||||
*/
|
||||
|
||||
# define MM_MIN_SHIFT B2C_SHIFT( 4) /* 16 bytes */
|
||||
# define MM_MAX_SHIFT B2C_SHIFT(22) /* 4 Mb */
|
||||
#endif
|
||||
|
||||
/* All other definitions derive from these two */
|
||||
|
||||
#define MM_MIN_CHUNK (1 << MM_MIN_SHIFT)
|
||||
#define MM_MAX_CHUNK (1 << MM_MAX_SHIFT)
|
||||
#define MM_NNODES (MM_MAX_SHIFT - MM_MIN_SHIFT + 1)
|
||||
|
||||
#define MM_GRAN_MASK (MM_MIN_CHUNK - 1)
|
||||
#define MM_ALIGN_UP(a) (((a) + MM_GRAN_MASK) & ~MM_GRAN_MASK)
|
||||
#define MM_ALIGN_DOWN(a) ((a) & ~MM_GRAN_MASK)
|
||||
|
||||
/* An allocated chunk is distinguished from a free chunk by bit 31 (or 15)
|
||||
* of the 'preceding' chunk size. If set, then this is an allocated chunk.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_MM_SMALL
|
||||
# define MM_ALLOC_BIT 0x8000
|
||||
#else
|
||||
# define MM_ALLOC_BIT 0x80000000
|
||||
#endif
|
||||
#define MM_IS_ALLOCATED(n) \
|
||||
((int)((FAR struct mm_allocnode_s *)(n)->preceding) < 0)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
struct mm_heap_s;
|
||||
|
||||
/* Determines the size of the chunk size/offset type */
|
||||
|
||||
#ifdef CONFIG_MM_SMALL
|
||||
typedef uint16_t mmsize_t;
|
||||
# define MMSIZE_MAX UINT16_MAX
|
||||
#else
|
||||
typedef uint32_t mmsize_t;
|
||||
# define MMSIZE_MAX UINT32_MAX
|
||||
#endif
|
||||
|
||||
/* This describes an allocated chunk. An allocated chunk is
|
||||
* distinguished from a free chunk by bit 15/31 of the 'preceding' chunk
|
||||
* size. If set, then this is an allocated chunk.
|
||||
*/
|
||||
|
||||
struct mm_allocnode_s
|
||||
{
|
||||
mmsize_t size; /* Size of this chunk */
|
||||
mmsize_t preceding; /* Size of the preceding chunk */
|
||||
};
|
||||
|
||||
/* What is the size of the allocnode? */
|
||||
|
||||
#ifdef CONFIG_MM_SMALL
|
||||
# define SIZEOF_MM_ALLOCNODE B2C(4)
|
||||
#else
|
||||
# define SIZEOF_MM_ALLOCNODE B2C(8)
|
||||
#endif
|
||||
|
||||
#define CHECK_ALLOCNODE_SIZE \
|
||||
DEBUGASSERT(sizeof(struct mm_allocnode_s) == SIZEOF_MM_ALLOCNODE)
|
||||
|
||||
/* This describes a free chunk */
|
||||
|
||||
struct mm_freenode_s
|
||||
{
|
||||
mmsize_t size; /* Size of this chunk */
|
||||
mmsize_t preceding; /* Size of the preceding chunk */
|
||||
FAR struct mm_freenode_s *flink; /* Supports a doubly linked list */
|
||||
FAR struct mm_freenode_s *blink;
|
||||
};
|
||||
|
||||
struct mm_delaynode_s
|
||||
{
|
||||
FAR struct mm_delaynode_s *flink;
|
||||
};
|
||||
|
||||
/* What is the size of the freenode? */
|
||||
|
||||
#define MM_PTR_SIZE sizeof(FAR struct mm_freenode_s *)
|
||||
#define SIZEOF_MM_FREENODE (SIZEOF_MM_ALLOCNODE + 2*MM_PTR_SIZE)
|
||||
|
||||
#define CHECK_FREENODE_SIZE \
|
||||
DEBUGASSERT(sizeof(struct mm_freenode_s) == SIZEOF_MM_FREENODE)
|
||||
|
||||
/* This describes one heap (possibly with multiple regions) */
|
||||
|
||||
struct mm_heap_impl_s
|
||||
{
|
||||
/* Mutually exclusive access to this data set is enforced with
|
||||
* the following un-named semaphore.
|
||||
*/
|
||||
|
||||
sem_t mm_semaphore;
|
||||
pid_t mm_holder;
|
||||
int mm_counts_held;
|
||||
|
||||
/* This is the size of the heap provided to mm */
|
||||
|
||||
size_t mm_heapsize;
|
||||
|
||||
/* This is the first and last nodes of the heap */
|
||||
|
||||
FAR struct mm_allocnode_s *mm_heapstart[CONFIG_MM_REGIONS];
|
||||
FAR struct mm_allocnode_s *mm_heapend[CONFIG_MM_REGIONS];
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int mm_nregions;
|
||||
#endif
|
||||
|
||||
/* All free nodes are maintained in a doubly linked list. This
|
||||
* array provides some hooks into the list at various points to
|
||||
* speed searches for free nodes.
|
||||
*/
|
||||
|
||||
struct mm_freenode_s mm_nodelist[MM_NNODES];
|
||||
|
||||
/* Free delay list, for some situation can't do free immdiately */
|
||||
|
||||
FAR struct mm_delaynode_s *mm_delaylist;
|
||||
};
|
||||
|
||||
/* Functions contained in mm_sem.c ******************************************/
|
||||
|
||||
void mm_seminitialize(FAR struct mm_heap_s *heap);
|
||||
void mm_takesemaphore(FAR struct mm_heap_s *heap);
|
||||
int mm_trysemaphore(FAR struct mm_heap_s *heap);
|
||||
void mm_givesemaphore(FAR struct mm_heap_s *heap);
|
||||
|
||||
/* Functions contained in mm_shrinkchunk.c **********************************/
|
||||
|
||||
void mm_shrinkchunk(FAR struct mm_heap_s *heap,
|
||||
FAR struct mm_allocnode_s *node, size_t size);
|
||||
|
||||
/* Functions contained in mm_addfreechunk.c *********************************/
|
||||
|
||||
void mm_addfreechunk(FAR struct mm_heap_s *heap,
|
||||
FAR struct mm_freenode_s *node);
|
||||
|
||||
/* Functions contained in mm_size2ndx.c.c ***********************************/
|
||||
|
||||
int mm_size2ndx(size_t size);
|
||||
|
||||
#endif /* __MM_MM_HEAP_MM_H */
|
||||
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_addfreechunk.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_addfreechunk
|
||||
*
|
||||
* Description:
|
||||
* Add a free chunk to the node next. It is assumed that the caller holds
|
||||
* the mm semaphore
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_addfreechunk(FAR struct mm_heap_s *heap,
|
||||
FAR struct mm_freenode_s *node)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_freenode_s *next;
|
||||
FAR struct mm_freenode_s *prev;
|
||||
int ndx;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
DEBUGASSERT(node->size >= SIZEOF_MM_FREENODE);
|
||||
DEBUGASSERT((node->preceding & MM_ALLOC_BIT) == 0);
|
||||
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Convert the size to a nodelist index */
|
||||
|
||||
ndx = mm_size2ndx(node->size);
|
||||
|
||||
/* Now put the new node into the next */
|
||||
|
||||
for (prev = &heap_impl->mm_nodelist[ndx],
|
||||
next = heap_impl->mm_nodelist[ndx].flink;
|
||||
next && next->size && next->size < node->size;
|
||||
prev = next, next = next->flink);
|
||||
|
||||
/* Does it go in mid next or at the end? */
|
||||
|
||||
prev->flink = node;
|
||||
node->blink = prev;
|
||||
node->flink = next;
|
||||
|
||||
if (next)
|
||||
{
|
||||
/* The new node goes between prev and next */
|
||||
|
||||
next->blink = node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_brkaddr.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_brkaddr
|
||||
*
|
||||
* Description:
|
||||
* Return the break address of a heap region. Zero is returned if the
|
||||
* memory region is not initialized.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_brkaddr(FAR struct mm_heap_s *heap, int region)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
uintptr_t brkaddr;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
DEBUGASSERT(heap && region < heap_impl->mm_nregions);
|
||||
#else
|
||||
DEBUGASSERT(heap && region == 0);
|
||||
#endif
|
||||
|
||||
brkaddr = (uintptr_t)heap_impl->mm_heapend[region];
|
||||
return brkaddr ? (FAR void *)(brkaddr + SIZEOF_MM_ALLOCNODE) : 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_calloc.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_calloc
|
||||
*
|
||||
* Descriptor:
|
||||
* mm_calloc() calculates the size of the allocation and calls mm_zalloc()
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_calloc(FAR struct mm_heap_s *heap, size_t n, size_t elem_size)
|
||||
{
|
||||
FAR void *ret = NULL;
|
||||
|
||||
/* Verify input parameters */
|
||||
|
||||
if (n > 0 && elem_size > 0)
|
||||
{
|
||||
/* Assure that the following multiplication cannot overflow the size_t
|
||||
* type, i.e., that: SIZE_MAX >= n * elem_size
|
||||
*
|
||||
* Refer to SEI CERT C Coding Standard.
|
||||
*/
|
||||
|
||||
if (n <= (SIZE_MAX / elem_size))
|
||||
{
|
||||
ret = mm_zalloc(heap, n * elem_size);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_checkcorruption.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/mm/mm.h>
|
||||
#include <nuttx/irq.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_checkcorruption
|
||||
*
|
||||
* Description:
|
||||
* mm_checkcorruption is used to check whether memory heap is normal.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_checkcorruption(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_allocnode_s *node;
|
||||
FAR struct mm_allocnode_s *prev;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int region;
|
||||
#else
|
||||
# define region 0
|
||||
#endif
|
||||
|
||||
/* Visit each region */
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
for (region = 0; region < heap_impl->mm_nregions; region++)
|
||||
#endif
|
||||
{
|
||||
irqstate_t flags = 0;
|
||||
|
||||
prev = NULL;
|
||||
|
||||
/* Visit each node in the region
|
||||
* Retake the semaphore for each region to reduce latencies
|
||||
*/
|
||||
|
||||
if (up_interrupt_context() || sched_idletask())
|
||||
{
|
||||
if (heap_impl->mm_counts_held)
|
||||
{
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
flags = enter_critical_section();
|
||||
}
|
||||
else
|
||||
{
|
||||
mm_takesemaphore(heap);
|
||||
}
|
||||
|
||||
for (node = heap_impl->mm_heapstart[region];
|
||||
node < heap_impl->mm_heapend[region];
|
||||
node = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)node + node->size))
|
||||
{
|
||||
if ((node->preceding & MM_ALLOC_BIT) != 0)
|
||||
{
|
||||
assert(node->size >= SIZEOF_MM_ALLOCNODE);
|
||||
}
|
||||
else
|
||||
{
|
||||
FAR struct mm_freenode_s *fnode = (FAR void *)node;
|
||||
|
||||
assert(node->size >= SIZEOF_MM_FREENODE);
|
||||
assert(fnode->blink->flink == fnode);
|
||||
assert(fnode->blink->size <= fnode->size);
|
||||
assert(fnode->flink == NULL ||
|
||||
fnode->flink->blink == fnode);
|
||||
assert(fnode->flink == NULL ||
|
||||
fnode->flink->size == 0 ||
|
||||
fnode->flink->size >= fnode->size);
|
||||
}
|
||||
|
||||
assert(prev == NULL ||
|
||||
prev->size == (node->preceding & ~MM_ALLOC_BIT));
|
||||
prev = node;
|
||||
}
|
||||
|
||||
assert(node == heap_impl->mm_heapend[region]);
|
||||
|
||||
if (up_interrupt_context() || sched_idletask())
|
||||
{
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
mm_givesemaphore(heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_extend.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MIN_EXTEND (2 * SIZEOF_MM_ALLOCNODE)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_extend
|
||||
*
|
||||
* Description:
|
||||
* Extend a heap region by add a block of (virtually) contiguous memory
|
||||
* to the end of the heap.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_extend(FAR struct mm_heap_s *heap, FAR void *mem, size_t size,
|
||||
int region)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_allocnode_s *oldnode;
|
||||
FAR struct mm_allocnode_s *newnode;
|
||||
uintptr_t blockstart;
|
||||
uintptr_t blockend;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Make sure that we were passed valid parameters */
|
||||
|
||||
DEBUGASSERT(heap && mem);
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
DEBUGASSERT(size >= MIN_EXTEND &&
|
||||
(size_t)region < (size_t)heap_impl->mm_nregions);
|
||||
#else
|
||||
DEBUGASSERT(size >= MIN_EXTEND && region == 0);
|
||||
#endif
|
||||
|
||||
/* Make sure that the memory region are properly aligned */
|
||||
|
||||
blockstart = (uintptr_t)mem;
|
||||
blockend = blockstart + size;
|
||||
|
||||
DEBUGASSERT(MM_ALIGN_UP(blockstart) == blockstart);
|
||||
DEBUGASSERT(MM_ALIGN_DOWN(blockend) == blockend);
|
||||
|
||||
/* Take the memory manager semaphore */
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
|
||||
/* Get the terminal node in the old heap. The block to extend must
|
||||
* immediately follow this node.
|
||||
*/
|
||||
|
||||
oldnode = heap_impl->mm_heapend[region];
|
||||
DEBUGASSERT((uintptr_t)oldnode + SIZEOF_MM_ALLOCNODE == (uintptr_t)mem);
|
||||
|
||||
/* The size of the old node now extends to the new terminal node.
|
||||
* This is the old size (SIZEOF_MM_ALLOCNODE) plus the size of
|
||||
* the block (size) minus the size of the new terminal node
|
||||
* (SIZEOF_MM_ALLOCNODE) or simply:
|
||||
*/
|
||||
|
||||
oldnode->size = size;
|
||||
|
||||
/* The old node should already be marked as allocated */
|
||||
|
||||
DEBUGASSERT((oldnode->preceding & MM_ALLOC_BIT) != 0);
|
||||
|
||||
/* Get and initialize the new terminal node in the heap */
|
||||
|
||||
newnode = (FAR struct mm_allocnode_s *)
|
||||
(blockend - SIZEOF_MM_ALLOCNODE);
|
||||
newnode->size = SIZEOF_MM_ALLOCNODE;
|
||||
newnode->preceding = oldnode->size | MM_ALLOC_BIT;
|
||||
|
||||
heap_impl->mm_heapend[region] = newnode;
|
||||
mm_givesemaphore(heap);
|
||||
|
||||
/* Finally "free" the new block of memory where the old terminal node was
|
||||
* located.
|
||||
*/
|
||||
|
||||
mm_free(heap, (FAR void *)mem);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_free.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
|
||||
static void mm_add_delaylist(FAR struct mm_heap_s *heap, FAR void *mem)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_delaynode_s *tmp = mem;
|
||||
irqstate_t flags;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Delay the deallocation until a more appropriate time. */
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
tmp->flink = heap_impl->mm_delaylist;
|
||||
heap_impl->mm_delaylist = tmp;
|
||||
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_free
|
||||
*
|
||||
* Description:
|
||||
* Returns a chunk of memory to the list of free nodes, merging with
|
||||
* adjacent free chunks if possible.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_free(FAR struct mm_heap_s *heap, FAR void *mem)
|
||||
{
|
||||
FAR struct mm_freenode_s *node;
|
||||
FAR struct mm_freenode_s *prev;
|
||||
FAR struct mm_freenode_s *next;
|
||||
int ret;
|
||||
|
||||
UNUSED(ret);
|
||||
minfo("Freeing %p\n", mem);
|
||||
|
||||
/* Protect against attempts to free a NULL reference */
|
||||
|
||||
if (!mem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
|
||||
/* Check current environment */
|
||||
|
||||
if (up_interrupt_context())
|
||||
{
|
||||
/* We are in ISR, add to mm_delaylist */
|
||||
|
||||
mm_add_delaylist(heap, mem);
|
||||
return;
|
||||
}
|
||||
else if ((ret = mm_trysemaphore(heap)) == 0)
|
||||
{
|
||||
/* Got the sem, do free immediately */
|
||||
}
|
||||
else if (ret == -ESRCH || sched_idletask())
|
||||
{
|
||||
/* We are in IDLE task & can't get sem, or meet -ESRCH return,
|
||||
* which means we are in situations during context switching(See
|
||||
* mm_trysemaphore() & getpid()). Then add to mm_delaylist.
|
||||
*/
|
||||
|
||||
mm_add_delaylist(heap, mem);
|
||||
return;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* We need to hold the MM semaphore while we muck with the
|
||||
* nodelist.
|
||||
*/
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
}
|
||||
|
||||
DEBUGASSERT(mm_heapmember(heap, mem));
|
||||
|
||||
/* Map the memory chunk into a free node */
|
||||
|
||||
node = (FAR struct mm_freenode_s *)((FAR char *)mem - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Sanity check against double-frees */
|
||||
|
||||
DEBUGASSERT(node->preceding & MM_ALLOC_BIT);
|
||||
|
||||
node->preceding &= ~MM_ALLOC_BIT;
|
||||
|
||||
/* Check if the following node is free and, if so, merge it */
|
||||
|
||||
next = (FAR struct mm_freenode_s *)((FAR char *)node + node->size);
|
||||
DEBUGASSERT((next->preceding & ~MM_ALLOC_BIT) == node->size);
|
||||
if ((next->preceding & MM_ALLOC_BIT) == 0)
|
||||
{
|
||||
FAR struct mm_allocnode_s *andbeyond;
|
||||
|
||||
/* Get the node following the next node (which will
|
||||
* become the new next node). We know that we can never
|
||||
* index past the tail chunk because it is always allocated.
|
||||
*/
|
||||
|
||||
andbeyond = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)next + next->size);
|
||||
|
||||
/* Remove the next node. There must be a predecessor,
|
||||
* but there may not be a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(next->blink);
|
||||
next->blink->flink = next->flink;
|
||||
if (next->flink)
|
||||
{
|
||||
next->flink->blink = next->blink;
|
||||
}
|
||||
|
||||
/* Then merge the two chunks */
|
||||
|
||||
node->size += next->size;
|
||||
andbeyond->preceding = node->size |
|
||||
(andbeyond->preceding & MM_ALLOC_BIT);
|
||||
next = (FAR struct mm_freenode_s *)andbeyond;
|
||||
}
|
||||
|
||||
/* Check if the preceding node is also free and, if so, merge
|
||||
* it with this node
|
||||
*/
|
||||
|
||||
prev = (FAR struct mm_freenode_s *)((FAR char *)node - node->preceding);
|
||||
DEBUGASSERT((node->preceding & ~MM_ALLOC_BIT) == prev->size);
|
||||
if ((prev->preceding & MM_ALLOC_BIT) == 0)
|
||||
{
|
||||
/* Remove the node. There must be a predecessor, but there may
|
||||
* not be a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(prev->blink);
|
||||
prev->blink->flink = prev->flink;
|
||||
if (prev->flink)
|
||||
{
|
||||
prev->flink->blink = prev->blink;
|
||||
}
|
||||
|
||||
/* Then merge the two chunks */
|
||||
|
||||
prev->size += node->size;
|
||||
next->preceding = prev->size | (next->preceding & MM_ALLOC_BIT);
|
||||
node = prev;
|
||||
}
|
||||
|
||||
/* Add the merged node to the nodelist */
|
||||
|
||||
mm_addfreechunk(heap, node);
|
||||
mm_givesemaphore(heap);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_heapmember.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_heapmember
|
||||
*
|
||||
* Description:
|
||||
* Check if an address lies in the heap.
|
||||
*
|
||||
* Parameters:
|
||||
* heap - The heap to check
|
||||
* mem - The address to check
|
||||
*
|
||||
* Return Value:
|
||||
* true if the address is a member of the heap. false if not
|
||||
* not. If the address is not a member of the heap, then it
|
||||
* must be a member of the user-space heap (unchecked)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool mm_heapmember(FAR struct mm_heap_s *heap, FAR void *mem)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int i;
|
||||
|
||||
/* A valid address from the heap for this region would have to lie
|
||||
* between the region's two guard nodes.
|
||||
*/
|
||||
|
||||
for (i = 0; i < heap_impl->mm_nregions; i++)
|
||||
{
|
||||
if (mem > (FAR void *)heap_impl->mm_heapstart[i] &&
|
||||
mem < (FAR void *)heap_impl->mm_heapend[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* The address does not like any any region assigned to the heap */
|
||||
|
||||
return false;
|
||||
|
||||
#else
|
||||
/* A valid address from the heap would have to lie between the
|
||||
* two guard nodes.
|
||||
*/
|
||||
|
||||
if (mem > (FAR void *)heap_impl->mm_heapstart[0] &&
|
||||
mem < (FAR void *)heap_impl->mm_heapend[0])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Otherwise, the address does not lie in the heap */
|
||||
|
||||
return false;
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_initialize.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_addregion
|
||||
*
|
||||
* Description:
|
||||
* This function adds a region of contiguous memory to the selected heap.
|
||||
*
|
||||
* Input Parameters:
|
||||
* heap - The selected heap
|
||||
* heapstart - Start of the heap region
|
||||
* heapsize - Size of the heap region
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_addregion(FAR struct mm_heap_s *heap, FAR void *heapstart,
|
||||
size_t heapsize)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_freenode_s *node;
|
||||
uintptr_t heapbase;
|
||||
uintptr_t heapend;
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int IDX;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
IDX = heap_impl->mm_nregions;
|
||||
|
||||
/* Writing past CONFIG_MM_REGIONS would have catastrophic consequences */
|
||||
|
||||
DEBUGASSERT(IDX < CONFIG_MM_REGIONS);
|
||||
if (IDX >= CONFIG_MM_REGIONS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
# define IDX 0
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MM_SMALL) && !defined(CONFIG_SMALL_MEMORY)
|
||||
/* If the MCU handles wide addresses but the memory manager is configured
|
||||
* for a small heap, then verify that the caller is not doing something
|
||||
* crazy.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(heapsize <= MMSIZE_MAX + 1);
|
||||
#endif
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
|
||||
/* Adjust the provide heap start and size so that they are both aligned
|
||||
* with the MM_MIN_CHUNK size.
|
||||
*/
|
||||
|
||||
heapbase = MM_ALIGN_UP((uintptr_t)heapstart);
|
||||
heapend = MM_ALIGN_DOWN((uintptr_t)heapstart + (uintptr_t)heapsize);
|
||||
heapsize = heapend - heapbase;
|
||||
|
||||
minfo("Region %d: base=%p size=%zu\n", IDX + 1, heapstart, heapsize);
|
||||
|
||||
/* Add the size of this region to the total size of the heap */
|
||||
|
||||
heap_impl->mm_heapsize += heapsize;
|
||||
|
||||
/* Create two "allocated" guard nodes at the beginning and end of
|
||||
* the heap. These only serve to keep us from allocating outside
|
||||
* of the heap.
|
||||
*
|
||||
* And create one free node between the guard nodes that contains
|
||||
* all available memory.
|
||||
*/
|
||||
|
||||
heap_impl->mm_heapstart[IDX] = (FAR struct mm_allocnode_s *)
|
||||
heapbase;
|
||||
heap_impl->mm_heapstart[IDX]->size = SIZEOF_MM_ALLOCNODE;
|
||||
heap_impl->mm_heapstart[IDX]->preceding = MM_ALLOC_BIT;
|
||||
node = (FAR struct mm_freenode_s *)
|
||||
(heapbase + SIZEOF_MM_ALLOCNODE);
|
||||
node->size = heapsize - 2*SIZEOF_MM_ALLOCNODE;
|
||||
node->preceding = SIZEOF_MM_ALLOCNODE;
|
||||
heap_impl->mm_heapend[IDX] = (FAR struct mm_allocnode_s *)
|
||||
(heapend - SIZEOF_MM_ALLOCNODE);
|
||||
heap_impl->mm_heapend[IDX]->size = SIZEOF_MM_ALLOCNODE;
|
||||
heap_impl->mm_heapend[IDX]->preceding = node->size | MM_ALLOC_BIT;
|
||||
|
||||
#undef IDX
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
heap_impl->mm_nregions++;
|
||||
#endif
|
||||
|
||||
/* Add the single, large free node to the nodelist */
|
||||
|
||||
mm_addfreechunk(heap, node);
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the selected heap data structures, providing the initial
|
||||
* heap region.
|
||||
*
|
||||
* Input Parameters:
|
||||
* heap - The selected heap
|
||||
* heapstart - Start of the initial heap region
|
||||
* heapsize - Size of the initial heap region
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_initialize(FAR struct mm_heap_s *heap, FAR void *heapstart,
|
||||
size_t heapsize)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
int i;
|
||||
|
||||
minfo("Heap: start=%p size=%zu\n", heapstart, heapsize);
|
||||
|
||||
/* Reserve a block space for mm_heap_impl_s context */
|
||||
|
||||
DEBUGASSERT(heapsize > sizeof(struct mm_heap_impl_s));
|
||||
heap->mm_impl = (FAR struct mm_heap_impl_s *)heapstart;
|
||||
heap_impl = heap->mm_impl;
|
||||
heapsize -= sizeof(struct mm_heap_impl_s);
|
||||
heapstart = (FAR char *)heapstart + sizeof(struct mm_heap_impl_s);
|
||||
|
||||
/* The following two lines have cause problems for some older ZiLog
|
||||
* compilers in the past (but not the more recent). Life is easier if we
|
||||
* just the suppress them altogther for those tools.
|
||||
*/
|
||||
|
||||
#ifndef __ZILOG__
|
||||
CHECK_ALLOCNODE_SIZE;
|
||||
CHECK_FREENODE_SIZE;
|
||||
#endif
|
||||
DEBUGASSERT(MM_MIN_CHUNK >= SIZEOF_MM_FREENODE);
|
||||
DEBUGASSERT(MM_MIN_CHUNK >= SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Set up global variables */
|
||||
|
||||
heap_impl->mm_heapsize = 0;
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
heap_impl->mm_nregions = 0;
|
||||
#endif
|
||||
|
||||
/* Initialize mm_delaylist */
|
||||
|
||||
heap_impl->mm_delaylist = NULL;
|
||||
|
||||
/* Initialize the node array */
|
||||
|
||||
memset(heap_impl->mm_nodelist, 0,
|
||||
sizeof(struct mm_freenode_s) * MM_NNODES);
|
||||
for (i = 1; i < MM_NNODES; i++)
|
||||
{
|
||||
heap_impl->mm_nodelist[i - 1].flink = &heap_impl->mm_nodelist[i];
|
||||
heap_impl->mm_nodelist[i].blink = &heap_impl->mm_nodelist[i - 1];
|
||||
}
|
||||
|
||||
/* Initialize the malloc semaphore to one (to support one-at-
|
||||
* a-time access to private data sets).
|
||||
*/
|
||||
|
||||
mm_seminitialize(heap);
|
||||
|
||||
/* Add the initial region of memory to the heap */
|
||||
|
||||
mm_addregion(heap, heapstart, heapsize);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_mallinfo.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_mallinfo
|
||||
*
|
||||
* Description:
|
||||
* mallinfo returns a copy of updated current heap information.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mm_mallinfo(FAR struct mm_heap_s *heap, FAR struct mallinfo *info)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_allocnode_s *node;
|
||||
FAR struct mm_allocnode_s *prev;
|
||||
size_t mxordblk = 0;
|
||||
int ordblks = 0; /* Number of non-inuse chunks */
|
||||
size_t uordblks = 0; /* Total allocated space */
|
||||
size_t fordblks = 0; /* Total non-inuse space */
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int region;
|
||||
#else
|
||||
# define region 0
|
||||
#endif
|
||||
|
||||
DEBUGASSERT(info);
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Visit each region */
|
||||
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
for (region = 0; region < heap_impl->mm_nregions; region++)
|
||||
#endif
|
||||
{
|
||||
prev = NULL;
|
||||
|
||||
/* Visit each node in the region
|
||||
* Retake the semaphore for each region to reduce latencies
|
||||
*/
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
|
||||
for (node = heap_impl->mm_heapstart[region];
|
||||
node < heap_impl->mm_heapend[region];
|
||||
node = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)node + node->size))
|
||||
{
|
||||
minfo("region=%d node=%p size=%u preceding=%u (%c)\n",
|
||||
region, node, (unsigned int)node->size,
|
||||
(unsigned int)(node->preceding & ~MM_ALLOC_BIT),
|
||||
(node->preceding & MM_ALLOC_BIT) ? 'A' : 'F');
|
||||
|
||||
/* Check if the node corresponds to an allocated memory chunk */
|
||||
|
||||
if ((node->preceding & MM_ALLOC_BIT) != 0)
|
||||
{
|
||||
DEBUGASSERT(node->size >= SIZEOF_MM_ALLOCNODE);
|
||||
uordblks += node->size;
|
||||
}
|
||||
else
|
||||
{
|
||||
FAR struct mm_freenode_s *fnode = (FAR void *)node;
|
||||
|
||||
DEBUGASSERT(node->size >= SIZEOF_MM_FREENODE);
|
||||
DEBUGASSERT(fnode->blink->flink == fnode);
|
||||
DEBUGASSERT(fnode->blink->size <= fnode->size);
|
||||
DEBUGASSERT(fnode->flink == NULL ||
|
||||
fnode->flink->blink == fnode);
|
||||
DEBUGASSERT(fnode->flink == NULL ||
|
||||
fnode->flink->size == 0 ||
|
||||
fnode->flink->size >= fnode->size);
|
||||
ordblks++;
|
||||
fordblks += node->size;
|
||||
if (node->size > mxordblk)
|
||||
{
|
||||
mxordblk = node->size;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGASSERT(prev == NULL ||
|
||||
prev->size == (node->preceding & ~MM_ALLOC_BIT));
|
||||
prev = node;
|
||||
}
|
||||
|
||||
minfo("region=%d node=%p heapend=%p\n",
|
||||
region, node, heap_impl->mm_heapend[region]);
|
||||
DEBUGASSERT(node == heap_impl->mm_heapend[region]);
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
|
||||
uordblks += SIZEOF_MM_ALLOCNODE; /* account for the tail node */
|
||||
}
|
||||
#undef region
|
||||
|
||||
DEBUGASSERT(uordblks + fordblks == heap_impl->mm_heapsize);
|
||||
|
||||
info->arena = heap_impl->mm_heapsize;
|
||||
info->ordblks = ordblks;
|
||||
info->mxordblk = mxordblk;
|
||||
info->uordblks = uordblks;
|
||||
info->fordblks = fordblks;
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_malloc.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void mm_free_delaylist(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_delaynode_s *tmp;
|
||||
irqstate_t flags;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Move the delay list to local */
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
tmp = heap_impl->mm_delaylist;
|
||||
heap_impl->mm_delaylist = NULL;
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
/* Test if the delayed is empty */
|
||||
|
||||
while (tmp)
|
||||
{
|
||||
FAR void *address;
|
||||
|
||||
/* Get the first delayed deallocation */
|
||||
|
||||
address = tmp;
|
||||
tmp = tmp->flink;
|
||||
|
||||
/* The address should always be non-NULL since that was checked in the
|
||||
* 'while' condition above.
|
||||
*/
|
||||
|
||||
mm_free(heap, address);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_malloc
|
||||
*
|
||||
* Description:
|
||||
* Find the smallest chunk that satisfies the request. Take the memory from
|
||||
* that chunk, save the remaining, smaller chunk (if any).
|
||||
*
|
||||
* 8-byte alignment of the allocated data is assured.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_malloc(FAR struct mm_heap_s *heap, size_t size)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
FAR struct mm_freenode_s *node;
|
||||
size_t alignsize;
|
||||
FAR void *ret = NULL;
|
||||
int ndx;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Firstly, free mm_delaylist */
|
||||
|
||||
mm_free_delaylist(heap);
|
||||
|
||||
/* Ignore zero-length allocations */
|
||||
|
||||
if (size < 1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adjust the size to account for (1) the size of the allocated node and
|
||||
* (2) to make sure that it is an even multiple of our granule size.
|
||||
*/
|
||||
|
||||
alignsize = MM_ALIGN_UP(size + SIZEOF_MM_ALLOCNODE);
|
||||
if (alignsize < size)
|
||||
{
|
||||
/* There must have been an integer overflow */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUGASSERT(alignsize >= MM_MIN_CHUNK);
|
||||
DEBUGASSERT(alignsize >= SIZEOF_MM_FREENODE);
|
||||
|
||||
/* We need to hold the MM semaphore while we muck with the nodelist. */
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
|
||||
/* Get the location in the node list to start the search. Special case
|
||||
* really big allocations
|
||||
*/
|
||||
|
||||
if (alignsize >= MM_MAX_CHUNK)
|
||||
{
|
||||
ndx = MM_NNODES - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert the request size into a nodelist index */
|
||||
|
||||
ndx = mm_size2ndx(alignsize);
|
||||
}
|
||||
|
||||
/* Search for a large enough chunk in the list of nodes. This list is
|
||||
* ordered by size, but will have occasional zero sized nodes as we visit
|
||||
* other mm_nodelist[] entries.
|
||||
*/
|
||||
|
||||
for (node = heap_impl->mm_nodelist[ndx].flink;
|
||||
node && node->size < alignsize;
|
||||
node = node->flink)
|
||||
{
|
||||
DEBUGASSERT(node->blink->flink == node);
|
||||
}
|
||||
|
||||
/* If we found a node with non-zero size, then this is one to use. Since
|
||||
* the list is ordered, we know that is must be best fitting chunk
|
||||
* available.
|
||||
*/
|
||||
|
||||
if (node)
|
||||
{
|
||||
FAR struct mm_freenode_s *remainder;
|
||||
FAR struct mm_freenode_s *next;
|
||||
size_t remaining;
|
||||
|
||||
/* Remove the node. There must be a predecessor, but there may not be
|
||||
* a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(node->blink);
|
||||
node->blink->flink = node->flink;
|
||||
if (node->flink)
|
||||
{
|
||||
node->flink->blink = node->blink;
|
||||
}
|
||||
|
||||
/* Check if we have to split the free node into one of the allocated
|
||||
* size and another smaller freenode. In some cases, the remaining
|
||||
* bytes can be smaller (they may be SIZEOF_MM_ALLOCNODE). In that
|
||||
* case, we will just carry the few wasted bytes at the end of the
|
||||
* allocation.
|
||||
*/
|
||||
|
||||
remaining = node->size - alignsize;
|
||||
if (remaining >= SIZEOF_MM_FREENODE)
|
||||
{
|
||||
/* Get a pointer to the next node in physical memory */
|
||||
|
||||
next = (FAR struct mm_freenode_s *)
|
||||
(((FAR char *)node) + node->size);
|
||||
|
||||
/* Create the remainder node */
|
||||
|
||||
remainder = (FAR struct mm_freenode_s *)
|
||||
(((FAR char *)node) + alignsize);
|
||||
|
||||
remainder->size = remaining;
|
||||
remainder->preceding = alignsize;
|
||||
|
||||
/* Adjust the size of the node under consideration */
|
||||
|
||||
node->size = alignsize;
|
||||
|
||||
/* Adjust the 'preceding' size of the (old) next node, preserving
|
||||
* the allocated flag.
|
||||
*/
|
||||
|
||||
next->preceding = remaining | (next->preceding & MM_ALLOC_BIT);
|
||||
|
||||
/* Add the remainder back into the nodelist */
|
||||
|
||||
mm_addfreechunk(heap, remainder);
|
||||
}
|
||||
|
||||
/* Handle the case of an exact size match */
|
||||
|
||||
node->preceding |= MM_ALLOC_BIT;
|
||||
ret = (FAR void *)((FAR char *)node + SIZEOF_MM_ALLOCNODE);
|
||||
}
|
||||
|
||||
DEBUGASSERT(ret == NULL || mm_heapmember(heap, ret));
|
||||
mm_givesemaphore(heap);
|
||||
|
||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||
if (ret)
|
||||
{
|
||||
memset(ret, 0xaa, alignsize - SIZEOF_MM_ALLOCNODE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If CONFIG_DEBUG_MM is defined, then output the result of the allocation
|
||||
* to the SYSLOG.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_MM
|
||||
if (!ret)
|
||||
{
|
||||
mwarn("WARNING: Allocation failed, size %zu\n", alignsize);
|
||||
}
|
||||
else
|
||||
{
|
||||
minfo("Allocated %p, size %zu\n", ret, alignsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_malloc_usable_size.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
size_t malloc_usable_size(FAR void *mem)
|
||||
{
|
||||
FAR struct mm_freenode_s *node;
|
||||
|
||||
/* Protect against attempts to query a NULL reference */
|
||||
|
||||
if (!mem)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Map the memory chunk into a free node */
|
||||
|
||||
node = (FAR struct mm_freenode_s *)((FAR char *)mem - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Sanity check against double-frees */
|
||||
|
||||
DEBUGASSERT(node->preceding & MM_ALLOC_BIT);
|
||||
|
||||
return node->size - SIZEOF_MM_ALLOCNODE;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_memalign.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_memalign
|
||||
*
|
||||
* Description:
|
||||
* memalign requests more than enough space from malloc, finds a region
|
||||
* within that chunk that meets the alignment request and then frees any
|
||||
* leading or trailing space.
|
||||
*
|
||||
* The alignment argument must be a power of two. 8-byte alignment is
|
||||
* guaranteed by normal malloc calls.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_memalign(FAR struct mm_heap_s *heap, size_t alignment,
|
||||
size_t size)
|
||||
{
|
||||
FAR struct mm_allocnode_s *node;
|
||||
size_t rawchunk;
|
||||
size_t alignedchunk;
|
||||
size_t mask = (size_t)(alignment - 1);
|
||||
size_t allocsize;
|
||||
size_t newsize;
|
||||
|
||||
/* Make sure that alignment is less than half max size_t */
|
||||
|
||||
if (alignment >= (SIZE_MAX / 2))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Make sure that alignment is a power of 2 */
|
||||
|
||||
if ((alignment & -alignment) != alignment)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If this requested alinement's less than or equal to the natural
|
||||
* alignment of malloc, then just let malloc do the work.
|
||||
*/
|
||||
|
||||
if (alignment <= MM_MIN_CHUNK)
|
||||
{
|
||||
return mm_malloc(heap, size);
|
||||
}
|
||||
|
||||
/* Adjust the size to account for (1) the size of the allocated node, (2)
|
||||
* to make sure that it is an even multiple of our granule size, and to
|
||||
* include the alignment amount.
|
||||
*
|
||||
* Notice that we increase the allocation size by twice the requested
|
||||
* alignment. We do this so that there will be at least two valid
|
||||
* alignment points within the allocated memory.
|
||||
*
|
||||
* NOTE: These are sizes given to malloc and not chunk sizes. They do
|
||||
* not include SIZEOF_MM_ALLOCNODE.
|
||||
*/
|
||||
|
||||
newsize = MM_ALIGN_UP(size); /* Make multiples of our granule size */
|
||||
|
||||
allocsize = newsize + 2 * alignment; /* Add double full alignment size */
|
||||
|
||||
if ((newsize < size) || (allocsize < newsize))
|
||||
{
|
||||
/* Integer overflow */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Then malloc that size */
|
||||
|
||||
rawchunk = (size_t)mm_malloc(heap, allocsize);
|
||||
if (rawchunk == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We need to hold the MM semaphore while we muck with the chunks and
|
||||
* nodelist.
|
||||
*/
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
|
||||
/* Get the node associated with the allocation and the next node after
|
||||
* the allocation.
|
||||
*/
|
||||
|
||||
node = (FAR struct mm_allocnode_s *)(rawchunk - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Find the aligned subregion */
|
||||
|
||||
alignedchunk = (rawchunk + mask) & ~mask;
|
||||
|
||||
/* Check if there is free space at the beginning of the aligned chunk */
|
||||
|
||||
if (alignedchunk != rawchunk)
|
||||
{
|
||||
FAR struct mm_allocnode_s *newnode;
|
||||
FAR struct mm_allocnode_s *next;
|
||||
size_t precedingsize;
|
||||
|
||||
/* Get the node the next node after the allocation. */
|
||||
|
||||
next = (FAR struct mm_allocnode_s *)((FAR char *)node + node->size);
|
||||
|
||||
/* Make sure that there is space to convert the preceding
|
||||
* mm_allocnode_s into an mm_freenode_s. I think that this should
|
||||
* always be true
|
||||
*/
|
||||
|
||||
DEBUGASSERT(alignedchunk >= rawchunk + 8);
|
||||
|
||||
newnode = (FAR struct mm_allocnode_s *)
|
||||
(alignedchunk - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Preceding size is full size of the new 'node,' including
|
||||
* SIZEOF_MM_ALLOCNODE
|
||||
*/
|
||||
|
||||
precedingsize = (size_t)newnode - (size_t)node;
|
||||
|
||||
/* If we were unlucky, then the alignedchunk can lie in such a position
|
||||
* that precedingsize < SIZEOF_NODE_FREENODE. We can't let that happen
|
||||
* because we are going to cast 'node' to struct mm_freenode_s below.
|
||||
* This is why we allocated memory large enough to support two
|
||||
* alignment points. In this case, we will simply use the second
|
||||
* alignment point.
|
||||
*/
|
||||
|
||||
if (precedingsize < SIZEOF_MM_FREENODE)
|
||||
{
|
||||
alignedchunk += alignment;
|
||||
newnode = (FAR struct mm_allocnode_s *)
|
||||
(alignedchunk - SIZEOF_MM_ALLOCNODE);
|
||||
precedingsize = (size_t)newnode - (size_t)node;
|
||||
}
|
||||
|
||||
/* Set up the size of the new node */
|
||||
|
||||
newnode->size = (size_t)next - (size_t)newnode;
|
||||
newnode->preceding = precedingsize | MM_ALLOC_BIT;
|
||||
|
||||
/* Reduce the size of the original chunk and mark it not allocated, */
|
||||
|
||||
node->size = precedingsize;
|
||||
node->preceding &= ~MM_ALLOC_BIT;
|
||||
|
||||
/* Fix the preceding size of the next node */
|
||||
|
||||
next->preceding = newnode->size | (next->preceding & MM_ALLOC_BIT);
|
||||
|
||||
/* Convert the newnode chunk size back into malloc-compatible size by
|
||||
* subtracting the header size SIZEOF_MM_ALLOCNODE.
|
||||
*/
|
||||
|
||||
allocsize = newnode->size - SIZEOF_MM_ALLOCNODE;
|
||||
|
||||
/* Add the original, newly freed node to the free nodelist */
|
||||
|
||||
mm_addfreechunk(heap, (FAR struct mm_freenode_s *)node);
|
||||
|
||||
/* Replace the original node with the newlay realloaced,
|
||||
* aligned node
|
||||
*/
|
||||
|
||||
node = newnode;
|
||||
}
|
||||
|
||||
/* Check if there is free space at the end of the aligned chunk. Convert
|
||||
* malloc-compatible chunk size to include SIZEOF_MM_ALLOCNODE as needed
|
||||
* for mm_shrinkchunk.
|
||||
*/
|
||||
|
||||
size = MM_ALIGN_UP(size + SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
if (allocsize > size)
|
||||
{
|
||||
/* Shrink the chunk by that much -- remember, mm_shrinkchunk wants
|
||||
* internal chunk sizes that include SIZEOF_MM_ALLOCNODE.
|
||||
*/
|
||||
|
||||
mm_shrinkchunk(heap, node, size);
|
||||
}
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
return (FAR void *)alignedchunk;
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_realloc.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_realloc
|
||||
*
|
||||
* Description:
|
||||
* If the reallocation is for less space, then:
|
||||
*
|
||||
* (1) the current allocation is reduced in size
|
||||
* (2) the remainder at the end of the allocation is returned to the
|
||||
* free list.
|
||||
*
|
||||
* If the request is for more space and the current allocation can be
|
||||
* extended, it will be extended by:
|
||||
*
|
||||
* (1) Taking the additional space from the following free chunk, or
|
||||
* (2) Taking the additional space from the preceding free chunk.
|
||||
* (3) Or both
|
||||
*
|
||||
* If the request is for more space but the current chunk cannot be
|
||||
* extended, then malloc a new buffer, copy the data into the new buffer,
|
||||
* and free the old buffer.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_realloc(FAR struct mm_heap_s *heap, FAR void *oldmem,
|
||||
size_t size)
|
||||
{
|
||||
FAR struct mm_allocnode_s *oldnode;
|
||||
FAR struct mm_freenode_s *prev;
|
||||
FAR struct mm_freenode_s *next;
|
||||
size_t newsize;
|
||||
size_t oldsize;
|
||||
size_t prevsize = 0;
|
||||
size_t nextsize = 0;
|
||||
FAR void *newmem;
|
||||
|
||||
/* If oldmem is NULL, then realloc is equivalent to malloc */
|
||||
|
||||
if (oldmem == NULL)
|
||||
{
|
||||
return mm_malloc(heap, size);
|
||||
}
|
||||
|
||||
/* If size is zero, then realloc is equivalent to free */
|
||||
|
||||
if (size < 1)
|
||||
{
|
||||
mm_free(heap, oldmem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adjust the size to account for (1) the size of the allocated node and
|
||||
* (2) to make sure that it is an even multiple of our granule size.
|
||||
*/
|
||||
|
||||
newsize = MM_ALIGN_UP(size + SIZEOF_MM_ALLOCNODE);
|
||||
if (newsize < size)
|
||||
{
|
||||
/* There must have been an integer overflow */
|
||||
|
||||
DEBUGASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Map the memory chunk into an allocated node structure */
|
||||
|
||||
oldnode = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)oldmem - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* We need to hold the MM semaphore while we muck with the nodelist. */
|
||||
|
||||
mm_takesemaphore(heap);
|
||||
DEBUGASSERT(oldnode->preceding & MM_ALLOC_BIT);
|
||||
DEBUGASSERT(mm_heapmember(heap, oldmem));
|
||||
|
||||
/* Check if this is a request to reduce the size of the allocation. */
|
||||
|
||||
oldsize = oldnode->size;
|
||||
if (newsize <= oldsize)
|
||||
{
|
||||
/* Handle the special case where we are not going to change the size
|
||||
* of the allocation.
|
||||
*/
|
||||
|
||||
if (newsize < oldsize)
|
||||
{
|
||||
mm_shrinkchunk(heap, oldnode, newsize);
|
||||
}
|
||||
|
||||
/* Then return the original address */
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
return oldmem;
|
||||
}
|
||||
|
||||
/* This is a request to increase the size of the allocation, Get the
|
||||
* available sizes before and after the oldnode so that we can make the
|
||||
* best decision
|
||||
*/
|
||||
|
||||
next = (FAR struct mm_freenode_s *)
|
||||
((FAR char *)oldnode + oldnode->size);
|
||||
if ((next->preceding & MM_ALLOC_BIT) == 0)
|
||||
{
|
||||
nextsize = next->size;
|
||||
}
|
||||
|
||||
prev = (FAR struct mm_freenode_s *)
|
||||
((FAR char *)oldnode - (oldnode->preceding & ~MM_ALLOC_BIT));
|
||||
if ((prev->preceding & MM_ALLOC_BIT) == 0)
|
||||
{
|
||||
prevsize = prev->size;
|
||||
}
|
||||
|
||||
/* Now, check if we can extend the current allocation or not */
|
||||
|
||||
if (nextsize + prevsize + oldsize >= newsize)
|
||||
{
|
||||
size_t needed = newsize - oldsize;
|
||||
size_t takeprev = 0;
|
||||
size_t takenext = 0;
|
||||
|
||||
/* Check if we can extend into the previous chunk and if the
|
||||
* previous chunk is smaller than the next chunk.
|
||||
*/
|
||||
|
||||
if (prevsize > 0 && (nextsize >= prevsize || nextsize < 1))
|
||||
{
|
||||
/* Can we get everything we need from the previous chunk? */
|
||||
|
||||
if (needed > prevsize)
|
||||
{
|
||||
/* No, take the whole previous chunk and get the
|
||||
* rest that we need from the next chunk.
|
||||
*/
|
||||
|
||||
takeprev = prevsize;
|
||||
takenext = needed - prevsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes, take what we need from the previous chunk */
|
||||
|
||||
takeprev = needed;
|
||||
takenext = 0;
|
||||
}
|
||||
|
||||
needed = 0;
|
||||
}
|
||||
|
||||
/* Check if we can extend into the next chunk and if we still need
|
||||
* more memory.
|
||||
*/
|
||||
|
||||
if (nextsize > 0 && needed)
|
||||
{
|
||||
/* Can we get everything we need from the next chunk? */
|
||||
|
||||
if (needed > nextsize)
|
||||
{
|
||||
/* No, take the whole next chunk and get the rest that we
|
||||
* need from the previous chunk.
|
||||
*/
|
||||
|
||||
takeprev = needed - nextsize;
|
||||
takenext = nextsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes, take what we need from the previous chunk */
|
||||
|
||||
takeprev = 0;
|
||||
takenext = needed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extend into the previous free chunk */
|
||||
|
||||
newmem = oldmem;
|
||||
if (takeprev)
|
||||
{
|
||||
FAR struct mm_allocnode_s *newnode;
|
||||
|
||||
/* Remove the previous node. There must be a predecessor, but
|
||||
* there may not be a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(prev->blink);
|
||||
prev->blink->flink = prev->flink;
|
||||
if (prev->flink)
|
||||
{
|
||||
prev->flink->blink = prev->blink;
|
||||
}
|
||||
|
||||
/* Extend the node into the previous free chunk */
|
||||
|
||||
newnode = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)oldnode - takeprev);
|
||||
|
||||
/* Did we consume the entire preceding chunk? */
|
||||
|
||||
if (takeprev < prevsize)
|
||||
{
|
||||
/* No.. just take what we need from the previous chunk and put
|
||||
* it back into the free list
|
||||
*/
|
||||
|
||||
prev->size -= takeprev;
|
||||
DEBUGASSERT(prev->size >= SIZEOF_MM_FREENODE);
|
||||
newnode->size = oldsize + takeprev;
|
||||
newnode->preceding = prev->size | MM_ALLOC_BIT;
|
||||
next->preceding = newnode->size |
|
||||
(next->preceding & MM_ALLOC_BIT);
|
||||
|
||||
/* Return the previous free node to the nodelist
|
||||
* (with the new size)
|
||||
*/
|
||||
|
||||
mm_addfreechunk(heap, prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes.. update its size (newnode->preceding is already set) */
|
||||
|
||||
newnode->size += oldsize;
|
||||
newnode->preceding |= MM_ALLOC_BIT;
|
||||
next->preceding = newnode->size |
|
||||
(next->preceding & MM_ALLOC_BIT);
|
||||
}
|
||||
|
||||
/* Now we have to move the user contents 'down' in memory. memcpy
|
||||
* should be safe for this.
|
||||
*/
|
||||
|
||||
newmem = (FAR void *)((FAR char *)newnode + SIZEOF_MM_ALLOCNODE);
|
||||
memcpy(newmem, oldmem, oldsize - SIZEOF_MM_ALLOCNODE);
|
||||
|
||||
/* Now we want to return newnode */
|
||||
|
||||
oldnode = newnode;
|
||||
oldsize = newnode->size;
|
||||
}
|
||||
|
||||
/* Extend into the next free chunk */
|
||||
|
||||
if (takenext)
|
||||
{
|
||||
FAR struct mm_freenode_s *newnode;
|
||||
FAR struct mm_allocnode_s *andbeyond;
|
||||
|
||||
/* Get the chunk following the next node (which could be the tail
|
||||
* chunk)
|
||||
*/
|
||||
|
||||
andbeyond = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)next + nextsize);
|
||||
|
||||
/* Remove the next node. There must be a predecessor, but there
|
||||
* may not be a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(next->blink);
|
||||
next->blink->flink = next->flink;
|
||||
if (next->flink)
|
||||
{
|
||||
next->flink->blink = next->blink;
|
||||
}
|
||||
|
||||
/* Extend the node into the next chunk */
|
||||
|
||||
oldnode->size = oldsize + takenext;
|
||||
newnode = (FAR struct mm_freenode_s *)
|
||||
((FAR char *)oldnode + oldnode->size);
|
||||
|
||||
/* Did we consume the entire preceding chunk? */
|
||||
|
||||
if (takenext < nextsize)
|
||||
{
|
||||
/* No, take what we need from the next chunk and return it to
|
||||
* the free nodelist.
|
||||
*/
|
||||
|
||||
newnode->size = nextsize - takenext;
|
||||
DEBUGASSERT(newnode->size >= SIZEOF_MM_FREENODE);
|
||||
newnode->preceding = oldnode->size;
|
||||
andbeyond->preceding = newnode->size |
|
||||
(andbeyond->preceding & MM_ALLOC_BIT);
|
||||
|
||||
/* Add the new free node to the nodelist (with the new size) */
|
||||
|
||||
mm_addfreechunk(heap, newnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes, just update some pointers. */
|
||||
|
||||
andbeyond->preceding = oldnode->size |
|
||||
(andbeyond->preceding & MM_ALLOC_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
return newmem;
|
||||
}
|
||||
|
||||
/* The current chunk cannot be extended.
|
||||
* Just allocate a new chunk and copy
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Allocate a new block. On failure, realloc must return NULL but
|
||||
* leave the original memory in place.
|
||||
*/
|
||||
|
||||
mm_givesemaphore(heap);
|
||||
newmem = (FAR void *)mm_malloc(heap, size);
|
||||
if (newmem)
|
||||
{
|
||||
memcpy(newmem, oldmem, oldsize);
|
||||
mm_free(heap, oldmem);
|
||||
}
|
||||
|
||||
return newmem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_sbrk.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/mm/mm.h>
|
||||
#include <nuttx/pgalloc.h>
|
||||
|
||||
#ifdef CONFIG_BUILD_KERNEL
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_sbrk
|
||||
*
|
||||
* Description:
|
||||
* The sbrk() function is used to change the amount of space allocated
|
||||
* for the calling process. The change is made by resetting the process's
|
||||
* break value and allocating the appropriate amount of space. The amount
|
||||
* of allocated space increases as the break value increases.
|
||||
*
|
||||
* The sbrk() function adds 'incr' bytes to the break value and changes
|
||||
* the allocated space accordingly. If incr is negative, the amount of
|
||||
* allocated space is decreased by incr bytes. The current value of the
|
||||
* program break is returned by sbrk(0).
|
||||
*
|
||||
* Input Parameters:
|
||||
* heap - A reference to the data structure that defines this heap.
|
||||
* incr - Specifies the number of bytes to add or to remove from the
|
||||
* space allocated for the process.
|
||||
* maxbreak - The maximum permissible break address.
|
||||
*
|
||||
* Returned Value:
|
||||
* Upon successful completion, sbrk() returns the prior break value.
|
||||
* Otherwise, it returns (void *)-1 and sets errno to indicate the
|
||||
* error:
|
||||
*
|
||||
* ENOMEM - The requested change would allocate more space than
|
||||
* allowed under system limits.
|
||||
* EAGAIN - The total amount of system memory available for allocation
|
||||
* to this process is temporarily insufficient. This may occur even
|
||||
* though the space requested was less than the maximum data segment
|
||||
* size.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_sbrk(FAR struct mm_heap_s *heap, intptr_t incr,
|
||||
uintptr_t maxbreak)
|
||||
{
|
||||
uintptr_t brkaddr;
|
||||
uintptr_t allocbase;
|
||||
unsigned int pgincr;
|
||||
size_t bytesize;
|
||||
int errcode;
|
||||
|
||||
DEBUGASSERT(incr >= 0);
|
||||
if (incr < 0)
|
||||
{
|
||||
errcode = ENOSYS;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Get the current break address (NOTE: assumes region 0). If
|
||||
* the memory manager is uninitialized, mm_brkaddr() will return
|
||||
* zero.
|
||||
*/
|
||||
|
||||
brkaddr = (uintptr_t)mm_brkaddr(heap, 0);
|
||||
if (incr > 0)
|
||||
{
|
||||
/* Convert the increment to multiples of the page size */
|
||||
|
||||
pgincr = MM_NPAGES(incr);
|
||||
|
||||
/* Check if this increment would exceed the maximum break value */
|
||||
|
||||
if ((brkaddr > 0) && ((maxbreak - brkaddr) < (pgincr << MM_PGSHIFT)))
|
||||
{
|
||||
errcode = ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Allocate the requested number of pages and map them to the
|
||||
* break address. If we provide a zero brkaddr to pgalloc(), it
|
||||
* will create the first block in the correct virtual address
|
||||
* space and return the start address of that block.
|
||||
*/
|
||||
|
||||
allocbase = pgalloc(brkaddr, pgincr);
|
||||
if (allocbase == 0)
|
||||
{
|
||||
errcode = EAGAIN;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Has the been been initialized? brkaddr will be zero if the
|
||||
* memory manager has not yet been initialized.
|
||||
*/
|
||||
|
||||
bytesize = pgincr << MM_PGSHIFT;
|
||||
if (brkaddr == 0)
|
||||
{
|
||||
/* No... then initialize it now */
|
||||
|
||||
mm_initialize(heap, (FAR void *)allocbase, bytesize);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Extend the heap (region 0) */
|
||||
|
||||
mm_extend(heap, (FAR void *)allocbase, bytesize, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return (FAR void *)brkaddr;
|
||||
|
||||
errout:
|
||||
set_errno(errcode);
|
||||
return (FAR void *)-1;
|
||||
}
|
||||
#endif /* CONFIG_BUILD_KERNEL */
|
||||
@@ -0,0 +1,283 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_sem.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* This is a special value that indicates that there is no holder of the
|
||||
* semaphore. The valid range of PIDs is 0-32767 and any value outside of
|
||||
* that range could be used (except -ESRCH which is a special return value
|
||||
* from getpid())
|
||||
*/
|
||||
|
||||
#define NO_HOLDER ((pid_t)-1)
|
||||
|
||||
/* Define MONITOR_MM_SEMAPHORE to enable semaphore state monitoring */
|
||||
|
||||
#ifdef MONITOR_MM_SEMAPHORE
|
||||
# define msemerr _err
|
||||
# define msemwarn _warn
|
||||
# define mseminfo _info
|
||||
#else
|
||||
# define msemerr _none
|
||||
# define msemwarn _none
|
||||
# define mseminfo _none
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_seminitialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the MM mutex
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_seminitialize(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Initialize the MM semaphore to one (to support one-at-a-time access to
|
||||
* private data sets).
|
||||
*/
|
||||
|
||||
_SEM_INIT(&heap_impl->mm_semaphore, 0, 1);
|
||||
|
||||
heap_impl->mm_holder = NO_HOLDER;
|
||||
heap_impl->mm_counts_held = 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_trysemaphore
|
||||
*
|
||||
* Description:
|
||||
* Try to take the MM mutex. This is called only from the OS in certain
|
||||
* conditions when it is necessary to have exclusive access to the memory
|
||||
* manager but it is impossible to wait on a semaphore (e.g., the idle
|
||||
* process when it performs its background memory cleanup).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mm_trysemaphore(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
pid_t my_pid = getpid();
|
||||
int ret;
|
||||
|
||||
/* getpid() returns the task ID of the task at the head of the ready-to-
|
||||
* run task list. mm_trysemaphore() may be called during context
|
||||
* switches. There are certain situations during context switching when
|
||||
* the OS data structures are in flux and where the current task (i.e.,
|
||||
* the task at the head of the ready-to-run task list) is not actually
|
||||
* running. Granting the semaphore access in that case is known to result
|
||||
* in heap corruption as in the following failure scenario.
|
||||
*
|
||||
* ---------------------------- -------------------------------
|
||||
* TASK A TASK B
|
||||
* ---------------------------- -------------------------------
|
||||
* Begins memory allocation.
|
||||
* - Holder is set to TASK B
|
||||
* <---- Task B preempted, Task A runs
|
||||
* Task A exits
|
||||
* - Current task set to Task B
|
||||
* Free tcb and stack memory
|
||||
* - Since holder is Task B,
|
||||
* memory manager is re-
|
||||
* entered, and
|
||||
* - Heap is corrupted.
|
||||
* ---------------------------- -------------------------------
|
||||
*
|
||||
* This is handled by getpid(): If the case where Task B is not actually
|
||||
* running, then getpid() will return the special value -ESRCH. That will
|
||||
* avoid taking the fatal 'if' logic and will fall through to use the
|
||||
* 'else', albeit with a nonsensical PID value.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
if (my_pid < 0)
|
||||
{
|
||||
ret = my_pid;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Does the current task already hold the semaphore? Is the current
|
||||
* task actually running?
|
||||
*/
|
||||
|
||||
if (heap_impl->mm_holder == my_pid)
|
||||
{
|
||||
/* Yes, just increment the number of references held by the current
|
||||
* task.
|
||||
*/
|
||||
|
||||
heap_impl->mm_counts_held++;
|
||||
ret = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try to take the semaphore */
|
||||
|
||||
ret = _SEM_TRYWAIT(&heap_impl->mm_semaphore);
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = _SEM_ERRVAL(ret);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* We have it. Claim the heap for the current task and return */
|
||||
|
||||
heap_impl->mm_holder = my_pid;
|
||||
heap_impl->mm_counts_held = 1;
|
||||
ret = OK;
|
||||
}
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_takesemaphore
|
||||
*
|
||||
* Description:
|
||||
* Take the MM mutex. This is the normal action before all memory
|
||||
* management actions.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_takesemaphore(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
pid_t my_pid = getpid();
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* Does the current task already hold the semaphore? */
|
||||
|
||||
if (heap_impl->mm_holder == my_pid)
|
||||
{
|
||||
/* Yes, just increment the number of references held by the current
|
||||
* task.
|
||||
*/
|
||||
|
||||
heap_impl->mm_counts_held++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Take the semaphore (perhaps waiting) */
|
||||
|
||||
mseminfo("PID=%d taking\n", my_pid);
|
||||
do
|
||||
{
|
||||
ret = _SEM_WAIT(&heap_impl->mm_semaphore);
|
||||
|
||||
/* The only case that an error should occur here is if the wait
|
||||
* was awakened by a signal.
|
||||
*/
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
ret = _SEM_ERRVAL(ret);
|
||||
DEBUGASSERT(ret == -EINTR || ret == -ECANCELED);
|
||||
}
|
||||
}
|
||||
while (ret == -EINTR);
|
||||
|
||||
/* We have it (or some awful, unexpected error occurred). Claim
|
||||
* the semaphore for the current task and return.
|
||||
*/
|
||||
|
||||
heap_impl->mm_holder = my_pid;
|
||||
heap_impl->mm_counts_held = 1;
|
||||
}
|
||||
|
||||
mseminfo("Holder=%d count=%d\n", heap_impl->mm_holder,
|
||||
heap_impl->mm_counts_held);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_givesemaphore
|
||||
*
|
||||
* Description:
|
||||
* Release the MM mutex when it is not longer needed.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_givesemaphore(FAR struct mm_heap_s *heap)
|
||||
{
|
||||
FAR struct mm_heap_impl_s *heap_impl;
|
||||
|
||||
DEBUGASSERT(MM_IS_VALID(heap));
|
||||
heap_impl = heap->mm_impl;
|
||||
|
||||
/* The current task should be holding at least one reference to the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(heap_impl->mm_holder == getpid());
|
||||
|
||||
/* Does the current task hold multiple references to the semaphore */
|
||||
|
||||
if (heap_impl->mm_counts_held > 1)
|
||||
{
|
||||
/* Yes, just release one count and return */
|
||||
|
||||
heap_impl->mm_counts_held--;
|
||||
mseminfo("Holder=%d count=%d\n", heap_impl->mm_holder,
|
||||
heap_impl->mm_counts_held);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nope, this is the last reference held by the current task. */
|
||||
|
||||
mseminfo("PID=%d giving\n", getpid());
|
||||
|
||||
heap_impl->mm_holder = NO_HOLDER;
|
||||
heap_impl->mm_counts_held = 0;
|
||||
DEBUGVERIFY(_SEM_POST(&heap_impl->mm_semaphore));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_shrinkchunk.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_shrinkchunk
|
||||
*
|
||||
* Description:
|
||||
* Reduce the size of the chunk specified by the node structure to the
|
||||
* specified size. this internal logic is used both from memalign to
|
||||
* dispose of any trailing memory in the aligned allocation and also by
|
||||
* realloc when there is a request to reduce the size of an allocation.
|
||||
*
|
||||
* NOTES:
|
||||
* (1) size is the whole chunk size (payload and header)
|
||||
* (2) the caller must hold the MM semaphore.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void mm_shrinkchunk(FAR struct mm_heap_s *heap,
|
||||
FAR struct mm_allocnode_s *node, size_t size)
|
||||
{
|
||||
FAR struct mm_freenode_s *next;
|
||||
|
||||
DEBUGASSERT((size & MM_GRAN_MASK) == 0);
|
||||
|
||||
/* Get a reference to the next node */
|
||||
|
||||
next = (FAR struct mm_freenode_s *)((FAR char *)node + node->size);
|
||||
|
||||
/* Check if it is free */
|
||||
|
||||
if ((next->preceding & MM_ALLOC_BIT) == 0)
|
||||
{
|
||||
FAR struct mm_allocnode_s *andbeyond;
|
||||
FAR struct mm_freenode_s *newnode;
|
||||
|
||||
/* Get the chunk next the next node (which could be the tail chunk) */
|
||||
|
||||
andbeyond = (FAR struct mm_allocnode_s *)
|
||||
((FAR char *)next + next->size);
|
||||
|
||||
/* Remove the next node. There must be a predecessor, but there may
|
||||
* not be a successor node.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(next->blink);
|
||||
next->blink->flink = next->flink;
|
||||
if (next->flink)
|
||||
{
|
||||
next->flink->blink = next->blink;
|
||||
}
|
||||
|
||||
/* Create a new chunk that will hold both the next chunk and the
|
||||
* tailing memory from the aligned chunk.
|
||||
*/
|
||||
|
||||
newnode = (FAR struct mm_freenode_s *)((FAR char *)node + size);
|
||||
|
||||
/* Set up the size of the new node */
|
||||
|
||||
newnode->size = next->size + node->size - size;
|
||||
newnode->preceding = size;
|
||||
node->size = size;
|
||||
andbeyond->preceding = newnode->size |
|
||||
(andbeyond->preceding & MM_ALLOC_BIT);
|
||||
|
||||
/* Add the new node to the freenodelist */
|
||||
|
||||
mm_addfreechunk(heap, newnode);
|
||||
}
|
||||
|
||||
/* The next chunk is allocated. Try to free the end portion at the end
|
||||
* chunk to be shrunk.
|
||||
*/
|
||||
|
||||
else if (node->size >= size + SIZEOF_MM_FREENODE)
|
||||
{
|
||||
FAR struct mm_freenode_s *newnode;
|
||||
|
||||
/* Create a new chunk that will hold both the next chunk and the
|
||||
* tailing memory from the aligned chunk.
|
||||
*/
|
||||
|
||||
newnode = (FAR struct mm_freenode_s *)((FAR char *)node + size);
|
||||
|
||||
/* Set up the size of the new node */
|
||||
|
||||
newnode->size = node->size - size;
|
||||
newnode->preceding = size;
|
||||
node->size = size;
|
||||
next->preceding = newnode->size | MM_ALLOC_BIT;
|
||||
|
||||
/* Add the new node to the freenodelist */
|
||||
|
||||
mm_addfreechunk(heap, newnode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_size2ndx.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_size2ndx
|
||||
*
|
||||
* Description:
|
||||
* Convert the size to a nodelist index.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mm_size2ndx(size_t size)
|
||||
{
|
||||
int ndx = 0;
|
||||
|
||||
if (size >= MM_MAX_CHUNK)
|
||||
{
|
||||
return MM_NNODES - 1;
|
||||
}
|
||||
|
||||
size >>= MM_MIN_SHIFT;
|
||||
while (size > 1)
|
||||
{
|
||||
ndx++;
|
||||
size >>= 1;
|
||||
}
|
||||
|
||||
return ndx;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
* mm/mm_heap/mm_zalloc.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mm_zalloc
|
||||
*
|
||||
* Description:
|
||||
* mm_zalloc calls mm_malloc, then zeroes out the allocated chunk.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR void *mm_zalloc(FAR struct mm_heap_s *heap, size_t size)
|
||||
{
|
||||
FAR void *alloc = mm_malloc(heap, size);
|
||||
if (alloc)
|
||||
{
|
||||
memset(alloc, 0, size);
|
||||
}
|
||||
|
||||
return alloc;
|
||||
}
|
||||
Reference in New Issue
Block a user