[dpdk-dev,v2,1/2] malloc: add biggest free IOVA-contiguous element to stats

Message ID 777ae6b10a7524e188c07ba14e576fc7b0e21018.1524729978.git.anatoly.burakov@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel-compilation success Compilation OK

Commit Message

Anatoly Burakov April 26, 2018, 8:06 a.m. UTC
  User might be interested to find out what is the biggest chunk of
IOVA-contiguous free space that can be allocated from malloc. Add
relevant malloc-internal functions and expose this through malloc
stats calculation call.

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---

Notes:
    v2:
    - Add header to newly recalculated element start

 lib/librte_eal/common/include/rte_malloc.h |  1 +
 lib/librte_eal/common/malloc_elem.c        | 59 ++++++++++++++++++++++++++++++
 lib/librte_eal/common/malloc_elem.h        |  3 ++
 lib/librte_eal/common/malloc_heap.c        |  8 ++++
 lib/librte_eal/common/rte_malloc.c         |  2 +
 5 files changed, 73 insertions(+)
  

Comments

Anatoly Burakov May 3, 2018, 5:17 p.m. UTC | #1
This patchset does two things. First, it enables reserving
memzones of zero-length that are IOVA-contiguous. Second,
it fixes a long-standing race condition in reserving
zero-length memzones, where malloc heap is not locked between
stats collection and reservation, and will instead allocate
biggest element on the spot.

Some limitations are added, but they are a trade-off between
not having race conditions and user convenience. It would be
possible to lock all heaps during memzone reserve for zero-
length, and that would keep the old behavior, but given how
such allocation (especially when asking for IOVA-contiguous
memory) may take a long time, a design decision was made to
keep things simple, and only check other heaps if the
current one is completely busy.

Ideas on improvement are welcome.

Anatoly Burakov (3):
  malloc: add biggest free IOVA-contiguous element to stats
  malloc: allow reserving biggest element
  memzone: improve zero-length memzone reserve

 lib/librte_eal/common/eal_common_memzone.c  |  62 ++---------
 lib/librte_eal/common/include/rte_malloc.h  |   1 +
 lib/librte_eal/common/include/rte_memzone.h |  18 ++++
 lib/librte_eal/common/malloc_elem.c         |  77 ++++++++++++++
 lib/librte_eal/common/malloc_elem.h         |   6 ++
 lib/librte_eal/common/malloc_heap.c         | 137 ++++++++++++++++++++++++
 lib/librte_eal/common/malloc_heap.h         |   4 +
 lib/librte_eal/common/rte_malloc.c          |   2 +
 test/test/test_memzone.c                    | 157 +++++++++++++++-------------
 9 files changed, 342 insertions(+), 122 deletions(-)
  
Anatoly Burakov May 14, 2018, 11:19 a.m. UTC | #2
This patchset does two things. First, it enables reserving
memzones of zero-length that are IOVA-contiguous. Second,
it fixes a long-standing race condition in reserving
zero-length memzones, where malloc heap is not locked between
stats collection and reservation, and will instead allocate
biggest element on the spot.

Some limitations are added, but they are a trade-off between
not having race conditions and user convenience. It would be
possible to lock all heaps during memzone reserve for zero-
length, and that would keep the old behavior, but given how
such allocation (especially when asking for IOVA-contiguous
memory) may take a long time, a design decision was made to
keep things simple, and only check other heaps if the
current one is completely busy.

Ideas on improvement are welcome.

v4:
- Fixes in memzone test
- Account for element padding
- Fix for wrong memzone size being returned
- Documentation fixes

Anatoly Burakov (3):
  malloc: add biggest free IOVA-contiguous element to stats
  malloc: allow reserving biggest element
  memzone: improve zero-length memzone reserve

 lib/librte_eal/common/eal_common_memzone.c  |  68 +++---------
 lib/librte_eal/common/include/rte_malloc.h  |   1 +
 lib/librte_eal/common/include/rte_memzone.h |  21 ++++
 lib/librte_eal/common/malloc_elem.c         |  79 +++++++++++++
 lib/librte_eal/common/malloc_elem.h         |   6 +
 lib/librte_eal/common/malloc_heap.c         | 137 +++++++++++++++++++++++
 lib/librte_eal/common/malloc_heap.h         |   4 +
 lib/librte_eal/common/rte_malloc.c          |   2 +
 test/test/test_memzone.c                    | 165 ++++++++++++++++------------
 9 files changed, 358 insertions(+), 125 deletions(-)
  
Anatoly Burakov May 14, 2018, 1:47 p.m. UTC | #3
This patchset does two things. First, it enables reserving
memzones of zero-length that are IOVA-contiguous. Second,
it fixes a long-standing race condition in reserving
zero-length memzones, where malloc heap is not locked between
stats collection and reservation, and will instead allocate
biggest element on the spot.

Some limitations are added, but they are a trade-off between
not having race conditions and user convenience. It would be
possible to lock all heaps during memzone reserve for zero-
length, and that would keep the old behavior, but given how
such allocation (especially when asking for IOVA-contiguous
memory) may take a long time, a design decision was made to
keep things simple, and only check other heaps if the
current one is completely busy.

Ideas on improvement are welcome.

v5:
- Use bound length if reserving memzone with zero length

v4:
- Fixes in memzone test
- Account for element padding
- Fix for wrong memzone size being returned
- Documentation fixes

Anatoly Burakov (3):
  malloc: add biggest free IOVA-contiguous element to stats
  malloc: allow reserving biggest element
  memzone: improve zero-length memzone reserve

 lib/librte_eal/common/eal_common_memzone.c  |  70 +++---------
 lib/librte_eal/common/include/rte_malloc.h  |   1 +
 lib/librte_eal/common/include/rte_memzone.h |  21 ++++
 lib/librte_eal/common/malloc_elem.c         |  79 +++++++++++++
 lib/librte_eal/common/malloc_elem.h         |   6 +
 lib/librte_eal/common/malloc_heap.c         | 137 +++++++++++++++++++++++
 lib/librte_eal/common/malloc_heap.h         |   4 +
 lib/librte_eal/common/rte_malloc.c          |   2 +
 test/test/test_memzone.c                    | 165 ++++++++++++++++------------
 9 files changed, 360 insertions(+), 125 deletions(-)
  
Anatoly Burakov May 31, 2018, 9:50 a.m. UTC | #4
This patchset does two things. First, it enables reserving
memzones of zero-length that are IOVA-contiguous. Second,
it fixes a long-standing race condition in reserving
zero-length memzones, where malloc heap is not locked between
stats collection and reservation, and will instead allocate
biggest element on the spot.

Some limitations are added, but they are a trade-off between
not having race conditions and user convenience. It would be
possible to lock all heaps during memzone reserve for zero-
length, and that would keep the old behavior, but given how
such allocation (especially when asking for IOVA-contiguous
memory) may take a long time, a design decision was made to
keep things simple, and only check other heaps if the
current one is completely busy.

Ideas on improvement are welcome.

v6:
- Rebase on top of 18.05
- Dropped malloc stats changes as no deprecation notice was
  sent, and i would like to integrate these changes in this
  release :)

v5:
- Use bound length if reserving memzone with zero length

v4:
- Fixes in memzone test
- Account for element padding
- Fix for wrong memzone size being returned
- Documentation fixes

Anatoly Burakov (3):
  malloc: add finding biggest free IOVA-contiguous element
  malloc: allow reserving biggest element
  memzone: improve zero-length memzone reserve

 lib/librte_eal/common/eal_common_memzone.c  |  70 ++-------
 lib/librte_eal/common/include/rte_memzone.h |  24 ++-
 lib/librte_eal/common/malloc_elem.c         |  79 ++++++++++
 lib/librte_eal/common/malloc_elem.h         |   6 +
 lib/librte_eal/common/malloc_heap.c         | 126 +++++++++++++++
 lib/librte_eal/common/malloc_heap.h         |   4 +
 test/test/test_memzone.c                    | 165 +++++++++++---------
 7 files changed, 343 insertions(+), 131 deletions(-)
  
Thomas Monjalon July 13, 2018, 9:24 a.m. UTC | #5
31/05/2018 11:50, Anatoly Burakov:
> This patchset does two things. First, it enables reserving
> memzones of zero-length that are IOVA-contiguous. Second,
> it fixes a long-standing race condition in reserving
> zero-length memzones, where malloc heap is not locked between
> stats collection and reservation, and will instead allocate
> biggest element on the spot.
> 
> Some limitations are added, but they are a trade-off between
> not having race conditions and user convenience. It would be
> possible to lock all heaps during memzone reserve for zero-
> length, and that would keep the old behavior, but given how
> such allocation (especially when asking for IOVA-contiguous
> memory) may take a long time, a design decision was made to
> keep things simple, and only check other heaps if the
> current one is completely busy.
> 
> Ideas on improvement are welcome.
> 
> Anatoly Burakov (3):
>   malloc: add finding biggest free IOVA-contiguous element
>   malloc: allow reserving biggest element
>   memzone: improve zero-length memzone reserve

Applied, thanks
  

Patch

diff --git a/lib/librte_eal/common/include/rte_malloc.h b/lib/librte_eal/common/include/rte_malloc.h
index a9fb7e4..2553201 100644
--- a/lib/librte_eal/common/include/rte_malloc.h
+++ b/lib/librte_eal/common/include/rte_malloc.h
@@ -27,6 +27,7 @@  struct rte_malloc_socket_stats {
 	size_t heap_totalsz_bytes; /**< Total bytes on heap */
 	size_t heap_freesz_bytes;  /**< Total free bytes on heap */
 	size_t greatest_free_size; /**< Size in bytes of largest free block */
+	size_t greatest_free_iova_contig_size; /**< Size in bytes of largest IOVA-contiguous block */
 	unsigned free_count;       /**< Number of free elements on heap */
 	unsigned alloc_count;      /**< Number of allocated elements on heap */
 	size_t heap_allocsz_bytes; /**< Total allocated bytes on heap */
diff --git a/lib/librte_eal/common/malloc_elem.c b/lib/librte_eal/common/malloc_elem.c
index ee79dcd..d28ede6 100644
--- a/lib/librte_eal/common/malloc_elem.c
+++ b/lib/librte_eal/common/malloc_elem.c
@@ -18,12 +18,71 @@ 
 #include <rte_common.h>
 #include <rte_spinlock.h>
 
+#include "eal_internal_cfg.h"
 #include "eal_memalloc.h"
 #include "malloc_elem.h"
 #include "malloc_heap.h"
 
 #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
 
+size_t
+malloc_elem_find_max_iova_contig(struct malloc_elem *elem)
+{
+	void *cur_page, *contig_seg_start, *page_end, *elem_end, *cur_seg_end;
+	rte_iova_t expected_iova;
+	struct rte_memseg *ms;
+	size_t page_sz, cur, max;
+
+	/* if we're in IOVA as VA mode, or if we're in legacy mode with
+	 * hugepages, all elements are IOVA-contiguous.
+	 */
+	if (rte_eal_iova_mode() == RTE_IOVA_VA ||
+			(internal_config.legacy_mem && rte_eal_has_hugepages()))
+		return elem->size;
+
+	page_sz = (size_t)elem->msl->page_sz;
+	elem_end = RTE_PTR_ADD(elem, elem->size);
+	cur_page = RTE_PTR_ALIGN_FLOOR(elem, page_sz);
+	ms = rte_mem_virt2memseg(cur_page, elem->msl);
+	contig_seg_start = elem;
+
+	/* do first iteration outside the loop */
+	page_end = RTE_PTR_ADD(cur_page, page_sz);
+	cur_seg_end = RTE_MIN(page_end, elem_end);
+	cur = RTE_PTR_DIFF(cur_seg_end, contig_seg_start);
+	max = cur;
+	expected_iova = ms->iova + page_sz;
+	/* memsegs are contiguous in memory */
+	ms++;
+
+	cur_page = RTE_PTR_ADD(cur_page, page_sz);
+
+	while (cur_page < elem_end) {
+		page_end = RTE_PTR_ADD(cur_page, page_sz);
+		cur_seg_end = RTE_MIN(page_end, elem_end);
+
+		/* reset start of contiguous segment if unexpected iova. this is
+		 * a contiguous segment, and elem->size includes header len, so
+		 * move element start point to where header would've started
+		 */
+		if (ms->iova != expected_iova)
+			contig_seg_start = RTE_PTR_SUB(cur_page,
+					MALLOC_ELEM_HEADER_LEN);
+		cur = RTE_PTR_DIFF(cur_seg_end, contig_seg_start);
+		/* update max if cur value is bigger */
+		if (cur > max)
+			max = cur;
+
+		/* move to next page */
+		cur_page = page_end;
+		expected_iova = ms->iova + page_sz;
+		/* memsegs are contiguous in memory */
+		ms++;
+	}
+
+	return max;
+}
+
 /*
  * Initialize a general malloc_elem header structure
  */
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index 8f4aef8..19e8db5 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -177,4 +177,7 @@  malloc_elem_free_list_index(size_t size);
 void
 malloc_elem_free_list_insert(struct malloc_elem *elem);
 
+size_t
+malloc_elem_find_max_iova_contig(struct malloc_elem *elem);
+
 #endif /* MALLOC_ELEM_H_ */
diff --git a/lib/librte_eal/common/malloc_heap.c b/lib/librte_eal/common/malloc_heap.c
index 590e9e3..d8ad164 100644
--- a/lib/librte_eal/common/malloc_heap.c
+++ b/lib/librte_eal/common/malloc_heap.c
@@ -764,16 +764,24 @@  malloc_heap_get_stats(struct malloc_heap *heap,
 	socket_stats->free_count = 0;
 	socket_stats->heap_freesz_bytes = 0;
 	socket_stats->greatest_free_size = 0;
+	socket_stats->greatest_free_iova_contig_size = 0;
 
 	/* Iterate through free list */
 	for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
 		for (elem = LIST_FIRST(&heap->free_head[idx]);
 			!!elem; elem = LIST_NEXT(elem, free_list))
 		{
+			size_t iova_contig_sz;
 			socket_stats->free_count++;
 			socket_stats->heap_freesz_bytes += elem->size;
 			if (elem->size > socket_stats->greatest_free_size)
 				socket_stats->greatest_free_size = elem->size;
+			iova_contig_sz =
+					malloc_elem_find_max_iova_contig(elem);
+			if (iova_contig_sz >
+					socket_stats->greatest_free_iova_contig_size)
+				socket_stats->greatest_free_iova_contig_size =
+						iova_contig_sz;
 		}
 	}
 	/* Get stats on overall heap and allocated memory on this heap */
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index b51a6d1..b4e87a3 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -195,6 +195,8 @@  rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
 		fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
 		fprintf(f, "\tGreatest_free_size:%zu,\n",
 				sock_stats.greatest_free_size);
+		fprintf(f, "\tGreatest_free_iova_contig_size:%zu,\n",
+				sock_stats.greatest_free_iova_contig_size);
 		fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
 		fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
 	}