14f8ab
From f305ee93ec9dbbd679e1eb58c7c0bf8d9b5659d5 Mon Sep 17 00:00:00 2001
14f8ab
From: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
Date: Fri, 12 Apr 2019 13:40:59 +0200
14f8ab
Subject: [PATCH 129/141] core: handle memory accounting correctly
14f8ab
14f8ab
When a translator stops, memory accounting for that translator is not
14f8ab
destroyed (because there could remain memory allocated that references
14f8ab
it), but mutexes that coordinate updates of memory accounting were
14f8ab
destroyed. This caused incorrect memory accounting and even crashes in
14f8ab
debug mode.
14f8ab
14f8ab
This patch also fixes some other things:
14f8ab
14f8ab
* Reduce the number of atomic operations needed to manage memory
14f8ab
  accounting.
14f8ab
* Correctly account memory when realloc() is used.
14f8ab
* Merge two critical sections into one.
14f8ab
* Cleaned the code a bit.
14f8ab
14f8ab
Upstream patch:
14f8ab
> Change-Id: Id5eaee7338729b9bc52c931815ca3ff1e5a7dcc8
14f8ab
> Upstream patch link : https://review.gluster.org/#/c/glusterfs/+/22554/
14f8ab
> BUG: 1659334
14f8ab
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
14f8ab
Change-Id: Id5eaee7338729b9bc52c931815ca3ff1e5a7dcc8
14f8ab
Fixes: bz#1702270
14f8ab
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
Reviewed-on: https://code.engineering.redhat.com/gerrit/169325
14f8ab
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
14f8ab
Tested-by: RHGS Build Bot <nigelb@redhat.com>
14f8ab
---
14f8ab
 libglusterfs/src/glusterfs/xlator.h |   2 +
14f8ab
 libglusterfs/src/libglusterfs.sym   |   1 +
14f8ab
 libglusterfs/src/mem-pool.c         | 193 ++++++++++++++++--------------------
14f8ab
 libglusterfs/src/xlator.c           |  23 +++--
14f8ab
 4 files changed, 105 insertions(+), 114 deletions(-)
14f8ab
14f8ab
diff --git a/libglusterfs/src/glusterfs/xlator.h b/libglusterfs/src/glusterfs/xlator.h
14f8ab
index 06152ec..8998976 100644
14f8ab
--- a/libglusterfs/src/glusterfs/xlator.h
14f8ab
+++ b/libglusterfs/src/glusterfs/xlator.h
14f8ab
@@ -1035,6 +1035,8 @@ gf_boolean_t
14f8ab
 loc_is_nameless(loc_t *loc);
14f8ab
 int
14f8ab
 xlator_mem_acct_init(xlator_t *xl, int num_types);
14f8ab
+void
14f8ab
+xlator_mem_acct_unref(struct mem_acct *mem_acct);
14f8ab
 int
14f8ab
 is_gf_log_command(xlator_t *trans, const char *name, char *value);
14f8ab
 int
14f8ab
diff --git a/libglusterfs/src/libglusterfs.sym b/libglusterfs/src/libglusterfs.sym
14f8ab
index fa2025e..cf5757c 100644
14f8ab
--- a/libglusterfs/src/libglusterfs.sym
14f8ab
+++ b/libglusterfs/src/libglusterfs.sym
14f8ab
@@ -1093,6 +1093,7 @@ xlator_foreach
14f8ab
 xlator_foreach_depth_first
14f8ab
 xlator_init
14f8ab
 xlator_mem_acct_init
14f8ab
+xlator_mem_acct_unref
14f8ab
 xlator_notify
14f8ab
 xlator_option_info_list
14f8ab
 xlator_option_init_bool
14f8ab
diff --git a/libglusterfs/src/mem-pool.c b/libglusterfs/src/mem-pool.c
14f8ab
index 34cb87a..3934a78 100644
14f8ab
--- a/libglusterfs/src/mem-pool.c
14f8ab
+++ b/libglusterfs/src/mem-pool.c
14f8ab
@@ -35,61 +35,92 @@ gf_mem_acct_enable_set(void *data)
14f8ab
     return;
14f8ab
 }
14f8ab
 
14f8ab
-int
14f8ab
-gf_mem_set_acct_info(xlator_t *xl, char **alloc_ptr, size_t size, uint32_t type,
14f8ab
-                     const char *typestr)
14f8ab
+static void *
14f8ab
+gf_mem_header_prepare(struct mem_header *header, size_t size)
14f8ab
 {
14f8ab
-    void *ptr = NULL;
14f8ab
-    struct mem_header *header = NULL;
14f8ab
+    void *ptr;
14f8ab
 
14f8ab
-    if (!alloc_ptr)
14f8ab
-        return -1;
14f8ab
+    header->size = size;
14f8ab
 
14f8ab
-    ptr = *alloc_ptr;
14f8ab
+    ptr = header + 1;
14f8ab
 
14f8ab
-    GF_ASSERT(xl != NULL);
14f8ab
+    /* data follows in this gap of 'size' bytes */
14f8ab
+    *(uint32_t *)(ptr + size) = GF_MEM_TRAILER_MAGIC;
14f8ab
 
14f8ab
-    GF_ASSERT(xl->mem_acct != NULL);
14f8ab
+    return ptr;
14f8ab
+}
14f8ab
 
14f8ab
-    GF_ASSERT(type <= xl->mem_acct->num_types);
14f8ab
+static void *
14f8ab
+gf_mem_set_acct_info(struct mem_acct *mem_acct, struct mem_header *header,
14f8ab
+                     size_t size, uint32_t type, const char *typestr)
14f8ab
+{
14f8ab
+    struct mem_acct_rec *rec = NULL;
14f8ab
+    bool new_ref = false;
14f8ab
 
14f8ab
-    LOCK(&xl->mem_acct->rec[type].lock);
14f8ab
-    {
14f8ab
-        if (!xl->mem_acct->rec[type].typestr)
14f8ab
-            xl->mem_acct->rec[type].typestr = typestr;
14f8ab
-        xl->mem_acct->rec[type].size += size;
14f8ab
-        xl->mem_acct->rec[type].num_allocs++;
14f8ab
-        xl->mem_acct->rec[type].total_allocs++;
14f8ab
-        xl->mem_acct->rec[type].max_size = max(xl->mem_acct->rec[type].max_size,
14f8ab
-                                               xl->mem_acct->rec[type].size);
14f8ab
-        xl->mem_acct->rec[type].max_num_allocs = max(
14f8ab
-            xl->mem_acct->rec[type].max_num_allocs,
14f8ab
-            xl->mem_acct->rec[type].num_allocs);
14f8ab
-    }
14f8ab
-    UNLOCK(&xl->mem_acct->rec[type].lock);
14f8ab
+    if (mem_acct != NULL) {
14f8ab
+        GF_ASSERT(type <= mem_acct->num_types);
14f8ab
 
14f8ab
-    GF_ATOMIC_INC(xl->mem_acct->refcnt);
14f8ab
+        rec = &mem_acct->rec[type];
14f8ab
+        LOCK(&rec->lock);
14f8ab
+        {
14f8ab
+            if (!rec->typestr) {
14f8ab
+                rec->typestr = typestr;
14f8ab
+            }
14f8ab
+            rec->size += size;
14f8ab
+            new_ref = (rec->num_allocs == 0);
14f8ab
+            rec->num_allocs++;
14f8ab
+            rec->total_allocs++;
14f8ab
+            rec->max_size = max(rec->max_size, rec->size);
14f8ab
+            rec->max_num_allocs = max(rec->max_num_allocs, rec->num_allocs);
14f8ab
+
14f8ab
+#ifdef DEBUG
14f8ab
+            list_add(&header->acct_list, &rec->obj_list);
14f8ab
+#endif
14f8ab
+        }
14f8ab
+        UNLOCK(&rec->lock);
14f8ab
+
14f8ab
+        /* We only take a reference for each memory type used, not for each
14f8ab
+         * allocation. This minimizes the use of atomic operations. */
14f8ab
+        if (new_ref) {
14f8ab
+            GF_ATOMIC_INC(mem_acct->refcnt);
14f8ab
+        }
14f8ab
+    }
14f8ab
 
14f8ab
-    header = (struct mem_header *)ptr;
14f8ab
     header->type = type;
14f8ab
-    header->size = size;
14f8ab
-    header->mem_acct = xl->mem_acct;
14f8ab
+    header->mem_acct = mem_acct;
14f8ab
     header->magic = GF_MEM_HEADER_MAGIC;
14f8ab
 
14f8ab
+    return gf_mem_header_prepare(header, size);
14f8ab
+}
14f8ab
+
14f8ab
+static void *
14f8ab
+gf_mem_update_acct_info(struct mem_acct *mem_acct, struct mem_header *header,
14f8ab
+                        size_t size)
14f8ab
+{
14f8ab
+    struct mem_acct_rec *rec = NULL;
14f8ab
+
14f8ab
+    if (mem_acct != NULL) {
14f8ab
+        rec = &mem_acct->rec[header->type];
14f8ab
+        LOCK(&rec->lock);
14f8ab
+        {
14f8ab
+            rec->size += size - header->size;
14f8ab
+            rec->total_allocs++;
14f8ab
+            rec->max_size = max(rec->max_size, rec->size);
14f8ab
+
14f8ab
 #ifdef DEBUG
14f8ab
-    INIT_LIST_HEAD(&header->acct_list);
14f8ab
-    LOCK(&xl->mem_acct->rec[type].lock);
14f8ab
-    {
14f8ab
-        list_add(&header->acct_list, &(xl->mem_acct->rec[type].obj_list));
14f8ab
-    }
14f8ab
-    UNLOCK(&xl->mem_acct->rec[type].lock);
14f8ab
+            /* The old 'header' already was present in 'obj_list', but
14f8ab
+             * realloc() could have changed its address. We need to remove
14f8ab
+             * the old item from the list and add the new one. This can be
14f8ab
+             * done this way because list_move() doesn't use the pointers
14f8ab
+             * to the old location (which are not valid anymore) already
14f8ab
+             * present in the list, it simply overwrites them. */
14f8ab
+            list_move(&header->acct_list, &rec->obj_list);
14f8ab
 #endif
14f8ab
-    ptr += sizeof(struct mem_header);
14f8ab
-    /* data follows in this gap of 'size' bytes */
14f8ab
-    *(uint32_t *)(ptr + size) = GF_MEM_TRAILER_MAGIC;
14f8ab
+        }
14f8ab
+        UNLOCK(&rec->lock);
14f8ab
+    }
14f8ab
 
14f8ab
-    *alloc_ptr = ptr;
14f8ab
-    return 0;
14f8ab
+    return gf_mem_header_prepare(header, size);
14f8ab
 }
14f8ab
 
14f8ab
 void *
14f8ab
@@ -97,7 +128,7 @@ __gf_calloc(size_t nmemb, size_t size, uint32_t type, const char *typestr)
14f8ab
 {
14f8ab
     size_t tot_size = 0;
14f8ab
     size_t req_size = 0;
14f8ab
-    char *ptr = NULL;
14f8ab
+    void *ptr = NULL;
14f8ab
     xlator_t *xl = NULL;
14f8ab
 
14f8ab
     if (!THIS->ctx->mem_acct_enable)
14f8ab
@@ -114,16 +145,15 @@ __gf_calloc(size_t nmemb, size_t size, uint32_t type, const char *typestr)
14f8ab
         gf_msg_nomem("", GF_LOG_ALERT, tot_size);
14f8ab
         return NULL;
14f8ab
     }
14f8ab
-    gf_mem_set_acct_info(xl, &ptr, req_size, type, typestr);
14f8ab
 
14f8ab
-    return (void *)ptr;
14f8ab
+    return gf_mem_set_acct_info(xl->mem_acct, ptr, req_size, type, typestr);
14f8ab
 }
14f8ab
 
14f8ab
 void *
14f8ab
 __gf_malloc(size_t size, uint32_t type, const char *typestr)
14f8ab
 {
14f8ab
     size_t tot_size = 0;
14f8ab
-    char *ptr = NULL;
14f8ab
+    void *ptr = NULL;
14f8ab
     xlator_t *xl = NULL;
14f8ab
 
14f8ab
     if (!THIS->ctx->mem_acct_enable)
14f8ab
@@ -138,84 +168,32 @@ __gf_malloc(size_t size, uint32_t type, const char *typestr)
14f8ab
         gf_msg_nomem("", GF_LOG_ALERT, tot_size);
14f8ab
         return NULL;
14f8ab
     }
14f8ab
-    gf_mem_set_acct_info(xl, &ptr, size, type, typestr);
14f8ab
 
14f8ab
-    return (void *)ptr;
14f8ab
+    return gf_mem_set_acct_info(xl->mem_acct, ptr, size, type, typestr);
14f8ab
 }
14f8ab
 
14f8ab
 void *
14f8ab
 __gf_realloc(void *ptr, size_t size)
14f8ab
 {
14f8ab
     size_t tot_size = 0;
14f8ab
-    char *new_ptr;
14f8ab
-    struct mem_header *old_header = NULL;
14f8ab
-    struct mem_header *new_header = NULL;
14f8ab
-    struct mem_header tmp_header;
14f8ab
+    struct mem_header *header = NULL;
14f8ab
 
14f8ab
     if (!THIS->ctx->mem_acct_enable)
14f8ab
         return REALLOC(ptr, size);
14f8ab
 
14f8ab
     REQUIRE(NULL != ptr);
14f8ab
 
14f8ab
-    old_header = (struct mem_header *)(ptr - GF_MEM_HEADER_SIZE);
14f8ab
-    GF_ASSERT(old_header->magic == GF_MEM_HEADER_MAGIC);
14f8ab
-    tmp_header = *old_header;
14f8ab
-
14f8ab
-#ifdef DEBUG
14f8ab
-    int type = 0;
14f8ab
-    size_t copy_size = 0;
14f8ab
-
14f8ab
-    /* Making these changes for realloc is not straightforward. So
14f8ab
-     * I am simulating realloc using calloc and free
14f8ab
-     */
14f8ab
-
14f8ab
-    type = tmp_header.type;
14f8ab
-    new_ptr = __gf_calloc(1, size, type,
14f8ab
-                          tmp_header.mem_acct->rec[type].typestr);
14f8ab
-    if (new_ptr) {
14f8ab
-        copy_size = (size > tmp_header.size) ? tmp_header.size : size;
14f8ab
-        memcpy(new_ptr, ptr, copy_size);
14f8ab
-        __gf_free(ptr);
14f8ab
-    }
14f8ab
-
14f8ab
-    /* This is not quite what the man page says should happen */
14f8ab
-    return new_ptr;
14f8ab
-#endif
14f8ab
+    header = (struct mem_header *)(ptr - GF_MEM_HEADER_SIZE);
14f8ab
+    GF_ASSERT(header->magic == GF_MEM_HEADER_MAGIC);
14f8ab
 
14f8ab
     tot_size = size + GF_MEM_HEADER_SIZE + GF_MEM_TRAILER_SIZE;
14f8ab
-    new_ptr = realloc(old_header, tot_size);
14f8ab
-    if (!new_ptr) {
14f8ab
+    header = realloc(header, tot_size);
14f8ab
+    if (!header) {
14f8ab
         gf_msg_nomem("", GF_LOG_ALERT, tot_size);
14f8ab
         return NULL;
14f8ab
     }
14f8ab
 
14f8ab
-    /*
14f8ab
-     * We used to pass (char **)&ptr as the second
14f8ab
-     * argument after the value of realloc was saved
14f8ab
-     * in ptr, but the compiler warnings complained
14f8ab
-     * about the casting to and forth from void ** to
14f8ab
-     * char **.
14f8ab
-     * TBD: it would be nice to adjust the memory accounting info here,
14f8ab
-     * but calling gf_mem_set_acct_info here is wrong because it bumps
14f8ab
-     * up counts as though this is a new allocation - which it's not.
14f8ab
-     * The consequence of doing nothing here is only that the sizes will be
14f8ab
-     * wrong, but at least the counts won't be.
14f8ab
-    uint32_t           type = 0;
14f8ab
-    xlator_t          *xl = NULL;
14f8ab
-    type = header->type;
14f8ab
-    xl = (xlator_t *) header->xlator;
14f8ab
-    gf_mem_set_acct_info (xl, &new_ptr, size, type, NULL);
14f8ab
-     */
14f8ab
-
14f8ab
-    new_header = (struct mem_header *)new_ptr;
14f8ab
-    *new_header = tmp_header;
14f8ab
-    new_header->size = size;
14f8ab
-
14f8ab
-    new_ptr += sizeof(struct mem_header);
14f8ab
-    /* data follows in this gap of 'size' bytes */
14f8ab
-    *(uint32_t *)(new_ptr + size) = GF_MEM_TRAILER_MAGIC;
14f8ab
-
14f8ab
-    return (void *)new_ptr;
14f8ab
+    return gf_mem_update_acct_info(header->mem_acct, header, size);
14f8ab
 }
14f8ab
 
14f8ab
 int
14f8ab
@@ -321,6 +299,7 @@ __gf_free(void *free_ptr)
14f8ab
     void *ptr = NULL;
14f8ab
     struct mem_acct *mem_acct;
14f8ab
     struct mem_header *header = NULL;
14f8ab
+    bool last_ref = false;
14f8ab
 
14f8ab
     if (!THIS->ctx->mem_acct_enable) {
14f8ab
         FREE(free_ptr);
14f8ab
@@ -352,16 +331,18 @@ __gf_free(void *free_ptr)
14f8ab
         mem_acct->rec[header->type].num_allocs--;
14f8ab
         /* If all the instances are freed up then ensure typestr is set
14f8ab
          * to NULL */
14f8ab
-        if (!mem_acct->rec[header->type].num_allocs)
14f8ab
+        if (!mem_acct->rec[header->type].num_allocs) {
14f8ab
+            last_ref = true;
14f8ab
             mem_acct->rec[header->type].typestr = NULL;
14f8ab
+        }
14f8ab
 #ifdef DEBUG
14f8ab
         list_del(&header->acct_list);
14f8ab
 #endif
14f8ab
     }
14f8ab
     UNLOCK(&mem_acct->rec[header->type].lock);
14f8ab
 
14f8ab
-    if (GF_ATOMIC_DEC(mem_acct->refcnt) == 0) {
14f8ab
-        FREE(mem_acct);
14f8ab
+    if (last_ref) {
14f8ab
+        xlator_mem_acct_unref(mem_acct);
14f8ab
     }
14f8ab
 
14f8ab
 free:
14f8ab
diff --git a/libglusterfs/src/xlator.c b/libglusterfs/src/xlator.c
14f8ab
index 5d6f8d2..022c3ed 100644
14f8ab
--- a/libglusterfs/src/xlator.c
14f8ab
+++ b/libglusterfs/src/xlator.c
14f8ab
@@ -736,6 +736,19 @@ xlator_mem_acct_init(xlator_t *xl, int num_types)
14f8ab
 }
14f8ab
 
14f8ab
 void
14f8ab
+xlator_mem_acct_unref(struct mem_acct *mem_acct)
14f8ab
+{
14f8ab
+    uint32_t i;
14f8ab
+
14f8ab
+    if (GF_ATOMIC_DEC(mem_acct->refcnt) == 0) {
14f8ab
+        for (i = 0; i < mem_acct->num_types; i++) {
14f8ab
+            LOCK_DESTROY(&(mem_acct->rec[i].lock));
14f8ab
+        }
14f8ab
+        FREE(mem_acct);
14f8ab
+    }
14f8ab
+}
14f8ab
+
14f8ab
+void
14f8ab
 xlator_tree_fini(xlator_t *xl)
14f8ab
 {
14f8ab
     xlator_t *top = NULL;
14f8ab
@@ -766,7 +779,6 @@ xlator_list_destroy(xlator_list_t *list)
14f8ab
 int
14f8ab
 xlator_memrec_free(xlator_t *xl)
14f8ab
 {
14f8ab
-    uint32_t i = 0;
14f8ab
     struct mem_acct *mem_acct = NULL;
14f8ab
 
14f8ab
     if (!xl) {
14f8ab
@@ -775,13 +787,8 @@ xlator_memrec_free(xlator_t *xl)
14f8ab
     mem_acct = xl->mem_acct;
14f8ab
 
14f8ab
     if (mem_acct) {
14f8ab
-        for (i = 0; i < mem_acct->num_types; i++) {
14f8ab
-            LOCK_DESTROY(&(mem_acct->rec[i].lock));
14f8ab
-        }
14f8ab
-        if (GF_ATOMIC_DEC(mem_acct->refcnt) == 0) {
14f8ab
-            FREE(mem_acct);
14f8ab
-            xl->mem_acct = NULL;
14f8ab
-        }
14f8ab
+        xlator_mem_acct_unref(mem_acct);
14f8ab
+        xl->mem_acct = NULL;
14f8ab
     }
14f8ab
 
14f8ab
     return 0;
14f8ab
-- 
14f8ab
1.8.3.1
14f8ab