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