/* Copyright (C) 2014 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 Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* * shim_ipc_pid.c * * This file contains functions and callbacks to handle IPC of SYSV namespace. */ #include #include #include #include #include #include #include #include #define SYSV_RANGE_SIZE 128 #define SYSV_LEASE_TIME 1000 #define KEY_HASH(k) ((k)->key) #define KEY_COMP(k1, k2) ((k1)->key != (k2)->key || (k1)->type != (k2)->type) #define KEY_COPY(k1, k2) \ do { \ (k1)->key = (k2)->key; \ (k1)->type = (k2)->type; \ } while (0) #define NS sysv #define NS_CAP SYSV #define NS_KEY struct sysv_key #define INCLUDE_IPC_NSIMPL #include "shim_ipc_nsimpl.h" int init_ns_sysv(void) { return init_namespace(); } DEFINE_PROFILE_INTERVAL(ipc_sysv_delres_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_delres_callback, ipc); int ipc_sysv_delres_send(struct shim_ipc_port* port, IDTYPE dest, IDTYPE resid, enum sysv_type type) { BEGIN_PROFILE_INTERVAL(); int ret = 0; bool owned = false; if (!port) { if ((ret = connect_owner(resid, &port, &dest)) < 0) goto out; owned = true; } if (!owned) { size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_delres)); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_DELRES, total_msg_size, dest); struct shim_ipc_sysv_delres* msgin = (struct shim_ipc_sysv_delres*)&msg->msg; msgin->resid = resid; msgin->type = type; debug("ipc send to %u: IPC_SYSV_DELRES(%u, %s)\n", dest, resid, SYSV_TYPE_STR(type)); ret = send_ipc_message(msg, port); goto out; } size_t total_msg_size = get_ipc_msg_duplex_size(sizeof(struct shim_ipc_sysv_delres)); struct shim_ipc_msg_duplex* msg = __alloca(total_msg_size); init_ipc_msg_duplex(msg, IPC_SYSV_DELRES, total_msg_size, dest); struct shim_ipc_sysv_delres* msgin = (struct shim_ipc_sysv_delres*)&msg->msg.msg; msgin->resid = resid; msgin->type = type; debug("ipc send to %u: IPC_SYSV_DELRES(%u, %s)\n", dest, resid, SYSV_TYPE_STR(type)); ret = send_ipc_message_duplex(msg, port, NULL, NULL); put_ipc_port(port); out: SAVE_PROFILE_INTERVAL(ipc_sysv_delres_send); return ret; } int ipc_sysv_delres_callback(IPC_CALLBACK_ARGS) { __UNUSED(port); BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_delres* msgin = (struct shim_ipc_sysv_delres*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_DELRES(%u, %s)\n", msg->src, msgin->resid, SYSV_TYPE_STR(msgin->type)); bool owned = false; ret = -ENOENT; switch (msgin->type) { case SYSV_MSGQ: { struct shim_msg_handle* msgq = get_msg_handle_by_id(msgin->resid); if (!msgq) goto out; owned = msgq->owned; ret = del_msg_handle(msgq); break; } case SYSV_SEM: { struct shim_sem_handle* sem = get_sem_handle_by_id(msgin->resid); if (!sem) goto out; owned = sem->owned; ret = del_sem_handle(sem); break; } default: ret = -ENOSYS; break; } if (!ret) ret = owned ? RESPONSE_CALLBACK : 0; out: SAVE_PROFILE_INTERVAL(ipc_sysv_delres_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_movres_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_movres_callback, ipc); int ipc_sysv_movres_send(struct sysv_client* client, IDTYPE owner, const char* uri, LEASETYPE lease, IDTYPE resid, enum sysv_type type) { BEGIN_PROFILE_INTERVAL(); int ret = 0; int len = strlen(uri); size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_movres) + len); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_MOVRES, total_msg_size, client->vmid); struct shim_ipc_sysv_movres* msgin = (struct shim_ipc_sysv_movres*)&msg->msg; msgin->resid = resid; msgin->type = type; msgin->owner = owner; msgin->lease = lease; memcpy(msgin->uri, uri, len + 1); msg->seq = client->seq; debug("ipc send to %u: IPC_SYSV_MOVRES(%u, %s, %u, %s)\n", client->vmid, resid, SYSV_TYPE_STR(type), owner, uri); ret = send_ipc_message(msg, client->port); SAVE_PROFILE_INTERVAL(ipc_sysv_movres_send); return ret; } int ipc_sysv_movres_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_movres* msgin = (struct shim_ipc_sysv_movres*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_MOVRES(%u, %s, %u, %s)\n", msg->src, msgin->resid, SYSV_TYPE_STR(msgin->type), msgin->owner, msgin->uri); struct shim_ipc_msg_duplex* obj = pop_ipc_msg_duplex(port, msg->seq); if (!obj) goto out; switch (msgin->type) { case SYSV_MSGQ: case SYSV_SEM: obj->retval = -EAGAIN; break; default: ret = -ENOSYS; goto out; } add_sysv_subrange(msgin->resid, msgin->owner, msgin->uri, &msgin->lease); if (obj->thread) thread_wakeup(obj->thread); out: SAVE_PROFILE_INTERVAL(ipc_sysv_movres_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_msgsnd_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_msgsnd_callback, ipc); int ipc_sysv_msgsnd_send(struct shim_ipc_port* port, IDTYPE dest, IDTYPE msgid, long msgtype, const void* buf, size_t size, unsigned long seq) { BEGIN_PROFILE_INTERVAL(); int ret = 0; bool owned = true; if (!dest) { if ((ret = connect_owner(msgid, &port, &dest)) < 0) goto out; owned = false; } size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_msgsnd) + size); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_MSGSND, total_msg_size, dest); struct shim_ipc_sysv_msgsnd* msgin = (struct shim_ipc_sysv_msgsnd*)&msg->msg; msgin->msgid = msgid; msgin->msgtype = msgtype; memcpy(msgin->msg, buf, size); msg->seq = seq; debug("ipc send to %u: IPC_SYSV_MSGSND(%u, %ld)\n", dest, msgid, msgtype); ret = send_ipc_message(msg, port); if (!owned) put_ipc_port(port); out: SAVE_PROFILE_INTERVAL(ipc_sysv_msgsnd_send); return ret; } int ipc_sysv_msgsnd_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_msgsnd* msgin = (struct shim_ipc_sysv_msgsnd*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_MSGSND(%u, %ld)\n", msg->src, msgin->msgid, msgin->msgtype); size_t size = msg->size - sizeof(*msg) - sizeof(*msgin); if (msg->seq) { struct shim_ipc_msg_duplex* obj = pop_ipc_msg_duplex(port, msg->seq); void* priv = obj ? obj->private : NULL; if (priv) { struct shim_ipc_sysv_msgrcv* rcv = (struct shim_ipc_sysv_msgrcv*)obj->msg.msg; if (size > rcv->size) size = rcv->size; memcpy(priv, msgin->msg, size); obj->retval = size; if (obj->thread) thread_wakeup(obj->thread); goto out; } } struct shim_msg_handle* msgq = get_msg_handle_by_id(msgin->msgid); if (!msgq) { ret = -ENOENT; goto out; } if (msg->seq) { ret = add_sysv_msg(msgq, msgin->msgtype, size, msgin->msg, NULL); } else { struct sysv_client src; src.port = port; src.vmid = msg->src; src.seq = msg->seq; ret = add_sysv_msg(msgq, msgin->msgtype, size, msgin->msg, &src); } out: SAVE_PROFILE_INTERVAL(ipc_sysv_msgsnd_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_msgrcv_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_msgrcv_callback, ipc); int ipc_sysv_msgrcv_send(IDTYPE msgid, long msgtype, int flags, void* buf, size_t size) { BEGIN_PROFILE_INTERVAL(); IDTYPE owner; struct shim_ipc_port* port = NULL; int ret = 0; if ((ret = connect_owner(msgid, &port, &owner)) < 0) goto out; if (owner == cur_process.vmid) { ret = -EAGAIN; goto out; } assert(port); size_t total_msg_size = get_ipc_msg_duplex_size(sizeof(struct shim_ipc_sysv_msgrcv)); struct shim_ipc_msg_duplex* msg = __alloca(total_msg_size); init_ipc_msg_duplex(msg, IPC_SYSV_MSGRCV, total_msg_size, owner); struct shim_ipc_sysv_msgrcv* msgin = (struct shim_ipc_sysv_msgrcv*)&msg->msg.msg; msgin->msgid = msgid; msgin->msgtype = msgtype; msgin->size = size; msgin->flags = flags; debug("ipc send to %u: IPC_SYSV_MSGRCV(%u, %ld)\n", owner, msgid, msgtype); ret = send_ipc_message_duplex(msg, port, NULL, buf); put_ipc_port(port); out: SAVE_PROFILE_INTERVAL(ipc_sysv_msgrcv_send); return ret; } int ipc_sysv_msgrcv_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_msgrcv* msgin = (struct shim_ipc_sysv_msgrcv*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_MSGRCV(%u, %ld)\n", msg->src, msgin->msgid, msgin->msgtype); struct shim_msg_handle* msgq = get_msg_handle_by_id(msgin->msgid); if (!msgq) { ret = -ENOENT; goto out; } void* buf = __alloca(msgin->size); struct sysv_client src; src.port = port; src.vmid = msg->src; src.seq = msg->seq; ret = get_sysv_msg(msgq, msgin->msgtype, msgin->size, buf, msgin->flags, &src); if (ret > 0) { size_t size = ret; ret = ipc_sysv_msgsnd_send(port, msg->src, msgin->msgid, msgin->msgtype, buf, size, msg->seq); } put_msg_handle(msgq); out: SAVE_PROFILE_INTERVAL(ipc_sysv_msgrcv_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_msgmov_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_msgmov_callback, ipc); int ipc_sysv_msgmov_send(struct shim_ipc_port* port, IDTYPE dest, IDTYPE msgid, LEASETYPE lease, struct sysv_score* scores, int nscores) { BEGIN_PROFILE_INTERVAL(); size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_msgmov) + sizeof(struct sysv_score) * nscores); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_MSGMOV, total_msg_size, dest); struct shim_ipc_sysv_msgmov* msgin = (struct shim_ipc_sysv_msgmov*)&msg->msg; msgin->msgid = msgid; msgin->lease = lease; msgin->nscores = nscores; if (nscores) memcpy(msgin->scores, scores, sizeof(struct sysv_score) * nscores); debug("ipc send to %u: IPC_SYSV_MSGMOV(%d)\n", dest, msgid); int ret = send_ipc_message(msg, port); SAVE_PROFILE_INTERVAL(ipc_sysv_msgmov_send); return ret; } int ipc_sysv_msgmov_callback(IPC_CALLBACK_ARGS) { __UNUSED(port); BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_msgmov* msgin = (struct shim_ipc_sysv_msgmov*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_MSGMOV(%d)\n", msg->src, msgin->msgid); struct shim_msg_handle* msgq = get_msg_handle_by_id(msgin->msgid); if (!msgq) { ret = -ENOENT; goto out; } struct shim_handle* hdl = container_of(msgq, struct shim_handle, info.msg); lock(&hdl->lock); int nscores = (msgin->nscores > MAX_SYSV_CLIENTS) ? MAX_SYSV_CLIENTS : msgin->nscores; if (nscores) memcpy(msgq->scores, msgin->scores, nscores); if (nscores < MAX_SYSV_CLIENTS) memset(msgq->scores + nscores, 0, sizeof(struct sysv_score) * (MAX_SYSV_CLIENTS - nscores)); unlock(&hdl->lock); ret = recover_msg_ownership(msgq); struct shim_ipc_info* info; if (!get_ipc_info_cur_process(&info)) { add_sysv_subrange(msgin->msgid, info->vmid, qstrgetstr(&info->uri), &msgin->lease); put_ipc_info(info); } put_msg_handle(msgq); out: SAVE_PROFILE_INTERVAL(ipc_sysv_msgmov_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_semop_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semop_callback, ipc); int ipc_sysv_semop_send(IDTYPE semid, struct sembuf* sops, int nsops, unsigned long timeout, unsigned long* seq) { BEGIN_PROFILE_INTERVAL(); IDTYPE owner; struct shim_ipc_port* port = NULL; int ret = 0; bool waitforreply = false; for (int i = 0; i < nsops; i++) if (sops[i].sem_op <= 0) { waitforreply = true; break; } if ((ret = connect_owner(semid, &port, &owner)) < 0) goto out; if (owner == cur_process.vmid) { ret = -EAGAIN; goto out; } assert(port); if (!waitforreply) { size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_semop) + sizeof(struct sembuf) * nsops); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_SEMOP, total_msg_size, owner); struct shim_ipc_sysv_semop* msgin = (struct shim_ipc_sysv_semop*)&msg->msg; msgin->semid = semid; msgin->timeout = timeout; msgin->nsops = nsops; memcpy(msgin->sops, sops, sizeof(struct sembuf) * nsops); msg->seq = *seq; debug("ipc send to %u: IPC_SYSV_SEMOP(%u, %ld, %u)\n", owner, semid, timeout, nsops); ret = send_ipc_message(msg, port); put_ipc_port(port); goto out; } size_t total_msg_size = get_ipc_msg_duplex_size(sizeof(struct shim_ipc_sysv_semop) + sizeof(struct sembuf) * nsops); struct shim_ipc_msg_duplex* msg = __alloca(total_msg_size); init_ipc_msg_duplex(msg, IPC_SYSV_SEMOP, total_msg_size, owner); struct shim_ipc_sysv_semop* msgin = (struct shim_ipc_sysv_semop*)&msg->msg.msg; msgin->semid = semid; msgin->timeout = timeout; msgin->nsops = nsops; memcpy(msgin->sops, sops, sizeof(struct sembuf) * nsops); msg->msg.seq = *seq; debug("ipc send to %u: IPC_SYSV_SEMOP(%u, %ld, %u)\n", owner, semid, timeout, nsops); ret = send_ipc_message_duplex(msg, port, seq, NULL); put_ipc_port(port); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semop_send); return ret; } int ipc_sysv_semop_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_semop* msgin = (struct shim_ipc_sysv_semop*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMOP(%u, %ld, %u)\n", msg->src, msgin->semid, msgin->timeout, msgin->nsops); struct shim_sem_handle* sem = get_sem_handle_by_id(msgin->semid); if (!sem) { ret = -ENOENT; goto out; } struct sysv_client client; client.port = port; client.vmid = msg->src; client.seq = msg->seq; ret = submit_sysv_sem(sem, msgin->sops, msgin->nsops, msgin->timeout, &client); put_sem_handle(sem); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semop_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_semctl_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semctl_callback, ipc); int ipc_sysv_semctl_send(IDTYPE semid, int semnum, int cmd, void* vals, size_t valsize) { BEGIN_PROFILE_INTERVAL(); IDTYPE owner; struct shim_ipc_port* port = NULL; int ret = 0; if ((ret = connect_owner(semid, &port, &owner)) < 0) goto out; int ctlvalsize = (cmd == SETALL || cmd == SETVAL) ? valsize : 0; size_t total_msg_size = get_ipc_msg_duplex_size(sizeof(struct shim_ipc_sysv_semctl) + ctlvalsize); struct shim_ipc_msg_duplex* msg = __alloca(total_msg_size); init_ipc_msg_duplex(msg, IPC_SYSV_SEMCTL, total_msg_size, owner); struct shim_ipc_sysv_semctl* msgin = (struct shim_ipc_sysv_semctl*)&msg->msg.msg; msgin->semid = semid; msgin->semnum = semnum; msgin->cmd = cmd; msgin->valsize = ctlvalsize; if (ctlvalsize) memcpy(msgin->vals, vals, ctlvalsize); debug("ipc send to %u: IPC_SYSV_SEMCTL(%u, %d, %d)\n", owner, semid, semnum, cmd); ret = send_ipc_message_duplex(msg, port, NULL, vals); put_ipc_port(port); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semctl_send); return ret; } int ipc_sysv_semctl_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_semctl* msgin = (struct shim_ipc_sysv_semctl*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMCTL(%u, %d, %d)\n", msg->src, msgin->semid, msgin->semnum, msgin->cmd); struct shim_sem_handle* sem = get_sem_handle_by_id(msgin->semid); if (!sem) { ret = -ENOENT; goto out; } void* vals = NULL; size_t valsize; switch (msgin->cmd) { case GETALL: { unsigned short* allsems = __alloca(sizeof(unsigned short) * sem->nsems); for (int i = 0; i < sem->nsems; i++) { allsems[i] = sem->sems[i].val; } vals = allsems; valsize = sizeof(unsigned short) * sem->nsems; goto semret; } case GETNCNT: vals = &sem->sems[msgin->semnum].ncnt; valsize = sizeof(unsigned short); goto semret; case GETPID: vals = &sem->sems[msgin->semnum].pid; valsize = sizeof(IDTYPE); goto semret; case GETVAL: vals = &sem->sems[msgin->semnum].val; valsize = sizeof(unsigned short); goto semret; case GETZCNT: vals = &sem->sems[msgin->semnum].zcnt; valsize = sizeof(unsigned short); break; case SETALL: { if (msgin->valsize != sizeof(unsigned short) * sem->nsems) { ret = -EINVAL; break; } unsigned short* vals = (void*)msgin->vals; for (int i = 0; i < sem->nsems; i++) { sem->sems[i].val = vals[i]; } ret = RESPONSE_CALLBACK; break; } case SETVAL: { ret = -EINVAL; if (msgin->valsize != sizeof(sem->sems[msgin->semnum].val)) break; if (msgin->semnum >= sem->nsems) break; memcpy(&sem->sems[msgin->semnum].val, msgin->vals, msgin->valsize); ret = RESPONSE_CALLBACK; break; } default: ret = -ENOSYS; break; } put_sem_handle(sem); goto out; semret: ret = ipc_sysv_semret_send(port, msg->src, vals, valsize, msg->seq); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semctl_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_semret_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semret_callback, ipc); int ipc_sysv_semret_send(struct shim_ipc_port* port, IDTYPE dest, void* vals, size_t valsize, unsigned long seq) { BEGIN_PROFILE_INTERVAL(); int ret = 0; size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_semret) + valsize); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_SEMRET, total_msg_size, dest); struct shim_ipc_sysv_semret* msgin = (struct shim_ipc_sysv_semret*)&msg->msg; msgin->valsize = valsize; memcpy(msgin->vals, vals, valsize); msg->seq = seq; debug("ipc send to %u: IPC_SYSV_SEMRET\n", dest); ret = send_ipc_message(msg, port); SAVE_PROFILE_INTERVAL(ipc_sysv_semret_send); return ret; } int ipc_sysv_semret_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); struct shim_ipc_sysv_semret* semret = (struct shim_ipc_sysv_semret*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMRET\n", msg->src); struct shim_ipc_msg_duplex* obj = pop_ipc_msg_duplex(port, msg->seq); if (obj) { struct shim_ipc_sysv_semctl* semctl = (struct shim_ipc_sysv_semctl*)&obj->msg.msg; void* vals = obj->private; if (vals) { switch (semctl->cmd) { case GETALL: case GETNCNT: case GETPID: case GETVAL: case GETZCNT: { size_t retvalsize = semret->valsize; if (retvalsize > semctl->valsize) retvalsize = semctl->valsize; memcpy(vals, semret->vals, retvalsize); break; } } } if (obj->thread) thread_wakeup(obj->thread); } SAVE_PROFILE_INTERVAL(ipc_sysv_semret_callback); return 0; } DEFINE_PROFILE_INTERVAL(ipc_sysv_semmov_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semmov_callback, ipc); int ipc_sysv_semmov_send(struct shim_ipc_port* port, IDTYPE dest, IDTYPE semid, LEASETYPE lease, struct sem_backup* sems, int nsems, struct sem_client_backup* srcs, int nsrcs, struct sysv_score* scores, int nscores) { BEGIN_PROFILE_INTERVAL(); size_t total_msg_size = get_ipc_msg_size( sizeof(struct shim_ipc_sysv_semmov) + sizeof(struct sem_backup) * nsems + sizeof(struct sem_client_backup) * nsrcs + sizeof(struct sysv_score) * nscores); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_SEMMOV, total_msg_size, dest); struct shim_ipc_sysv_semmov* msgin = (struct shim_ipc_sysv_semmov*)&msg->msg; msgin->semid = semid; msgin->lease = lease; msgin->nsems = nsems; msgin->nsrcs = nsrcs; msgin->nscores = nscores; memcpy(&msgin->sems, sems, sizeof(struct sem_backup) * nsems); memcpy((void*)msgin->sems + sizeof(struct sem_backup) * nsems, srcs, sizeof(struct sem_client_backup) * nsrcs); memcpy((void*)msgin->sems + sizeof(struct sem_backup) * nsems + sizeof(struct sem_client_backup) * nsrcs, scores, sizeof(struct sysv_score) * nscores); debug("ipc send to : IPC_SYSV_SEMMOV(%d)\n", semid); int ret = send_ipc_message(msg, port); SAVE_PROFILE_INTERVAL(ipc_sysv_semmov_send); return ret; } int ipc_sysv_semmov_callback(IPC_CALLBACK_ARGS) { __UNUSED(port); BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_semmov* msgin = (struct shim_ipc_sysv_semmov*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMMOV(%d)\n", msg->src, msgin->semid); struct sem_backup* sems = msgin->sems; struct sem_client_backup* clients = (struct sem_client_backup*)(sems + msgin->nsems); struct sysv_score* scores = (struct sysv_score*)(clients + msgin->nsrcs); struct shim_sem_handle* sem = get_sem_handle_by_id(msgin->semid); if (!sem) { ret = -ENOENT; goto out; } struct shim_handle* hdl = container_of(sem, struct shim_handle, info.sem); lock(&hdl->lock); int nscores = (msgin->nscores > MAX_SYSV_CLIENTS) ? MAX_SYSV_CLIENTS : msgin->nscores; if (nscores) memcpy(sem->scores, scores, nscores); if (nscores < MAX_SYSV_CLIENTS) memset(sem->scores + nscores, 0, sizeof(struct sysv_score) * (MAX_SYSV_CLIENTS - nscores)); unlock(&hdl->lock); ret = recover_sem_ownership(sem, sems, msgin->nsems, clients, msgin->nsrcs); struct shim_ipc_info* info; if (!get_ipc_info_cur_process(&info)) { add_sysv_subrange(msgin->semid, info->vmid, qstrgetstr(&info->uri), &msgin->lease); put_ipc_info(info); } put_sem_handle(sem); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semmov_callback); return ret; } #ifdef USE_SHARED_SEMAPHORE DEFINE_PROFILE_INTERVAL(ipc_sysv_semquery_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semquery_callback, ipc); int ipc_sysv_semquery_send(IDTYPE semid, int* nsems, PAL_NUM** host_sem_ids) { BEGIN_PROFILE_INTERVAL(); int ret = 0; IDTYPE dest; struct shim_ipc_port* port = NULL; if ((ret = connect_owner(semid, &port, &dest)) < 0) goto out; if (dest == cur_process.vmid) { ret = -EAGAIN; goto out; } assert(port); size_t total_msg_size = get_ipc_msg_duplex_size(sizeof(struct shim_ipc_sysv_semquery)); struct shim_ipc_msg_duplex* msg = __alloca(total_msg_size); init_ipc_msg_duplex(msg, IPC_SYSV_SEMQUERY, total_msg_size, dest); struct shim_ipc_sysv_semquery* msgin = (struct shim_ipc_sysv_semquery*)&msg->msg.msg; msgin->semid = semid; debug("ipc send to %u: IPC_SYSV_SEMQUERY(%u)\n", dest, semid); ret = send_ipc_message_duplex(msg, port, NULL, host_sem_ids); put_ipc_port(port); if (ret >= 0) { *nsems = ret; ret = 0; } out: SAVE_PROFILE_INTERVAL(ipc_sysv_semquery_send); return ret; } int ipc_sysv_semquery_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_semquery* msgin = (struct shim_ipc_sysv_semquery*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMQUERY(%u)\n", msg->src, msgin->semid); struct shim_sem_handle* sem = get_sem_handle_by_id(msgin->semid); if (!sem) { ret = -ENOENT; goto out; } ret = send_sem_host_ids(sem, port, msg->src, msg->seq); put_sem_handle(sem); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semreply_callback); return ret; } DEFINE_PROFILE_INTERVAL(ipc_sysv_semreply_send, ipc); DEFINE_PROFILE_INTERVAL(ipc_sysv_semreply_callback, ipc); int ipc_sysv_semreply_send(struct shim_ipc_port* port, IDTYPE dest, IDTYPE semid, int nsems, PAL_NUM* host_sem_ids, unsigned long seq) { BEGIN_PROFILE_INTERVAL(); int ret = 0; size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_sysv_semreply) + sizeof(PAL_NUM) * nsems); struct shim_ipc_msg* msg = __alloca(total_msg_size); init_ipc_msg(msg, IPC_SYSV_SEMREPLY, total_msg_size, dest); struct shim_ipc_sysv_semreply* msgin = (struct shim_ipc_sysv_semreply*)&msg->msg; msgin->semid = semid; msgin->nsems = nsems; if (nsems) memcpy(msgin->host_sem_ids, host_sem_ids, sizeof(PAL_NUM) * nsems); msg->seq = seq; debug("ipc send to %u: IPC_SYSV_SEMREPLY(%u, %d)\n", dest, semid, nsems); ret = send_ipc_message(msg, port); SAVE_PROFILE_INTERVAL(ipc_sysv_semreply_send); return ret; } int ipc_sysv_semreply_callback(IPC_CALLBACK_ARGS) { BEGIN_PROFILE_INTERVAL(); int ret = 0; struct shim_ipc_sysv_semreply* msgin = (struct shim_ipc_sysv_semreply*)&msg->msg; debug("ipc callback from %u: IPC_SYSV_SEMREPLY(%u, %d)\n", msg->src, msgin->semid, msgin->nsems); struct shim_ipc_msg_duplex* obj = pop_ipc_msg_duplex(port, msg->seq); if (!obj) goto out; PAL_NUM** semids = obj->private; if (semids) *semids = malloc_copy(msgin->host_sem_ids, sizeof(PAL_NUM) * msgin->nsems); obj->retval = msgin->nsems; if (obj->thread) thread_wakeup(obj->thread); out: SAVE_PROFILE_INTERVAL(ipc_sysv_semreply_callback); return ret; } #endif /* USE_SHARED_SEMAPHORE */ int __balance_sysv_score(struct sysv_balance_policy* policy, struct shim_handle* hdl, struct sysv_score* scores, int nscores, struct sysv_client* src, long score) { assert(locked(&hdl->lock)); struct sysv_score* s = scores; struct sysv_score* last = scores + nscores; for (; s < last && !s->vmid; s++) ; struct sysv_score* free = s > scores ? scores : NULL; struct sysv_score* highest = s < last ? s : NULL; struct sysv_score* lowest = highest; struct sysv_score* owner = NULL; struct sysv_score* chosen = NULL; for (; s < last; s++) { if (!s->vmid) { if (!free) free = s; continue; } if (s->score >= highest->score) highest = s; if (s->score < lowest->score) lowest = s; if (src) { if (s->vmid == cur_process.vmid) owner = s; if (s->vmid == src->vmid) { chosen = s; continue; } } else { if (s->vmid == cur_process.vmid) { owner = chosen = s; continue; } } s->score = (s->score >= policy->score_decay) ? s->score - policy->score_decay : 0; debug("balance: %u => %ld\n", s->vmid, s->score); } if (!chosen) { chosen = free ?: lowest; chosen->vmid = src ? src->vmid : cur_process.vmid; chosen->score = 0; } chosen->score += score; if (chosen->score > policy->score_max) chosen->score = policy->score_max; debug("balance: %u => %ld\n", chosen->vmid, chosen->score); if (!src || chosen != highest || chosen->score < (owner ? owner->score : 0) + policy->balance_threshold) return 0; return policy->migrate(hdl, src); }