From d4292769072935e4ec3834836ae7f86ddf1043a2 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Jul 2013 11:15:56 +0200 Subject: [PATCH] MEMORY introduce a small allocation pool This patch finally introduces a small allocation pool which recycles all the small portions of memory that are used for headers and pdu structures. This was the initial idea behind wrapping all memory functions in libiscsi. The results of booting are test system up to the login prompt are quite impressive: BEFORE: libiscsi:5 memory is clean at iscsi_destroy_context() after 10712 mallocs, 18 realloc(s) and 10712 free(s) AFTER: libiscsi:5 memory is clean at iscsi_destroy_context() after 41 mallocs, 18 realloc(s), 41 free(s) and 10584 reused small allocations Signed-off-by: Peter Lieven --- include/iscsi-private.h | 7 ++++++ lib/init.c | 50 ++++++++++++++++++++++++++++++++++++++--- lib/pdu.c | 29 +++++++++++++++++------- lib/socket.c | 4 ++-- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index e9b9e7e..df20702 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -39,6 +39,7 @@ extern "C" { #define ISCSI_HEADER_SIZE (ISCSI_RAW_HEADER_SIZE \ + (iscsi->header_digest == ISCSI_HEADER_DIGEST_NONE?0:ISCSI_DIGEST_SIZE)) +#define SMALL_ALLOC_MAX_FREE (128) /* must be power of 2 */ struct iscsi_in_pdu { struct iscsi_in_pdu *next; @@ -131,6 +132,10 @@ struct iscsi_context { int mallocs; int reallocs; int frees; + int smallocs; + void* smalloc_ptrs[SMALL_ALLOC_MAX_FREE]; + int smalloc_free; + size_t smalloc_size; time_t last_reconnect; int scsi_timeout; @@ -301,6 +306,8 @@ inline void* iscsi_zmalloc(struct iscsi_context *iscsi, size_t size); inline void* iscsi_realloc(struct iscsi_context *iscsi, void* ptr, size_t size); inline void iscsi_free(struct iscsi_context *iscsi, void* ptr); inline char* iscsi_strdup(struct iscsi_context *iscsi, const char* str); +inline void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size); +inline void iscsi_sfree(struct iscsi_context *iscsi, void* ptr); unsigned long crc32c(char *buf, int len); diff --git a/lib/init.c b/lib/init.c index 60a1b6d..14a865f 100644 --- a/lib/init.c +++ b/lib/init.c @@ -33,7 +33,6 @@ #include "iscsi-private.h" #include "slist.h" - inline void* iscsi_malloc(struct iscsi_context *iscsi, size_t size) { void * ptr = malloc(size); if (ptr != NULL) iscsi->mallocs++; @@ -69,6 +68,36 @@ inline char* iscsi_strdup(struct iscsi_context *iscsi, const char* str) { return str2; } +inline void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size) { + void *ptr; + if (size > iscsi->smalloc_size) return NULL; + if (iscsi->smalloc_free > 0) { + ptr = iscsi->smalloc_ptrs[--iscsi->smalloc_free]; + memset(ptr, 0, iscsi->smalloc_size); + iscsi->smallocs++; + } else { + ptr = iscsi_zmalloc(iscsi, iscsi->smalloc_size); + } + return ptr; +} + +inline void iscsi_sfree(struct iscsi_context *iscsi, void* ptr) { + if (ptr == NULL) return; + if (iscsi->smalloc_free == SMALL_ALLOC_MAX_FREE) { + /* SMALL_ALLOC_MAX_FREE should be adjusted that this happens rarely */ + ISCSI_LOG(iscsi,6,"smalloc free == SMALLOC_MAX_FREE"); + int i; + /* remove oldest half of free pointers and copy + * upper half to lower half */ + iscsi->smalloc_free>>=1; + for (i=0; ismalloc_free; i++) { + iscsi_free(iscsi, iscsi->smalloc_ptrs[i]); + iscsi->smalloc_ptrs[i] = iscsi->smalloc_ptrs[i+iscsi->smalloc_free]; + } + } + iscsi->smalloc_ptrs[iscsi->smalloc_free++] = ptr; +} + struct iscsi_context * iscsi_create_context(const char *initiator_name) { @@ -144,6 +173,16 @@ iscsi_create_context(const char *initiator_name) iscsi_set_bind_interfaces(iscsi,getenv("LIBISCSI_BIND_INTERFACES")); } + /* iscsi->smalloc_size is the size for small allocations. this should be + max(ISCSI_HEADER_SIZE, sizeof(struct iscsi_pdu), sizeof(struct iscsi_in_pdu)) + rounded up to the next power of 2. */ + iscsi->smalloc_size = 1; + size_t required = ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE; + if (sizeof(struct iscsi_pdu) > required) required = sizeof(struct iscsi_pdu); + if (sizeof(struct iscsi_in_pdu) > required) required = sizeof(struct iscsi_in_pdu); + while (iscsi->smalloc_size < required) iscsi->smalloc_size <<= 1; + ISCSI_LOG(iscsi,5,"small allocation size is %d byte", iscsi->smalloc_size); + return iscsi; } @@ -236,6 +275,7 @@ int iscsi_destroy_context(struct iscsi_context *iscsi) { struct iscsi_pdu *pdu; + int i; if (iscsi == NULL) { return 0; @@ -283,10 +323,14 @@ iscsi_destroy_context(struct iscsi_context *iscsi) iscsi->connect_data = NULL; + for (i=0;ismalloc_free;i++) { + iscsi_free(iscsi, iscsi->smalloc_ptrs[i]); + } + if (iscsi->mallocs != iscsi->frees) { - ISCSI_LOG(iscsi,1,"%d memory blocks lost at iscsi_destroy_context() after %d malloc(s), %d realloc(s) and %d free(s)",iscsi->mallocs-iscsi->frees,iscsi->mallocs,iscsi->reallocs,iscsi->frees); + ISCSI_LOG(iscsi,1,"%d memory blocks lost at iscsi_destroy_context() after %d malloc(s), %d realloc(s), %d free(s) and %d reused small allocations",iscsi->mallocs-iscsi->frees,iscsi->mallocs,iscsi->reallocs,iscsi->frees,iscsi->smallocs); } else { - ISCSI_LOG(iscsi,5,"memory is clean at iscsi_destroy_context() after %d mallocs, %d realloc(s) and %d frees",iscsi->mallocs,iscsi->reallocs,iscsi->frees); + ISCSI_LOG(iscsi,5,"memory is clean at iscsi_destroy_context() after %d mallocs, %d realloc(s), %d free(s) and %d reused small allocations",iscsi->mallocs,iscsi->reallocs,iscsi->frees,iscsi->smallocs); } memset(iscsi, 0, sizeof(struct iscsi_context)); diff --git a/lib/pdu.c b/lib/pdu.c index f53490a..e682abd 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -84,15 +84,14 @@ iscsi_allocate_pdu_with_itt_flags(struct iscsi_context *iscsi, enum iscsi_opcode { struct iscsi_pdu *pdu; - pdu = iscsi_zmalloc(iscsi, sizeof(struct iscsi_pdu)); + pdu = iscsi_szmalloc(iscsi, sizeof(struct iscsi_pdu)); if (pdu == NULL) { iscsi_set_error(iscsi, "failed to allocate pdu"); return NULL; } pdu->outdata.size = ISCSI_HEADER_SIZE; - pdu->outdata.data = iscsi_malloc(iscsi, pdu->outdata.size); - memset(pdu->outdata.data, 0, ISCSI_HEADER_SIZE); + pdu->outdata.data = iscsi_szmalloc(iscsi, pdu->outdata.size); if (pdu->outdata.data == NULL) { iscsi_set_error(iscsi, "failed to allocate pdu header"); @@ -134,13 +133,21 @@ iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) return; } - iscsi_free(iscsi, pdu->outdata.data); + if (pdu->outdata.size <= iscsi->smalloc_size) { + iscsi_sfree(iscsi, pdu->outdata.data); + } else { + iscsi_free(iscsi, pdu->outdata.data); + } pdu->outdata.data = NULL; - iscsi_free(iscsi, pdu->indata.data); + if (pdu->indata.size <= iscsi->smalloc_size) { + iscsi_sfree(iscsi, pdu->indata.data); + } else { + iscsi_free(iscsi, pdu->indata.data); + } pdu->indata.data = NULL; - iscsi_free(iscsi, pdu); + iscsi_sfree(iscsi, pdu); } @@ -164,9 +171,15 @@ iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data, } if (data->size == 0) { - data->data = iscsi_malloc(iscsi, aligned); + if (aligned <= iscsi->smalloc_size) { + data->data = iscsi_szmalloc(iscsi, aligned); + } else { + data->data = iscsi_malloc(iscsi, aligned); + } } else { - data->data = iscsi_realloc(iscsi, data->data, aligned); + if (aligned > iscsi->smalloc_size) { + data->data = iscsi_realloc(iscsi, data->data, aligned); + } } if (data->data == NULL) { iscsi_set_error(iscsi, "failed to allocate buffer for %d " diff --git a/lib/socket.c b/lib/socket.c index 5d98783..7e71820 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -482,7 +482,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) ssize_t data_size, count; if (iscsi->incoming == NULL) { - iscsi->incoming = iscsi_zmalloc(iscsi, sizeof(struct iscsi_in_pdu)); + iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu)); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; @@ -810,7 +810,7 @@ iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { iscsi_free(iscsi, in->data); in->data=NULL; - iscsi_free(iscsi, in); + iscsi_sfree(iscsi, in); in=NULL; }