/*
Copyright (c) 2012-2014 DataLab, s.l. <http://www.datalab.es>
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 "ec-mem-types.h"
#include "ec-helpers.h"
#include "ec-common.h"
#include "ec-data.h"
#include "ec-messages.h"
ec_cbk_data_t *
ec_cbk_data_allocate(call_frame_t *frame, xlator_t *this, ec_fop_data_t *fop,
int32_t id, int32_t idx, int32_t op_ret, int32_t op_errno)
{
ec_cbk_data_t *cbk;
ec_t *ec = this->private;
if (fop->xl != this) {
gf_msg(this->name, GF_LOG_ERROR, EINVAL, EC_MSG_XLATOR_MISMATCH,
"Mismatching xlators between request "
"and answer (req=%s, ans=%s).",
fop->xl->name, this->name);
return NULL;
}
if (fop->frame != frame) {
gf_msg(this->name, GF_LOG_ERROR, EINVAL, EC_MSG_FRAME_MISMATCH,
"Mismatching frames between request "
"and answer (req=%p, ans=%p).",
fop->frame, frame);
return NULL;
}
if (fop->id != id) {
gf_msg(this->name, GF_LOG_ERROR, EINVAL, EC_MSG_FOP_MISMATCH,
"Mismatching fops between request "
"and answer (req=%d, ans=%d).",
fop->id, id);
return NULL;
}
cbk = mem_get0(ec->cbk_pool);
if (cbk == NULL) {
gf_msg(this->name, GF_LOG_ERROR, ENOMEM, EC_MSG_NO_MEMORY,
"Failed to allocate memory for an "
"answer.");
return NULL;
}
cbk->fop = fop;
cbk->idx = idx;
cbk->mask = 1ULL << idx;
cbk->count = 1;
cbk->op_ret = op_ret;
cbk->op_errno = op_errno;
INIT_LIST_HEAD(&cbk->entries.list);
LOCK(&fop->lock);
list_add_tail(&cbk->answer_list, &fop->answer_list);
UNLOCK(&fop->lock);
return cbk;
}
void
ec_cbk_data_destroy(ec_cbk_data_t *cbk)
{
if (cbk->xdata != NULL) {
dict_unref(cbk->xdata);
}
if (cbk->dict != NULL) {
dict_unref(cbk->dict);
}
if (cbk->inode != NULL) {
inode_unref(cbk->inode);
}
if (cbk->fd != NULL) {
fd_unref(cbk->fd);
}
if (cbk->buffers != NULL) {
iobref_unref(cbk->buffers);
}
GF_FREE(cbk->vector);
gf_dirent_free(&cbk->entries);
GF_FREE(cbk->str);
mem_put(cbk);
}
ec_fop_data_t *
ec_fop_data_allocate(call_frame_t *frame, xlator_t *this, int32_t id,
uint32_t flags, uintptr_t target, uint32_t fop_flags,
ec_wind_f wind, ec_handler_f handler, ec_cbk_t cbks,
void *data)
{
ec_fop_data_t *fop, *parent;
ec_t *ec = this->private;
fop = mem_get0(ec->fop_pool);
if (fop == NULL) {
gf_msg(this->name, GF_LOG_ERROR, ENOMEM, EC_MSG_NO_MEMORY,
"Failed to allocate memory for a "
"request.");
return NULL;
}
INIT_LIST_HEAD(&fop->cbk_list);
INIT_LIST_HEAD(&fop->healer);
INIT_LIST_HEAD(&fop->answer_list);
INIT_LIST_HEAD(&fop->pending_list);
INIT_LIST_HEAD(&fop->locks[0].owner_list);
INIT_LIST_HEAD(&fop->locks[0].wait_list);
INIT_LIST_HEAD(&fop->locks[1].owner_list);
INIT_LIST_HEAD(&fop->locks[1].wait_list);
fop->xl = this;
fop->req_frame = frame;
/* fops need a private frame to be able to execute some postop operations
* even if the original fop has completed and reported back to the upper
* xlator and it has destroyed the base frame.
*
* TODO: minimize usage of private frames. Reuse req_frame as much as
* possible.
*/
if (frame != NULL) {
fop->frame = copy_frame(frame);
} else {
fop->frame = create_frame(this, this->ctx->pool);
}
if (fop->frame == NULL) {
gf_msg(this->name, GF_LOG_ERROR, ENOMEM, EC_MSG_NO_MEMORY,
"Failed to create a private frame "
"for a request");
mem_put(fop);
return NULL;
}
fop->id = id;
fop->refs = 1;
fop->flags = flags;
fop->minimum = EC_FOP_MINIMUM(fop_flags);
fop->fop_flags = EC_FOP_FLAGS(fop_flags);
fop->mask = target;
fop->wind = wind;
fop->handler = handler;
fop->cbks = cbks;
fop->data = data;
fop->uid = fop->frame->root->uid;
fop->gid = fop->frame->root->gid;
LOCK_INIT(&fop->lock);
fop->frame->local = fop;
if (frame != NULL) {
parent = frame->local;
if (parent != NULL) {
ec_sleep(parent);
}
fop->parent = parent;
}
LOCK(&ec->lock);
list_add_tail(&fop->pending_list, &ec->pending_fops);
UNLOCK(&ec->lock);
return fop;
}
void
ec_fop_data_acquire(ec_fop_data_t *fop)
{
LOCK(&fop->lock);
ec_trace("ACQUIRE", fop, "");
fop->refs++;
UNLOCK(&fop->lock);
}
static void
ec_handle_last_pending_fop_completion(ec_fop_data_t *fop, gf_boolean_t *notify)
{
ec_t *ec = fop->xl->private;
if (!list_empty(&fop->pending_list)) {
LOCK(&ec->lock);
{
list_del_init(&fop->pending_list);
*notify = list_empty(&ec->pending_fops);
}
UNLOCK(&ec->lock);
}
}
void
ec_fop_cleanup(ec_fop_data_t *fop)
{
ec_cbk_data_t *cbk, *tmp;
list_for_each_entry_safe(cbk, tmp, &fop->answer_list, answer_list)
{
list_del_init(&cbk->answer_list);
ec_cbk_data_destroy(cbk);
}
INIT_LIST_HEAD(&fop->cbk_list);
fop->answer = NULL;
}
void
ec_fop_data_release(ec_fop_data_t *fop)
{
ec_t *ec = NULL;
int32_t refs;
gf_boolean_t notify = _gf_false;
LOCK(&fop->lock);
ec_trace("RELEASE", fop, "");
GF_ASSERT(fop->refs > 0);
refs = --fop->refs;
UNLOCK(&fop->lock);
if (refs == 0) {
fop->frame->local = NULL;
STACK_DESTROY(fop->frame->root);
LOCK_DESTROY(&fop->lock);
if (fop->xdata != NULL) {
dict_unref(fop->xdata);
}
if (fop->dict != NULL) {
dict_unref(fop->dict);
}
if (fop->inode != NULL) {
inode_unref(fop->inode);
}
if (fop->fd != NULL) {
fd_unref(fop->fd);
}
if (fop->buffers != NULL) {
iobref_unref(fop->buffers);
}
GF_FREE(fop->vector);
GF_FREE(fop->str[0]);
GF_FREE(fop->str[1]);
loc_wipe(&fop->loc[0]);
loc_wipe(&fop->loc[1]);
GF_FREE(fop->errstr);
ec_resume_parent(fop);
ec_fop_cleanup(fop);
ec = fop->xl->private;
ec_handle_last_pending_fop_completion(fop, ¬ify);
ec_handle_healers_done(fop);
mem_put(fop);
if (notify) {
ec_pending_fops_completed(ec);
}
}
}