89f3a81574
Some unit test are not relevant anymore. It is the case of those malloc UTs that checked corner cases when allocating MALLOC_MEMZONE_SIZE chunks, and the case of those memzone UTs relaying of specific free memsegs of rhte reserved memzone. Other UTs just need to be update, for example, to calculate maximum free block size available. Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
967 lines
24 KiB
C
967 lines
24 KiB
C
/*-
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <rte_common.h>
|
|
#include <rte_memory.h>
|
|
#include <rte_memzone.h>
|
|
#include <rte_per_lcore.h>
|
|
#include <rte_launch.h>
|
|
#include <rte_eal.h>
|
|
#include <rte_per_lcore.h>
|
|
#include <rte_lcore.h>
|
|
#include <rte_malloc.h>
|
|
#include <rte_cycles.h>
|
|
#include <rte_random.h>
|
|
#include <rte_string_fns.h>
|
|
|
|
#include "test.h"
|
|
|
|
#define N 10000
|
|
|
|
/*
|
|
* Malloc
|
|
* ======
|
|
*
|
|
* Allocate some dynamic memory from heap (3 areas). Check that areas
|
|
* don't overlap and that alignment constraints match. This test is
|
|
* done many times on different lcores simultaneously.
|
|
*/
|
|
|
|
/* Test if memory overlaps: return 1 if true, or 0 if false. */
|
|
static int
|
|
is_memory_overlap(void *p1, size_t len1, void *p2, size_t len2)
|
|
{
|
|
unsigned long ptr1 = (unsigned long)p1;
|
|
unsigned long ptr2 = (unsigned long)p2;
|
|
|
|
if (ptr2 >= ptr1 && (ptr2 - ptr1) < len1)
|
|
return 1;
|
|
else if (ptr2 < ptr1 && (ptr1 - ptr2) < len2)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
is_aligned(void *p, int align)
|
|
{
|
|
unsigned long addr = (unsigned long)p;
|
|
unsigned mask = align - 1;
|
|
|
|
if (addr & mask)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
test_align_overlap_per_lcore(__attribute__((unused)) void *arg)
|
|
{
|
|
const unsigned align1 = 8,
|
|
align2 = 64,
|
|
align3 = 2048;
|
|
unsigned i,j;
|
|
void *p1 = NULL, *p2 = NULL, *p3 = NULL;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < N; i++) {
|
|
p1 = rte_zmalloc("dummy", 1000, align1);
|
|
if (!p1){
|
|
printf("rte_zmalloc returned NULL (i=%u)\n", i);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
for(j = 0; j < 1000 ; j++) {
|
|
if( *(char *)p1 != 0) {
|
|
printf("rte_zmalloc didn't zero"
|
|
"the allocated memory\n");
|
|
ret = -1;
|
|
}
|
|
}
|
|
p2 = rte_malloc("dummy", 1000, align2);
|
|
if (!p2){
|
|
printf("rte_malloc returned NULL (i=%u)\n", i);
|
|
ret = -1;
|
|
rte_free(p1);
|
|
break;
|
|
}
|
|
p3 = rte_malloc("dummy", 1000, align3);
|
|
if (!p3){
|
|
printf("rte_malloc returned NULL (i=%u)\n", i);
|
|
ret = -1;
|
|
rte_free(p1);
|
|
rte_free(p2);
|
|
break;
|
|
}
|
|
if (is_memory_overlap(p1, 1000, p2, 1000)) {
|
|
printf("p1 and p2 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (is_memory_overlap(p2, 1000, p3, 1000)) {
|
|
printf("p2 and p3 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (is_memory_overlap(p1, 1000, p3, 1000)) {
|
|
printf("p1 and p3 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p1, align1)) {
|
|
printf("p1 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p2, align2)) {
|
|
printf("p2 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p3, align3)) {
|
|
printf("p3 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
rte_free(p1);
|
|
rte_free(p2);
|
|
rte_free(p3);
|
|
}
|
|
rte_malloc_dump_stats(stdout, "dummy");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
test_reordered_free_per_lcore(__attribute__((unused)) void *arg)
|
|
{
|
|
const unsigned align1 = 8,
|
|
align2 = 64,
|
|
align3 = 2048;
|
|
unsigned i,j;
|
|
void *p1, *p2, *p3;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < 30; i++) {
|
|
p1 = rte_zmalloc("dummy", 1000, align1);
|
|
if (!p1){
|
|
printf("rte_zmalloc returned NULL (i=%u)\n", i);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
for(j = 0; j < 1000 ; j++) {
|
|
if( *(char *)p1 != 0) {
|
|
printf("rte_zmalloc didn't zero"
|
|
"the allocated memory\n");
|
|
ret = -1;
|
|
}
|
|
}
|
|
/* use calloc to allocate 1000 16-byte items this time */
|
|
p2 = rte_calloc("dummy", 1000, 16, align2);
|
|
/* for third request use regular malloc again */
|
|
p3 = rte_malloc("dummy", 1000, align3);
|
|
if (!p2 || !p3){
|
|
printf("rte_malloc returned NULL (i=%u)\n", i);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
if (is_memory_overlap(p1, 1000, p2, 1000)) {
|
|
printf("p1 and p2 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (is_memory_overlap(p2, 1000, p3, 1000)) {
|
|
printf("p2 and p3 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (is_memory_overlap(p1, 1000, p3, 1000)) {
|
|
printf("p1 and p3 overlaps\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p1, align1)) {
|
|
printf("p1 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p2, align2)) {
|
|
printf("p2 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
if (!is_aligned(p3, align3)) {
|
|
printf("p3 is not aligned\n");
|
|
ret = -1;
|
|
}
|
|
/* try freeing in every possible order */
|
|
switch (i%6){
|
|
case 0:
|
|
rte_free(p1);
|
|
rte_free(p2);
|
|
rte_free(p3);
|
|
break;
|
|
case 1:
|
|
rte_free(p1);
|
|
rte_free(p3);
|
|
rte_free(p2);
|
|
break;
|
|
case 2:
|
|
rte_free(p2);
|
|
rte_free(p1);
|
|
rte_free(p3);
|
|
break;
|
|
case 3:
|
|
rte_free(p2);
|
|
rte_free(p3);
|
|
rte_free(p1);
|
|
break;
|
|
case 4:
|
|
rte_free(p3);
|
|
rte_free(p1);
|
|
rte_free(p2);
|
|
break;
|
|
case 5:
|
|
rte_free(p3);
|
|
rte_free(p2);
|
|
rte_free(p1);
|
|
break;
|
|
}
|
|
}
|
|
rte_malloc_dump_stats(stdout, "dummy");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* test function inside the malloc lib*/
|
|
static int
|
|
test_str_to_size(void)
|
|
{
|
|
struct {
|
|
const char *str;
|
|
uint64_t value;
|
|
} test_values[] =
|
|
{{ "5G", (uint64_t)5 * 1024 * 1024 *1024 },
|
|
{"0x20g", (uint64_t)0x20 * 1024 * 1024 *1024},
|
|
{"10M", 10 * 1024 * 1024},
|
|
{"050m", 050 * 1024 * 1024},
|
|
{"8K", 8 * 1024},
|
|
{"15k", 15 * 1024},
|
|
{"0200", 0200},
|
|
{"0x103", 0x103},
|
|
{"432", 432},
|
|
{"-1", 0}, /* negative values return 0 */
|
|
{" -2", 0},
|
|
{" -3MB", 0},
|
|
{"18446744073709551616", 0} /* ULLONG_MAX + 1 == out of range*/
|
|
};
|
|
unsigned i;
|
|
for (i = 0; i < sizeof(test_values)/sizeof(test_values[0]); i++)
|
|
if (rte_str_to_size(test_values[i].str) != test_values[i].value)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_multi_alloc_statistics(void)
|
|
{
|
|
int socket = 0;
|
|
struct rte_malloc_socket_stats pre_stats, post_stats ,first_stats, second_stats;
|
|
size_t size = 2048;
|
|
int align = 1024;
|
|
#ifndef RTE_LIBRTE_MALLOC_DEBUG
|
|
int trailer_size = 0;
|
|
#else
|
|
int trailer_size = RTE_CACHE_LINE_SIZE;
|
|
#endif
|
|
int overhead = RTE_CACHE_LINE_SIZE + trailer_size;
|
|
|
|
rte_malloc_get_socket_stats(socket, &pre_stats);
|
|
|
|
void *p1 = rte_malloc_socket("stats", size , align, socket);
|
|
if (!p1)
|
|
return -1;
|
|
rte_free(p1);
|
|
rte_malloc_dump_stats(stdout, "stats");
|
|
|
|
rte_malloc_get_socket_stats(socket,&post_stats);
|
|
/* Check statistics reported are correct */
|
|
/* All post stats should be equal to pre stats after alloc freed */
|
|
if ((post_stats.heap_totalsz_bytes != pre_stats.heap_totalsz_bytes) &&
|
|
(post_stats.heap_freesz_bytes!=pre_stats.heap_freesz_bytes) &&
|
|
(post_stats.heap_allocsz_bytes!=pre_stats.heap_allocsz_bytes)&&
|
|
(post_stats.alloc_count!=pre_stats.alloc_count)&&
|
|
(post_stats.free_count!=pre_stats.free_count)) {
|
|
printf("Malloc statistics are incorrect - freed alloc\n");
|
|
return -1;
|
|
}
|
|
/* Check two consecutive allocations */
|
|
size = 1024;
|
|
align = 0;
|
|
rte_malloc_get_socket_stats(socket,&pre_stats);
|
|
void *p2 = rte_malloc_socket("add", size ,align, socket);
|
|
if (!p2)
|
|
return -1;
|
|
rte_malloc_get_socket_stats(socket,&first_stats);
|
|
|
|
void *p3 = rte_malloc_socket("add2", size,align, socket);
|
|
if (!p3)
|
|
return -1;
|
|
|
|
rte_malloc_get_socket_stats(socket,&second_stats);
|
|
|
|
rte_free(p2);
|
|
rte_free(p3);
|
|
|
|
/* After freeing both allocations check stats return to original */
|
|
rte_malloc_get_socket_stats(socket, &post_stats);
|
|
|
|
if(second_stats.heap_totalsz_bytes != first_stats.heap_totalsz_bytes) {
|
|
printf("Incorrect heap statistics: Total size \n");
|
|
return -1;
|
|
}
|
|
/* Check allocated size is equal to two additions plus overhead */
|
|
if(second_stats.heap_allocsz_bytes !=
|
|
size + overhead + first_stats.heap_allocsz_bytes) {
|
|
printf("Incorrect heap statistics: Allocated size \n");
|
|
return -1;
|
|
}
|
|
/* Check that allocation count increments correctly i.e. +1 */
|
|
if (second_stats.alloc_count != first_stats.alloc_count + 1) {
|
|
printf("Incorrect heap statistics: Allocated count \n");
|
|
return -1;
|
|
}
|
|
|
|
if (second_stats.free_count != first_stats.free_count){
|
|
printf("Incorrect heap statistics: Free count \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Make sure that we didn't touch our greatest chunk: 2 * 11M) */
|
|
if (post_stats.greatest_free_size != pre_stats.greatest_free_size) {
|
|
printf("Incorrect heap statistics: Greatest free size \n");
|
|
return -1;
|
|
}
|
|
/* Free size must equal the original free size minus the new allocation*/
|
|
if (first_stats.heap_freesz_bytes <= second_stats.heap_freesz_bytes) {
|
|
printf("Incorrect heap statistics: Free size \n");
|
|
return -1;
|
|
}
|
|
|
|
if ((post_stats.heap_totalsz_bytes != pre_stats.heap_totalsz_bytes) &&
|
|
(post_stats.heap_freesz_bytes!=pre_stats.heap_freesz_bytes) &&
|
|
(post_stats.heap_allocsz_bytes!=pre_stats.heap_allocsz_bytes)&&
|
|
(post_stats.alloc_count!=pre_stats.alloc_count)&&
|
|
(post_stats.free_count!=pre_stats.free_count)) {
|
|
printf("Malloc statistics are incorrect - freed alloc\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_rte_malloc_type_limits(void)
|
|
{
|
|
/* The type-limits functionality is not yet implemented,
|
|
* so always return 0 no matter what the retval.
|
|
*/
|
|
const char *typename = "limit_test";
|
|
rte_malloc_set_limit(typename, 64 * 1024);
|
|
rte_malloc_dump_stats(stdout, typename);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_realloc(void)
|
|
{
|
|
const char hello_str[] = "Hello, world!";
|
|
const unsigned size1 = 1024;
|
|
const unsigned size2 = size1 + 1024;
|
|
const unsigned size3 = size2;
|
|
const unsigned size4 = size3 + 1024;
|
|
|
|
/* test data is the same even if element is moved*/
|
|
char *ptr1 = rte_zmalloc(NULL, size1, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr1){
|
|
printf("NULL pointer returned from rte_zmalloc\n");
|
|
return -1;
|
|
}
|
|
snprintf(ptr1, size1, "%s" ,hello_str);
|
|
char *ptr2 = rte_realloc(ptr1, size2, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr2){
|
|
rte_free(ptr1);
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
return -1;
|
|
}
|
|
if (ptr1 == ptr2){
|
|
printf("unexpected - ptr1 == ptr2\n");
|
|
}
|
|
if (strcmp(ptr2, hello_str) != 0){
|
|
printf("Error - lost data from pointed area\n");
|
|
rte_free(ptr2);
|
|
return -1;
|
|
}
|
|
unsigned i;
|
|
for (i = strnlen(hello_str, sizeof(hello_str)); i < size1; i++)
|
|
if (ptr2[i] != 0){
|
|
printf("Bad data in realloc\n");
|
|
rte_free(ptr2);
|
|
return -1;
|
|
}
|
|
/* now allocate third element, free the second
|
|
* and resize third. It should not move. (ptr1 is now invalid)
|
|
*/
|
|
char *ptr3 = rte_zmalloc(NULL, size3, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr3){
|
|
printf("NULL pointer returned from rte_zmalloc\n");
|
|
rte_free(ptr2);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < size3; i++)
|
|
if (ptr3[i] != 0){
|
|
printf("Bad data in zmalloc\n");
|
|
rte_free(ptr3);
|
|
rte_free(ptr2);
|
|
return -1;
|
|
}
|
|
rte_free(ptr2);
|
|
/* first resize to half the size of the freed block */
|
|
char *ptr4 = rte_realloc(ptr3, size4, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr4){
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
rte_free(ptr3);
|
|
return -1;
|
|
}
|
|
if (ptr3 != ptr4){
|
|
printf("Unexpected - ptr4 != ptr3\n");
|
|
rte_free(ptr4);
|
|
return -1;
|
|
}
|
|
/* now resize again to the full size of the freed block */
|
|
ptr4 = rte_realloc(ptr3, size3 + size2 + size1, RTE_CACHE_LINE_SIZE);
|
|
if (ptr3 != ptr4){
|
|
printf("Unexpected - ptr4 != ptr3 on second resize\n");
|
|
rte_free(ptr4);
|
|
return -1;
|
|
}
|
|
rte_free(ptr4);
|
|
|
|
/* now try a resize to a smaller size, see if it works */
|
|
const unsigned size5 = 1024;
|
|
const unsigned size6 = size5 / 2;
|
|
char *ptr5 = rte_malloc(NULL, size5, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr5){
|
|
printf("NULL pointer returned from rte_malloc\n");
|
|
return -1;
|
|
}
|
|
char *ptr6 = rte_realloc(ptr5, size6, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr6){
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
rte_free(ptr5);
|
|
return -1;
|
|
}
|
|
if (ptr5 != ptr6){
|
|
printf("Error, resizing to a smaller size moved data\n");
|
|
rte_free(ptr6);
|
|
return -1;
|
|
}
|
|
rte_free(ptr6);
|
|
|
|
/* check for behaviour changing alignment */
|
|
const unsigned size7 = 1024;
|
|
const unsigned orig_align = RTE_CACHE_LINE_SIZE;
|
|
unsigned new_align = RTE_CACHE_LINE_SIZE * 2;
|
|
char *ptr7 = rte_malloc(NULL, size7, orig_align);
|
|
if (!ptr7){
|
|
printf("NULL pointer returned from rte_malloc\n");
|
|
return -1;
|
|
}
|
|
/* calc an alignment we don't already have */
|
|
while(RTE_PTR_ALIGN(ptr7, new_align) == ptr7)
|
|
new_align *= 2;
|
|
char *ptr8 = rte_realloc(ptr7, size7, new_align);
|
|
if (!ptr8){
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
rte_free(ptr7);
|
|
return -1;
|
|
}
|
|
if (RTE_PTR_ALIGN(ptr8, new_align) != ptr8){
|
|
printf("Failure to re-align data\n");
|
|
rte_free(ptr8);
|
|
return -1;
|
|
}
|
|
rte_free(ptr8);
|
|
|
|
/* test behaviour when there is a free block after current one,
|
|
* but its not big enough
|
|
*/
|
|
unsigned size9 = 1024, size10 = 1024;
|
|
unsigned size11 = size9 + size10 + 256;
|
|
char *ptr9 = rte_malloc(NULL, size9, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr9){
|
|
printf("NULL pointer returned from rte_malloc\n");
|
|
return -1;
|
|
}
|
|
char *ptr10 = rte_malloc(NULL, size10, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr10){
|
|
printf("NULL pointer returned from rte_malloc\n");
|
|
return -1;
|
|
}
|
|
rte_free(ptr9);
|
|
char *ptr11 = rte_realloc(ptr10, size11, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr11){
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
rte_free(ptr10);
|
|
return -1;
|
|
}
|
|
if (ptr11 == ptr10){
|
|
printf("Error, unexpected that realloc has not created new buffer\n");
|
|
rte_free(ptr11);
|
|
return -1;
|
|
}
|
|
rte_free(ptr11);
|
|
|
|
/* check we don't crash if we pass null to realloc
|
|
* We should get a malloc of the size requested*/
|
|
const size_t size12 = 1024;
|
|
size_t size12_check;
|
|
char *ptr12 = rte_realloc(NULL, size12, RTE_CACHE_LINE_SIZE);
|
|
if (!ptr12){
|
|
printf("NULL pointer returned from rte_realloc\n");
|
|
return -1;
|
|
}
|
|
if (rte_malloc_validate(ptr12, &size12_check) < 0 ||
|
|
size12_check != size12){
|
|
rte_free(ptr12);
|
|
return -1;
|
|
}
|
|
rte_free(ptr12);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_random_alloc_free(void *_ __attribute__((unused)))
|
|
{
|
|
struct mem_list {
|
|
struct mem_list *next;
|
|
char data[0];
|
|
} *list_head = NULL;
|
|
unsigned i;
|
|
unsigned count = 0;
|
|
|
|
rte_srand((unsigned)rte_rdtsc());
|
|
|
|
for (i = 0; i < N; i++){
|
|
unsigned free_mem = 0;
|
|
size_t allocated_size;
|
|
while (!free_mem){
|
|
const unsigned mem_size = sizeof(struct mem_list) + \
|
|
rte_rand() % (64 * 1024);
|
|
const unsigned align = 1 << (rte_rand() % 12); /* up to 4k alignment */
|
|
struct mem_list *entry = rte_malloc(NULL,
|
|
mem_size, align);
|
|
if (entry == NULL)
|
|
return -1;
|
|
if (RTE_PTR_ALIGN(entry, align)!= entry)
|
|
return -1;
|
|
if (rte_malloc_validate(entry, &allocated_size) == -1
|
|
|| allocated_size < mem_size)
|
|
return -1;
|
|
memset(entry->data, rte_lcore_id(),
|
|
mem_size - sizeof(*entry));
|
|
entry->next = list_head;
|
|
if (rte_malloc_validate(entry, NULL) == -1)
|
|
return -1;
|
|
list_head = entry;
|
|
|
|
count++;
|
|
/* switch to freeing the memory with a 20% probability */
|
|
free_mem = ((rte_rand() % 10) >= 8);
|
|
}
|
|
while (list_head){
|
|
struct mem_list *entry = list_head;
|
|
list_head = list_head->next;
|
|
rte_free(entry);
|
|
}
|
|
}
|
|
printf("Lcore %u allocated/freed %u blocks\n", rte_lcore_id(), count);
|
|
return 0;
|
|
}
|
|
|
|
#define err_return() do { \
|
|
printf("%s: %d - Error\n", __func__, __LINE__); \
|
|
goto err_return; \
|
|
} while (0)
|
|
|
|
static int
|
|
test_rte_malloc_validate(void)
|
|
{
|
|
const size_t request_size = 1024;
|
|
size_t allocated_size;
|
|
char *data_ptr = rte_malloc(NULL, request_size, RTE_CACHE_LINE_SIZE);
|
|
#ifdef RTE_LIBRTE_MALLOC_DEBUG
|
|
int retval;
|
|
char *over_write_vals = NULL;
|
|
#endif
|
|
|
|
if (data_ptr == NULL) {
|
|
printf("%s: %d - Allocation error\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
/* check that a null input returns -1 */
|
|
if (rte_malloc_validate(NULL, NULL) != -1)
|
|
err_return();
|
|
|
|
/* check that we get ok on a valid pointer */
|
|
if (rte_malloc_validate(data_ptr, &allocated_size) < 0)
|
|
err_return();
|
|
|
|
/* check that the returned size is ok */
|
|
if (allocated_size < request_size)
|
|
err_return();
|
|
|
|
#ifdef RTE_LIBRTE_MALLOC_DEBUG
|
|
|
|
/****** change the header to be bad */
|
|
char save_buf[64];
|
|
over_write_vals = (char *)((uintptr_t)data_ptr - sizeof(save_buf));
|
|
/* first save the data as a backup before overwriting it */
|
|
memcpy(save_buf, over_write_vals, sizeof(save_buf));
|
|
memset(over_write_vals, 1, sizeof(save_buf));
|
|
/* then run validate */
|
|
retval = rte_malloc_validate(data_ptr, NULL);
|
|
/* finally restore the data again */
|
|
memcpy(over_write_vals, save_buf, sizeof(save_buf));
|
|
/* check we previously had an error */
|
|
if (retval != -1)
|
|
err_return();
|
|
|
|
/* check all ok again */
|
|
if (rte_malloc_validate(data_ptr, &allocated_size) < 0)
|
|
err_return();
|
|
|
|
/**** change the trailer to be bad */
|
|
over_write_vals = (char *)((uintptr_t)data_ptr + allocated_size);
|
|
/* first save the data as a backup before overwriting it */
|
|
memcpy(save_buf, over_write_vals, sizeof(save_buf));
|
|
memset(over_write_vals, 1, sizeof(save_buf));
|
|
/* then run validate */
|
|
retval = rte_malloc_validate(data_ptr, NULL);
|
|
/* finally restore the data again */
|
|
memcpy(over_write_vals, save_buf, sizeof(save_buf));
|
|
if (retval != -1)
|
|
err_return();
|
|
|
|
/* check all ok again */
|
|
if (rte_malloc_validate(data_ptr, &allocated_size) < 0)
|
|
err_return();
|
|
#endif
|
|
|
|
rte_free(data_ptr);
|
|
return 0;
|
|
|
|
err_return:
|
|
/*clean up */
|
|
rte_free(data_ptr);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
test_zero_aligned_alloc(void)
|
|
{
|
|
char *p1 = rte_malloc(NULL,1024, 0);
|
|
if (!p1)
|
|
goto err_return;
|
|
if (!rte_is_aligned(p1, RTE_CACHE_LINE_SIZE))
|
|
goto err_return;
|
|
rte_free(p1);
|
|
return 0;
|
|
|
|
err_return:
|
|
/*clean up */
|
|
if (p1) rte_free(p1);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
test_malloc_bad_params(void)
|
|
{
|
|
const char *type = NULL;
|
|
size_t size = 0;
|
|
unsigned align = RTE_CACHE_LINE_SIZE;
|
|
|
|
/* rte_malloc expected to return null with inappropriate size */
|
|
char *bad_ptr = rte_malloc(type, size, align);
|
|
if (bad_ptr != NULL)
|
|
goto err_return;
|
|
|
|
/* rte_malloc expected to return null with inappropriate alignment */
|
|
align = 17;
|
|
size = 1024;
|
|
|
|
bad_ptr = rte_malloc(type, size, align);
|
|
if (bad_ptr != NULL)
|
|
goto err_return;
|
|
|
|
return 0;
|
|
|
|
err_return:
|
|
/* clean up pointer */
|
|
if (bad_ptr)
|
|
rte_free(bad_ptr);
|
|
return -1;
|
|
}
|
|
|
|
/* Check if memory is avilable on a specific socket */
|
|
static int
|
|
is_mem_on_socket(int32_t socket)
|
|
{
|
|
const struct rte_memseg *ms = rte_eal_get_physmem_layout();
|
|
unsigned i;
|
|
|
|
for (i = 0; i < RTE_MAX_MEMSEG; i++) {
|
|
if (socket == ms[i].socket_id)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find what socket a memory address is on. Only works for addresses within
|
|
* memsegs, not heap or stack...
|
|
*/
|
|
static int32_t
|
|
addr_to_socket(void * addr)
|
|
{
|
|
const struct rte_memseg *ms = rte_eal_get_physmem_layout();
|
|
unsigned i;
|
|
|
|
for (i = 0; i < RTE_MAX_MEMSEG; i++) {
|
|
if ((ms[i].addr <= addr) &&
|
|
((uintptr_t)addr <
|
|
((uintptr_t)ms[i].addr + (uintptr_t)ms[i].len)))
|
|
return ms[i].socket_id;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Test using rte_[c|m|zm]alloc_socket() on a specific socket */
|
|
static int
|
|
test_alloc_single_socket(int32_t socket)
|
|
{
|
|
const char *type = NULL;
|
|
const size_t size = 10;
|
|
const unsigned align = 0;
|
|
char *mem = NULL;
|
|
int32_t desired_socket = (socket == SOCKET_ID_ANY) ?
|
|
(int32_t)rte_socket_id() : socket;
|
|
|
|
/* Test rte_calloc_socket() */
|
|
mem = rte_calloc_socket(type, size, sizeof(char), align, socket);
|
|
if (mem == NULL)
|
|
return -1;
|
|
if (addr_to_socket(mem) != desired_socket) {
|
|
rte_free(mem);
|
|
return -1;
|
|
}
|
|
rte_free(mem);
|
|
|
|
/* Test rte_malloc_socket() */
|
|
mem = rte_malloc_socket(type, size, align, socket);
|
|
if (mem == NULL)
|
|
return -1;
|
|
if (addr_to_socket(mem) != desired_socket) {
|
|
return -1;
|
|
}
|
|
rte_free(mem);
|
|
|
|
/* Test rte_zmalloc_socket() */
|
|
mem = rte_zmalloc_socket(type, size, align, socket);
|
|
if (mem == NULL)
|
|
return -1;
|
|
if (addr_to_socket(mem) != desired_socket) {
|
|
rte_free(mem);
|
|
return -1;
|
|
}
|
|
rte_free(mem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_alloc_socket(void)
|
|
{
|
|
unsigned socket_count = 0;
|
|
unsigned i;
|
|
|
|
if (test_alloc_single_socket(SOCKET_ID_ANY) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
|
|
if (is_mem_on_socket(i)) {
|
|
socket_count++;
|
|
if (test_alloc_single_socket(i) < 0) {
|
|
printf("Fail: rte_malloc_socket(..., %u) did not succeed\n",
|
|
i);
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
if (test_alloc_single_socket(i) == 0) {
|
|
printf("Fail: rte_malloc_socket(..., %u) succeeded\n",
|
|
i);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Print warnign if only a single socket, but don't fail the test */
|
|
if (socket_count < 2) {
|
|
printf("WARNING: alloc_socket test needs memory on multiple sockets!\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_malloc(void)
|
|
{
|
|
unsigned lcore_id;
|
|
int ret = 0;
|
|
|
|
if (test_str_to_size() < 0){
|
|
printf("test_str_to_size() failed\n");
|
|
return -1;
|
|
}
|
|
else printf("test_str_to_size() passed\n");
|
|
|
|
if (test_zero_aligned_alloc() < 0){
|
|
printf("test_zero_aligned_alloc() failed\n");
|
|
return -1;
|
|
}
|
|
else printf("test_zero_aligned_alloc() passed\n");
|
|
|
|
if (test_malloc_bad_params() < 0){
|
|
printf("test_malloc_bad_params() failed\n");
|
|
return -1;
|
|
}
|
|
else printf("test_malloc_bad_params() passed\n");
|
|
|
|
if (test_realloc() < 0){
|
|
printf("test_realloc() failed\n");
|
|
return -1;
|
|
}
|
|
else printf("test_realloc() passed\n");
|
|
|
|
/*----------------------------*/
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
rte_eal_remote_launch(test_align_overlap_per_lcore, NULL, lcore_id);
|
|
}
|
|
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
if (rte_eal_wait_lcore(lcore_id) < 0)
|
|
ret = -1;
|
|
}
|
|
if (ret < 0){
|
|
printf("test_align_overlap_per_lcore() failed\n");
|
|
return ret;
|
|
}
|
|
else printf("test_align_overlap_per_lcore() passed\n");
|
|
|
|
/*----------------------------*/
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
rte_eal_remote_launch(test_reordered_free_per_lcore, NULL, lcore_id);
|
|
}
|
|
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
if (rte_eal_wait_lcore(lcore_id) < 0)
|
|
ret = -1;
|
|
}
|
|
if (ret < 0){
|
|
printf("test_reordered_free_per_lcore() failed\n");
|
|
return ret;
|
|
}
|
|
else printf("test_reordered_free_per_lcore() passed\n");
|
|
|
|
/*----------------------------*/
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
rte_eal_remote_launch(test_random_alloc_free, NULL, lcore_id);
|
|
}
|
|
|
|
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
|
|
if (rte_eal_wait_lcore(lcore_id) < 0)
|
|
ret = -1;
|
|
}
|
|
if (ret < 0){
|
|
printf("test_random_alloc_free() failed\n");
|
|
return ret;
|
|
}
|
|
else printf("test_random_alloc_free() passed\n");
|
|
|
|
/*----------------------------*/
|
|
ret = test_rte_malloc_type_limits();
|
|
if (ret < 0){
|
|
printf("test_rte_malloc_type_limits() failed\n");
|
|
return ret;
|
|
}
|
|
/* TODO: uncomment following line once type limits are valid */
|
|
/*else printf("test_rte_malloc_type_limits() passed\n");*/
|
|
|
|
/*----------------------------*/
|
|
ret = test_rte_malloc_validate();
|
|
if (ret < 0){
|
|
printf("test_rte_malloc_validate() failed\n");
|
|
return ret;
|
|
}
|
|
else printf("test_rte_malloc_validate() passed\n");
|
|
|
|
ret = test_alloc_socket();
|
|
if (ret < 0){
|
|
printf("test_alloc_socket() failed\n");
|
|
return ret;
|
|
}
|
|
else printf("test_alloc_socket() passed\n");
|
|
|
|
ret = test_multi_alloc_statistics();
|
|
if (ret < 0) {
|
|
printf("test_multi_alloc_statistics() failed\n");
|
|
return ret;
|
|
}
|
|
else
|
|
printf("test_multi_alloc_statistics() passed\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct test_command malloc_cmd = {
|
|
.command = "malloc_autotest",
|
|
.callback = test_malloc,
|
|
};
|
|
REGISTER_TEST_COMMAND(malloc_cmd);
|