eal: add function to create control threads
[dpdk.git] / lib / librte_eal / common / malloc_elem.c
index e02ed88..ee79dcd 100644 (file)
@@ -6,6 +6,7 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 #include <sys/queue.h>
 
 #include <rte_memory.h>
@@ -17,6 +18,7 @@
 #include <rte_common.h>
 #include <rte_spinlock.h>
 
+#include "eal_memalloc.h"
 #include "malloc_elem.h"
 #include "malloc_heap.h"
 
  * Initialize a general malloc_elem header structure
  */
 void
-malloc_elem_init(struct malloc_elem *elem,
-               struct malloc_heap *heap, const struct rte_memseg *ms, size_t size)
+malloc_elem_init(struct malloc_elem *elem, struct malloc_heap *heap,
+               struct rte_memseg_list *msl, size_t size)
 {
        elem->heap = heap;
-       elem->ms = ms;
+       elem->msl = msl;
        elem->prev = NULL;
        elem->next = NULL;
        memset(&elem->free_list, 0, sizeof(elem->free_list));
@@ -93,6 +95,18 @@ malloc_elem_insert(struct malloc_elem *elem)
                next_elem->prev = elem;
 }
 
+/*
+ * Attempt to find enough physically contiguous memory in this block to store
+ * our data. Assume that element has at least enough space to fit in the data,
+ * so we just check the page addresses.
+ */
+static bool
+elem_check_phys_contig(const struct rte_memseg_list *msl,
+               void *start, size_t size)
+{
+       return eal_memalloc_is_contig(msl, start, size);
+}
+
 /*
  * calculate the starting point of where data of the requested size
  * and alignment would fit in the current element. If the data doesn't
@@ -100,27 +114,59 @@ malloc_elem_insert(struct malloc_elem *elem)
  */
 static void *
 elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
-               size_t bound)
+               size_t bound, bool contig)
 {
-       const size_t bmask = ~(bound - 1);
-       uintptr_t end_pt = (uintptr_t)elem +
-                       elem->size - MALLOC_ELEM_TRAILER_LEN;
-       uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
-       uintptr_t new_elem_start;
-
-       /* check boundary */
-       if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
-               end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
-               new_data_start = RTE_ALIGN_FLOOR((end_pt - size), align);
-               end_pt = new_data_start + size;
-               if (((end_pt - 1) & bmask) != (new_data_start & bmask))
-                       return NULL;
-       }
+       size_t elem_size = elem->size;
 
-       new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+       /*
+        * we're allocating from the end, so adjust the size of element by
+        * alignment size.
+        */
+       while (elem_size >= size) {
+               const size_t bmask = ~(bound - 1);
+               uintptr_t end_pt = (uintptr_t)elem +
+                               elem_size - MALLOC_ELEM_TRAILER_LEN;
+               uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size),
+                               align);
+               uintptr_t new_elem_start;
+
+               /* check boundary */
+               if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
+                       end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
+                       new_data_start = RTE_ALIGN_FLOOR((end_pt - size),
+                                       align);
+                       end_pt = new_data_start + size;
+
+                       if (((end_pt - 1) & bmask) != (new_data_start & bmask))
+                               return NULL;
+               }
 
-       /* if the new start point is before the exist start, it won't fit */
-       return (new_elem_start < (uintptr_t)elem) ? NULL : (void *)new_elem_start;
+               new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
+
+               /* if the new start point is before the exist start,
+                * it won't fit
+                */
+               if (new_elem_start < (uintptr_t)elem)
+                       return NULL;
+
+               if (contig) {
+                       size_t new_data_size = end_pt - new_data_start;
+
+                       /*
+                        * if physical contiguousness was requested and we
+                        * couldn't fit all data into one physically contiguous
+                        * block, try again with lower addresses.
+                        */
+                       if (!elem_check_phys_contig(elem->msl,
+                                       (void *)new_data_start,
+                                       new_data_size)) {
+                               elem_size -= align;
+                               continue;
+                       }
+               }
+               return (void *)new_elem_start;
+       }
+       return NULL;
 }
 
 /*
@@ -129,9 +175,9 @@ elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
  */
 int
 malloc_elem_can_hold(struct malloc_elem *elem, size_t size,    unsigned align,
-               size_t bound)
+               size_t bound, bool contig)
 {
-       return elem_start_pt(elem, size, align, bound) != NULL;
+       return elem_start_pt(elem, size, align, bound, contig) != NULL;
 }
 
 /*
@@ -145,7 +191,7 @@ split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
        const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
        const size_t new_elem_size = elem->size - old_elem_size;
 
-       malloc_elem_init(split_pt, elem->heap, elem->ms, new_elem_size);
+       malloc_elem_init(split_pt, elem->heap, elem->msl, new_elem_size);
        split_pt->prev = elem;
        split_pt->next = next_elem;
        if (next_elem)
@@ -245,8 +291,8 @@ malloc_elem_free_list_insert(struct malloc_elem *elem)
 /*
  * Remove the specified element from its heap's free list.
  */
-static void
-elem_free_list_remove(struct malloc_elem *elem)
+void
+malloc_elem_free_list_remove(struct malloc_elem *elem)
 {
        LIST_REMOVE(elem, free_list);
 }
@@ -259,14 +305,15 @@ elem_free_list_remove(struct malloc_elem *elem)
  */
 struct malloc_elem *
 malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
-               size_t bound)
+               size_t bound, bool contig)
 {
-       struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound);
+       struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound,
+                       contig);
        const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
        const size_t trailer_size = elem->size - old_elem_size - size -
                MALLOC_ELEM_OVERHEAD;
 
-       elem_free_list_remove(elem);
+       malloc_elem_free_list_remove(elem);
 
        if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
                /* split it, too much free space after elem */
@@ -325,8 +372,8 @@ join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
        elem1->next = next;
 }
 
-static struct malloc_elem *
-elem_join_adjacent_free(struct malloc_elem *elem)
+struct malloc_elem *
+malloc_elem_join_adjacent_free(struct malloc_elem *elem)
 {
        /*
         * check if next element exists, is adjacent and is free, if so join
@@ -340,7 +387,7 @@ elem_join_adjacent_free(struct malloc_elem *elem)
                erase = RTE_PTR_SUB(elem->next, MALLOC_ELEM_TRAILER_LEN);
 
                /* remove from free list, join to this one */
-               elem_free_list_remove(elem->next);
+               malloc_elem_free_list_remove(elem->next);
                join_elem(elem, elem->next);
 
                /* erase header and trailer */
@@ -360,7 +407,7 @@ elem_join_adjacent_free(struct malloc_elem *elem)
                erase = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN);
 
                /* remove from free list, join to this one */
-               elem_free_list_remove(elem->prev);
+               malloc_elem_free_list_remove(elem->prev);
 
                new_elem = elem->prev;
                join_elem(new_elem, elem);
@@ -379,7 +426,7 @@ elem_join_adjacent_free(struct malloc_elem *elem)
  * blocks either immediately before or immediately after newly freed block
  * are also free, the blocks are merged together.
  */
-int
+struct malloc_elem *
 malloc_elem_free(struct malloc_elem *elem)
 {
        void *ptr;
@@ -388,7 +435,7 @@ malloc_elem_free(struct malloc_elem *elem)
        ptr = RTE_PTR_ADD(elem, sizeof(*elem));
        data_len = elem->size - MALLOC_ELEM_OVERHEAD;
 
-       elem = elem_join_adjacent_free(elem);
+       elem = malloc_elem_join_adjacent_free(elem);
 
        malloc_elem_free_list_insert(elem);
 
@@ -397,7 +444,99 @@ malloc_elem_free(struct malloc_elem *elem)
 
        memset(ptr, 0, data_len);
 
-       return 0;
+       return elem;
+}
+
+/* assume all checks were already done */
+void
+malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len)
+{
+       struct malloc_elem *hide_start, *hide_end, *prev, *next;
+       size_t len_before, len_after;
+
+       hide_start = start;
+       hide_end = RTE_PTR_ADD(start, len);
+
+       prev = elem->prev;
+       next = elem->next;
+
+       /* we cannot do anything with non-adjacent elements */
+       if (next && next_elem_is_adjacent(elem)) {
+               len_after = RTE_PTR_DIFF(next, hide_end);
+               if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+                       /* split after */
+                       split_elem(elem, hide_end);
+
+                       malloc_elem_free_list_insert(hide_end);
+               } else if (len_after >= MALLOC_ELEM_HEADER_LEN) {
+                       /* shrink current element */
+                       elem->size -= len_after;
+                       memset(hide_end, 0, sizeof(*hide_end));
+
+                       /* copy next element's data to our pad */
+                       memcpy(hide_end, next, sizeof(*hide_end));
+
+                       /* pad next element */
+                       next->state = ELEM_PAD;
+                       next->pad = len_after;
+                       next->size -= len_after;
+
+                       /* next element busy, would've been merged otherwise */
+                       hide_end->pad = len_after;
+                       hide_end->size += len_after;
+
+                       /* adjust pointers to point to our new pad */
+                       if (next->next)
+                               next->next->prev = hide_end;
+                       elem->next = hide_end;
+               } else if (len_after > 0) {
+                       RTE_LOG(ERR, EAL, "Unaligned element, heap is probably corrupt\n");
+                       return;
+               }
+       }
+
+       /* we cannot do anything with non-adjacent elements */
+       if (prev && prev_elem_is_adjacent(elem)) {
+               len_before = RTE_PTR_DIFF(hide_start, elem);
+               if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
+                       /* split before */
+                       split_elem(elem, hide_start);
+
+                       prev = elem;
+                       elem = hide_start;
+
+                       malloc_elem_free_list_insert(prev);
+               } else if (len_before > 0) {
+                       /*
+                        * unlike with elements after current, here we don't
+                        * need to pad elements, but rather just increase the
+                        * size of previous element, copy the old header and set
+                        * up trailer.
+                        */
+                       void *trailer = RTE_PTR_ADD(prev,
+                                       prev->size - MALLOC_ELEM_TRAILER_LEN);
+
+                       memcpy(hide_start, elem, sizeof(*elem));
+                       hide_start->size = len;
+
+                       prev->size += len_before;
+                       set_trailer(prev);
+
+                       /* update pointers */
+                       prev->next = hide_start;
+                       if (next)
+                               next->prev = hide_start;
+
+                       /* erase old trailer */
+                       memset(trailer, 0, MALLOC_ELEM_TRAILER_LEN);
+                       /* erase old header */
+                       memset(elem, 0, sizeof(*elem));
+
+                       elem = hide_start;
+               }
+       }
+
+       remove_elem(elem);
 }
 
 /*
@@ -423,7 +562,7 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size)
        /* we now know the element fits, so remove from free list,
         * join the two
         */
-       elem_free_list_remove(elem->next);
+       malloc_elem_free_list_remove(elem->next);
        join_elem(elem, elem->next);
 
        if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD) {