/*
   Copyright (c) 2010-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 "quiesce.h"
#include <glusterfs/defaults.h>
#include <glusterfs/call-stub.h>

/* TODO: */
/* Think about 'writev/_*_lk/setattr/xattrop/' fops to do re-transmittion */

void
gf_quiesce_timeout(void *data);

/* Quiesce Specific Functions */
void
gf_quiesce_local_wipe(xlator_t *this, quiesce_local_t *local)
{
    if (!local || !this || !this->private)
        return;

    if (local->loc.inode)
        loc_wipe(&local->loc);
    if (local->fd)
        fd_unref(local->fd);
    GF_FREE(local->name);
    GF_FREE(local->volname);
    if (local->dict)
        dict_unref(local->dict);
    if (local->iobref)
        iobref_unref(local->iobref);
    GF_FREE(local->vector);

    mem_put(local);
}

void
__gf_quiesce_start_timer(xlator_t *this, quiesce_priv_t *priv)
{
    struct timespec timeout = {
        0,
    };

    if (!priv->timer) {
        timeout.tv_sec = priv->timeout;
        timeout.tv_nsec = 0;

        priv->timer = gf_timer_call_after(this->ctx, timeout,
                                          gf_quiesce_timeout, (void *)this);
        if (priv->timer == NULL) {
            gf_log(this->name, GF_LOG_ERROR, "Cannot create timer");
        }
    }
}

static void
__gf_quiesce_cleanup_failover_hosts(xlator_t *this, quiesce_priv_t *priv)
{
    quiesce_failover_hosts_t *tmp = NULL;
    quiesce_failover_hosts_t *failover_host = NULL;

    list_for_each_entry_safe(failover_host, tmp, &priv->failover_list, list)
    {
        GF_FREE(failover_host->addr);
        list_del(&failover_host->list);
        GF_FREE(failover_host);
    }
    return;
}

void
gf_quiesce_populate_failover_hosts(xlator_t *this, quiesce_priv_t *priv,
                                   const char *value)
{
    char *dup_val = NULL;
    char *addr_tok = NULL;
    char *save_ptr = NULL;
    quiesce_failover_hosts_t *failover_host = NULL;

    if (!value)
        goto out;

    dup_val = gf_strdup(value);
    if (!dup_val)
        goto out;

    addr_tok = strtok_r(dup_val, ",", &save_ptr);
    LOCK(&priv->lock);
    {
        if (!list_empty(&priv->failover_list))
            __gf_quiesce_cleanup_failover_hosts(this, priv);

        while (addr_tok) {
            if (!valid_internet_address(addr_tok, _gf_true, _gf_false)) {
                gf_msg(this->name, GF_LOG_INFO, 0, QUIESCE_MSG_INVAL_HOST,
                       "Specified "
                       "invalid internet address:%s",
                       addr_tok);
                continue;
            }
            failover_host = GF_CALLOC(1, sizeof(*failover_host),
                                      gf_quiesce_mt_failover_hosts);
            failover_host->addr = gf_strdup(addr_tok);
            INIT_LIST_HEAD(&failover_host->list);
            list_add(&failover_host->list, &priv->failover_list);
            addr_tok = strtok_r(NULL, ",", &save_ptr);
        }
    }
    UNLOCK(&priv->lock);
    GF_FREE(dup_val);
out:
    return;
}

int32_t
gf_quiesce_failover_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                        int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;

    if (op_ret < 0) {
        /* Failure here doesn't mean the failover to another host didn't
         * succeed, we will know if failover succeeds or not by the
         * CHILD_UP/CHILD_DOWN event. A failure here indicates something
         * went wrong with the submission of failover command, hence
         * just abort the failover attempts without retrying with other
         * hosts.
         */
        gf_msg(this->name, GF_LOG_INFO, op_errno, QUIESCE_MSG_FAILOVER_FAILED,
               "Initiating failover to host:%s failed:", (char *)cookie);
    }

    GF_FREE(cookie);
    STACK_DESTROY(frame->root);

    priv = this->private;
    __gf_quiesce_start_timer(this, priv);

    return 0;
}

int
__gf_quiesce_perform_failover(xlator_t *this)
{
    int ret = 0;
    call_frame_t *frame = NULL;
    dict_t *dict = NULL;
    quiesce_priv_t *priv = NULL;
    quiesce_failover_hosts_t *failover_host = NULL;
    quiesce_failover_hosts_t *host = NULL;

    priv = this->private;

    if (priv->pass_through) {
        gf_msg_trace(this->name, 0,
                     "child is up, hence not "
                     "performing any failover");
        goto out;
    }

    list_for_each_entry(failover_host, &priv->failover_list, list)
    {
        if (failover_host->tried == 0) {
            host = failover_host;
            failover_host->tried = 1;
            break;
        }
    }
    if (!host) {
        /*TODO: Keep trying until any of the gfproxy comes back up.
                Currently it tries failing over once for each host,
                if it doesn't succeed then returns error to mount point
           list_for_each_entry (failover_host,
                        &priv->failover_list, list) {
                failover_host->tried = 0;
        }*/
        gf_msg_debug(this->name, 0,
                     "all the failover hosts have "
                     "been tried and looks like didn't succeed");
        ret = -1;
        goto out;
    }

    frame = create_frame(this, this->ctx->pool);
    if (!frame) {
        gf_msg_debug(this->name, 0, "failed to create the frame");
        ret = -1;
        goto out;
    }

    dict = dict_new();

    ret = dict_set_dynstr(dict, CLIENT_CMD_CONNECT, gf_strdup(host->addr));

    gf_msg_trace(this->name, 0, "Initiating failover to:%s", host->addr);

    STACK_WIND_COOKIE(frame, gf_quiesce_failover_cbk, NULL, FIRST_CHILD(this),
                      FIRST_CHILD(this)->fops->setxattr, NULL, dict, 0, NULL);
out:

    if (dict)
        dict_unref(dict);

    return ret;
}

call_stub_t *
gf_quiesce_dequeue(xlator_t *this)
{
    call_stub_t *stub = NULL;
    quiesce_priv_t *priv = NULL;

    priv = this->private;

    if (!priv || list_empty(&priv->req))
        return NULL;

    LOCK(&priv->lock);
    {
        stub = list_entry(priv->req.next, call_stub_t, list);
        list_del_init(&stub->list);
        priv->queue_size--;
    }
    UNLOCK(&priv->lock);

    return stub;
}

void *
gf_quiesce_dequeue_start(void *data)
{
    xlator_t *this = NULL;
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    this = data;
    priv = this->private;
    THIS = this;

    while (!list_empty(&priv->req)) {
        stub = gf_quiesce_dequeue(this);
        if (stub) {
            call_resume(stub);
        }
    }

    return 0;
}

void
gf_quiesce_timeout(void *data)
{
    xlator_t *this = NULL;
    quiesce_priv_t *priv = NULL;
    int ret = -1;

    this = data;
    priv = this->private;
    THIS = this;

    LOCK(&priv->lock);
    {
        priv->timer = NULL;
        if (priv->pass_through) {
            UNLOCK(&priv->lock);
            goto out;
        }
        ret = __gf_quiesce_perform_failover(THIS);
    }
    UNLOCK(&priv->lock);

    if (ret < 0) {
        priv->pass_through = _gf_true;
        gf_quiesce_dequeue_start(this);
    }

out:
    return;
}

void
gf_quiesce_enqueue(xlator_t *this, call_stub_t *stub)
{
    quiesce_priv_t *priv = NULL;

    priv = this->private;
    if (!priv) {
        gf_log_callingfn(this->name, GF_LOG_ERROR, "this->private == NULL");
        return;
    }

    LOCK(&priv->lock);
    {
        list_add_tail(&stub->list, &priv->req);
        priv->queue_size++;
        __gf_quiesce_start_timer(this, priv);
    }
    UNLOCK(&priv->lock);

    return;
}

/* _CBK function section */

int32_t
quiesce_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                   int32_t op_ret, int32_t op_errno, inode_t *inode,
                   struct iatt *buf, dict_t *dict, struct iatt *postparent)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_lookup_stub(frame, default_lookup_resume, &local->loc,
                               local->dict);
        if (!stub) {
            STACK_UNWIND_STRICT(lookup, frame, -1, ENOMEM, NULL, NULL, NULL,
                                NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(lookup, frame, op_ret, op_errno, inode, buf, dict,
                        postparent);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                 int32_t op_ret, int32_t op_errno, struct iatt *buf,
                 dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_stat_stub(frame, default_stat_resume, &local->loc, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(stat, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(stat, frame, op_ret, op_errno, buf, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_access_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                   int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_access_stub(frame, default_access_resume, &local->loc,
                               local->flag, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(access, frame, -1, ENOMEM, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(access, frame, op_ret, op_errno, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_readlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, const char *path,
                     struct iatt *buf, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_readlink_stub(frame, default_readlink_resume, &local->loc,
                                 local->size, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(readlink, frame, -1, ENOMEM, NULL, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(readlink, frame, op_ret, op_errno, path, buf, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_open_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                 int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_open_stub(frame, default_open_resume, &local->loc,
                             local->flag, local->fd, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(open, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(open, frame, op_ret, op_errno, fd, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_readv_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                  int32_t op_ret, int32_t op_errno, struct iovec *vector,
                  int32_t count, struct iatt *stbuf, struct iobref *iobref,
                  dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_readv_stub(frame, default_readv_resume, local->fd,
                              local->size, local->offset, local->io_flag,
                              xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(readv, frame, -1, ENOMEM, NULL, 0, NULL, NULL,
                                NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(readv, frame, op_ret, op_errno, vector, count, stbuf,
                        iobref, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_flush_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                  int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_flush_stub(frame, default_flush_resume, local->fd, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(flush, frame, -1, ENOMEM, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(flush, frame, op_ret, op_errno, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_fsync_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                  int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
                  struct iatt *postbuf, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_fsync_stub(frame, default_fsync_resume, local->fd,
                              local->flag, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(fsync, frame, -1, ENOMEM, NULL, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(fsync, frame, op_ret, op_errno, prebuf, postbuf, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_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)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_fstat_stub(frame, default_fstat_resume, local->fd, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(fstat, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(fstat, frame, op_ret, op_errno, buf, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_opendir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_opendir_stub(frame, default_opendir_resume, &local->loc,
                                local->fd, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(opendir, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(opendir, frame, op_ret, op_errno, fd, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_fsyncdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_fsyncdir_stub(frame, default_fsyncdir_resume, local->fd,
                                 local->flag, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(fsyncdir, frame, -1, ENOMEM, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(fsyncdir, frame, op_ret, op_errno, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_statfs_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                   int32_t op_ret, int32_t op_errno, struct statvfs *buf,
                   dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_statfs_stub(frame, default_statfs_resume, &local->loc,
                               xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(statfs, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(statfs, frame, op_ret, op_errno, buf, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_fgetxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, dict_t *dict,
                      dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_fgetxattr_stub(frame, default_fgetxattr_resume, local->fd,
                                  local->name, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(fgetxattr, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(fgetxattr, frame, op_ret, op_errno, dict, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_getxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, dict_t *dict,
                     dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_getxattr_stub(frame, default_getxattr_resume, &local->loc,
                                 local->name, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(getxattr, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(getxattr, frame, op_ret, op_errno, dict, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_rchecksum_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, uint32_t weak_checksum,
                      uint8_t *strong_checksum, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_rchecksum_stub(frame, default_rchecksum_resume, local->fd,
                                  local->offset, local->flag, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(rchecksum, frame, -1, ENOMEM, 0, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(rchecksum, frame, op_ret, op_errno, weak_checksum,
                        strong_checksum, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_readdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, gf_dirent_t *entries,
                    dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_readdir_stub(frame, default_readdir_resume, local->fd,
                                local->size, local->offset, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(readdir, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(readdir, frame, op_ret, op_errno, entries, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int32_t
quiesce_readdirp_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, gf_dirent_t *entries,
                     dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_readdirp_stub(frame, default_readdirp_resume, local->fd,
                                 local->size, local->offset, local->dict);
        if (!stub) {
            STACK_UNWIND_STRICT(readdirp, frame, -1, ENOMEM, NULL, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(readdirp, frame, op_ret, op_errno, entries, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

#if 0

int32_t
quiesce_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                    int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
                    struct iatt *postbuf, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_writev_stub (frame, default_writev_resume,
                                        local->fd, local->vector, local->flag,
                                        local->offset, local->io_flags,
                                        local->iobref, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (writev, frame, -1, ENOMEM,
                                             NULL, NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (writev, frame, op_ret, op_errno, prebuf, postbuf, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_xattrop_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, dict_t *dict, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_xattrop_stub (frame, default_xattrop_resume,
                                         &local->loc, local->xattrop_flags,
                                         local->dict, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (xattrop, frame, -1, ENOMEM,
                                             NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (xattrop, frame, op_ret, op_errno, dict, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_fxattrop_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, dict_t *dict, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_fxattrop_stub (frame, default_fxattrop_resume,
                                          local->fd, local->xattrop_flags,
                                          local->dict, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (fxattrop, frame, -1, ENOMEM,
                                             NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (fxattrop, frame, op_ret, op_errno, dict, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_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)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_lk_stub (frame, default_lk_resume,
                                    local->fd, local->flag, &local->flock, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (lk, frame, -1, ENOMEM,
                                             NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (lk, frame, op_ret, op_errno, lock, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_inodelk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_inodelk_stub (frame, default_inodelk_resume,
                                         local->volname, &local->loc,
                                         local->flag, &local->flock, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (inodelk, frame, -1, ENOMEM, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (inodelk, frame, op_ret, op_errno, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}


int32_t
quiesce_finodelk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_finodelk_stub (frame, default_finodelk_resume,
                                         local->volname, local->fd,
                                         local->flag, &local->flock, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (finodelk, frame, -1, ENOMEM, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (finodelk, frame, op_ret, op_errno, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_entrylk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_entrylk_stub (frame, default_entrylk_resume,
                                         local->volname, &local->loc,
                                         local->name, local->cmd, local->type, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (entrylk, frame, -1, ENOMEM, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (entrylk, frame, op_ret, op_errno, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_fentrylk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_fentrylk_stub (frame, default_fentrylk_resume,
                                          local->volname, local->fd,
                                          local->name, local->cmd, local->type, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (fentrylk, frame, -1, ENOMEM, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (fentrylk, frame, op_ret, op_errno, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                     int32_t op_ret, int32_t op_errno, struct iatt *statpre,
                     struct iatt *statpost, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;
        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_setattr_stub (frame, default_setattr_resume,
                                         &local->loc, &local->stbuf, local->flag, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (setattr, frame, -1, ENOMEM,
                                             NULL, NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, statpre,
                             statpost, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

int32_t
quiesce_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                      int32_t op_ret, int32_t op_errno, struct iatt *statpre,
                      struct iatt *statpost, dict_t *xdata)
{
        quiesce_priv_t *priv = NULL;
        call_stub_t    *stub = NULL;
        quiesce_local_t *local = NULL;

        priv = this->private;

        local = frame->local;
        frame->local = NULL;

        if ((op_ret == -1) && (op_errno == ENOTCONN)) {
                /* Re-transmit (by putting in the queue) */
                stub = fop_fsetattr_stub (frame, default_fsetattr_resume,
                                          local->fd, &local->stbuf, local->flag, xdata);
                if (!stub) {
                        STACK_UNWIND_STRICT (fsetattr, frame, -1, ENOMEM,
                                             NULL, NULL, NULL);
                        goto out;
                }

                gf_quiesce_enqueue (this, stub);
                goto out;
        }

        STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, statpre,
                             statpost, xdata);
out:
        gf_quiesce_local_wipe (this, local);

        return 0;
}

#endif /* if 0 */

/* FOP */

/* No retransmittion */

int32_t
quiesce_removexattr(call_frame_t *frame, xlator_t *this, loc_t *loc,
                    const char *name, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_removexattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->removexattr, loc, name, xdata);
        return 0;
    }

    stub = fop_removexattr_stub(frame, default_removexattr_resume, loc, name,
                                xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(removexattr, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fremovexattr(call_frame_t *frame, xlator_t *this, fd_t *fd,
                     const char *name, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_fremovexattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fremovexattr, fd, name, xdata);
        return 0;
    }

    stub = fop_fremovexattr_stub(frame, default_fremovexattr_resume, fd, name,
                                 xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fremovexattr, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
                 dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_truncate_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->truncate, loc, offset, xdata);
        return 0;
    }

    stub = fop_truncate_stub(frame, default_truncate_resume, loc, offset,
                             xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(truncate, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fsetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict,
                  int32_t flags, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_fsetxattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fsetxattr, fd, dict, flags, xdata);
        return 0;
    }

    stub = fop_fsetxattr_stub(frame, default_fsetxattr_resume, fd, dict, flags,
                              xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fsetxattr, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_setxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict,
                 int32_t flags, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_setxattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->setxattr, loc, dict, flags, xdata);
        return 0;
    }

    stub = fop_setxattr_stub(frame, default_setxattr_resume, loc, dict, flags,
                             xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(setxattr, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
               mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        /* Don't send O_APPEND below, as write() re-transmittions can
           fail with O_APPEND */
        STACK_WIND(frame, default_create_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->create, loc, (flags & ~O_APPEND),
                   mode, umask, fd, xdata);
        return 0;
    }

    stub = fop_create_stub(frame, default_create_resume, loc,
                           (flags & ~O_APPEND), mode, umask, fd, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(create, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
             dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_link_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata);
        return 0;
    }

    stub = fop_link_stub(frame, default_link_resume, oldloc, newloc, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(link, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc,
               loc_t *newloc, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_rename_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata);
        return 0;
    }

    stub = fop_rename_stub(frame, default_rename_resume, oldloc, newloc, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(rename, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int
quiesce_symlink(call_frame_t *frame, xlator_t *this, const char *linkpath,
                loc_t *loc, mode_t umask, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_symlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->symlink, linkpath, loc, umask,
                   xdata);
        return 0;
    }

    stub = fop_symlink_stub(frame, default_symlink_resume, linkpath, loc, umask,
                            xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(symlink, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int
quiesce_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
              dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_rmdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->rmdir, loc, flags, xdata);
        return 0;
    }

    stub = fop_rmdir_stub(frame, default_rmdir_resume, loc, flags, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(rmdir, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag,
               dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_unlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata);
        return 0;
    }

    stub = fop_unlink_stub(frame, default_unlink_resume, loc, xflag, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(unlink, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int
quiesce_mkdir(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
              mode_t umask, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_mkdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->mkdir, loc, mode, umask, xdata);
        return 0;
    }

    stub = fop_mkdir_stub(frame, default_mkdir_resume, loc, mode, umask, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(mkdir, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int
quiesce_mknod(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
              dev_t rdev, mode_t umask, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_mknod_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->mknod, loc, mode, rdev, umask,
                   xdata);
        return 0;
    }

    stub = fop_mknod_stub(frame, default_mknod_resume, loc, mode, rdev, umask,
                          xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(mknod, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
                            NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
                  dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv->pass_through) {
        STACK_WIND(frame, default_ftruncate_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata);
        return 0;
    }

    stub = fop_ftruncate_stub(frame, default_ftruncate_resume, fd, offset,
                              xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(ftruncate, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

/* Re-transmittion */

int32_t
quiesce_readlink(call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size,
                 dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        local->size = size;
        frame->local = local;

        STACK_WIND(frame, quiesce_readlink_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->readlink, loc, size, xdata);
        return 0;
    }

    stub = fop_readlink_stub(frame, default_readlink_resume, loc, size, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(readlink, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_access(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask,
               dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        local->flag = mask;
        frame->local = local;

        STACK_WIND(frame, quiesce_access_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->access, loc, mask, xdata);
        return 0;
    }

    stub = fop_access_stub(frame, default_access_resume, loc, mask, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(access, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fgetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd,
                  const char *name, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        if (name)
            local->name = gf_strdup(name);

        frame->local = local;

        STACK_WIND(frame, quiesce_fgetxattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fgetxattr, fd, name, xdata);
        return 0;
    }

    stub = fop_fgetxattr_stub(frame, default_fgetxattr_resume, fd, name, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fgetxattr, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_statfs(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        frame->local = local;

        STACK_WIND(frame, quiesce_statfs_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->statfs, loc, xdata);
        return 0;
    }

    stub = fop_statfs_stub(frame, default_statfs_resume, loc, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(statfs, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fsyncdir(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags,
                 dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->flag = flags;
        frame->local = local;

        STACK_WIND(frame, quiesce_fsyncdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fsyncdir, fd, flags, xdata);
        return 0;
    }

    stub = fop_fsyncdir_stub(frame, default_fsyncdir_resume, fd, flags, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fsyncdir, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_opendir(call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,
                dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        local->fd = fd_ref(fd);
        frame->local = local;

        STACK_WIND(frame, quiesce_opendir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->opendir, loc, fd, xdata);
        return 0;
    }

    stub = fop_opendir_stub(frame, default_opendir_resume, loc, fd, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(opendir, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fstat(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        frame->local = local;

        STACK_WIND(frame, quiesce_fstat_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fstat, fd, xdata);
        return 0;
    }

    stub = fop_fstat_stub(frame, default_fstat_resume, fd, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fstat, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fsync(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags,
              dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->flag = flags;
        frame->local = local;

        STACK_WIND(frame, quiesce_fsync_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fsync, fd, flags, xdata);
        return 0;
    }

    stub = fop_fsync_stub(frame, default_fsync_resume, fd, flags, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fsync, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_flush(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        frame->local = local;

        STACK_WIND(frame, quiesce_flush_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->flush, fd, xdata);
        return 0;
    }

    stub = fop_flush_stub(frame, default_flush_resume, fd, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(flush, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_writev(call_frame_t *frame, xlator_t *this, fd_t *fd,
               struct iovec *vector, int32_t count, off_t off, uint32_t flags,
               struct iobref *iobref, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_writev_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->writev, fd, vector, count, off,
                   flags, iobref, xdata);
        return 0;
    }

    stub = fop_writev_stub(frame, default_writev_resume, fd, vector, count, off,
                           flags, iobref, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(writev, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_readv(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
              off_t offset, uint32_t flags, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->size = size;
        local->offset = offset;
        local->io_flag = flags;
        frame->local = local;

        STACK_WIND(frame, quiesce_readv_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->readv, fd, size, offset, flags,
                   xdata);
        return 0;
    }

    stub = fop_readv_stub(frame, default_readv_resume, fd, size, offset, flags,
                          xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(readv, frame, -1, ENOMEM, NULL, 0, NULL, NULL,
                            NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_open(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
             fd_t *fd, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        local->fd = fd_ref(fd);

        /* Don't send O_APPEND below, as write() re-transmittions can
           fail with O_APPEND */
        local->flag = (flags & ~O_APPEND);
        frame->local = local;

        STACK_WIND(frame, quiesce_open_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->open, loc, (flags & ~O_APPEND), fd,
                   xdata);
        return 0;
    }

    stub = fop_open_stub(frame, default_open_resume, loc, (flags & ~O_APPEND),
                         fd, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(open, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_getxattr(call_frame_t *frame, xlator_t *this, loc_t *loc,
                 const char *name, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        if (name)
            local->name = gf_strdup(name);

        frame->local = local;

        STACK_WIND(frame, quiesce_getxattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->getxattr, loc, name, xdata);
        return 0;
    }

    stub = fop_getxattr_stub(frame, default_getxattr_resume, loc, name, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(getxattr, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_xattrop(call_frame_t *frame, xlator_t *this, loc_t *loc,
                gf_xattrop_flags_t flags, dict_t *dict, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_xattrop_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->xattrop, loc, flags, dict, xdata);
        return 0;
    }

    stub = fop_xattrop_stub(frame, default_xattrop_resume, loc, flags, dict,
                            xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(xattrop, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fxattrop(call_frame_t *frame, xlator_t *this, fd_t *fd,
                 gf_xattrop_flags_t flags, dict_t *dict, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_fxattrop_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fxattrop, fd, flags, dict, xdata);
        return 0;
    }

    stub = fop_fxattrop_stub(frame, default_fxattrop_resume, fd, flags, dict,
                             xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fxattrop, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_lk(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t cmd,
           struct gf_flock *lock, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_lk_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->lk, fd, cmd, lock, xdata);
        return 0;
    }

    stub = fop_lk_stub(frame, default_lk_resume, fd, cmd, lock, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(lk, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_inodelk(call_frame_t *frame, xlator_t *this, const char *volume,
                loc_t *loc, int32_t cmd, struct gf_flock *lock, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_inodelk_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->inodelk, volume, loc, cmd, lock,
                   xdata);
        return 0;
    }

    stub = fop_inodelk_stub(frame, default_inodelk_resume, volume, loc, cmd,
                            lock, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(inodelk, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_finodelk(call_frame_t *frame, xlator_t *this, const char *volume,
                 fd_t *fd, int32_t cmd, struct gf_flock *lock, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_finodelk_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->finodelk, volume, fd, cmd, lock,
                   xdata);
        return 0;
    }

    stub = fop_finodelk_stub(frame, default_finodelk_resume, volume, fd, cmd,
                             lock, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(finodelk, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_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)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_entrylk_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->entrylk, volume, loc, basename, cmd,
                   type, xdata);
        return 0;
    }

    stub = fop_entrylk_stub(frame, default_entrylk_resume, volume, loc,
                            basename, cmd, type, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(entrylk, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_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)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_fentrylk_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fentrylk, volume, fd, basename, cmd,
                   type, xdata);
        return 0;
    }

    stub = fop_fentrylk_stub(frame, default_fentrylk_resume, volume, fd,
                             basename, cmd, type, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fentrylk, frame, -1, ENOMEM, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_rchecksum(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
                  int32_t len, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->offset = offset;
        local->flag = len;
        frame->local = local;

        STACK_WIND(frame, quiesce_rchecksum_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->rchecksum, fd, offset, len, xdata);
        return 0;
    }

    stub = fop_rchecksum_stub(frame, default_rchecksum_resume, fd, offset, len,
                              xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(rchecksum, frame, -1, ENOMEM, 0, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_readdir(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
                off_t off, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->size = size;
        local->offset = off;
        frame->local = local;

        STACK_WIND(frame, quiesce_readdir_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->readdir, fd, size, off, xdata);
        return 0;
    }

    stub = fop_readdir_stub(frame, default_readdir_resume, fd, size, off,
                            xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(readdir, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_readdirp(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
                 off_t off, dict_t *dict)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->size = size;
        local->offset = off;
        local->dict = dict_ref(dict);
        frame->local = local;

        STACK_WIND(frame, quiesce_readdirp_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->readdirp, fd, size, off, dict);
        return 0;
    }

    stub = fop_readdirp_stub(frame, default_readdirp_resume, fd, size, off,
                             dict);
    if (!stub) {
        STACK_UNWIND_STRICT(readdirp, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc,
                struct iatt *stbuf, int32_t valid, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_setattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->setattr, loc, stbuf, valid, xdata);
        return 0;
    }

    stub = fop_setattr_stub(frame, default_setattr_resume, loc, stbuf, valid,
                            xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(setattr, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_stat(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        frame->local = local;

        STACK_WIND(frame, quiesce_stat_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->stat, loc, xdata);
        return 0;
    }

    stub = fop_stat_stub(frame, default_stat_resume, loc, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(stat, frame, -1, ENOMEM, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc,
               dict_t *xattr_req)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        loc_dup(loc, &local->loc);
        local->dict = dict_ref(xattr_req);
        frame->local = local;

        STACK_WIND(frame, quiesce_lookup_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->lookup, loc, xattr_req);
        return 0;
    }

    stub = fop_lookup_stub(frame, default_lookup_resume, loc, xattr_req);
    if (!stub) {
        STACK_UNWIND_STRICT(lookup, frame, -1, ENOMEM, NULL, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fsetattr(call_frame_t *frame, xlator_t *this, fd_t *fd,
                 struct iatt *stbuf, int32_t valid, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_fsetattr_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fsetattr, fd, stbuf, valid, xdata);
        return 0;
    }

    stub = fop_fsetattr_stub(frame, default_fsetattr_resume, fd, stbuf, valid,
                             xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fsetattr, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
quiesce_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t mode,
                  off_t offset, size_t len, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        STACK_WIND(frame, default_fallocate_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->fallocate, fd, mode, offset, len,
                   xdata);
        return 0;
    }

    stub = fop_fallocate_stub(frame, default_fallocate_resume, fd, mode, offset,
                              len, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(fallocate, frame, -1, ENOMEM, NULL, NULL, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int
quiesce_seek_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
                 int32_t op_ret, int32_t op_errno, off_t offset, dict_t *xdata)
{
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    local = frame->local;
    frame->local = NULL;
    if ((op_ret == -1) && (op_errno == ENOTCONN)) {
        /* Re-transmit (by putting in the queue) */
        stub = fop_seek_stub(frame, default_seek_resume, local->fd,
                             local->offset, local->what, xdata);
        if (!stub) {
            STACK_UNWIND_STRICT(seek, frame, -1, ENOMEM, 0, NULL);
            goto out;
        }

        gf_quiesce_enqueue(this, stub);
        goto out;
    }

    STACK_UNWIND_STRICT(seek, frame, op_ret, op_errno, offset, xdata);
out:
    gf_quiesce_local_wipe(this, local);

    return 0;
}

int
quiesce_seek(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
             gf_seek_what_t what, dict_t *xdata)
{
    quiesce_priv_t *priv = NULL;
    call_stub_t *stub = NULL;
    quiesce_local_t *local = NULL;

    priv = this->private;

    if (priv && priv->pass_through) {
        local = mem_get0(priv->local_pool);
        local->fd = fd_ref(fd);
        local->offset = offset;
        local->what = what;

        frame->local = local;

        STACK_WIND(frame, quiesce_seek_cbk, FIRST_CHILD(this),
                   FIRST_CHILD(this)->fops->seek, fd, offset, what, xdata);
        return 0;
    }

    stub = fop_seek_stub(frame, default_seek_resume, fd, offset, what, xdata);
    if (!stub) {
        STACK_UNWIND_STRICT(seek, frame, -1, ENOMEM, 0, NULL);
        return 0;
    }

    gf_quiesce_enqueue(this, stub);

    return 0;
}

int32_t
mem_acct_init(xlator_t *this)
{
    int ret = -1;

    ret = xlator_mem_acct_init(this, gf_quiesce_mt_end + 1);

    return ret;
}

int
reconfigure(xlator_t *this, dict_t *options)
{
    int32_t ret = -1;
    quiesce_priv_t *priv = NULL;

    priv = this->private;

    GF_OPTION_RECONF("timeout", priv->timeout, options, time, out);
    GF_OPTION_RECONF("failover-hosts", priv->failover_hosts, options, str, out);
    gf_quiesce_populate_failover_hosts(this, priv, priv->failover_hosts);

    ret = 0;
out:
    return ret;
}

int
init(xlator_t *this)
{
    int ret = -1;
    quiesce_priv_t *priv = NULL;

    if (!this->children || this->children->next) {
        gf_log(this->name, GF_LOG_ERROR,
               "'quiesce' not configured with exactly one child");
        goto out;
    }

    if (!this->parents) {
        gf_log(this->name, GF_LOG_WARNING, "dangling volume. check volfile ");
    }

    priv = GF_CALLOC(1, sizeof(*priv), gf_quiesce_mt_priv_t);
    if (!priv)
        goto out;

    INIT_LIST_HEAD(&priv->failover_list);

    GF_OPTION_INIT("timeout", priv->timeout, time, out);
    GF_OPTION_INIT("failover-hosts", priv->failover_hosts, str, out);
    gf_quiesce_populate_failover_hosts(this, priv, priv->failover_hosts);

    priv->local_pool = mem_pool_new(quiesce_local_t,
                                    GF_FOPS_EXPECTED_IN_PARALLEL);

    LOCK_INIT(&priv->lock);
    priv->pass_through = _gf_false;

    INIT_LIST_HEAD(&priv->req);

    this->private = priv;
    ret = 0;
out:
    return ret;
}

void
fini(xlator_t *this)
{
    quiesce_priv_t *priv = NULL;

    priv = this->private;
    if (!priv)
        goto out;
    this->private = NULL;

    mem_pool_destroy(priv->local_pool);
    priv->local_pool = NULL;
    LOCK_DESTROY(&priv->lock);
    GF_FREE(priv);
out:
    return;
}

int
notify(xlator_t *this, int event, void *data, ...)
{
    int ret = 0;
    quiesce_priv_t *priv = NULL;

    priv = this->private;
    if (!priv)
        goto out;

    switch (event) {
        case GF_EVENT_CHILD_UP: {
            ret = gf_thread_create(&priv->thr, NULL, gf_quiesce_dequeue_start,
                                   this, "quiesce");
            if (ret) {
                gf_log(this->name, GF_LOG_ERROR,
                       "failed to create the quiesce-dequeue thread");
            }

            LOCK(&priv->lock);
            {
                priv->pass_through = _gf_true;
            }
            UNLOCK(&priv->lock);
            break;
        }
        case GF_EVENT_CHILD_DOWN:
            LOCK(&priv->lock);
            {
                priv->pass_through = _gf_false;
                __gf_quiesce_start_timer(this, priv);
            }
            UNLOCK(&priv->lock);
            break;
        default:
            break;
    }

    ret = default_notify(this, event, data);
out:
    return ret;
}

struct xlator_fops fops = {
    /* write/modifying fops */
    .mknod = quiesce_mknod,
    .create = quiesce_create,
    .truncate = quiesce_truncate,
    .ftruncate = quiesce_ftruncate,
    .setxattr = quiesce_setxattr,
    .fsetxattr = quiesce_fsetxattr,
    .removexattr = quiesce_removexattr,
    .fremovexattr = quiesce_fremovexattr,
    .symlink = quiesce_symlink,
    .unlink = quiesce_unlink,
    .link = quiesce_link,
    .mkdir = quiesce_mkdir,
    .rmdir = quiesce_rmdir,
    .rename = quiesce_rename,
    .fallocate = quiesce_fallocate,

    /* The below calls are known to change state, hence
       re-transmittion is not advised */
    .lk = quiesce_lk,
    .inodelk = quiesce_inodelk,
    .finodelk = quiesce_finodelk,
    .entrylk = quiesce_entrylk,
    .fentrylk = quiesce_fentrylk,
    .xattrop = quiesce_xattrop,
    .fxattrop = quiesce_fxattrop,
    .setattr = quiesce_setattr,
    .fsetattr = quiesce_fsetattr,

    /* Special case, re-transmittion is not harmful *
     * as offset is properly sent from above layers */
    /* TODO: not re-transmitted as of now */
    .writev = quiesce_writev,

    /* re-transmittable fops */
    .lookup = quiesce_lookup,
    .stat = quiesce_stat,
    .fstat = quiesce_fstat,
    .access = quiesce_access,
    .readlink = quiesce_readlink,
    .getxattr = quiesce_getxattr,
    .fgetxattr = quiesce_fgetxattr,
    .open = quiesce_open,
    .readv = quiesce_readv,
    .flush = quiesce_flush,
    .fsync = quiesce_fsync,
    .statfs = quiesce_statfs,
    .opendir = quiesce_opendir,
    .readdir = quiesce_readdir,
    .readdirp = quiesce_readdirp,
    .fsyncdir = quiesce_fsyncdir,
    .seek = quiesce_seek,
};

struct xlator_dumpops dumpops;

struct xlator_cbks cbks;

struct volume_options options[] = {
    {
        .key = {"timeout"},
        .type = GF_OPTION_TYPE_TIME,
        .default_value = "45",
        .description =
            "After 'timeout' seconds since the time 'quiesce' "
            "option was set to \"!pass-through\", acknowledgements to file "
            "operations are no longer quiesced and previously "
            "quiesced acknowledgements are sent to the application",
        .op_version = {GD_OP_VERSION_4_0_0},
        .flags = OPT_FLAG_CLIENT_OPT | OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
    },
    {.key = {"failover-hosts"},
     .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
     .op_version = {GD_OP_VERSION_4_0_0},
     .flags = OPT_FLAG_CLIENT_OPT | OPT_FLAG_SETTABLE | OPT_FLAG_DOC,
     .description = "It is a comma separated list of hostname/IP "
                    "addresses. It Specifies the list of hosts where "
                    "the gfproxy daemons are running, to which the "
                    "the thin clients can failover to."},
    {.key = {NULL}},
};

xlator_api_t xlator_api = {
    .init = init,
    .fini = fini,
    .notify = notify,
    .reconfigure = reconfigure,
    .mem_acct_init = mem_acct_init,
    .op_version = {GD_OP_VERSION_3_12_0},
    .dumpops = &dumpops,
    .fops = &fops,
    .cbks = &cbks,
    .options = options,
    .identifier = "quiesce",
    .category = GF_TECH_PREVIEW,
};
