/*
Copyright (c) 2008-2013 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 "glusterfs/syncop.h"
#include "glusterfs/libglusterfs-messages.h"
int
syncopctx_setfsuid(void *uid)
{
struct syncopctx *opctx = NULL;
int ret = 0;
/* In args check */
if (!uid) {
ret = -1;
errno = EINVAL;
goto out;
}
opctx = syncopctx_getctx();
/* alloc for this thread the first time */
if (!opctx) {
opctx = GF_CALLOC(1, sizeof(*opctx), gf_common_mt_syncopctx);
if (!opctx) {
ret = -1;
goto out;
}
ret = syncopctx_setctx(opctx);
if (ret != 0) {
GF_FREE(opctx);
opctx = NULL;
goto out;
}
}
out:
if (opctx && uid) {
opctx->uid = *(uid_t *)uid;
opctx->valid |= SYNCOPCTX_UID;
}
return ret;
}
int
syncopctx_setfsgid(void *gid)
{
struct syncopctx *opctx = NULL;
int ret = 0;
/* In args check */
if (!gid) {
ret = -1;
errno = EINVAL;
goto out;
}
opctx = syncopctx_getctx();
/* alloc for this thread the first time */
if (!opctx) {
opctx = GF_CALLOC(1, sizeof(*opctx), gf_common_mt_syncopctx);
if (!opctx) {
ret = -1;
goto out;
}
ret = syncopctx_setctx(opctx);
if (ret != 0) {
GF_FREE(opctx);
opctx = NULL;
goto out;
}
}
out:
if (opctx && gid) {
opctx->gid = *(gid_t *)gid;
opctx->valid |= SYNCOPCTX_GID;
}
return ret;
}
int
syncopctx_setfsgroups(int count, const void *groups)
{
struct syncopctx *opctx = NULL;
gid_t *tmpgroups = NULL;
int ret = 0;
/* In args check */
if (count != 0 && !groups) {
ret = -1;
errno = EINVAL;
goto out;
}
opctx = syncopctx_getctx();
/* alloc for this thread the first time */
if (!opctx) {
opctx = GF_CALLOC(1, sizeof(*opctx), gf_common_mt_syncopctx);
if (!opctx) {
ret = -1;
goto out;
}
ret = syncopctx_setctx(opctx);
if (ret != 0) {
GF_FREE(opctx);
opctx = NULL;
goto out;
}
}
/* resize internal groups as required */
if (count && opctx->grpsize < count) {
if (opctx->groups) {
tmpgroups = GF_REALLOC(opctx->groups, (sizeof(gid_t) * count));
/* NOTE: Not really required to zero the reallocation,
* as ngrps controls the validity of data,
* making a note irrespective */
if (tmpgroups == NULL) {
opctx->grpsize = 0;
GF_FREE(opctx->groups);
opctx->groups = NULL;
ret = -1;
goto out;
}
} else {
tmpgroups = GF_CALLOC(count, sizeof(gid_t), gf_common_mt_syncopctx);
if (tmpgroups == NULL) {
opctx->grpsize = 0;
ret = -1;
goto out;
}
}
opctx->groups = tmpgroups;
opctx->grpsize = count;
}
/* copy out the groups passed */
if (count)
memcpy(opctx->groups, groups, (sizeof(gid_t) * count));
/* set/reset the ngrps, this is where reset of groups is handled */
opctx->ngrps = count;
opctx->valid |= SYNCOPCTX_GROUPS;
out:
return ret;
}
int
syncopctx_setfspid(void *pid)
{
struct syncopctx *opctx = NULL;
int ret = 0;
/* In args check */
if (!pid) {
ret = -1;
errno = EINVAL;
goto out;
}
opctx = syncopctx_getctx();
/* alloc for this thread the first time */
if (!opctx) {
opctx = GF_CALLOC(1, sizeof(*opctx), gf_common_mt_syncopctx);
if (!opctx) {
ret = -1;
goto out;
}
ret = syncopctx_setctx(opctx);
if (ret != 0) {
GF_FREE(opctx);
opctx = NULL;
goto out;
}
}
out:
if (opctx && pid) {
opctx->pid = *(pid_t *)pid;
opctx->valid |= SYNCOPCTX_PID;
}
return ret;
}
int
syncopctx_setfslkowner(gf_lkowner_t *lk_owner)
{
struct syncopctx *opctx = NULL;
int ret = 0;
/* In args check */
if (!lk_owner) {
ret = -1;
errno = EINVAL;
goto out;
}
opctx = syncopctx_getctx();
/* alloc for this thread the first time */
if (!opctx) {
opctx = GF_CALLOC(1, sizeof(*opctx), gf_common_mt_syncopctx);
if (!opctx) {
ret = -1;
goto out;
}
ret = syncopctx_setctx(opctx);
if (ret != 0) {
GF_FREE(opctx);
opctx = NULL;
goto out;
}
}
out:
if (opctx && lk_owner) {
opctx->lk_owner = *lk_owner;
opctx->valid |= SYNCOPCTX_LKOWNER;
}
return ret;
}
static void
__run(struct synctask *task)
{
struct syncenv *env = NULL;
env = task->env;
list_del_init(&task->all_tasks);
switch (task->state) {
case SYNCTASK_INIT:
case SYNCTASK_SUSPEND:
break;
case SYNCTASK_RUN:
gf_msg_debug(task->xl->name, 0,
"re-running already running"
" task");
env->runcount--;
break;
case SYNCTASK_WAIT:
env->waitcount--;
break;
case SYNCTASK_DONE:
gf_msg(task->xl->name, GF_LOG_WARNING, 0, LG_MSG_COMPLETED_TASK,
"running completed task");
return;
case SYNCTASK_ZOMBIE:
gf_msg(task->xl->name, GF_LOG_WARNING, 0, LG_MSG_WAKE_UP_ZOMBIE,
"attempted to wake up "
"zombie!!");
return;
}
list_add_tail(&task->all_tasks, &env->runq);
env->runcount++;
task->state = SYNCTASK_RUN;
}
static void
__wait(struct synctask *task)
{
struct syncenv *env = NULL;
env = task->env;
list_del_init(&task->all_tasks);
switch (task->state) {
case SYNCTASK_INIT:
case SYNCTASK_SUSPEND:
break;
case SYNCTASK_RUN:
env->runcount--;
break;
case SYNCTASK_WAIT:
gf_msg(task->xl->name, GF_LOG_WARNING, 0, LG_MSG_REWAITING_TASK,
"re-waiting already waiting "
"task");
env->waitcount--;
break;
case SYNCTASK_DONE:
gf_msg(task->xl->name, GF_LOG_WARNING, 0, LG_MSG_COMPLETED_TASK,
"running completed task");
return;
case SYNCTASK_ZOMBIE:
gf_msg(task->xl->name, GF_LOG_WARNING, 0, LG_MSG_SLEEP_ZOMBIE,
"attempted to sleep a zombie!!");
return;
}
list_add_tail(&task->all_tasks, &env->waitq);
env->waitcount++;
task->state = SYNCTASK_WAIT;
}
void
synctask_yield(struct synctask *task)
{
xlator_t *oldTHIS = THIS;
#if defined(__NetBSD__) && defined(_UC_TLSBASE)
/* Preserve pthread private pointer through swapcontex() */
task->proc->sched.uc_flags &= ~_UC_TLSBASE;
#endif
if (task->state != SYNCTASK_DONE) {
task->state = SYNCTASK_SUSPEND;
(void)gf_backtrace_save(task->btbuf);
}
if (swapcontext(&task->ctx, &task->proc->sched) < 0) {
gf_msg("syncop", GF_LOG_ERROR, errno, LG_MSG_SWAPCONTEXT_FAILED,
"swapcontext failed");
}
THIS = oldTHIS;
}
void
synctask_wake(struct synctask *task)
{
struct syncenv *env = NULL;
env = task->env;
pthread_mutex_lock(&env->mutex);
{
task->woken = 1;
if (task->slept)
__run(task);
pthread_cond_broadcast(&env->cond);
}
pthread_mutex_unlock(&env->mutex);
}
void
synctask_wrap(void)
{
struct synctask *task = NULL;
/* Do not trust the pointer received. It may be
wrong and can lead to crashes. */
task = synctask_get();
task->ret = task->syncfn(task->opaque);
if (task->synccbk)
task->synccbk(task->ret, task->frame, task->opaque);
task->state = SYNCTASK_DONE;
synctask_yield(task);
}
void
synctask_destroy(struct synctask *task)
{
if (!task)
return;
GF_FREE(task->stack);
if (task->opframe)
STACK_DESTROY(task->opframe->root);
if (task->synccbk == NULL) {
pthread_mutex_destroy(&task->mutex);
pthread_cond_destroy(&task->cond);
}
GF_FREE(task);
}
void
synctask_done(struct synctask *task)
{
if (task->synccbk) {
synctask_destroy(task);
return;
}
pthread_mutex_lock(&task->mutex);
{
task->state = SYNCTASK_ZOMBIE;
task->done = 1;
pthread_cond_broadcast(&task->cond);
}
pthread_mutex_unlock(&task->mutex);
}
int
synctask_setid(struct synctask *task, uid_t uid, gid_t gid)
{
if (!task)
return -1;
if (uid != -1)
task->uid = uid;
if (gid != -1)
task->gid = gid;
return 0;
}
struct synctask *
synctask_create(struct syncenv *env, size_t stacksize, synctask_fn_t fn,
synctask_cbk_t cbk, call_frame_t *frame, void *opaque)
{
struct synctask *newtask = NULL;
xlator_t *this = THIS;
int destroymode = 0;
VALIDATE_OR_GOTO(env, err);
VALIDATE_OR_GOTO(fn, err);
/* Check if the syncenv is in destroymode i.e. destroy is SET.
* If YES, then don't allow any new synctasks on it. Return NULL.
*/
pthread_mutex_lock(&env->mutex);
{
destroymode = env->destroy;
}
pthread_mutex_unlock(&env->mutex);
/* syncenv is in DESTROY mode, return from here */
if (destroymode)
return NULL;
newtask = GF_CALLOC(1, sizeof(*newtask), gf_common_mt_synctask);
if (!newtask)
return NULL;
newtask->frame = frame;
if (!frame) {
newtask->opframe = create_frame(this, this->ctx->pool);
if (!newtask->opframe)
goto err;
set_lk_owner_from_ptr(&newtask->opframe->root->lk_owner,
newtask->opframe->root);
} else {
newtask->opframe = copy_frame(frame);
}
if (!newtask->opframe)
goto err;
newtask->env = env;
|