[Mesa-dev] [PATCH 06/15] ralloc: add a linear allocator as a child node of ralloc

Nicolai Hähnle nhaehnle at gmail.com
Wed Oct 12 12:37:50 UTC 2016


On 08.10.2016 12:58, Marek Olšák wrote:
> From: Marek Olšák <marek.olsak at amd.com>

It would be useful to hook this up with Valgrind and address sanitizer. 
Some drivers already do that; basically, there are macros 
VALGRIND_MAKE_MEM_{NOACCESS, UNDEFINED, DEFINED} in valgrind/memcheck.h, 
and similarly address sanitizer has 
__asan_{poison,unpoison}_memory_region intrinsics.

The linear_size_chunks can serve as red zones helping to catch array 
overflows.

This doesn't necessarily have to be in the same commit, but it would be 
good to do it soon.


>
> ---
>  src/util/ralloc.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/util/ralloc.h |  84 ++++++++++++-
>  2 files changed, 435 insertions(+), 4 deletions(-)
>
> diff --git a/src/util/ralloc.c b/src/util/ralloc.c
> index bf21ac3..a8e660f 100644
> --- a/src/util/ralloc.c
> +++ b/src/util/ralloc.c
> @@ -522,10 +522,365 @@ ralloc_vasprintf_rewrite_tail(char **str, size_t *start, const char *fmt,
>
>     ptr = resize(*str, *start + new_length + 1);
>     if (unlikely(ptr == NULL))
>        return false;
>
>     vsnprintf(ptr + *start, new_length + 1, fmt, args);
>     *str = ptr;
>     *start += new_length;
>     return true;
>  }
> +
> +/***************************************************************************
> + * Linear allocator for short-lived allocations.
> + ***************************************************************************
> + *
> + * The allocator consists of a parent node (2K buffer), which requires
> + * a ralloc parent, and child nodes (allocations). Child nodes can't be freed
> + * directly, because the parent doesn't track them. You have to release
> + * the parent node in order to release all its children.
> + *
> + * The allocator uses a fixed-sized buffer with a monotonically increasing
> + * offset after each allocation. If the buffer is all used, another buffer
> + * is allocated, sharing the same ralloc parent, so all buffers are at
> + * the same level in the ralloc hierarchy.
> + *
> + * The linear parent node is always the first buffer and keeps track of all
> + * other buffers.
> + */
> +
> +#define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1))

Please rename this to ALIGN_POT or similar.


> +#define MIN_LINEAR_BUFSIZE 2048
> +#define LMAGIC 0x87b9c7d3
> +
> +struct linear_header {
> +#ifdef DEBUG
> +   unsigned magic;   /* for debugging */
> +#endif
> +   unsigned offset;  /* points to the first unused byte in the buffer */
> +   unsigned size;    /* size of the buffer */
> +   void *ralloc_parent;          /* new buffers will use this */
> +   struct linear_header *next;   /* next buffer if we have more */
> +   struct linear_header *latest; /* the only buffer that has free space */
> +
> +   /* After this structure, the buffer begins.
> +    * Each suballocation consists of linear_size_chunk as its header followed
> +    * by the suballocation, so it goes:
> +    *
> +    * - linear_size_chunk
> +    * - allocated space
> +    * - linear_size_chunk
> +    * - allocated space
> +    * etc.
> +    *
> +    * linear_size_chunk is only needed by linear_realloc.
> +    */
> +};
> +
> +struct linear_size_chunk {
> +   unsigned size; /* for realloc */
> +   unsigned _padding;
> +};
> +
> +typedef struct linear_header linear_header;
> +typedef struct linear_size_chunk linear_size_chunk;
> +
> +#define LINEAR_PARENT_TO_HEADER(parent) \
> +   (linear_header*) \
> +   ((char*)(parent) - sizeof(linear_size_chunk) - sizeof(linear_header))
> +
> +/* Allocate the linear buffer with its header. */
> +static linear_header *
> +create_linear_node(void *ralloc_ctx, unsigned min_size)
> +{
> +   linear_header *node;
> +
> +   min_size += sizeof(linear_size_chunk);
> +
> +   if (likely(min_size < MIN_LINEAR_BUFSIZE))
> +      min_size = MIN_LINEAR_BUFSIZE;
> +
> +   node = ralloc_size(ralloc_ctx, sizeof(linear_header) + min_size);
> +   if (unlikely(!node))
> +      return NULL;
> +
> +#ifdef DEBUG
> +   node->magic = LMAGIC;
> +#endif
> +   node->offset = 0;
> +   node->size = min_size;
> +   node->ralloc_parent = ralloc_ctx;
> +   node->next = NULL;
> +   node->latest = node;
> +   return node;
> +}
> +
> +void *
> +linear_alloc_child(void *parent, unsigned size)
> +{
> +   linear_header *first = LINEAR_PARENT_TO_HEADER(parent);
> +   linear_header *latest = first->latest;
> +   linear_header *new_node;
> +   linear_size_chunk *ptr;
> +   unsigned full_size;
> +
> +   assert(first->magic == LMAGIC);
> +   assert(!latest->next);
> +
> +   size = ALIGN(size, 8);

sizeof(size_t) instead of 8? In a similar vein, you could use size_t in 
linear_size_chunk.


> +   full_size = sizeof(linear_size_chunk) + size;
> +
> +   if (likely(latest->offset + full_size <= latest->size)) {
> +alloc:

Please avoid the goto by inverting the logic here.


> +      ptr = (linear_size_chunk *)((char*)&latest[1] + latest->offset);
> +      ptr->size = size;
> +      latest->offset += full_size;
> +      return &ptr[1];
> +   }
> +
> +   /* allocate a new node */
> +   new_node = create_linear_node(latest->ralloc_parent, size);
> +   if (unlikely(!new_node))
> +      return NULL;
> +
> +   first->latest = new_node;
> +   latest->latest = new_node;
> +   latest->next = new_node;
> +   latest = new_node;
> +
> +   goto alloc;
> +}
> +
> +void *
> +linear_alloc_parent(void *ralloc_ctx, unsigned size)
> +{
> +   linear_header *node;
> +
> +   if (unlikely(!ralloc_ctx))
> +      return NULL;
> +
> +   size = ALIGN(size, 8);
> +
> +   node = create_linear_node(ralloc_ctx, size);
> +   if (unlikely(!node))
> +      return NULL;
> +
> +   return linear_alloc_child((char*)node +
> +                             sizeof(linear_header) +
> +                             sizeof(linear_size_chunk), size);
> +}
> +
> +void *
> +linear_zalloc_child(void *parent, unsigned size)
> +{
> +   void *ptr = linear_alloc_child(parent, size);
> +
> +   if (likely(ptr))
> +      memset(ptr, 0, size);
> +   return ptr;
> +}
> +
> +void *
> +linear_zalloc_parent(void *parent, unsigned size)
> +{
> +   void *ptr = linear_alloc_parent(parent, size);
> +
> +   if (likely(ptr))
> +      memset(ptr, 0, size);
> +   return ptr;
> +}
> +
> +void
> +linear_free_parent(void *ptr)
> +{
> +   linear_header *node;
> +
> +   if (unlikely(!ptr))
> +      return;
> +
> +   node = LINEAR_PARENT_TO_HEADER(ptr);
> +   assert(node->magic == LMAGIC);
> +
> +   while (node) {
> +      void *ptr = node;
> +
> +      node = node->next;
> +      ralloc_free(ptr);
> +   }
> +}
> +
> +void
> +ralloc_steal_linear_parent(void *new_ralloc_ctx, void *ptr)
> +{
> +   linear_header *node;
> +
> +   if (unlikely(!ptr))
> +      return;
> +
> +   node = LINEAR_PARENT_TO_HEADER(ptr);
> +   assert(node->magic == LMAGIC);
> +
> +   while (node) {
> +      ralloc_steal(new_ralloc_ctx, node);
> +      node->ralloc_parent = new_ralloc_ctx;
> +      node = node->next;
> +   }
> +}
> +
> +void *
> +ralloc_parent_of_linear_parent(void *ptr)
> +{
> +   linear_header *node = LINEAR_PARENT_TO_HEADER(ptr);
> +   assert(node->magic == LMAGIC);
> +   return node->ralloc_parent;
> +}
> +
> +void *
> +linear_realloc(void *parent, void *old, unsigned new_size)
> +{
> +   size_t old_size = 0;
> +   ralloc_header *new_ptr;
> +
> +   new_ptr = linear_alloc_child(parent, new_size);
> +
> +   if (unlikely(!old))
> +      return new_ptr;
> +
> +   old_size = ((linear_size_chunk*)old)[-1].size;
> +
> +   if (likely(new_ptr && old_size))
> +      memcpy(new_ptr, old, old_size > new_size ? new_size : old_size);

Use MIN2.

Nicolai


More information about the mesa-dev mailing list