14f8ab
From 7b7ec67680415c22773ebb2a5daacf298b6b1e06 Mon Sep 17 00:00:00 2001
14f8ab
From: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
Date: Sat, 13 Feb 2021 18:37:32 +0100
14f8ab
Subject: [PATCH 537/538] cluster/afr: Fix race in lockinfo (f)getxattr
14f8ab
14f8ab
A shared dictionary was updated outside the lock after having updated
14f8ab
the number of remaining answers. This means that one thread may be
14f8ab
processing the last answer and unwinding the request before another
14f8ab
thread completes updating the dict.
14f8ab
14f8ab
    Thread 1                           Thread 2
14f8ab
14f8ab
    LOCK()
14f8ab
    call_cnt-- (=1)
14f8ab
    UNLOCK()
14f8ab
                                       LOCK()
14f8ab
                                       call_cnt-- (=0)
14f8ab
                                       UNLOCK()
14f8ab
                                       update_dict(dict)
14f8ab
                                       if (call_cnt == 0) {
14f8ab
                                           STACK_UNWIND(dict);
14f8ab
                                       }
14f8ab
    update_dict(dict)
14f8ab
    if (call_cnt == 0) {
14f8ab
        STACK_UNWIND(dict);
14f8ab
    }
14f8ab
14f8ab
The updates from thread 1 are lost.
14f8ab
14f8ab
This patch also reduces the work done inside the locked region and
14f8ab
reduces code duplication.
14f8ab
14f8ab
Upstream-patch:
14f8ab
> Upstream-patch-link: https://github.com/gluster/glusterfs/pull/2162
14f8ab
> Fixes: #2161
14f8ab
> Change-Id: Idc0d34ab19ea6031de0641f7b05c624d90fac8fa
14f8ab
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
14f8ab
BUG: 1911292
14f8ab
Change-Id: Idc0d34ab19ea6031de0641f7b05c624d90fac8fa
14f8ab
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
14f8ab
Reviewed-on: https://code.engineering.redhat.com/gerrit/228924
14f8ab
Tested-by: RHGS Build Bot <nigelb@redhat.com>
14f8ab
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
14f8ab
---
14f8ab
 xlators/cluster/afr/src/afr-inode-read.c | 254 ++++++++++++++-----------------
14f8ab
 1 file changed, 112 insertions(+), 142 deletions(-)
14f8ab
14f8ab
diff --git a/xlators/cluster/afr/src/afr-inode-read.c b/xlators/cluster/afr/src/afr-inode-read.c
14f8ab
index cf305af..98e195a 100644
14f8ab
--- a/xlators/cluster/afr/src/afr-inode-read.c
14f8ab
+++ b/xlators/cluster/afr/src/afr-inode-read.c
14f8ab
@@ -15,6 +15,8 @@
14f8ab
 #include <stdlib.h>
14f8ab
 #include <signal.h>
14f8ab
 
14f8ab
+#include <urcu/uatomic.h>
14f8ab
+
14f8ab
 #include <glusterfs/glusterfs.h>
14f8ab
 #include "afr.h"
14f8ab
 #include <glusterfs/dict.h>
14f8ab
@@ -868,188 +870,121 @@ afr_getxattr_quota_size_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
14f8ab
     return 0;
14f8ab
 }
14f8ab
 
14f8ab
-int32_t
14f8ab
-afr_getxattr_lockinfo_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
14f8ab
-                          int32_t op_ret, int32_t op_errno, dict_t *dict,
14f8ab
-                          dict_t *xdata)
14f8ab
+static int32_t
14f8ab
+afr_update_local_dicts(call_frame_t *frame, dict_t *dict, dict_t *xdata)
14f8ab
 {
14f8ab
-    int call_cnt = 0, len = 0;
14f8ab
-    char *lockinfo_buf = NULL;
14f8ab
-    dict_t *lockinfo = NULL, *newdict = NULL;
14f8ab
-    afr_local_t *local = NULL;
14f8ab
+    afr_local_t *local;
14f8ab
+    dict_t *local_dict;
14f8ab
+    dict_t *local_xdata;
14f8ab
+    int32_t ret;
14f8ab
 
14f8ab
-    LOCK(&frame->lock);
14f8ab
-    {
14f8ab
-        local = frame->local;
14f8ab
+    local = frame->local;
14f8ab
+    local_dict = NULL;
14f8ab
+    local_xdata = NULL;
14f8ab
 
14f8ab
-        call_cnt = --local->call_count;
14f8ab
+    ret = -ENOMEM;
14f8ab
 
14f8ab
-        if ((op_ret < 0) || (!dict && !xdata)) {
14f8ab
-            goto unlock;
14f8ab
-        }
14f8ab
-
14f8ab
-        if (xdata) {
14f8ab
-            if (!local->xdata_rsp) {
14f8ab
-                local->xdata_rsp = dict_new();
14f8ab
-                if (!local->xdata_rsp) {
14f8ab
-                    local->op_ret = -1;
14f8ab
-                    local->op_errno = ENOMEM;
14f8ab
-                    goto unlock;
14f8ab
-                }
14f8ab
-            }
14f8ab
+    if ((dict != NULL) && (local->dict == NULL)) {
14f8ab
+        local_dict = dict_new();
14f8ab
+        if (local_dict == NULL) {
14f8ab
+            goto done;
14f8ab
         }
14f8ab
+    }
14f8ab
 
14f8ab
-        if (!dict) {
14f8ab
-            goto unlock;
14f8ab
+    if ((xdata != NULL) && (local->xdata_rsp == NULL)) {
14f8ab
+        local_xdata = dict_new();
14f8ab
+        if (local_xdata == NULL) {
14f8ab
+            goto done;
14f8ab
         }
14f8ab
+    }
14f8ab
 
14f8ab
-        op_ret = dict_get_ptr_and_len(dict, GF_XATTR_LOCKINFO_KEY,
14f8ab
-                                      (void **)&lockinfo_buf, &len;;
14f8ab
+    if ((local_dict != NULL) || (local_xdata != NULL)) {
14f8ab
+        /* TODO: Maybe it would be better to preallocate both dicts before
14f8ab
+         *       sending the requests. This way we don't need to use a LOCK()
14f8ab
+         *       here. */
14f8ab
+        LOCK(&frame->lock);
14f8ab
 
14f8ab
-        if (!lockinfo_buf) {
14f8ab
-            goto unlock;
14f8ab
+        if ((local_dict != NULL) && (local->dict == NULL)) {
14f8ab
+            local->dict = local_dict;
14f8ab
+            local_dict = NULL;
14f8ab
         }
14f8ab
 
14f8ab
-        if (!local->dict) {
14f8ab
-            local->dict = dict_new();
14f8ab
-            if (!local->dict) {
14f8ab
-                local->op_ret = -1;
14f8ab
-                local->op_errno = ENOMEM;
14f8ab
-                goto unlock;
14f8ab
-            }
14f8ab
+        if ((local_xdata != NULL) && (local->xdata_rsp == NULL)) {
14f8ab
+            local->xdata_rsp = local_xdata;
14f8ab
+            local_xdata = NULL;
14f8ab
         }
14f8ab
-    }
14f8ab
-unlock:
14f8ab
-    UNLOCK(&frame->lock);
14f8ab
 
14f8ab
-    if (lockinfo_buf != NULL) {
14f8ab
-        lockinfo = dict_new();
14f8ab
-        if (lockinfo == NULL) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            local->op_errno = ENOMEM;
14f8ab
-        } else {
14f8ab
-            op_ret = dict_unserialize(lockinfo_buf, len, &lockinfo);
14f8ab
-
14f8ab
-            if (lockinfo && local->dict) {
14f8ab
-                dict_copy(lockinfo, local->dict);
14f8ab
-            }
14f8ab
-        }
14f8ab
-    }
14f8ab
-
14f8ab
-    if (xdata && local->xdata_rsp) {
14f8ab
-        dict_copy(xdata, local->xdata_rsp);
14f8ab
+        UNLOCK(&frame->lock);
14f8ab
     }
14f8ab
 
14f8ab
-    if (!call_cnt) {
14f8ab
-        newdict = dict_new();
14f8ab
-        if (!newdict) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            local->op_errno = ENOMEM;
14f8ab
-            goto unwind;
14f8ab
+    if (dict != NULL) {
14f8ab
+        if (dict_copy(dict, local->dict) < 0) {
14f8ab
+            goto done;
14f8ab
         }
14f8ab
+    }
14f8ab
 
14f8ab
-        op_ret = dict_allocate_and_serialize(
14f8ab
-            local->dict, (char **)&lockinfo_buf, (unsigned int *)&len;;
14f8ab
-        if (op_ret != 0) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            goto unwind;
14f8ab
+    if (xdata != NULL) {
14f8ab
+        if (dict_copy(xdata, local->xdata_rsp) < 0) {
14f8ab
+            goto done;
14f8ab
         }
14f8ab
+    }
14f8ab
 
14f8ab
-        op_ret = dict_set_dynptr(newdict, GF_XATTR_LOCKINFO_KEY,
14f8ab
-                                 (void *)lockinfo_buf, len);
14f8ab
-        if (op_ret < 0) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            local->op_errno = -op_ret;
14f8ab
-            goto unwind;
14f8ab
-        }
14f8ab
+    ret = 0;
14f8ab
 
14f8ab
-    unwind:
14f8ab
-        AFR_STACK_UNWIND(getxattr, frame, op_ret, op_errno, newdict,
14f8ab
-                         local->xdata_rsp);
14f8ab
+done:
14f8ab
+    if (local_dict != NULL) {
14f8ab
+        dict_unref(local_dict);
14f8ab
     }
14f8ab
 
14f8ab
-    dict_unref(lockinfo);
14f8ab
+    if (local_xdata != NULL) {
14f8ab
+        dict_unref(local_xdata);
14f8ab
+    }
14f8ab
 
14f8ab
-    return 0;
14f8ab
+    return ret;
14f8ab
 }
14f8ab
 
14f8ab
-int32_t
14f8ab
-afr_fgetxattr_lockinfo_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
14f8ab
-                           int32_t op_ret, int32_t op_errno, dict_t *dict,
14f8ab
-                           dict_t *xdata)
14f8ab
+static void
14f8ab
+afr_getxattr_lockinfo_cbk_common(call_frame_t *frame, int32_t op_ret,
14f8ab
+                                 int32_t op_errno, dict_t *dict, dict_t *xdata,
14f8ab
+                                 bool is_fgetxattr)
14f8ab
 {
14f8ab
-    int call_cnt = 0, len = 0;
14f8ab
+    int len = 0;
14f8ab
     char *lockinfo_buf = NULL;
14f8ab
     dict_t *lockinfo = NULL, *newdict = NULL;
14f8ab
     afr_local_t *local = NULL;
14f8ab
 
14f8ab
-    LOCK(&frame->lock);
14f8ab
-    {
14f8ab
-        local = frame->local;
14f8ab
-
14f8ab
-        call_cnt = --local->call_count;
14f8ab
-
14f8ab
-        if ((op_ret < 0) || (!dict && !xdata)) {
14f8ab
-            goto unlock;
14f8ab
-        }
14f8ab
-
14f8ab
-        if (xdata) {
14f8ab
-            if (!local->xdata_rsp) {
14f8ab
-                local->xdata_rsp = dict_new();
14f8ab
-                if (!local->xdata_rsp) {
14f8ab
-                    local->op_ret = -1;
14f8ab
-                    local->op_errno = ENOMEM;
14f8ab
-                    goto unlock;
14f8ab
-                }
14f8ab
-            }
14f8ab
-        }
14f8ab
-
14f8ab
-        if (!dict) {
14f8ab
-            goto unlock;
14f8ab
-        }
14f8ab
+    local = frame->local;
14f8ab
 
14f8ab
+    if ((op_ret >= 0) && (dict != NULL)) {
14f8ab
         op_ret = dict_get_ptr_and_len(dict, GF_XATTR_LOCKINFO_KEY,
14f8ab
                                       (void **)&lockinfo_buf, &len;;
14f8ab
-
14f8ab
-        if (!lockinfo_buf) {
14f8ab
-            goto unlock;
14f8ab
-        }
14f8ab
-
14f8ab
-        if (!local->dict) {
14f8ab
-            local->dict = dict_new();
14f8ab
-            if (!local->dict) {
14f8ab
-                local->op_ret = -1;
14f8ab
-                local->op_errno = ENOMEM;
14f8ab
-                goto unlock;
14f8ab
+        if (lockinfo_buf != NULL) {
14f8ab
+            lockinfo = dict_new();
14f8ab
+            if (lockinfo == NULL) {
14f8ab
+                op_ret = -1;
14f8ab
+            } else {
14f8ab
+                op_ret = dict_unserialize(lockinfo_buf, len, &lockinfo);
14f8ab
             }
14f8ab
         }
14f8ab
     }
14f8ab
-unlock:
14f8ab
-    UNLOCK(&frame->lock);
14f8ab
 
14f8ab
-    if (lockinfo_buf != NULL) {
14f8ab
-        lockinfo = dict_new();
14f8ab
-        if (lockinfo == NULL) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            local->op_errno = ENOMEM;
14f8ab
-        } else {
14f8ab
-            op_ret = dict_unserialize(lockinfo_buf, len, &lockinfo);
14f8ab
-
14f8ab
-            if (lockinfo && local->dict) {
14f8ab
-                dict_copy(lockinfo, local->dict);
14f8ab
-            }
14f8ab
+    if ((op_ret >= 0) && ((lockinfo != NULL) || (xdata != NULL))) {
14f8ab
+        op_ret = afr_update_local_dicts(frame, lockinfo, xdata);
14f8ab
+        if (lockinfo != NULL) {
14f8ab
+            dict_unref(lockinfo);
14f8ab
         }
14f8ab
     }
14f8ab
 
14f8ab
-    if (xdata && local->xdata_rsp) {
14f8ab
-        dict_copy(xdata, local->xdata_rsp);
14f8ab
+    if (op_ret < 0) {
14f8ab
+        local->op_ret = -1;
14f8ab
+        local->op_errno = ENOMEM;
14f8ab
     }
14f8ab
 
14f8ab
-    if (!call_cnt) {
14f8ab
+    if (uatomic_sub_return(&local->call_count, 1) == 0) {
14f8ab
         newdict = dict_new();
14f8ab
         if (!newdict) {
14f8ab
             local->op_ret = -1;
14f8ab
-            local->op_errno = ENOMEM;
14f8ab
+            local->op_errno = op_errno = ENOMEM;
14f8ab
             goto unwind;
14f8ab
         }
14f8ab
 
14f8ab
@@ -1057,23 +992,58 @@ unlock:
14f8ab
             local->dict, (char **)&lockinfo_buf, (unsigned int *)&len;;
14f8ab
         if (op_ret != 0) {
14f8ab
             local->op_ret = -1;
14f8ab
+            local->op_errno = op_errno = ENOMEM;
14f8ab
             goto unwind;
14f8ab
         }
14f8ab
 
14f8ab
         op_ret = dict_set_dynptr(newdict, GF_XATTR_LOCKINFO_KEY,
14f8ab
                                  (void *)lockinfo_buf, len);
14f8ab
         if (op_ret < 0) {
14f8ab
-            local->op_ret = -1;
14f8ab
-            local->op_errno = -op_ret;
14f8ab
+            GF_FREE(lockinfo_buf);
14f8ab
+            local->op_ret = op_ret = -1;
14f8ab
+            local->op_errno = op_errno = -op_ret;
14f8ab
             goto unwind;
14f8ab
         }
14f8ab
 
14f8ab
     unwind:
14f8ab
-        AFR_STACK_UNWIND(fgetxattr, frame, op_ret, op_errno, newdict,
14f8ab
-                         local->xdata_rsp);
14f8ab
+        /* TODO: These unwinds use op_ret and op_errno instead of local->op_ret
14f8ab
+         *       and local->op_errno. This doesn't seem right because any
14f8ab
+         *       failure during processing of each answer could be silently
14f8ab
+         *       ignored. This is kept this was the old behavior and because
14f8ab
+         *       local->op_ret is initialized as -1 and local->op_errno is
14f8ab
+         *       initialized as EUCLEAN, which makes these values useless. */
14f8ab
+        if (is_fgetxattr) {
14f8ab
+            AFR_STACK_UNWIND(fgetxattr, frame, op_ret, op_errno, newdict,
14f8ab
+                             local->xdata_rsp);
14f8ab
+        } else {
14f8ab
+            AFR_STACK_UNWIND(getxattr, frame, op_ret, op_errno, newdict,
14f8ab
+                             local->xdata_rsp);
14f8ab
+        }
14f8ab
+
14f8ab
+        if (newdict != NULL) {
14f8ab
+            dict_unref(newdict);
14f8ab
+        }
14f8ab
     }
14f8ab
+}
14f8ab
+
14f8ab
+static int32_t
14f8ab
+afr_getxattr_lockinfo_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
14f8ab
+                          int32_t op_ret, int32_t op_errno, dict_t *dict,
14f8ab
+                          dict_t *xdata)
14f8ab
+{
14f8ab
+    afr_getxattr_lockinfo_cbk_common(frame, op_ret, op_errno, dict, xdata,
14f8ab
+                                     false);
14f8ab
 
14f8ab
-    dict_unref(lockinfo);
14f8ab
+    return 0;
14f8ab
+}
14f8ab
+
14f8ab
+static int32_t
14f8ab
+afr_fgetxattr_lockinfo_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
14f8ab
+                           int32_t op_ret, int32_t op_errno, dict_t *dict,
14f8ab
+                           dict_t *xdata)
14f8ab
+{
14f8ab
+    afr_getxattr_lockinfo_cbk_common(frame, op_ret, op_errno, dict, xdata,
14f8ab
+                                     true);
14f8ab
 
14f8ab
     return 0;
14f8ab
 }
14f8ab
-- 
14f8ab
1.8.3.1
14f8ab