summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvmallika <vmallika@redhat.com>2015-10-07 15:24:46 +0530
committerRaghavendra G <rgowdapp@redhat.com>2015-10-12 03:14:16 -0700
commitd4bd690adae7ce69594c3322d0d7a8e3cb4f7303 (patch)
tree519312560db82aed9e8c2c1dc8a50aeeeef97e0a
parentfdff192b918ca9cd237f3f784c627102377e3661 (diff)
quota/marker: dir_count accounting is not atomic
Consider below scenario: Quota enabled on pre-existing data Now quota-crawl process will start healing xattrs Now if write is performed where healing is not complete, there is a possibility that 'update txn' is started before 'create xattr txn', in this case dir count can be missed on a dir where quota size xattr is not yet created. Solution is to get size xattr and if xattr is missing, add 1 for dir_count, this requires one additional fop if done in marker during each update iteration Better solution is to us xattrop GF_XATTROP_ADD_ARRAY64_WITH_DEFAULT Change-Id: Idc8978860a3914e70c98f96effeff52e9a24e6ba BUG: 1243798 Signed-off-by: vmallika <vmallika@redhat.com> Reviewed-on: http://review.gluster.org/11694 Tested-by: NetBSD Build System <jenkins@build.gluster.org> Reviewed-by: Raghavendra G <rgowdapp@redhat.com>
-rw-r--r--tests/bugs/quota/bug-1243798.t50
-rw-r--r--xlators/features/marker/src/marker-quota.c170
2 files changed, 148 insertions, 72 deletions
diff --git a/tests/bugs/quota/bug-1243798.t b/tests/bugs/quota/bug-1243798.t
new file mode 100644
index 00000000000..dc53c6928ec
--- /dev/null
+++ b/tests/bugs/quota/bug-1243798.t
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+. $(dirname $0)/../../include.rc
+. $(dirname $0)/../../volume.rc
+. $(dirname $0)/../../nfs.rc
+
+cleanup;
+
+TEST glusterd
+TEST pidof glusterd
+TEST $CLI volume info;
+
+TEST $CLI volume create $V0 $H0:$B0/$V0
+EXPECT 'Created' volinfo_field $V0 'Status';
+
+TEST $CLI volume start $V0;
+EXPECT 'Started' volinfo_field $V0 'Status';
+
+EXPECT_WITHIN $NFS_EXPORT_TIMEOUT "1" is_nfs_export_available;
+TEST mount_nfs $H0:/$V0 $N0 noac,nolock
+
+TEST mkdir -p $N0/dir1/dir2
+TEST touch $N0/dir1/dir2/file
+
+TEST $CLI volume quota $V0 enable
+TEST $CLI volume quota $V0 hard-timeout 0
+TEST $CLI volume quota $V0 soft-timeout 0
+TEST $CLI volume quota $V0 limit-objects /dir1 10
+
+TEST stat $N0/dir1/dir2/file
+
+sleep 2
+
+#Remove size and contri xattr from /dir1
+#Remove contri xattr from /dir1/dir2
+setfattr -x trusted.glusterfs.quota.size $B0/$V0/dir1
+setfattr -x trusted.glusterfs.quota.00000000-0000-0000-0000-000000000001.contri $B0/$V0/dir1
+contri=$(getfattr -d -m . -e hex $B0/$V0/dir1/dir2 | grep contri | awk -F= '{print $1}')
+setfattr -x $contri $B0/$V0/dir1/dir2
+
+#Initiate healing by writing to a file
+echo Hello > $N0/dir1/dir2/file
+
+EXPECT_WITHIN $MARKER_UPDATE_TIMEOUT "2" quota_object_list_field "/dir1" 5
+
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $N0
+TEST $CLI volume stop $V0
+EXPECT "1" get_aux
+
+cleanup;
diff --git a/xlators/features/marker/src/marker-quota.c b/xlators/features/marker/src/marker-quota.c
index 5a0b7332feb..77b0021196c 100644
--- a/xlators/features/marker/src/marker-quota.c
+++ b/xlators/features/marker/src/marker-quota.c
@@ -164,6 +164,15 @@ out:
return -1;
}
+/* This function should be used only in inspect_directory and inspect_file
+ * function to heal quota xattrs.
+ * Inode quota feature is introduced in 3.7.
+ * If gluster setup is upgraded from 3.6 to 3.7, there can be a
+ * getxattr and setxattr spikes with quota heal as inode quota is missing.
+ * So this wrapper function is to avoid xattrs spikes during upgrade.
+ * This function returns success even is inode-quota xattrs are missing and
+ * hence no healing performed.
+ */
int32_t
_quota_dict_get_meta (xlator_t *this, dict_t *dict, char *key,
quota_meta_t *meta, ia_type_t ia_type,
@@ -196,6 +205,34 @@ _quota_dict_get_meta (xlator_t *this, dict_t *dict, char *key,
return ret;
}
+int32_t
+quota_dict_set_size_meta (dict_t *dict, const quota_meta_t *meta)
+{
+ int32_t ret = -ENOMEM;
+ quota_meta_t *value = NULL;
+
+ value = GF_CALLOC (2, sizeof (quota_meta_t), gf_common_quota_meta_t);
+ if (value == NULL) {
+ goto out;
+ }
+ value[0].size = hton64 (meta->size);
+ value[0].file_count = hton64 (meta->file_count);
+ value[0].dir_count = hton64 (meta->dir_count);
+
+ value[1].size = 0;
+ value[1].file_count = 0;
+ value[1].dir_count = hton64 (1);
+
+ ret = dict_set_bin (dict, QUOTA_SIZE_KEY, value,
+ (sizeof (quota_meta_t) * 2));
+ if (ret < 0) {
+ gf_log_callingfn ("quota", GF_LOG_ERROR, "dict set failed");
+ GF_FREE (value);
+ }
+out:
+ return ret;
+}
+
void
mq_compute_delta (quota_meta_t *delta, const quota_meta_t *op1,
const quota_meta_t *op2)
@@ -239,8 +276,8 @@ quota_meta_is_null (const quota_meta_t *meta)
}
int32_t
-mq_are_xattrs_set (xlator_t *this, loc_t *loc, gf_boolean_t *result,
- gf_boolean_t *objects)
+mq_are_xattrs_set (xlator_t *this, loc_t *loc, gf_boolean_t *contri_set,
+ gf_boolean_t *size_set)
{
int32_t ret = -1;
char contri_key[CONTRI_KEY_MAX] = {0, };
@@ -268,33 +305,25 @@ mq_are_xattrs_set (xlator_t *this, loc_t *loc, gf_boolean_t *result,
goto out;
}
- if (rsp_dict == NULL) {
- *result = _gf_false;
+ if (rsp_dict == NULL)
goto out;
- }
- *result = _gf_true;
+ *contri_set = _gf_true;
+ *size_set = _gf_true;
if (loc->inode->ia_type == IA_IFDIR) {
- ret = _quota_dict_get_meta (this, rsp_dict, QUOTA_SIZE_KEY,
- &meta, IA_IFDIR, _gf_false);
- if (ret < 0 || meta.dir_count == 0) {
- ret = 0;
- *result = _gf_false;
- goto out;
- }
- *objects = _gf_true;
+ ret = quota_dict_get_inode_meta (rsp_dict, QUOTA_SIZE_KEY,
+ &meta);
+ if (ret < 0 || meta.dir_count == 0)
+ *size_set = _gf_false;
}
if (!loc_is_root(loc)) {
- ret = _quota_dict_get_meta (this, rsp_dict, contri_key,
- &meta, IA_IFREG, _gf_false);
- if (ret < 0) {
- ret = 0;
- *result = _gf_false;
- goto out;
- }
+ ret = quota_dict_get_inode_meta (rsp_dict, contri_key, &meta);
+ if (ret < 0)
+ *contri_set = _gf_false;
}
+ ret = 0;
out:
if (dict)
dict_unref (dict);
@@ -306,19 +335,20 @@ out:
}
int32_t
-mq_create_xattrs (xlator_t *this, quota_inode_ctx_t *ctx, loc_t *loc,
- gf_boolean_t objects)
+mq_create_size_xattrs (xlator_t *this, quota_inode_ctx_t *ctx, loc_t *loc)
{
- quota_meta_t size = {0, };
- quota_meta_t contri = {0, };
int32_t ret = -1;
- char key[CONTRI_KEY_MAX] = {0, };
+ quota_meta_t size = {0, };
dict_t *dict = NULL;
- inode_contribution_t *contribution = NULL;
GF_VALIDATE_OR_GOTO ("marker", loc, out);
GF_VALIDATE_OR_GOTO ("marker", loc->inode, out);
+ if (loc->inode->ia_type != IA_IFDIR) {
+ ret = 0;
+ goto out;
+ }
+
dict = dict_new ();
if (!dict) {
gf_log (this->name, GF_LOG_ERROR, "dict_new failed");
@@ -326,33 +356,13 @@ mq_create_xattrs (xlator_t *this, quota_inode_ctx_t *ctx, loc_t *loc,
goto out;
}
- if (loc->inode->ia_type == IA_IFDIR) {
- if (objects == _gf_false) {
- /* Initial object count of a directory is 1 */
- size.dir_count = 1;
- }
- ret = quota_dict_set_meta (dict, QUOTA_SIZE_KEY, &size,
- IA_IFDIR);
- if (ret < 0)
- goto out;
- }
-
- if (!loc_is_root (loc)) {
- contribution = mq_add_new_contribution_node (this, ctx, loc);
- if (contribution == NULL) {
- ret = -1;
- goto out;
- }
-
- GET_CONTRI_KEY (key, contribution->gfid, ret);
- ret = quota_dict_set_meta (dict, key, &contri,
- loc->inode->ia_type);
- if (ret < 0)
- goto out;
- }
+ ret = quota_dict_set_size_meta (dict, &size);
+ if (ret < 0)
+ goto out;
- ret = syncop_xattrop (FIRST_CHILD(this), loc, GF_XATTROP_ADD_ARRAY64,
- dict, NULL, NULL);
+ ret = syncop_xattrop (FIRST_CHILD(this), loc,
+ GF_XATTROP_ADD_ARRAY64_WITH_DEFAULT, dict, NULL,
+ NULL);
if (ret < 0) {
gf_log_callingfn (this->name, (-ret == ENOENT || -ret == ESTALE)
@@ -365,9 +375,6 @@ out:
if (dict)
dict_unref (dict);
- if (contribution)
- GF_REF_PUT (contribution);
-
return ret;
}
@@ -877,13 +884,13 @@ mq_update_size (xlator_t *this, loc_t *loc, quota_meta_t *delta)
goto out;
}
- ret = quota_dict_set_meta (dict, QUOTA_SIZE_KEY, delta,
- loc->inode->ia_type);
+ ret = quota_dict_set_size_meta (dict, delta);
if (ret < 0)
goto out;
- ret = syncop_xattrop(FIRST_CHILD(this), loc, GF_XATTROP_ADD_ARRAY64,
- dict, NULL, NULL);
+ ret = syncop_xattrop(FIRST_CHILD(this), loc,
+ GF_XATTROP_ADD_ARRAY64_WITH_DEFAULT, dict, NULL,
+ NULL);
if (ret < 0) {
gf_log_callingfn (this->name, (-ret == ENOENT || -ret == ESTALE)
? GF_LOG_DEBUG:GF_LOG_ERROR, "xattrop failed "
@@ -895,7 +902,10 @@ mq_update_size (xlator_t *this, loc_t *loc, quota_meta_t *delta)
{
ctx->size += delta->size;
ctx->file_count += delta->file_count;
- ctx->dir_count += delta->dir_count;
+ if (ctx->dir_count == 0)
+ ctx->dir_count += delta->dir_count + 1;
+ else
+ ctx->dir_count += delta->dir_count;
}
UNLOCK (&ctx->lock);
@@ -1036,8 +1046,8 @@ mq_create_xattrs_task (void *opaque)
{
int32_t ret = -1;
gf_boolean_t locked = _gf_false;
- gf_boolean_t xattrs_set = _gf_false;
- gf_boolean_t objects = _gf_false;
+ gf_boolean_t contri_set = _gf_false;
+ gf_boolean_t size_set = _gf_false;
gf_boolean_t need_txn = _gf_false;
quota_synctask_t *args = NULL;
quota_inode_ctx_t *ctx = NULL;
@@ -1067,16 +1077,18 @@ mq_create_xattrs_task (void *opaque)
locked = _gf_true;
}
- ret = mq_are_xattrs_set (this, loc, &xattrs_set, &objects);
- if (ret < 0 || xattrs_set)
+ ret = mq_are_xattrs_set (this, loc, &contri_set, &size_set);
+ if (ret < 0 || (contri_set && size_set))
goto out;
mq_set_ctx_create_status (ctx, _gf_false);
status = _gf_true;
- ret = mq_create_xattrs (this, ctx, loc, objects);
- if (ret < 0)
- goto out;
+ if (loc->inode->ia_type == IA_IFDIR && size_set == _gf_false) {
+ ret = mq_create_size_xattrs (this, ctx, loc);
+ if (ret < 0)
+ goto out;
+ }
need_txn = _gf_true;
out:
@@ -1096,10 +1108,11 @@ static int
_mq_create_xattrs_txn (xlator_t *this, loc_t *origin_loc, struct iatt *buf,
gf_boolean_t spawn)
{
- int32_t ret = -1;
- quota_inode_ctx_t *ctx = NULL;
- gf_boolean_t status = _gf_true;
- loc_t loc = {0, };
+ int32_t ret = -1;
+ quota_inode_ctx_t *ctx = NULL;
+ gf_boolean_t status = _gf_true;
+ loc_t loc = {0, };
+ inode_contribution_t *contribution = NULL;
ret = mq_prevalidate_txn (this, origin_loc, &loc, &ctx, buf);
if (ret < 0)
@@ -1109,6 +1122,19 @@ _mq_create_xattrs_txn (xlator_t *this, loc_t *origin_loc, struct iatt *buf,
if (ret < 0 || status == _gf_true)
goto out;
+ if (!loc_is_root(&loc)) {
+ contribution = mq_add_new_contribution_node (this, ctx, &loc);
+ if (contribution == NULL) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "cannot add a new contribution node "
+ "(%s)", uuid_utoa (loc.gfid));
+ ret = -1;
+ goto out;
+ } else {
+ GF_REF_PUT (contribution);
+ }
+ }
+
ret = mq_synctask (this, mq_create_xattrs_task, spawn, &loc);
out:
if (ret < 0 && status == _gf_false)