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