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