/* -*- 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_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
#include "shim_ipc_nsimpl.h"
int init_ns_sysv (void)
{
init_namespace();
return 0;
}
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) {
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_DELRES,
sizeof(struct shim_ipc_sysv_delres),
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;
}
struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
IPC_SYSV_DELRES,
sizeof(struct shim_ipc_sysv_delres),
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 = do_ipc_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)
{
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);
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_MOVRES,
sizeof(struct shim_ipc_sysv_movres) +
len, 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_obj * obj = find_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;
}
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_MSGSND,
sizeof(struct shim_ipc_sysv_msgsnd) +
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_obj * obj = find_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);
struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
IPC_SYSV_MSGRCV,
sizeof(struct shim_ipc_sysv_msgrcv),
true);
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 = do_ipc_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();
struct shim_ipc_msg * msg =
create_ipc_msg_on_stack(IPC_SYSV_MSGMOV,
sizeof(struct shim_ipc_sysv_msgmov) +
sizeof(struct sysv_score) * nscores,
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(%ld)\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)
{
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(%ld)\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 (!create_ipc_location(&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) {
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_SEMOP,
sizeof(struct shim_ipc_sysv_semop) +
sizeof(struct sembuf) * nsops,
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;
}
struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
IPC_SYSV_SEMOP,
sizeof(struct shim_ipc_sysv_semop) +
sizeof(struct sembuf) * nsops,
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 = do_ipc_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,
int 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;
struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
IPC_SYSV_SEMCTL,
sizeof(struct shim_ipc_sysv_semctl) +
ctlvalsize,
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 = do_ipc_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;
int 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(unsigned short))
break;
if (msgin->semnum >= sem->nsems)
break;
sem->sems[msgin->semnum].val = *((int *) msgin->vals);
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,
int valsize, unsigned long seq)
{
BEGIN_PROFILE_INTERVAL();
int ret = 0;
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_SEMRET,
sizeof(struct shim_ipc_sysv_semret) +
valsize,
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_obj * obj = find_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: {
int 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();
struct shim_ipc_msg * msg =
create_ipc_msg_on_stack(IPC_SYSV_SEMMOV,
sizeof(struct shim_ipc_sysv_semmov) +
sizeof(struct sem_backup) * nsems +
sizeof(struct sem_client_backup) * nsrcs +
sizeof(struct sysv_score) * nscores,
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 %u: IPC_SYSV_SEMMOV(%ld)\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)
{
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(%ld)\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 (!create_ipc_location(&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);
struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
IPC_SYSV_SEMQUERY,
sizeof(struct shim_ipc_sysv_semquery),
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 = do_ipc_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;
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_SYSV_SEMREPLY,
sizeof(struct shim_ipc_sysv_semreply)
+ sizeof(PAL_NUM) * nsems,
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_obj * obj = find_ipc_msg_duplex(port, msg->seq);
if (!obj)
goto out;
PAL_NUM ** semids = obj->private;
if (semids)
*semids = remalloc(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)
{
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 => %d\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 => %d\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);
}