e7a346
From acfde85dc1e44f37432ee80619ed28cfe4df280b Mon Sep 17 00:00:00 2001
e7a346
From: Sanoj Unnikrishnan <sunnikri@redhat.com>
e7a346
Date: Wed, 4 Apr 2018 14:36:56 +0530
e7a346
Subject: [PATCH 219/236] Quota: heal directory on newly added bricks when
e7a346
 quota limit is reached
e7a346
e7a346
Problem: if a lookup is done on a newly added brick for a path on which limit
e7a346
has been reached, the lookup fails to heal the directory tree due to quota.
e7a346
e7a346
Solution: Tag the lookup as an internal fop and ignore it in quota.
e7a346
Since marking internal fop does not usually give enough contextual information.
e7a346
Introducing new flags to pass the contextual info.
e7a346
e7a346
Adding dict_check_flag and dict_set_flag to aid flag operations.
e7a346
A flag is a single bit in a bit array (currently limited to 256 bits).
e7a346
e7a346
Upstream Reference:
e7a346
> Change-Id: Ifb6a68bcaffedd425dd0f01f7db24edd5394c095
e7a346
> fixes: bz#1505355
e7a346
> BUG: 1505355
e7a346
> Signed-off-by: Sanoj Unnikrishnan <sunnikri@redhat.com>
e7a346
> Patch : https://review.gluster.org/#/c/18554/
e7a346
e7a346
BUG: 1475779
e7a346
Change-Id: Ifb6a68bcaffedd425dd0f01f7db24edd5394c095
e7a346
Signed-off-by: Sanoj Unnikrishnan <sunnikri@redhat.com>
e7a346
Reviewed-on: https://code.engineering.redhat.com/gerrit/134506
e7a346
Tested-by: RHGS Build Bot <nigelb@redhat.com>
e7a346
Reviewed-by: Hari Gowtham Gopal <hgowtham@redhat.com>
e7a346
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
e7a346
---
e7a346
 libglusterfs/src/dict.c                     | 190 ++++++++++++++++++++++++++++
e7a346
 libglusterfs/src/dict.h                     |   8 ++
e7a346
 libglusterfs/src/glusterfs.h                |  27 ++++
e7a346
 xlators/cluster/dht/src/dht-selfheal.c      |  17 ++-
e7a346
 xlators/features/quota/src/quota-messages.h |  19 ++-
e7a346
 xlators/features/quota/src/quota.c          |  31 ++++-
e7a346
 xlators/storage/posix/src/posix-helpers.c   |   4 +
e7a346
 7 files changed, 292 insertions(+), 4 deletions(-)
e7a346
e7a346
diff --git a/libglusterfs/src/dict.c b/libglusterfs/src/dict.c
e7a346
index 243c929..ebcf694 100644
e7a346
--- a/libglusterfs/src/dict.c
e7a346
+++ b/libglusterfs/src/dict.c
e7a346
@@ -2020,6 +2020,196 @@ err:
e7a346
         return ret;
e7a346
 }
e7a346
 
e7a346
+/*
e7a346
+ * dict_check_flag can be used to check a one bit flag in an array of flags
e7a346
+ * The flag argument indicates the bit position (within the array of bits).
e7a346
+ * Currently limited to max of 256 flags for a key.
e7a346
+ * return value,
e7a346
+ * 1 : flag is set
e7a346
+ * 0 : flag is not set
e7a346
+ * <0: Error
e7a346
+ */
e7a346
+int
e7a346
+dict_check_flag (dict_t *this, char *key, int flag)
e7a346
+{
e7a346
+        data_t  *data = NULL;
e7a346
+        int     ret = -ENOENT;
e7a346
+
e7a346
+        ret = dict_get_with_ref (this, key, &data);
e7a346
+        if (ret < 0) {
e7a346
+                return ret;
e7a346
+        }
e7a346
+
e7a346
+        if (BIT_VALUE((unsigned char *)(data->data), flag))
e7a346
+                ret = 1;
e7a346
+        else
e7a346
+                ret = 0;
e7a346
+
e7a346
+        data_unref(data);
e7a346
+        return ret;
e7a346
+}
e7a346
+
e7a346
+/*
e7a346
+ * _dict_modify_flag can be used to set/clear a bit flag in an array of flags
e7a346
+ * flag: indicates the bit position. limited to max of DICT_MAX_FLAGS.
e7a346
+ * op: Indicates operation DICT_FLAG_SET / DICT_FLAG_CLEAR
e7a346
+ */
e7a346
+static int
e7a346
+_dict_modify_flag (dict_t *this, char *key, int flag, int op)
e7a346
+{
e7a346
+        data_t          *data           = NULL;
e7a346
+        int             ret             = 0;
e7a346
+        uint32_t        hash            = 0;
e7a346
+        data_pair_t     *pair           = NULL;
e7a346
+        char            *ptr            = NULL;
e7a346
+        int             hashval         = 0;
e7a346
+
e7a346
+        if (!this || !key) {
e7a346
+                gf_msg_callingfn ("dict", GF_LOG_WARNING, EINVAL,
e7a346
+                                  LG_MSG_INVALID_ARG,
e7a346
+                                  "dict OR key (%s) is NULL", key);
e7a346
+                ret = -EINVAL;
e7a346
+                goto err;
e7a346
+        }
e7a346
+
e7a346
+        /*
e7a346
+         * Using a size of 32 bytes to support max of 256
e7a346
+         * flags in a single key. This should be suffcient.
e7a346
+         */
e7a346
+        GF_ASSERT(flag >= 0 && flag < DICT_MAX_FLAGS);
e7a346
+
e7a346
+        hash = SuperFastHash (key, strlen (key));
e7a346
+        LOCK (&this->lock);
e7a346
+        {
e7a346
+                pair = dict_lookup_common (this, key, hash);
e7a346
+
e7a346
+                if (pair) {
e7a346
+                        data = pair->value;
e7a346
+                        if (op == DICT_FLAG_SET)
e7a346
+                                BIT_SET((unsigned char *)(data->data), flag);
e7a346
+                        else
e7a346
+                                BIT_CLEAR((unsigned char *)(data->data), flag);
e7a346
+                        ret = 0;
e7a346
+                } else {
e7a346
+                        ptr = GF_CALLOC(1, DICT_MAX_FLAGS / 8,
e7a346
+                                        gf_common_mt_char);
e7a346
+                        if (!ptr) {
e7a346
+                                gf_msg("dict", GF_LOG_ERROR, ENOMEM,
e7a346
+                                       LG_MSG_NO_MEMORY,
e7a346
+                                       "unable to allocate flag bit array");
e7a346
+                                ret = -ENOMEM;
e7a346
+                                goto err;
e7a346
+                        }
e7a346
+
e7a346
+                        data = data_from_dynptr(ptr, DICT_MAX_FLAGS / 8);
e7a346
+
e7a346
+                        if (!data) {
e7a346
+                                gf_msg("dict", GF_LOG_ERROR, ENOMEM,
e7a346
+                                       LG_MSG_NO_MEMORY,
e7a346
+                                       "unable to allocate data");
e7a346
+                                GF_FREE(ptr);
e7a346
+                                ret = -ENOMEM;
e7a346
+                                goto err;
e7a346
+                        }
e7a346
+
e7a346
+                        if (op == DICT_FLAG_SET)
e7a346
+                                BIT_SET((unsigned char *)(data->data), flag);
e7a346
+                        else
e7a346
+                                BIT_CLEAR((unsigned char *)(data->data), flag);
e7a346
+
e7a346
+                        if (this->free_pair_in_use) {
e7a346
+                                pair = mem_get0 (THIS->ctx->dict_pair_pool);
e7a346
+                                if (!pair) {
e7a346
+                                        gf_msg("dict", GF_LOG_ERROR, ENOMEM,
e7a346
+                                               LG_MSG_NO_MEMORY,
e7a346
+                                               "unable to allocate dict pair");
e7a346
+                                        ret = -ENOMEM;
e7a346
+                                        goto err;
e7a346
+                                }
e7a346
+                        } else {
e7a346
+                                pair = &this->free_pair;
e7a346
+                                this->free_pair_in_use = _gf_true;
e7a346
+                        }
e7a346
+
e7a346
+                        pair->key = (char *)GF_CALLOC(1, strlen (key) + 1,
e7a346
+                                                      gf_common_mt_char);
e7a346
+                        if (!pair->key) {
e7a346
+                                gf_msg("dict", GF_LOG_ERROR, ENOMEM,
e7a346
+                                       LG_MSG_NO_MEMORY,
e7a346
+                                       "unable to allocate dict pair");
e7a346
+                                ret = -ENOMEM;
e7a346
+                                goto err;
e7a346
+                        }
e7a346
+                        strcpy (pair->key, key);
e7a346
+                        pair->key_hash = hash;
e7a346
+                        pair->value = data_ref (data);
e7a346
+
e7a346
+                        hashval = hash % this->hash_size;
e7a346
+                        pair->hash_next = this->members[hashval];
e7a346
+                        this->members[hashval] = pair;
e7a346
+
e7a346
+                        pair->next = this->members_list;
e7a346
+                        pair->prev = NULL;
e7a346
+                        if (this->members_list)
e7a346
+                                this->members_list->prev = pair;
e7a346
+                        this->members_list = pair;
e7a346
+                        this->count++;
e7a346
+
e7a346
+
e7a346
+                        if (this->max_count < this->count)
e7a346
+                                this->max_count = this->count;
e7a346
+                }
e7a346
+        }
e7a346
+
e7a346
+        UNLOCK (&this->lock);
e7a346
+        return 0;
e7a346
+
e7a346
+err:
e7a346
+        UNLOCK (&this->lock);
e7a346
+        if (pair) {
e7a346
+                if (pair->key)
e7a346
+                        free(pair->key);
e7a346
+
e7a346
+                if (pair == &this->free_pair) {
e7a346
+                        this->free_pair_in_use = _gf_false;
e7a346
+                } else {
e7a346
+                        mem_put (pair);
e7a346
+                }
e7a346
+        }
e7a346
+
e7a346
+        if (data)
e7a346
+                data_destroy(data);
e7a346
+
e7a346
+
e7a346
+        gf_msg("dict", GF_LOG_ERROR, EINVAL,
e7a346
+               LG_MSG_DICT_SET_FAILED,
e7a346
+               "unable to set key (%s) in dict ", key);
e7a346
+
e7a346
+        return ret;
e7a346
+}
e7a346
+
e7a346
+/*
e7a346
+ * Todo:
e7a346
+ * Add below primitives as needed:
e7a346
+ * dict_check_flags(this, key, flag...): variadic function to check
e7a346
+ *                                       multiple flags at a time.
e7a346
+ * dict_set_flags(this, key, flag...): set multiple flags
e7a346
+ * dict_clear_flags(this, key, flag...): reset multiple flags
e7a346
+ */
e7a346
+
e7a346
+int
e7a346
+dict_set_flag (dict_t *this, char *key, int flag)
e7a346
+{
e7a346
+        return _dict_modify_flag (this, key, flag, DICT_FLAG_SET);
e7a346
+}
e7a346
+
e7a346
+int
e7a346
+dict_clear_flag (dict_t *this, char *key, int flag)
e7a346
+{
e7a346
+        return _dict_modify_flag (this, key, flag, DICT_FLAG_CLEAR);
e7a346
+}
e7a346
+
e7a346
+
e7a346
 int
e7a346
 dict_get_double (dict_t *this, char *key, double *val)
e7a346
 {
e7a346
diff --git a/libglusterfs/src/dict.h b/libglusterfs/src/dict.h
e7a346
index b131363..be3b0ad 100644
e7a346
--- a/libglusterfs/src/dict.h
e7a346
+++ b/libglusterfs/src/dict.h
e7a346
@@ -60,6 +60,10 @@ typedef struct _data_pair data_pair_t;
e7a346
                                                                         \
e7a346
         } while (0)
e7a346
 
e7a346
+#define DICT_MAX_FLAGS            256
e7a346
+#define DICT_FLAG_SET          1
e7a346
+#define DICT_FLAG_CLEAR        0
e7a346
+
e7a346
 struct _data {
e7a346
         unsigned char  is_static:1;
e7a346
         unsigned char  is_const:1;
e7a346
@@ -222,6 +226,10 @@ GF_MUST_CHECK int dict_set_uint32 (dict_t *this, char *key, uint32_t val);
e7a346
 GF_MUST_CHECK int dict_get_uint64 (dict_t *this, char *key, uint64_t *val);
e7a346
 GF_MUST_CHECK int dict_set_uint64 (dict_t *this, char *key, uint64_t val);
e7a346
 
e7a346
+GF_MUST_CHECK int dict_check_flag (dict_t *this, char *key, int flag);
e7a346
+GF_MUST_CHECK int dict_set_flag (dict_t *this, char *key, int flag);
e7a346
+GF_MUST_CHECK int dict_clear_flag (dict_t *this, char *key, int flag);
e7a346
+
e7a346
 GF_MUST_CHECK int dict_get_double (dict_t *this, char *key, double *val);
e7a346
 GF_MUST_CHECK int dict_set_double (dict_t *this, char *key, double val);
e7a346
 
e7a346
diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h
e7a346
index 5d5f5c8..b161bf0 100644
e7a346
--- a/libglusterfs/src/glusterfs.h
e7a346
+++ b/libglusterfs/src/glusterfs.h
e7a346
@@ -156,6 +156,33 @@
e7a346
 #define GLUSTERFS_VERSION_XCHG_KEY     "glusterfs.version.xchg"
e7a346
 
e7a346
 #define GLUSTERFS_INTERNAL_FOP_KEY  "glusterfs-internal-fop"
e7a346
+
e7a346
+/* GlusterFS Internal FOP Indicator flags
e7a346
+ * (To pass information on the context in which a paritcular
e7a346
+ *  fop is performed between translators)
e7a346
+ * The presence of a particular flag must be treated as an
e7a346
+ * indicator of the context, however the flag is added only in
e7a346
+ * a scenario where there is a need for such context across translators.
e7a346
+ * So it cannot be an absolute information on context.
e7a346
+ */
e7a346
+#define GF_INTERNAL_CTX_KEY  "glusterfs.internal-ctx"
e7a346
+
e7a346
+/*
e7a346
+ * Always append entries to end of the enum, do not delete entries.
e7a346
+ * Currently dict_set_flag allows to set upto 256 flag, if the enum
e7a346
+ * needs to grow beyond this dict_set_flag has to be changed accordingly
e7a346
+ */
e7a346
+enum gf_internal_fop_indicator {
e7a346
+        GF_DHT_HEAL_DIR       /* Index 0 in bit array*/
e7a346
+};
e7a346
+
e7a346
+/* Todo:
e7a346
+ * Add GF_FOP_LINK_FILE         0x2ULL
e7a346
+ * address GLUSTERFS_MARKER_DONT_ACCOUNT_KEY and
e7a346
+ * GLUSTERFS_INTERNAL_FOP_KEY with this flag
e7a346
+ */
e7a346
+
e7a346
+
e7a346
 #define DHT_CHANGELOG_RENAME_OP_KEY   "changelog.rename-op"
e7a346
 
e7a346
 #define ZR_FILE_CONTENT_STR     "glusterfs.file."
e7a346
diff --git a/xlators/cluster/dht/src/dht-selfheal.c b/xlators/cluster/dht/src/dht-selfheal.c
e7a346
index c2c4034..7b192d3 100644
e7a346
--- a/xlators/cluster/dht/src/dht-selfheal.c
e7a346
+++ b/xlators/cluster/dht/src/dht-selfheal.c
e7a346
@@ -1404,10 +1404,25 @@ dht_selfheal_dir_mkdir_lookup_done (call_frame_t *frame, xlator_t *this)
e7a346
         dht_dir_set_heal_xattr (this, local, dict, local->xattr, NULL,
e7a346
                                 NULL);
e7a346
 
e7a346
-        if (!dict)
e7a346
+        if (!dict) {
e7a346
                 gf_msg (this->name, GF_LOG_WARNING, 0,
e7a346
                         DHT_MSG_DICT_SET_FAILED,
e7a346
                         "dict is NULL, need to make sure gfids are same");
e7a346
+                dict = dict_new ();
e7a346
+                if (!dict)
e7a346
+                        return -1;
e7a346
+        }
e7a346
+        ret = dict_set_flag (dict, GF_INTERNAL_CTX_KEY, GF_DHT_HEAL_DIR);
e7a346
+        if (ret) {
e7a346
+                gf_msg (this->name, GF_LOG_ERROR, 0,
e7a346
+                        DHT_MSG_DICT_SET_FAILED,
e7a346
+                        "Failed to set dictionary value for"
e7a346
+                        " key = %s at path: %s",
e7a346
+                        GF_INTERNAL_CTX_KEY, loc->path);
e7a346
+                /* We can still continue. As heal can still happen
e7a346
+                 * unless quota limits have reached for the dir.
e7a346
+                 */
e7a346
+        }
e7a346
 
e7a346
         cnt = layout->cnt;
e7a346
         for (i = 0; i < cnt; i++) {
e7a346
diff --git a/xlators/features/quota/src/quota-messages.h b/xlators/features/quota/src/quota-messages.h
e7a346
index b01fe98..7478b20 100644
e7a346
--- a/xlators/features/quota/src/quota-messages.h
e7a346
+++ b/xlators/features/quota/src/quota-messages.h
e7a346
@@ -46,7 +46,7 @@
e7a346
  */
e7a346
 
e7a346
 #define GLFS_QUOTA_BASE          GLFS_MSGID_COMP_QUOTA
e7a346
-#define GLFS_NUM_MESSAGES        23
e7a346
+#define GLFS_NUM_MESSAGES        25
e7a346
 #define GLFS_MSGID_END     (GLFS_QUOTA_BASE + GLFS_NUM_MESSAGES + 1)
e7a346
 /* Messaged with message IDs */
e7a346
 #define glfs_msg_start_x GLFS_QUOTA_BASE, "Invalid: Start of messages"
e7a346
@@ -240,6 +240,23 @@
e7a346
 
e7a346
 #define Q_MSG_RPC_SUBMIT_FAILED     (GLFS_QUOTA_BASE + 23)
e7a346
 
e7a346
+/*!
e7a346
+ * @messageid 120024
e7a346
+ * @diagnosis
e7a346
+ * @recommendedaction
e7a346
+ */
e7a346
+
e7a346
+#define Q_MSG_ENFORCEMENT_SKIPPED     (GLFS_QUOTA_BASE + 24)
e7a346
+
e7a346
+/*!
e7a346
+ * @messageid 120025
e7a346
+ * @diagnosis
e7a346
+ * @recommendedaction
e7a346
+ */
e7a346
+
e7a346
+#define Q_MSG_INTERNAL_FOP_KEY_MISSING    (GLFS_QUOTA_BASE + 25)
e7a346
+
e7a346
+
e7a346
 /*------------*/
e7a346
 #define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages"
e7a346
 
e7a346
diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c
e7a346
index a307845..af7b65a 100644
e7a346
--- a/xlators/features/quota/src/quota.c
e7a346
+++ b/xlators/features/quota/src/quota.c
e7a346
@@ -1591,6 +1591,28 @@ out:
e7a346
         return ret;
e7a346
 }
e7a346
 
e7a346
+/*
e7a346
+ * return _gf_true if enforcement is needed and _gf_false otherwise
e7a346
+ */
e7a346
+gf_boolean_t
e7a346
+should_quota_enforce (xlator_t *this, dict_t *dict, glusterfs_fop_t fop)
e7a346
+{
e7a346
+        int               ret = 0;
e7a346
+
e7a346
+        ret = dict_check_flag(dict, GF_INTERNAL_CTX_KEY, GF_DHT_HEAL_DIR);
e7a346
+
e7a346
+        if (fop == GF_FOP_MKDIR && ret == DICT_FLAG_SET) {
e7a346
+                return _gf_false;
e7a346
+        } else if (ret == -ENOENT) {
e7a346
+                gf_msg (this->name, GF_LOG_DEBUG, EINVAL,
e7a346
+			Q_MSG_INTERNAL_FOP_KEY_MISSING,
e7a346
+                        "No internal fop context present");
e7a346
+                goto out;
e7a346
+        }
e7a346
+out:
e7a346
+        return _gf_true;
e7a346
+}
e7a346
+
e7a346
 int32_t
e7a346
 quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
e7a346
                   int32_t op_ret, int32_t op_errno, inode_t *inode,
e7a346
@@ -1965,7 +1987,6 @@ unwind:
e7a346
         return 0;
e7a346
 }
e7a346
 
e7a346
-
e7a346
 int32_t
e7a346
 quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
e7a346
              mode_t umask, dict_t *xdata)
e7a346
@@ -1976,9 +1997,15 @@ quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
e7a346
         call_stub_t   *stub  = NULL;
e7a346
 
e7a346
         priv = this->private;
e7a346
-
e7a346
         WIND_IF_QUOTAOFF (priv->is_quota_on, off);
e7a346
 
e7a346
+        if (!should_quota_enforce(this, xdata, GF_FOP_MKDIR)) {
e7a346
+                gf_msg (this->name, GF_LOG_DEBUG, 0,
e7a346
+			Q_MSG_ENFORCEMENT_SKIPPED,
e7a346
+                        "Enforcement has been skipped(internal fop).");
e7a346
+                goto off;
e7a346
+        }
e7a346
+
e7a346
         local = quota_local_new ();
e7a346
         if (local == NULL) {
e7a346
                 op_errno = ENOMEM;
e7a346
diff --git a/xlators/storage/posix/src/posix-helpers.c b/xlators/storage/posix/src/posix-helpers.c
e7a346
index ba1d8c3..4107265 100644
e7a346
--- a/xlators/storage/posix/src/posix-helpers.c
e7a346
+++ b/xlators/storage/posix/src/posix-helpers.c
e7a346
@@ -1200,6 +1200,10 @@ posix_handle_pair (xlator_t *this, const char *real_path,
e7a346
         } else if (!strncmp(key, POSIX_ACL_ACCESS_XATTR, strlen(key))
e7a346
                    && stbuf && IS_DHT_LINKFILE_MODE (stbuf)) {
e7a346
                 goto out;
e7a346
+        } else if (!strncmp(key, GF_INTERNAL_CTX_KEY, strlen(key))) {
e7a346
+                /* ignore this key value pair */
e7a346
+                ret = 0;
e7a346
+                goto out;
e7a346
         } else {
e7a346
                 sys_ret = sys_lsetxattr (real_path, key, value->data,
e7a346
                                          value->len, flags);
e7a346
-- 
e7a346
1.8.3.1
e7a346