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