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