|
@@ -17,7 +17,9 @@
|
|
|
/*
|
|
|
* shim_ipc.c
|
|
|
*
|
|
|
- * This file contains codes to maintain generic bookkeeping of IPC.
|
|
|
+ * This file contains codes to maintain generic bookkeeping of IPC: operations
|
|
|
+ * on shim_ipc_msg (one-way IPC messages), shim_ipc_msg_duplex (IPC messages
|
|
|
+ * with acknowledgement), shim_ipc_info (IPC ports of process), shim_process.
|
|
|
*/
|
|
|
|
|
|
#include <shim_internal.h>
|
|
@@ -35,25 +37,29 @@
|
|
|
|
|
|
#define IPC_INFO_MGR_ALLOC 32
|
|
|
#define PAGE_SIZE allocsize
|
|
|
-
|
|
|
#define OBJ_TYPE struct shim_ipc_info
|
|
|
#include "memmgr.h"
|
|
|
-
|
|
|
static MEM_MGR ipc_info_mgr;
|
|
|
|
|
|
struct shim_lock ipc_info_lock;
|
|
|
|
|
|
struct shim_process cur_process;
|
|
|
|
|
|
+#define CLIENT_HASH_LEN 6
|
|
|
+#define CLIENT_HASH_NUM (1 << CLIENT_HASH_LEN)
|
|
|
+#define CLIENT_HASH_MASK (CLIENT_HASH_NUM - 1)
|
|
|
+#define CLIENT_HASH(vmid) ((vmid) & CLIENT_HASH_MASK)
|
|
|
+DEFINE_LISTP(shim_ipc_info);
|
|
|
+static LISTP_TYPE(shim_ipc_info) info_hlist[CLIENT_HASH_NUM];
|
|
|
+
|
|
|
DEFINE_PROFILE_CATEGORY(ipc, );
|
|
|
DEFINE_PROFILE_OCCURENCE(syscall_use_ipc, ipc);
|
|
|
|
|
|
-int init_ipc_ports (void);
|
|
|
-int init_ns_pid (void);
|
|
|
-int init_ns_sysv (void);
|
|
|
+int init_ipc_ports(void);
|
|
|
+int init_ns_pid(void);
|
|
|
+int init_ns_sysv(void);
|
|
|
|
|
|
-int init_ipc (void)
|
|
|
-{
|
|
|
+int init_ipc(void) {
|
|
|
int ret = 0;
|
|
|
|
|
|
create_lock(&ipc_info_lock);
|
|
@@ -63,18 +69,15 @@ int init_ipc (void)
|
|
|
|
|
|
if ((ret = init_ipc_ports()) < 0)
|
|
|
return ret;
|
|
|
-
|
|
|
if ((ret = init_ns_pid()) < 0)
|
|
|
return ret;
|
|
|
-
|
|
|
if ((ret = init_ns_sysv()) < 0)
|
|
|
return ret;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int prepare_ns_leaders (void)
|
|
|
-{
|
|
|
+int prepare_ns_leaders(void) {
|
|
|
int ret = 0;
|
|
|
if ((ret = prepare_pid_leader()) < 0)
|
|
|
return ret;
|
|
@@ -83,18 +86,14 @@ int prepare_ns_leaders (void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static struct shim_ipc_info * __get_new_ipc_info (IDTYPE vmid, const char * uri,
|
|
|
- size_t len)
|
|
|
-{
|
|
|
- struct shim_ipc_info * info =
|
|
|
- get_mem_obj_from_mgr_enlarge(ipc_info_mgr,
|
|
|
- size_align_up(IPC_INFO_MGR_ALLOC));
|
|
|
+static struct shim_ipc_info* __create_ipc_info(IDTYPE vmid, const char* uri, size_t len) {
|
|
|
+ struct shim_ipc_info* info =
|
|
|
+ get_mem_obj_from_mgr_enlarge(ipc_info_mgr, size_align_up(IPC_INFO_MGR_ALLOC));
|
|
|
if (!info)
|
|
|
return NULL;
|
|
|
|
|
|
memset(info, 0, sizeof(struct shim_ipc_info));
|
|
|
- if (vmid)
|
|
|
- info->vmid = vmid;
|
|
|
+ info->vmid = vmid;
|
|
|
if (uri)
|
|
|
qstrsetstr(&info->uri, uri, len);
|
|
|
REF_SET(info->ref_count, 1);
|
|
@@ -102,257 +101,168 @@ static struct shim_ipc_info * __get_new_ipc_info (IDTYPE vmid, const char * uri,
|
|
|
return info;
|
|
|
}
|
|
|
|
|
|
-struct shim_ipc_info * get_new_ipc_info (IDTYPE vmid, const char * uri,
|
|
|
- size_t len)
|
|
|
-{
|
|
|
- lock(&ipc_info_lock);
|
|
|
- struct shim_ipc_info * info = __get_new_ipc_info(vmid, uri, len);
|
|
|
- unlock(&ipc_info_lock);
|
|
|
- return info;
|
|
|
+static void __free_ipc_info(struct shim_ipc_info* info) {
|
|
|
+ if (info->pal_handle) {
|
|
|
+ DkObjectClose(info->pal_handle);
|
|
|
+ info->pal_handle = NULL;
|
|
|
+ }
|
|
|
+ if (info->port)
|
|
|
+ put_ipc_port(info->port);
|
|
|
+ qstrfree(&info->uri);
|
|
|
+ free_mem_obj_to_mgr(ipc_info_mgr, info);
|
|
|
}
|
|
|
|
|
|
-static void __get_ipc_info (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
+static void __get_ipc_info(struct shim_ipc_info* info) {
|
|
|
REF_INC(info->ref_count);
|
|
|
}
|
|
|
|
|
|
-void get_ipc_info (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
- __get_ipc_info(info);
|
|
|
+static void __put_ipc_info(struct shim_ipc_info* info) {
|
|
|
+ int ref_count = REF_DEC(info->ref_count);
|
|
|
+ if (!ref_count)
|
|
|
+ __free_ipc_info(info);
|
|
|
}
|
|
|
|
|
|
-static void unset_ipc_info (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
- qstrfree(&info->uri);
|
|
|
-
|
|
|
- if (info->port)
|
|
|
- put_ipc_port(info->port);
|
|
|
-
|
|
|
- if (info->pal_handle)
|
|
|
- DkObjectClose(info->pal_handle);
|
|
|
+void get_ipc_info(struct shim_ipc_info* info) {
|
|
|
+ /* no need to grab ipc_info_lock because __get_ipc_info() is atomic */
|
|
|
+ __get_ipc_info(info);
|
|
|
}
|
|
|
|
|
|
-static void __put_ipc_info (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
+void put_ipc_info(struct shim_ipc_info* info) {
|
|
|
+ /* this is atomic so we don't grab lock in common case of ref_count > 0 */
|
|
|
int ref_count = REF_DEC(info->ref_count);
|
|
|
|
|
|
- if (ref_count)
|
|
|
- return;
|
|
|
-
|
|
|
- unset_ipc_info(info);
|
|
|
- free_mem_obj_to_mgr(ipc_info_mgr, info);
|
|
|
+ if (!ref_count) {
|
|
|
+ lock(&ipc_info_lock);
|
|
|
+ __free_ipc_info(info);
|
|
|
+ unlock(&ipc_info_lock);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-void put_ipc_info (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
- int ref_count = REF_DEC(info->ref_count);
|
|
|
-
|
|
|
- if (ref_count)
|
|
|
- return;
|
|
|
-
|
|
|
- unset_ipc_info(info);
|
|
|
+struct shim_ipc_info* create_ipc_info(IDTYPE vmid, const char* uri, size_t len) {
|
|
|
lock(&ipc_info_lock);
|
|
|
- free_mem_obj_to_mgr(ipc_info_mgr, info);
|
|
|
+ struct shim_ipc_info* info = __create_ipc_info(vmid, uri, len);
|
|
|
unlock(&ipc_info_lock);
|
|
|
+ return info;
|
|
|
}
|
|
|
|
|
|
-#define CLIENT_HASH_LEN 6
|
|
|
-#define CLIENT_HASH_NUM (1 << CLIENT_HASH_LEN)
|
|
|
-#define CLIENT_HASH_MASK (CLIENT_HASH_NUM - 1)
|
|
|
-#define CLIENT_HASH(vmid) ((vmid) & CLIENT_HASH_MASK)
|
|
|
-
|
|
|
-/* Links to shim_ipc_info->hlist */
|
|
|
-DEFINE_LISTP(shim_ipc_info);
|
|
|
-static LISTP_TYPE(shim_ipc_info) client_table [CLIENT_HASH_NUM];
|
|
|
+struct shim_ipc_info* create_ipc_info_in_list(IDTYPE vmid, const char* uri) {
|
|
|
+ assert(vmid);
|
|
|
|
|
|
-struct shim_ipc_info *
|
|
|
-lookup_and_alloc_client (IDTYPE vmid, const char * uri)
|
|
|
-{
|
|
|
- struct shim_ipc_info * p;
|
|
|
- LISTP_TYPE(shim_ipc_info) *head = client_table + CLIENT_HASH(vmid);
|
|
|
+ struct shim_ipc_info* info;
|
|
|
size_t len = strlen(uri);
|
|
|
|
|
|
- assert(vmid);
|
|
|
-
|
|
|
lock(&ipc_info_lock);
|
|
|
- LISTP_FOR_EACH_ENTRY(p, head, hlist)
|
|
|
- if (p->vmid == vmid && !qstrcmpstr(&p->uri, uri, len)) {
|
|
|
- get_ipc_info(p);
|
|
|
+
|
|
|
+ /* check if info with this vmid and uri already exists and return it */
|
|
|
+ LISTP_TYPE(shim_ipc_info)* info_bucket = &info_hlist[CLIENT_HASH(vmid)];
|
|
|
+ LISTP_FOR_EACH_ENTRY(info, info_bucket, hlist)
|
|
|
+ if (info->vmid == vmid && !qstrcmpstr(&info->uri, uri, len)) {
|
|
|
+ get_ipc_info(info);
|
|
|
unlock(&ipc_info_lock);
|
|
|
- return p;
|
|
|
+ return info;
|
|
|
}
|
|
|
|
|
|
- p = __get_new_ipc_info(vmid, uri, len);
|
|
|
- if (p) {
|
|
|
- LISTP_ADD(p, head, hlist);
|
|
|
- get_ipc_info(p);
|
|
|
+ /* otherwise create new info and return it */
|
|
|
+ info = __create_ipc_info(vmid, uri, len);
|
|
|
+ if (info) {
|
|
|
+ LISTP_ADD(info, info_bucket, hlist);
|
|
|
+ get_ipc_info(info);
|
|
|
}
|
|
|
+
|
|
|
unlock(&ipc_info_lock);
|
|
|
- return p;
|
|
|
+ return info;
|
|
|
}
|
|
|
|
|
|
-void put_client (struct shim_ipc_info * info)
|
|
|
-{
|
|
|
+void put_ipc_info_in_list(struct shim_ipc_info* info) {
|
|
|
+ LISTP_TYPE(shim_ipc_info)* info_bucket = &info_hlist[CLIENT_HASH(info->vmid)];
|
|
|
+
|
|
|
lock(&ipc_info_lock);
|
|
|
- /* Look up the hash */
|
|
|
- LISTP_TYPE(shim_ipc_info) *head = client_table + CLIENT_HASH(info->vmid);
|
|
|
__put_ipc_info(info);
|
|
|
if (REF_GET(info->ref_count) == 1) {
|
|
|
- LISTP_DEL_INIT(info, head, hlist);
|
|
|
+ LISTP_DEL_INIT(info, info_bucket, hlist);
|
|
|
__put_ipc_info(info);
|
|
|
}
|
|
|
unlock(&ipc_info_lock);
|
|
|
}
|
|
|
|
|
|
-struct shim_ipc_info * discover_client (struct shim_ipc_port * port,
|
|
|
- IDTYPE vmid)
|
|
|
-{
|
|
|
- struct shim_ipc_info * p;
|
|
|
- LISTP_TYPE(shim_ipc_info) * head = client_table + CLIENT_HASH(vmid);
|
|
|
-
|
|
|
+struct shim_ipc_info* lookup_ipc_info(IDTYPE vmid) {
|
|
|
assert(vmid);
|
|
|
-
|
|
|
lock(&ipc_info_lock);
|
|
|
- LISTP_FOR_EACH_ENTRY(p, head, hlist)
|
|
|
- if (p->vmid == vmid && !qstrempty(&p->uri)) {
|
|
|
- __get_ipc_info(p);
|
|
|
+
|
|
|
+ struct shim_ipc_info* info;
|
|
|
+ LISTP_TYPE(shim_ipc_info)* info_bucket = &info_hlist[CLIENT_HASH(vmid)];
|
|
|
+ LISTP_FOR_EACH_ENTRY(info, info_bucket, hlist)
|
|
|
+ if (info->vmid == vmid && !qstrempty(&info->uri)) {
|
|
|
+ __get_ipc_info(info);
|
|
|
unlock(&ipc_info_lock);
|
|
|
- return p;
|
|
|
+ return info;
|
|
|
}
|
|
|
- unlock(&ipc_info_lock);
|
|
|
- return NULL;
|
|
|
-
|
|
|
- if (!ipc_finduri_send(port, vmid, &p))
|
|
|
- return p;
|
|
|
|
|
|
+ unlock(&ipc_info_lock);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-struct shim_process * create_new_process (bool inherit_parent)
|
|
|
-{
|
|
|
- struct shim_process * new_process = calloc(1, sizeof(struct shim_process));
|
|
|
+struct shim_process* create_process(void) {
|
|
|
+ struct shim_process* new_process = calloc(1, sizeof(struct shim_process));
|
|
|
if (!new_process)
|
|
|
return NULL;
|
|
|
|
|
|
- new_process->parent = get_new_ipc_info(cur_process.vmid, NULL, 0);
|
|
|
-
|
|
|
- if (!inherit_parent)
|
|
|
- return new_process;
|
|
|
+ new_process->parent = create_ipc_info(cur_process.vmid, NULL, 0);
|
|
|
|
|
|
lock(&cur_process.lock);
|
|
|
|
|
|
if (cur_process.self)
|
|
|
qstrcopy(&new_process->parent->uri, &cur_process.self->uri);
|
|
|
-
|
|
|
- for (int i = 0 ; i < TOTAL_NS ; i++)
|
|
|
+ for (int i = 0; i < TOTAL_NS; i++) {
|
|
|
if (cur_process.ns[i])
|
|
|
- new_process->ns[i] =
|
|
|
- get_new_ipc_info(cur_process.ns[i]->vmid,
|
|
|
- qstrgetstr(&cur_process.ns[i]->uri),
|
|
|
- cur_process.ns[i]->uri.len);
|
|
|
+ new_process->ns[i] = create_ipc_info(cur_process.ns[i]->vmid,
|
|
|
+ qstrgetstr(&cur_process.ns[i]->uri),
|
|
|
+ cur_process.ns[i]->uri.len);
|
|
|
+ }
|
|
|
|
|
|
unlock(&cur_process.lock);
|
|
|
return new_process;
|
|
|
}
|
|
|
|
|
|
-void destroy_process (struct shim_process * proc)
|
|
|
-{
|
|
|
- if (proc->self)
|
|
|
- put_ipc_info(proc->self);
|
|
|
-
|
|
|
- if (proc->parent)
|
|
|
- put_ipc_info(proc->parent);
|
|
|
-
|
|
|
- for (int i = 0 ; i < TOTAL_NS ; i++)
|
|
|
- if (proc->ns[i])
|
|
|
- put_ipc_info(proc->ns[i]);
|
|
|
-
|
|
|
- free(proc);
|
|
|
+void free_process(struct shim_process* process) {
|
|
|
+ if (process->self)
|
|
|
+ put_ipc_info(process->self);
|
|
|
+ if (process->parent)
|
|
|
+ put_ipc_info(process->parent);
|
|
|
+ for (int i = 0; i < TOTAL_NS; i++)
|
|
|
+ if (process->ns[i])
|
|
|
+ put_ipc_info(process->ns[i]);
|
|
|
+ free(process);
|
|
|
}
|
|
|
|
|
|
-int __init_ipc_msg (struct shim_ipc_msg * msg, int code, int size, IDTYPE dest)
|
|
|
-{
|
|
|
+void init_ipc_msg(struct shim_ipc_msg* msg, int code, size_t size, IDTYPE dest) {
|
|
|
msg->code = code;
|
|
|
- msg->size = IPC_MSG_SIZE(size);
|
|
|
+ msg->size = get_ipc_msg_size(size);
|
|
|
msg->src = cur_process.vmid;
|
|
|
msg->dst = dest;
|
|
|
msg->seq = 0;
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
-struct shim_ipc_msg * create_ipc_msg (int code, int size, IDTYPE dest)
|
|
|
-{
|
|
|
- struct shim_ipc_msg * msg = malloc(IPC_MSG_SIZE(size));
|
|
|
-
|
|
|
- if (msg && __init_ipc_msg(msg, code, size, dest)) {
|
|
|
- free(msg);
|
|
|
- msg = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return msg;
|
|
|
-}
|
|
|
-
|
|
|
-int __init_ipc_msg_duplex (struct shim_ipc_msg_obj * msg, int code, int size,
|
|
|
- IDTYPE dest)
|
|
|
-{
|
|
|
- __init_ipc_msg(&msg->msg, code, size, dest);
|
|
|
+void init_ipc_msg_duplex(struct shim_ipc_msg_duplex* msg, int code, size_t size, IDTYPE dest) {
|
|
|
+ init_ipc_msg(&msg->msg, code, size, dest);
|
|
|
msg->thread = NULL;
|
|
|
INIT_LIST_HEAD(msg, list);
|
|
|
msg->retval = 0;
|
|
|
msg->private = NULL;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-struct shim_ipc_msg_obj *
|
|
|
-create_ipc_msg_duplex (int code, int size, IDTYPE dest)
|
|
|
-{
|
|
|
- struct shim_ipc_msg_obj * msg = malloc(IPC_MSGOBJ_SIZE(size));
|
|
|
-
|
|
|
- if (msg && __init_ipc_msg_duplex(msg, code, size, dest)) {
|
|
|
- free(msg);
|
|
|
- msg = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return msg;
|
|
|
-}
|
|
|
-
|
|
|
-int __init_ipc_resp_msg (struct shim_ipc_msg * resp, int ret,
|
|
|
- unsigned long seq)
|
|
|
-{
|
|
|
- struct shim_ipc_resp * resp_in = (struct shim_ipc_resp *) resp->msg;
|
|
|
- resp->seq = seq;
|
|
|
- resp_in->retval = ret;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-struct shim_ipc_msg *
|
|
|
-create_ipc_resp_msg (int ret, IDTYPE dest, unsigned long seq)
|
|
|
-{
|
|
|
- struct shim_ipc_msg * resp =
|
|
|
- create_ipc_msg(IPC_RESP, sizeof(struct shim_ipc_resp), dest);
|
|
|
-
|
|
|
- if (resp && __init_ipc_resp_msg(resp, ret, seq)) {
|
|
|
- free(resp);
|
|
|
- resp = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return resp;
|
|
|
}
|
|
|
|
|
|
-int send_ipc_message (struct shim_ipc_msg * msg, struct shim_ipc_port * port)
|
|
|
-{
|
|
|
+int send_ipc_message(struct shim_ipc_msg* msg, struct shim_ipc_port* port) {
|
|
|
assert(msg->size >= IPC_MSG_MINIMAL_SIZE);
|
|
|
- msg->src = cur_process.vmid;
|
|
|
|
|
|
- debug("send ipc message to port %p (handle %p)\n", port,
|
|
|
- port->pal_handle);
|
|
|
+ msg->src = cur_process.vmid;
|
|
|
+ debug("Sending ipc message to port %p (handle %p)\n", port, port->pal_handle);
|
|
|
|
|
|
+ /* TODO: Handle benign EINTR case? */
|
|
|
+ /* TODO: Add while-loop to send all msg */
|
|
|
int ret = DkStreamWrite(port->pal_handle, 0, msg->size, msg, NULL);
|
|
|
|
|
|
if (ret == 0 && PAL_NATIVE_ERRNO) {
|
|
|
- debug("port %p (handle %p) is removed at sending\n", port,
|
|
|
- port->pal_handle);
|
|
|
-
|
|
|
+ debug("Port %p (handle %p) was removed during sending\n", port, port->pal_handle);
|
|
|
del_ipc_port_fini(port, -ECHILD);
|
|
|
return -PAL_ERRNO;
|
|
|
}
|
|
@@ -360,57 +270,11 @@ int send_ipc_message (struct shim_ipc_msg * msg, struct shim_ipc_port * port)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int close_ipc_message_duplex (struct shim_ipc_msg_obj * msg,
|
|
|
- struct shim_ipc_port * port)
|
|
|
-{
|
|
|
- if (port) {
|
|
|
- // Check if the message is pending on the port for response. If so,
|
|
|
- // remove the message from the list.
|
|
|
- lock(&port->msgs_lock);
|
|
|
- if (!LIST_EMPTY(msg, list))
|
|
|
- LISTP_DEL_INIT(msg, &port->msgs, list);
|
|
|
- unlock(&port->msgs_lock);
|
|
|
- }
|
|
|
-
|
|
|
- if (msg->thread) {
|
|
|
- put_thread(msg->thread);
|
|
|
- msg->thread = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static struct atomic_int ipc_seq_counter;
|
|
|
-
|
|
|
-int send_ipc_message_duplex (struct shim_ipc_msg_obj * msg,
|
|
|
- struct shim_ipc_port * port, bool save,
|
|
|
- void * private_data)
|
|
|
-{
|
|
|
- msg->msg.seq = atomic_inc_return(&ipc_seq_counter);
|
|
|
-
|
|
|
- if (save) {
|
|
|
- lock(&port->msgs_lock);
|
|
|
- msg->private = private_data;
|
|
|
- LISTP_ADD_TAIL(msg, &port->msgs, list);
|
|
|
- unlock(&port->msgs_lock);
|
|
|
- }
|
|
|
-
|
|
|
- int ret = send_ipc_message(&msg->msg, port);
|
|
|
-
|
|
|
- if (ret < 0) {
|
|
|
- if (save)
|
|
|
- close_ipc_message_duplex(msg, port);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
+struct shim_ipc_msg_duplex* pop_ipc_msg_duplex(struct shim_ipc_port* port, unsigned long seq) {
|
|
|
+ struct shim_ipc_msg_duplex* found = NULL;
|
|
|
|
|
|
-struct shim_ipc_msg_obj * find_ipc_msg_duplex (struct shim_ipc_port * port,
|
|
|
- unsigned long seq)
|
|
|
-{
|
|
|
- struct shim_ipc_msg_obj * tmp, * found = NULL;
|
|
|
lock(&port->msgs_lock);
|
|
|
+ struct shim_ipc_msg_duplex* tmp;
|
|
|
LISTP_FOR_EACH_ENTRY(tmp, &port->msgs, list)
|
|
|
if (tmp->msg.seq == seq) {
|
|
|
found = tmp;
|
|
@@ -418,262 +282,197 @@ struct shim_ipc_msg_obj * find_ipc_msg_duplex (struct shim_ipc_port * port,
|
|
|
break;
|
|
|
}
|
|
|
unlock(&port->msgs_lock);
|
|
|
+
|
|
|
return found;
|
|
|
}
|
|
|
|
|
|
-/* for convenience */
|
|
|
-int do_ipc_duplex (struct shim_ipc_msg_obj * msg,
|
|
|
- struct shim_ipc_port * port, unsigned long * seq,
|
|
|
- void * private_data)
|
|
|
-{
|
|
|
+int send_ipc_message_duplex(struct shim_ipc_msg_duplex* msg, struct shim_ipc_port* port,
|
|
|
+ unsigned long* seq, void* private_data) {
|
|
|
int ret = 0;
|
|
|
+
|
|
|
struct shim_thread * thread = get_cur_thread();
|
|
|
assert(thread);
|
|
|
|
|
|
+ /* prepare thread which sends the message for waiting for response
|
|
|
+ * (this also acquires reference to the thread) */
|
|
|
if (!msg->thread)
|
|
|
thread_setwait(&msg->thread, thread);
|
|
|
|
|
|
- ret = send_ipc_message_duplex(msg, port, true, private_data);
|
|
|
+ static struct atomic_int ipc_seq_counter;
|
|
|
+ msg->msg.seq = atomic_inc_return(&ipc_seq_counter);
|
|
|
|
|
|
- if (seq)
|
|
|
- *seq = (ret < 0) ? 0 : msg->msg.seq;
|
|
|
+ /* save the message to list of port msgs together with its private data */
|
|
|
+ lock(&port->msgs_lock);
|
|
|
+ msg->private = private_data;
|
|
|
+ LISTP_ADD_TAIL(msg, &port->msgs, list);
|
|
|
+ unlock(&port->msgs_lock);
|
|
|
|
|
|
+ ret = send_ipc_message(&msg->msg, port);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
|
|
|
- debug("wait for response (seq = %lu)\n", msg->msg.seq);
|
|
|
- thread_sleep(NO_TIMEOUT);
|
|
|
+ if (seq)
|
|
|
+ *seq = msg->msg.seq;
|
|
|
+
|
|
|
+ debug("Start waiting for response (seq = %lu)\n", msg->msg.seq);
|
|
|
|
|
|
+ /* force thread which sends the message to wait for response;
|
|
|
+ * ignore unrelated interrupts but fail on actual errors */
|
|
|
+ do {
|
|
|
+ ret = thread_sleep(NO_TIMEOUT);
|
|
|
+ if (ret < 0 && ret != -EINTR && ret != -EAGAIN)
|
|
|
+ goto out;
|
|
|
+ } while (ret != 0);
|
|
|
+
|
|
|
+ debug("Finished waiting for response (seq = %lu, ret = %d)\n",
|
|
|
+ msg->msg.seq, msg->retval);
|
|
|
ret = msg->retval;
|
|
|
out:
|
|
|
- close_ipc_message_duplex(msg, port);
|
|
|
+ lock(&port->msgs_lock);
|
|
|
+ if (!LIST_EMPTY(msg, list))
|
|
|
+ LISTP_DEL_INIT(msg, &port->msgs, list);
|
|
|
+ unlock(&port->msgs_lock);
|
|
|
+
|
|
|
+ if (msg->thread) {
|
|
|
+ /* put reference to the thread acquired earlier */
|
|
|
+ put_thread(msg->thread);
|
|
|
+ msg->thread = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-struct shim_ipc_info * create_ipc_port (IDTYPE vmid, bool listen)
|
|
|
-{
|
|
|
- struct shim_ipc_info * proc = get_new_ipc_info(vmid, NULL, 0);
|
|
|
- if (!proc)
|
|
|
+/* must be called with cur_process.lock taken */
|
|
|
+struct shim_ipc_info* create_ipc_info_cur_process(void) {
|
|
|
+ struct shim_ipc_info* info = create_ipc_info(cur_process.vmid, NULL, 0);
|
|
|
+ if (!info)
|
|
|
return NULL;
|
|
|
|
|
|
char uri[PIPE_URI_SIZE];
|
|
|
- if (create_pipe(NULL, uri, PIPE_URI_SIZE, &proc->pal_handle,
|
|
|
- &proc->uri) < 0) {
|
|
|
- put_ipc_info(proc);
|
|
|
+ if (create_pipe(NULL, uri, PIPE_URI_SIZE, &info->pal_handle,
|
|
|
+ &info->uri) < 0) {
|
|
|
+ put_ipc_info(info);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- if (listen)
|
|
|
- add_ipc_port_by_id(0, proc->pal_handle, IPC_PORT_SERVER,
|
|
|
- NULL, &proc->port);
|
|
|
- return proc;
|
|
|
+ add_ipc_port_by_id(0, info->pal_handle, IPC_PORT_SERVER,
|
|
|
+ NULL, &info->port);
|
|
|
+
|
|
|
+ return info;
|
|
|
}
|
|
|
|
|
|
-int create_ipc_location (struct shim_ipc_info ** info)
|
|
|
-{
|
|
|
+int get_ipc_info_cur_process(struct shim_ipc_info** info) {
|
|
|
lock(&cur_process.lock);
|
|
|
- int ret = -EACCES;
|
|
|
|
|
|
- if (cur_process.self)
|
|
|
- goto success;
|
|
|
-
|
|
|
- cur_process.self = create_ipc_port(cur_process.vmid, true);
|
|
|
- if (!cur_process.self)
|
|
|
- goto out;
|
|
|
+ if (!cur_process.self) {
|
|
|
+ cur_process.self = create_ipc_info_cur_process();
|
|
|
+ if (!cur_process.self) {
|
|
|
+ unlock(&cur_process.lock);
|
|
|
+ return -EACCES;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-success:
|
|
|
get_ipc_info(cur_process.self);
|
|
|
*info = cur_process.self;
|
|
|
- ret = 0;
|
|
|
-out:
|
|
|
- unlock(&cur_process.lock);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-DEFINE_PROFILE_INTERVAL(ipc_finduri_send, ipc);
|
|
|
-DEFINE_PROFILE_INTERVAL(ipc_finduri_callback, ipc);
|
|
|
-
|
|
|
-int ipc_finduri_send (struct shim_ipc_port * port, IDTYPE dest,
|
|
|
- struct shim_ipc_info ** info)
|
|
|
-{
|
|
|
- BEGIN_PROFILE_INTERVAL();
|
|
|
- int ret;
|
|
|
- struct shim_ipc_msg_obj * msg = create_ipc_msg_duplex_on_stack(
|
|
|
- IPC_FINDURI, 0, dest);
|
|
|
-
|
|
|
- debug("ipc send to %u: IPC_FINDURI\n", dest);
|
|
|
|
|
|
- ret = do_ipc_duplex(msg, port, NULL, info);
|
|
|
- SAVE_PROFILE_INTERVAL(ipc_finduri_send);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-int ipc_finduri_callback (IPC_CALLBACK_ARGS)
|
|
|
-{
|
|
|
- BEGIN_PROFILE_INTERVAL();
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- debug("ipc callback from %u: IPC_FINDURI\n", msg->src);
|
|
|
-
|
|
|
- struct shim_ipc_info * info;
|
|
|
-
|
|
|
- if ((ret = create_ipc_location(&info)) < 0)
|
|
|
- goto out;
|
|
|
-
|
|
|
- ret = ipc_telluri_send(port, msg->src, info);
|
|
|
-out:
|
|
|
- SAVE_PROFILE_INTERVAL(ipc_finduri_callback);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-DEFINE_PROFILE_INTERVAL(ipc_telluri_send, ipc);
|
|
|
-DEFINE_PROFILE_INTERVAL(ipc_telluri_callback, ipc);
|
|
|
-
|
|
|
-int ipc_telluri_send (struct shim_ipc_port * port, IDTYPE dest,
|
|
|
- struct shim_ipc_info * info)
|
|
|
-{
|
|
|
- BEGIN_PROFILE_INTERVAL();
|
|
|
- int ret;
|
|
|
- struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
|
|
|
- IPC_TELLURI,
|
|
|
- info->uri.len, dest);
|
|
|
- struct shim_ipc_telluri * msgin =
|
|
|
- (struct shim_ipc_telluri *) &msg->msg;
|
|
|
-
|
|
|
- if (qstrempty(&info->uri)) {
|
|
|
- ret = -ENOENT;
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(msgin->uri, qstrgetstr(&info->uri), info->uri.len + 1);
|
|
|
-
|
|
|
- debug("ipc send to %u: IPC_TELLURI(%s)\n", dest,
|
|
|
- qstrgetstr(&info->uri));
|
|
|
-
|
|
|
- ret = send_ipc_message(msg, port);
|
|
|
- SAVE_PROFILE_INTERVAL(ipc_telluri_send);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-int ipc_telluri_callback (IPC_CALLBACK_ARGS)
|
|
|
-{
|
|
|
- BEGIN_PROFILE_INTERVAL();
|
|
|
- int ret = 0;
|
|
|
- struct shim_ipc_telluri * msgin =
|
|
|
- (struct shim_ipc_telluri *) &msg->msg;
|
|
|
-
|
|
|
- debug("ipc callback from %u: IPC_TELLURI(%s)\n", msg->src, msgin->uri);
|
|
|
-
|
|
|
- struct shim_ipc_info * info =
|
|
|
- lookup_and_alloc_client(msg->src, msgin->uri);
|
|
|
-
|
|
|
- struct shim_ipc_msg_obj * obj = find_ipc_msg_duplex(port, msg->seq);
|
|
|
-
|
|
|
- if (obj) {
|
|
|
- if (info) {
|
|
|
- if (obj->private)
|
|
|
- *(struct shim_ipc_info **) obj->private = info;
|
|
|
- obj->retval = 0;
|
|
|
- } else {
|
|
|
- obj->retval = -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- if (obj->thread)
|
|
|
- thread_wakeup(obj->thread);
|
|
|
- }
|
|
|
-
|
|
|
- SAVE_PROFILE_INTERVAL(ipc_telluri_callback);
|
|
|
- return ret;
|
|
|
+ unlock(&cur_process.lock);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
DEFINE_PROFILE_INTERVAL(ipc_checkpoint_send, ipc);
|
|
|
DEFINE_PROFILE_INTERVAL(ipc_checkpoint_callback, ipc);
|
|
|
|
|
|
-int ipc_checkpoint_send (const char * cpdir, IDTYPE cpsession)
|
|
|
-{
|
|
|
+/* Graphene's checkpoint() syscall broadcasts a msg to all processes
|
|
|
+ * asking to checkpoint their state and save in process-unique file in
|
|
|
+ * directory cpdir under session cpsession. */
|
|
|
+int ipc_checkpoint_send(const char* cpdir, IDTYPE cpsession) {
|
|
|
BEGIN_PROFILE_INTERVAL();
|
|
|
int ret;
|
|
|
- int len = strlen(cpdir);
|
|
|
+ size_t len = strlen(cpdir);
|
|
|
|
|
|
- struct shim_ipc_msg * msg = create_ipc_msg_on_stack(
|
|
|
- IPC_CHECKPOINT,
|
|
|
- sizeof(struct shim_ipc_checkpoint)
|
|
|
- + len, 0);
|
|
|
- struct shim_ipc_checkpoint * msgin =
|
|
|
- (struct shim_ipc_checkpoint *) &msg->msg;
|
|
|
+ size_t total_msg_size = get_ipc_msg_size(sizeof(struct shim_ipc_checkpoint) + len);
|
|
|
+ struct shim_ipc_msg* msg = __alloca(total_msg_size);
|
|
|
+ init_ipc_msg(msg, IPC_CHECKPOINT, total_msg_size, 0);
|
|
|
|
|
|
+ struct shim_ipc_checkpoint* msgin = (struct shim_ipc_checkpoint *) &msg->msg;
|
|
|
msgin->cpsession = cpsession;
|
|
|
memcpy(&msgin->cpdir, cpdir, len + 1);
|
|
|
|
|
|
- debug("ipc broadcast to all: IPC_CHECKPOINT(%u, %s)\n",
|
|
|
- cpsession, cpdir);
|
|
|
+ debug("IPC broadcast to all: IPC_CHECKPOINT(%u, %s)\n", cpsession, cpdir);
|
|
|
|
|
|
+ /* broadcast to all including myself (so I can also checkpoint) */
|
|
|
ret = broadcast_ipc(msg, IPC_PORT_DIRCLD|IPC_PORT_DIRPRT, /*exclude_port*/ NULL);
|
|
|
SAVE_PROFILE_INTERVAL(ipc_checkpoint_send);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-int ipc_checkpoint_callback (IPC_CALLBACK_ARGS)
|
|
|
-{
|
|
|
+/* This process is asked to create a checkpoint, so it:
|
|
|
+ * - sends a Graphene-specific SIGCP signal to all its threads (for
|
|
|
+ * all to stop and join the checkpoint for consistent state),
|
|
|
+ * - broadcasts checkpoint msg further to other processes. */
|
|
|
+int ipc_checkpoint_callback(struct shim_ipc_msg* msg, struct shim_ipc_port* port) {
|
|
|
BEGIN_PROFILE_INTERVAL();
|
|
|
int ret = 0;
|
|
|
- struct shim_ipc_checkpoint * msgin =
|
|
|
- (struct shim_ipc_checkpoint *) msg->msg;
|
|
|
+ struct shim_ipc_checkpoint* msgin = (struct shim_ipc_checkpoint *) msg->msg;
|
|
|
|
|
|
- debug("ipc callback form %u: IPC_CHECKPOINT(%u, %s)\n", msg->src,
|
|
|
- msgin->cpsession, msgin->cpdir);
|
|
|
+ debug("IPC callback from %u: IPC_CHECKPOINT(%u, %s)\n",
|
|
|
+ msg->src, msgin->cpsession, msgin->cpdir);
|
|
|
|
|
|
ret = create_checkpoint(msgin->cpdir, &msgin->cpsession);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
|
|
|
kill_all_threads(NULL, msgin->cpsession, SIGCP);
|
|
|
- broadcast_ipc(msg, IPC_PORT_DIRPRT|IPC_PORT_DIRCLD, port);
|
|
|
+ broadcast_ipc(msg, IPC_PORT_DIRCLD|IPC_PORT_DIRPRT, port);
|
|
|
out:
|
|
|
SAVE_PROFILE_INTERVAL(ipc_checkpoint_callback);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-BEGIN_CP_FUNC(ipc_info)
|
|
|
-{
|
|
|
+BEGIN_CP_FUNC(ipc_info) {
|
|
|
assert(size == sizeof(struct shim_ipc_info));
|
|
|
|
|
|
- struct shim_ipc_info * port = (struct shim_ipc_info *) obj;
|
|
|
- struct shim_ipc_info * new_port = NULL;
|
|
|
+ struct shim_ipc_info* info = (struct shim_ipc_info *) obj;
|
|
|
+ struct shim_ipc_info* new_info = NULL;
|
|
|
|
|
|
ptr_t off = GET_FROM_CP_MAP(obj);
|
|
|
|
|
|
if (!off) {
|
|
|
off = ADD_CP_OFFSET(sizeof(struct shim_ipc_info));
|
|
|
+ ADD_TO_CP_MAP(obj, off);
|
|
|
|
|
|
- new_port = (struct shim_ipc_info *) (base + off);
|
|
|
- memcpy(new_port, port, sizeof(struct shim_ipc_info));
|
|
|
- REF_SET(new_port->ref_count, 0);
|
|
|
-
|
|
|
- DO_CP_IN_MEMBER(qstr, new_port, uri);
|
|
|
-
|
|
|
- if (port->pal_handle &&
|
|
|
- port->pal_handle != IPC_FORCE_RECONNECT) {
|
|
|
- struct shim_palhdl_entry * entry;
|
|
|
- DO_CP(palhdl, port->pal_handle, &entry);
|
|
|
- entry->uri = &new_port->uri;
|
|
|
- entry->phandle = &new_port->pal_handle;
|
|
|
+ new_info = (struct shim_ipc_info *) (base + off);
|
|
|
+ memcpy(new_info, info, sizeof(struct shim_ipc_info));
|
|
|
+ REF_SET(new_info->ref_count, 0);
|
|
|
+
|
|
|
+ /* call qstr-specific checkpointing function for new_info->uri */
|
|
|
+ DO_CP_IN_MEMBER(qstr, new_info, uri);
|
|
|
+
|
|
|
+ if (info->pal_handle && info->pal_handle != IPC_FORCE_RECONNECT) {
|
|
|
+ struct shim_palhdl_entry* entry;
|
|
|
+ /* call palhdl-specific checkpointing function to checkpoint
|
|
|
+ * info->pal_handle and return created object in entry */
|
|
|
+ DO_CP(palhdl, info->pal_handle, &entry);
|
|
|
+ /* info's PAL handle will be re-opened with new URI during
|
|
|
+ * palhdl restore (see checkpoint.c) */
|
|
|
+ entry->uri = &new_info->uri;
|
|
|
+ entry->phandle = &new_info->pal_handle;
|
|
|
}
|
|
|
} else {
|
|
|
- new_port = (struct shim_ipc_info *) (base + off);
|
|
|
+ /* already checkpointed */
|
|
|
+ new_info = (struct shim_ipc_info *) (base + off);
|
|
|
}
|
|
|
|
|
|
- if (new_port && objp)
|
|
|
- *objp = (void *) new_port;
|
|
|
+ if (new_info && objp)
|
|
|
+ *objp = (void *) new_info;
|
|
|
}
|
|
|
END_CP_FUNC_NO_RS(ipc_info)
|
|
|
|
|
|
-BEGIN_CP_FUNC(process)
|
|
|
-{
|
|
|
+BEGIN_CP_FUNC(process) {
|
|
|
assert(size == sizeof(struct shim_process));
|
|
|
|
|
|
- struct shim_process * proc = (struct shim_process *) obj;
|
|
|
- struct shim_process * new_proc = NULL;
|
|
|
+ struct shim_process* process = (struct shim_process *) obj;
|
|
|
+ struct shim_process* new_process = NULL;
|
|
|
|
|
|
ptr_t off = GET_FROM_CP_MAP(obj);
|
|
|
|
|
@@ -681,57 +480,55 @@ BEGIN_CP_FUNC(process)
|
|
|
off = ADD_CP_OFFSET(sizeof(struct shim_process));
|
|
|
ADD_TO_CP_MAP(obj, off);
|
|
|
|
|
|
- new_proc = (struct shim_process *) (base + off);
|
|
|
- memcpy(new_proc, proc, sizeof(struct shim_process));
|
|
|
+ new_process = (struct shim_process *) (base + off);
|
|
|
+ memcpy(new_process, process, sizeof(struct shim_process));
|
|
|
|
|
|
- if (proc->self)
|
|
|
- DO_CP_MEMBER(ipc_info, proc, new_proc, self);
|
|
|
-
|
|
|
- if (proc->parent)
|
|
|
- DO_CP_MEMBER(ipc_info, proc, new_proc, parent);
|
|
|
-
|
|
|
- for (int i = 0 ; i < TOTAL_NS ; i++)
|
|
|
- if (proc->ns[i])
|
|
|
- DO_CP_MEMBER(ipc_info, proc, new_proc, ns[i]);
|
|
|
+ /* call ipc_info-specific checkpointing functions
|
|
|
+ * for new_process's self, parent, and ns infos */
|
|
|
+ if (process->self)
|
|
|
+ DO_CP_MEMBER(ipc_info, process, new_process, self);
|
|
|
+ if (process->parent)
|
|
|
+ DO_CP_MEMBER(ipc_info, process, new_process, parent);
|
|
|
+ for (int i = 0; i < TOTAL_NS; i++)
|
|
|
+ if (process->ns[i])
|
|
|
+ DO_CP_MEMBER(ipc_info, process, new_process, ns[i]);
|
|
|
|
|
|
ADD_CP_FUNC_ENTRY(off);
|
|
|
} else {
|
|
|
- new_proc = (struct shim_process *) (base + off);
|
|
|
+ /* already checkpointed */
|
|
|
+ new_process = (struct shim_process *) (base + off);
|
|
|
}
|
|
|
|
|
|
if (objp)
|
|
|
- *objp = (void *) new_proc;
|
|
|
+ *objp = (void *) new_process;
|
|
|
}
|
|
|
END_CP_FUNC(process)
|
|
|
|
|
|
-BEGIN_RS_FUNC(process)
|
|
|
-{
|
|
|
+BEGIN_RS_FUNC(process) {
|
|
|
__UNUSED(offset);
|
|
|
- struct shim_process * proc = (void *) (base + GET_CP_FUNC_ENTRY());
|
|
|
+ struct shim_process* process = (void *) (base + GET_CP_FUNC_ENTRY());
|
|
|
|
|
|
- CP_REBASE(proc->self);
|
|
|
- CP_REBASE(proc->parent);
|
|
|
- CP_REBASE(proc->ns);
|
|
|
+ CP_REBASE(process->self);
|
|
|
+ CP_REBASE(process->parent);
|
|
|
+ CP_REBASE(process->ns);
|
|
|
|
|
|
- if (proc->self) {
|
|
|
- proc->self->vmid = cur_process.vmid;
|
|
|
- get_ipc_info(proc->self);
|
|
|
+ if (process->self) {
|
|
|
+ process->self->vmid = cur_process.vmid;
|
|
|
+ get_ipc_info(process->self);
|
|
|
}
|
|
|
-
|
|
|
- if (proc->parent)
|
|
|
- get_ipc_info(proc->parent);
|
|
|
-
|
|
|
- for (int i = 0 ; i < TOTAL_NS ; i++)
|
|
|
- if (proc->ns[i])
|
|
|
- get_ipc_info(proc->ns[i]);
|
|
|
-
|
|
|
- proc->vmid = cur_process.vmid;
|
|
|
- memcpy(&cur_process, proc, sizeof(struct shim_process));
|
|
|
+ if (process->parent)
|
|
|
+ get_ipc_info(process->parent);
|
|
|
+ for (int i = 0; i < TOTAL_NS; i++)
|
|
|
+ if (process->ns[i])
|
|
|
+ get_ipc_info(process->ns[i]);
|
|
|
+
|
|
|
+ process->vmid = cur_process.vmid;
|
|
|
+ memcpy(&cur_process, process, sizeof(struct shim_process));
|
|
|
create_lock(&cur_process.lock);
|
|
|
|
|
|
- DEBUG_RS("vmid=%u,uri=%s,parent=%u(%s)", proc->vmid,
|
|
|
- proc->self ? qstrgetstr(&proc->self->uri) : "",
|
|
|
- proc->parent ? proc->parent->vmid : 0,
|
|
|
- proc->parent ? qstrgetstr(&proc->parent->uri) : "");
|
|
|
+ DEBUG_RS("vmid=%u,uri=%s,parent=%u(%s)", process->vmid,
|
|
|
+ process->self ? qstrgetstr(&process->self->uri) : "",
|
|
|
+ process->parent ? process->parent->vmid : 0,
|
|
|
+ process->parent ? qstrgetstr(&process->parent->uri) : "");
|
|
|
}
|
|
|
END_RS_FUNC(process)
|