diff options
author | Ravishankar N <ravishankar@redhat.com> | 2015-01-09 14:43:22 +0000 |
---|---|---|
committer | Pranith Kumar Karampuri <pkarampu@redhat.com> | 2015-01-15 01:28:37 -0800 |
commit | 8beaf169e39b262416e2274a028292379d39b310 (patch) | |
tree | e5cfd6da9af293ba7625c057914583a03bbeadab | |
parent | 6da85222e5e49bcb15c4c8998f26c8dffb6a5b34 (diff) |
cluster/afr: split-brain resolution CLI
Extend the AFR heal command to include automated split-brain resolution.
This patch [3/3] is the final patch for afr automated split-brain resolution
implementation.
"gluster volume heal <VOLNAME> [full | statistics [heal-count [replica
<HOSTNAME:BRICKNAME>]] |info [healed | heal-failed | split-brain]| split-brain
{bigger-file <FILE> |source-brick <HOSTNAME:BRICKNAME> [<FILE>]}]"
The new additions being:
1.gluster volume heal <VOLNAME> split-brain bigger-file <FILE>
Locates the replica containing the FILE, selects bigger-file as source and
completes heal.
2.gluster volume heal <VOLNAME> split-brain source-brick <HOSTNAME:BRICKNAME>
<FILE>
Selects <FILE> present in <HOSTNAME:BRICKNAME> as source and completes heal.
3.gluster volume heal <VOLNAME> split-brain <HOSTNAME:BRICKNAME>
Selects all split-brained files in <HOSTNAME:BRICKNAME> as source and completes
heal.
Note: <FILE> can be either the full file name as seen from the root of the
volume (or) the gfid-string representation of the file, which sometimes gets
displayed in the heal info command's output.
Entry/gfid split-brain resolution is not supported.
Example can be found in the test case.
Change-Id: I4649733922d406f14f28ee9033a5cb627b9538b3
BUG: 1136769
Signed-off-by: Ravishankar N <ravishankar@redhat.com>
Reviewed-on: http://review.gluster.org/9377
Reviewed-by: Pranith Kumar Karampuri <pkarampu@redhat.com>
Tested-by: Pranith Kumar Karampuri <pkarampu@redhat.com>
Tested-by: Gluster Build System <jenkins@build.gluster.com>
-rw-r--r-- | cli/src/cli-cmd-parser.c | 91 | ||||
-rw-r--r-- | cli/src/cli-cmd-volume.c | 79 | ||||
-rw-r--r-- | cli/src/cli-rpc-ops.c | 6 | ||||
-rw-r--r-- | heal/src/glfs-heal.c | 416 | ||||
-rw-r--r-- | libglusterfs/src/glusterfs.h | 1 | ||||
-rw-r--r-- | rpc/rpc-lib/src/protocol-common.h | 2 | ||||
-rw-r--r-- | tests/basic/afr/split-brain-healing.t | 183 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-common.c | 76 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-inode-read.c | 5 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-self-heal-common.c | 191 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-self-heal-data.c | 62 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-self-heal-metadata.c | 34 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr-self-heal.h | 18 | ||||
-rw-r--r-- | xlators/cluster/afr/src/afr.h | 4 | ||||
-rw-r--r-- | xlators/cluster/dht/src/dht-common.c | 12 |
15 files changed, 1039 insertions, 141 deletions
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index 28888ba656d..53b14d27708 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -2929,6 +2929,43 @@ out: return ret; } +static int +set_hostname_path_in_dict (const char *token, dict_t *dict, int heal_op) +{ + char *hostname = NULL; + char *path = NULL; + int ret = 0; + + ret = extract_hostname_path_from_token (token, &hostname, &path); + if (ret) + goto out; + + switch (heal_op) { + case GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK: + ret = dict_set_dynstr (dict, "heal-source-hostname", + hostname); + if (ret) + goto out; + ret = dict_set_dynstr (dict, "heal-source-brickpath", + path); + break; + case GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: + ret = dict_set_dynstr (dict, "per-replica-cmd-hostname", + hostname); + if (ret) + goto out; + ret = dict_set_dynstr (dict, "per-replica-cmd-path", + path); + break; + default: + ret = -1; + break; + } + +out: + return ret; + +} int cli_cmd_volume_heal_options_parse (const char **words, int wordcount, @@ -2936,8 +2973,6 @@ cli_cmd_volume_heal_options_parse (const char **words, int wordcount, { int ret = 0; dict_t *dict = NULL; - char *hostname = NULL; - char *path = NULL; dict = dict_new (); if (!dict) @@ -3008,6 +3043,35 @@ cli_cmd_volume_heal_options_parse (const char **words, int wordcount, ret = -1; goto out; } + if (wordcount == 6) { + if (strcmp (words[3], "split-brain")) { + ret = -1; + goto out; + } + if (!strcmp (words[4], "bigger-file")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE); + if (ret) + goto out; + ret = dict_set_str (dict, "file", (char *)words[5]); + if (ret) + goto out; + goto done; + } + if (!strcmp (words[4], "source-brick")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK); + if (ret) + goto out; + ret = set_hostname_path_in_dict (words[5], dict, + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK); + if (ret) + goto out; + goto done; + } + ret = -1; + goto out; + } if (wordcount == 7) { if (!strcmp (words[3], "statistics") && !strcmp (words[4], "heal-count") @@ -3017,21 +3081,26 @@ cli_cmd_volume_heal_options_parse (const char **words, int wordcount, GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; - ret = extract_hostname_path_from_token (words[6], - &hostname, &path); + ret = set_hostname_path_in_dict (words[6], dict, + GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA); if (ret) goto out; - ret = dict_set_dynstr (dict, "per-replica-cmd-hostname", - hostname); + goto done; + + } + if (!strcmp (words[3], "split-brain") && + !strcmp (words[4], "source-brick")) { + ret = dict_set_int32 (dict, "heal-op", + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK); + ret = set_hostname_path_in_dict (words[5], dict, + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK); if (ret) goto out; - ret = dict_set_dynstr (dict, "per-replica-cmd-path", - path); + ret = dict_set_str (dict, "file", + (char *) words[6]); if (ret) goto out; - else - goto done; - + goto done; } } ret = -1; diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index 238c8673d75..501b5776dec 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -1879,6 +1879,60 @@ cli_print_brick_status (cli_volume_status_t *status) return 0; } +#define NEEDS_GLFS_HEAL(op) ((op == GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE) || \ + (op == GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK) || \ + (op == GF_AFR_OP_INDEX_SUMMARY)) + +int +cli_launch_glfs_heal (int heal_op, dict_t *options) +{ + char buff[PATH_MAX] = {0}; + runner_t runner = {0}; + char *filename = NULL; + char *hostname = NULL; + char *path = NULL; + char *volname = NULL; + char *out = NULL; + int ret = 0; + + runinit (&runner); + ret = dict_get_str (options, "volname", &volname); + runner_add_args (&runner, SBIN_DIR"/glfsheal", volname, NULL); + runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); + + switch (heal_op) { + case GF_AFR_OP_INDEX_SUMMARY: + break; + case GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE: + ret = dict_get_str (options, "file", &filename); + runner_add_args (&runner, "bigger-file", filename, NULL); + break; + case GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK: + ret = dict_get_str (options, "heal-source-hostname", + &hostname); + ret = dict_get_str (options, "heal-source-brickpath", + &path); + runner_add_args (&runner, "source-brick", NULL); + runner_argprintf (&runner, "%s:%s", hostname, path); + if (dict_get_str (options, "file", &filename) == 0) + runner_argprintf (&runner, filename); + break; + default: + ret = -1; + } + ret = runner_start (&runner); + if (ret == -1) + goto out; + while ((out = fgets (buff, sizeof(buff), + runner_chio (&runner, STDOUT_FILENO)))) { + printf ("%s", out); + } + ret = runner_end (&runner); + ret = WEXITSTATUS (ret); + +out: + return ret; +} int cli_cmd_volume_heal_cbk (struct cli_state *state, struct cli_cmd_word *word, const char **words, int wordcount) @@ -1892,9 +1946,6 @@ cli_cmd_volume_heal_cbk (struct cli_state *state, struct cli_cmd_word *word, xlator_t *this = NULL; cli_local_t *local = NULL; int heal_op = 0; - runner_t runner = {0}; - char buff[PATH_MAX] = {0}; - char *out = NULL; this = THIS; frame = create_frame (this, this->ctx->pool); @@ -1916,21 +1967,10 @@ cli_cmd_volume_heal_cbk (struct cli_state *state, struct cli_cmd_word *word, ret = dict_get_int32 (options, "heal-op", &heal_op); if (ret < 0) goto out; - - if (heal_op == GF_AFR_OP_INDEX_SUMMARY) { - runinit (&runner); - runner_add_args (&runner, SBIN_DIR"/glfsheal", words[2], NULL); - runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); - ret = runner_start (&runner); + if (NEEDS_GLFS_HEAL (heal_op)) { + ret = cli_launch_glfs_heal (heal_op, options); if (ret == -1) goto out; - while ((out = fgets(buff, sizeof(buff), - runner_chio (&runner, STDOUT_FILENO)))) { - printf ("%s", out); - } - - ret = runner_end (&runner); - ret = WEXITSTATUS (ret); } else { proc = &cli_rpc_prog->proctable[GLUSTER_CLI_HEAL_VOLUME]; @@ -1946,7 +1986,7 @@ out: if (ret) { cli_cmd_sent_status_get (&sent); if ((sent == 0) && (parse_error == 0)) - cli_out ("Volume heal failed"); + cli_out ("Volume heal failed."); } CLI_STACK_DESTROY (frame); @@ -2316,7 +2356,10 @@ struct cli_cmd volume_cmds[] = { cli_cmd_volume_status_cbk, "display status of all or specified volume(s)/brick"}, - { "volume heal <VOLNAME> [{full | statistics {heal-count {replica <hostname:brickname>}} |info {healed | heal-failed | split-brain}}]", + { "volume heal <VOLNAME> [full | statistics [heal-count "\ + "[replica <HOSTNAME:BRICKNAME>]] |info [healed | heal-failed | "\ + "split-brain]| split-brain {bigger-file <FILE> |source-brick "\ + "<HOSTNAME:BRICKNAME> [<FILE>]}]", cli_cmd_volume_heal_cbk, "self-heal commands on volume specified by <VOLNAME>"}, diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 1d8cf23ff42..72ffaf4129a 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -7358,6 +7358,12 @@ gf_cli_heal_volume_cbk (struct rpc_req *req, struct iovec *iov, case GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA: heal_op_str = "count of entries to be healed per replica"; break; + /* The below 2 cases are never hit; they're coded only to make + * compiler warnings go away.*/ + case GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE: + case GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK: + break; + case GF_AFR_OP_INVALID: heal_op_str = "invalid heal op"; break; diff --git a/heal/src/glfs-heal.c b/heal/src/glfs-heal.c index a9baad3ac56..f49f3a58afc 100644 --- a/heal/src/glfs-heal.c +++ b/heal/src/glfs-heal.c @@ -14,11 +14,17 @@ #include "glfs.h" #include "glfs-handles.h" #include "glfs-internal.h" +#include "protocol-common.h" #include "syncop.h" #include <string.h> #include <time.h> #define DEFAULT_HEAL_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" +#define USAGE_STR "Usage: %s <VOLNAME> [bigger-file <FILE> | "\ + "source-brick <HOSTNAME:BRICKNAME> [<FILE>]]\n" + +int glfsh_heal_splitbrain_file (glfs_t *fs, xlator_t *top_subvol, + loc_t *rootloc, char *file, dict_t *xattr_req); int glfsh_link_inode_update_loc (loc_t *loc, struct iatt *iattr) @@ -83,6 +89,37 @@ out: return ret; } +int +glfsh_get_index_dir_fd (xlator_t *xl, loc_t *loc, fd_t **fd) +{ + int ret = -1; + + *fd = fd_create (loc->inode, GF_CLIENT_PID_GLFS_HEAL); + if (!*fd) { + printf ("fd_create failed: %s", strerror(errno)); + goto out; + } + ret = syncop_opendir (xl, loc, *fd); + if (ret) { + fd_unref(*fd); +#ifdef GF_LINUX_HOST_OS /* See comment in afr_shd_index_opendir() */ + *fd = fd_anonymous (loc->inode); + if (!*fd) { + printf ("fd_anonymous failed: %s", + strerror(errno)); + goto out; + } + ret = 0; +#else + printf ("opendir failed: %s", strerror(errno)); + goto out; +#endif + } + +out: + return ret; +} + static xlator_t* _get_afr_ancestor (xlator_t *xl) { @@ -185,6 +222,33 @@ glfsh_print_heal_status (dict_t *dict, char *path, uuid_t gfid, } static int +glfsh_heal_entries (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + gf_dirent_t *entries, uint64_t *offset, + uint64_t *num_entries, dict_t *xattr_req) { + + gf_dirent_t *entry = NULL; + gf_dirent_t *tmp = NULL; + int ret = 0; + char file[64] = {0}; + + list_for_each_entry_safe (entry, tmp, &entries->list, list) { + *offset = entry->d_off; + if ((strcmp (entry->d_name, ".") == 0) || + (strcmp (entry->d_name, "..") == 0)) + continue; + memset (file, 0, sizeof(file)); + snprintf (file, sizeof(file), "gfid:%s", entry->d_name); + ret = glfsh_heal_splitbrain_file (fs, top_subvol, rootloc, file, + xattr_req); + if (ret) + continue; + (*num_entries)++; + } + + return ret; +} + +static int glfsh_process_entries (xlator_t *xl, fd_t *fd, gf_dirent_t *entries, uint64_t *offset, uint64_t *num_entries) { @@ -240,15 +304,21 @@ glfsh_process_entries (xlator_t *xl, fd_t *fd, gf_dirent_t *entries, } static int -glfsh_crawl_directory (xlator_t *readdir_xl, fd_t *fd, loc_t *loc) +glfsh_crawl_directory (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + xlator_t *readdir_xl, fd_t *fd, loc_t *loc, + dict_t *xattr_req) { uint64_t offset = 0; gf_dirent_t entries; int ret = 0; gf_boolean_t free_entries = _gf_false; uint64_t num_entries = 0; + int heal_op = -1; INIT_LIST_HEAD (&entries.list); + ret = dict_get_int32 (xattr_req, "heal-op", &heal_op); + if (ret) + return ret; while (1) { ret = syncop_readdir (readdir_xl, fd, 131072, offset, &entries); @@ -260,11 +330,16 @@ glfsh_crawl_directory (xlator_t *readdir_xl, fd_t *fd, loc_t *loc) if (list_empty (&entries.list)) goto out; - ret = glfsh_process_entries (readdir_xl, fd, &entries, &offset, - &num_entries); - if (ret < 0) - goto out; - + if (heal_op == GF_AFR_OP_INDEX_SUMMARY) { + ret = glfsh_process_entries (readdir_xl, fd, &entries, + &offset, &num_entries); + if (ret < 0) + goto out; + } else if (heal_op == GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK) { + ret = glfsh_heal_entries (fs, top_subvol, rootloc, + &entries, &offset, + &num_entries, xattr_req); + } gf_dirent_free (&entries); free_entries = _gf_false; } @@ -275,9 +350,12 @@ out: if (ret < 0) { printf ("Failed to complete gathering info. " "Number of entries so far: %"PRIu64"\n", num_entries); - } - else { - printf ("Number of entries: %"PRIu64"\n", num_entries); + } else { + if (heal_op == GF_AFR_OP_INDEX_SUMMARY) + printf ("Number of entries: %"PRIu64"\n", num_entries); + else if (heal_op == GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK) + printf ("Number of healed entries: %"PRIu64"\n", + num_entries); } return ret; } @@ -333,13 +411,22 @@ out: } void -glfsh_print_pending_heals (xlator_t *xl, loc_t *rootloc) +glfsh_print_pending_heals (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + xlator_t *xl) { int ret = 0; loc_t dirloc = {0}; fd_t *fd = NULL; int32_t op_errno = 0; + dict_t *xattr_req = NULL; + xattr_req = dict_new(); + if (!xattr_req) + goto out; + + ret = dict_set_int32 (xattr_req, "heal-op", GF_AFR_OP_INDEX_SUMMARY); + if (ret) + goto out; ret = glfsh_print_brick (xl, rootloc); if (ret < 0) { glfsh_print_brick_from_xl (xl); @@ -356,30 +443,16 @@ glfsh_print_pending_heals (xlator_t *xl, loc_t *rootloc) goto out; } - fd = fd_create (dirloc.inode, GF_CLIENT_PID_GLFS_HEAL); - if (!fd) { - printf ("fd_create failed: %s", strerror(errno)); - goto out; - } - ret = syncop_opendir (xl, &dirloc, fd); - if (ret) { - fd_unref(fd); -#ifdef GF_LINUX_HOST_OS /* See comment in afr_shd_index_opendir() */ - fd = fd_anonymous (dirloc.inode); - if (!fd) { - printf ("fd_anonymous failed: %s", - strerror(errno)); - goto out; - } -#else - printf ("opendir failed: %s", strerror(errno)); + ret = glfsh_get_index_dir_fd (xl, &dirloc, &fd); + if (ret) goto out; -#endif - } - ret = glfsh_crawl_directory (xl, fd, &dirloc); + ret = glfsh_crawl_directory (fs, top_subvol, rootloc, xl, fd, &dirloc, + xattr_req); if (fd) fd_unref (fd); + if (xattr_req) + dict_unref (xattr_req); if (ret < 0) printf ("Failed to find entries with pending self-heal\n"); out: @@ -411,6 +484,209 @@ glfsh_validate_replicate_volume (xlator_t *xl) return ret; } +static xlator_t* +_brick_path_to_client_xlator (xlator_t *top_subvol, char *hostname, + char *brickpath) +{ + int ret = 0; + xlator_t *xl = NULL; + char *remote_host = NULL; + char *remote_subvol = NULL; + + xl = top_subvol; + + while (xl->next) + xl = xl->next; + + while (xl) { + if (!strcmp (xl->type, "protocol/client")) { + ret = dict_get_str (xl->options, "remote-host", + &remote_host); + if (ret < 0) + goto out; + ret = dict_get_str (xl->options, + "remote-subvolume", &remote_subvol); + if (ret < 0) + goto out; + if (!strcmp (hostname, remote_host) && + !strcmp (brickpath, remote_subvol)) + return xl; + } + xl = xl->prev; + } + +out: + return NULL; +} + + +int +glfsh_gather_heal_info (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc) +{ + xlator_t *xl = NULL; + xlator_t *afr_xl = NULL; + xlator_t *old_THIS = NULL; + + xl = top_subvol; + while (xl->next) + xl = xl->next; + while (xl) { + if (strcmp (xl->type, "protocol/client") == 0) { + afr_xl = _get_afr_ancestor (xl); + if (afr_xl) + old_THIS = THIS; + THIS = afr_xl; + glfsh_print_pending_heals (fs, top_subvol, + rootloc, xl); + THIS = old_THIS; + printf ("\n"); + } + + xl = xl->prev; + } + + return 0; +} + +int +glfsh_heal_splitbrain_file (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + char *file, dict_t *xattr_req) +{ + int ret = -1; + int reval = 0; + loc_t loc = {0, }; + char *path = NULL; + char *filename = NULL; + struct iatt iatt = {0, }; + xlator_t *xl = top_subvol; + dict_t *xattr_rsp = NULL; + char *sh_fail_msg = NULL; + int32_t op_errno = 0; + + if (!strncmp (file, "gfid:", 5)) { + filename = gf_strdup(file); + path = strtok (filename, ":"); + path = strtok (NULL, ";"); + uuid_parse (path, loc.gfid); + loc.path = gf_strdup (uuid_utoa (loc.gfid)); + loc.inode = inode_new (rootloc->inode->table); + ret = syncop_lookup (xl, &loc, xattr_req, 0, &xattr_rsp, 0); + if (ret) { + op_errno = -ret; + printf ("Lookup failed on %s:%s.\n", file, + strerror(op_errno)); + goto out; + } + } else { + if (file[0] != '/') { + printf ("<FILE> must be absolute path w.r.t. the " + "volume, starting with '/'\n"); + ret = -1; + goto out; + } +retry: + ret = glfs_resolve (fs, xl, file, &loc, &iatt, reval); + ESTALE_RETRY (ret, errno, reval, &loc, retry); + if (ret) { + printf("Lookup failed on %s:%s\n", + file, strerror (errno)); + goto out; + } + } + + ret = syncop_getxattr (xl, &loc, &xattr_rsp, GF_AFR_HEAL_SBRAIN, + xattr_req); + if (ret) { + op_errno = -ret; + printf ("Healing %s failed:%s.\n", file, strerror(op_errno)); + goto out; + } + ret = dict_get_str (xattr_rsp, "sh-fail-msg", &sh_fail_msg); + if (!ret) { + printf ("Healing %s failed: %s.\n", file, sh_fail_msg); + ret = -1; + goto out; + } + printf ("Healed %s.\n", file); + ret = 0; +out: + if (xattr_rsp) + dict_unref (xattr_rsp); + return ret; +} + +int +glfsh_heal_from_brick (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + char *hostname, char *brickpath, char *file) +{ + int ret = -1; + dict_t *xattr_req = NULL; + xlator_t *client = NULL; + fd_t *fd = NULL; + loc_t dirloc = {0}; + int32_t op_errno = 0; + + xattr_req = dict_new(); + if (!xattr_req) + goto out; + ret = dict_set_int32 (xattr_req, "heal-op", + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK); + if (ret) + goto out; + client = _brick_path_to_client_xlator (top_subvol, hostname, brickpath); + if (!client) { + printf("\"%s:%s\"- No such brick available in the volume.\n", + hostname, brickpath); + ret = -1; + goto out; + } + ret = dict_set_str (xattr_req, "child-name", client->name); + if (ret) + goto out; + if (file) + ret = glfsh_heal_splitbrain_file (fs, top_subvol, rootloc, file, + xattr_req); + else { + ret = glfsh_get_index_dir_loc (rootloc, client, &dirloc, + &op_errno); + ret = glfsh_get_index_dir_fd (client, &dirloc, &fd); + if (ret) + goto out; + ret = glfsh_crawl_directory (fs, top_subvol, rootloc, client, + fd, &dirloc, xattr_req); + if (fd) + fd_unref (fd); + } +out: + if (xattr_req) + dict_unref (xattr_req); + loc_wipe (&dirloc); + return ret; +} + +int +glfsh_heal_from_bigger_file (glfs_t *fs, xlator_t *top_subvol, loc_t *rootloc, + char *file) +{ + + int ret = -1; + dict_t *xattr_req = NULL; + + xattr_req = dict_new(); + if (!xattr_req) + goto out; + ret = dict_set_int32 (xattr_req, "heal-op", + GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE); + if (ret) + goto out; + ret = glfsh_heal_splitbrain_file (fs, top_subvol, rootloc, file, + xattr_req); +out: + if (xattr_req) + dict_unref (xattr_req); + return ret; +} + int main (int argc, char **argv) { @@ -418,18 +694,54 @@ main (int argc, char **argv) int ret = 0; char *volname = NULL; xlator_t *top_subvol = NULL; - xlator_t *xl = NULL; loc_t rootloc = {0}; char logfilepath[PATH_MAX] = {0}; - xlator_t *old_THIS = NULL; - xlator_t *afr_xl = NULL; + char *hostname = NULL; + char *path = NULL; + char *file = NULL; + gf_xl_afr_op_t heal_op = -1; - if (argc != 2) { - printf ("Usage: %s <volname>\n", argv[0]); + if (argc < 2) { + printf (USAGE_STR, argv[0]); ret = -1; goto out; } volname = argv[1]; + switch (argc) { + case 2: + heal_op = GF_AFR_OP_INDEX_SUMMARY; + break; + case 4: + if (!strcmp (argv[2], "bigger-file")) { + heal_op = GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE; + file = argv[3]; + } else if (!strcmp (argv[2], "source-brick")) { + heal_op = GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK; + hostname = strtok (argv[3], ":"); + path = strtok (NULL, ":"); + } else { + printf (USAGE_STR, argv[0]); + ret = -1; + goto out; + } + break; + case 5: + if (!strcmp (argv[2], "source-brick")) { + heal_op = GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK; + hostname = strtok (argv[3], ":"); + path = strtok (NULL, ":"); + file = argv[4]; + } else { + printf (USAGE_STR, argv[0]); + ret = -1; + goto out; + } + break; + default: + printf (USAGE_STR, argv[0]); + ret = -1; + goto out; + } fs = glfs_new (volname); if (!fs) { @@ -485,30 +797,28 @@ main (int argc, char **argv) rootloc.inode = inode_ref (top_subvol->itable->root); glfs_loc_touchup (&rootloc); - xl = top_subvol; - while (xl->next) - xl = xl->next; - - while (xl) { - if (strcmp (xl->type, "protocol/client") == 0) { - afr_xl = _get_afr_ancestor (xl); - if (afr_xl) { - old_THIS = THIS; - THIS = afr_xl; - glfsh_print_pending_heals (xl, &rootloc); - THIS = old_THIS; - printf("\n"); - } - } - - xl = xl->prev; + switch (heal_op) { + case GF_AFR_OP_INDEX_SUMMARY: + ret = glfsh_gather_heal_info (fs, top_subvol, &rootloc); + break; + case GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE: + ret = glfsh_heal_from_bigger_file (fs, top_subvol, + &rootloc, file); + break; + case GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK: + ret = glfsh_heal_from_brick (fs, top_subvol, &rootloc, + hostname, path, file); + break; + default: + ret = -1; + break; } loc_wipe (&rootloc); glfs_subvol_done (fs, top_subvol); glfs_fini (fs); - return 0; + return ret; out: if (fs && top_subvol) glfs_subvol_done (fs, top_subvol); diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 4c213f41576..73945e578fe 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -138,6 +138,7 @@ #define GF_XATTROP_INDEX_COUNT "glusterfs.xattrop_index_count" #define GF_AFR_HEAL_INFO "glusterfs.heal-info" +#define GF_AFR_HEAL_SBRAIN "glusterfs.heal-sbrain" #define GF_GFIDLESS_LOOKUP "gfidless-lookup" /* replace-brick and pump related internal xattrs */ diff --git a/rpc/rpc-lib/src/protocol-common.h b/rpc/rpc-lib/src/protocol-common.h index 1fd063aec25..f560c103acd 100644 --- a/rpc/rpc-lib/src/protocol-common.h +++ b/rpc/rpc-lib/src/protocol-common.h @@ -231,6 +231,8 @@ typedef enum { GF_AFR_OP_STATISTICS, GF_AFR_OP_STATISTICS_HEAL_COUNT, GF_AFR_OP_STATISTICS_HEAL_COUNT_PER_REPLICA, + GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE, + GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK, } gf_xl_afr_op_t ; struct gf_gsync_detailed_status_ { diff --git a/tests/basic/afr/split-brain-healing.t b/tests/basic/afr/split-brain-healing.t new file mode 100644 index 00000000000..1dc317df8dd --- /dev/null +++ b/tests/basic/afr/split-brain-healing.t @@ -0,0 +1,183 @@ +#!/bin/bash + +#Test the split-brain resolution CLI commands. +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc + +function get_replicate_subvol_number { + local filename=$1 + #get_backend_paths + if [ -f $B0/${V0}1/$filename ] + then + echo 0 + elif [ -f $B0/${V0}3/$filename ] + then echo 1 + else + echo -1 + fi +} + +cleanup; + +AREQUAL_PATH=$(dirname $0)/../../utils +CFLAGS="" +test "`uname -s`" != "Linux" && { + CFLAGS="$CFLAGS -I$(dirname $0)/../../../contrib/argp-standalone "; + CFLAGS="$CFLAGS -L$(dirname $0)/../../../contrib/argp-standalone -largp "; + CFLAGS="$CFLAGS -lintl"; +} +build_tester $AREQUAL_PATH/arequal-checksum.c $CFLAGS +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/${V0}{1,2,3,4} +TEST $CLI volume set $V0 cluster.self-heal-daemon off +TEST $CLI volume start $V0 +TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 $M0 --attribute-timeout=0 --entry-timeout=0 + +cd $M0 +for i in {1..10} +do + echo "Initial content">>file$i +done + +replica_0_files_list=(`ls $B0/${V0}1`) +replica_1_files_list=(`ls $B0/${V0}3`) + +############ Create data split-brain in the files. ########################### +TEST kill_brick $V0 $H0 $B0/${V0}1 +for file in ${!replica_0_files_list[*]} +do + echo "B1 is down">>${replica_0_files_list[$file]} +done +TEST kill_brick $V0 $H0 $B0/${V0}3 +for file in ${!replica_1_files_list[*]} +do + echo "B3 is down">>${replica_1_files_list[$file]} +done + +SMALLER_FILE_SIZE=$(stat -c %s file1) + +TEST $CLI volume start $V0 force +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 0 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 2 + +TEST kill_brick $V0 $H0 $B0/${V0}2 +for file in ${!replica_0_files_list[*]} +do + echo "B2 is down">>${replica_0_files_list[$file]} + echo "appending more content to make it the bigger file">>${replica_0_files_list[$file]} +done +TEST kill_brick $V0 $H0 $B0/${V0}4 +for file in ${!replica_1_files_list[*]} +do + echo "B4 is down">>${replica_1_files_list[$file]} + echo "appending more content to make it the bigger file">>${replica_1_files_list[$file]} +done + +BIGGER_FILE_SIZE=$(stat -c %s file1) + +TEST $CLI volume start $V0 force +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 3 + + +############### Acessing the files should now give EIO. ############################### +TEST ! cat file1 +TEST ! cat file2 +TEST ! cat file3 +TEST ! cat file4 +TEST ! cat file5 +TEST ! cat file6 +TEST ! cat file7 +TEST ! cat file8 +TEST ! cat file9 +TEST ! cat file10 +################### +TEST $CLI volume set $V0 cluster.self-heal-daemon on +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "Y" glustershd_up_status +EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 1 +EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 2 +EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 3 + +################ Heal file1 using the bigger-file option ############## +$CLI volume heal $V0 split-brain bigger-file /file1 +EXPECT "0" echo $? +EXPECT $BIGGER_FILE_SIZE stat -c %s file1 + +################ Heal file2 using the bigger-file option and its gfid ############## +subvolume=$(get_replicate_subvol_number file2) +if [ $subvolume == 0 ] +then + GFID=$(gf_get_gfid_xattr $B0/${V0}1/file2) +elif [ $subvolume == 1 ] +then + GFID=$(gf_get_gfid_xattr $B0/${V0}3/file2) +fi +GFIDSTR="gfid:$(gf_gfid_xattr_to_str $GFID)" +$CLI volume heal $V0 split-brain bigger-file $GFIDSTR +EXPECT "0" echo $? + +################ Heal file3 using the source-brick option ############## +################ Use the brick having smaller file size as source ####### +subvolume=$(get_replicate_subvol_number file3) +if [ $subvolume == 0 ] +then + $CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}2 /file3 +elif [ $subvolume == 1] +then + $CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}4 /file3 +fi +EXPECT "0" echo $? +EXPECT $SMALLER_FILE_SIZE stat -c %s file3 + +################ Heal file4 using the source-brick option and it's gfid ############## +################ Use the brick having smaller file size as source ####### +subvolume=$(get_replicate_subvol_number file4) +if [ $subvolume == 0 ] +then + GFID=$(gf_get_gfid_xattr $B0/${V0}1/file4) + GFIDSTR="gfid:$(gf_gfid_xattr_to_str $GFID)" + $CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}2 $GFIDSTR +elif [ $subvolume == 1] +then + GFID=$(gf_get_gfid_xattr $B0/${V0}3/file4) + GFIDSTR="gfid:$(gf_gfid_xattr_to_str $GFID)" + $CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}4 $GFIDSTR +fi +EXPECT "0" echo $? +EXPECT $SMALLER_FILE_SIZE stat -c %s file4 + +################ Heal remaining SB'ed files of replica_0 using B1 as source ############## +$CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}1 +EXPECT "0" echo $? + +################ Heal remaining SB'ed files of replica_1 using B3 as source ############## +$CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}3 +EXPECT "0" echo $? + +############### Reading the files should now suceed. ############################### +TEST cat file1 +TEST cat file2 +TEST cat file3 +TEST cat file4 +TEST cat file5 +TEST cat file6 +TEST cat file7 +TEST cat file8 +TEST cat file9 +TEST cat file10 + +################ File contents on the bricks must be same. ################################ +TEST diff <(arequal-checksum -p $B0/$V01 -i .glusterfs) <(arequal-checksum -p $B0/$V02 -i .glusterfs) +TEST diff <(arequal-checksum -p $B0/$V03 -i .glusterfs) <(arequal-checksum -p $B0/$V04 -i .glusterfs) + +############### Trying to heal files not in SB should fail. ############################### +$CLI volume heal $V0 split-brain bigger-file /file1 +EXPECT "1" echo $? +$CLI volume heal $V0 split-brain source-brick $H0:$B0/${V0}4 /file3 +EXPECT "1" echo $? + +cd - +TEST rm $AREQUAL_PATH/arequal-checksum +cleanup diff --git a/xlators/cluster/afr/src/afr-common.c b/xlators/cluster/afr/src/afr-common.c index f39db802588..e6d45add4e8 100644 --- a/xlators/cluster/afr/src/afr-common.c +++ b/xlators/cluster/afr/src/afr-common.c @@ -4471,5 +4471,81 @@ out: AFR_STACK_UNWIND (getxattr, frame, ret, op_errno, dict, NULL); if (dict) dict_unref (dict); + if (inode) { + inode_forget (inode, 1); + inode_unref (inode); + } + return ret; +} + +int32_t +afr_heal_splitbrain_file(call_frame_t *frame, xlator_t *this, loc_t *loc) +{ + gf_boolean_t data_selfheal = _gf_false; + gf_boolean_t metadata_selfheal = _gf_false; + gf_boolean_t entry_selfheal = _gf_false; + dict_t *dict = NULL; + afr_local_t *local = NULL; + inode_t *inode = NULL; + int entry_ret = 0, metadata_ret = 0, data_ret = 0; + int ret = 0, op_errno = 0; + + local = frame->local; + dict = dict_new (); + if (!dict) { + op_errno = ENOMEM; + ret = -1; + goto out; + } + + ret = afr_selfheal_unlocked_inspect (frame, this, loc->gfid, &inode, + &data_selfheal, + &metadata_selfheal, + &entry_selfheal); + if (ret) { + op_errno = -ret; + ret = -1; + goto out; + } + + if (!data_selfheal && !metadata_selfheal && !entry_selfheal) { + ret = dict_set_str (dict, "sh-fail-msg", + "File not in split-brain"); + if (ret) + gf_log (this->name, GF_LOG_WARNING, + "Failed to set sh-fail-msg in dict"); + ret = 0; + goto out; + } + + if (data_selfheal) + data_ret = afr_selfheal_data (frame, this, inode); + + if (metadata_selfheal) + metadata_ret = afr_selfheal_metadata (frame, this, inode); + + if (entry_selfheal) + entry_ret = afr_selfheal_entry (frame, this, inode); + + ret = (data_ret | metadata_ret | entry_ret); + + if (local->xdata_rsp) { + /* 'sh-fail-msg' has been set in the dict during self-heal.*/ + dict_copy (local->xdata_rsp, dict); + ret = 0; + } else if (ret) { + /*Some other error during self-heal. Just propagate it.*/ + op_errno = -ret; + ret = -1; + } + +out: + AFR_STACK_UNWIND (getxattr, frame, ret, op_errno, dict, NULL); + if (dict) + dict_unref(dict); + if (inode) { + inode_forget (inode, 1); + inode_unref (inode); + } return ret; } diff --git a/xlators/cluster/afr/src/afr-inode-read.c b/xlators/cluster/afr/src/afr-inode-read.c index e64070e1bcd..78dd65f30e7 100644 --- a/xlators/cluster/afr/src/afr-inode-read.c +++ b/xlators/cluster/afr/src/afr-inode-read.c @@ -1380,6 +1380,11 @@ afr_getxattr (call_frame_t *frame, xlator_t *this, return 0; } + if (!strcmp (name, GF_AFR_HEAL_SBRAIN)) { + afr_heal_splitbrain_file (frame, this, loc); + return 0; + } + /* * if we are doing getxattr with pathinfo as the key then we * collect information from all childs diff --git a/xlators/cluster/afr/src/afr-self-heal-common.c b/xlators/cluster/afr/src/afr-self-heal-common.c index 6198d4cf72c..e9d853c4ecd 100644 --- a/xlators/cluster/afr/src/afr-self-heal-common.c +++ b/xlators/cluster/afr/src/afr-self-heal-common.c @@ -17,7 +17,7 @@ #include "afr.h" #include "afr-self-heal.h" #include "byte-order.h" - +#include "protocol-common.h" int afr_selfheal_post_op_cbk (call_frame_t *frame, void *cookie, xlator_t *this, @@ -287,6 +287,39 @@ afr_selfheal_extract_xattr (xlator_t *this, struct afr_reply *replies, return 0; } +/* + * If by chance there are multiple sources with differing sizes, select + * the largest file as the source. + * + * This can happen if data was directly modified in the backend or for snapshots + */ +void +afr_mark_largest_file_as_source (xlator_t *this, unsigned char *sources, + struct afr_reply *replies) +{ + int i = 0; + afr_private_t *priv = NULL; + uint64_t size = 0; + + /* Find source with biggest file size */ + priv = this->private; + for (i = 0; i < priv->child_count; i++) { + if (!sources[i]) + continue; + if (size <= replies[i].poststat.ia_size) { + size = replies[i].poststat.ia_size; + } + } + + /* Mark sources with less size as not source */ + for (i = 0; i < priv->child_count; i++) { + if (!sources[i]) + continue; + if (size > replies[i].poststat.ia_size) + sources[i] = 0; + } +} + void afr_mark_active_sinks (xlator_t *this, unsigned char *sources, unsigned char *locked_on, unsigned char *sinks) @@ -304,6 +337,154 @@ afr_mark_active_sinks (xlator_t *this, unsigned char *sources, } gf_boolean_t +afr_dict_contains_heal_op (call_frame_t *frame) +{ + afr_local_t *local = NULL; + dict_t *xdata_req = NULL; + int ret = 0; + int heal_op = -1; + + local = frame->local; + xdata_req = local->xdata_req; + ret = dict_get_int32 (xdata_req, "heal-op", &heal_op); + if (ret) + return _gf_false; + if (local->xdata_rsp == NULL) { + local->xdata_rsp = dict_new(); + if (!local->xdata_rsp) + return _gf_true; + } + ret = dict_set_str (local->xdata_rsp, "sh-fail-msg", + "File not in split-brain"); + + return _gf_true; +} + +/* Return a source depending on the type of heal_op, and set sources[source], + * sinks[source] and healed_sinks[source] to 1, 0 and 0 respectively. Do so + * only if the following condition is met: + * ∀i((i ∈ locked_on[] ∧ i=1)==>(sources[i]=0 ∧ sinks[i]=1 ∧ healed_sinks[i]=1)) + * i.e. for each locked node, sources[node] is 0; healed_sinks[node] and + * sinks[node] are 1. This should be the case if the file is in split-brain. + */ +int +afr_mark_split_brain_source_sinks (call_frame_t *frame, xlator_t *this, + unsigned char *sources, + unsigned char *sinks, + unsigned char *healed_sinks, + unsigned char *locked_on, + struct afr_reply *replies, + afr_transaction_type type) +{ + afr_local_t *local = NULL; + afr_private_t *priv = NULL; + dict_t *xdata_req = NULL; + dict_t *xdata_rsp = NULL; + int ret = 0; + int heal_op = -1; + int i = 0; + char *name = NULL; + int source = -1; + + local = frame->local; + priv = this->private; + xdata_req = local->xdata_req; + ret = dict_get_int32 (xdata_req, "heal-op", &heal_op); + if (ret) + goto out; + for (i = 0; i < priv->child_count; i++) { + if (locked_on[i]) + if (sources[i] || !sinks[i] || !healed_sinks[i]) { + ret = -1; + goto out; + } + } + if (local->xdata_rsp == NULL) { + local->xdata_rsp = dict_new(); + if (!local->xdata_rsp) { + ret = -1; + goto out; + } + } + xdata_rsp = local->xdata_rsp; + + switch (heal_op) { + case GF_AFR_OP_SBRAIN_HEAL_FROM_BIGGER_FILE: + if (type == AFR_METADATA_TRANSACTION) { + ret = dict_set_str (xdata_rsp, "sh-fail-msg", + "Use source-brick option to" + " heal metadata split-brain"); + if (!ret) + ret = -1; + goto out; + } + for (i = 0 ; i < priv->child_count; i++) + if (locked_on[i]) + sources[i] = 1; + afr_mark_largest_file_as_source (this, sources, replies); + if (AFR_COUNT (sources, priv->child_count) != 1) { + ret = dict_set_str (xdata_rsp, "sh-fail-msg", + "No bigger file"); + if (!ret) + ret = -1; + goto out; + } + for (i = 0 ; i < priv->child_count; i++) + if (sources[i]) + source = i; + sinks[source] = 0; + healed_sinks[source] = 0; + break; + case GF_AFR_OP_SBRAIN_HEAL_FROM_BRICK: + ret = dict_get_str (xdata_req, "child-name", &name); + if (ret) + goto out; + source = afr_get_child_index_from_name (this, name); + if (source < 0) { + ret = dict_set_str (xdata_rsp, "sh-fail-msg", + "Invalid brick name"); + if (!ret) + ret = -1; + goto out; + } + if (locked_on[source] != 1) { + ret = dict_set_str (xdata_rsp, "sh-fail-msg", + "Brick is not up"); + if (!ret) + ret = -1; + goto out; + } + sources[source] = 1; + sinks[source] = 0; + healed_sinks[source] = 0; + break; + default: + ret = -1; + goto out; + } + ret = source; +out: + return ret; + +} + +int +afr_get_child_index_from_name (xlator_t *this, char *name) +{ + afr_private_t *priv = this->private; + int index = -1; + + for (index = 0; index < priv->child_count; index++) { + if (!strcmp (priv->children[index]->name, name)) + goto out; + } + index = -1; +out: + return index; +} + + +gf_boolean_t afr_does_witness_exist (xlator_t *this, uint64_t *witness) { int i = 0; @@ -427,6 +608,14 @@ afr_selfheal_find_direction (call_frame_t *frame, xlator_t *this, } } + /* If no sources, all locked nodes are sinks - split brain */ + if (AFR_COUNT (sources, priv->child_count) == 0) { + for (i = 0; i < priv->child_count; i++) { + if (locked_on[i]) + sinks[i] = 1; + } + } + /* In afr-v1 if a file is self-accused but didn't have any pending * operations on others then it is similar to 'dirty' in afr-v2. * Consider such cases as witness. diff --git a/xlators/cluster/afr/src/afr-self-heal-data.c b/xlators/cluster/afr/src/afr-self-heal-data.c index a434b9e6ba1..45a099cec86 100644 --- a/xlators/cluster/afr/src/afr-self-heal-data.c +++ b/xlators/cluster/afr/src/afr-self-heal-data.c @@ -17,6 +17,7 @@ #include "afr.h" #include "afr-self-heal.h" #include "byte-order.h" +#include "protocol-common.h" enum { AFR_SELFHEAL_DATA_FULL = 0, @@ -426,41 +427,6 @@ afr_does_size_mismatch (xlator_t *this, unsigned char *sources, return _gf_false; } -/* - * If by chance there are multiple sources with differing sizes, select - * the largest file as the source. - * - * This can happen if data was directly modified in the backend or for snapshots - */ - -static void -afr_mark_largest_file_as_source (xlator_t *this, unsigned char *sources, - struct afr_reply *replies) -{ - int i = 0; - afr_private_t *priv = NULL; - uint64_t size = 0; - - /* Find source with biggest file size */ - priv = this->private; - for (i = 0; i < priv->child_count; i++) { - if (!sources[i]) - continue; - if (size <= replies[i].poststat.ia_size) { - size = replies[i].poststat.ia_size; - } - } - - /* Mark sources with less size as not source */ - for (i = 0; i < priv->child_count; i++) { - if (!sources[i]) - continue; - if (size > replies[i].poststat.ia_size) - sources[i] = 0; - } - - return; -} static void afr_mark_biggest_witness_as_source (xlator_t *this, unsigned char *sources, @@ -518,7 +484,9 @@ afr_mark_newest_file_as_source (xlator_t *this, unsigned char *sources, } static int -__afr_selfheal_data_finalize_source (xlator_t *this, unsigned char *sources, +__afr_selfheal_data_finalize_source (call_frame_t *frame, xlator_t *this, + unsigned char *sources, + unsigned char *sinks, unsigned char *healed_sinks, unsigned char *locked_on, struct afr_reply *replies, @@ -528,7 +496,6 @@ __afr_selfheal_data_finalize_source (xlator_t *this, unsigned char *sources, afr_private_t *priv = NULL; int source = -1; int sources_count = 0; - priv = this->private; sources_count = AFR_COUNT (sources, priv->child_count); @@ -536,9 +503,21 @@ __afr_selfheal_data_finalize_source (xlator_t *this, unsigned char *sources, if ((AFR_CMP (locked_on, healed_sinks, priv->child_count) == 0) || !sources_count) { /* split brain */ - return -EIO; + source = afr_mark_split_brain_source_sinks (frame, this, + sources, sinks, + healed_sinks, + locked_on, replies, + AFR_DATA_TRANSACTION); + if (source < 0) + return -EIO; + return source; } + /* No split brain at this point. If we were called from + * afr_heal_splitbrain_file(), abort.*/ + if (afr_dict_contains_heal_op(frame)) + return -EIO; + /* If there are no witnesses/size-mismatches on sources we are done*/ if (!afr_does_size_mismatch (this, sources, replies) && !afr_has_source_witnesses (this, sources, witness)) @@ -605,9 +584,10 @@ __afr_selfheal_data_prepare (call_frame_t *frame, xlator_t *this, */ AFR_INTERSECT (healed_sinks, sinks, locked_on, priv->child_count); - source = __afr_selfheal_data_finalize_source (this, sources, - healed_sinks, locked_on, - replies, witness); + source = __afr_selfheal_data_finalize_source (frame, this, sources, + sinks, healed_sinks, + locked_on, replies, + witness); if (source < 0) return -EIO; diff --git a/xlators/cluster/afr/src/afr-self-heal-metadata.c b/xlators/cluster/afr/src/afr-self-heal-metadata.c index 0518c1821e3..05d9f2b4917 100644 --- a/xlators/cluster/afr/src/afr-self-heal-metadata.c +++ b/xlators/cluster/afr/src/afr-self-heal-metadata.c @@ -17,6 +17,7 @@ #include "afr.h" #include "afr-self-heal.h" #include "byte-order.h" +#include "protocol-common.h" #define AFR_HEAL_ATTR (GF_SET_ATTR_UID|GF_SET_ATTR_GID|GF_SET_ATTR_MODE) @@ -199,6 +200,7 @@ out: static int __afr_selfheal_metadata_finalize_source (call_frame_t *frame, xlator_t *this, unsigned char *sources, + unsigned char *sinks, unsigned char *healed_sinks, unsigned char *locked_on, struct afr_reply *replies) @@ -208,13 +210,26 @@ __afr_selfheal_metadata_finalize_source (call_frame_t *frame, xlator_t *this, struct iatt first = {0, }; int source = -1; int sources_count = 0; + dict_t *xdata_req = NULL; + afr_local_t *local = NULL; priv = this->private; + local = frame->local; + xdata_req = local->xdata_req; sources_count = AFR_COUNT (sources, priv->child_count); if ((AFR_CMP (locked_on, healed_sinks, priv->child_count) == 0) || !sources_count) { + + source = afr_mark_split_brain_source_sinks (frame, this, + sources, sinks, + healed_sinks, + locked_on, replies, + AFR_METADATA_TRANSACTION); + if (source >= 0) + return source; + /* If this is a directory mtime/ctime only split brain use the most recent */ source = afr_dirtime_splitbrain_source (frame, this, @@ -224,17 +239,7 @@ __afr_selfheal_metadata_finalize_source (call_frame_t *frame, xlator_t *this, "split brain on %s", uuid_utoa (replies[source].poststat.ia_gfid)); sources[source] = 1; - - for (i = 0; i < priv->child_count; i++) { - if (i == source) - continue; - - if (!locked_on[i]) - continue; - - healed_sinks[i] = 1; - } - + healed_sinks[source] = 0; return source; } @@ -253,6 +258,11 @@ __afr_selfheal_metadata_finalize_source (call_frame_t *frame, xlator_t *this, } } + /* No split brain at this point. If we were called from + * afr_heal_splitbrain_file(), abort.*/ + if (afr_dict_contains_heal_op(frame)) + return -EIO; + for (i = 0; i < priv->child_count; i++) { if (!sources[i]) continue; @@ -352,7 +362,7 @@ __afr_selfheal_metadata_prepare (call_frame_t *frame, xlator_t *this, inode_t *i } source = __afr_selfheal_metadata_finalize_source (frame, this, sources, - healed_sinks, + sinks, healed_sinks, locked_on, replies); if (source < 0) diff --git a/xlators/cluster/afr/src/afr-self-heal.h b/xlators/cluster/afr/src/afr-self-heal.h index 50cff91ccb3..74cc9608cf6 100644 --- a/xlators/cluster/afr/src/afr-self-heal.h +++ b/xlators/cluster/afr/src/afr-self-heal.h @@ -193,10 +193,28 @@ afr_log_selfheal (uuid_t gfid, xlator_t *this, int ret, char *type, int source, unsigned char *healed_sinks); void +afr_mark_largest_file_as_source (xlator_t *this, unsigned char *sources, + struct afr_reply *replies); +void afr_mark_active_sinks (xlator_t *this, unsigned char *sources, unsigned char *locked_on, unsigned char *sinks); gf_boolean_t +afr_dict_contains_heal_op (call_frame_t *frame); + +int +afr_mark_split_brain_source_sinks (call_frame_t *frame, xlator_t *this, + unsigned char *sources, + unsigned char *sinks, + unsigned char *healed_sinks, + unsigned char *locked_on, + struct afr_reply *replies, + afr_transaction_type type); + +int +afr_get_child_index_from_name (xlator_t *this, char *name); + +gf_boolean_t afr_does_witness_exist (xlator_t *this, uint64_t *witness); int diff --git a/xlators/cluster/afr/src/afr.h b/xlators/cluster/afr/src/afr.h index 4fdc5f774cc..09821b724fe 100644 --- a/xlators/cluster/afr/src/afr.h +++ b/xlators/cluster/afr/src/afr.h @@ -1021,4 +1021,8 @@ afr_is_xattr_ignorable (char *key); int afr_get_heal_info (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata); + +int +afr_heal_splitbrain_file(call_frame_t *frame, xlator_t *this, loc_t *loc); + #endif /* __AFR_H__ */ diff --git a/xlators/cluster/dht/src/dht-common.c b/xlators/cluster/dht/src/dht-common.c index 82b527e9141..866e3faf629 100644 --- a/xlators/cluster/dht/src/dht-common.c +++ b/xlators/cluster/dht/src/dht-common.c @@ -2636,8 +2636,10 @@ dht_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, this_call_cnt = dht_frame_return (frame); - if (!xattr || (op_ret == -1)) + if (!xattr || (op_ret == -1)) { + local->op_ret = op_ret; goto out; + } if (dict_get (xattr, conf->xattr_name)) { dict_del (xattr, conf->xattr_name); @@ -2808,7 +2810,7 @@ dht_getxattr (call_frame_t *frame, xlator_t *this, subvol = layout->list[i].xlator; STACK_WIND (frame, dht_vgetxattr_dir_cbk, subvol, subvol->fops->getxattr, - loc, key, NULL); + loc, key, xdata); } return 0; } @@ -2821,7 +2823,7 @@ dht_getxattr (call_frame_t *frame, xlator_t *this, local->call_cnt = 1; STACK_WIND (frame, dht_vgetxattr_cbk, cached_subvol, - cached_subvol->fops->getxattr, loc, key, NULL); + cached_subvol->fops->getxattr, loc, key, xdata); return 0; } @@ -2854,7 +2856,7 @@ dht_getxattr (call_frame_t *frame, xlator_t *this, if (hashed_subvol) { STACK_WIND (frame, dht_linkinfo_getxattr_cbk, hashed_subvol, hashed_subvol->fops->getxattr, loc, - GF_XATTR_PATHINFO_KEY, NULL); + GF_XATTR_PATHINFO_KEY, xdata); return 0; } op_errno = ENODATA; @@ -2933,7 +2935,7 @@ dht_getxattr (call_frame_t *frame, xlator_t *this, subvol = layout->list[i].xlator; STACK_WIND (frame, dht_getxattr_cbk, subvol, subvol->fops->getxattr, - loc, key, NULL); + loc, key, xdata); } return 0; |