f338ef
From 0bf728030e0ad7a49e6e1737ea06ae74da9279d3 Mon Sep 17 00:00:00 2001
f338ef
From: Xavi Hernandez <xhernandez@redhat.com>
f338ef
Date: Fri, 21 Jun 2019 11:28:08 +0200
f338ef
Subject: [PATCH 212/221] core: fix memory allocation issues
f338ef
f338ef
Two problems have been identified that caused that gluster's memory
f338ef
usage were twice higher than required.
f338ef
f338ef
1. An off by 1 error caused that all objects allocated from the memory
f338ef
   pools were taken from a pool bigger than required. Since each pool
f338ef
   corresponds to a size equal to a power of two, this was wasting half
f338ef
   of the available memory.
f338ef
f338ef
2. The header information used for accounting on each memory object was
f338ef
   not taken into consideration when searching for a suitable memory
f338ef
   pool. It was added later when each individual block was allocated.
f338ef
   This made this space "invisible" to memory accounting.
f338ef
f338ef
Credits: Thanks to Nithya Balachandran for identifying this problem and
f338ef
         testing this patch.
f338ef
f338ef
Upstream patch:
f338ef
> BUG: 1722802
f338ef
> Upstream patch link: https://review.gluster.org/c/glusterfs/+/22921
f338ef
> Change-Id: I90e27ad795fe51ca11c13080f62207451f6c138c
f338ef
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
f338ef
f338ef
Fixes: bz#1722801
f338ef
Change-Id: I90e27ad795fe51ca11c13080f62207451f6c138c
f338ef
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
f338ef
Reviewed-on: https://code.engineering.redhat.com/gerrit/174714
f338ef
Tested-by: RHGS Build Bot <nigelb@redhat.com>
f338ef
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
f338ef
---
f338ef
 libglusterfs/src/glusterfs/mem-pool.h |  5 ++-
f338ef
 libglusterfs/src/mem-pool.c           | 57 +++++++++++++++++++----------------
f338ef
 2 files changed, 35 insertions(+), 27 deletions(-)
f338ef
f338ef
diff --git a/libglusterfs/src/glusterfs/mem-pool.h b/libglusterfs/src/glusterfs/mem-pool.h
f338ef
index c5a486b..be0a26d 100644
f338ef
--- a/libglusterfs/src/glusterfs/mem-pool.h
f338ef
+++ b/libglusterfs/src/glusterfs/mem-pool.h
f338ef
@@ -231,7 +231,10 @@ typedef struct pooled_obj_hdr {
f338ef
     struct mem_pool *pool;
f338ef
 } pooled_obj_hdr_t;
f338ef
 
f338ef
-#define AVAILABLE_SIZE(p2) (1 << (p2))
f338ef
+/* Each memory block inside a pool has a fixed size that is a power of two.
f338ef
+ * However each object will have a header that will reduce the available
f338ef
+ * space. */
f338ef
+#define AVAILABLE_SIZE(p2) ((1UL << (p2)) - sizeof(pooled_obj_hdr_t))
f338ef
 
f338ef
 typedef struct per_thread_pool {
f338ef
     /* the pool that was used to request this allocation */
f338ef
diff --git a/libglusterfs/src/mem-pool.c b/libglusterfs/src/mem-pool.c
f338ef
index df167b6..d88041d 100644
f338ef
--- a/libglusterfs/src/mem-pool.c
f338ef
+++ b/libglusterfs/src/mem-pool.c
f338ef
@@ -627,6 +627,7 @@ struct mem_pool *
f338ef
 mem_pool_new_fn(glusterfs_ctx_t *ctx, unsigned long sizeof_type,
f338ef
                 unsigned long count, char *name)
f338ef
 {
f338ef
+    unsigned long extra_size, size;
f338ef
     unsigned int power;
f338ef
     struct mem_pool *new = NULL;
f338ef
     struct mem_pool_shared *pool = NULL;
f338ef
@@ -637,10 +638,25 @@ mem_pool_new_fn(glusterfs_ctx_t *ctx, unsigned long sizeof_type,
f338ef
         return NULL;
f338ef
     }
f338ef
 
f338ef
-    /* We ensure sizeof_type > 1 and the next power of two will be, at least,
f338ef
-     * 2^POOL_SMALLEST */
f338ef
-    sizeof_type |= (1 << POOL_SMALLEST) - 1;
f338ef
-    power = sizeof(sizeof_type) * 8 - __builtin_clzl(sizeof_type - 1) + 1;
f338ef
+    /* This is the overhead we'll have because of memory accounting for each
f338ef
+     * memory block. */
f338ef
+    extra_size = sizeof(pooled_obj_hdr_t);
f338ef
+
f338ef
+    /* We need to compute the total space needed to hold the data type and
f338ef
+     * the header. Given that the smallest block size we have in the pools
f338ef
+     * is 2^POOL_SMALLEST, we need to take the MAX(size, 2^POOL_SMALLEST).
f338ef
+     * However, since this value is only needed to compute its rounded
f338ef
+     * logarithm in base 2, and this only depends on the highest bit set,
f338ef
+     * we can simply do a bitwise or with the minimum size. We need to
f338ef
+     * subtract 1 for correct handling of sizes that are exactly a power
f338ef
+     * of 2. */
f338ef
+    size = (sizeof_type + extra_size - 1UL) | ((1UL << POOL_SMALLEST) - 1UL);
f338ef
+
f338ef
+    /* We compute the logarithm in base 2 rounded up of the resulting size.
f338ef
+     * This value will identify which pool we need to use from the pools of
f338ef
+     * powers of 2. This is equivalent to finding the position of the highest
f338ef
+     * bit set. */
f338ef
+    power = sizeof(size) * 8 - __builtin_clzl(size);
f338ef
     if (power > POOL_LARGEST) {
f338ef
         gf_msg_callingfn("mem-pool", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
f338ef
                          "invalid argument");
f338ef
@@ -732,8 +748,8 @@ mem_get_pool_list(void)
f338ef
     return pool_list;
f338ef
 }
f338ef
 
f338ef
-pooled_obj_hdr_t *
f338ef
-mem_get_from_pool(struct mem_pool *mem_pool, struct mem_pool_shared *pool)
f338ef
+static pooled_obj_hdr_t *
f338ef
+mem_get_from_pool(struct mem_pool *mem_pool)
f338ef
 {
f338ef
     per_thread_pool_list_t *pool_list;
f338ef
     per_thread_pool_t *pt_pool;
f338ef
@@ -747,12 +763,7 @@ mem_get_from_pool(struct mem_pool *mem_pool, struct mem_pool_shared *pool)
f338ef
         return NULL;
f338ef
     }
f338ef
 
f338ef
-    if (mem_pool) {
f338ef
-        pt_pool = &pool_list
f338ef
-                       ->pools[mem_pool->pool->power_of_two - POOL_SMALLEST];
f338ef
-    } else {
f338ef
-        pt_pool = &pool_list->pools[pool->power_of_two - POOL_SMALLEST];
f338ef
-    }
f338ef
+    pt_pool = &pool_list->pools[mem_pool->pool->power_of_two - POOL_SMALLEST];
f338ef
 
f338ef
     (void)pthread_spin_lock(&pool_list->lock);
f338ef
 
f338ef
@@ -770,8 +781,7 @@ mem_get_from_pool(struct mem_pool *mem_pool, struct mem_pool_shared *pool)
f338ef
         } else {
f338ef
             (void)pthread_spin_unlock(&pool_list->lock);
f338ef
             GF_ATOMIC_INC(pt_pool->parent->allocs_stdc);
f338ef
-            retval = malloc((1 << pt_pool->parent->power_of_two) +
f338ef
-                            sizeof(pooled_obj_hdr_t));
f338ef
+            retval = malloc(1 << pt_pool->parent->power_of_two);
f338ef
 #ifdef DEBUG
f338ef
             hit = _gf_false;
f338ef
 #endif
f338ef
@@ -779,19 +789,14 @@ mem_get_from_pool(struct mem_pool *mem_pool, struct mem_pool_shared *pool)
f338ef
     }
f338ef
 
f338ef
     if (retval != NULL) {
f338ef
-        if (mem_pool) {
f338ef
-            retval->pool = mem_pool;
f338ef
-            retval->power_of_two = mem_pool->pool->power_of_two;
f338ef
+        retval->pool = mem_pool;
f338ef
+        retval->power_of_two = mem_pool->pool->power_of_two;
f338ef
 #ifdef DEBUG
f338ef
-            if (hit == _gf_true)
f338ef
-                GF_ATOMIC_INC(mem_pool->hit);
f338ef
-            else
f338ef
-                GF_ATOMIC_INC(mem_pool->miss);
f338ef
+        if (hit == _gf_true)
f338ef
+            GF_ATOMIC_INC(mem_pool->hit);
f338ef
+        else
f338ef
+            GF_ATOMIC_INC(mem_pool->miss);
f338ef
 #endif
f338ef
-        } else {
f338ef
-            retval->power_of_two = pool->power_of_two;
f338ef
-            retval->pool = NULL;
f338ef
-        }
f338ef
         retval->magic = GF_MEM_HEADER_MAGIC;
f338ef
         retval->pool_list = pool_list;
f338ef
     }
f338ef
@@ -811,7 +816,7 @@ mem_get(struct mem_pool *mem_pool)
f338ef
 #if defined(GF_DISABLE_MEMPOOL)
f338ef
     return GF_MALLOC(mem_pool->sizeof_type, gf_common_mt_mem_pool);
f338ef
 #else
f338ef
-    pooled_obj_hdr_t *retval = mem_get_from_pool(mem_pool, NULL);
f338ef
+    pooled_obj_hdr_t *retval = mem_get_from_pool(mem_pool);
f338ef
     if (!retval) {
f338ef
         return NULL;
f338ef
     }
f338ef
-- 
f338ef
1.8.3.1
f338ef