/*
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#include <libgen.h>
#include <unistd.h>
#include <fnmatch.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>

#include "afr.h"
#include <glusterfs/dict.h>
#include <glusterfs/hashfn.h>
#include <glusterfs/list.h>
#include <glusterfs/compat-errno.h>
#include <glusterfs/compat.h>
#include <glusterfs/statedump.h>
#include <glusterfs/events.h>
#include <glusterfs/upcall-utils.h>

#include "afr-inode-read.h"
#include "afr-inode-write.h"
#include "afr-dir-read.h"
#include "afr-dir-write.h"
#include "afr-transaction.h"
#include "afr-self-heal.h"
#include "afr-self-heald.h"
#include "afr-messages.h"

static afr_fd_ctx_t *
__afr_fd_ctx_get(fd_t *fd, xlator_t *this);

static void
afr_set_need_heal(xlator_t *this, afr_local_t *local);

static int
__afr_inode_need_refresh_set(inode_t *inode, xlator_t *this);

int32_t
afr_quorum_errno(afr_private_t *priv)
{
    return ENOTCONN;
}

gf_boolean_t
afr_is_private_directory(afr_private_t *priv, const char *name, pid_t pid)
{
    if (strcmp(name, GF_REPLICATE_TRASH_DIR) == 0) {
        /*For backward compatibility /.landfill is private*/
        return _gf_true;
    }

    if (pid == GF_CLIENT_PID_GSYNCD) {
        /*geo-rep needs to create/sync private directory on secondary because
         * it appears in changelog*/
        return _gf_false;
    }

    if (pid == GF_CLIENT_PID_GLFS_HEAL || pid == GF_CLIENT_PID_SELF_HEALD) {
        if (strcmp(name, priv->anon_inode_name) == 0) {
            /* anonymous-inode dir is private*/
            return _gf_true;
        }
    } else {
        if (strncmp(name, AFR_ANON_DIR_PREFIX, strlen(AFR_ANON_DIR_PREFIX)) ==
            0) {
            /* anonymous-inode dir prefix is private for geo-rep to work*/
            return _gf_true;
        }
    }

    return _gf_false;
}

void
afr_fill_success_replies(afr_local_t *local, afr_private_t *priv,
                         unsigned char *replies)
{
    int i = 0;

    for (i = 0; i < priv->child_count; i++) {
        if (local->replies[i].valid && local->replies[i].op_ret == 0) {
            replies[i] = 1;
        } else {
            replies[i] = 0;
        }
    }
}

int
afr_fav_child_reset_sink_xattrs(void *opaque);

static int
afr_fav_child_reset_sink_xattrs_cbk(int ret, call_frame_t *frame, void *opaque);

static void
afr_discover_done(call_frame_t *frame, xlator_t *this);

static int
afr_dom_lock_acquire_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                         int op_ret, int op_errno, dict_t *xdata)
{
    afr_local_t *local = frame->local;
    afr_private_t *priv = this->private;
    int i = (long)cookie;

    local->cont.lk.dom_lock_op_ret[i] = op_ret;
    local->cont.lk.dom_lock_op_errno[i] = op_errno;
    if (op_ret < 0) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_LK_HEAL_DOM,
               "%s: Failed to acquire %s on %s",
               uuid_utoa(local->fd->inode->gfid), AFR_LK_HEAL_DOM,
               priv->children[i]->name);
    } else {
        local->cont.lk.dom_locked_nodes[i] = 1;
    }

    syncbarrier_wake(&local->barrier);

    return 0;
}

static int
afr_dom_lock_acquire(call_frame_t *frame)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    struct gf_flock flock = {
        0,
    };
    int i = 0;

    priv = frame->this->private;
    local = frame->local;
    local->cont.lk.dom_locked_nodes = GF_CALLOC(
        priv->child_count, sizeof(*local->cont.lk.locked_nodes),
        gf_afr_mt_char);
    if (!local->cont.lk.dom_locked_nodes) {
        return -ENOMEM;
    }
    local->cont.lk.dom_lock_op_ret = GF_CALLOC(
        priv->child_count, sizeof(*local->cont.lk.dom_lock_op_ret),
        gf_afr_mt_int32_t);
    if (!local->cont.lk.dom_lock_op_ret) {
        return -ENOMEM; /* CALLOC'd members are freed in afr_local_cleanup. */
    }
    local->cont.lk.dom_lock_op_errno = GF_CALLOC(
        priv->child_count, sizeof(*local->cont.lk.dom_lock_op_errno),
        gf_afr_mt_int32_t);
    if (!local->cont.lk.dom_lock_op_errno) {
        return -ENOMEM; /* CALLOC'd members are freed in afr_local_cleanup. */
    }
    flock.l_type = F_WRLCK;

    AFR_ONALL(frame, afr_dom_lock_acquire_cbk, finodelk, AFR_LK_HEAL_DOM,
              local->fd, F_SETLK, &flock, NULL);

    if (!afr_has_quorum(local->cont.lk.dom_locked_nodes, priv, NULL))
        goto blocking_lock;

    /*If any of the bricks returned EAGAIN, we still need blocking locks.*/
    if (AFR_COUNT(local->cont.lk.dom_locked_nodes, priv->child_count) !=
        priv->child_count) {
        for (i = 0; i < priv->child_count; i++) {
            if (local->cont.lk.dom_lock_op_ret[i] == -1 &&
                local->cont.lk.dom_lock_op_errno[i] == EAGAIN)
                goto blocking_lock;
        }
    }

    return 0;

blocking_lock:
    afr_dom_lock_release(frame);
    AFR_ONALL(frame, afr_dom_lock_acquire_cbk, finodelk, AFR_LK_HEAL_DOM,
              local->fd, F_SETLKW, &flock, NULL);
    if (!afr_has_quorum(local->cont.lk.dom_locked_nodes, priv, NULL)) {
        afr_dom_lock_release(frame);
        return -afr_quorum_errno(priv);
    }

    return 0;
}

static int
afr_dom_lock_release_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                         int op_ret, int op_errno, dict_t *xdata)
{
    afr_local_t *local = frame->local;
    afr_private_t *priv = this->private;
    int i = (long)cookie;

    if (op_ret < 0) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_LK_HEAL_DOM,
               "%s: Failed to release %s on %s", local->loc.path,
               AFR_LK_HEAL_DOM, priv->children[i]->name);
    }
    local->cont.lk.dom_locked_nodes[i] = 0;

    syncbarrier_wake(&local->barrier);

    return 0;
}

void
afr_dom_lock_release(call_frame_t *frame)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    unsigned char *locked_on = NULL;
    struct gf_flock flock = {
        0,
    };

    local = frame->local;
    priv = frame->this->private;
    locked_on = local->cont.lk.dom_locked_nodes;
    if (AFR_COUNT(locked_on, priv->child_count) == 0)
        return;
    flock.l_type = F_UNLCK;

    AFR_ONLIST(locked_on, frame, afr_dom_lock_release_cbk, finodelk,
               AFR_LK_HEAL_DOM, local->fd, F_SETLK, &flock, NULL);

    return;
}

static void
afr_lk_heal_info_cleanup(afr_lk_heal_info_t *info)
{
    if (!info)
        return;
    if (info->xdata_req)
        dict_unref(info->xdata_req);
    if (info->fd)
        fd_unref(info->fd);
    GF_FREE(info->locked_nodes);
    GF_FREE(info->child_up_event_gen);
    GF_FREE(info->child_down_event_gen);
    GF_FREE(info);
}

static int
afr_add_lock_to_saved_locks(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = this->private;
    afr_local_t *local = frame->local;
    afr_lk_heal_info_t *info = NULL;
    afr_fd_ctx_t *fd_ctx = NULL;
    int ret = -ENOMEM;

    info = GF_CALLOC(sizeof(*info), 1, gf_afr_mt_lk_heal_info_t);
    if (!info) {
        goto cleanup;
    }
    INIT_LIST_HEAD(&info->pos);
    info->fd = fd_ref(local->fd);
    info->cmd = local->cont.lk.cmd;
    info->pid = frame->root->pid;
    gf_flock_copy(&info->flock, &local->cont.lk.user_flock);
    info->xdata_req = dict_copy_with_ref(local->xdata_req, NULL);
    if (!info->xdata_req) {
        goto cleanup;
    }
    lk_owner_copy(&info->lk_owner, &frame->root->lk_owner);
    info->locked_nodes = GF_MALLOC(
        sizeof(*info->locked_nodes) * priv->child_count, gf_afr_mt_char);
    if (!info->locked_nodes) {
        goto cleanup;
    }
    memcpy(info->locked_nodes, local->cont.lk.locked_nodes,
           sizeof(*info->locked_nodes) * priv->child_count);
    info->child_up_event_gen = GF_CALLOC(sizeof(*info->child_up_event_gen),
                                         priv->child_count, gf_afr_mt_int32_t);
    if (!info->child_up_event_gen) {
        goto cleanup;
    }
    info->child_down_event_gen = GF_CALLOC(sizeof(*info->child_down_event_gen),
                                           priv->child_count,
                                           gf_afr_mt_int32_t);
    if (!info->child_down_event_gen) {
        goto cleanup;
    }

    LOCK(&local->fd->lock);
    {
        fd_ctx = __afr_fd_ctx_get(local->fd, this);
        if (fd_ctx)
            fd_ctx->lk_heal_info = info;
    }
    UNLOCK(&local->fd->lock);
    if (!fd_ctx) {
        goto cleanup;
    }

    LOCK(&priv->lock);
    {
        list_add_tail(&info->pos, &priv->saved_locks);
    }
    UNLOCK(&priv->lock);

    return 0;
cleanup:
    gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_LK_HEAL_DOM,
           "%s: Failed to add lock to healq",
           uuid_utoa(local->fd->inode->gfid));
    if (info) {
        afr_lk_heal_info_cleanup(info);
        if (fd_ctx) {
            LOCK(&local->fd->lock);
            {
                fd_ctx->lk_heal_info = NULL;
            }
            UNLOCK(&local->fd->lock);
        }
    }
    return ret;
}

static int
afr_remove_lock_from_saved_locks(afr_local_t *local, xlator_t *this)
{
    afr_private_t *priv = this->private;
    struct gf_flock *user_flock;
    afr_lk_heal_info_t *info = NULL;
    afr_fd_ctx_t *fd_ctx = NULL;
    int ret = -EINVAL;

    fd_ctx = afr_fd_ctx_get(local->fd, this);
    if (!fd_ctx || !fd_ctx->lk_heal_info) {
        goto out;
    }

    user_flock = &local->cont.lk.user_flock;
    info = fd_ctx->lk_heal_info;
    if ((info->flock.l_start != user_flock->l_start) ||
        (info->flock.l_whence != user_flock->l_whence) ||
        (info->flock.l_len != user_flock->l_len)) {
        /*TODO: Compare lkowners too.*/
        goto out;
    }

    LOCK(&priv->lock);
    {
        list_del(&fd_ctx->lk_heal_info->pos);
    }
    UNLOCK(&priv->lock);

    afr_lk_heal_info_cleanup(info);
    fd_ctx->lk_heal_info = NULL;
    return 0;
out:
    if (ret)
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_LK_HEAL_DOM,
               "%s: Failed to remove lock from healq",
               uuid_utoa(local->fd->inode->gfid));
    return ret;
}

static int
afr_lock_heal_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                  int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
                  dict_t *xdata)
{
    afr_local_t *local = frame->local;
    int i = (long)cookie;

    local->replies[i].valid = 1;
    local->replies[i].op_ret = op_ret;
    local->replies[i].op_errno = op_errno;
    if (op_ret != 0) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_LK_HEAL_DOM,
               "Failed to heal lock on child %d for %s", i,
               uuid_utoa(local->fd->inode->gfid));
    }
    syncbarrier_wake(&local->barrier);
    return 0;
}

static int
afr_getlk_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
              int32_t op_errno, struct gf_flock *lock, dict_t *xdata)
{
    afr_local_t *local = frame->local;
    int i = (long)cookie;

    local->replies[i].valid = 1;
    local->replies[i].op_ret = op_ret;
    local->replies[i].op_errno = op_errno;
    if (op_ret != 0) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_LK_HEAL_DOM,
               "Failed getlk for %s", uuid_utoa(local->fd->inode->gfid));
    } else {
        gf_flock_copy(&local->cont.lk.getlk_rsp[i], lock);
    }

    syncbarrier_wake(&local->barrier);
    return 0;
}

static gf_boolean_t
afr_does_lk_owner_match(call_frame_t *frame, afr_private_t *priv,
                        afr_lk_heal_info_t *info)
{
    int i = 0;
    afr_local_t *local = frame->local;
    struct gf_flock flock;
    gf_boolean_t ret = _gf_true;
    char *wind_on = alloca0(priv->child_count);
    unsigned char *success_replies = alloca0(priv->child_count);
    local->cont.lk.getlk_rsp = GF_CALLOC(sizeof(*local->cont.lk.getlk_rsp),
                                         priv->child_count, gf_afr_mt_gf_lock);

    gf_flock_copy(&flock, &info->flock);
    for (i = 0; i < priv->child_count; i++) {
        if (info->locked_nodes[i])
            wind_on[i] = 1;
    }

    AFR_ONLIST(wind_on, frame, afr_getlk_cbk, lk, info->fd, F_GETLK, &flock,
               info->xdata_req);

    afr_fill_success_replies(local, priv, success_replies);
    if (AFR_COUNT(success_replies, priv->child_count) == 0) {
        ret = _gf_false;
        goto out;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid || local->replies[i].op_ret != 0)
            continue;
        if (local->cont.lk.getlk_rsp[i].l_type == F_UNLCK)
            continue;
        /*TODO: Do we really need to compare lkowner if F_UNLCK is true?*/
        if (!is_same_lkowner(&local->cont.lk.getlk_rsp[i].l_owner,
                             &info->lk_owner)) {
            ret = _gf_false;
            break;
        }
    }
out:
    afr_local_replies_wipe(local, priv);
    GF_FREE(local->cont.lk.getlk_rsp);
    local->cont.lk.getlk_rsp = NULL;
    return ret;
}

static void
afr_mark_fd_bad(fd_t *fd, xlator_t *this)
{
    afr_fd_ctx_t *fd_ctx = NULL;

    if (!fd)
        return;
    LOCK(&fd->lock);
    {
        fd_ctx = __afr_fd_ctx_get(fd, this);
        if (fd_ctx) {
            fd_ctx->is_fd_bad = _gf_true;
            fd_ctx->lk_heal_info = NULL;
        }
    }
    UNLOCK(&fd->lock);
}

static void
afr_add_lock_to_lkhealq(afr_private_t *priv, afr_lk_heal_info_t *info)
{
    LOCK(&priv->lock);
    {
        list_del(&info->pos);
        list_add_tail(&info->pos, &priv->lk_healq);
    }
    UNLOCK(&priv->lock);
}

static void
afr_lock_heal_do(call_frame_t *frame, afr_private_t *priv,
                 afr_lk_heal_info_t *info)
{
    int i = 0;
    int op_errno = 0;
    int32_t *current_event_gen = NULL;
    afr_local_t *local = frame->local;
    xlator_t *this = frame->this;
    char *wind_on = alloca0(priv->child_count);
    gf_boolean_t retry = _gf_true;

    frame->root->pid = info->pid;
    lk_owner_copy(&frame->root->lk_owner, &info->lk_owner);

    op_errno = -afr_dom_lock_acquire(frame);
    if ((op_errno != 0)) {
        goto release;
    }

    if (!afr_does_lk_owner_match(frame, priv, info)) {
        gf_msg(this->name, GF_LOG_WARNING, 0, AFR_MSG_LK_HEAL_DOM,
               "Ignoring lock heal for %s since lk-onwers mismatch. "
               "Lock possibly pre-empted by another client.",
               uuid_utoa(info->fd->inode->gfid));
        goto release;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (info->locked_nodes[i])
            continue;
        wind_on[i] = 1;
    }

    current_event_gen = alloca(priv->child_count * sizeof(*current_event_gen));
    memcpy(current_event_gen, info->child_up_event_gen,
           priv->child_count * sizeof *current_event_gen);
    AFR_ONLIST(wind_on, frame, afr_lock_heal_cbk, lk, info->fd, info->cmd,
               &info->flock, info->xdata_req);

    LOCK(&priv->lock);
    {
        for (i = 0; i < priv->child_count; i++) {
            if (!wind_on[i])
                continue;
            if ((!local->replies[i].valid) || (local->replies[i].op_ret != 0)) {
                continue;
            }

            if ((current_event_gen[i] == info->child_up_event_gen[i]) &&
                (current_event_gen[i] > info->child_down_event_gen[i])) {
                info->locked_nodes[i] = 1;
                retry = _gf_false;
                list_del_init(&info->pos);
                list_add_tail(&info->pos, &priv->saved_locks);
            } else {
                /*We received subsequent child up/down events while heal was in
                 * progress; don't mark child as healed. Attempt again on the
                 * new child up*/
                gf_msg(this->name, GF_LOG_ERROR, 0, AFR_MSG_LK_HEAL_DOM,
                       "Event gen mismatch: skipped healing lock on child %d "
                       "for %s.",
                       i, uuid_utoa(info->fd->inode->gfid));
            }
        }
    }
    UNLOCK(&priv->lock);

release:
    afr_dom_lock_release(frame);
    if (retry)
        afr_add_lock_to_lkhealq(priv, info);
    return;
}

static int
afr_lock_heal_done(int ret, call_frame_t *frame, void *opaque)
{
    STACK_DESTROY(frame->root);
    return 0;
}

static int
afr_lock_heal(void *opaque)
{
    call_frame_t *frame = (call_frame_t *)opaque;
    call_frame_t *iter_frame = NULL;
    xlator_t *this = frame->this;
    afr_private_t *priv = this->private;
    afr_lk_heal_info_t *info = NULL;
    afr_lk_heal_info_t *tmp = NULL;
    struct list_head healq = {
        0,
    };
    int ret = 0;

    iter_frame = afr_copy_frame(frame);
    if (!iter_frame) {
        return ENOMEM;
    }

    INIT_LIST_HEAD(&healq);
    LOCK(&priv->lock);
    {
        list_splice_init(&priv->lk_healq, &healq);
    }
    UNLOCK(&priv->lock);

    list_for_each_entry_safe(info, tmp, &healq, pos)
    {
        GF_ASSERT((AFR_COUNT(info->locked_nodes, priv->child_count) <
                   priv->child_count));
        ((afr_local_t *)(iter_frame->local))->fd = fd_ref(info->fd);
        afr_lock_heal_do(iter_frame, priv, info);
        AFR_STACK_RESET(iter_frame);
        if (iter_frame->local == NULL) {
            ret = ENOTCONN;
            gf_msg(frame->this->name, GF_LOG_ERROR, ENOTCONN,
                   AFR_MSG_LK_HEAL_DOM,
                   "Aborting processing of lk_healq."
                   "Healing will be reattempted on next child up for locks "
                   "that are still in quorum.");
            LOCK(&priv->lock);
            {
                list_add_tail(&healq, &priv->lk_healq);
            }
            UNLOCK(&priv->lock);
            break;
        }
    }

    AFR_STACK_DESTROY(iter_frame);
    return ret;
}

static int
__afr_lock_heal_synctask(xlator_t *this, afr_private_t *priv, int child)
{
    int ret = 0;
    call_frame_t *frame = NULL;
    afr_lk_heal_info_t *info = NULL;
    afr_lk_heal_info_t *tmp = NULL;

    if (priv->shd.iamshd)
        return 0;

    list_for_each_entry_safe(info, tmp, &priv->saved_locks, pos)
    {
        info->child_up_event_gen[child] = priv->event_generation;
        list_del_init(&info->pos);
        list_add_tail(&info->pos, &priv->lk_healq);
    }

    frame = create_frame(this, this->ctx->pool);
    if (!frame)
        return -1;

    ret = synctask_new(this->ctx->env, afr_lock_heal, afr_lock_heal_done, frame,
                       frame);
    if (ret)
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, AFR_MSG_LK_HEAL_DOM,
               "Failed to launch lock heal synctask");

    return ret;
}

static int
__afr_mark_pending_lk_heal(xlator_t *this, afr_private_t *priv, int child)
{
    afr_lk_heal_info_t *info = NULL;
    afr_lk_heal_info_t *tmp = NULL;

    if (priv->shd.iamshd)
        return 0;
    list_for_each_entry_safe(info, tmp, &priv->saved_locks, pos)
    {
        info->child_down_event_gen[child] = priv->event_generation;
        if (info->locked_nodes[child] == 1)
            info->locked_nodes[child] = 0;
        if (!afr_has_quorum(info->locked_nodes, priv, NULL)) {
            /* Since the lock was lost on quorum no. of nodes, we should
             * not attempt to heal it anymore. Some other client could have
             * acquired the lock, modified data and released it and this
             * client wouldn't know about it if we heal it.*/
            afr_mark_fd_bad(info->fd, this);
            list_del(&info->pos);
            afr_lk_heal_info_cleanup(info);
            /* We're not winding an unlock on the node where the lock is still
             * present because when fencing logic switches over to the new
             * client (since we marked the fd bad), it should preempt any
             * existing lock. */
        }
    }
    return 0;
}

gf_boolean_t
afr_is_consistent_io_possible(afr_local_t *local, afr_private_t *priv,
                              int32_t *op_errno)
{
    if (priv->consistent_io && local->call_count != priv->child_count) {
        gf_msg(THIS->name, GF_LOG_INFO, 0, AFR_MSG_SUBVOLS_DOWN,
               "All subvolumes are not up");
        if (op_errno)
            *op_errno = ENOTCONN;
        return _gf_false;
    }
    return _gf_true;
}

gf_boolean_t
afr_is_lock_mode_mandatory(dict_t *xdata)
{
    int ret = 0;
    uint32_t lk_mode = GF_LK_ADVISORY;

    ret = dict_get_uint32(xdata, GF_LOCK_MODE, &lk_mode);
    if (!ret && lk_mode == GF_LK_MANDATORY)
        return _gf_true;

    return _gf_false;
}

call_frame_t *
afr_copy_frame(call_frame_t *base)
{
    afr_local_t *local = NULL;
    call_frame_t *frame = NULL;
    int op_errno = 0;

    frame = copy_frame(base);
    if (!frame)
        return NULL;
    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local) {
        AFR_STACK_DESTROY(frame);
        return NULL;
    }

    return frame;
}

/* Check if an entry or inode could be undergoing a transaction. */
static gf_boolean_t
afr_is_possibly_under_txn(afr_transaction_type type, afr_local_t *local,
                          const unsigned int child_count)
{
    int i = 0;
    int tmp = 0;
    GF_UNUSED char *key = NULL;
    int keylen = 0;

    if (type == AFR_ENTRY_TRANSACTION) {
        key = GLUSTERFS_PARENT_ENTRYLK;
        keylen = SLEN(GLUSTERFS_PARENT_ENTRYLK);
    } else if (type == AFR_DATA_TRANSACTION) {
        /*FIXME: Use GLUSTERFS_INODELK_DOM_COUNT etc. once
         * pl_inodelk_xattr_fill supports separate keys for different
         * domains.*/
        key = GLUSTERFS_INODELK_COUNT;
        keylen = SLEN(GLUSTERFS_INODELK_COUNT);
    }
    for (i = 0; i < child_count; i++) {
        if (!local->replies[i].xdata)
            continue;
        if (dict_get_int32n(local->replies[i].xdata, key, keylen, &tmp) == 0)
            if (tmp)
                return _gf_true;
    }

    return _gf_false;
}

static void
afr_inode_ctx_destroy(afr_inode_ctx_t *ctx)
{
    int i = 0;

    if (!ctx)
        return;

    for (i = 0; i < AFR_NUM_CHANGE_LOGS; i++) {
        GF_FREE(ctx->pre_op_done[i]);
    }

    GF_FREE(ctx);
}

afr_inode_ctx_t *
__afr_inode_ctx_get(xlator_t *this, inode_t *inode)
{
    uint64_t ctx_int = 0;
    int ret;
    int i;
    int num_locks;
    afr_inode_ctx_t *ictx = NULL;
    afr_lock_t *lock = NULL;
    afr_private_t *priv = NULL;

    ret = __inode_ctx_get(inode, this, &ctx_int);
    if (ret == 0) {
        ictx = (afr_inode_ctx_t *)(uintptr_t)ctx_int;
        goto out;
    }

    ictx = GF_CALLOC(1, sizeof(afr_inode_ctx_t), gf_afr_mt_inode_ctx_t);
    if (!ictx)
        goto err;

    priv = this->private;
    for (i = 0; i < AFR_NUM_CHANGE_LOGS; i++) {
        ictx->pre_op_done[i] = GF_CALLOC(sizeof *ictx->pre_op_done[i],
                                         priv->child_count, gf_afr_mt_int32_t);
        if (!ictx->pre_op_done[i]) {
            goto err;
        }
    }

    num_locks = sizeof(ictx->lock) / sizeof(afr_lock_t);
    for (i = 0; i < num_locks; i++) {
        lock = &ictx->lock[i];
        INIT_LIST_HEAD(&lock->post_op);
        INIT_LIST_HEAD(&lock->frozen);
        INIT_LIST_HEAD(&lock->waiting);
        INIT_LIST_HEAD(&lock->owners);
    }

    ctx_int = (uint64_t)(uintptr_t)ictx;
    ret = __inode_ctx_set(inode, this, &ctx_int);
    if (ret) {
        goto err;
    }

    ictx->spb_choice = -1;
    ictx->read_subvol = 0;
    ictx->write_subvol = 0;
    ictx->lock_count = 0;
out:
    return ictx;
err:
    if (ictx) {
        afr_inode_ctx_destroy(ictx);
    }
    return NULL;
}

/*
 * INODE CTX 64-bit VALUE FORMAT FOR SMALL (<= 16) SUBVOL COUNTS:
 *
 * |<----------   64bit   ------------>|
 *  63           32 31    16 15       0
 * |   EVENT_GEN   |  DATA  | METADATA |
 *
 *
 *  METADATA (bit-0 .. bit-15): bitmap representing subvolumes from which
 *                              metadata can be attempted to be read.
 *
 *                              bit-0 => priv->subvolumes[0]
 *                              bit-1 => priv->subvolumes[1]
 *                              ... etc. till bit-15
 *
 *  DATA (bit-16 .. bit-31): bitmap representing subvolumes from which data
 *                           can be attempted to be read.
 *
 *                           bit-16 => priv->subvolumes[0]
 *                           bit-17 => priv->subvolumes[1]
 *                           ... etc. till bit-31
 *
 *  EVENT_GEN (bit-32 .. bit-63): event generation (i.e priv->event_generation)
 *                                when DATA and METADATA was last updated.
 *
 *                                If EVENT_GEN is < priv->event_generation,
 *                                or is 0, it means afr_inode_refresh() needs
 *                                to be called to recalculate the bitmaps.
 */

static int
__afr_set_in_flight_sb_status(xlator_t *this, afr_local_t *local,
                              inode_t *inode)
{
    int i = 0;
    int txn_type = 0;
    int count = 0;
    int index = -1;
    uint16_t datamap_old = 0;
    uint16_t metadatamap_old = 0;
    uint16_t datamap = 0;
    uint16_t metadatamap = 0;
    uint16_t tmp_map = 0;
    uint16_t mask = 0;
    uint32_t event = 0;
    uint64_t val = 0;
    afr_private_t *priv = NULL;

    priv = this->private;
    txn_type = local->transaction.type;

    if (txn_type == AFR_DATA_TRANSACTION)
        val = local->inode_ctx->write_subvol;
    else
        val = local->inode_ctx->read_subvol;

    metadatamap_old = metadatamap = (val & 0x000000000000ffff);
    datamap_old = datamap = (val & 0x00000000ffff0000) >> 16;
    event = (val & 0xffffffff00000000) >> 32;

    if (txn_type == AFR_DATA_TRANSACTION)
        tmp_map = datamap;
    else if (txn_type == AFR_METADATA_TRANSACTION)
        tmp_map = metadatamap;

    count = gf_bits_count(tmp_map);

    for (i = 0; i < priv->child_count; i++) {
        if (!local->transaction.failed_subvols[i])
            continue;

        mask = 1 << i;
        if (txn_type == AFR_METADATA_TRANSACTION)
            metadatamap &= ~mask;
        else if (txn_type == AFR_DATA_TRANSACTION)
            datamap &= ~mask;
    }

    switch (txn_type) {
        case AFR_METADATA_TRANSACTION:
            if ((metadatamap_old != 0) && (metadatamap == 0) && (count == 1)) {
                index = gf_bits_index(tmp_map);
                local->transaction.in_flight_sb_errno = local->replies[index]
                                                            .op_errno;
                local->transaction.in_flight_sb = _gf_true;
                metadatamap |= (1 << index);
            }
            if (metadatamap_old != metadatamap) {
                __afr_inode_need_refresh_set(inode, this);
            }
            break;

        case AFR_DATA_TRANSACTION:
            if ((datamap_old != 0) && (datamap == 0) && (count == 1)) {
                index = gf_bits_index(tmp_map);
                local->transaction.in_flight_sb_errno = local->replies[index]
                                                            .op_errno;
                local->transaction.in_flight_sb = _gf_true;
                datamap |= (1 << index);
            }
            if (datamap_old != datamap)
                __afr_inode_need_refresh_set(inode, this);
            break;

        default:
            break;
    }

    val = ((uint64_t)metadatamap) | (((uint64_t)datamap) << 16) |
          (((uint64_t)event) << 32);

    if (txn_type == AFR_DATA_TRANSACTION)
        local->inode_ctx->write_subvol = val;
    local->inode_ctx->read_subvol = val;

    return 0;
}

gf_boolean_t
afr_is_symmetric_error(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int op_errno = 0;
    int i_errno = 0;
    gf_boolean_t matching_errors = _gf_true;
    int i = 0;

    priv = this->private;
    local = frame->local;

    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid)
            continue;
        if (local->replies[i].op_ret != -1) {
            /* Operation succeeded on at least one subvol,
               so it is not a failed-everywhere situation.
            */
            matching_errors = _gf_false;
            break;
        }
        i_errno = local->replies[i].op_errno;

        if (i_errno == ENOTCONN) {
            /* ENOTCONN is not a symmetric error. We do not
               know if the operation was performed on the
               backend or not.
            */
            matching_errors = _gf_false;
            break;
        }

        if (!op_errno) {
            op_errno = i_errno;
        } else if (op_errno != i_errno) {
            /* Mismatching op_errno's */
            matching_errors = _gf_false;
            break;
        }
    }

    return matching_errors;
}

int
afr_set_in_flight_sb_status(xlator_t *this, call_frame_t *frame, inode_t *inode)
{
    int ret = -1;
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;

    priv = this->private;
    local = frame->local;

    /* If this transaction saw no failures, then exit. */
    if (AFR_COUNT(local->transaction.failed_subvols, priv->child_count) == 0)
        return 0;

    if (afr_is_symmetric_error(frame, this))
        return 0;

    LOCK(&inode->lock);
    {
        ret = __afr_set_in_flight_sb_status(this, local, inode);
    }
    UNLOCK(&inode->lock);

    return ret;
}

static int
__afr_inode_read_subvol_get_small(inode_t *inode, xlator_t *this,
                                  unsigned char *data, unsigned char *metadata,
                                  int *event_p)
{
    afr_private_t *priv = NULL;
    uint16_t datamap = 0;
    uint16_t metadatamap = 0;
    uint32_t event = 0;
    uint64_t val = 0;
    int i = 0;
    afr_inode_ctx_t *ctx = NULL;

    priv = this->private;

    ctx = __afr_inode_ctx_get(this, inode);
    if (ctx == NULL)
        return -1;

    val = ctx->read_subvol;

    metadatamap = (val & 0x000000000000ffff);
    datamap = (val & 0x00000000ffff0000) >> 16;
    event = (val & 0xffffffff00000000) >> 32;

    for (i = 0; i < priv->child_count; i++) {
        if (metadata)
            metadata[i] = (metadatamap >> i) & 1;
        if (data)
            data[i] = (datamap >> i) & 1;
    }

    if (event_p)
        *event_p = event;
    return 0;
}

static int
__afr_inode_read_subvol_set_small(inode_t *inode, xlator_t *this,
                                  unsigned char *data, unsigned char *metadata,
                                  int event)
{
    afr_private_t *priv = NULL;
    uint16_t datamap = 0;
    uint16_t metadatamap = 0;
    uint64_t val = 0;
    int i = 0;
    afr_inode_ctx_t *ctx = NULL;

    priv = this->private;

    ctx = __afr_inode_ctx_get(this, inode);
    if (ctx == NULL)
        return -1;

    for (i = 0; i < priv->child_count; i++) {
        if (data[i])
            datamap |= (1 << i);
        if (metadata[i])
            metadatamap |= (1 << i);
    }

    val = ((uint64_t)metadatamap) | (((uint64_t)datamap) << 16) |
          (((uint64_t)event) << 32);

    ctx->read_subvol = val;

    return 0;
}

static int
__afr_inode_read_subvol_get(inode_t *inode, xlator_t *this, unsigned char *data,
                            unsigned char *metadata, int *event_p)
{
    afr_private_t *priv = NULL;
    int ret = -1;

    priv = this->private;

    if (priv->child_count <= 16)
        ret = __afr_inode_read_subvol_get_small(inode, this, data, metadata,
                                                event_p);
    else
        /* TBD: allocate structure with array and read from it */
        ret = -1;

    return ret;
}

static int
__afr_inode_read_subvol_set(inode_t *inode, xlator_t *this, unsigned char *data,
                            unsigned char *metadata, int event)
{
    afr_private_t *priv = NULL;
    int ret = -1;

    priv = this->private;

    if (priv->child_count <= 16)
        ret = __afr_inode_read_subvol_set_small(inode, this, data, metadata,
                                                event);
    else
        ret = -1;

    return ret;
}

static int
__afr_inode_split_brain_choice_set(inode_t *inode, xlator_t *this,
                                   int spb_choice)
{
    afr_inode_ctx_t *ctx = NULL;

    ctx = __afr_inode_ctx_get(this, inode);
    if (ctx == NULL)
        return -1;

    ctx->spb_choice = spb_choice;

    return 0;
}

int
afr_inode_read_subvol_get(inode_t *inode, xlator_t *this, unsigned char *data,
                          unsigned char *metadata, int *event_p)
{
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, inode, out);

    LOCK(&inode->lock);
    {
        ret = __afr_inode_read_subvol_get(inode, this, data, metadata, event_p);
    }
    UNLOCK(&inode->lock);
out:
    return ret;
}

int
afr_inode_get_readable(call_frame_t *frame, inode_t *inode, xlator_t *this,
                       unsigned char *readable, int *event_p, int type)
{
    afr_private_t *priv = this->private;
    afr_local_t *local = frame->local;
    unsigned char *data = alloca0(priv->child_count);
    unsigned char *metadata = alloca0(priv->child_count);
    int data_count = 0;
    int metadata_count = 0;
    int event_generation = 0;
    int ret = 0;

    ret = afr_inode_read_subvol_get(inode, this, data, metadata,
                                    &event_generation);
    if (ret == -1)
        return -EIO;

    data_count = AFR_COUNT(data, priv->child_count);
    metadata_count = AFR_COUNT(metadata, priv->child_count);

    if (inode->ia_type == IA_IFDIR) {
        /* For directories, allow even if it is in data split-brain. */
        if (type == AFR_METADATA_TRANSACTION || local->op == GF_FOP_STAT ||
            local->op == GF_FOP_FSTAT) {
            if (!metadata_count)
                return -EIO;
        }
    } else {
        /* For files, abort in case of data/metadata split-brain. */
        if (!data_count || !metadata_count) {
            return -EIO;
        }
    }

    if (type == AFR_METADATA_TRANSACTION && readable)
        memcpy(readable, metadata, priv->child_count * sizeof *metadata);
    if (type == AFR_DATA_TRANSACTION && readable) {
        if (!data_count)
            memcpy(readable, local->child_up,
                   priv->child_count * sizeof *readable);
        else
            memcpy(readable, data, priv->child_count * sizeof *data);
    }
    if (event_p)
        *event_p = event_generation;
    return 0;
}

static int
afr_inode_split_brain_choice_get(inode_t *inode, xlator_t *this,
                                 int *spb_choice)
{
    int ret = 0;
    afr_inode_ctx_t *ctx = NULL;

    LOCK(&inode->lock);
    {
        ctx = __afr_inode_ctx_get(this, inode);
        if (ctx) {
            *spb_choice = ctx->spb_choice;
        } else {
            ret = -1;
        }
    }
    UNLOCK(&inode->lock);

    return ret;
}

/*
 * frame is used to get the favourite policy. Since
 * afr_inode_split_brain_choice_get was called with afr_open, it is possible to
 * have a frame with out local->replies. So in that case, frame is passed as
 * null, hence this function will handle the frame NULL case.
 */
int
afr_split_brain_read_subvol_get(inode_t *inode, xlator_t *this,
                                call_frame_t *frame, int *spb_subvol)
{
    int ret = -1;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;

    GF_VALIDATE_OR_GOTO("afr", this, out);
    GF_VALIDATE_OR_GOTO(this->name, this->private, out);
    GF_VALIDATE_OR_GOTO(this->name, inode, out);
    GF_VALIDATE_OR_GOTO(this->name, spb_subvol, out);

    priv = this->private;

    ret = afr_inode_split_brain_choice_get(inode, this, spb_subvol);
    if (*spb_subvol < 0 && priv->fav_child_policy && frame && frame->local) {
        local = frame->local;
        *spb_subvol = afr_sh_get_fav_by_policy(this, local->replies, inode,
                                               NULL);
        if (*spb_subvol >= 0) {
            ret = 0;
        }
    }

out:
    return ret;
}

static int
afr_inode_read_subvol_set(inode_t *inode, xlator_t *this, unsigned char *data,
                          unsigned char *metadata, int event)
{
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, inode, out);

    LOCK(&inode->lock);
    {
        ret = __afr_inode_read_subvol_set(inode, this, data, metadata, event);
    }
    UNLOCK(&inode->lock);
out:
    return ret;
}

int
afr_inode_split_brain_choice_set(inode_t *inode, xlator_t *this, int spb_choice)
{
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, inode, out);

    LOCK(&inode->lock);
    {
        ret = __afr_inode_split_brain_choice_set(inode, this, spb_choice);
    }
    UNLOCK(&inode->lock);
out:
    return ret;
}

/* The caller of this should perform afr_inode_refresh, if this function
 * returns _gf_true
 */
gf_boolean_t
afr_is_inode_refresh_reqd(inode_t *inode, xlator_t *this, int event_gen1,
                          int event_gen2)
{
    gf_boolean_t need_refresh = _gf_false;
    afr_inode_ctx_t *ctx = NULL;

    GF_VALIDATE_OR_GOTO(this->name, inode, out);

    LOCK(&inode->lock);
    {
        ctx = __afr_inode_ctx_get(this, inode);
        if (ctx == NULL)
            goto unlock;

        need_refresh = ctx->need_refresh;
        /* Hoping that the caller will do inode_refresh followed by
         * this, hence setting the need_refresh to false */
        ctx->need_refresh = _gf_false;
    }
unlock:
    UNLOCK(&inode->lock);

    if (event_gen1 != event_gen2)
        need_refresh = _gf_true;
out:
    return need_refresh;
}

static int
__afr_inode_need_refresh_set(inode_t *inode, xlator_t *this)
{
    afr_inode_ctx_t *ctx = NULL;

    ctx = __afr_inode_ctx_get(this, inode);
    if (ctx) {
        ctx->need_refresh = _gf_true;
        return 0;
    }

    return -1;
}

int
afr_inode_need_refresh_set(inode_t *inode, xlator_t *this)
{
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, inode, out);

    LOCK(&inode->lock);
    {
        ret = __afr_inode_need_refresh_set(inode, this);
    }
    UNLOCK(&inode->lock);
out:
    return ret;
}

static void
afr_spb_choice_timeout_cancel(xlator_t *this, inode_t *inode)
{
    afr_inode_ctx_t *ctx = NULL;

    if (!inode)
        return;

    LOCK(&inode->lock);
    {
        ctx = __afr_inode_ctx_get(this, inode);
        if (ctx == NULL) {
            UNLOCK(&inode->lock);
            gf_msg(this->name, GF_LOG_WARNING, 0,
                   AFR_MSG_SPLIT_BRAIN_CHOICE_ERROR,
                   "Failed to cancel split-brain choice timer.");
            return;
        }
        ctx->spb_choice = -1;
        if (ctx->timer) {
            gf_timer_call_cancel(this->ctx, ctx->timer);
            ctx->timer = NULL;
        }
    }
    UNLOCK(&inode->lock);
}

static void
afr_set_split_brain_choice_cbk(void *data)
{
    inode_t *inode = data;
    xlator_t *this = THIS;

    afr_spb_choice_timeout_cancel(this, inode);
    inode_invalidate(inode);
    inode_unref(inode);
    return;
}

int
afr_set_split_brain_choice(int ret, call_frame_t *frame, void *opaque)
{
    int op_errno = ENOMEM;
    afr_private_t *priv = NULL;
    afr_inode_ctx_t *ctx = NULL;
    inode_t *inode = NULL;
    loc_t *loc = NULL;
    xlator_t *this = NULL;
    afr_spbc_timeout_t *data = opaque;
    struct timespec delta = {
        0,
    };
    gf_boolean_t timer_set = _gf_false;
    gf_boolean_t timer_cancelled = _gf_false;
    gf_boolean_t timer_reset = _gf_false;
    int old_spb_choice = -1;

    frame = data->frame;
    loc = data->loc;
    this = frame->this;
    priv = this->private;

    if (ret) {
        op_errno = -ret;
        ret = -1;
        goto out;
    }

    delta.tv_sec = priv->spb_choice_timeout;
    delta.tv_nsec = 0;

    if (!loc->inode) {
        ret = -1;
        op_errno = EINVAL;
        goto out;
    }

    if (!(data->d_spb || data->m_spb)) {
        gf_msg(this->name, GF_LOG_WARNING, 0, AFR_MSG_SPLIT_BRAIN_CHOICE_ERROR,
               "Cannot set "
               "replica.split-brain-choice on %s. File is"
               " not in data/metadata split-brain.",
               uuid_utoa(loc->gfid));
        ret = -1;
        op_errno = EINVAL;
        goto out;
    }

    /*
     * we're ref'ing the inode before LOCK like it is done elsewhere in the
     * code. If we ref after LOCK, coverity complains of possible deadlocks.
     */
    inode = inode_ref(loc->inode);

    LOCK(&inode->lock);
    {
        ctx = __afr_inode_ctx_get(this, inode);
        if (ctx == NULL) {
            UNLOCK(&inode->lock);
            ret = -1;
            gf_msg(this->name, GF_LOG_ERROR, 0,
                   AFR_MSG_SPLIT_BRAIN_CHOICE_ERROR,
                   "Failed to get inode_ctx for %s", loc->name);
            goto post_unlock;
        } else {
            ret = 0;
        }

        old_spb_choice = ctx->spb_choice;
        ctx->spb_choice = data->spb_child_index;

        /* Possible changes in spb-choice :
         *         valid to -1    : cancel timer and unref
         *         valid to valid : cancel timer and inject new one
         *         -1    to -1    : unref and do not do anything
         *         -1 to valid    : inject timer
         */

        /* ctx->timer is NULL iff previous value of
         * ctx->spb_choice is -1
         */
        if (ctx->timer) {
            if (ctx->spb_choice == -1) {
                if (!gf_timer_call_cancel(this->ctx, ctx->timer)) {
                    ctx->timer = NULL;
                    timer_cancelled = _gf_true;
                }
                /* If timer cancel failed here it means that the
                 *  previous cbk will be executed which will set
                 *  spb_choice to -1. So we can consider the
                 *  'valid to -1' case to be a success
                 *  (i.e. ret = 0) and goto unlock.
                 */
                goto unlock;
            }
            goto reset_timer;
        } else {
            if (ctx->spb_choice == -1)
                goto unlock;
            goto set_timer;
        }

    reset_timer:
        ret = gf_timer_call_cancel(this->ctx, ctx->timer);
        if (ret != 0) {
            /* We need to bail out now instead of launching a new
             * timer. Otherwise the cbk of the previous timer event
             * will cancel the new ctx->timer.
             */
            ctx->spb_choice = old_spb_choice;
            ret = -1;
            op_errno = EAGAIN;
            goto unlock;
        }
        ctx->timer = NULL;
        timer_reset = _gf_true;

    set_timer:
        ctx->timer = gf_timer_call_after(this->ctx, delta,
                                         afr_set_split_brain_choice_cbk, inode);
        if (!ctx->timer) {
            ctx->spb_choice = old_spb_choice;
            ret = -1;
            op_errno = ENOMEM;
        }
        if (!timer_reset && ctx->timer)
            timer_set = _gf_true;
        if (timer_reset && !ctx->timer)
            timer_cancelled = _gf_true;
    }
unlock:
    UNLOCK(&inode->lock);
post_unlock:
    if (!timer_set)
        inode_unref(inode);
    if (timer_cancelled)
        inode_unref(inode);
    /*
     * We need to invalidate the inode to prevent the kernel from serving
     * reads from an older cached value despite a change in spb_choice to
     * a new value.
     */
    inode_invalidate(inode);
out:
    GF_FREE(data);
    AFR_STACK_UNWIND(setxattr, frame, ret, op_errno, NULL);
    return 0;
}

static int
afr_accused_fill(afr_private_t *priv, dict_t *xdata, unsigned char *accused,
                 afr_transaction_type type)
{
    int i = 0;
    int idx = afr_index_for_transaction_type(type);
    void *pending_raw = NULL;
    int ret = 0;

    for (i = 0; i < priv->child_count; i++) {
        ret = dict_get_ptr(xdata, priv->pending_key[i], &pending_raw);
        if (ret) /* no pending flags */
            continue;

        if (*((int *)pending_raw + idx))
            accused[i] = 1;
    }

    return 0;
}

static int
afr_accuse_smallfiles(afr_private_t *priv, struct afr_reply *replies,
                      unsigned char *data_accused)
{
    int i = 0;
    uint64_t maxsize = 0;

    for (i = 0; i < priv->child_count; i++) {
        if (replies[i].valid && replies[i].xdata &&
            dict_get_sizen(replies[i].xdata, GLUSTERFS_BAD_INODE))
            continue;
        if (data_accused[i])
            continue;
        if (replies[i].poststat.ia_size > maxsize)
            maxsize = replies[i].poststat.ia_size;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (data_accused[i])
            continue;
        if (AFR_IS_ARBITER_BRICK(priv, i))
            continue;
        if (replies[i].poststat.ia_size < maxsize)
            data_accused[i] = 1;
    }

    return 0;
}

static int
afr_readables_fill(afr_local_t *local, xlator_t *this, inode_t *inode,
                   unsigned char *data_accused, unsigned char *metadata_accused,
                   unsigned char *data_readable,
                   unsigned char *metadata_readable, struct afr_reply *replies)
{
    afr_private_t *priv = NULL;
    dict_t *xdata = NULL;
    int i = 0;
    int ret = 0;
    ia_type_t ia_type = IA_INVAL;

    priv = this->private;

    for (i = 0; i < priv->child_count; i++) {
        data_readable[i] = 1;
        metadata_readable[i] = 1;
    }
    if (AFR_IS_ARBITER_BRICK(priv, ARBITER_BRICK_INDEX)) {
        data_readable[ARBITER_BRICK_INDEX] = 0;
        metadata_readable[ARBITER_BRICK_INDEX] = 0;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (replies) { /* Lookup */
            if (!replies[i].valid || replies[i].op_ret == -1 ||
                (replies[i].xdata &&
                 dict_get_sizen(replies[i].xdata, GLUSTERFS_BAD_INODE))) {
                data_readable[i] = 0;
                metadata_readable[i] = 0;
                continue;
            }

            xdata = replies[i].xdata;
            ia_type = replies[i].poststat.ia_type;
        } else { /* pre-op xattrop */
            xdata = local->transaction.changelog_xdata[i];
            ia_type = inode->ia_type;
        }

        if (!xdata)
            continue; /* mkdir_cbk sends NULL xdata_rsp. */
        afr_accused_fill(priv, xdata, data_accused,
                         (ia_type == IA_IFDIR) ? AFR_ENTRY_TRANSACTION
                                               : AFR_DATA_TRANSACTION);

        afr_accused_fill(priv, xdata, metadata_accused,
                         AFR_METADATA_TRANSACTION);
    }

    if (replies && ia_type != IA_INVAL && ia_type != IA_IFDIR &&
        /* We want to accuse small files only when we know for
         * sure that there is no IO happening. Otherwise, the
         * ia_sizes obtained in post-refresh replies may
         * mismatch due to a race between inode-refresh and
         * ongoing writes, causing spurious heal launches*/
        !afr_is_possibly_under_txn(AFR_DATA_TRANSACTION, local,
                                   priv->child_count)) {
        afr_accuse_smallfiles(priv, replies, data_accused);
    }

    for (i = 0; i < priv->child_count; i++) {
        if (data_accused[i]) {
            data_readable[i] = 0;
            ret = 1;
        }
        if (metadata_accused[i]) {
            metadata_readable[i] = 0;
            ret = 1;
        }
    }
    return ret;
}

int
afr_replies_interpret(afr_local_t *local, xlator_t *this, inode_t *inode,
                      gf_boolean_t *start_heal)
{
    afr_private_t *priv = NULL;
    struct afr_reply *replies = NULL;
    int event_generation = 0;
    int i = 0;
    unsigned char *data_accused = NULL;
    unsigned char *metadata_accused = NULL;
    unsigned char *data_readable = NULL;
    unsigned char *metadata_readable = NULL;
    int ret = 0;

    priv = this->private;
    replies = local->replies;
    event_generation = local->event_generation;

    data_accused = alloca0(priv->child_count);
    data_readable = alloca0(priv->child_count);
    metadata_accused = alloca0(priv->child_count);
    metadata_readable = alloca0(priv->child_count);

    ret = afr_readables_fill(local, this, inode, data_accused, metadata_accused,
                             data_readable, metadata_readable, replies);

    for (i = 0; i < priv->child_count; i++) {
        if (start_heal && priv->child_up[i] &&
            (data_accused[i] || metadata_accused[i])) {
            *start_heal = _gf_true;
            break;
        }
    }
    afr_inode_read_subvol_set(inode, this, data_readable, metadata_readable,
                              event_generation);
    return ret;
}

static int
afr_refresh_selfheal_done(int ret, call_frame_t *heal, void *opaque)
{
    if (heal)
        AFR_STACK_DESTROY(heal);
    return 0;
}

static int
afr_inode_refresh_err(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int i = 0;
    int err = 0;

    local = frame->local;
    priv = this->private;

    for (i = 0; i < priv->child_count; i++) {
        if (local->replies[i].valid && !local->replies[i].op_ret) {
            err = 0;
            goto ret;
        }
    }

    err = afr_final_errno(local, priv);
ret:
    return err;
}

static gf_boolean_t
afr_selfheal_enabled(const xlator_t *this)
{
    const afr_private_t *priv = this->private;

    return priv->data_self_heal || priv->metadata_self_heal ||
           priv->entry_self_heal;
}

static int
afr_txn_refresh_done(call_frame_t *frame, xlator_t *this, int err)
{
    call_frame_t *heal_frame = NULL;
    afr_local_t *heal_local = NULL;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    inode_t *inode = NULL;
    int event_generation = 0;
    int read_subvol = -1;
    int ret = 0;

    local = frame->local;
    inode = local->inode;
    priv = this->private;

    if (err)
        goto refresh_done;

    if (local->op == GF_FOP_LOOKUP)
        goto refresh_done;

    ret = afr_inode_get_readable(frame, inode, this, local->readable,
                                 &event_generation, local->transaction.type);

    if (ret == -EIO) {
        /* No readable subvolume even after refresh ==> splitbrain.*/
        if (!priv->fav_child_policy) {
            err = EIO;
            goto refresh_done;
        }
        read_subvol = afr_sh_get_fav_by_policy(this, local->replies, inode,
                                               NULL);
        if (read_subvol == -1) {
            err = EIO;
            goto refresh_done;
        }

        heal_frame = afr_frame_create(this, NULL);
        if (!heal_frame) {
            err = EIO;
            goto refresh_done;
        }
        heal_local = heal_frame->local;
        heal_local->xdata_req = dict_new();
        if (!heal_local->xdata_req) {
            err = EIO;
            AFR_STACK_DESTROY(heal_frame);
            goto refresh_done;
        }
        heal_local->heal_frame = frame;
        ret = synctask_new(this->ctx->env, afr_fav_child_reset_sink_xattrs,
                           afr_fav_child_reset_sink_xattrs_cbk, heal_frame,
                           heal_frame);
        return 0;
    }

refresh_done:
    afr_local_replies_wipe(local, this->private);
    local->refreshfn(frame, this, err);

    return 0;
}

static int
afr_inode_refresh_done(call_frame_t *frame, xlator_t *this, int error)
{
    call_frame_t *heal_frame = NULL;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    gf_boolean_t start_heal = _gf_false;
    afr_local_t *heal_local = NULL;
    unsigned char *success_replies = NULL;
    int ret = 0;

    if (error != 0) {
        goto refresh_done;
    }

    local = frame->local;
    priv = this->private;
    success_replies = alloca0(priv->child_count);
    afr_fill_success_replies(local, priv, success_replies);

    if (priv->thin_arbiter_count && local->is_read_txn &&
        AFR_COUNT(success_replies, priv->child_count) != priv->child_count) {
        /* We need to query the good bricks and/or thin-arbiter.*/
        if (success_replies[0]) {
            local->read_txn_query_child = AFR_CHILD_ZERO;
        } else if (success_replies[1]) {
            local->read_txn_query_child = AFR_CHILD_ONE;
        }
        error = EINVAL;
        goto refresh_done;
    }

    if (!afr_has_quorum(success_replies, priv, frame)) {
        error = afr_final_errno(frame->local, this->private);
        if (!error)
            error = afr_quorum_errno(priv);
        goto refresh_done;
    }

    ret = afr_replies_interpret(local, this, local->refreshinode, &start_heal);

    if (ret && afr_selfheal_enabled(this) && start_heal) {
        heal_frame = afr_frame_create(this, NULL);
        if (!heal_frame)
            goto refresh_done;
        heal_local = heal_frame->local;
        heal_local->refreshinode = inode_ref(local->refreshinode);
        heal_local->heal_frame = heal_frame;
        if (!afr_throttled_selfheal(heal_frame, this)) {
            AFR_STACK_DESTROY(heal_frame);
            goto refresh_done;
        }
    }

refresh_done:
    afr_txn_refresh_done(frame, this, error);

    return 0;
}

static void
afr_inode_refresh_subvol_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                             int op_ret, int op_errno, struct iatt *buf,
                             dict_t *xdata, struct iatt *par)
{
    afr_local_t *local = NULL;
    int call_child = (long)cookie;
    int8_t need_heal = 1;
    int call_count = 0;
    int ret = 0;

    local = frame->local;
    local->replies[call_child].valid = 1;
    local->replies[call_child].op_ret = op_ret;
    local->replies[call_child].op_errno = op_errno;
    if (op_ret != -1) {
        local->replies[call_child].poststat = *buf;
        if (par)
            local->replies[call_child].postparent = *par;
        if (xdata)
            local->replies[call_child].xdata = dict_ref(xdata);
    }

    if (xdata) {
        ret = dict_get_int8(xdata, "link-count", &need_heal);
        if (ret) {
            gf_msg_debug(this->name, -ret, "Unable to get link count");
        }
    }

    local->replies[call_child].need_heal = need_heal;
    call_count = afr_frame_return(frame);
    if (call_count == 0) {
        afr_set_need_heal(this, local);
        ret = afr_inode_refresh_err(frame, this);
        if (ret) {
            gf_msg_debug(this->name, ret, "afr_inode_refresh_err failed");
        }
        afr_inode_refresh_done(frame, this, ret);
    }
}

static int
afr_inode_refresh_subvol_with_lookup_cbk(call_frame_t *frame, void *cookie,
                                         xlator_t *this, int op_ret,
                                         int op_errno, inode_t *inode,
                                         struct iatt *buf, dict_t *xdata,
                                         struct iatt *par)
{
    afr_inode_refresh_subvol_cbk(frame, cookie, this, op_ret, op_errno, buf,
                                 xdata, par);
    return 0;
}

static int
afr_inode_refresh_subvol_with_lookup(call_frame_t *frame, xlator_t *this, int i,
                                     inode_t *inode, uuid_t gfid, dict_t *xdata)
{
    loc_t loc = {
        0,
    };
    afr_private_t *priv = NULL;

    priv = this->private;

    loc.inode = inode;
    if (gf_uuid_is_null(inode->gfid) && gfid) {
        /* To handle setattr/setxattr on yet to be linked inode from
         * dht */
        gf_uuid_copy(loc.gfid, gfid);
    } else {
        gf_uuid_copy(loc.gfid, inode->gfid);
    }

    STACK_WIND_COOKIE(frame, afr_inode_refresh_subvol_with_lookup_cbk,
                      (void *)(long)i, priv->children[i],
                      priv->children[i]->fops->lookup, &loc, xdata);
    return 0;
}

int
afr_inode_refresh_subvol_with_fstat_cbk(call_frame_t *frame, void *cookie,
                                        xlator_t *this, int32_t op_ret,
                                        int32_t op_errno, struct iatt *buf,
                                        dict_t *xdata)
{
    afr_inode_refresh_subvol_cbk(frame, cookie, this, op_ret, op_errno, buf,
                                 xdata, NULL);
    return 0;
}

static int
afr_inode_refresh_subvol_with_fstat(call_frame_t *frame, xlator_t *this, int i,
                                    dict_t *xdata)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;

    priv = this->private;
    local = frame->local;

    STACK_WIND_COOKIE(frame, afr_inode_refresh_subvol_with_fstat_cbk,
                      (void *)(long)i, priv->children[i],
                      priv->children[i]->fops->fstat, local->fd, xdata);
    return 0;
}

static int
afr_inode_refresh_do(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int call_count = 0;
    int i = 0;
    int ret = 0;
    dict_t *xdata = NULL;
    afr_fd_ctx_t *fd_ctx = NULL;
    unsigned char *wind_subvols = NULL;

    priv = this->private;
    local = frame->local;
    wind_subvols = alloca0(priv->child_count);

    afr_local_replies_wipe(local, priv);

    if (local->fd) {
        fd_ctx = afr_fd_ctx_get(local->fd, this);
        if (!fd_ctx) {
            afr_inode_refresh_done(frame, this, EINVAL);
            return 0;
        }
    }

    xdata = dict_new();
    if (!xdata) {
        afr_inode_refresh_done(frame, this, ENOMEM);
        return 0;
    }

    ret = afr_xattr_req_prepare(this, xdata);
    if (ret != 0) {
        dict_unref(xdata);
        afr_inode_refresh_done(frame, this, -ret);
        return 0;
    }

    ret = dict_set_sizen_str_sizen(xdata, "link-count", GF_XATTROP_INDEX_COUNT);
    if (ret) {
        gf_msg_debug(this->name, -ret, "Unable to set link-count in dict ");
    }

    ret = dict_set_str_sizen(xdata, GLUSTERFS_INODELK_DOM_COUNT, this->name);
    if (ret) {
        gf_msg_debug(this->name, -ret,
                     "Unable to set inodelk-dom-count in dict ");
    }

    if (local->fd) {
        for (i = 0; i < priv->child_count; i++) {
            if (local->child_up[i] && fd_ctx->opened_on[i] == AFR_FD_OPENED)
                wind_subvols[i] = 1;
        }
    } else {
        memcpy(wind_subvols, local->child_up,
               sizeof(*local->child_up) * priv->child_count);
    }

    local->call_count = AFR_COUNT(wind_subvols, priv->child_count);

    call_count = local->call_count;
    if (!call_count) {
        dict_unref(xdata);
        if (local->fd && AFR_COUNT(local->child_up, priv->child_count))
            afr_inode_refresh_done(frame, this, EBADFD);
        else
            afr_inode_refresh_done(frame, this, ENOTCONN);
        return 0;
    }
    for (i = 0; i < priv->child_count; i++) {
        if (!wind_subvols[i])
            continue;

        if (local->fd)
            afr_inode_refresh_subvol_with_fstat(frame, this, i, xdata);
        else
            afr_inode_refresh_subvol_with_lookup(
                frame, this, i, local->refreshinode, local->refreshgfid, xdata);

        if (!--call_count)
            break;
    }

    dict_unref(xdata);

    return 0;
}

int
afr_inode_refresh(call_frame_t *frame, xlator_t *this, inode_t *inode,
                  uuid_t gfid, afr_inode_refresh_cbk_t refreshfn)
{
    afr_local_t *local = NULL;

    local = frame->local;

    local->refreshfn = refreshfn;

    if (local->refreshinode) {
        inode_unref(local->refreshinode);
        local->refreshinode = NULL;
    }

    local->refreshinode = inode_ref(inode);

    if (gfid)
        gf_uuid_copy(local->refreshgfid, gfid);
    else
        gf_uuid_clear(local->refreshgfid);

    afr_inode_refresh_do(frame, this);

    return 0;
}

int
afr_xattr_req_prepare(xlator_t *this, dict_t *xattr_req)
{
    int i = 0;
    afr_private_t *priv = NULL;
    int ret = 0;

    priv = this->private;

    for (i = 0; i < priv->child_count; i++) {
        ret = dict_set_uint64(xattr_req, priv->pending_key[i],
                              AFR_NUM_CHANGE_LOGS * sizeof(int));
        if (ret < 0)
            gf_msg(this->name, GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
                   "Unable to set dict value for %s", priv->pending_key[i]);
        /* 3 = data+metadata+entry */
    }
    ret = dict_set_uint64(xattr_req, AFR_DIRTY,
                          AFR_NUM_CHANGE_LOGS * sizeof(int));
    if (ret) {
        gf_msg_debug(this->name, -ret,
                     "failed to set dirty "
                     "query flag");
    }

    ret = dict_set_int32_sizen(xattr_req, "list-xattr", 1);
    if (ret) {
        gf_msg_debug(this->name, -ret, "Unable to set list-xattr in dict ");
    }

    return ret;
}

static int
afr_lookup_xattr_req_prepare(afr_local_t *local, xlator_t *this,
                             dict_t *xattr_req, loc_t *loc)
{
    int ret = -ENOMEM;

    if (!local->xattr_req)
        local->xattr_req = dict_new();

    if (!local->xattr_req)
        goto out;

    if (xattr_req && (xattr_req != local->xattr_req))
        dict_copy(xattr_req, local->xattr_req);

    ret = afr_xattr_req_prepare(this, local->xattr_req);

    ret = dict_set_uint64(local->xattr_req, GLUSTERFS_INODELK_COUNT, 0);
    if (ret < 0) {
        gf_msg(this->name, GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
               "%s: Unable to set dict value for %s", loc->path,
               GLUSTERFS_INODELK_COUNT);
    }
    ret = dict_set_uint64(local->xattr_req, GLUSTERFS_ENTRYLK_COUNT, 0);
    if (ret < 0) {
        gf_msg(this->name, GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
               "%s: Unable to set dict value for %s", loc->path,
               GLUSTERFS_ENTRYLK_COUNT);
    }

    ret = dict_set_uint32(local->xattr_req, GLUSTERFS_PARENT_ENTRYLK, 0);
    if (ret < 0) {
        gf_msg(this->name, GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
               "%s: Unable to set dict value for %s", loc->path,
               GLUSTERFS_PARENT_ENTRYLK);
    }

    ret = dict_set_sizen_str_sizen(local->xattr_req, "link-count",
                                   GF_XATTROP_INDEX_COUNT);
    if (ret) {
        gf_msg_debug(this->name, -ret, "Unable to set link-count in dict ");
    }

    ret = 0;
out:
    return ret;
}

static int
afr_least_pending_reads_child(afr_private_t *priv, unsigned char *readable)
{
    int i = 0;
    int child = -1;
    int64_t read_iter = -1;
    int64_t pending_read = -1;

    for (i = 0; i < priv->child_count; i++) {
        if (AFR_IS_ARBITER_BRICK(priv, i) || !readable[i])
            continue;
        read_iter = GF_ATOMIC_GET(priv->pending_reads[i]);
        if (child == -1 || read_iter < pending_read) {
            pending_read = read_iter;
            child = i;
        }
    }

    return child;
}

static int32_t
afr_least_latency_child(afr_private_t *priv, unsigned char *readable)
{
    int32_t i = 0;
    int child = -1;

    for (i = 0; i < priv->child_count; i++) {
        if (AFR_IS_ARBITER_BRICK(priv, i) || !readable[i] ||
            priv->child_latency[i] < 0)
            continue;

        if (child == -1 ||
            priv->child_latency[i] < priv->child_latency[child]) {
            child = i;
        }
    }
    return child;
}

static int32_t
afr_least_latency_times_pending_reads_child(afr_private_t *priv,
                                            unsigned char *readable)
{
    int32_t i = 0;
    int child = -1;
    int64_t pending_read = 0;
    int64_t latency = -1;
    int64_t least_latency = -1;

    for (i = 0; i < priv->child_count; i++) {
        if (AFR_IS_ARBITER_BRICK(priv, i) || !readable[i] ||
            priv->child_latency[i] < 0)
            continue;

        pending_read = GF_ATOMIC_GET(priv->pending_reads[i]);
        latency = (pending_read + 1) * priv->child_latency[i];

        if (child == -1 || latency < least_latency) {
            least_latency = latency;
            child = i;
        }
    }
    return child;
}

static int
afr_hash_child(afr_read_subvol_args_t *args, afr_private_t *priv,
               unsigned char *readable)
{
    uuid_t gfid_copy = {
        0,
    };
    pid_t pid;
    int child = -1;

    switch (priv->hash_mode) {
        case AFR_READ_POLICY_FIRST_UP:
            break;
        case AFR_READ_POLICY_GFID_HASH:
            gf_uuid_copy(gfid_copy, args->gfid);
            child = SuperFastHash((char *)gfid_copy, sizeof(gfid_copy)) %
                    priv->child_count;
            break;
        case AFR_READ_POLICY_GFID_PID_HASH:
            if (args->ia_type != IA_IFDIR) {
                /*
                 * Why getpid?  Because it's one of the cheapest calls
                 * available - faster than gethostname etc. - and
                 * returns a constant-length value that's sure to be
                 * shorter than a UUID. It's still very unlikely to be
                 * the same across clients, so it still provides good
                 * mixing.  We're not trying for perfection here. All we
                 * need is a low probability that multiple clients
                 * won't converge on the same subvolume.
                 */
                gf_uuid_copy(gfid_copy, args->gfid);
                pid = getpid();
                *(pid_t *)gfid_copy ^= pid;
            }
            child = SuperFastHash((char *)gfid_copy, sizeof(gfid_copy)) %
                    priv->child_count;
            break;
        case AFR_READ_POLICY_LESS_LOAD:
            child = afr_least_pending_reads_child(priv, readable);
            break;
        case AFR_READ_POLICY_LEAST_LATENCY:
            child = afr_least_latency_child(priv, readable);
            break;
        case AFR_READ_POLICY_LOAD_LATENCY_HYBRID:
            child = afr_least_latency_times_pending_reads_child(priv, readable);
            break;
    }

    return child;
}

int
afr_read_subvol_select_by_policy(inode_t *inode, xlator_t *this,
                                 unsigned char *readable,
                                 afr_read_subvol_args_t *args)
{
    int i = 0;
    int read_subvol = -1;
    afr_private_t *priv = NULL;
    afr_read_subvol_args_t local_args = {
        0,
    };

    priv = this->private;

    /* first preference - explicitly specified or local subvolume */
    if (priv->read_child >= 0 && readable[priv->read_child])
        return priv->read_child;

    if (inode_is_linked(inode)) {
        gf_uuid_copy(local_args.gfid, inode->gfid);
        local_args.ia_type = inode->ia_type;
    } else if (args) {
        local_args = *args;
    }

    /* second preference - use hashed mode */
    read_subvol = afr_hash_child(&local_args, priv, readable);
    if (read_subvol >= 0 && readable[read_subvol])
        return read_subvol;

    for (i = 0; i < priv->child_count; i++) {
        if (readable[i])
            return i;
    }

    /* no readable subvolumes, either split brain or all subvols down */

    return -1;
}

static int
afr_inode_read_subvol_type_get(inode_t *inode, xlator_t *this,
                               unsigned char *readable, int *event_p, int type)
{
    if (type == AFR_METADATA_TRANSACTION)
        return afr_inode_read_subvol_get(inode, this, 0, readable, event_p);
    else
        return afr_inode_read_subvol_get(inode, this, readable, 0, event_p);
}

static void
afr_readables_intersect_get(inode_t *inode, xlator_t *this, int *event,
                            unsigned char *intersection)
{
    afr_private_t *priv = NULL;
    unsigned char *data_readable = NULL;
    unsigned char *metadata_readable = NULL;
    unsigned char *intersect = NULL;

    priv = this->private;
    data_readable = alloca0(priv->child_count);
    metadata_readable = alloca0(priv->child_count);
    intersect = alloca0(priv->child_count);

    afr_inode_read_subvol_get(inode, this, data_readable, metadata_readable,
                              event);

    AFR_INTERSECT(intersect, data_readable, metadata_readable,
                  priv->child_count);
    if (intersection)
        memcpy(intersection, intersect,
               sizeof(*intersection) * priv->child_count);
}

int
afr_read_subvol_get(inode_t *inode, xlator_t *this, int *subvol_p,
                    unsigned char *readables, int *event_p,
                    afr_transaction_type type, afr_read_subvol_args_t *args)
{
    afr_private_t *priv = NULL;
    unsigned char *readable = NULL;
    unsigned char *intersection = NULL;
    int subvol = -1;
    int event = 0;

    priv = this->private;

    readable = alloca0(priv->child_count);
    intersection = alloca0(priv->child_count);

    afr_inode_read_subvol_type_get(inode, this, readable, &event, type);

    afr_readables_intersect_get(inode, this, &event, intersection);

    if (AFR_COUNT(intersection, priv->child_count) > 0)
        subvol = afr_read_subvol_select_by_policy(inode, this, intersection,
                                                  args);
    else
        subvol = afr_read_subvol_select_by_policy(inode, this, readable, args);
    if (subvol_p)
        *subvol_p = subvol;
    if (event_p)
        *event_p = event;
    if (readables)
        memcpy(readables, readable, sizeof(*readables) * priv->child_count);
    return subvol;
}

static void
afr_local_transaction_cleanup(afr_local_t *local, unsigned int child_count)
{
    int i = 0;

    afr_matrix_cleanup(local->pending, child_count);

    afr_lockees_cleanup(&local->internal_lock);

    GF_FREE(local->transaction.pre_op);

    GF_FREE(local->transaction.pre_op_sources);
    if (local->transaction.changelog_xdata) {
        for (i = 0; i < child_count; i++) {
            if (!local->transaction.changelog_xdata[i])
                continue;
            dict_unref(local->transaction.changelog_xdata[i]);
        }
        GF_FREE(local->transaction.changelog_xdata);
    }

    GF_FREE(local->transaction.failed_subvols);

    GF_FREE(local->transaction.basename);
    GF_FREE(local->transaction.new_basename);

    loc_wipe(&local->transaction.parent_loc);
    loc_wipe(&local->transaction.new_parent_loc);
}

void
afr_reply_wipe(struct afr_reply *reply)
{
    if (reply->xdata) {
        dict_unref(reply->xdata);
        reply->xdata = NULL;
    }

    if (reply->xattr) {
        dict_unref(reply->xattr);
        reply->xattr = NULL;
    }
}

void
afr_replies_wipe(struct afr_reply *replies, int count)
{
    int i = 0;

    for (i = 0; i < count; i++) {
        afr_reply_wipe(&replies[i]);
    }
}

void
afr_local_replies_wipe(afr_local_t *local, afr_private_t *priv)
{
    if (!local->replies)
        return;

    afr_replies_wipe(local->replies, priv->child_count);

    memset(local->replies, 0, sizeof(*local->replies) * priv->child_count);
}

static gf_boolean_t
afr_fop_lock_is_unlock(call_frame_t *frame)
{
    afr_local_t *local = frame->local;
    switch (local->op) {
        case GF_FOP_INODELK:
        case GF_FOP_FINODELK:
            if ((F_UNLCK == local->cont.inodelk.in_flock.l_type) &&
                (local->cont.inodelk.in_cmd == F_SETLKW ||
                 local->cont.inodelk.in_cmd == F_SETLK))
                return _gf_true;
            break;
        case GF_FOP_ENTRYLK:
        case GF_FOP_FENTRYLK:
            if (ENTRYLK_UNLOCK == local->cont.entrylk.in_cmd)
                return _gf_true;
            break;
        default:
            return _gf_false;
    }
    return _gf_false;
}

static gf_boolean_t
afr_lk_is_unlock(int32_t cmd, struct gf_flock *flock)
{
    switch (cmd) {
        case F_RESLK_UNLCK:
            return _gf_true;
            break;

#if F_SETLKW != F_SETLKW64
        case F_SETLKW64:
#endif
        case F_SETLKW:

#if F_SETLK != F_SETLK64
        case F_SETLK64:
#endif
        case F_SETLK:
            if (F_UNLCK == flock->l_type)
                return _gf_true;
            break;
        default:
            return _gf_false;
    }
    return _gf_false;
}

void
afr_handle_inconsistent_fop(call_frame_t *frame, int32_t *op_ret,
                            int32_t *op_errno)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;

    if (!frame || !frame->this || !frame->local || !frame->this->private)
        return;

    if (*op_ret < 0)
        return;

    /* Failing inodelk/entrylk/lk here is not a good idea because we
     * need to cleanup the locks on the other bricks if we choose to fail
     * the fop here. The brick may go down just after unwind happens as well
     * so anyways the fop will fail when the next fop is sent so leaving
     * it like this for now.*/
    local = frame->local;
    switch (local->op) {
        case GF_FOP_LOOKUP:
        case GF_FOP_INODELK:
        case GF_FOP_FINODELK:
        case GF_FOP_ENTRYLK:
        case GF_FOP_FENTRYLK:
        case GF_FOP_LK:
            return;
        default:
            break;
    }

    priv = frame->this->private;
    if (!priv->consistent_io)
        return;

    if (local->event_generation &&
        (local->event_generation != priv->event_generation))
        goto inconsistent;

    return;
inconsistent:
    *op_ret = -1;
    *op_errno = ENOTCONN;
}

void
afr_local_cleanup(afr_local_t *local, xlator_t *this)
{
    afr_private_t *priv = NULL;

    if (!local)
        return;
    priv = this->private;

    syncbarrier_destroy(&local->barrier);

    afr_local_transaction_cleanup(local, priv->child_count);

    loc_wipe(&local->loc);
    loc_wipe(&local->newloc);

    if (local->fd)
        fd_unref(local->fd);

    if (local->xattr_req)
        dict_unref(local->xattr_req);

    if (local->xattr_rsp)
        dict_unref(local->xattr_rsp);

    if (local->dict)
        dict_unref(local->dict);

    afr_local_replies_wipe(local, priv);
    GF_FREE(local->replies);

    GF_FREE(local->child_up);

    GF_FREE(local->read_attempted);

    GF_FREE(local->readable);
    GF_FREE(local->readable2);

    if (local->inode)
        inode_unref(local->inode);

    if (local->parent)
        inode_unref(local->parent);

    if (local->parent2)
        inode_unref(local->parent2);

    if (local->refreshinode)
        inode_unref(local->refreshinode);

    { /* getxattr */
        GF_FREE(local->cont.getxattr.name);
    }

    { /* lk */
        GF_FREE(local->cont.lk.locked_nodes);
        GF_FREE(local->cont.lk.dom_locked_nodes);
        GF_FREE(local->cont.lk.dom_lock_op_ret);
        GF_FREE(local->cont.lk.dom_lock_op_errno);
    }

    { /* create */
        if (local->cont.create.fd)
            fd_unref(local->cont.create.fd);
        if (local->cont.create.params)
            dict_unref(local->cont.create.params);
    }

    { /* mknod */
        if (local->cont.mknod.params)
            dict_unref(local->cont.mknod.params);
    }

    { /* mkdir */
        if (local->cont.mkdir.params)
            dict_unref(local->cont.mkdir.params);
    }

    { /* symlink */
        if (local->cont.symlink.params)
            dict_unref(local->cont.symlink.params);
    }

    { /* writev */
        GF_FREE(local->cont.writev.vector);
        if (local->cont.writev.iobref)
            iobref_unref(local->cont.writev.iobref);
    }

    { /* setxattr */
        if (local->cont.setxattr.dict)
            dict_unref(local->cont.setxattr.dict);
    }

    { /* fsetxattr */
        if (local->cont.fsetxattr.dict)
            dict_unref(local->cont.fsetxattr.dict);
    }

    { /* removexattr */
        GF_FREE(local->cont.removexattr.name);
    }
    { /* xattrop */
        if (local->cont.xattrop.xattr)
            dict_unref(local->cont.xattrop.xattr);
    }
    { /* symlink */
        GF_FREE(local->cont.symlink.linkpath);
    }

    { /* opendir */
        GF_FREE(local->cont.opendir.checksum);
    }

    { /* open */
        if (local->cont.open.fd)
            fd_unref(local->cont.open.fd);
    }

    { /* readdirp */
        if (local->cont.readdir.dict)
            dict_unref(local->cont.readdir.dict);
    }

    { /* inodelk */
        GF_FREE(local->cont.inodelk.volume);
        if (local->cont.inodelk.xdata)
            dict_unref(local->cont.inodelk.xdata);
    }

    { /* entrylk */
        GF_FREE(local->cont.entrylk.volume);
        GF_FREE(local->cont.entrylk.basename);
        if (local->cont.entrylk.xdata)
            dict_unref(local->cont.entrylk.xdata);
    }

    GF_FREE(local->need_open);

    if (local->xdata_req)
        dict_unref(local->xdata_req);

    if (local->xdata_rsp)
        dict_unref(local->xdata_rsp);
}

int
afr_frame_return(call_frame_t *frame)
{
    afr_local_t *local = NULL;
    int call_count = 0;

    local = frame->local;

    LOCK(&frame->lock);
    {
        call_count = --local->call_count;
    }
    UNLOCK(&frame->lock);

    return call_count;
}

static char *afr_ignore_xattrs[] = {GF_SELINUX_XATTR_KEY, QUOTA_SIZE_KEY,
                                    SQUOTA_SIZE_KEY, SQUOTA_LIMIT_KEY, NULL};

gf_boolean_t
afr_is_xattr_ignorable(char *key)
{
    int i = 0;

    if (!strncmp(key, AFR_XATTR_PREFIX, SLEN(AFR_XATTR_PREFIX)))
        return _gf_true;
    for (i = 0; afr_ignore_xattrs[i]; i++) {
        if (!strcmp(key, afr_ignore_xattrs[i]))
            return _gf_true;
    }
    return _gf_false;
}

static gf_boolean_t
afr_xattr_match_needed(dict_t *this, char *key1, data_t *value1, void *data)
{
    gf_boolean_t *is_arbiter = NULL;

    is_arbiter = data;
    /* Ignore all non-disk (i.e. virtual) xattrs right away. */
    if (!gf_is_valid_xattr_namespace(key1))
        return _gf_false;

    if (is_arbiter && *is_arbiter && !strcmp(key1, GF_XATTR_MDATA_KEY)) {
        return _gf_false;
    }
    /* Ignore on-disk xattrs that AFR doesn't need to heal. */
    if (!afr_is_xattr_ignorable(key1))
        return _gf_true;

    return _gf_false;
}

gf_boolean_t
afr_xattrs_are_equal(dict_t *dict1, dict_t *dict2, gf_boolean_t is_arbiter)
{
    return are_dicts_equal(dict1, dict2, afr_xattr_match_needed, &is_arbiter,
                           NULL);
}

static int
afr_get_parent_read_subvol(xlator_t *this, inode_t *parent,
                           struct afr_reply *replies, unsigned char *readable)
{
    int i = 0;
    int par_read_subvol = -1;
    int par_read_subvol_iter = -1;
    afr_private_t *priv = NULL;

    priv = this->private;

    if (parent)
        par_read_subvol = afr_data_subvol_get(parent, this, NULL, NULL, NULL,
                                              NULL);

    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid)
            continue;

        if (replies[i].op_ret < 0)
            continue;

        if (par_read_subvol_iter == -1) {
            par_read_subvol_iter = i;
            continue;
        }

        if ((par_read_subvol_iter != par_read_subvol) && readable[i])
            par_read_subvol_iter = i;

        if (i == par_read_subvol)
            par_read_subvol_iter = i;
    }
    /* At the end of the for-loop, the only reason why @par_read_subvol_iter
     * could be -1 is when this LOOKUP has failed on all sub-volumes.
     * So it is okay to send an arbitrary subvolume (0 in this case)
     * as parent read subvol.
     */
    if (par_read_subvol_iter == -1)
        par_read_subvol_iter = 0;

    return par_read_subvol_iter;
}

static int
afr_read_subvol_decide(inode_t *inode, xlator_t *this,
                       afr_read_subvol_args_t *args, unsigned char *readable)
{
    int event = 0;
    afr_private_t *priv = NULL;
    unsigned char *intersection = NULL;

    priv = this->private;
    intersection = alloca0(priv->child_count);

    afr_readables_intersect_get(inode, this, &event, intersection);

    if (AFR_COUNT(intersection, priv->child_count) <= 0) {
        /* TODO: If we have one brick with valid data_readable and
         * another with metadata_readable, try to send an iatt with
         * valid bits from both.*/
        return -1;
    }

    memcpy(readable, intersection, sizeof(*readable) * priv->child_count);

    return afr_read_subvol_select_by_policy(inode, this, intersection, args);
}

static inline int
afr_first_up_child(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int i = 0;

    local = frame->local;
    priv = this->private;

    for (i = 0; i < priv->child_count; i++)
        if (local->replies[i].valid && local->replies[i].op_ret == 0)
            return i;
    return -1;
}

static void
afr_attempt_readsubvol_set(call_frame_t *frame, xlator_t *this,
                           unsigned char *success_replies,
                           unsigned char *data_readable, int *read_subvol)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int spb_subvol = -1;
    int child_count = -1;

    if (*read_subvol != -1)
        return;

    priv = this->private;
    local = frame->local;
    child_count = priv->child_count;

    afr_split_brain_read_subvol_get(local->inode, this, frame, &spb_subvol);
    if ((spb_subvol >= 0) &&
        (AFR_COUNT(success_replies, child_count) == child_count)) {
        *read_subvol = spb_subvol;
    } else if (!priv->quorum_count ||
               frame->root->pid == GF_CLIENT_PID_GLFS_HEAL) {
        *read_subvol = afr_first_up_child(frame, this);
    } else if (priv->quorum_count &&
               afr_has_quorum(data_readable, priv, NULL)) {
        /* read_subvol is guaranteed to be valid if we hit this path. */
        *read_subvol = afr_first_up_child(frame, this);
    } else {
        /* If quorum is enabled and we do not have a
           readable yet, it means all good copies are down.
        */
        local->op_ret = -1;
        local->op_errno = ENOTCONN;
        gf_msg(this->name, GF_LOG_WARNING, 0, AFR_MSG_READ_SUBVOL_ERROR,
               "no read "
               "subvols for %s",
               local->loc.path);
    }
    if (*read_subvol >= 0)
        dict_del_sizen(local->replies[*read_subvol].xdata, GF_CONTENT_KEY);
}

static void
afr_lookup_done(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int i = -1;
    int op_errno = 0;
    int read_subvol = 0;
    int par_read_subvol = 0;
    int ret = -1;
    unsigned char *readable = NULL;
    unsigned char *success_replies = NULL;
    int event = 0;
    struct afr_reply *replies = NULL;
    uuid_t read_gfid = {
        0,
    };
    gf_boolean_t locked_entry = _gf_false;
    gf_boolean_t in_flight_create = _gf_false;
    gf_boolean_t can_interpret = _gf_true;
    inode_t *parent = NULL;
    ia_type_t ia_type = IA_INVAL;
    afr_read_subvol_args_t args = {
        0,
    };
    char *gfid_heal_msg = NULL;

    priv = this->private;
    local = frame->local;
    replies = local->replies;
    parent = local->loc.parent;

    locked_entry = afr_is_possibly_under_txn(AFR_ENTRY_TRANSACTION, local,
                                             priv->child_count);

    readable = alloca0(priv->child_count);
    success_replies = alloca0(priv->child_count);

    afr_inode_read_subvol_get(parent, this, readable, NULL, &event);
    par_read_subvol = afr_get_parent_read_subvol(this, parent, replies,
                                                 readable);

    /* First, check if we have a gfid-change from somewhere,
       If so, propagate that so that a fresh lookup can be
       issued
    */
    if (local->cont.lookup.needs_fresh_lookup) {
        local->op_ret = -1;
        local->op_errno = ESTALE;
        goto error;
    }

    op_errno = afr_final_errno(frame->local, this->private);
    local->op_errno = op_errno;

    read_subvol = -1;
    afr_fill_success_replies(local, priv, success_replies);

    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid)
            continue;

        if (replies[i].op_ret == -1) {
            if (locked_entry && replies[i].op_errno == ENOENT) {
                in_flight_create = _gf_true;
            }
            continue;
        }

        if (read_subvol == -1 || !readable[read_subvol]) {
            read_subvol = i;
            gf_uuid_copy(read_gfid, replies[i].poststat.ia_gfid);
            ia_type = replies[i].poststat.ia_type;
            local->op_ret = 0;
        }
    }

    if (in_flight_create && !afr_has_quorum(success_replies, priv, NULL)) {
        local->op_ret = -1;
        local->op_errno = ENOENT;
        goto error;
    }

    if (read_subvol == -1)
        goto error;
    /* We now have a read_subvol, which is readable[] (if there
       were any). Next we look for GFID mismatches. We don't
       consider a GFID mismatch as an error if read_subvol is
       readable[] but the mismatching GFID subvol is not.
    */
    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid || replies[i].op_ret == -1) {
            continue;
        }

        if (!gf_uuid_compare(replies[i].poststat.ia_gfid, read_gfid))
            continue;

        can_interpret = _gf_false;

        if (locked_entry)
            continue;

        /* Now GFIDs mismatch. It's OK as long as this subvol
           is not readable[] but read_subvol is */
        if (readable[read_subvol] && !readable[i])
            continue;

        /* If we were called from glfsheal and there is still a gfid
         * mismatch, succeed the lookup and let glfsheal print the
         * response via gfid-heal-msg.*/
        if (!dict_get_str_sizen(local->xattr_rsp, "gfid-heal-msg",
                                &gfid_heal_msg))
            goto cant_interpret;

        /* LOG ERROR */
        local->op_ret = -1;
        local->op_errno = EIO;
        goto error;
    }

    /* Forth, for the finalized GFID, pick the best subvolume
       to return stats from.
    */
    read_subvol = -1;
    memset(readable, 0, sizeof(*readable) * priv->child_count);
    if (can_interpret) {
        if (!afr_has_quorum(success_replies, priv, NULL))
            goto cant_interpret;
        /* It is safe to call afr_replies_interpret() because we have
           a response from all the UP subvolumes and all of them resolved
           to the same GFID
        */
        gf_uuid_copy(args.gfid, read_gfid);
        args.ia_type = ia_type;
        ret = afr_replies_interpret(local, this, local->inode, NULL);
        read_subvol = afr_read_subvol_decide(local->inode, this, &args,
                                             readable);
        if (read_subvol == -1)
            goto cant_interpret;
        if (ret) {
            afr_inode_need_refresh_set(local->inode, this);
            dict_del_sizen(local->replies[read_subvol].xdata, GF_CONTENT_KEY);
        }
    } else {
    cant_interpret:
        afr_attempt_readsubvol_set(frame, this, success_replies, readable,
                                   &read_subvol);
        if (read_subvol == -1) {
            goto error;
        }
    }

    afr_handle_quota_size(local, this);

    afr_set_need_heal(this, local);
    if (AFR_IS_ARBITER_BRICK(priv, read_subvol) && local->op_ret == 0) {
        local->op_ret = -1;
        local->op_errno = ENOTCONN;
        gf_msg_debug(this->name, 0,
                     "Arbiter cannot be a read subvol "
                     "for %s",
                     local->loc.path);
        goto error;
    }

    ret = dict_get_str_sizen(local->xattr_rsp, "gfid-heal-msg", &gfid_heal_msg);
    if (!ret) {
        ret = dict_set_str_sizen(local->replies[read_subvol].xdata,
                                 "gfid-heal-msg", gfid_heal_msg);
        if (ret) {
            gf_msg(this->name, GF_LOG_ERROR, 0, AFR_MSG_DICT_SET_FAILED,
                   "Error setting gfid-heal-msg dict");
            local->op_ret = -1;
            local->op_errno = ENOMEM;
        }
    }

    AFR_STACK_UNWIND(lookup, frame, local->op_ret, local->op_errno,
                     local->inode, &local->replies[read_subvol].poststat,
                     local->replies[read_subvol].xdata,
                     &local->replies[par_read_subvol].postparent);
    return;

error:
    AFR_STACK_UNWIND(lookup, frame, local->op_ret, local->op_errno, NULL, NULL,
                     NULL, NULL);
}

/*
 * During a lookup, some errors are more "important" than
 * others in that they must be given higher priority while
 * returning to the user.
 *
 * The hierarchy is ENODATA > ENOENT > ESTALE > ENOSPC others
 */

int
afr_higher_errno(int32_t old_errno, int32_t new_errno)
{
    if (old_errno == ENODATA || new_errno == ENODATA)
        return ENODATA;
    if (old_errno == ENOENT || new_errno == ENOENT)
        return ENOENT;
    if (old_errno == ESTALE || new_errno == ESTALE)
        return ESTALE;
    if (old_errno == ENOSPC || new_errno == ENOSPC)
        return ENOSPC;

    return new_errno;
}

int
afr_final_errno(afr_local_t *local, afr_private_t *priv)
{
    int i = 0;
    int op_errno = 0;
    int tmp_errno = 0;

    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid)
            continue;
        if (local->replies[i].op_ret >= 0)
            continue;
        tmp_errno = local->replies[i].op_errno;
        op_errno = afr_higher_errno(op_errno, tmp_errno);
    }

    return op_errno;
}

static int32_t
afr_local_discovery_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, dict_t *dict,
                        dict_t *xdata)
{
    int ret = 0;
    char *pathinfo = NULL;
    gf_boolean_t is_local = _gf_false;
    afr_private_t *priv = NULL;
    int32_t child_index = -1;

    if (op_ret != 0) {
        goto out;
    }

    priv = this->private;
    child_index = (int32_t)(long)cookie;

    ret = dict_get_str_sizen(dict, GF_XATTR_PATHINFO_KEY, &pathinfo);
    if (ret != 0) {
        goto out;
    }

    ret = glusterfs_is_local_pathinfo(pathinfo, &is_local);
    if (ret) {
        goto out;
    }

    /*
     * Note that one local subvolume will override another here.  The only
     * way to avoid that would be to retain extra information about whether
     * the previous read_child is local, and it's just not worth it.  Even
     * the slowest local subvolume is far preferable to a remote one.
     */
    if (is_local) {
        priv->local[child_index] = 1;
        /* Don't set arbiter as read child. */
        if (AFR_IS_ARBITER_BRICK(priv, child_index))
            goto out;
        gf_msg(this->name, GF_LOG_INFO, 0, AFR_MSG_LOCAL_CHILD,
               "selecting local read_child %s",
               priv->children[child_index]->name);

        priv->read_child = child_index;
    }
out:
    STACK_DESTROY(frame->root);
    return 0;
}

static void
afr_attempt_local_discovery(xlator_t *this, int32_t child_index)
{
    call_frame_t *newframe = NULL;
    loc_t tmploc = {
        0,
    };
    afr_private_t *priv = this->private;

    newframe = create_frame(this, this->ctx->pool);
    if (!newframe) {
        return;
    }

    tmploc.gfid[sizeof(tmploc.gfid) - 1] = 1;
    STACK_WIND_COOKIE(newframe, afr_local_discovery_cbk,
                      (void *)(long)child_index, priv->children[child_index],
                      priv->children[child_index]->fops->getxattr, &tmploc,
                      GF_XATTR_PATHINFO_KEY, NULL);
}

static int
afr_lookup_sh_metadata_wrap(void *opaque)
{
    call_frame_t *frame = opaque;
    afr_local_t *local = NULL;
    xlator_t *this = NULL;
    inode_t *inode = NULL;
    afr_private_t *priv = NULL;
    struct afr_reply *replies = NULL;
    int i = 0, first = -1;
    int ret = -1;
    dict_t *dict = NULL;

    local = frame->local;
    this = frame->this;
    priv = this->private;
    replies = local->replies;

    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid || replies[i].op_ret == -1)
            continue;
        first = i;
        break;
    }
    if (first == -1)
        goto out;

    if (afr_selfheal_metadata_by_stbuf(this, &replies[first].poststat))
        goto out;

    afr_local_replies_wipe(local, this->private);

    dict = dict_new();
    if (!dict)
        goto out;
    if (local->xattr_req) {
        dict_copy(local->xattr_req, dict);
    }

    ret = dict_set_sizen_str_sizen(dict, "link-count", GF_XATTROP_INDEX_COUNT);
    if (ret) {
        gf_msg_debug(this->name, -ret, "Unable to set link-count in dict ");
    }

    if (loc_is_nameless(&local->loc)) {
        ret = afr_selfheal_unlocked_discover_on(frame, local->inode,
                                                local->loc.gfid, local->replies,
                                                local->child_up, dict);
    } else {
        inode = afr_selfheal_unlocked_lookup_on(frame, local->loc.parent,
                                                local->loc.name, local->replies,
                                                local->child_up, dict);
    }
    if (inode)
        inode_unref(inode);
out:
    if (loc_is_nameless(&local->loc))
        afr_discover_done(frame, this);
    else
        afr_lookup_done(frame, this);

    if (dict)
        dict_unref(dict);

    return 0;
}

gf_boolean_t
afr_is_pending_set(xlator_t *this, dict_t *xdata, int type)
{
    int idx = -1;
    afr_private_t *priv = NULL;
    void *pending_raw = NULL;
    int i = 0;

    priv = this->private;
    idx = afr_index_for_transaction_type(type);

    if (dict_get_ptr(xdata, AFR_DIRTY, &pending_raw) == 0) {
        if (pending_raw) {
            if (*((int *)pending_raw + idx))
                return _gf_true;
        }
    }

    for (i = 0; i < priv->child_count; i++) {
        if (dict_get_ptr(xdata, priv->pending_key[i], &pending_raw))
            continue;
        if (!pending_raw)
            continue;
        if (*((int *)pending_raw + idx))
            return _gf_true;
    }

    return _gf_false;
}

static gf_boolean_t
afr_can_start_metadata_self_heal(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    struct afr_reply *replies = NULL;
    int i = 0, first = -1;
    gf_boolean_t start = _gf_false;
    struct iatt stbuf = {
        0,
    };

    local = frame->local;
    replies = local->replies;
    priv = this->private;

    if (!priv->metadata_self_heal)
        return _gf_false;

    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid || replies[i].op_ret == -1)
            continue;
        if (first == -1) {
            first = i;
            stbuf = replies[i].poststat;
            continue;
        }

        if (afr_is_pending_set(this, replies[i].xdata,
                               AFR_METADATA_TRANSACTION)) {
            /* Let shd do the heal so that lookup is not blocked
             * on getting metadata lock/doing the heal */
            start = _gf_false;
            break;
        }

        if (gf_uuid_compare(stbuf.ia_gfid, replies[i].poststat.ia_gfid)) {
            start = _gf_false;
            break;
        }
        if (!IA_EQUAL(stbuf, replies[i].poststat, type)) {
            start = _gf_false;
            break;
        }

        /*Check if iattrs need heal*/
        if ((!IA_EQUAL(stbuf, replies[i].poststat, uid)) ||
            (!IA_EQUAL(stbuf, replies[i].poststat, gid)) ||
            (!IA_EQUAL(stbuf, replies[i].poststat, prot))) {
            start = _gf_true;
            continue;
        }

        /*Check if xattrs need heal*/
        if (!afr_xattrs_are_equal(
                replies[first].xdata, replies[i].xdata,
                AFR_IS_ARBITER_BRICK(priv, i) ? _gf_true : _gf_false))
            start = _gf_true;
    }

    return start;
}

int
afr_lookup_metadata_heal_check(call_frame_t *frame, xlator_t *this)

{
    call_frame_t *heal = NULL;
    afr_local_t *local = NULL;
    int ret = 0;

    local = frame->local;
    if (!afr_can_start_metadata_self_heal(frame, this))
        goto out;

    heal = afr_frame_create(this, &ret);
    if (!heal) {
        ret = -ret;
        goto out;
    }

    ret = synctask_new(this->ctx->env, afr_lookup_sh_metadata_wrap,
                       afr_refresh_selfheal_done, heal, frame);
    if (ret)
        goto out;
    return ret;
out:
    if (loc_is_nameless(&local->loc))
        afr_discover_done(frame, this);
    else
        afr_lookup_done(frame, this);
    if (heal)
        AFR_STACK_DESTROY(heal);
    return ret;
}

int
afr_lookup_selfheal_wrap(void *opaque)
{
    int ret = 0;
    call_frame_t *frame = opaque;
    afr_local_t *local = NULL;
    xlator_t *this = NULL;
    inode_t *inode = NULL;
    uuid_t pargfid = {
        0,
    };

    local = frame->local;
    this = frame->this;
    loc_pargfid(&local->loc, pargfid);
    if (!local->xattr_rsp)
        local->xattr_rsp = dict_new();

    ret = afr_selfheal_name(frame->this, pargfid, local->loc.name,
                            &local->cont.lookup.gfid_req, local->xattr_req,
                            local->xattr_rsp);
    if (ret == -EIO)
        goto unwind;

    afr_local_replies_wipe(local, this->private);

    inode = afr_selfheal_unlocked_lookup_on(frame, local->loc.parent,
                                            local->loc.name, local->replies,
                                            local->child_up, local->xattr_req);
    if (inode)
        inode_unref(inode);

    afr_lookup_metadata_heal_check(frame, this);
    return 0;

unwind:
    AFR_STACK_UNWIND(lookup, frame, -1, EIO, NULL, NULL, local->xattr_rsp,
                     NULL);
    return 0;
}

static int
afr_lookup_entry_heal(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    call_frame_t *heal = NULL;
    int i = 0, first = -1;
    gf_boolean_t name_state_mismatch = _gf_false;
    struct afr_reply *replies = NULL;
    int ret = 0;
    unsigned char *par_readables = NULL;
    unsigned char *success = NULL;
    int32_t op_errno = 0;
    uuid_t gfid = {0};

    local = frame->local;
    replies = local->replies;
    priv = this->private;
    par_readables = alloca0(priv->child_count);
    success = alloca0(priv->child_count);

    ret = afr_inode_read_subvol_get(local->loc.parent, this, par_readables,
                                    NULL, NULL);
    if (ret < 0 || AFR_COUNT(par_readables, priv->child_count) == 0) {
        /* In this case set par_readables to all 1 so that name_heal
         * need checks at the end of this function will flag missing
         * entry when name state mismatches*/
        memset(par_readables, 1, priv->child_count);
    }

    for (i = 0; i < priv->child_count; i++) {
        if (!replies[i].valid)
            continue;

        if (replies[i].op_ret == 0) {
            if (gf_uuid_is_null(gfid)) {
                gf_uuid_copy(gfid, replies[i].poststat.ia_gfid);
            }
            success[i] = 1;
        } else {
            if ((replies[i].op_errno != ENOTCONN) &&
                (replies[i].op_errno != ENOENT) &&
                (replies[i].op_errno != ESTALE)) {
                op_errno = replies[i].op_errno;
            }
        }

        /*gfid is missing, needs heal*/
        if ((replies[i].op_ret == -1) && (replies[i].op_errno == ENODATA)) {
            goto name_heal;
        }

        if (first == -1) {
            first = i;
            continue;
        }

        if (replies[i].op_ret != replies[first].op_ret) {
            name_state_mismatch = _gf_true;
        }

        if (replies[i].op_ret == 0) {
            /* Rename after this lookup may succeed if we don't do
             * a name-heal and the destination may not have pending xattrs
             * to indicate which name is good and which is bad so always do
             * this heal*/
            if (gf_uuid_compare(replies[i].poststat.ia_gfid, gfid)) {
                goto name_heal;
            }
        }
    }

    if (name_state_mismatch) {
        if (!priv->quorum_count)
            goto name_heal;
        if (!afr_has_quorum(success, priv, NULL))
            goto name_heal;
        if (op_errno)
            goto name_heal;
        for (i = 0; i < priv->child_count; i++) {
            if (!replies[i].valid)
                continue;
            if (par_readables[i] && replies[i].op_ret < 0 &&
                replies[i].op_errno != ENOTCONN) {
                goto name_heal;
            }
        }
    }

    goto metadata_heal;

name_heal:
    heal = afr_frame_create(this, NULL);
    if (!heal)
        goto metadata_heal;

    ret = synctask_new(this->ctx->env, afr_lookup_selfheal_wrap,
                       afr_refresh_selfheal_done, heal, frame);
    if (ret) {
        AFR_STACK_DESTROY(heal);
        goto metadata_heal;
    }
    return ret;

metadata_heal:
    ret = afr_lookup_metadata_heal_check(frame, this);

    return ret;
}

int
afr_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
               int op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata,
               struct iatt *postparent)
{
    afr_local_t *local = NULL;
    int call_count = -1;
    int child_index = -1;
    GF_UNUSED int ret = 0;
    int8_t need_heal = 1;

    child_index = (long)cookie;

    local = frame->local;

    local->replies[child_index].valid = 1;
    local->replies[child_index].op_ret = op_ret;
    local->replies[child_index].op_errno = op_errno;
    /*
     * On revalidate lookup if the gfid-changed, afr should unwind the fop
     * with ESTALE so that a fresh lookup will be sent by the top xlator.
     * So remember it.
     */
    if (xdata && dict_get_sizen(xdata, "gfid-changed"))
        local->cont.lookup.needs_fresh_lookup = _gf_true;

    if (xdata) {
        ret = dict_get_int8(xdata, "link-count", &need_heal);
        local->replies[child_index].need_heal = need_heal;
    } else {
        local->replies[child_index].need_heal = need_heal;
    }
    if (op_ret != -1) {
        local->replies[child_index].poststat = *buf;
        local->replies[child_index].postparent = *postparent;
        if (xdata)
            local->replies[child_index].xdata = dict_ref(xdata);
    }

    call_count = afr_frame_return(frame);
    if (call_count == 0) {
        afr_set_need_heal(this, local);
        afr_lookup_entry_heal(frame, this);
    }

    return 0;
}

static void
afr_discover_unwind(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int read_subvol = -1;
    int ret = 0;
    unsigned char *data_readable = NULL;
    unsigned char *success_replies = NULL;

    priv = this->private;
    local = frame->local;
    data_readable = alloca0(priv->child_count);
    success_replies = alloca0(priv->child_count);

    afr_fill_success_replies(local, priv, success_replies);
    if (AFR_COUNT(success_replies, priv->child_count) > 0)
        local->op_ret = 0;

    if (local->op_ret < 0) {
        local->op_ret = -1;
        local->op_errno = afr_final_errno(frame->local, this->private);
        goto error;
    }

    if (!afr_has_quorum(success_replies, priv, frame))
        goto unwind;

    ret = afr_replies_interpret(local, this, local->inode, NULL);
    if (ret) {
        afr_inode_need_refresh_set(local->inode, this);
    }

    read_subvol = afr_read_subvol_decide(local->inode, this, NULL,
                                         data_readable);

unwind:
    afr_attempt_readsubvol_set(frame, this, success_replies, data_readable,
                               &read_subvol);
    if (read_subvol == -1)
        goto error;

    if (AFR_IS_ARBITER_BRICK(priv, read_subvol) && local->op_ret == 0) {
        local->op_ret = -1;
        local->op_errno = ENOTCONN;
        gf_msg_debug(this->name, 0,
                     "Arbiter cannot be a read subvol "
                     "for %s",
                     local->loc.path);
    }

    AFR_STACK_UNWIND(lookup, frame, local->op_ret, local->op_errno,
                     local->inode, &local->replies[read_subvol].poststat,
                     local->replies[read_subvol].xdata,
                     &local->replies[read_subvol].postparent);
    return;

error:
    AFR_STACK_UNWIND(lookup, frame, local->op_ret, local->op_errno, NULL, NULL,
                     NULL, NULL);
}

static int
afr_ta_id_file_check(void *opaque)
{
    afr_private_t *priv = NULL;
    xlator_t *this = NULL;
    loc_t loc = {
        0,
    };
    struct iatt stbuf = {
        0,
    };
    dict_t *dict = NULL;
    uuid_t gfid = {
        0,
    };
    fd_t *fd = NULL;
    int ret = 0;

    this = opaque;
    priv = this->private;

    ret = afr_fill_ta_loc(this, &loc, _gf_false);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
               "Failed to populate thin-arbiter loc for: %s.", loc.name);
        goto out;
    }

    ret = syncop_lookup(priv->children[THIN_ARBITER_BRICK_INDEX], &loc, &stbuf,
                        0, 0, 0);
    if (ret == 0) {
        goto out;
    } else if (ret == -ENOENT) {
        fd = fd_create(loc.inode, getpid());
        if (!fd)
            goto out;
        dict = dict_new();
        if (!dict)
            goto out;
        gf_uuid_generate(gfid);
        ret = dict_set_gfuuid(dict, "gfid-req", gfid, true);
        ret = syncop_create(priv->children[THIN_ARBITER_BRICK_INDEX], &loc,
                            O_RDWR, 0664, fd, &stbuf, dict, NULL);
    }

out:
    if (ret == 0) {
        gf_uuid_copy(priv->ta_gfid, stbuf.ia_gfid);
    } else {
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
               "Failed to lookup/create thin-arbiter id file.");
    }
    if (dict)
        dict_unref(dict);
    if (fd)
        fd_unref(fd);
    loc_wipe(&loc);

    return 0;
}

static int
afr_ta_id_file_check_cbk(int ret, call_frame_t *ta_frame, void *opaque)
{
    return 0;
}

static void
afr_discover_done(call_frame_t *frame, xlator_t *this)
{
    int ret = 0;
    afr_private_t *priv = NULL;

    priv = this->private;
    if (!priv->thin_arbiter_count)
        goto unwind;
    if (!gf_uuid_is_null(priv->ta_gfid))
        goto unwind;

    ret = synctask_new(this->ctx->env, afr_ta_id_file_check,
                       afr_ta_id_file_check_cbk, NULL, this);
    if (ret)
        goto unwind;
unwind:
    afr_discover_unwind(frame, this);
}

int
afr_discover_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
                 int op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata,
                 struct iatt *postparent)
{
    afr_local_t *local = NULL;
    int call_count = -1;
    int child_index = -1;
    GF_UNUSED int ret = 0;
    int8_t need_heal = 1;

    child_index = (long)cookie;

    local = frame->local;

    local->replies[child_index].valid = 1;
    local->replies[child_index].op_ret = op_ret;
    local->replies[child_index].op_errno = op_errno;
    if (op_ret != -1) {
        local->replies[child_index].poststat = *buf;
        local->replies[child_index].postparent = *postparent;
        if (xdata)
            local->replies[child_index].xdata = dict_ref(xdata);
    }

    if (local->do_discovery && (op_ret == 0))
        afr_attempt_local_discovery(this, child_index);

    if (xdata) {
        ret = dict_get_int8(xdata, "link-count", &need_heal);
        local->replies[child_index].need_heal = need_heal;
    } else {
        local->replies[child_index].need_heal = need_heal;
    }

    call_count = afr_frame_return(frame);
    if (call_count == 0) {
        afr_set_need_heal(this, local);
        afr_lookup_metadata_heal_check(frame, this);
    }

    return 0;
}

int
afr_discover_do(call_frame_t *frame, xlator_t *this, int err)
{
    int ret = 0;
    int i = 0;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int call_count = 0;

    local = frame->local;
    priv = this->private;

    if (err) {
        local->op_errno = err;
        goto out;
    }

    call_count = local->call_count = AFR_COUNT(local->child_up,
                                               priv->child_count);

    ret = afr_lookup_xattr_req_prepare(local, this, local->xattr_req,
                                       &local->loc);
    if (ret) {
        local->op_errno = -ret;
        goto out;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            STACK_WIND_COOKIE(
                frame, afr_discover_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->lookup, &local->loc, local->xattr_req);
            if (!--call_count)
                break;
        }
    }

    return 0;
out:
    AFR_STACK_UNWIND(lookup, frame, -1, local->op_errno, 0, 0, 0, 0);
    return 0;
}

int
afr_discover(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr_req)
{
    int op_errno = ENOMEM;
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int event = 0;

    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    if (!local->call_count) {
        op_errno = ENOTCONN;
        goto out;
    }

    if (__is_root_gfid(loc->inode->gfid)) {
        if (priv->choose_local && !priv->did_discovery) {
            /* Logic to detect which subvolumes of AFR are
               local, in order to prefer them for reads
            */
            local->do_discovery = _gf_true;
            priv->did_discovery = _gf_true;
        }
    }

    local->op = GF_FOP_LOOKUP;

    loc_copy(&local->loc, loc);

    local->inode = inode_ref(loc->inode);

    if (xattr_req) {
        /* If xattr_req was null, afr_lookup_xattr_req_prepare() will
           allocate one for us */
        local->xattr_req = dict_copy_with_ref(xattr_req, NULL);
        if (!local->xattr_req) {
            op_errno = ENOMEM;
            goto out;
        }
    }

    if (gf_uuid_is_null(loc->inode->gfid)) {
        afr_discover_do(frame, this, 0);
        return 0;
    }

    afr_read_subvol_get(loc->inode, this, NULL, NULL, &event,
                        AFR_DATA_TRANSACTION, NULL);

    afr_discover_do(frame, this, 0);

    return 0;
out:
    AFR_STACK_UNWIND(lookup, frame, -1, op_errno, NULL, NULL, NULL, NULL);
    return 0;
}

static int
afr_lookup_do(call_frame_t *frame, xlator_t *this, int err)
{
    int ret = 0;
    int i = 0;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int call_count = 0;

    local = frame->local;
    priv = this->private;

    if (err < 0) {
        local->op_errno = err;
        goto out;
    }

    call_count = local->call_count = AFR_COUNT(local->child_up,
                                               priv->child_count);

    ret = afr_lookup_xattr_req_prepare(local, this, local->xattr_req,
                                       &local->loc);
    if (ret) {
        local->op_errno = -ret;
        goto out;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            STACK_WIND_COOKIE(
                frame, afr_lookup_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->lookup, &local->loc, local->xattr_req);
            if (!--call_count)
                break;
        }
    }
    return 0;
out:
    AFR_STACK_UNWIND(lookup, frame, -1, local->op_errno, 0, 0, 0, 0);
    return 0;
}

/*
 * afr_lookup()
 *
 * The goal here is to figure out what the element getting looked up is.
 * i.e what is the GFID, inode type and a conservative estimate of the
 * inode attributes are.
 *
 * As we lookup, operations may be underway on the entry name and the
 * inode. In lookup() we are primarily concerned only with the entry
 * operations. If the entry is getting unlinked or renamed, we detect
 * what operation is underway by querying for on-going transactions and
 * pending self-healing on the entry through xdata.
 *
 * If the entry is a file/dir, it may need self-heal and/or in a
 * split-brain condition. Lookup is not the place to worry about these
 * conditions. Outcast marking will naturally handle them in the read
 * paths.
 *
 * Here is a brief goal of what we are trying to achieve:
 *
 * - LOOKUP on all subvolumes concurrently, querying on-going transaction
 *   and pending self-heal info from the servers.
 *
 * - If all servers reply the same inode type and GFID, the overall call
 *   MUST be a success.
 *
 * - If inode types or GFIDs mismatch, and there IS either an on-going
 *   transaction or pending self-heal, inspect what the nature of the
 *   transaction or pending heal is, and select the appropriate subvolume's
 *   reply as the winner.
 *
 * - If inode types or GFIDs mismatch, and there are no on-going transactions
 *   or pending self-heal on the entry name on any of the servers, fail the
 *   lookup with EIO. Something has gone wrong beyond reasonable action.
 */

int
afr_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr_req)
{
    afr_local_t *local = NULL;
    int32_t op_errno = 0;
    int event = 0;
    int ret = 0;

    if (loc_is_nameless(loc)) {
        if (xattr_req)
            dict_del_sizen(xattr_req, "gfid-req");
        afr_discover(frame, this, loc, xattr_req);
        return 0;
    }

    if (__is_root_gfid(loc->parent->gfid) &&
        afr_is_private_directory(this->private, loc->name, frame->root->pid)) {
        op_errno = EPERM;
        goto out;
    }

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    if (!local->call_count) {
        op_errno = ENOTCONN;
        goto out;
    }

    local->op = GF_FOP_LOOKUP;

    loc_copy(&local->loc, loc);

    local->inode = inode_ref(loc->inode);

    if (xattr_req) {
        /* If xattr_req was null, afr_lookup_xattr_req_prepare() will
           allocate one for us */
        local->xattr_req = dict_copy_with_ref(xattr_req, NULL);
        if (!local->xattr_req) {
            op_errno = ENOMEM;
            goto out;
        }
        ret = dict_get_gfuuid(local->xattr_req, "gfid-req",
                              &local->cont.lookup.gfid_req);
        if (ret == 0) {
            dict_del_sizen(local->xattr_req, "gfid-req");
        }
    }

    afr_read_subvol_get(loc->parent, this, NULL, NULL, &event,
                        AFR_DATA_TRANSACTION, NULL);

    afr_lookup_do(frame, this, 0);

    return 0;
out:
    AFR_STACK_UNWIND(lookup, frame, -1, op_errno, NULL, NULL, NULL, NULL);

    return 0;
}

static void
_afr_cleanup_fd_ctx(xlator_t *this, afr_fd_ctx_t *fd_ctx)
{
    afr_private_t *priv = this->private;

    if (fd_ctx->lk_heal_info) {
        LOCK(&priv->lock);
        {
            list_del(&fd_ctx->lk_heal_info->pos);
        }
        UNLOCK(&priv->lock);
        afr_lk_heal_info_cleanup(fd_ctx->lk_heal_info);
        fd_ctx->lk_heal_info = NULL;
    }
    GF_FREE(fd_ctx->opened_on);
    GF_FREE(fd_ctx);
    return;
}

void
afr_cleanup_fd_ctx(xlator_t *this, fd_t *fd)
{
    afr_fd_ctx_t *fd_ctx = NULL;

    fd_ctx = fd_ctx_get_ptr(fd, this);
    if (fd_ctx)
        _afr_cleanup_fd_ctx(this, fd_ctx);
}

int
afr_release(xlator_t *this, fd_t *fd)
{
    afr_cleanup_fd_ctx(this, fd);

    return 0;
}

static afr_fd_ctx_t *
__afr_fd_ctx_set(xlator_t *this, fd_t *fd)
{
    afr_private_t *priv = NULL;
    int ret = -1;
    afr_fd_ctx_t *fd_ctx = NULL;
    int i = 0;

    VALIDATE_OR_GOTO(this->private, out);

    priv = this->private;

    fd_ctx = GF_CALLOC(1, sizeof(afr_fd_ctx_t), gf_afr_mt_afr_fd_ctx_t);
    if (!fd_ctx) {
        ret = -ENOMEM;
        goto out;
    }

    fd_ctx->opened_on = GF_CALLOC(sizeof(*fd_ctx->opened_on), priv->child_count,
                                  gf_afr_mt_int32_t);
    if (!fd_ctx->opened_on) {
        ret = -ENOMEM;
        goto out;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (fd_is_anonymous(fd))
            fd_ctx->opened_on[i] = AFR_FD_OPENED;
        else
            fd_ctx->opened_on[i] = AFR_FD_NOT_OPENED;
    }

    fd_ctx->readdir_subvol = -1;
    fd_ctx->lk_heal_info = NULL;

    ret = __fd_ctx_set(fd, this, (uint64_t)(long)fd_ctx);
    if (ret)
        gf_msg_debug(this->name, 0, "failed to set fd ctx (%p)", fd);
out:
    if (ret && fd_ctx) {
        _afr_cleanup_fd_ctx(this, fd_ctx);
        fd_ctx = NULL;
    }
    return fd_ctx;
}

static afr_fd_ctx_t *
__afr_fd_ctx_get(fd_t *fd, xlator_t *this)
{
    afr_fd_ctx_t *fd_ctx = NULL;

    fd_ctx = __fd_ctx_get_ptr(fd, this);

    if (!fd_ctx) {
        fd_ctx = __afr_fd_ctx_set(this, fd);
    }
    return fd_ctx;
}

afr_fd_ctx_t *
afr_fd_ctx_get(fd_t *fd, xlator_t *this)
{
    afr_fd_ctx_t *fd_ctx = NULL;

    LOCK(&fd->lock);
    {
        fd_ctx = __afr_fd_ctx_get(fd, this);
    }
    UNLOCK(&fd->lock);

    return fd_ctx;
}

/* {{{ flush */

int
afr_flush_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
              int32_t op_errno, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int call_count = -1;

    local = frame->local;

    LOCK(&frame->lock);
    {
        if (op_ret != -1) {
            local->op_ret = op_ret;
            if (!local->xdata_rsp && xdata)
                local->xdata_rsp = dict_ref(xdata);
        } else {
            local->op_errno = op_errno;
        }
        call_count = --local->call_count;
    }
    UNLOCK(&frame->lock);

    if (call_count == 0)
        AFR_STACK_UNWIND(flush, frame, local->op_ret, local->op_errno,
                         local->xdata_rsp);

    return 0;
}

static int
afr_flush_wrapper(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
{
    int i = 0;
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int call_count = -1;

    priv = this->private;
    local = frame->local;
    call_count = local->call_count;

    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            STACK_WIND_COOKIE(frame, afr_flush_cbk, (void *)(long)i,
                              priv->children[i], priv->children[i]->fops->flush,
                              local->fd, xdata);
            if (!--call_count)
                break;
        }
    }

    return 0;
}

static afr_local_t *
afr_wakeup_same_fd_delayed_op(xlator_t *this, afr_lock_t *lock, fd_t *fd)
{
    afr_local_t *local = NULL;

    if (lock->delay_timer) {
        local = list_entry(lock->post_op.next, afr_local_t,
                           transaction.owner_list);
        if (fd == local->fd) {
            if (gf_timer_call_cancel(this->ctx, lock->delay_timer)) {
                local = NULL;
            } else {
                lock->release = _gf_true;
                lock->delay_timer = NULL;
            }
        } else {
            local = NULL;
        }
    }

    return local;
}

static void
afr_delayed_changelog_wake_resume(xlator_t *this, inode_t *inode,
                                  call_stub_t *stub)
{
    afr_inode_ctx_t *ctx = NULL;
    afr_lock_t *lock = NULL;
    afr_local_t *metadata_local = NULL;
    afr_local_t *data_local = NULL;
    LOCK(&inode->lock);
    {
        ctx = __afr_inode_ctx_get(this, inode);
        if (ctx == NULL)
            goto unlock;
        lock = &ctx->lock[AFR_DATA_TRANSACTION];
        data_local = afr_wakeup_same_fd_delayed_op(this, lock, stub->args.fd);
        lock = &ctx->lock[AFR_METADATA_TRANSACTION];
        metadata_local = afr_wakeup_same_fd_delayed_op(this, lock,
                                                       stub->args.fd);
    }
unlock:
    UNLOCK(&inode->lock);

    if (data_local) {
        data_local->transaction.resume_stub = stub;
    } else if (metadata_local) {
        metadata_local->transaction.resume_stub = stub;
    } else {
        call_resume(stub);
    }
    if (data_local) {
        afr_delayed_changelog_wake_up_cbk(data_local);
    }
    if (metadata_local) {
        afr_delayed_changelog_wake_up_cbk(metadata_local);
    }
}

int
afr_flush(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
{
    afr_local_t *local = NULL;
    call_stub_t *stub = NULL;
    int op_errno = ENOMEM;

    AFR_ERROR_OUT_IF_FDCTX_INVALID(fd, this, op_errno, out);
    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = GF_FOP_FLUSH;
    if (!afr_is_consistent_io_possible(local, this->private, &op_errno))
        goto out;

    local->fd = fd_ref(fd);

    stub = fop_flush_stub(frame, afr_flush_wrapper, fd, xdata);
    if (!stub)
        goto out;

    afr_delayed_changelog_wake_resume(this, fd->inode, stub);

    return 0;
out:
    AFR_STACK_UNWIND(flush, frame, -1, op_errno, NULL);
    return 0;
}

int
afr_fsyncdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                 int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int call_count = -1;

    local = frame->local;

    LOCK(&frame->lock);
    {
        if (op_ret == 0) {
            local->op_ret = 0;
            if (!local->xdata_rsp && xdata)
                local->xdata_rsp = dict_ref(xdata);
        } else {
            local->op_errno = op_errno;
        }
        call_count = --local->call_count;
    }
    UNLOCK(&frame->lock);

    if (call_count == 0)
        AFR_STACK_UNWIND(fsyncdir, frame, local->op_ret, local->op_errno,
                         local->xdata_rsp);

    return 0;
}

int
afr_fsyncdir(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t datasync,
             dict_t *xdata)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int i = 0;
    int32_t call_count = 0;
    int32_t op_errno = ENOMEM;

    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = GF_FOP_FSYNCDIR;
    if (!afr_is_consistent_io_possible(local, priv, &op_errno))
        goto out;

    call_count = local->call_count;
    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            STACK_WIND(frame, afr_fsyncdir_cbk, priv->children[i],
                       priv->children[i]->fops->fsyncdir, fd, datasync, xdata);
            if (!--call_count)
                break;
        }
    }

    return 0;
out:
    AFR_STACK_UNWIND(fsyncdir, frame, -1, op_errno, NULL);

    return 0;
}

/* }}} */

static int
afr_serialized_lock_wind(call_frame_t *frame, xlator_t *this);

static gf_boolean_t
afr_is_conflicting_lock_present(int32_t op_ret, int32_t op_errno)
{
    if (op_ret == -1 && op_errno == EAGAIN)
        return _gf_true;
    return _gf_false;
}

static void
afr_fop_lock_unwind(call_frame_t *frame, glusterfs_fop_t op, int32_t op_ret,
                    int32_t op_errno, dict_t *xdata)
{
    switch (op) {
        case GF_FOP_INODELK:
            AFR_STACK_UNWIND(inodelk, frame, op_ret, op_errno, xdata);
            break;
        case GF_FOP_FINODELK:
            AFR_STACK_UNWIND(finodelk, frame, op_ret, op_errno, xdata);
            break;
        case GF_FOP_ENTRYLK:
            AFR_STACK_UNWIND(entrylk, frame, op_ret, op_errno, xdata);
            break;
        case GF_FOP_FENTRYLK:
            AFR_STACK_UNWIND(fentrylk, frame, op_ret, op_errno, xdata);
            break;
        default:
            break;
    }
}

static void
afr_fop_lock_wind(call_frame_t *frame, xlator_t *this, int child_index,
                  int32_t (*lock_cbk)(call_frame_t *, void *, xlator_t *,
                                      int32_t, int32_t, dict_t *))
{
    afr_local_t *local = frame->local;
    afr_private_t *priv = this->private;
    int i = child_index;

    switch (local->op) {
        case GF_FOP_INODELK:
            STACK_WIND_COOKIE(
                frame, lock_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->inodelk,
                (const char *)local->cont.inodelk.volume, &local->loc,
                local->cont.inodelk.cmd, &local->cont.inodelk.flock,
                local->cont.inodelk.xdata);
            break;
        case GF_FOP_FINODELK:
            STACK_WIND_COOKIE(
                frame, lock_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->finodelk,
                (const char *)local->cont.inodelk.volume, local->fd,
                local->cont.inodelk.cmd, &local->cont.inodelk.flock,
                local->cont.inodelk.xdata);
            break;
        case GF_FOP_ENTRYLK:
            STACK_WIND_COOKIE(
                frame, lock_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->entrylk, local->cont.entrylk.volume,
                &local->loc, local->cont.entrylk.basename,
                local->cont.entrylk.cmd, local->cont.entrylk.type,
                local->cont.entrylk.xdata);
            break;
        case GF_FOP_FENTRYLK:
            STACK_WIND_COOKIE(
                frame, lock_cbk, (void *)(long)i, priv->children[i],
                priv->children[i]->fops->fentrylk, local->cont.entrylk.volume,
                local->fd, local->cont.entrylk.basename,
                local->cont.entrylk.cmd, local->cont.entrylk.type,
                local->cont.entrylk.xdata);
            break;
        default:
            break;
    }
}

static void
afr_fop_lock_proceed(call_frame_t *frame)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;

    local = frame->local;
    priv = frame->this->private;

    if (local->fop_lock_state != AFR_FOP_LOCK_PARALLEL) {
        afr_fop_lock_unwind(frame, local->op, local->op_ret, local->op_errno,
                            local->xdata_rsp);
        return;
    }
    /* At least one child is up */
    /*
     * Non-blocking locks also need to be serialized.  Otherwise there is
     * a chance that both the mounts which issued same non-blocking inodelk
     * may endup not acquiring the lock on any-brick.
     * Ex: Mount1 and Mount2
     * request for full length lock on file f1.  Mount1 afr may acquire the
     * partial lock on brick-1 and may not acquire the lock on brick-2
     * because Mount2 already got the lock on brick-2, vice versa.  Since
     * both the mounts only got partial locks, afr treats them as failure in
     * gaining the locks and unwinds with EAGAIN errno.
     */
    local->op_ret = -1;
    local->op_errno = EUCLEAN;
    local->fop_lock_state = AFR_FOP_LOCK_SERIAL;
    afr_local_replies_wipe(local, priv);
    if (local->xdata_rsp)
        dict_unref(local->xdata_rsp);
    local->xdata_rsp = NULL;
    switch (local->op) {
        case GF_FOP_INODELK:
        case GF_FOP_FINODELK:
            local->cont.inodelk.cmd = local->cont.inodelk.in_cmd;
            gf_flock_copy(&local->cont.inodelk.flock,
                          &local->cont.inodelk.in_flock);
            if (local->cont.inodelk.xdata)
                dict_unref(local->cont.inodelk.xdata);
            local->cont.inodelk.xdata = NULL;
            if (local->xdata_req)
                local->cont.inodelk.xdata = dict_ref(local->xdata_req);
            break;
        case GF_FOP_ENTRYLK:
        case GF_FOP_FENTRYLK:
            local->cont.entrylk.cmd = local->cont.entrylk.in_cmd;
            if (local->cont.entrylk.xdata)
                dict_unref(local->cont.entrylk.xdata);
            local->cont.entrylk.xdata = NULL;
            if (local->xdata_req)
                local->cont.entrylk.xdata = dict_ref(local->xdata_req);
            break;
        default:
            break;
    }
    afr_serialized_lock_wind(frame, frame->this);
}

static int32_t
afr_unlock_partial_lock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                            int32_t op_ret, int32_t op_errno, dict_t *xdata)

{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int call_count = -1;
    int child_index = (long)cookie;
    uuid_t gfid = {0};

    local = frame->local;
    priv = this->private;

    if (op_ret < 0 && op_errno != ENOTCONN) {
        if (local->fd)
            gf_uuid_copy(gfid, local->fd->inode->gfid);
        else
            loc_gfid(&local->loc, gfid);
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_UNLOCK_FAIL,
               "%s: Failed to unlock %s on %s "
               "with lk_owner: %s",
               uuid_utoa(gfid), gf_fop_list[local->op],
               priv->children[child_index]->name,
               lkowner_utoa(&frame->root->lk_owner));
    }

    call_count = afr_frame_return(frame);
    if (call_count == 0)
        afr_fop_lock_proceed(frame);

    return 0;
}

static int32_t
afr_unlock_locks_and_proceed(call_frame_t *frame, xlator_t *this,
                             int call_count)
{
    int i = 0;
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;

    if (call_count == 0) {
        afr_fop_lock_proceed(frame);
        goto out;
    }

    local = frame->local;
    priv = this->private;
    local->call_count = call_count;
    switch (local->op) {
        case GF_FOP_INODELK:
        case GF_FOP_FINODELK:
            local->cont.inodelk.flock.l_type = F_UNLCK;
            local->cont.inodelk.cmd = F_SETLK;
            if (local->cont.inodelk.xdata)
                dict_unref(local->cont.inodelk.xdata);
            local->cont.inodelk.xdata = NULL;
            break;
        case GF_FOP_ENTRYLK:
        case GF_FOP_FENTRYLK:
            local->cont.entrylk.cmd = ENTRYLK_UNLOCK;
            if (local->cont.entrylk.xdata)
                dict_unref(local->cont.entrylk.xdata);
            local->cont.entrylk.xdata = NULL;
            break;
        default:
            break;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid)
            continue;

        if (local->replies[i].op_ret == -1)
            continue;

        afr_fop_lock_wind(frame, this, i, afr_unlock_partial_lock_cbk);

        if (!--call_count)
            break;
    }

out:
    return 0;
}

static int32_t
afr_fop_lock_done(call_frame_t *frame, xlator_t *this)
{
    int i = 0;
    int lock_count = 0;
    unsigned char *success = NULL;

    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;

    local = frame->local;
    priv = this->private;
    success = alloca0(priv->child_count);

    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid)
            continue;

        if (local->replies[i].op_ret == 0) {
            lock_count++;
            success[i] = 1;
        }

        if (local->op_ret == -1 && local->op_errno == EAGAIN)
            continue;

        if ((local->replies[i].op_ret == -1) &&
            (local->replies[i].op_errno == EAGAIN)) {
            local->op_ret = -1;
            local->op_errno = EAGAIN;
            continue;
        }

        if (local->replies[i].op_ret == 0)
            local->op_ret = 0;

        local->op_errno = local->replies[i].op_errno;
    }

    if (afr_fop_lock_is_unlock(frame))
        goto unwind;

    if (afr_is_conflicting_lock_present(local->op_ret, local->op_errno)) {
        afr_unlock_locks_and_proceed(frame, this, lock_count);
    } else if (priv->quorum_count && !afr_has_quorum(success, priv, NULL)) {
        local->fop_lock_state = AFR_FOP_LOCK_QUORUM_FAILED;
        local->op_ret = -1;
        local->op_errno = afr_final_errno(local, priv);
        if (local->op_errno == 0)
            local->op_errno = afr_quorum_errno(priv);
        afr_unlock_locks_and_proceed(frame, this, lock_count);
    } else {
        goto unwind;
    }

    return 0;
unwind:
    afr_fop_lock_unwind(frame, local->op, local->op_ret, local->op_errno,
                        local->xdata_rsp);
    return 0;
}

static int
afr_common_lock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int child_index = (long)cookie;

    local = frame->local;

    local->replies[child_index].valid = 1;
    local->replies[child_index].op_ret = op_ret;
    local->replies[child_index].op_errno = op_errno;
    if (op_ret == 0 && xdata) {
        local->replies[child_index].xdata = dict_ref(xdata);
        LOCK(&frame->lock);
        {
            if (!local->xdata_rsp)
                local->xdata_rsp = dict_ref(xdata);
        }
        UNLOCK(&frame->lock);
    }
    return 0;
}

static int32_t
afr_serialized_lock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, dict_t *xdata)

{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int child_index = (long)cookie;
    int next_child = 0;

    local = frame->local;
    priv = this->private;

    afr_common_lock_cbk(frame, cookie, this, op_ret, op_errno, xdata);

    for (next_child = child_index + 1; next_child < priv->child_count;
         next_child++) {
        if (local->child_up[next_child])
            break;
    }

    if (afr_is_conflicting_lock_present(op_ret, op_errno) ||
        (next_child == priv->child_count)) {
        afr_fop_lock_done(frame, this);
    } else {
        afr_fop_lock_wind(frame, this, next_child, afr_serialized_lock_cbk);
    }

    return 0;
}

static int
afr_serialized_lock_wind(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int i = 0;

    priv = this->private;
    local = frame->local;

    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            afr_fop_lock_wind(frame, this, i, afr_serialized_lock_cbk);
            break;
        }
    }
    return 0;
}

static int32_t
afr_parallel_lock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, dict_t *xdata)

{
    int call_count = 0;

    afr_common_lock_cbk(frame, cookie, this, op_ret, op_errno, xdata);

    call_count = afr_frame_return(frame);
    if (call_count == 0)
        afr_fop_lock_done(frame, this);

    return 0;
}

static int
afr_parallel_lock_wind(call_frame_t *frame, xlator_t *this)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int call_count = 0;
    int i = 0;

    priv = this->private;
    local = frame->local;
    call_count = local->call_count;

    for (i = 0; i < priv->child_count; i++) {
        if (!local->child_up[i])
            continue;
        afr_fop_lock_wind(frame, this, i, afr_parallel_lock_cbk);
        if (!--call_count)
            break;
    }
    return 0;
}

static int
afr_fop_handle_lock(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = frame->local;
    int op_errno = 0;

    if (!afr_fop_lock_is_unlock(frame)) {
        if (!afr_is_consistent_io_possible(local, this->private, &op_errno))
            goto out;

        switch (local->op) {
            case GF_FOP_INODELK:
            case GF_FOP_FINODELK:
                local->cont.inodelk.cmd = F_SETLK;
                break;
            case GF_FOP_ENTRYLK:
            case GF_FOP_FENTRYLK:
                local->cont.entrylk.cmd = ENTRYLK_LOCK_NB;
                break;
            default:
                break;
        }
    }

    if (local->xdata_req) {
        switch (local->op) {
            case GF_FOP_INODELK:
            case GF_FOP_FINODELK:
                local->cont.inodelk.xdata = dict_ref(local->xdata_req);
                break;
            case GF_FOP_ENTRYLK:
            case GF_FOP_FENTRYLK:
                local->cont.entrylk.xdata = dict_ref(local->xdata_req);
                break;
            default:
                break;
        }
    }

    local->fop_lock_state = AFR_FOP_LOCK_PARALLEL;
    afr_parallel_lock_wind(frame, this);
out:
    return -op_errno;
}

static int32_t
afr_handle_inodelk(call_frame_t *frame, xlator_t *this, glusterfs_fop_t fop,
                   const char *volume, loc_t *loc, fd_t *fd, int32_t cmd,
                   struct gf_flock *flock, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int32_t op_errno = ENOMEM;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = fop;
    if (loc)
        loc_copy(&local->loc, loc);
    if (fd && (flock->l_type != F_UNLCK)) {
        AFR_ERROR_OUT_IF_FDCTX_INVALID(fd, this, op_errno, out);
        local->fd = fd_ref(fd);
    }

    local->cont.inodelk.volume = gf_strdup(volume);
    if (!local->cont.inodelk.volume) {
        op_errno = ENOMEM;
        goto out;
    }

    local->cont.inodelk.in_cmd = cmd;
    local->cont.inodelk.cmd = cmd;
    gf_flock_copy(&local->cont.inodelk.in_flock, flock);
    gf_flock_copy(&local->cont.inodelk.flock, flock);
    if (xdata)
        local->xdata_req = dict_ref(xdata);

    op_errno = -afr_fop_handle_lock(frame, frame->this);
    if (op_errno)
        goto out;
    return 0;
out:
    afr_fop_lock_unwind(frame, fop, -1, op_errno, NULL);

    return 0;
}

int32_t
afr_inodelk(call_frame_t *frame, xlator_t *this, const char *volume, loc_t *loc,
            int32_t cmd, struct gf_flock *flock, dict_t *xdata)
{
    afr_handle_inodelk(frame, this, GF_FOP_INODELK, volume, loc, NULL, cmd,
                       flock, xdata);
    return 0;
}

int32_t
afr_finodelk(call_frame_t *frame, xlator_t *this, const char *volume, fd_t *fd,
             int32_t cmd, struct gf_flock *flock, dict_t *xdata)
{
    afr_handle_inodelk(frame, this, GF_FOP_FINODELK, volume, NULL, fd, cmd,
                       flock, xdata);
    return 0;
}

static int
afr_handle_entrylk(call_frame_t *frame, xlator_t *this, glusterfs_fop_t fop,
                   const char *volume, loc_t *loc, fd_t *fd,
                   const char *basename, entrylk_cmd cmd, entrylk_type type,
                   dict_t *xdata)
{
    afr_local_t *local = NULL;
    int32_t op_errno = ENOMEM;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = fop;
    if (loc)
        loc_copy(&local->loc, loc);
    if (fd && (cmd != ENTRYLK_UNLOCK)) {
        AFR_ERROR_OUT_IF_FDCTX_INVALID(fd, this, op_errno, out);
        local->fd = fd_ref(fd);
    }
    local->cont.entrylk.cmd = cmd;
    local->cont.entrylk.in_cmd = cmd;
    local->cont.entrylk.type = type;
    local->cont.entrylk.volume = gf_strdup(volume);
    local->cont.entrylk.basename = gf_strdup(basename);
    if (!local->cont.entrylk.volume || !local->cont.entrylk.basename) {
        op_errno = ENOMEM;
        goto out;
    }
    if (xdata)
        local->xdata_req = dict_ref(xdata);
    op_errno = -afr_fop_handle_lock(frame, frame->this);
    if (op_errno)
        goto out;

    return 0;
out:
    afr_fop_lock_unwind(frame, fop, -1, op_errno, NULL);
    return 0;
}

int
afr_entrylk(call_frame_t *frame, xlator_t *this, const char *volume, loc_t *loc,
            const char *basename, entrylk_cmd cmd, entrylk_type type,
            dict_t *xdata)
{
    afr_handle_entrylk(frame, this, GF_FOP_ENTRYLK, volume, loc, NULL, basename,
                       cmd, type, xdata);
    return 0;
}

int
afr_fentrylk(call_frame_t *frame, xlator_t *this, const char *volume, fd_t *fd,
             const char *basename, entrylk_cmd cmd, entrylk_type type,
             dict_t *xdata)
{
    afr_handle_entrylk(frame, this, GF_FOP_FENTRYLK, volume, NULL, fd, basename,
                       cmd, type, xdata);
    return 0;
}

int
afr_statfs_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
               int op_errno, struct statvfs *statvfs, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int call_count = 0;
    struct statvfs *buf = NULL;

    local = frame->local;

    LOCK(&frame->lock);
    {
        if (op_ret != 0) {
            local->op_errno = op_errno;
            goto unlock;
        }

        local->op_ret = op_ret;

        buf = &local->cont.statfs.buf;
        if (local->cont.statfs.buf_set) {
            if (statvfs->f_bavail < buf->f_bavail) {
                *buf = *statvfs;
                if (xdata) {
                    if (local->xdata_rsp)
                        dict_unref(local->xdata_rsp);
                    local->xdata_rsp = dict_ref(xdata);
                }
            }
        } else {
            *buf = *statvfs;
            local->cont.statfs.buf_set = 1;
            if (xdata)
                local->xdata_rsp = dict_ref(xdata);
        }
    }
unlock:
    call_count = --local->call_count;
    UNLOCK(&frame->lock);

    if (call_count == 0)
        AFR_STACK_UNWIND(statfs, frame, local->op_ret, local->op_errno,
                         &local->cont.statfs.buf, local->xdata_rsp);

    return 0;
}

int
afr_statfs(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int i = 0;
    int call_count = 0;
    int32_t op_errno = ENOMEM;

    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = GF_FOP_STATFS;
    if (!afr_is_consistent_io_possible(local, priv, &op_errno))
        goto out;

    if (priv->arbiter_count == 1 && local->child_up[ARBITER_BRICK_INDEX])
        local->call_count--;
    call_count = local->call_count;
    if (!call_count) {
        op_errno = ENOTCONN;
        goto out;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (local->child_up[i]) {
            if (AFR_IS_ARBITER_BRICK(priv, i))
                continue;
            STACK_WIND(frame, afr_statfs_cbk, priv->children[i],
                       priv->children[i]->fops->statfs, loc, xdata);
            if (!--call_count)
                break;
        }
    }

    return 0;
out:
    AFR_STACK_UNWIND(statfs, frame, -1, op_errno, NULL, NULL);

    return 0;
}

int32_t
afr_lk_unlock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                  int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
                  dict_t *xdata)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = this->private;
    int call_count = -1;
    int child_index = (long)cookie;

    local = frame->local;

    if (op_ret < 0 && op_errno != ENOTCONN && op_errno != EBADFD) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_UNLOCK_FAIL,
               "gfid=%s: unlock failed on subvolume %s "
               "with lock owner %s",
               uuid_utoa(local->fd->inode->gfid),
               priv->children[child_index]->name,
               lkowner_utoa(&frame->root->lk_owner));
    }

    call_count = afr_frame_return(frame);
    if (call_count == 0) {
        AFR_STACK_UNWIND(lk, frame, local->op_ret, local->op_errno, NULL,
                         local->xdata_rsp);
    }

    return 0;
}

int32_t
afr_lk_unlock(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int i = 0;
    int call_count = 0;

    local = frame->local;
    priv = this->private;

    call_count = afr_locked_nodes_count(local->cont.lk.locked_nodes,
                                        priv->child_count);

    if (call_count == 0) {
        AFR_STACK_UNWIND(lk, frame, local->op_ret, local->op_errno, NULL,
                         local->xdata_rsp);
        return 0;
    }

    local->call_count = call_count;

    local->cont.lk.user_flock.l_type = F_UNLCK;

    for (i = 0; i < priv->child_count; i++) {
        if (local->cont.lk.locked_nodes[i]) {
            STACK_WIND_COOKIE(frame, afr_lk_unlock_cbk, (void *)(long)i,
                              priv->children[i], priv->children[i]->fops->lk,
                              local->fd, F_SETLK, &local->cont.lk.user_flock,
                              NULL);

            if (!--call_count)
                break;
        }
    }

    return 0;
}

int32_t
afr_lk_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
           int32_t op_errno, struct gf_flock *lock, dict_t *xdata)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int child_index = -1;

    local = frame->local;
    priv = this->private;

    child_index = (long)cookie;

    afr_common_lock_cbk(frame, cookie, this, op_ret, op_errno, xdata);

    /* We have two options here to handle EINTR
       1) Either Ignore EINTR and wind a call on other subvolume
       2) or Unlock the already granted lock in the previous children and return
       the error.

       For EINTR we can't chose first option because EINTR is not a regular
       error. The user expecation is whole lock should be clear after receive an
       interrupt. However there is an issue if we do unlock already granted
       locks. Locks xlator implements posix locks as they are defined by the
       posix standard. This means that granted locks are "combined" with other
       existing granted locks. Unfortunately, in some cases this makes it
       impossible to revoke an already granted lock without unwanted side
       effects. For example consider a process that takes two read locks on the
       same file, but their ranges overlap a bit. Once both of them are granted
       in one brick, revoking any of them because of an interrupt will cause the
       overlapped range to be unlocked, even if one of the locks is still
       granted (from the point of view of the user, one of the locks has never
       been granted because it returned EINTR, so the user still expects that
       the other lock still covers the overlapping region). For the time being
       we have decided consider EINTR as same EAGAIN.
    */

    if (op_ret < 0 && ((op_errno == EAGAIN) || (op_errno == EINTR))) {
        local->op_ret = -1;
        local->op_errno = op_errno;

        afr_lk_unlock(frame, this);
        return 0;
    }

    if (op_ret == 0) {
        local->op_ret = 0;
        local->op_errno = 0;
        local->cont.lk.locked_nodes[child_index] = 1;
        gf_flock_copy(&local->cont.lk.ret_flock, lock);
    }

    child_index++;

    if (child_index < priv->child_count) {
        STACK_WIND_COOKIE(frame, afr_lk_cbk, (void *)(long)child_index,
                          priv->children[child_index],
                          priv->children[child_index]->fops->lk, local->fd,
                          local->cont.lk.cmd, &local->cont.lk.user_flock,
                          local->xdata_req);
    } else if (priv->quorum_count &&
               !afr_has_quorum(local->cont.lk.locked_nodes, priv, NULL)) {
        local->op_ret = -1;
        local->op_errno = afr_final_errno(local, priv);

        afr_lk_unlock(frame, this);
    } else {
        if (local->op_ret < 0)
            local->op_errno = afr_final_errno(local, priv);

        AFR_STACK_UNWIND(lk, frame, local->op_ret, local->op_errno,
                         &local->cont.lk.ret_flock, local->xdata_rsp);
    }

    return 0;
}

int
afr_lk_transaction_cbk(int ret, call_frame_t *frame, void *opaque)
{
    return 0;
}

int
afr_lk_txn_wind_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
                    dict_t *xdata)
{
    afr_local_t *local = NULL;
    int child_index = -1;

    local = frame->local;
    child_index = (long)cookie;
    afr_common_lock_cbk(frame, cookie, this, op_ret, op_errno, xdata);
    if (op_ret == 0) {
        local->op_ret = 0;
        local->op_errno = 0;
        local->cont.lk.locked_nodes[child_index] = 1;
        gf_flock_copy(&local->cont.lk.ret_flock, lock);
    }
    syncbarrier_wake(&local->barrier);
    return 0;
}

int
afr_lk_txn_unlock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
                      dict_t *xdata)
{
    afr_local_t *local = frame->local;
    afr_private_t *priv = this->private;
    int child_index = (long)cookie;

    if (op_ret < 0 && op_errno != ENOTCONN && op_errno != EBADFD) {
        gf_msg(this->name, GF_LOG_ERROR, op_errno, AFR_MSG_UNLOCK_FAIL,
               "gfid=%s: unlock failed on subvolume %s "
               "with lock owner %s",
               uuid_utoa(local->fd->inode->gfid),
               priv->children[child_index]->name,
               lkowner_utoa(&frame->root->lk_owner));
    }
    return 0;
}
int
afr_lk_transaction(void *opaque)
{
    call_frame_t *frame = NULL;
    xlator_t *this = NULL;
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    char *wind_on = NULL;
    int op_errno = 0;
    int i = 0;
    int ret = 0;

    frame = (call_frame_t *)opaque;
    local = frame->local;
    this = frame->this;
    priv = this->private;
    wind_on = alloca0(priv->child_count);

    if (priv->arbiter_count || priv->child_count != 3) {
        op_errno = ENOTSUP;
        gf_msg(frame->this->name, GF_LOG_ERROR, op_errno, AFR_MSG_LK_HEAL_DOM,
               "%s: Lock healing supported only for replica 3 volumes.",
               uuid_utoa(local->fd->inode->gfid));
        goto err;
    }

    op_errno = -afr_dom_lock_acquire(frame);  // Released during
                                              // AFR_STACK_UNWIND
    if (op_errno != 0) {
        goto err;
    }
    if (priv->quorum_count &&
        !afr_has_quorum(local->cont.lk.dom_locked_nodes, priv, NULL)) {
        op_errno = afr_final_errno(local, priv);
        goto err;
    }

    for (i = 0; i < priv->child_count; i++) {
        if (priv->child_up[i] && local->cont.lk.dom_locked_nodes[i])
            wind_on[i] = 1;
    }
    AFR_ONLIST(wind_on, frame, afr_lk_txn_wind_cbk, lk, local->fd,
               local->cont.lk.cmd, &local->cont.lk.user_flock,
               local->xdata_req);

    if (priv->quorum_count &&
        !afr_has_quorum(local->cont.lk.locked_nodes, priv, NULL)) {
        local->op_ret = -1;
        local->op_errno = afr_final_errno(local, priv);
        goto unlock;
    } else {
        if (local->cont.lk.user_flock.l_type == F_UNLCK)
            ret = afr_remove_lock_from_saved_locks(local, this);
        else
            ret = afr_add_lock_to_saved_locks(frame, this);
        if (ret) {
            local->op_ret = -1;
            local->op_errno = -ret;
            goto unlock;
        }
        AFR_STACK_UNWIND(lk, frame, local->op_ret, local->op_errno,
                         &local->cont.lk.ret_flock, local->xdata_rsp);
    }

    return 0;

unlock:
    local->cont.lk.user_flock.l_type = F_UNLCK;
    AFR_ONLIST(local->cont.lk.locked_nodes, frame, afr_lk_txn_unlock_cbk, lk,
               local->fd, F_SETLK, &local->cont.lk.user_flock, NULL);
err:
    AFR_STACK_UNWIND(lk, frame, -1, op_errno, NULL, NULL);
    return -1;
}

int
afr_lk(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t cmd,
       struct gf_flock *flock, dict_t *xdata)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int ret = 0;
    int i = 0;
    int32_t op_errno = ENOMEM;

    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = GF_FOP_LK;
    if (!afr_lk_is_unlock(cmd, flock)) {
        AFR_ERROR_OUT_IF_FDCTX_INVALID(fd, this, op_errno, out);
        if (!afr_is_consistent_io_possible(local, priv, &op_errno))
            goto out;
    }

    local->cont.lk.locked_nodes = GF_CALLOC(
        priv->child_count, sizeof(*local->cont.lk.locked_nodes),
        gf_afr_mt_char);

    if (!local->cont.lk.locked_nodes) {
        op_errno = ENOMEM;
        goto out;
    }

    local->fd = fd_ref(fd);
    local->cont.lk.cmd = cmd;
    gf_flock_copy(&local->cont.lk.user_flock, flock);
    gf_flock_copy(&local->cont.lk.ret_flock, flock);
    if (xdata) {
        local->xdata_req = dict_ref(xdata);
        if (afr_is_lock_mode_mandatory(xdata)) {
            ret = synctask_new(this->ctx->env, afr_lk_transaction,
                               afr_lk_transaction_cbk, frame, frame);
            if (ret) {
                op_errno = ENOMEM;
                goto out;
            }
            return 0;
        }
    }

    STACK_WIND_COOKIE(frame, afr_lk_cbk, (void *)(long)0, priv->children[i],
                      priv->children[i]->fops->lk, fd, cmd, flock,
                      local->xdata_req);

    return 0;
out:
    AFR_STACK_UNWIND(lk, frame, -1, op_errno, NULL, NULL);

    return 0;
}

static int32_t
afr_lease_unlock_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, struct gf_lease *lease,
                     dict_t *xdata)
{
    afr_local_t *local = NULL;
    int call_count = -1;

    local = frame->local;
    call_count = afr_frame_return(frame);

    if (call_count == 0)
        AFR_STACK_UNWIND(lease, frame, local->op_ret, local->op_errno, lease,
                         xdata);

    return 0;
}

int32_t
afr_lease_unlock(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int i = 0;
    int call_count = 0;

    local = frame->local;
    priv = this->private;

    call_count = afr_locked_nodes_count(local->cont.lease.locked_nodes,
                                        priv->child_count);

    if (call_count == 0) {
        AFR_STACK_UNWIND(lease, frame, local->op_ret, local->op_errno,
                         &local->cont.lease.ret_lease, NULL);
        return 0;
    }

    local->call_count = call_count;

    local->cont.lease.user_lease.cmd = GF_UNLK_LEASE;

    for (i = 0; i < priv->child_count; i++) {
        if (local->cont.lease.locked_nodes[i]) {
            STACK_WIND(frame, afr_lease_unlock_cbk, priv->children[i],
                       priv->children[i]->fops->lease, &local->loc,
                       &local->cont.lease.user_lease, NULL);

            if (!--call_count)
                break;
        }
    }

    return 0;
}

int32_t
afr_lease_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
              int32_t op_errno, struct gf_lease *lease, dict_t *xdata)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    int child_index = -1;

    local = frame->local;
    priv = this->private;

    child_index = (long)cookie;

    afr_common_lock_cbk(frame, cookie, this, op_ret, op_errno, xdata);
    if (op_ret < 0 && op_errno == EAGAIN) {
        local->op_ret = -1;
        local->op_errno = EAGAIN;

        afr_lease_unlock(frame, this);
        return 0;
    }

    if (op_ret == 0) {
        local->op_ret = 0;
        local->op_errno = 0;
        local->cont.lease.locked_nodes[child_index] = 1;
        local->cont.lease.ret_lease = *lease;
    }

    child_index++;
    if (child_index < priv->child_count) {
        STACK_WIND_COOKIE(frame, afr_lease_cbk, (void *)(long)child_index,
                          priv->children[child_index],
                          priv->children[child_index]->fops->lease, &local->loc,
                          &local->cont.lease.user_lease, xdata);
    } else if (priv->quorum_count &&
               !afr_has_quorum(local->cont.lease.locked_nodes, priv, NULL)) {
        local->op_ret = -1;
        local->op_errno = afr_final_errno(local, priv);

        afr_lease_unlock(frame, this);
    } else {
        if (local->op_ret < 0)
            local->op_errno = afr_final_errno(local, priv);
        AFR_STACK_UNWIND(lease, frame, local->op_ret, local->op_errno,
                         &local->cont.lease.ret_lease, NULL);
    }

    return 0;
}

int
afr_lease(call_frame_t *frame, xlator_t *this, loc_t *loc,
          struct gf_lease *lease, dict_t *xdata)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    int32_t op_errno = ENOMEM;

    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto out;

    local->op = GF_FOP_LEASE;
    local->cont.lease.locked_nodes = GF_CALLOC(
        priv->child_count, sizeof(*local->cont.lease.locked_nodes),
        gf_afr_mt_char);

    if (!local->cont.lease.locked_nodes) {
        op_errno = ENOMEM;
        goto out;
    }

    loc_copy(&local->loc, loc);
    local->cont.lease.user_lease = *lease;
    local->cont.lease.ret_lease = *lease;

    STACK_WIND_COOKIE(frame, afr_lease_cbk, (void *)(long)0, priv->children[0],
                      priv->children[0]->fops->lease, loc, lease, xdata);

    return 0;
out:
    AFR_STACK_UNWIND(lease, frame, -1, op_errno, NULL, NULL);

    return 0;
}

int
afr_ipc_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
            int32_t op_errno, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int child_index = (long)cookie;
    int call_count = 0;
    gf_boolean_t failed = _gf_false;
    gf_boolean_t succeeded = _gf_false;
    int i = 0;
    afr_private_t *priv = NULL;

    local = frame->local;
    priv = this->private;

    local->replies[child_index].valid = 1;
    local->replies[child_index].op_ret = op_ret;
    local->replies[child_index].op_errno = op_errno;
    if (xdata)
        local->replies[child_index].xdata = dict_ref(xdata);

    call_count = afr_frame_return(frame);
    if (call_count)
        goto out;
    /* If any of the subvolumes failed with other than ENOTCONN
     * return error else return success unless all the subvolumes
     * failed.
     * TODO: In case of failure, we need to unregister the xattrs
     * from the other subvolumes where it succeeded (once upcall
     * fixes the Bz-1371622)*/
    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid)
            continue;
        if (local->replies[i].op_ret < 0 &&
            local->replies[i].op_errno != ENOTCONN) {
            local->op_ret = local->replies[i].op_ret;
            local->op_errno = local->replies[i].op_errno;
            if (local->xdata_rsp)
                dict_unref(local->xdata_rsp);
            local->xdata_rsp = NULL;
            if (local->replies[i].xdata) {
                local->xdata_rsp = dict_ref(local->replies[i].xdata);
            }
            failed = _gf_true;
            break;
        }
        if (local->replies[i].op_ret == 0) {
            succeeded = _gf_true;
            local->op_ret = 0;
            local->op_errno = 0;
            if (!local->xdata_rsp && local->replies[i].xdata) {
                local->xdata_rsp = dict_ref(local->replies[i].xdata);
            }
        }
    }

    if (!succeeded && !failed) {
        local->op_ret = -1;
        local->op_errno = ENOTCONN;
    }

    AFR_STACK_UNWIND(ipc, frame, local->op_ret, local->op_errno,
                     local->xdata_rsp);

out:
    return 0;
}

int
afr_ipc(call_frame_t *frame, xlator_t *this, int32_t op, dict_t *xdata)
{
    afr_local_t *local = NULL;
    int32_t op_errno = -1;
    afr_private_t *priv = NULL;
    int i = 0;
    int call_cnt = -1;

    VALIDATE_OR_GOTO(frame, err);
    VALIDATE_OR_GOTO(this, err);

    if (op != GF_IPC_TARGET_UPCALL)
        goto wind_default;

    VALIDATE_OR_GOTO(this->private, err);
    priv = this->private;

    local = AFR_FRAME_INIT(frame, op_errno);
    if (!local)
        goto err;

    call_cnt = local->call_count;

    if (xdata) {
        for (i = 0; i < priv->child_count; i++) {
            if (dict_set_int8(xdata, priv->pending_key[i], 0) < 0)
                goto err;
        }
    }

    for (i = 0; i < priv->child_count; i++) {
        if (!local->child_up[i])
            continue;

        STACK_WIND_COOKIE(frame, afr_ipc_cbk, (void *)(long)i,
                          priv->children[i], priv->children[i]->fops->ipc, op,
                          xdata);
        if (!--call_cnt)
            break;
    }
    return 0;

err:
    if (op_errno == -1)
        op_errno = errno;
    AFR_STACK_UNWIND(ipc, frame, -1, op_errno, NULL);

    return 0;

wind_default:
    STACK_WIND(frame, default_ipc_cbk, FIRST_CHILD(this),
               FIRST_CHILD(this)->fops->ipc, op, xdata);
    return 0;
}

int
afr_forget(xlator_t *this, inode_t *inode)
{
    uint64_t ctx_int = 0;
    afr_inode_ctx_t *ctx = NULL;

    afr_spb_choice_timeout_cancel(this, inode);
    inode_ctx_del(inode, this, &ctx_int);
    if (!ctx_int)
        return 0;

    ctx = (afr_inode_ctx_t *)(uintptr_t)ctx_int;
    afr_inode_ctx_destroy(ctx);
    return 0;
}

int
afr_priv_dump(xlator_t *this)
{
    afr_private_t *priv = NULL;
    char key_prefix[GF_DUMP_MAX_BUF_LEN];
    char key[GF_DUMP_MAX_BUF_LEN];
    int i = 0;

    GF_ASSERT(this);
    priv = this->private;

    GF_ASSERT(priv);
    snprintf(key_prefix, GF_DUMP_MAX_BUF_LEN, "%s.%s", this->type, this->name);
    gf_proc_dump_add_section("%s", key_prefix);
    gf_proc_dump_write("child_count", "%u", priv->child_count);
    for (i = 0; i < priv->child_count; i++) {
        sprintf(key, "child_up[%d]", i);
        gf_proc_dump_write(key, "%d", priv->child_up[i]);
        sprintf(key, "pending_key[%d]", i);
        gf_proc_dump_write(key, "%s", priv->pending_key[i]);
        sprintf(key, "pending_reads[%d]", i);
        gf_proc_dump_write(key, "%" PRId64,
                           GF_ATOMIC_GET(priv->pending_reads[i]));
        sprintf(key, "child_latency[%d]", i);
        gf_proc_dump_write(key, "%" PRId64, priv->child_latency[i]);
        sprintf(key, "halo_child_up[%d]", i);
        gf_proc_dump_write(key, "%d", priv->halo_child_up[i]);
    }
    gf_proc_dump_write("data_self_heal", "%d", priv->data_self_heal);
    gf_proc_dump_write("metadata_self_heal", "%d", priv->metadata_self_heal);
    gf_proc_dump_write("entry_self_heal", "%d", priv->entry_self_heal);
    gf_proc_dump_write("read_child", "%d", priv->read_child);
    gf_proc_dump_write("wait_count", "%u", priv->wait_count);
    gf_proc_dump_write("heal-wait-queue-length", "%d", priv->heal_wait_qlen);
    gf_proc_dump_write("heal-waiters", "%d", priv->heal_waiters);
    gf_proc_dump_write("background-self-heal-count", "%d",
                       priv->background_self_heal_count);
    gf_proc_dump_write("healers", "%d", priv->healers);
    gf_proc_dump_write("read-hash-mode", "%d", priv->hash_mode);
    gf_proc_dump_write("use-anonymous-inode", "%d", priv->use_anon_inode);
    if (priv->quorum_count == AFR_QUORUM_AUTO) {
        gf_proc_dump_write("quorum-type", "auto");
    } else if (priv->quorum_count == 0) {
        gf_proc_dump_write("quorum-type", "none");
    } else {
        gf_proc_dump_write("quorum-type", "fixed");
        gf_proc_dump_write("quorum-count", "%d", priv->quorum_count);
    }
    gf_proc_dump_write("up", "%u", afr_has_quorum(priv->child_up, priv, NULL));
    if (priv->thin_arbiter_count) {
        gf_proc_dump_write("ta_child_up", "%d", priv->ta_child_up);
        gf_proc_dump_write("ta_bad_child_index", "%d",
                           priv->ta_bad_child_index);
        gf_proc_dump_write("ta_notify_dom_lock_offset", "%" PRId64,
                           priv->ta_notify_dom_lock_offset);
    }

    return 0;
}

/**
 * find_child_index - find the child's index in the array of subvolumes
 * @this: AFR
 * @child: child
 */

static int
afr_find_child_index(xlator_t *this, xlator_t *child)
{
    afr_private_t *priv = NULL;
    int child_count = -1;
    int i = -1;

    priv = this->private;
    child_count = priv->child_count;
    if (priv->thin_arbiter_count) {
        child_count++;
    }

    for (i = 0; i < child_count; i++) {
        if ((xlator_t *)child == priv->children[i])
            break;
    }

    return i;
}

int
__afr_get_up_children_count(afr_private_t *priv)
{
    int up_children = 0;
    int i = 0;

    for (i = 0; i < priv->child_count; i++)
        if (priv->child_up[i] == 1)
            up_children++;

    return up_children;
}

static int
__get_heard_from_all_status(afr_private_t *priv)
{
    int i;

    for (i = 0; i < priv->child_count; i++) {
        if (!priv->last_event[i]) {
            return 0;
        }
    }
    if (priv->thin_arbiter_count && !priv->ta_child_up) {
        return 0;
    }
    return 1;
}

static glusterfs_event_t
__afr_transform_event_from_state(afr_private_t *priv)
{
    int i = 0;
    int up_children = 0;

    if (__get_heard_from_all_status(priv))
        /* have_heard_from_all. Let afr_notify() do the propagation. */
        return GF_EVENT_MAXVAL;

    up_children = __afr_get_up_children_count(priv);
    /* Treat the children with pending notification, as having sent a
     * GF_EVENT_CHILD_DOWN. i.e. set the event as GF_EVENT_SOME_DESCENDENT_DOWN,
     * as done in afr_notify() */
    for (i = 0; i < priv->child_count; i++) {
        if (priv->last_event[i])
            continue;
        priv->last_event[i] = GF_EVENT_SOME_DESCENDENT_DOWN;
        priv->child_up[i] = 0;
    }

    if (up_children)
        /* We received at least one child up */
        return GF_EVENT_CHILD_UP;
    else
        return GF_EVENT_CHILD_DOWN;

    return GF_EVENT_MAXVAL;
}

static void
afr_notify_cbk(void *data)
{
    xlator_t *this = data;
    afr_private_t *priv = this->private;
    glusterfs_event_t event = GF_EVENT_MAXVAL;
    gf_boolean_t propagate = _gf_false;

    LOCK(&priv->lock);
    {
        if (!priv->timer) {
            /*
             * Either child_up/child_down is already sent to parent.
             * This is a spurious wake up.
             */
            goto unlock;
        }
        priv->timer = NULL;
        event = __afr_transform_event_from_state(priv);
        if (event != GF_EVENT_MAXVAL)
            propagate = _gf_true;
    }
unlock:
    UNLOCK(&priv->lock);
    if (propagate)
        default_notify(this, event, NULL);
}

static void
__afr_launch_notify_timer(xlator_t *this, afr_private_t *priv)
{
    struct timespec delay = {
        0,
    };

    gf_msg_debug(this->name, 0, "Initiating child-down timer");
    delay.tv_sec = 10;
    delay.tv_nsec = 0;
    priv->timer = gf_timer_call_after(this->ctx, delay, afr_notify_cbk, this);
    if (priv->timer == NULL) {
        gf_msg(this->name, GF_LOG_ERROR, 0, AFR_MSG_TIMER_CREATE_FAIL,
               "Cannot create timer for delayed initialization");
    }
}

static int
find_best_down_child(xlator_t *this)
{
    afr_private_t *priv = NULL;
    int i = -1;
    int32_t best_child = -1;
    int64_t best_latency = INT64_MAX;

    priv = this->private;

    for (i = 0; i < priv->child_count; i++) {
        if (!priv->child_up[i] && priv->child_latency[i] >= 0 &&
            priv->child_latency[i] < best_latency) {
            best_child = i;
            best_latency = priv->child_latency[i];
        }
    }
    if (best_child >= 0) {
        gf_msg_debug(this->name, 0,
                     "Found best down child (%d) @ %" PRId64 " ms latency",
                     best_child, best_latency);
    }
    return best_child;
}

static int
find_worst_up_child(xlator_t *this)
{
    afr_private_t *priv = NULL;
    int i = -1;
    int32_t worst_child = -1;
    int64_t worst_latency = INT64_MIN;

    priv = this->private;

    for (i = 0; i < priv->child_count; i++) {
        if (priv->child_up[i] && priv->child_latency[i] >= 0 &&
            priv->child_latency[i] > worst_latency) {
            worst_child = i;
            worst_latency = priv->child_latency[i];
        }
    }
    if (worst_child >= 0) {
        gf_msg_debug(this->name, 0,
                     "Found worst up child (%d) @ %" PRId64 " ms latency",
                     worst_child, worst_latency);
    }
    return worst_child;
}

static void
__afr_handle_ping_event(xlator_t *this, xlator_t *child_xlator, const int idx,
                        int64_t halo_max_latency_msec, int32_t *event,
                        int64_t child_latency_msec)
{
    afr_private_t *priv = NULL;
    int up_children = 0;

    priv = this->private;

    priv->child_latency[idx] = child_latency_msec;
    gf_msg_debug(child_xlator->name, 0, "Client ping @ %" PRId64 " ms",
                 child_latency_msec);
    if (priv->shd.iamshd)
        return;

    up_children = __afr_get_up_children_count(priv);

    if (child_latency_msec > halo_max_latency_msec &&
        priv->child_up[idx] == 1 && up_children > priv->halo_min_replicas) {
        if ((up_children - 1) < priv->halo_min_replicas) {
            gf_log(child_xlator->name, GF_LOG_INFO,
                   "Overriding halo threshold, "
                   "min replicas: %d",
                   priv->halo_min_replicas);
        } else {
            gf_log(child_xlator->name, GF_LOG_INFO,
                   "Child latency (%" PRId64
                   " ms) "
                   "exceeds halo threshold (%" PRId64
                   "), "
                   "marking child down.",
                   child_latency_msec, halo_max_latency_msec);
            if (priv->halo_child_up[idx]) {
                *event = GF_EVENT_CHILD_DOWN;
            }
        }
    } else if (child_latency_msec < halo_max_latency_msec &&
               priv->child_up[idx] == 0) {
        if (up_children < priv->halo_max_replicas) {
            gf_log(child_xlator->name, GF_LOG_INFO,
                   "Child latency (%" PRId64
                   " ms) "
                   "below halo threshold (%" PRId64
                   "), "
                   "marking child up.",
                   child_latency_msec, halo_max_latency_msec);
            if (priv->halo_child_up[idx]) {
                *event = GF_EVENT_CHILD_UP;
            }
        } else {
            gf_log(child_xlator->name, GF_LOG_INFO,
                   "Not marking child %d up, "
                   "max replicas (%d) reached.",
                   idx, priv->halo_max_replicas);
        }
    }
}

static int64_t
afr_get_halo_latency(xlator_t *this)
{
    afr_private_t *priv = NULL;
    int64_t halo_max_latency_msec = 0;

    priv = this->private;

    if (priv->shd.iamshd) {
        halo_max_latency_msec = priv->shd.halo_max_latency_msec;
    } else if (priv->nfsd.iamnfsd) {
        halo_max_latency_msec = priv->nfsd.halo_max_latency_msec;
    } else {
        halo_max_latency_msec = priv->halo_max_latency_msec;
    }
    gf_msg_debug(this->name, 0, "Using halo latency %" PRId64,
                 halo_max_latency_msec);
    return halo_max_latency_msec;
}

static void
__afr_handle_child_up_event(xlator_t *this, xlator_t *child_xlator,
                            const int idx, int64_t child_latency_msec,
                            int32_t *event, int32_t *call_psh,
                            int32_t *up_child)
{
    afr_private_t *priv = NULL;
    int up_children = 0;
    int worst_up_child = -1;
    int64_t halo_max_latency_msec = afr_get_halo_latency(this);

    priv = this->private;

    /*
     * This only really counts if the child was never up
     * (value = -1) or had been down (value = 0).  See
     * comment at GF_EVENT_CHILD_DOWN for a more detailed
     * explanation.
     */
    if (priv->child_up[idx] != 1) {
        priv->event_generation++;
    }
    priv->child_up[idx] = 1;

    *call_psh = 1;
    *up_child = idx;
    up_children = __afr_get_up_children_count(priv);
    /*
     * If this is an _actual_ CHILD_UP event, we
     * want to set the child_latency to MAX to indicate
     * the child needs ping data to be available before doing child-up
     */
    if (!priv->halo_enabled)
        goto out;

    if (child_latency_msec < 0) {
        /*set to INT64_MAX-1 so that it is found for best_down_child*/
        priv->halo_child_up[idx] = 1;
        if (priv->child_latency[idx] < 0) {
            priv->child_latency[idx] = AFR_HALO_MAX_LATENCY;
        }
    }

    /*
     * Handle the edge case where we exceed
     * halo_min_replicas and we've got a child which is
     * marked up as it was helping to satisfy the
     * halo_min_replicas even though it's latency exceeds
     * halo_max_latency_msec.
     */
    if (up_children > priv->halo_min_replicas) {
        worst_up_child = find_worst_up_child(this);
        if (worst_up_child >= 0 &&
            priv->child_latency[worst_up_child] > halo_max_latency_msec) {
            gf_msg_debug(this->name, 0,
                         "Marking child %d down, "
                         "doesn't meet halo threshold (%" PRId64
                         "), and > "
                         "halo_min_replicas (%d)",
                         worst_up_child, halo_max_latency_msec,
                         priv->halo_min_replicas);
            priv->child_up[worst_up_child] = 0;
            up_children--;
        }
    }

    if (up_children > priv->halo_max_replicas && !priv->shd.iamshd) {
        worst_up_child = find_worst_up_child(this);
        if (worst_up_child < 0) {
            worst_up_child = idx;
        }
        priv->child_up[worst_up_child] = 0;
        up_children--;
        gf_msg_debug(this->name, 0,
                     "Marking child %d down, "
                     "up_children (%d) > halo_max_replicas (%d)",
                     worst_up_child, up_children, priv->halo_max_replicas);
    }
out:
    if (up_children == 1) {
        gf_msg(this->name, GF_LOG_INFO, 0, AFR_MSG_SUBVOL_UP,
               "Subvolume '%s' came back up; "
               "going online.",
               child_xlator->name);
        gf_event(EVENT_AFR_SUBVOL_UP, "client-pid=%d; subvol=%s",
                 this->ctx->cmd_args.client_pid, this->name);
    } else {
        *event = GF_EVENT_SOME_DESCENDENT_UP;
    }

    priv->last_event[idx] = *event;
}

void
__afr_handle_child_down_event(xlator_t *this, xlator_t *child_xlator, int idx,
                              int64_t child_latency_msec, int32_t *event,
                              int32_t *call_psh, int32_t *up_child)
{
    afr_private_t *priv = NULL;
    int i = 0;
    int up_children = 0;
    int down_children = 0;
    int best_down_child = -1;

    priv = this->private;

    /*
     * If a brick is down when we start, we'll get a
     * CHILD_DOWN to indicate its initial state.  There
     * was never a CHILD_UP in this case, so if we
     * increment "down_count" the difference between than
     * and "up_count" will no longer be the number of
     * children that are currently up.  This has serious
     * implications e.g. for quorum enforcement, so we
     * don't increment these values unless the event
     * represents an actual state transition between "up"
     * (value = 1) and anything else.
     */
    if (priv->child_up[idx] == 1) {
        priv->event_generation++;
    }

    /*
     * If this is an _actual_ CHILD_DOWN event, we
     * want to set the child_latency to < 0 to indicate
     * the child is really disconnected.
     */
    if (child_latency_msec < 0) {
        priv->child_latency[idx] = child_latency_msec;
        priv->halo_child_up[idx] = 0;
    }
    priv->child_up[idx] = 0;

    up_children = __afr_get_up_children_count(priv);
    /*
     * Handle the edge case where we need to find the
     * next best child (to mark up) as marking this child
     * down would cause us to fall below halo_min_replicas.
     * We will also force the SHD to heal this child _now_
     * as we want it to be up to date if we are going to
     * begin using it synchronously.
     */
    if (priv->halo_enabled && up_children < priv->halo_min_replicas) {
        best_down_child = find_best_down_child(this);
        if (best_down_child >= 0) {
            gf_msg_debug(this->name, 0,
                         "Swapping out child %d for "
                         "child %d to satisfy halo_min_replicas (%d).",
                         idx, best_down_child, priv->halo_min_replicas);
            priv->child_up[best_down_child] = 1;
            *call_psh = 1;
            *up_child = best_down_child;
        }
    }
    for (i = 0; i < priv->child_count; i++)
        if (priv->child_up[i] == 0)
            down_children++;
    if (down_children == priv->child_count) {
        gf_msg(this->name, GF_LOG_ERROR, 0, AFR_MSG_SUBVOLS_DOWN,
               "All subvolumes are down. Going "
               "offline until at least one of them "
               "comes back up.");
        gf_event(EVENT_AFR_SUBVOLS_DOWN, "client-pid=%d; subvol=%s",
                 this->ctx->cmd_args.client_pid, this->name);
    } else {
        *event = GF_EVENT_SOME_DESCENDENT_DOWN;
    }
    priv->last_event[idx] = *event;
}

void
afr_ta_lock_release_synctask(xlator_t *this)
{
    call_frame_t *ta_frame = NULL;
    int ret = 0;

    ta_frame = afr_ta_frame_create(this);
    if (!ta_frame) {
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, AFR_MSG_THIN_ARB,
               "Failed to create ta_frame");
        return;
    }

    ret = synctask_new(this->ctx->env, afr_release_notify_lock_for_ta,
                       afr_ta_lock_release_done, ta_frame, this);
    if (ret) {
        STACK_DESTROY(ta_frame->root);
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, AFR_MSG_THIN_ARB,
               "Failed to release "
               "AFR_TA_DOM_NOTIFY lock.");
    }
}

static void
afr_handle_inodelk_contention(xlator_t *this, struct gf_upcall *upcall)
{
    struct gf_upcall_inodelk_contention *lc = NULL;
    unsigned int inmem_count = 0;
    unsigned int onwire_count = 0;
    afr_private_t *priv = this->private;

    lc = upcall->data;

    if (strcmp(lc->domain, AFR_TA_DOM_NOTIFY) != 0)
        return;

    if (priv->shd.iamshd) {
        /* shd should ignore AFR_TA_DOM_NOTIFY release requests. */
        return;
    }
    LOCK(&priv->lock);
    {
        if (priv->release_ta_notify_dom_lock == _gf_true) {
            /* Ignore multiple release requests from shds.*/
            UNLOCK(&priv->lock);
            return;
        }
        priv->release_ta_notify_dom_lock = _gf_true;
        inmem_count = priv->ta_in_mem_txn_count;
        onwire_count = priv->ta_on_wire_txn_count;
    }
    UNLOCK(&priv->lock);
    if (inmem_count || onwire_count)
        /* lock release will happen in txn code path after
         * in-memory or on-wire txns are over.*/
        return;

    afr_ta_lock_release_synctask(this);
}

static void
afr_handle_upcall_event(xlator_t *this, struct gf_upcall *upcall, const int idx)
{
    struct gf_upcall_cache_invalidation *up_ci = NULL;
    afr_private_t *priv = this->private;
    inode_t *inode = NULL;
    inode_table_t *itable = NULL;
    int i = 0;

    switch (upcall->event_type) {
        case GF_UPCALL_INODELK_CONTENTION:
            afr_handle_inodelk_contention(this, upcall);
            break;
        case GF_UPCALL_CACHE_INVALIDATION:
            up_ci = (struct gf_upcall_cache_invalidation *)upcall->data;

            if (AFR_IS_ARBITER_BRICK(priv, idx)) {
                /* Skip all update involving iatt from arbiter
                 * node, since the size contains invalid info
                 */
                up_ci->flags &= ~UP_ATTR_FLAGS;
                up_ci->flags &= ~UP_PARENT_DENTRY_FLAGS;
            }
            /* Since md-cache will be aggressively filtering
             * lookups, the stale read issue will be more
             * pronounced. Hence when a pending xattr is set notify
             * all the md-cache clients to invalidate the existing
             * stat cache and send the lookup next time */
            if (!up_ci->dict)
                break;
            for (i = 0; i < priv->child_count; i++) {
                if (!dict_get(up_ci->dict, priv->pending_key[i]))
                    continue;
                up_ci->flags |= UP_INVAL_ATTR;
                itable = ((xlator_t *)this->graph->top)->itable;
                /*Internal processes may not have itable for
                 *top xlator*/
                if (itable)
                    inode = inode_find(itable, upcall->gfid);
                if (inode)
                    afr_inode_need_refresh_set(inode, this);
                break;
            }
            break;
        default:
            break;
    }
}

int32_t
afr_notify(xlator_t *this, int32_t event, void *data, void *data2)
{
    afr_private_t *priv = NULL;
    xlator_t *child_xlator = NULL;
    int i = -1;
    int propagate = 0;
    int had_heard_from_all = 0;
    int have_heard_from_all = 0;
    int idx = -1;
    int ret = -1;
    int call_psh = 0;
    int up_child = -1;
    dict_t *input = NULL;
    dict_t *output = NULL;
    gf_boolean_t had_quorum = _gf_false;
    gf_boolean_t has_quorum = _gf_false;
    int64_t halo_max_latency_msec = 0;
    int64_t child_latency_msec = -1;

    child_xlator = (xlator_t *)data;

    priv = this->private;

    if (!priv)
        return 0;

    /*
     * We need to reset this in case children come up in "staggered"
     * fashion, so that we discover a late-arriving local subvolume.  Note
     * that we could end up issuing N lookups to the first subvolume, and
     * O(N^2) overall, but N is small for AFR so it shouldn't be an issue.
     */
    priv->did_discovery = _gf_false;

    /* parent xlators don't need to know about every child_up, child_down
     * because of afr ha. If all subvolumes go down, child_down has
     * to be triggered. In that state when 1 subvolume comes up child_up
     * needs to be triggered. dht optimizes revalidate lookup by sending
     * it only to one of its subvolumes. When child up/down happens
     * for afr's subvolumes dht should be notified by child_modified. The
     * subsequent revalidate lookup happens on all the dht's subvolumes
     * which triggers afr self-heals if any.
     */
    idx = afr_find_child_index(this, child_xlator);
    if (idx < 0) {
        gf_msg(this->name, GF_LOG_ERROR, 0, AFR_MSG_INVALID_CHILD_UP,
               "Received child_up from invalid subvolume");
        goto out;
    }

    had_quorum = priv->quorum_count &&
                 afr_has_quorum(priv->child_up, priv, NULL);
    if (event == GF_EVENT_CHILD_PING) {
        child_latency_msec = (int64_t)(uintptr_t)data2;
        if (priv->halo_enabled) {
            halo_max_latency_msec = afr_get_halo_latency(this);

            /* Calculates the child latency and sets event
             */
            LOCK(&priv->lock);
            {
                __afr_handle_ping_event(this, child_xlator, idx,
                                        halo_max_latency_msec, &event,
                                        child_latency_msec);
            }
            UNLOCK(&priv->lock);
        } else {
            LOCK(&priv->lock);
            {
                priv->child_latency[idx] = child_latency_msec;
            }
            UNLOCK(&priv->lock);
        }
    }

    if (event == GF_EVENT_CHILD_PING) {
        /* This is the only xlator that handles PING, no reason to
         * propagate.
         */
        goto out;
    }

    if (event == GF_EVENT_TRANSLATOR_OP) {
        LOCK(&priv->lock);
        {
            had_heard_from_all = __get_heard_from_all_status(priv);
        }
        UNLOCK(&priv->lock);

        if (!had_heard_from_all) {
            ret = -1;
        } else {
            input = data;
            output = data2;
            ret = afr_xl_op(this, input, output);
        }
        goto out;
    }

    if (event == GF_EVENT_UPCALL) {
        afr_handle_upcall_event(this, data, idx);
    }

    LOCK(&priv->lock);
    {
        had_heard_from_all = __get_heard_from_all_status(priv);
        switch (event) {
            case GF_EVENT_PARENT_UP:
                __afr_launch_notify_timer(this, priv);
                propagate = 1;
                break;
            case GF_EVENT_CHILD_UP:
                if (priv->thin_arbiter_count &&
                    (idx == AFR_CHILD_THIN_ARBITER)) {
                    priv->ta_child_up = 1;
                    priv->ta_event_gen++;
                    break;
                }
                __afr_handle_child_up_event(this, child_xlator, idx,
                                            child_latency_msec, &event,
                                            &call_psh, &up_child);
                __afr_lock_heal_synctask(this, priv, idx);
                break;

            case GF_EVENT_CHILD_DOWN:
                if (priv->thin_arbiter_count &&
                    (idx == AFR_CHILD_THIN_ARBITER)) {
                    priv->ta_child_up = 0;
                    priv->ta_event_gen++;
                    afr_ta_locked_priv_invalidate(priv);
                    break;
                }
                __afr_handle_child_down_event(this, child_xlator, idx,
                                              child_latency_msec, &event,
                                              &call_psh, &up_child);
                __afr_mark_pending_lk_heal(this, priv, idx);
                break;

            case GF_EVENT_CHILD_CONNECTING:
                priv->last_event[idx] = event;

                break;

            case GF_EVENT_SOME_DESCENDENT_DOWN:
                priv->last_event[idx] = event;
                break;
            default:
                propagate = 1;
                break;
        }
        have_heard_from_all = __get_heard_from_all_status(priv);
        if (!had_heard_from_all && have_heard_from_all) {
            if (priv->timer) {
                gf_timer_call_cancel(this->ctx, priv->timer);
                priv->timer = NULL;
            }
            /* This is the first event which completes aggregation
               of events from all subvolumes. If at least one subvol
               had come up, propagate CHILD_UP, but only this time
            */
            event = GF_EVENT_CHILD_DOWN;
            for (i = 0; i < priv->child_count; i++) {
                if (priv->last_event[i] == GF_EVENT_CHILD_UP) {
                    event = GF_EVENT_CHILD_UP;
                    break;
                }

                if (priv->last_event[i] == GF_EVENT_CHILD_CONNECTING) {
                    event = GF_EVENT_CHILD_CONNECTING;
                    /* continue to check other events for CHILD_UP */
                }
            }
        }
    }
    UNLOCK(&priv->lock);

    if (priv->quorum_count) {
        has_quorum = afr_has_quorum(priv->child_up, priv, NULL);
        if (!had_quorum && has_quorum) {
            gf_msg(this->name, GF_LOG_INFO, 0, AFR_MSG_QUORUM_MET,
                   "Client-quorum is met");
            gf_event(EVENT_AFR_QUORUM_MET, "client-pid=%d; subvol=%s",
                     this->ctx->cmd_args.client_pid, this->name);
        }
        if (had_quorum && !has_quorum) {
            gf_msg(this->name, GF_LOG_WARNING, 0, AFR_MSG_QUORUM_FAIL,
                   "Client-quorum is not met");
            gf_event(EVENT_AFR_QUORUM_FAIL, "client-pid=%d; subvol=%s",
                     this->ctx->cmd_args.client_pid, this->name);
        }
    }

    /* if all subvols have reported status, no need to hide anything
       or wait for anything else. Just propagate blindly */
    if (have_heard_from_all)
        propagate = 1;

    ret = 0;
    if (propagate)
        ret = default_notify(this, event, data);

    if ((!had_heard_from_all) || call_psh) {
        /* Launch self-heal on all local subvolumes if:
         * a) We have_heard_from_all for the first time
         * b) Already heard from everyone, but we now got a child-up
         *    event.
         */
        if (have_heard_from_all) {
            afr_selfheal_childup(this, priv);
        }
    }
out:
    return ret;
}

int
afr_local_init(afr_local_t *local, afr_private_t *priv, int32_t *op_errno)
{
    int __ret;
    local->op_ret = -1;
    local->op_errno = EUCLEAN;

    __ret = syncbarrier_init(&local->barrier);
    if (__ret) {
        if (op_errno)
            *op_errno = __ret;
        goto out;
    }

    local->child_up = GF_MALLOC(priv->child_count * sizeof(*local->child_up),
                                gf_afr_mt_char);
    if (!local->child_up) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    memcpy(local->child_up, priv->child_up,
           sizeof(*local->child_up) * priv->child_count);
    local->call_count = AFR_COUNT(local->child_up, priv->child_count);
    if (local->call_count == 0) {
        gf_msg(THIS->name, GF_LOG_INFO, 0, AFR_MSG_SUBVOLS_DOWN,
               "no subvolumes up");
        if (op_errno)
            *op_errno = ENOTCONN;
        goto out;
    }

    local->event_generation = priv->event_generation;

    local->read_attempted = GF_CALLOC(priv->child_count, sizeof(char),
                                      gf_afr_mt_char);
    if (!local->read_attempted) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    local->readable = GF_CALLOC(priv->child_count, sizeof(char),
                                gf_afr_mt_char);
    if (!local->readable) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    local->readable2 = GF_CALLOC(priv->child_count, sizeof(char),
                                 gf_afr_mt_char);
    if (!local->readable2) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    local->read_subvol = -1;

    local->replies = GF_CALLOC(priv->child_count, sizeof(*local->replies),
                               gf_afr_mt_reply_t);
    if (!local->replies) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    local->need_full_crawl = _gf_false;
    if (priv->thin_arbiter_count) {
        local->ta_child_up = priv->ta_child_up;
        local->ta_failed_subvol = AFR_CHILD_UNKNOWN;
        local->read_txn_query_child = AFR_CHILD_UNKNOWN;
        local->ta_event_gen = priv->ta_event_gen;
        local->fop_state = TA_SUCCESS;
    }
    local->is_new_entry = _gf_false;

    local->need_open = GF_CALLOC(priv->child_count, sizeof(*local->need_open),
                                 gf_afr_mt_char);
    if (!local->need_open) {
        if (op_errno)
            *op_errno = ENOMEM;
        goto out;
    }

    INIT_LIST_HEAD(&local->healer);
    return 0;
out:
    return -1;
}

static inline void
afr_internal_lock_init(afr_internal_lock_t *lk, size_t child_count)
{
    lk->lock_op_ret = -1;
    lk->lock_op_errno = EUCLEAN;
}

void
afr_matrix_cleanup(int32_t **matrix, unsigned int m)
{
    int i = 0;

    if (!matrix)
        goto out;
    for (i = 0; i < m; i++) {
        GF_FREE(matrix[i]);
    }

    GF_FREE(matrix);
out:
    return;
}

int32_t **
afr_matrix_create(unsigned int m, unsigned int n)
{
    int32_t **matrix = NULL;
    int i = 0;

    matrix = GF_CALLOC(sizeof(*matrix), m, gf_afr_mt_int32_t);
    if (!matrix)
        goto out;

    for (i = 0; i < m; i++) {
        matrix[i] = GF_CALLOC(sizeof(*matrix[i]), n, gf_afr_mt_int32_t);
        if (!matrix[i])
            goto out;
    }
    return matrix;
out:
    afr_matrix_cleanup(matrix, m);
    return NULL;
}

int
afr_transaction_local_init(afr_local_t *local, xlator_t *this)
{
    int ret = -ENOMEM;
    afr_private_t *priv = NULL;

    priv = this->private;
    INIT_LIST_HEAD(&local->transaction.wait_list);
    INIT_LIST_HEAD(&local->transaction.owner_list);
    INIT_LIST_HEAD(&local->ta_waitq);
    INIT_LIST_HEAD(&local->ta_onwireq);
    afr_internal_lock_init(&local->internal_lock, priv->child_count);

    ret = -ENOMEM;
    local->pre_op_compat = priv->pre_op_compat;

    local->transaction.pre_op = GF_CALLOC(sizeof(*local->transaction.pre_op),
                                          priv->child_count, gf_afr_mt_char);
    if (!local->transaction.pre_op)
        goto out;

    local->transaction.changelog_xdata = GF_CALLOC(
        sizeof(*local->transaction.changelog_xdata), priv->child_count,
        gf_afr_mt_dict_t);
    if (!local->transaction.changelog_xdata)
        goto out;

    if (priv->arbiter_count == 1) {
        local->transaction.pre_op_sources = GF_CALLOC(
            sizeof(*local->transaction.pre_op_sources), priv->child_count,
            gf_afr_mt_char);
        if (!local->transaction.pre_op_sources)
            goto out;
    }

    local->transaction.failed_subvols = GF_CALLOC(
        sizeof(*local->transaction.failed_subvols), priv->child_count,
        gf_afr_mt_char);
    if (!local->transaction.failed_subvols)
        goto out;

    local->pending = afr_matrix_create(priv->child_count, AFR_NUM_CHANGE_LOGS);
    if (!local->pending)
        goto out;

    ret = 0;
out:
    return ret;
}

void
afr_priv_destroy(afr_private_t *priv)
{
    int i = 0;
    int child_count = -1;

    if (!priv)
        goto out;

    GF_FREE(priv->sh_domain);
    GF_FREE(priv->last_event);

    child_count = priv->child_count;
    if (priv->thin_arbiter_count) {
        child_count++;
    }
    if (priv->pending_key) {
        for (i = 0; i < child_count; i++)
            GF_FREE(priv->pending_key[i]);
    }

    GF_FREE(priv->pending_reads);
    GF_FREE(priv->local);
    GF_FREE(priv->pending_key);
    GF_FREE(priv->children);
    GF_FREE(priv->anon_inode);
    GF_FREE(priv->child_up);
    GF_FREE(priv->halo_child_up);
    GF_FREE(priv->child_latency);
    LOCK_DESTROY(&priv->lock);

    GF_FREE(priv);
out:
    return;
}

int **
afr_mark_pending_changelog(afr_private_t *priv, unsigned char *pending,
                           dict_t *xattr, ia_type_t iat)
{
    int i = 0;
    int **changelog = NULL;
    int idx = -1;
    int m_idx = 0;
    int d_idx = 0;
    int ret = 0;
    uint32_t hton32_1;

    m_idx = afr_index_for_transaction_type(AFR_METADATA_TRANSACTION);
    d_idx = afr_index_for_transaction_type(AFR_DATA_TRANSACTION);

    idx = afr_index_from_ia_type(iat);

    changelog = afr_matrix_create(priv->child_count, AFR_NUM_CHANGE_LOGS);
    if (!changelog)
        goto out;

    hton32_1 = htobe32(1);
    for (i = 0; i < priv->child_count; i++) {
        if (!pending[i])
            continue;

        changelog[i][m_idx] = hton32_1;
        if (idx != -1)
            changelog[i][idx] = hton32_1;
        /* If the newentry marking is on a newly created directory,
         * then mark it with the full-heal indicator.
         */
        if ((IA_ISDIR(iat)) && (priv->esh_granular))
            changelog[i][d_idx] = hton32_1;
    }
    ret = afr_set_pending_dict(priv, xattr, changelog);
    if (ret < 0) {
        afr_matrix_cleanup(changelog, priv->child_count);
        return NULL;
    }
out:
    return changelog;
}

static dict_t *
afr_set_heal_info(char *status)
{
    dict_t *dict = NULL;
    int ret = -1;

    dict = dict_new();
    if (!dict) {
        ret = -ENOMEM;
        goto out;
    }

    ret = dict_set_dynstr_sizen(dict, "heal-info", status);
    if (ret)
        gf_msg("", GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
               "Failed to set heal-info key to "
               "%s",
               status);
out:
    /* Any error other than EINVAL, dict_set_dynstr frees status */
    if (ret == -ENOMEM || ret == -EINVAL) {
        GF_FREE(status);
    }

    if (ret && dict) {
        dict_unref(dict);
        dict = NULL;
    }
    return dict;
}

static gf_boolean_t
afr_is_dirty_count_non_unary_for_txn(xlator_t *this, struct afr_reply *replies,
                                     afr_transaction_type type)
{
    afr_private_t *priv = this->private;
    int *dirty = alloca0(priv->child_count * sizeof(int));
    int i = 0;

    afr_selfheal_extract_xattr(this, replies, type, dirty, NULL);
    for (i = 0; i < priv->child_count; i++) {
        if (dirty[i] > 1)
            return _gf_true;
    }

    return _gf_false;
}

static gf_boolean_t
afr_is_dirty_count_non_unary(xlator_t *this, struct afr_reply *replies,
                             ia_type_t ia_type)
{
    gf_boolean_t data_chk = _gf_false;
    gf_boolean_t mdata_chk = _gf_false;
    gf_boolean_t entry_chk = _gf_false;

    switch (ia_type) {
        case IA_IFDIR:
            mdata_chk = _gf_true;
            entry_chk = _gf_true;
            break;
        case IA_IFREG:
            mdata_chk = _gf_true;
            data_chk = _gf_true;
            break;
        default:
            /*IA_IFBLK, IA_IFCHR, IA_IFLNK, IA_IFIFO, IA_IFSOCK*/
            mdata_chk = _gf_true;
            break;
    }

    if (data_chk && afr_is_dirty_count_non_unary_for_txn(
                        this, replies, AFR_DATA_TRANSACTION)) {
        return _gf_true;
    } else if (mdata_chk && afr_is_dirty_count_non_unary_for_txn(
                                this, replies, AFR_METADATA_TRANSACTION)) {
        return _gf_true;
    } else if (entry_chk && afr_is_dirty_count_non_unary_for_txn(
                                this, replies, AFR_ENTRY_TRANSACTION)) {
        return _gf_true;
    }

    return _gf_false;
}

static int
afr_update_heal_status(xlator_t *this, struct afr_reply *replies,
                       ia_type_t ia_type, gf_boolean_t *esh, gf_boolean_t *dsh,
                       gf_boolean_t *msh, unsigned char pending)
{
    int ret = -1;
    GF_UNUSED int ret1 = 0;
    int i = 0;
    int io_domain_lk_count = 0;
    int shd_domain_lk_count = 0;
    afr_private_t *priv = NULL;
    char *key1 = NULL;
    char *key2 = NULL;

    priv = this->private;
    key1 = alloca0(strlen(GLUSTERFS_INODELK_DOM_PREFIX) + 2 +
                   strlen(this->name));
    key2 = alloca0(strlen(GLUSTERFS_INODELK_DOM_PREFIX) + 2 +
                   strlen(priv->sh_domain));
    sprintf(key1, "%s:%s", GLUSTERFS_INODELK_DOM_PREFIX, this->name);
    sprintf(key2, "%s:%s", GLUSTERFS_INODELK_DOM_PREFIX, priv->sh_domain);

    for (i = 0; i < priv->child_count; i++) {
        if ((replies[i].valid != 1) || (replies[i].op_ret != 0))
            continue;
        if (!io_domain_lk_count) {
            ret1 = dict_get_int32(replies[i].xdata, key1, &io_domain_lk_count);
        }
        if (!shd_domain_lk_count) {
            ret1 = dict_get_int32(replies[i].xdata, key2, &shd_domain_lk_count);
        }
    }

    if (!pending) {
        if ((afr_is_dirty_count_non_unary(this, replies, ia_type)) ||
            (!io_domain_lk_count)) {
            /* Needs heal. */
            ret = 0;
        } else {
            /* No heal needed. */
            *dsh = *esh = *msh = 0;
        }
    } else {
        if (shd_domain_lk_count) {
            ret = -EAGAIN; /*For 'possibly-healing'. */
        } else {
            ret = 0; /*needs heal. Just set a non -ve value so that it is
                       assumed as the source index.*/
        }
    }
    return ret;
}

/*return EIO, EAGAIN or pending*/
static int
afr_lockless_inspect(call_frame_t *frame, xlator_t *this, uuid_t gfid,
                     inode_t **inode, gf_boolean_t *entry_selfheal,
                     gf_boolean_t *data_selfheal,
                     gf_boolean_t *metadata_selfheal, unsigned char *pending)
{
    int ret = -1;
    int i = 0;
    afr_private_t *priv = NULL;
    struct afr_reply *replies = NULL;
    gf_boolean_t dsh = _gf_false;
    gf_boolean_t msh = _gf_false;
    gf_boolean_t esh = _gf_false;
    unsigned char *sources = NULL;
    unsigned char *sinks = NULL;
    unsigned char *valid_on = NULL;
    uint64_t *witness = NULL;

    priv = this->private;
    replies = alloca0(sizeof(*replies) * priv->child_count);
    sources = alloca0(sizeof(*sources) * priv->child_count);
    sinks = alloca0(sizeof(*sinks) * priv->child_count);
    witness = alloca0(sizeof(*witness) * priv->child_count);
    valid_on = alloca0(sizeof(*valid_on) * priv->child_count);

    ret = afr_selfheal_unlocked_inspect(frame, this, gfid, inode, &dsh, &msh,
                                        &esh, replies);
    if (ret)
        goto out;
    for (i = 0; i < priv->child_count; i++) {
        if (replies[i].valid && replies[i].op_ret == 0) {
            valid_on[i] = 1;
        }
    }
    if (msh) {
        ret = afr_selfheal_find_direction(frame, this, replies,
                                          AFR_METADATA_TRANSACTION, valid_on,
                                          sources, sinks, witness, pending);
        if (*pending & PFLAG_SBRAIN)
            ret = -EIO;
        if (ret)
            goto out;
    }
    if (dsh) {
        ret = afr_selfheal_find_direction(frame, this, replies,
                                          AFR_DATA_TRANSACTION, valid_on,
                                          sources, sinks, witness, pending);
        if (*pending & PFLAG_SBRAIN)
            ret = -EIO;
        if (ret)
            goto out;
    }
    if (esh) {
        ret = afr_selfheal_find_direction(frame, this, replies,
                                          AFR_ENTRY_TRANSACTION, valid_on,
                                          sources, sinks, witness, pending);
        if (*pending & PFLAG_SBRAIN)
            ret = -EIO;
        if (ret)
            goto out;
    }

    ret = afr_update_heal_status(this, replies, (*inode)->ia_type, &esh, &dsh,
                                 &msh, *pending);
out:
    *data_selfheal = dsh;
    *entry_selfheal = esh;
    *metadata_selfheal = msh;
    if (replies)
        afr_replies_wipe(replies, priv->child_count);
    return ret;
}

int
afr_get_heal_info(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;
    unsigned char pending = 0;
    dict_t *dict = NULL;
    int ret = -1;
    int op_errno = ENOMEM;
    inode_t *inode = NULL;
    char *substr = NULL;
    char *status = NULL;
    call_frame_t *heal_frame = NULL;
    afr_local_t *heal_local = NULL;

    /*Use frame with lk-owner set*/
    heal_frame = afr_frame_create(frame->this, &op_errno);
    if (!heal_frame) {
        ret = -1;
        goto out;
    }
    heal_local = heal_frame->local;
    heal_frame->local = frame->local;

    ret = afr_lockless_inspect(heal_frame, this, loc->gfid, &inode,
                               &entry_selfheal, &data_selfheal,
                               &metadata_selfheal, &pending);

    if (ret == -ENOMEM) {
        ret = -1;
        goto out;
    }

    if (pending & PFLAG_PENDING) {
        gf_asprintf(&substr, "-pending");
        if (!substr)
            goto out;
    }

    if (ret == -EIO) {
        ret = gf_asprintf(&status, "split-brain%s", substr ? substr : "");
        if (ret < 0) {
            goto out;
        }
        dict = afr_set_heal_info(status);
        if (!dict) {
            ret = -1;
            goto out;
        }
    } else if (ret == -EAGAIN) {
        ret = gf_asprintf(&status, "possibly-healing%s", substr ? substr : "");
        if (ret < 0) {
            goto out;
        }
        dict = afr_set_heal_info(status);
        if (!dict) {
            ret = -1;
            goto out;
        }
    } else if (ret >= 0) {
        /* value of ret = source index
         * so ret >= 0 and at least one of the 3 booleans set to
         * true means a source is identified; heal is required.
         */
        if (!data_selfheal && !entry_selfheal && !metadata_selfheal) {
            status = gf_strdup("no-heal");
            if (!status) {
                ret = -1;
                goto out;
            }
            dict = afr_set_heal_info(status);
            if (!dict) {
                ret = -1;
                goto out;
            }
        } else {
            ret = gf_asprintf(&status, "heal%s", substr ? substr : "");
            if (ret < 0) {
                goto out;
            }
            dict = afr_set_heal_info(status);
            if (!dict) {
                ret = -1;
                goto out;
            }
        }
    } else if (ret < 0) {
        /* Apart from above checked -ve ret values, there are
         * other possible ret values like ENOTCONN
         * (returned when number of valid replies received are
         * less than 2)
         * in which case heal is required when one of the
         * selfheal booleans is set.
         */
        if (data_selfheal || entry_selfheal || metadata_selfheal) {
            ret = gf_asprintf(&status, "heal%s", substr ? substr : "");
            if (ret < 0) {
                goto out;
            }
            dict = afr_set_heal_info(status);
            if (!dict) {
                ret = -1;
                goto out;
            }
        }
    }

    ret = 0;
    op_errno = 0;

out:
    if (heal_frame) {
        heal_frame->local = heal_local;
        AFR_STACK_DESTROY(heal_frame);
    }
    AFR_STACK_UNWIND(getxattr, frame, ret, op_errno, dict, NULL);
    if (dict)
        dict_unref(dict);
    if (inode)
        inode_unref(inode);
    GF_FREE(substr);
    return ret;
}

int
_afr_is_split_brain(call_frame_t *frame, xlator_t *this,
                    struct afr_reply *replies, afr_transaction_type type,
                    gf_boolean_t *spb)
{
    afr_private_t *priv = NULL;
    uint64_t *witness = NULL;
    unsigned char *sources = NULL;
    unsigned char *sinks = NULL;
    int sources_count = 0;
    int ret = 0;

    priv = this->private;

    sources = alloca0(priv->child_count);
    sinks = alloca0(priv->child_count);
    witness = alloca0(priv->child_count * sizeof(*witness));

    ret = afr_selfheal_find_direction(frame, this, replies, type,
                                      priv->child_up, sources, sinks, witness,
                                      NULL);
    if (ret)
        return ret;

    sources_count = AFR_COUNT(sources, priv->child_count);
    if (!sources_count)
        *spb = _gf_true;

    return ret;
}

int
afr_is_split_brain(call_frame_t *frame, xlator_t *this, inode_t *inode,
                   uuid_t gfid, gf_boolean_t *d_spb, gf_boolean_t *m_spb)
{
    int ret = -1;
    afr_private_t *priv = NULL;
    struct afr_reply *replies = NULL;

    priv = this->private;

    replies = alloca0(sizeof(*replies) * priv->child_count);

    ret = afr_selfheal_unlocked_discover(frame, inode, gfid, replies);
    if (ret)
        goto out;

    if (!afr_can_decide_split_brain_source_sinks(replies, priv->child_count)) {
        ret = -EAGAIN;
        goto out;
    }

    ret = _afr_is_split_brain(frame, this, replies, AFR_DATA_TRANSACTION,
                              d_spb);
    if (ret)
        goto out;

    ret = _afr_is_split_brain(frame, this, replies, AFR_METADATA_TRANSACTION,
                              m_spb);
out:
    if (replies) {
        afr_replies_wipe(replies, priv->child_count);
        replies = NULL;
    }
    return ret;
}

int
afr_get_split_brain_status_cbk(int ret, call_frame_t *frame, void *opaque)
{
    GF_FREE(opaque);
    return 0;
}

int
afr_get_split_brain_status(void *opaque)
{
    gf_boolean_t d_spb = _gf_false;
    gf_boolean_t m_spb = _gf_false;
    int ret = -1;
    int op_errno = 0;
    int i = 0;
    char *choices = NULL;
    char *status = NULL;
    dict_t *dict = NULL;
    inode_t *inode = NULL;
    afr_private_t *priv = NULL;
    xlator_t **children = NULL;
    call_frame_t *frame = NULL;
    xlator_t *this = NULL;
    loc_t *loc = NULL;
    afr_spb_status_t *data = NULL;

    data = opaque;
    frame = data->frame;
    this = frame->this;
    loc = data->loc;
    priv = this->private;
    children = priv->children;

    inode = afr_inode_find(this, loc->gfid);
    if (!inode)
        goto out;

    dict = dict_new();
    if (!dict) {
        op_errno = ENOMEM;
        ret = -1;
        goto out;
    }

    /* Calculation for string length :
     * (child_count X length of child-name) + SLEN("    Choices :")
     * child-name consists of :
     * a) 251 = max characters for volname according to GD_VOLUME_NAME_MAX
     * b) strlen("-client-00,") assuming 16 replicas
     */
    choices = alloca0(priv->child_count * (256 + SLEN("-client-00,")) +
                      SLEN("    Choices:"));

    ret = afr_is_split_brain(frame, this, inode, loc->gfid, &d_spb, &m_spb);
    if (ret) {
        op_errno = -ret;
        if (ret == -EAGAIN) {
            ret = dict_set_sizen_str_sizen(dict, GF_AFR_SBRAIN_STATUS,
                                           SBRAIN_HEAL_NO_GO_MSG);
            if (ret) {
                gf_msg(this->name, GF_LOG_WARNING, -ret,
                       AFR_MSG_DICT_SET_FAILED,
                       "Failed to set GF_AFR_SBRAIN_STATUS in dict");
            }
        }
        ret = -1;
        goto out;
    }

    if (d_spb || m_spb) {
        sprintf(choices, "    Choices:");
        for (i = 0; i < priv->child_count; i++) {
            strcat(choices, children[i]->name);
            strcat(choices, ",");
        }
        choices[strlen(choices) - 1] = '\0';

        ret = gf_asprintf(&status,
                          "data-split-brain:%s    "
                          "metadata-split-brain:%s%s",
                          (d_spb) ? "yes" : "no", (m_spb) ? "yes" : "no",
                          choices);

        if (-1 == ret) {
            op_errno = ENOMEM;
            goto out;
        }
        ret = dict_set_dynstr_sizen(dict, GF_AFR_SBRAIN_STATUS, status);
        if (ret) {
            op_errno = -ret;
            ret = -1;
            goto out;
        }
    } else {
        ret = dict_set_sizen_str_sizen(dict, GF_AFR_SBRAIN_STATUS,
                                       SFILE_NOT_UNDER_DATA);
        if (ret) {
            op_errno = -ret;
            ret = -1;
            goto out;
        }
    }

    ret = 0;
out:
    AFR_STACK_UNWIND(getxattr, frame, ret, op_errno, dict, NULL);
    if (dict)
        dict_unref(dict);
    if (inode)
        inode_unref(inode);
    return ret;
}

int32_t
afr_heal_splitbrain_file(call_frame_t *frame, xlator_t *this, loc_t *loc)
{
    int ret = 0;
    int op_errno = 0;
    dict_t *dict = NULL;
    afr_local_t *local = NULL;
    afr_local_t *heal_local = NULL;
    call_frame_t *heal_frame = NULL;

    local = frame->local;
    dict = dict_new();
    if (!dict) {
        op_errno = ENOMEM;
        ret = -1;
        goto out;
    }

    heal_frame = afr_frame_create(this, &op_errno);
    if (!heal_frame) {
        ret = -1;
        goto out;
    }
    heal_local = heal_frame->local;
    heal_frame->local = frame->local;
    /*Initiate heal with heal_frame with lk-owner set so that inodelk/entrylk
     * work correctly*/
    ret = afr_selfheal_do(heal_frame, this, loc->gfid);

    if (ret == 1 || ret == 2) {
        ret = dict_set_sizen_str_sizen(dict, "sh-fail-msg",
                                       SFILE_NOT_IN_SPLIT_BRAIN);
        if (ret)
            gf_msg(this->name, GF_LOG_WARNING, -ret, AFR_MSG_DICT_SET_FAILED,
                   "Failed to set sh-fail-msg in dict");
        ret = 0;
        goto out;
    } else {
        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 < 0) {
            op_errno = -ret;
            ret = -1;
        }
    }

out:
    if (heal_frame) {
        heal_frame->local = heal_local;
        AFR_STACK_DESTROY(heal_frame);
    }
    if (local->op == GF_FOP_GETXATTR)
        AFR_STACK_UNWIND(getxattr, frame, ret, op_errno, dict, NULL);
    else if (local->op == GF_FOP_SETXATTR)
        AFR_STACK_UNWIND(setxattr, frame, ret, op_errno, NULL);
    if (dict)
        dict_unref(dict);
    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;
}

void
afr_priv_need_heal_set(afr_private_t *priv, gf_boolean_t need_heal)
{
    LOCK(&priv->lock);
    {
        priv->need_heal = need_heal;
    }
    UNLOCK(&priv->lock);
}

static void
afr_set_need_heal(xlator_t *this, afr_local_t *local)
{
    int i = 0;
    afr_private_t *priv = this->private;
    gf_boolean_t need_heal = _gf_false;

    for (i = 0; i < priv->child_count; i++) {
        if (local->replies[i].valid && local->replies[i].need_heal) {
            need_heal = _gf_true;
            break;
        }
    }
    afr_priv_need_heal_set(priv, need_heal);
    return;
}

gf_boolean_t
afr_get_need_heal(afr_private_t *priv)
{
    gf_boolean_t need_heal = _gf_true;

    LOCK(&priv->lock);
    {
        need_heal = priv->need_heal;
    }
    UNLOCK(&priv->lock);
    return need_heal;
}

static int
afr_fav_child_reset_sink_xattrs_cbk(int ret, call_frame_t *heal_frame,
                                    void *opaque)
{
    call_frame_t *txn_frame = NULL;
    afr_local_t *local = NULL;
    afr_local_t *heal_local = NULL;
    xlator_t *this = NULL;

    heal_local = heal_frame->local;
    txn_frame = heal_local->heal_frame;
    local = txn_frame->local;
    this = txn_frame->this;

    /* Refresh the inode agan and proceed with the transaction.*/
    afr_inode_refresh(txn_frame, this, local->inode, NULL, local->refreshfn);

    AFR_STACK_DESTROY(heal_frame);

    return 0;
}

int
afr_fav_child_reset_sink_xattrs(void *opaque)
{
    call_frame_t *heal_frame = NULL;
    call_frame_t *txn_frame = NULL;
    xlator_t *this = NULL;
    gf_boolean_t d_spb = _gf_false;
    gf_boolean_t m_spb = _gf_false;
    afr_local_t *heal_local = NULL;
    afr_local_t *txn_local = NULL;
    afr_private_t *priv = NULL;
    inode_t *inode = NULL;
    unsigned char *locked_on = NULL;
    unsigned char *sources = NULL;
    unsigned char *sinks = NULL;
    unsigned char *healed_sinks = NULL;
    unsigned char *undid_pending = NULL;
    struct afr_reply *locked_replies = NULL;
    int ret = 0;

    heal_frame = (call_frame_t *)opaque;
    heal_local = heal_frame->local;
    txn_frame = heal_local->heal_frame;
    txn_local = txn_frame->local;
    this = txn_frame->this;
    inode = txn_local->inode;
    priv = this->private;
    locked_on = alloca0(priv->child_count);
    sources = alloca0(priv->child_count);
    sinks = alloca0(priv->child_count);
    healed_sinks = alloca0(priv->child_count);
    undid_pending = alloca0(priv->child_count);
    locked_replies = alloca0(sizeof(*locked_replies) * priv->child_count);

    ret = _afr_is_split_brain(txn_frame, this, txn_local->replies,
                              AFR_DATA_TRANSACTION, &d_spb);

    ret = _afr_is_split_brain(txn_frame, this, txn_local->replies,
                              AFR_METADATA_TRANSACTION, &m_spb);

    /* Take appropriate locks and reset sink xattrs. */
    if (d_spb) {
        ret = afr_selfheal_inodelk(heal_frame, this, inode, this->name, 0, 0,
                                   locked_on);
        {
            if (ret < priv->child_count)
                goto data_unlock;
            ret = __afr_selfheal_data_prepare(
                heal_frame, this, inode, locked_on, sources, sinks,
                healed_sinks, undid_pending, locked_replies, NULL);
        }
    data_unlock:
        afr_selfheal_uninodelk(heal_frame, this, inode, this->name, 0, 0,
                               locked_on);
    }

    if (m_spb) {
        memset(locked_on, 0, sizeof(*locked_on) * priv->child_count);
        memset(undid_pending, 0, sizeof(*undid_pending) * priv->child_count);
        ret = afr_selfheal_inodelk(heal_frame, this, inode, this->name,
                                   LLONG_MAX - 1, 0, locked_on);
        {
            if (ret < priv->child_count)
                goto mdata_unlock;
            ret = __afr_selfheal_metadata_prepare(
                heal_frame, this, inode, locked_on, sources, sinks,
                healed_sinks, undid_pending, locked_replies, NULL);
        }
    mdata_unlock:
        afr_selfheal_uninodelk(heal_frame, this, inode, this->name,
                               LLONG_MAX - 1, 0, locked_on);
    }

    return ret;
}

/*
 * Concatenates the xattrs in local->replies separated by a delimiter.
 */
int
afr_serialize_xattrs_with_delimiter(call_frame_t *frame, xlator_t *this,
                                    char *buf, size_t size,
                                    const char *default_str, int32_t *serz_len,
                                    char delimiter)
{
    afr_private_t *priv = NULL;
    afr_local_t *local = NULL;
    char *xattr = NULL;
    int i = 0;
    size_t len = 0;
    int keylen = 0;
    int ret = -1;

    priv = this->private;
    local = frame->local;

    keylen = strlen(local->cont.getxattr.name);
    for (i = 0; i < priv->child_count; i++) {
        if (!local->replies[i].valid || local->replies[i].op_ret) {
            len += snprintf(buf + len, size - len, "%s%c", default_str,
                            delimiter);
        } else {
            ret = dict_get_strn(local->replies[i].xattr,
                                local->cont.getxattr.name, keylen, &xattr);
            if (ret) {
                gf_msg("TEST", GF_LOG_ERROR, -ret, AFR_MSG_DICT_GET_FAILED,
                       "Failed to get the node_uuid of brick "
                       "%d",
                       i);
                goto out;
            }
            len += snprintf(buf + len, size - len, "%s%c", xattr, delimiter);
        }
        /* No overflow but buffer size is not enough. */
        GF_ASSERT(len <= size);
    }
    buf[--len] = '\0'; /*remove the last delimiter*/
    if (serz_len)
        *serz_len = ++len;
    ret = 0;

out:
    return ret;
}

uint64_t
afr_write_subvol_get(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    uint64_t write_subvol = 0;

    local = frame->local;
    LOCK(&local->inode->lock);
    write_subvol = local->inode_ctx->write_subvol;
    UNLOCK(&local->inode->lock);

    return write_subvol;
}

int
afr_write_subvol_set(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;
    afr_private_t *priv = NULL;
    unsigned char *data_accused = NULL;
    unsigned char *metadata_accused = NULL;
    unsigned char *data_readable = NULL;
    unsigned char *metadata_readable = NULL;
    uint16_t datamap = 0;
    uint16_t metadatamap = 0;
    uint64_t val = 0;
    int event = 0;
    int i = 0;

    local = frame->local;
    priv = this->private;
    data_accused = alloca0(priv->child_count);
    metadata_accused = alloca0(priv->child_count);
    data_readable = alloca0(priv->child_count);
    metadata_readable = alloca0(priv->child_count);
    event = local->event_generation;

    afr_readables_fill(local, this, local->inode, data_accused,
                       metadata_accused, data_readable, metadata_readable,
                       NULL);

    for (i = 0; i < priv->child_count; i++) {
        if (data_readable[i])
            datamap |= (1 << i);
        if (metadata_readable[i])
            metadatamap |= (1 << i);
    }

    val = ((uint64_t)metadatamap) | (((uint64_t)datamap) << 16) |
          (((uint64_t)event) << 32);

    LOCK(&local->inode->lock);
    {
        if (local->inode_ctx->write_subvol == 0 &&
            local->transaction.type == AFR_DATA_TRANSACTION) {
            local->inode_ctx->write_subvol = val;
        }
    }
    UNLOCK(&local->inode->lock);

    return 0;
}

int
afr_write_subvol_reset(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = NULL;

    local = frame->local;
    LOCK(&local->inode->lock);
    {
        GF_ASSERT(local->inode_ctx->lock_count > 0);
        local->inode_ctx->lock_count--;

        if (!local->inode_ctx->lock_count)
            local->inode_ctx->write_subvol = 0;
    }
    UNLOCK(&local->inode->lock);

    return 0;
}

int
afr_set_inode_local(xlator_t *this, afr_local_t *local, inode_t *inode)
{
    afr_inode_ctx_t *inode_ctx = NULL;

    local->inode = inode_ref(inode);
    LOCK(&local->inode->lock);
    {
        inode_ctx = __afr_inode_ctx_get(this, local->inode);
        if (inode_ctx) {
            local->inode_ctx = inode_ctx;
        }
    }
    UNLOCK(&local->inode->lock);
    if (inode_ctx == NULL) {
        gf_msg_callingfn(
            this->name, GF_LOG_ERROR, ENOMEM, AFR_MSG_INODE_CTX_GET_FAILED,
            "Error getting inode ctx %s", uuid_utoa(local->inode->gfid));
        return -1;
    }

    return 0;
}

gf_boolean_t
afr_ta_is_fop_called_from_synctask(xlator_t *this)
{
    struct synctask *task = NULL;
    gf_lkowner_t tmp_owner;

    task = synctask_get();
    if (!task)
        return _gf_false;

    set_lk_owner_from_ptr(&tmp_owner, (void *)this);

    if (!is_same_lkowner(&tmp_owner, &task->frame->root->lk_owner))
        return _gf_false;

    return _gf_true;
}

int
afr_ta_post_op_lock(xlator_t *this, loc_t *loc)
{
    int ret = 0;
    uuid_t gfid = {
        0,
    };
    afr_private_t *priv = this->private;
    gf_boolean_t locked = _gf_false;
    struct gf_flock flock1 = {
        0,
    };
    struct gf_flock flock2 = {
        0,
    };
    int32_t cmd = 0;

    /* Clients must take AFR_TA_DOM_NOTIFY lock only when the previous lock
     * has been released in afr_notify due to upcall notification from shd.
     */
    GF_ASSERT(priv->ta_notify_dom_lock_offset == 0);

    if (!priv->shd.iamshd)
        GF_ASSERT(afr_ta_is_fop_called_from_synctask(this));
    flock1.l_type = F_WRLCK;

    while (!locked) {
        if (priv->shd.iamshd) {
            cmd = F_SETLKW;
            flock1.l_start = 0;
            flock1.l_len = 0;
        } else {
            cmd = F_SETLK;
            gf_uuid_generate(gfid);
            flock1.l_start = gfid_to_ino(gfid);
            if (flock1.l_start < 0)
                flock1.l_start = -flock1.l_start;
            flock1.l_len = 1;
        }
        ret = syncop_inodelk(priv->children[THIN_ARBITER_BRICK_INDEX],
                             AFR_TA_DOM_NOTIFY, loc, cmd, &flock1, NULL, NULL);
        if (!ret) {
            locked = _gf_true;
            priv->ta_notify_dom_lock_offset = flock1.l_start;
        } else if (ret == -EAGAIN) {
            continue;
        } else {
            gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
                   "Failed to get "
                   "AFR_TA_DOM_NOTIFY lock on %s.",
                   loc->name);
            goto out;
        }
    }

    flock2.l_type = F_WRLCK;
    flock2.l_start = 0;
    flock2.l_len = 0;
    ret = syncop_inodelk(priv->children[THIN_ARBITER_BRICK_INDEX],
                         AFR_TA_DOM_MODIFY, loc, F_SETLKW, &flock2, NULL, NULL);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
               "Failed to get AFR_TA_DOM_MODIFY lock on %s.", loc->name);
        flock1.l_type = F_UNLCK;
        ret = syncop_inodelk(priv->children[THIN_ARBITER_BRICK_INDEX],
                             AFR_TA_DOM_NOTIFY, loc, F_SETLK, &flock1, NULL,
                             NULL);
    }
out:
    return ret;
}

int
afr_ta_post_op_unlock(xlator_t *this, loc_t *loc)
{
    afr_private_t *priv = this->private;
    struct gf_flock flock = {
        0,
    };
    int ret = 0;

    if (!priv->shd.iamshd)
        GF_ASSERT(afr_ta_is_fop_called_from_synctask(this));
    flock.l_type = F_UNLCK;
    flock.l_start = 0;
    flock.l_len = 0;

    ret = syncop_inodelk(priv->children[THIN_ARBITER_BRICK_INDEX],
                         AFR_TA_DOM_MODIFY, loc, F_SETLK, &flock, NULL, NULL);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
               "Failed to unlock AFR_TA_DOM_MODIFY lock.");
        goto out;
    }

    if (!priv->shd.iamshd)
        /* Mounts (clients) will not release the AFR_TA_DOM_NOTIFY lock
         * in post-op as they use it as a notification mechanism. When
         * shd sends a lock request on TA during heal, the clients will
         * receive a lock-contention upcall notification upon which they
         * will release the AFR_TA_DOM_NOTIFY lock after completing the
         * in flight I/O.*/
        goto out;

    ret = syncop_inodelk(priv->children[THIN_ARBITER_BRICK_INDEX],
                         AFR_TA_DOM_NOTIFY, loc, F_SETLK, &flock, NULL, NULL);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, -ret, AFR_MSG_THIN_ARB,
               "Failed to unlock AFR_TA_DOM_NOTIFY lock.");
    }
out:
    return ret;
}

call_frame_t *
afr_ta_frame_create(xlator_t *this)
{
    call_frame_t *frame = NULL;
    void *lk_owner = NULL;

    frame = create_frame(this, this->ctx->pool);
    if (!frame)
        return NULL;
    lk_owner = (void *)this;
    afr_set_lk_owner(frame, this, lk_owner);
    return frame;
}

gf_boolean_t
afr_ta_has_quorum(afr_private_t *priv, afr_local_t *local)
{
    int data_count = 0;

    data_count = AFR_COUNT(local->child_up, priv->child_count);
    if (data_count == 2) {
        return _gf_true;
    } else if (data_count == 1 && local->ta_child_up) {
        return _gf_true;
    }

    return _gf_false;
}

static gf_boolean_t
afr_is_add_replica_mount_lookup_on_root(call_frame_t *frame)
{
    afr_local_t *local = NULL;

    if (frame->root->pid != GF_CLIENT_PID_ADD_REPLICA_MOUNT)
        return _gf_false;

    local = frame->local;

    if (local->op != GF_FOP_LOOKUP)
        /* TODO:If the replica count is being increased on a plain distribute
         * volume that was never mounted, we need to allow setxattr on '/' with
         * GF_CLIENT_PID_NO_ROOT_SQUASH to accomodate for DHT layout setting */
        return _gf_false;

    if (local->inode == NULL)
        return _gf_false;

    if (!__is_root_gfid(local->inode->gfid))
        return _gf_false;

    return _gf_true;
}

gf_boolean_t
afr_lookup_has_quorum(call_frame_t *frame, const unsigned int up_children_count)
{
    if (frame && (up_children_count > 0) &&
        afr_is_add_replica_mount_lookup_on_root(frame))
        return _gf_true;

    return _gf_false;
}

void
afr_handle_replies_quorum(call_frame_t *frame, xlator_t *this)
{
    afr_local_t *local = frame->local;
    afr_private_t *priv = this->private;
    unsigned char *success_replies = NULL;

    success_replies = alloca0(priv->child_count);
    afr_fill_success_replies(local, priv, success_replies);

    if (priv->quorum_count && !afr_has_quorum(success_replies, priv, NULL)) {
        local->op_errno = afr_final_errno(local, priv);
        if (!local->op_errno)
            local->op_errno = afr_quorum_errno(priv);
        local->op_ret = -1;
    }
}

gf_boolean_t
afr_ta_dict_contains_pending_xattr(dict_t *dict, afr_private_t *priv, int child)
{
    int *pending = NULL;
    int ret = 0;
    int i = 0;

    ret = dict_get_ptr(dict, priv->pending_key[child], (void *)&pending);
    if (ret == 0) {
        for (i = 0; i < AFR_NUM_CHANGE_LOGS; i++) {
            if (pending[i]) {
                return _gf_true;
            }
        }
    }

    return _gf_false;
}
