9ae3f9
From 7b2f1bd4e5a57ea3abd5f14a7d81b120735faecd Mon Sep 17 00:00:00 2001
9ae3f9
From: Barak Sason Rofman <bsasonro@redhat.com>
9ae3f9
Date: Wed, 6 May 2020 13:28:40 +0300
9ae3f9
Subject: [PATCH 438/449] dht - sparse files rebalance enhancements
9ae3f9
9ae3f9
Currently data migration in rebalance reads sparse file sequentially,
9ae3f9
disregarding which segments are holes and which are data. This can lead
9ae3f9
to extremely long migration time for large sparse file.
9ae3f9
Data migration mechanism needs to be enhanced so only data segments are
9ae3f9
read and migrated. This can be achieved using lseek to seek for holes
9ae3f9
and data in the file.
9ae3f9
This enhancement is a consequence of
9ae3f9
https://bugzilla.redhat.com/show_bug.cgi?id=1823703
9ae3f9
9ae3f9
> fixes: #1222
9ae3f9
> Change-Id: If5f448a0c532926464e1f34f504c5c94749b08c3
9ae3f9
> Signed-off-by: Barak Sason Rofman <bsasonro@redhat.com>
9ae3f9
> (Cherry pick from commit 7b7559733ca0c25c63f9d56cb7f4650dbd694c40)
9ae3f9
> (Reviewed on upstream link https://review.gluster.org/#/c/glusterfs/+/24409/)
9ae3f9
9ae3f9
BUG: 1836099
9ae3f9
Change-Id: If5f448a0c532926464e1f34f504c5c94749b08c3
9ae3f9
Signed-off-by: Mohit Agrawal <moagrawa@redhat.com>
9ae3f9
Reviewed-on: https://code.engineering.redhat.com/gerrit/202647
9ae3f9
Reviewed-by: Barak Sason Rofman <bsasonro@redhat.com>
9ae3f9
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
9ae3f9
---
9ae3f9
 tests/basic/distribute/spare_file_rebalance.t |  51 ++++++++
9ae3f9
 xlators/cluster/dht/src/dht-rebalance.c       | 172 ++++++++++++--------------
9ae3f9
 2 files changed, 130 insertions(+), 93 deletions(-)
9ae3f9
 create mode 100644 tests/basic/distribute/spare_file_rebalance.t
9ae3f9
9ae3f9
diff --git a/tests/basic/distribute/spare_file_rebalance.t b/tests/basic/distribute/spare_file_rebalance.t
9ae3f9
new file mode 100644
9ae3f9
index 0000000..061c02f
9ae3f9
--- /dev/null
9ae3f9
+++ b/tests/basic/distribute/spare_file_rebalance.t
9ae3f9
@@ -0,0 +1,51 @@
9ae3f9
+#!/bin/bash
9ae3f9
+
9ae3f9
+. $(dirname $0)/../../include.rc
9ae3f9
+. $(dirname $0)/../../volume.rc
9ae3f9
+. $(dirname $0)/../../dht.rc
9ae3f9
+
9ae3f9
+# Initialize
9ae3f9
+#------------------------------------------------------------
9ae3f9
+cleanup;
9ae3f9
+
9ae3f9
+# Start glusterd
9ae3f9
+TEST glusterd;
9ae3f9
+TEST pidof glusterd;
9ae3f9
+TEST $CLI volume info;
9ae3f9
+
9ae3f9
+# Create a volume
9ae3f9
+TEST $CLI volume create $V0 $H0:$B0/${V0}{1,2};
9ae3f9
+
9ae3f9
+# Verify volume creation
9ae3f9
+EXPECT "$V0" volinfo_field $V0 'Volume Name';
9ae3f9
+EXPECT 'Created' volinfo_field $V0 'Status';
9ae3f9
+
9ae3f9
+# Start volume and verify successful start
9ae3f9
+TEST $CLI volume start $V0;
9ae3f9
+EXPECT 'Started' volinfo_field $V0 'Status';
9ae3f9
+TEST glusterfs --volfile-id=$V0 --volfile-server=$H0 --entry-timeout=0 $M0;
9ae3f9
+
9ae3f9
+#------------------------------------------------------------
9ae3f9
+
9ae3f9
+# Test case - Create sparse files on MP and verify
9ae3f9
+# file info after rebalance
9ae3f9
+#------------------------------------------------------------
9ae3f9
+
9ae3f9
+# Create some sparse files and get their size
9ae3f9
+TEST cd $M0;
9ae3f9
+dd if=/dev/urandom of=sparse_file bs=10k count=1 seek=2M
9ae3f9
+cp --sparse=always sparse_file sparse_file_3;
9ae3f9
+
9ae3f9
+# Add a 3rd brick
9ae3f9
+TEST $CLI volume add-brick $V0 $H0:$B0/${V0}3;
9ae3f9
+
9ae3f9
+# Trigger rebalance
9ae3f9
+TEST $CLI volume rebalance $V0 start force;
9ae3f9
+EXPECT_WITHIN $REBALANCE_TIMEOUT "0" rebalance_completed;
9ae3f9
+
9ae3f9
+# Compare original and rebalanced files
9ae3f9
+TEST cd $B0/${V0}2
9ae3f9
+TEST cmp sparse_file $B0/${V0}3/sparse_file_3
9ae3f9
+EXPECT_WITHIN 30 "";
9ae3f9
+
9ae3f9
+cleanup;
9ae3f9
diff --git a/xlators/cluster/dht/src/dht-rebalance.c b/xlators/cluster/dht/src/dht-rebalance.c
9ae3f9
index 88b6b54..d0c21b4 100644
9ae3f9
--- a/xlators/cluster/dht/src/dht-rebalance.c
9ae3f9
+++ b/xlators/cluster/dht/src/dht-rebalance.c
9ae3f9
@@ -18,8 +18,8 @@
9ae3f9
 #include <glusterfs/events.h>
9ae3f9
 
9ae3f9
 #define GF_DISK_SECTOR_SIZE 512
9ae3f9
-#define DHT_REBALANCE_PID 4242              /* Change it if required */
9ae3f9
-#define DHT_REBALANCE_BLKSIZE (1024 * 1024) /* 1 MB */
9ae3f9
+#define DHT_REBALANCE_PID 4242        /* Change it if required */
9ae3f9
+#define DHT_REBALANCE_BLKSIZE 1048576 /* 1 MB */
9ae3f9
 #define MAX_MIGRATE_QUEUE_COUNT 500
9ae3f9
 #define MIN_MIGRATE_QUEUE_COUNT 200
9ae3f9
 #define MAX_REBAL_TYPE_SIZE 16
9ae3f9
@@ -178,75 +178,6 @@ dht_strip_out_acls(dict_t *dict)
9ae3f9
     }
9ae3f9
 }
9ae3f9
 
9ae3f9
-static int
9ae3f9
-dht_write_with_holes(xlator_t *to, fd_t *fd, struct iovec *vec, int count,
9ae3f9
-                     int32_t size, off_t offset, struct iobref *iobref,
9ae3f9
-                     int *fop_errno)
9ae3f9
-{
9ae3f9
-    int i = 0;
9ae3f9
-    int ret = -1;
9ae3f9
-    int start_idx = 0;
9ae3f9
-    int tmp_offset = 0;
9ae3f9
-    int write_needed = 0;
9ae3f9
-    int buf_len = 0;
9ae3f9
-    int size_pending = 0;
9ae3f9
-    char *buf = NULL;
9ae3f9
-
9ae3f9
-    /* loop through each vector */
9ae3f9
-    for (i = 0; i < count; i++) {
9ae3f9
-        buf = vec[i].iov_base;
9ae3f9
-        buf_len = vec[i].iov_len;
9ae3f9
-
9ae3f9
-        for (start_idx = 0; (start_idx + GF_DISK_SECTOR_SIZE) <= buf_len;
9ae3f9
-             start_idx += GF_DISK_SECTOR_SIZE) {
9ae3f9
-            if (mem_0filled(buf + start_idx, GF_DISK_SECTOR_SIZE) != 0) {
9ae3f9
-                write_needed = 1;
9ae3f9
-                continue;
9ae3f9
-            }
9ae3f9
-
9ae3f9
-            if (write_needed) {
9ae3f9
-                ret = syncop_write(
9ae3f9
-                    to, fd, (buf + tmp_offset), (start_idx - tmp_offset),
9ae3f9
-                    (offset + tmp_offset), iobref, 0, NULL, NULL);
9ae3f9
-                /* 'path' will be logged in calling function */
9ae3f9
-                if (ret < 0) {
9ae3f9
-                    gf_log(THIS->name, GF_LOG_WARNING, "failed to write (%s)",
9ae3f9
-                           strerror(-ret));
9ae3f9
-                    *fop_errno = -ret;
9ae3f9
-                    ret = -1;
9ae3f9
-                    goto out;
9ae3f9
-                }
9ae3f9
-
9ae3f9
-                write_needed = 0;
9ae3f9
-            }
9ae3f9
-            tmp_offset = start_idx + GF_DISK_SECTOR_SIZE;
9ae3f9
-        }
9ae3f9
-
9ae3f9
-        if ((start_idx < buf_len) || write_needed) {
9ae3f9
-            /* This means, last chunk is not yet written.. write it */
9ae3f9
-            ret = syncop_write(to, fd, (buf + tmp_offset),
9ae3f9
-                               (buf_len - tmp_offset), (offset + tmp_offset),
9ae3f9
-                               iobref, 0, NULL, NULL);
9ae3f9
-            if (ret < 0) {
9ae3f9
-                /* 'path' will be logged in calling function */
9ae3f9
-                gf_log(THIS->name, GF_LOG_WARNING, "failed to write (%s)",
9ae3f9
-                       strerror(-ret));
9ae3f9
-                *fop_errno = -ret;
9ae3f9
-                ret = -1;
9ae3f9
-                goto out;
9ae3f9
-            }
9ae3f9
-        }
9ae3f9
-
9ae3f9
-        size_pending = (size - buf_len);
9ae3f9
-        if (!size_pending)
9ae3f9
-            break;
9ae3f9
-    }
9ae3f9
-
9ae3f9
-    ret = size;
9ae3f9
-out:
9ae3f9
-    return ret;
9ae3f9
-}
9ae3f9
-
9ae3f9
 /*
9ae3f9
    return values:
9ae3f9
    -1 : failure
9ae3f9
@@ -1101,32 +1032,97 @@ __dht_rebalance_migrate_data(xlator_t *this, gf_defrag_info_t *defrag,
9ae3f9
     int ret = 0;
9ae3f9
     int count = 0;
9ae3f9
     off_t offset = 0;
9ae3f9
+    off_t data_offset = 0;
9ae3f9
+    off_t hole_offset = 0;
9ae3f9
     struct iovec *vector = NULL;
9ae3f9
     struct iobref *iobref = NULL;
9ae3f9
     uint64_t total = 0;
9ae3f9
     size_t read_size = 0;
9ae3f9
+    size_t data_block_size = 0;
9ae3f9
     dict_t *xdata = NULL;
9ae3f9
     dht_conf_t *conf = NULL;
9ae3f9
 
9ae3f9
     conf = this->private;
9ae3f9
+
9ae3f9
     /* if file size is '0', no need to enter this loop */
9ae3f9
     while (total < ia_size) {
9ae3f9
-        read_size = (((ia_size - total) > DHT_REBALANCE_BLKSIZE)
9ae3f9
-                         ? DHT_REBALANCE_BLKSIZE
9ae3f9
-                         : (ia_size - total));
9ae3f9
+        /* This is a regular file - read it sequentially */
9ae3f9
+        if (!hole_exists) {
9ae3f9
+            read_size = (((ia_size - total) > DHT_REBALANCE_BLKSIZE)
9ae3f9
+                             ? DHT_REBALANCE_BLKSIZE
9ae3f9
+                             : (ia_size - total));
9ae3f9
+        } else {
9ae3f9
+            /* This is a sparse file - read only the data segments in the file
9ae3f9
+             */
9ae3f9
+
9ae3f9
+            /* If the previous data block is fully copied, find the next data
9ae3f9
+             * segment
9ae3f9
+             * starting at the offset of the last read and written byte,  */
9ae3f9
+            if (data_block_size <= 0) {
9ae3f9
+                ret = syncop_seek(from, src, offset, GF_SEEK_DATA, NULL,
9ae3f9
+                                  &data_offset);
9ae3f9
+                if (ret) {
9ae3f9
+                    if (ret == -ENXIO)
9ae3f9
+                        ret = 0; /* No more data segments */
9ae3f9
+                    else
9ae3f9
+                        *fop_errno = -ret; /* Error occurred */
9ae3f9
+
9ae3f9
+                    break;
9ae3f9
+                }
9ae3f9
+
9ae3f9
+                /* If the position of the current data segment is greater than
9ae3f9
+                 * the position of the next hole, find the next hole in order to
9ae3f9
+                 * calculate the length of the new data segment */
9ae3f9
+                if (data_offset > hole_offset) {
9ae3f9
+                    /* Starting at the offset of the last data segment, find the
9ae3f9
+                     * next hole */
9ae3f9
+                    ret = syncop_seek(from, src, data_offset, GF_SEEK_HOLE,
9ae3f9
+                                      NULL, &hole_offset);
9ae3f9
+                    if (ret) {
9ae3f9
+                        /* If an error occurred here it's a real error because
9ae3f9
+                         * if the seek for a data segment was successful then
9ae3f9
+                         * necessarily another hole must exist (EOF is a hole)
9ae3f9
+                         */
9ae3f9
+                        *fop_errno = -ret;
9ae3f9
+                        break;
9ae3f9
+                    }
9ae3f9
+
9ae3f9
+                    /* Calculate the total size of the current data block */
9ae3f9
+                    data_block_size = hole_offset - data_offset;
9ae3f9
+                }
9ae3f9
+            } else {
9ae3f9
+                /* There is still data in the current segment, move the
9ae3f9
+                 * data_offset to the position of the last written byte */
9ae3f9
+                data_offset = offset;
9ae3f9
+            }
9ae3f9
+
9ae3f9
+            /* Calculate how much data needs to be read and written. If the data
9ae3f9
+             * segment's length is bigger than DHT_REBALANCE_BLKSIZE, read and
9ae3f9
+             * write DHT_REBALANCE_BLKSIZE data length and the rest in the
9ae3f9
+             * next iteration(s) */
9ae3f9
+            read_size = ((data_block_size > DHT_REBALANCE_BLKSIZE)
9ae3f9
+                             ? DHT_REBALANCE_BLKSIZE
9ae3f9
+                             : data_block_size);
9ae3f9
+
9ae3f9
+            /* Calculate the remaining size of the data block - maybe there's no
9ae3f9
+             * need to seek for data in the next iteration */
9ae3f9
+            data_block_size -= read_size;
9ae3f9
+
9ae3f9
+            /* Set offset to the offset of the data segment so read and write
9ae3f9
+             * will have the correct position */
9ae3f9
+            offset = data_offset;
9ae3f9
+        }
9ae3f9
 
9ae3f9
         ret = syncop_readv(from, src, read_size, offset, 0, &vector, &count,
9ae3f9
                            &iobref, NULL, NULL, NULL);
9ae3f9
+
9ae3f9
         if (!ret || (ret < 0)) {
9ae3f9
             *fop_errno = -ret;
9ae3f9
             break;
9ae3f9
         }
9ae3f9
 
9ae3f9
-        if (hole_exists) {
9ae3f9
-            ret = dht_write_with_holes(to, dst, vector, count, ret, offset,
9ae3f9
-                                       iobref, fop_errno);
9ae3f9
-        } else {
9ae3f9
-            if (!conf->force_migration && !dht_is_tier_xlator(this)) {
9ae3f9
+        if (!conf->force_migration && !dht_is_tier_xlator(this)) {
9ae3f9
+            if (!xdata) {
9ae3f9
                 xdata = dict_new();
9ae3f9
                 if (!xdata) {
9ae3f9
                     gf_msg("dht", GF_LOG_ERROR, 0, DHT_MSG_MIGRATE_FILE_FAILED,
9ae3f9
@@ -1146,7 +1142,7 @@ __dht_rebalance_migrate_data(xlator_t *this, gf_defrag_info_t *defrag,
9ae3f9
                  * https://github.com/gluster/glusterfs/issues/308
9ae3f9
                  * for more details.
9ae3f9
                  */
9ae3f9
-                ret = dict_set_int32(xdata, GF_AVOID_OVERWRITE, 1);
9ae3f9
+                ret = dict_set_int32_sizen(xdata, GF_AVOID_OVERWRITE, 1);
9ae3f9
                 if (ret) {
9ae3f9
                     gf_msg("dht", GF_LOG_ERROR, 0, ENOMEM,
9ae3f9
                            "failed to set dict");
9ae3f9
@@ -1155,22 +1151,12 @@ __dht_rebalance_migrate_data(xlator_t *this, gf_defrag_info_t *defrag,
9ae3f9
                     break;
9ae3f9
                 }
9ae3f9
             }
9ae3f9
-
9ae3f9
-            ret = syncop_writev(to, dst, vector, count, offset, iobref, 0, NULL,
9ae3f9
-                                NULL, xdata, NULL);
9ae3f9
-            if (ret < 0) {
9ae3f9
-                *fop_errno = -ret;
9ae3f9
-            }
9ae3f9
-        }
9ae3f9
-
9ae3f9
-        if ((defrag && defrag->cmd == GF_DEFRAG_CMD_START_TIER) &&
9ae3f9
-            (gf_defrag_get_pause_state(&defrag->tier_conf) != TIER_RUNNING)) {
9ae3f9
-            gf_msg("tier", GF_LOG_INFO, 0, DHT_MSG_TIER_PAUSED,
9ae3f9
-                   "Migrate file paused");
9ae3f9
-            ret = -1;
9ae3f9
         }
9ae3f9
 
9ae3f9
+        ret = syncop_writev(to, dst, vector, count, offset, iobref, 0, NULL,
9ae3f9
+                            NULL, xdata, NULL);
9ae3f9
         if (ret < 0) {
9ae3f9
+            *fop_errno = -ret;
9ae3f9
             break;
9ae3f9
         }
9ae3f9
 
9ae3f9
-- 
9ae3f9
1.8.3.1
9ae3f9