/* -*- 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 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_helper.c
*
* This file contains functions and callbacks to handle IPC between parent
* processes and their children.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int ipc_thread_exit (IDTYPE vmid, IDTYPE ppid, IDTYPE tid,
unsigned int exitcode, unsigned int term_signal, unsigned long exit_time)
{
assert(vmid != cur_process.vmid);
#ifdef PROFILE
if (!exit_time)
exit_time = GET_PROFILE_INTERVAL();
#endif
struct shim_thread * thread = __lookup_thread(tid);
if (thread) {
int ret = 0;
//assert(thread->vmid == vmid && !thread->in_vm);
thread->exit_code = -exitcode;
thread->term_signal = term_signal;
#ifdef PROFILE
thread->exit_time = exit_time;
#endif
ret = thread_exit(thread, false);
put_thread(thread);
return ret;
}
struct shim_simple_thread * sthread = __lookup_simple_thread(tid);
if (!sthread) {
sthread = get_new_simple_thread();
sthread->vmid = vmid;
sthread->tid = tid;
add_simple_thread(sthread);
}
sthread->is_alive = 0;
sthread->exit_code = -exitcode;
sthread->term_signal = term_signal;
#ifdef PROFILE
sthread->exit_time = exit_time;
#endif
DkEventSet(sthread->exit_event);
put_simple_thread(sthread);
return 0;
}
void ipc_parent_exit (struct shim_ipc_port * port, IDTYPE vmid,
unsigned int exitcode)
{
debug("ipc port %p of process %u closed suggests parent exiting\n",
port, vmid);
struct shim_ipc_info * parent = NULL;
lock(cur_process.lock);
if (parent && vmid == cur_process.parent->vmid) {
parent = cur_process.parent;
cur_process.parent = NULL;
}
unlock(cur_process.lock);
if (parent)
put_ipc_info(parent);
}
struct thread_info {
IDTYPE vmid;
unsigned int exitcode;
unsigned int term_signal;
};
static int child_sthread_exit (struct shim_simple_thread * thread, void * arg,
bool * unlocked)
{
struct thread_info * info = (struct thread_info *) arg;
if (thread->vmid == info->vmid) {
if (thread->is_alive) {
thread->exit_code = -info->exitcode;
thread->term_signal = info->term_signal;
thread->is_alive = false;
DkEventSet(thread->exit_event);
}
return 1;
}
return 0;
}
static int child_thread_exit (struct shim_thread * thread, void * arg,
bool * unlocked)
{
struct thread_info * info = (struct thread_info *) arg;
if (thread->vmid == info->vmid) {
if (thread->is_alive) {
thread->exit_code = -info->exitcode;
thread->term_signal = info->term_signal;
thread_exit(thread, false);
}
return 1;
}
return 0;
}
int remove_child_thread (IDTYPE vmid, unsigned int exitcode, unsigned int term_signal)
{
struct thread_info info = { .vmid = vmid, .exitcode = exitcode, .term_signal = term_signal };
int nkilled = 0, ret;
assert(vmid != cur_process.vmid);
if ((ret = walk_thread_list(&child_thread_exit, &info, false)) > 0)
nkilled += ret;
if ((ret = walk_simple_thread_list(&child_sthread_exit, &info, false)) > 0)
nkilled += ret;
if (!nkilled)
debug("child port closed, no thread exited\n");
return 0;
}
void ipc_child_exit (struct shim_ipc_port * port, IDTYPE vmid,
unsigned int exitcode)
{
debug("ipc port %p of process %u closed suggests child exiting\n",
port, vmid);
/*
* Chia-Che 12/12/2017:
* Can't assume there is a termination signal. this callback
* is only called when the child process is not responding, and
* under this circumstance can only assume the child process
* has encountered severe failure, hence SIGKILL.
*/
remove_child_thread(vmid, exitcode, SIGKILL);
}
static struct shim_ipc_port * get_parent_port (IDTYPE * dest)
{
struct shim_ipc_port * port = NULL;
lock(cur_process.lock);
if (cur_process.parent && (port = cur_process.parent->port)) {
get_ipc_port(port);
*dest = cur_process.parent->vmid;
}
unlock(cur_process.lock);
return port;
}
DEFINE_PROFILE_INTERVAL(ipc_cld_exit_turnaround, ipc);
DEFINE_PROFILE_INTERVAL(ipc_cld_exit_send, ipc);
DEFINE_PROFILE_INTERVAL(ipc_cld_exit_callback, ipc);
int ipc_cld_exit_send (IDTYPE ppid, IDTYPE tid, unsigned int exitcode, unsigned int term_signal)
{
unsigned long send_time = GET_PROFILE_INTERVAL();
BEGIN_PROFILE_INTERVAL_SET(send_time);
int ret = 0;
struct shim_ipc_msg * msg =
create_ipc_msg_on_stack(IPC_CLD_EXIT,
sizeof(struct shim_ipc_cld_exit), 0);
struct shim_ipc_cld_exit * msgin =
(struct shim_ipc_cld_exit *) &msg->msg;
msgin->ppid = ppid;
msgin->tid = tid;
msgin->exitcode = exitcode;
msgin->term_signal = term_signal;
#ifdef PROFILE
msgin->time = send_time;
#endif
debug("ipc broadcast: IPC_CLD_EXIT(%u, %u, %d)\n", ppid, tid, exitcode);
ret = broadcast_ipc(msg, NULL, 0, IPC_PORT_DIRPRT|IPC_PORT_DIRCLD);
SAVE_PROFILE_INTERVAL(ipc_cld_exit_send);
return ret;
}
int ipc_cld_exit_callback (IPC_CALLBACK_ARGS)
{
struct shim_ipc_cld_exit * msgin =
(struct shim_ipc_cld_exit *) &msg->msg;
#ifdef PROFILE
unsigned long time = msgin->time;
#else
unsigned long time = 0;
#endif
BEGIN_PROFILE_INTERVAL_SET(time);
SAVE_PROFILE_INTERVAL(ipc_cld_exit_turnaround);
debug("ipc callback from %u: IPC_CLD_EXIT(%u, %u, %d)\n",
msg->src, msgin->ppid, msgin->tid, msgin->exitcode);
int ret = ipc_thread_exit(msg->src, msgin->ppid, msgin->tid,
msgin->exitcode, msgin->term_signal,
time);
SAVE_PROFILE_INTERVAL(ipc_cld_exit_callback);
return ret;
}
DEFINE_PROFILE_INTERVAL(ipc_cld_join_send, ipc);
DEFINE_PROFILE_INTERVAL(ipc_cld_join_callback, ipc);
int ipc_cld_join_send (IDTYPE dest)
{
BEGIN_PROFILE_INTERVAL();
struct shim_ipc_port * port = dest ?
lookup_ipc_port(dest, IPC_PORT_DIRPRT) :
get_parent_port(&dest);
if (!port)
return -ESRCH;
struct shim_ipc_msg * msg =
create_ipc_msg_on_stack(IPC_CLD_JOIN, 0, dest);
debug("ipc send to %u: IPC_CLD_JOIN\n", dest);
int ret = send_ipc_message(msg, port);
add_ipc_port(port, dest, IPC_PORT_DIRPRT, NULL);
put_ipc_port(port);
SAVE_PROFILE_INTERVAL(ipc_cld_join_send);
return ret;
}
int ipc_cld_join_callback (IPC_CALLBACK_ARGS)
{
BEGIN_PROFILE_INTERVAL();
debug("ipc callback from %u: IPC_CLD_JOIN\n", msg->src);
add_ipc_port(port, msg->src, IPC_PORT_DIRCLD, NULL);
SAVE_PROFILE_INTERVAL(ipc_cld_join_callback);
return 0;
}
DEFINE_PROFILE_INTERVAL(ipc_send_profile, ipc);
#ifdef PROFILE
int ipc_cld_profile_send (void)
{
IDTYPE dest;
struct shim_ipc_port * port = get_parent_port(&dest);
if (!port)
return -ESRCH;
unsigned long time = GET_PROFILE_INTERVAL();
int nsending = 0;
for (int i = 0 ; i < N_PROFILE ; i++)
switch (PROFILES[i].type) {
case OCCURENCE:
if (atomic_read(&PROFILES[i].val.occurence.count))
nsending++;
break;
case INTERVAL:
if (atomic_read(&PROFILES[i].val.interval.count))
nsending++;
break;
case CATAGORY:
break;
}
struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
IPC_CLD_PROFILE,
sizeof(struct shim_ipc_cld_profile) +
sizeof(struct profile_val) *
nsending, dest);
struct shim_ipc_cld_profile * msgin =
(struct shim_ipc_cld_profile *) &msg->msg;
int nsent = 0;
for (int i = 0 ; i < N_PROFILE && nsent < nsending ; i++)
switch (PROFILES[i].type) {
case OCCURENCE: {
unsigned long count =
atomic_read(&PROFILES[i].val.occurence.count);
if (count) {
msgin->profile[nsent].idx = i + 1;
msgin->profile[nsent].val.occurence.count = count;
debug("send %s: %lu times\n", PROFILES[i].name, count);
nsent++;
}
break;
}
case INTERVAL: {
unsigned long count =
atomic_read(&PROFILES[i].val.interval.count);
if (count) {
msgin->profile[nsent].idx = i + 1;
msgin->profile[nsent].val.interval.count = count;
msgin->profile[nsent].val.interval.time =
atomic_read(&PROFILES[i].val.interval.time);
debug("send %s: %lu times, %lu msec\n", PROFILES[i].name,
count, msgin->profile[nsent].val.interval.time);
nsent++;
}
break;
}
case CATAGORY:
break;
}
msgin->time = time;
msgin->nprofile = nsent;
debug("ipc send to %u: IPC_CLD_PROFILE\n", dest);
int ret = send_ipc_message(msg, port);
put_ipc_port(port);
return ret;
}
int ipc_cld_profile_callback (IPC_CALLBACK_ARGS)
{
struct shim_ipc_cld_profile * msgin =
(struct shim_ipc_cld_profile *) &msg->msg;
debug("ipc callback from %u: IPC_CLD_PROFILE\n", msg->src);
for (int i = 0 ; i < msgin->nprofile ; i++) {
int idx = msgin->profile[i].idx;
if (idx == 0)
break;
idx--;
switch (PROFILES[idx].type) {
case OCCURENCE:
debug("receive %s: %u times\n", PROFILES[idx].name,
msgin->profile[i].val.occurence.count);
atomic_add(msgin->profile[i].val.occurence.count,
&PROFILES[idx].val.occurence.count);
break;
case INTERVAL:
debug("receive %s: %u times, %lu msec\n", PROFILES[idx].name,
msgin->profile[i].val.interval.count,
msgin->profile[i].val.interval.time);
atomic_add(msgin->profile[i].val.interval.count,
&PROFILES[idx].val.interval.count);
atomic_add(msgin->profile[i].val.interval.time,
&PROFILES[idx].val.interval.time);
break;
case CATAGORY:
break;
}
}
SAVE_PROFILE_INTERVAL_SINCE(ipc_send_profile, msgin->time);
return 0;
}
#endif