123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942 |
- /* -*- 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 <http://www.gnu.org/licenses/>. */
- /*
- * shim_semget.c
- *
- * Implementation of system call "semget", "semop", "semtimedop" and "semctl".
- */
- #include <shim_internal.h>
- #include <shim_table.h>
- #include <shim_utils.h>
- #include <shim_handle.h>
- #include <shim_ipc.h>
- #include <shim_sysv.h>
- #include <shim_profile.h>
- #include <pal.h>
- #include <pal_error.h>
- #include <linux_list.h>
- #include <errno.h>
- #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(PAL_FALSE);
- 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;
- }
|