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