/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
/* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
/* Copyright (C) 2014 OSCAR lab, Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/*
* shim_semget.c
*
* Implementation of system call "semget", "semop", "semtimedop" and "semctl".
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SEM_HASH_LEN 8
#define SEM_HASH_NUM (1 << SEM_HASH_LEN)
#define SEM_HASH_MASK (SEM_HASH_NUM - 1)
#define SEM_HASH(idx) ((idx) & SEM_HASH_MASK)
static LIST_HEAD(sem_list);
static struct hlist_head sem_key_hlist [SEM_HASH_NUM];
static struct hlist_head sem_sid_hlist [SEM_HASH_NUM];
static LOCKTYPE sem_list_lock;
DEFINE_PROFILE_CATAGORY(sysv_sem, );
#define SEM_TO_HANDLE(semhdl) \
container_of((semhdl), struct shim_handle, info.sem)
static int __add_sem_handle (unsigned long key, IDTYPE semid,
int nsems, bool owned,
struct shim_sem_handle ** semhdl)
{
struct hlist_head * key_head = (key != IPC_PRIVATE) ?
&sem_key_hlist[SEM_HASH(key)] : NULL;
struct hlist_head * sid_head = semid ?
&sem_sid_hlist[SEM_HASH(semid)] : NULL;
struct shim_sem_handle * tmp;
struct hlist_node * pos;
int ret = 0;
if (key_head)
hlist_for_each_entry(tmp, pos, key_head, key_hlist)
if (tmp->semkey == key) {
if (tmp->semid == semid)
goto out;
return -EEXIST;
}
if (sid_head)
hlist_for_each_entry(tmp, pos, sid_head, sid_hlist)
if (tmp->semid == semid) {
if (key)
tmp->semkey = key;
goto out;
}
struct shim_handle * hdl = get_new_handle();
if (!hdl)
return -ENOMEM;
tmp = &hdl->info.sem;
hdl->type = TYPE_SEM;
tmp->semkey = key;
tmp->semid = semid;
tmp->owned = owned;
tmp->event = DkNotificationEventCreate(0);
if (owned && nsems) {
tmp->nsems = nsems;
tmp->sems = malloc(sizeof(struct sem_obj) * nsems);
if (!tmp->sems) {
ret = -ENOMEM;
goto failed;
}
for (int i = 0 ; i < nsems ; i++) {
tmp->sems[i].num = i;
tmp->sems[i].val = 0;
tmp->sems[i].host_sem_id = 0;
tmp->sems[i].host_sem = NULL;
INIT_LIST_HEAD(&tmp->sems[i].ops);
INIT_LIST_HEAD(&tmp->sems[i].next_ops);
}
}
INIT_LIST_HEAD(&tmp->migrated);
INIT_LIST_HEAD(&tmp->list);
get_handle(hdl);
list_add_tail(&tmp->list, &sem_list);
INIT_HLIST_NODE(&tmp->key_hlist);
if (key_head) {
get_handle(hdl);
hlist_add_head(&tmp->key_hlist, key_head);
}
if (sid_head) {
get_handle(hdl);
hlist_add_head(&tmp->sid_hlist, sid_head);
}
out:
if (!semhdl) {
put_handle(hdl);
return 0;
}
*semhdl = tmp;
return 0;
failed:
put_handle(hdl);
return ret;
}
int add_sem_handle (unsigned long key, IDTYPE id, int nsems, bool owned)
{
lock(sem_list_lock);
int ret = __add_sem_handle(key, id, nsems, owned, NULL);
unlock(sem_list_lock);
return ret;
}
struct shim_sem_handle * get_sem_handle_by_key (unsigned long key)
{
struct hlist_head * key_head = &sem_key_hlist[SEM_HASH(key)];
struct shim_sem_handle * tmp, * found = NULL;
struct hlist_node * pos;
lock(sem_list_lock);
hlist_for_each_entry(tmp, pos, key_head, key_hlist)
if (tmp->semkey == key) {
found = tmp;
break;
}
if (found)
get_handle(SEM_TO_HANDLE(found));
unlock(sem_list_lock);
return found;
}
struct shim_sem_handle * get_sem_handle_by_id (IDTYPE semid)
{
struct hlist_head * sid_head = &sem_sid_hlist[SEM_HASH(semid)];
struct shim_sem_handle * tmp, * found = NULL;
struct hlist_node * pos;
lock(sem_list_lock);
hlist_for_each_entry(tmp, pos, sid_head, sid_hlist)
if (tmp->semid == semid) {
found = tmp;
break;
}
if (found)
get_handle(SEM_TO_HANDLE(found));
unlock(sem_list_lock);
return found;
}
void put_sem_handle (struct shim_sem_handle * sem)
{
put_handle(SEM_TO_HANDLE(sem));
}
static int __del_sem_handle (struct shim_sem_handle * sem)
{
if (sem->deleted)
return 0;
sem->deleted = true;
struct shim_handle * hdl = SEM_TO_HANDLE(sem);
lock(sem_list_lock);
list_del_init(&sem->list);
put_handle(hdl);
if (!hlist_unhashed(&sem->key_hlist)) {
hlist_del_init(&sem->key_hlist);
put_handle(hdl);
}
if (!hlist_unhashed(&sem->sid_hlist)) {
hlist_del_init(&sem->sid_hlist);
put_handle(hdl);
}
unlock(sem_list_lock);
return 0;
}
int del_sem_handle (struct shim_sem_handle * sem)
{
struct shim_handle * hdl = SEM_TO_HANDLE(sem);
lock(hdl->lock);
int ret = del_sem_handle(sem);
unlock(hdl->lock);
return ret;
}
static void __try_create_lock (void)
{
create_lock_runtime(&sem_list_lock);
}
int shim_do_semget (key_t key, int nsems, int semflg)
{
INC_PROFILE_OCCURENCE(syscall_use_ipc);
IDTYPE semid = 0;
int ret;
__try_create_lock();
if (key != IPC_PRIVATE) {
struct shim_sem_handle * sem = get_sem_handle_by_key(key);
if (sem) {
semid = sem->semid;
put_sem_handle(sem);
return (semflg & IPC_EXCL) ? -EEXIST : semid;
}
}
struct sysv_key k;
k.key = key;
k.type = SYSV_SEM;
if (semflg & IPC_CREAT) {
do {
semid = allocate_sysv(0, 0);
if (!semid)
semid = ipc_sysv_lease_send(NULL);
} while (!semid);
if (key != IPC_PRIVATE) {
if ((ret = ipc_sysv_tellkey_send(NULL, 0, &k, semid, 0)) < 0) {
release_sysv(semid);
return ret;
}
}
add_sem_handle(key, semid, nsems, true);
} else {
if ((ret = ipc_sysv_findkey_send(&k)) < 0)
return ret;
semid = ret;
if ((ret = ipc_sysv_query_send(semid)) < 0)
return ret;
}
return semid;
}
static int connect_sem_handle (int semid, int nsems,
struct shim_sem_handle ** semp)
{
struct shim_sem_handle * sem = get_sem_handle_by_id(semid);
int ret;
if (!sem) {
if ((ret = ipc_sysv_query_send(semid)) < 0)
return ret;
if (!sem) {
lock(sem_list_lock);
ret = __add_sem_handle(IPC_PRIVATE, semid, nsems, false, &sem);
unlock(sem_list_lock);
if (ret < 0)
return ret;
}
}
*semp = sem;
return 0;
}
int recover_sem_ownership (struct shim_sem_handle * sem,
struct sem_backup * backups, int nbackups,
struct sem_client_backup * clients, int nclients)
{
struct shim_handle * hdl = SEM_TO_HANDLE(sem);
lock(hdl->lock);
assert(!sem->owned);
assert(!sem->nsems && !sem->sems);
sem->nsems = nbackups;
if (!sem->sems && !(sem->sems = malloc(sizeof(struct sem_obj) * nbackups)))
goto out;
for (int i = 0 ; i < nbackups ; i++) {
sem->sems[i].num = i;
sem->sems[i].val = backups[i].val;
sem->sems[i].zcnt = backups[i].zcnt;
sem->sems[i].ncnt = backups[i].ncnt;
sem->sems[i].pid = backups[i].pid;
INIT_LIST_HEAD(&sem->sems[i].ops);
INIT_LIST_HEAD(&sem->sems[i].next_ops);
}
for (int i = 0 ; i < nclients ; i++) {
struct sem_ops * op = malloc(sizeof(struct sem_ops));
if (!op)
continue;
op->stat.completed = false;
op->stat.failed = false;
op->stat.nops = clients[i].nops;
op->stat.current = clients[i].current;
op->stat.timeout = -1;
op->client.vmid = clients[i].vmid;
op->client.port = NULL;
op->client.seq = clients[i].seq;
INIT_LIST_HEAD(&op->progress);
list_add_tail(&op->progress, &sem->migrated);
}
sem->owned = true;
DkEventSet(sem->event);
out:
unlock(hdl->lock);
return 0;
}
static int __do_semop (int semid, struct sembuf * sops, unsigned int nsops,
unsigned long timeout)
{
int ret;
struct shim_sem_handle * sem;
int nsems = 0;
for (int i = 0 ; i < nsops ; i++)
if (sops[i].sem_num >= nsems)
nsems = sops[i].sem_num + 1;
__try_create_lock();
if ((ret = connect_sem_handle(semid, nsems, &sem)) < 0)
return ret;
ret = submit_sysv_sem(sem, sops, nsops, timeout, NULL);
put_sem_handle(sem);
return ret;
}
int shim_do_semop (int semid, struct sembuf * sops, unsigned int nsops)
{
INC_PROFILE_OCCURENCE(syscall_use_ipc);
return __do_semop(semid, sops, nsops, IPC_SEM_NOTIMEOUT);
}
int shim_do_semtimedop (int semid, struct sembuf * sops, unsigned int nsops,
const struct timespec * timeout)
{
INC_PROFILE_OCCURENCE(syscall_use_ipc);
return __do_semop(semid, sops, nsops,
timeout->tv_sec * 1000000000ULL + timeout->tv_nsec);
}
int shim_do_semctl (int semid, int semnum, int cmd, unsigned long arg)
{
INC_PROFILE_OCCURENCE(syscall_use_ipc);
struct shim_sem_handle * sem;
int ret;
__try_create_lock();
if ((ret = connect_sem_handle(semid, 0, &sem)) < 0)
return ret;
struct shim_handle * hdl = SEM_TO_HANDLE(sem);
lock(hdl->lock);
switch (cmd) {
case IPC_RMID: {
if (!sem->owned) {
ret = ipc_sysv_delres_send(NULL, 0, semid, SYSV_SEM);
if (ret < 0)
goto out;
}
__del_sem_handle(sem);
goto out;
}
}
if (sem->owned) {
if (sem->deleted) {
ret = -EIDRM;
goto out;
}
switch (cmd) {
case GETALL:
for (int i = 0 ; i < sem->nsems ; i++) {
unsigned short val = sem->sems[i].val;
((unsigned short *) arg)[i] = val;
}
break;
case GETNCNT:
ret = sem->sems[semnum].ncnt;
break;
case GETPID:
ret = sem->sems[semnum].pid;
break;
case GETVAL:
ret = sem->sems[semnum].val;
break;
case GETZCNT:
ret = sem->sems[semnum].zcnt;
break;
case SETALL:
for (int i = 0 ; i < sem->nsems ; i++) {
unsigned short val = ((unsigned short *) arg)[i];
sem->sems[i].val = val;
}
break;
case SETVAL: {
unsigned short val = arg;
sem->sems[semnum].val = val;
break;
}
}
} else {
switch (cmd) {
case GETALL:
case SETALL: {
int valsize = sem->nsems * sizeof(unsigned short);
ret = ipc_sysv_semctl_send(sem->semid, 0, cmd,
(unsigned short *) arg, valsize);
break;
}
case GETVAL:
case GETNCNT:
case GETPID:
case GETZCNT: {
int valsize = sizeof(unsigned short);
unsigned short val;
ret = ipc_sysv_semctl_send(sem->semid, semnum, cmd,
&val, valsize);
if (!ret)
ret = val;
break;
}
case SETVAL: {
unsigned short val = arg;
ret = ipc_sysv_semctl_send(sem->semid, semnum, cmd,
&val, sizeof(unsigned short));
break;
}
}
}
out:
unlock(hdl->lock);
put_sem_handle(sem);
return ret;
}
static bool __handle_sysv_sems (struct shim_sem_handle * sem)
{
bool progressed = false;
bool setevent = false;
struct sem_obj * sobj;
for (sobj = sem->sems ; sobj < &sem->sems[sem->nsems] ; sobj++)
list_splice_tail_init(&sobj->next_ops, &sobj->ops);
for (sobj = sem->sems ; sobj < &sem->sems[sem->nsems] ; sobj++) {
struct sem_ops * sops, * n;
list_for_each_entry_safe(sops, n, &sobj->ops, progress) {
struct sembuf * op = &sops->ops[sops->stat.current];
assert(op->sem_num == sobj->num);
assert(sops != n);
if (sops->stat.completed)
goto send_result;
again:
if (op->sem_op > 0) {
sobj->val += op->sem_op;
debug("sem %u: add %u => %u\n", sobj->num, op->sem_op,
sobj->val);
} else if (op->sem_op < 0) {
if (sobj->val < -op->sem_op) {
if (op->sem_flg & IPC_NOWAIT) {
debug("sem %u: wait for %u failed\n", sobj->num,
-op->sem_op);
goto failed;
}
continue;
}
sobj->val -= -op->sem_op;
debug("sem %u: wait for %u => %u\n", sobj->num, -op->sem_op,
sobj->val);
} else {
if (sobj->val) {
if (op->sem_flg & IPC_NOWAIT) {
debug("sem %u: wait for 0 failed\n", sobj->num);
goto failed;
}
continue;
}
debug("sem %u: wait for 0\n", sobj->num);
}
progressed = true;
sops->stat.current++;
if (sops->stat.current == sops->stat.nops) {
sops->stat.completed = true;
goto send_result;
}
op = &sops->ops[sops->stat.current];
if (op->sem_num != sobj->num) {
list_move_tail(&sops->progress,
&sem->sems[op->sem_num].next_ops);
continue;
}
goto again;
failed:
progressed = true;
sops->stat.failed = true;
send_result:
list_del_init(&sops->progress);
sem->nreqs--;
if (!sops->client.vmid) {
setevent = true;
continue;
}
send_ipc_message(create_ipc_resp_msg_on_stack(
sops->stat.completed ? 0 : -EAGAIN,
sops->client.vmid,
sops->client.seq), sops->client.port);
put_ipc_port(sops->client.port);
sops->client.vmid = 0;
sops->client.port = NULL;
sops->client.seq = 0;
free(sops);
}
}
if (setevent)
DkEventSet(sem->event);
return progressed;
}
static void __handle_one_sysv_sem (struct shim_sem_handle * sem,
struct sem_stat * stat,
struct sembuf * sops)
{
bool progressed = false;
again:
while (stat->current < stat->nops) {
struct sem_obj * sobj = &sem->sems[sops[stat->current].sem_num];
struct sembuf * op = &sops[stat->current];
if (op->sem_op > 0) {
progressed = true;
sobj->val += op->sem_op;
debug("sem %u: add %u => %u\n", sobj->num, op->sem_op,
sobj->val);
} else if (op->sem_op < 0) {
if (sobj->val < -op->sem_op) {
if (op->sem_flg & IPC_NOWAIT) {
stat->failed = true;
debug("sem %u: wait for %u failed\n", sobj->num,
-op->sem_op);
return;
}
goto failed;
}
progressed = true;
sobj->val -= -op->sem_op;
debug("sem %u: wait for %u => %u\n", sobj->num, -op->sem_op,
sobj->val);
} else {
if (sobj->val) {
if (op->sem_flg & IPC_NOWAIT) {
stat->failed = true;
debug("sem %u: wait for 0 failed\n", sobj->num);
return;
}
goto failed;
}
progressed = true;
debug("sem %u: wait for 0\n", sobj->num);
}
stat->current++;
}
stat->completed = true;
failed:
if (progressed) {
while (__handle_sysv_sems(sem));
progressed = false;
if (!stat->completed)
goto again;
}
}
static int sem_balance_migrate (struct shim_handle * hdl,
struct sysv_client * client);
static struct sysv_balance_policy sem_policy = {
.score_decay = SEM_SCORE_DECAY,
.score_max = SEM_SCORE_MAX,
.balance_threshold = SEM_BALANCE_THRESHOLD,
.migrate = &sem_balance_migrate,
};
DEFINE_PROFILE_CATAGORY(submit_sysv_sem, sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_prepare_stat, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_lock_handle, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_count_score, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_handle_by_shared_semaphore, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_send_ipc_movres, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_send_ipc_semop, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_handle_one_sysv_sem, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_send_ipc_response, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_alloc_semop, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_append_semop, submit_sysv_sem);
DEFINE_PROFILE_INTERVAL(sem_wait_for_complete, submit_sysv_sem);
int submit_sysv_sem (struct shim_sem_handle * sem, struct sembuf * sops,
int nsops, unsigned long timeout,
struct sysv_client * client)
{
BEGIN_PROFILE_INTERVAL();
int ret = 0;
struct shim_handle * hdl = SEM_TO_HANDLE(sem);
struct sem_ops * sem_ops = NULL;
bool malloced = false;
struct sem_stat stat;
stat.nops = nsops;
stat.current = 0;
stat.timeout = timeout;
stat.completed = false;
stat.failed = false;
SAVE_PROFILE_INTERVAL(sem_prepare_stat);
lock(hdl->lock);
SAVE_PROFILE_INTERVAL(sem_lock_handle);
if (sem->deleted) {
ret = -EIDRM;
goto out_locked;
}
IDTYPE semid = sem->semid;
bool sendreply = false;
unsigned long seq = client ? client->seq : 0;
int score = 0;
for (int i = 0 ; i < nsops ; i++) {
struct sembuf * op = &sops[i];
if (op->sem_num > 0) {
score += SEM_POSITIVE_SCORE(op->sem_num);
} else if (op->sem_num < 0) {
score += SEM_NEGATIVE_SCORE(-op->sem_num);
sendreply = true;
} else {
score += SEM_ZERO_SCORE;
sendreply = true;
}
}
SAVE_PROFILE_INTERVAL(sem_count_score);
if (sem->deleted) {
if (!client || sendreply) {
ret = -EIDRM;
goto out_locked;
}
ret = ipc_sysv_delres_send(client->port, client->vmid, sem->semid,
SYSV_SEM);
goto out_locked;
}
if (sem->owned) {
__balance_sysv_score(&sem_policy, hdl, sem->scores, MAX_SYSV_CLIENTS,
client, score);
if (!sem->owned && client) {
struct shim_ipc_info * owner = sem->owner;
assert(owner);
ret = ipc_sysv_movres_send(client, owner->vmid,
qstrgetstr(&owner->uri), sem->lease,
sem->semid, SYSV_SEM);
goto out_locked;
}
}
if (!sem->owned) {
if (client) {
struct shim_ipc_info * owner = sem->owner;
ret = owner ?
ipc_sysv_movres_send(client, owner->vmid,
qstrgetstr(&owner->uri), sem->lease,
sem->semid, SYSV_SEM) :
-ECONNREFUSED;
SAVE_PROFILE_INTERVAL(sem_send_ipc_movres);
goto out_locked;
}
unowned:
unlock(hdl->lock);
ret = ipc_sysv_semop_send(semid, sops, nsops, timeout, &seq);
if (ret != -EAGAIN &&
ret != -ECONNREFUSED)
goto out;
lock(hdl->lock);
SAVE_PROFILE_INTERVAL(sem_send_ipc_semop);
if (!sem->owned)
goto out_locked;
}
if (seq) {
struct sem_ops * op;
list_for_each_entry(op, &sem->migrated, progress)
if (op->client.vmid == (client ? client->vmid : cur_process.vmid)
&& seq == op->client.seq) {
list_del_init(&op->progress);
sem_ops = op;
stat = sem_ops->stat;
malloced = true;
break;
}
}
__handle_one_sysv_sem(sem, &stat, sops);
SAVE_PROFILE_INTERVAL(sem_handle_one_sysv_sem);
if (stat.completed || stat.failed) {
ret = stat.completed ? 0 : -EAGAIN;
if (client && sendreply)
ret = send_ipc_message(create_ipc_resp_msg_on_stack(
ret, client->vmid,
client->seq), client->port);
SAVE_PROFILE_INTERVAL(sem_send_ipc_response);
goto out_locked;
}
if (client) {
assert(sendreply);
if (!sem_ops || !malloced) {
sem_ops = malloc(sizeof(struct sem_ops) +
sizeof(struct sembuf) * nsops);
if (!sem_ops) {
ret = -ENOMEM;
goto out_locked;
}
sem_ops->client.vmid = 0;
sem_ops->client.port = NULL;
sem_ops->client.seq = 0;
INIT_LIST_HEAD(&sem_ops->progress);
malloced = true;
SAVE_PROFILE_INTERVAL(sem_alloc_semop);
}
} else {
if (!sem_ops) {
sem_ops = __alloca(sizeof(struct sem_ops) +
sizeof(struct sembuf) * nsops);
sem_ops->client.vmid = 0;
sem_ops->client.port = NULL;
sem_ops->client.seq = 0;
INIT_LIST_HEAD(&sem_ops->progress);
SAVE_PROFILE_INTERVAL(sem_alloc_semop);
}
}
sem_ops->stat = stat;
for (int i = 0 ; i < nsops ; i++)
sem_ops->ops[i] = sops[i];
struct list_head * next_ops =
&sem->sems[sops[stat.current].sem_num].next_ops;
assert(list_empty(&sem_ops->progress));
list_add_tail(&sem_ops->progress, next_ops);
//check_list_head(next_ops);
sem->nreqs++;
SAVE_PROFILE_INTERVAL(sem_append_semop);
if (client) {
assert(sendreply);
add_ipc_port(client->port, client->vmid, IPC_PORT_SYSVCON, NULL);
get_ipc_port(client->port);
sem_ops->client = *client;
sem_ops = NULL;
goto out_locked;
}
while (!sem_ops->stat.completed &&
!sem_ops->stat.failed) {
if (!sem->owned) {
list_del_init(&sem_ops->progress);
goto unowned;
}
unlock(hdl->lock);
DkObjectsWaitAny(1, &sem->event, NO_TIMEOUT);
lock(hdl->lock);
SAVE_PROFILE_INTERVAL(sem_wait_for_complete);
}
ret = sem_ops->stat.completed ? 0 : -EAGAIN;
out_locked:
unlock(hdl->lock);
out:
if (sem_ops && malloced)
free(sem_ops);
return ret;
}
static int sem_balance_migrate (struct shim_handle * hdl,
struct sysv_client * src)
{
struct shim_sem_handle * sem = &hdl->info.sem;
int ret = 0;
debug("trigger semaphore balancing, migrate to process %u\n", src->vmid);
struct sem_backup * sem_backups = __alloca(sizeof(struct sem_backup) *
sem->nsems);
struct sem_client_backup * clients =
__alloca(sizeof(struct sem_client_backup) * sem->nreqs);
int sem_cnt = 0, client_cnt = 0;
struct sem_obj * sobj;
for (sobj = sem->sems ; sobj < &sem->sems[sem->nsems] ; sobj++) {
assert(sem_cnt < sem->nsems);
struct sem_backup * b = sem_backups + (sem_cnt++);
b->val = sobj->val;
b->zcnt = sobj->zcnt;
b->ncnt = sobj->ncnt;
b->pid = sobj->pid;
list_splice_tail(&sobj->next_ops, &sobj->ops);
struct sem_ops * sops;
list_for_each_entry(sops, &sobj->ops, progress) {
assert(client_cnt < sem->nreqs);
struct sem_client_backup * c = clients + (client_cnt)++;
c->vmid = sops->client.vmid;
c->seq = sops->client.seq;
c->current = sops->stat.current;
c->nops = sops->stat.nops;
}
}
struct shim_ipc_info * info = discover_client(src->port, src->vmid);
if (!info)
goto out;
ipc_sysv_sublease_send(src->vmid, sem->semid,
qstrgetstr(&info->uri),
&sem->lease);
ret = ipc_sysv_semmov_send(src->port, src->vmid, sem->semid, sem->lease,
sem_backups, sem_cnt, clients, client_cnt,
sem->scores, MAX_SYSV_CLIENTS);
if (ret < 0)
goto failed_info;
sem->owned = false;
sem->owner = info;
for (sobj = sem->sems ; sobj < &sem->sems[sem->nsems] ; sobj++) {
struct sem_ops * sops, * n;
list_for_each_entry_safe(sops, n, &sobj->ops, progress) {
list_del_init(&sops->progress);
sem->nreqs--;
sops->stat.failed = true;
if (!sops->client.vmid)
continue;
ipc_sysv_movres_send(&sops->client, src->vmid,
qstrgetstr(&info->uri), sem->lease,
sem->semid, SYSV_SEM);
put_ipc_port(sops->client.port);
free(sops);
}
}
sem->nsems = 0;
free(sem->sems);
sem->sems = NULL;
ret = 0;
DkEventSet(sem->event);
goto out;
failed_info:
put_ipc_info(info);
out:
return ret;
}