-
Notifications
You must be signed in to change notification settings - Fork 201
/
libos_ipc.h
314 lines (270 loc) · 10 KB
/
libos_ipc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/* Copyright (C) 2014 Stony Brook University
* Copyright (C) 2021 Intel Corporation
* Borys Popławski <borysp@invisiblethingslab.com>
*/
#pragma once
#include <stdint.h>
#include "avl_tree.h"
#include "libos_defs.h"
#include "libos_fs_lock.h"
#include "libos_handle.h"
#include "libos_internal.h"
#include "libos_thread.h"
#include "libos_types.h"
#include "pal.h"
enum {
IPC_MSG_RESP = 0,
IPC_MSG_GET_NEW_VMID, /*!< Request new VMID. */
IPC_MSG_CHILDEXIT, /*!< Child exit/death information. */
IPC_MSG_ALLOC_ID_RANGE, /*!< Request new IDs range. */
IPC_MSG_RELEASE_ID_RANGE, /*!< Release IDs range. */
IPC_MSG_CHANGE_ID_OWNER, /*!< Change the owner of an ID. */
IPC_MSG_GET_ID_OWNER, /*!< Find the owner of an ID. */
IPC_MSG_PID_KILL,
IPC_MSG_PID_GETMETA,
IPC_MSG_SYNC_REQUEST_UPGRADE,
IPC_MSG_SYNC_REQUEST_DOWNGRADE,
IPC_MSG_SYNC_REQUEST_CLOSE,
IPC_MSG_SYNC_CONFIRM_UPGRADE,
IPC_MSG_SYNC_CONFIRM_DOWNGRADE,
IPC_MSG_SYNC_CONFIRM_CLOSE,
IPC_MSG_FILE_LOCK_SET,
IPC_MSG_FILE_LOCK_GET,
IPC_MSG_FILE_LOCK_CLEAR_PID,
IPC_MSG_CODE_BOUND,
};
enum kill_type { KILL_THREAD, KILL_PROCESS, KILL_PGROUP, KILL_ALL };
enum pid_meta_code { PID_META_CRED, PID_META_EXEC, PID_META_CWD, PID_META_ROOT };
#define STARTING_VMID 1
struct libos_ipc_ids {
IDTYPE self_vmid;
IDTYPE parent_vmid;
IDTYPE leader_vmid;
};
extern struct libos_ipc_ids g_process_ipc_ids;
int init_ipc(void);
int init_ipc_ids(void);
/*!
* \brief Initialize the IPC worker thread.
*/
int init_ipc_worker(void);
/*!
* \brief Terminate the IPC worker thread.
*/
void terminate_ipc_worker(void);
/*!
* \brief Establish a one-way IPC connection to another process.
*
* \param dest VMID of the destination process to connect to.
*/
int connect_to_process(IDTYPE dest);
/*!
* \brief Remove an outgoing IPC connection.
*
* \param dest VMID of the destination process.
*
* If there is no outgoing connection to \p dest, does nothing. If any thread waits for a response
* to a message sent to \p dest, it is woken up and notified about the disconnect.
*/
void remove_outgoing_ipc_connection(IDTYPE dest);
struct ipc_msg_header {
size_t size;
uint64_t seq;
unsigned char code;
} __attribute__((packed));
struct libos_ipc_msg {
struct ipc_msg_header header;
char data[];
} __attribute__((packed));
static inline size_t get_ipc_msg_size(size_t payload) {
return sizeof(struct libos_ipc_msg) + payload;
}
void init_ipc_msg(struct libos_ipc_msg* msg, unsigned char code, size_t size);
void init_ipc_response(struct libos_ipc_msg* msg, uint64_t seq, size_t size);
/*!
* \brief Send an IPC message.
*
* \param dest VMID of the destination process.
* \param msg Message to send.
*/
int ipc_send_message(IDTYPE dest, struct libos_ipc_msg* msg);
/*!
* \brief Send an IPC message and wait for a response.
*
* \param dest VMID of the destination process.
* \param msg Message to send.
* \param[out] resp Upon successful return contains a pointer to the response.
*
* Send an IPC message to the \p dest process and wait for a response. An unique number is assigned
* before sending the message and this thread will wait for a response IPC message, which contains
* the same sequence number. If this function succeeds, \p resp will contain pointer to the response
* data, which should be freed using `free` function. If \p resp is NULL, the response will be
* discarded, but still awaited for.
*/
int ipc_send_msg_and_get_response(IDTYPE dest, struct libos_ipc_msg* msg, void** resp);
/*!
* \brief Broadcast an IPC message.
*
* \param msg Message to send.
* \param exclude_vmid VMID of process to be excluded.
*
* Send an IPC message \p msg to all known (connected) processes except for \p exclude_vmid.
*/
int ipc_broadcast(struct libos_ipc_msg* msg, IDTYPE exclude_vmid);
/*!
* \brief Handle a response to a previously sent message.
*
* \param src ID of sender.
* \param data Body of the response.
* \param seq Sequence number of the original message.
*
* Searches for a thread waiting for a response to a message previously sent to \p src with
* the sequence number \p seq. If such thread is found, it is woken up and \p data is passed to it
* (returned in `resp` argument of #ipc_send_msg_and_get_response).
* This function always takes the ownership of \p data, the caller of this function should never
* free it!
*/
int ipc_response_callback(IDTYPE src, void* data, uint64_t seq);
/*!
* \brief Get a new VMID.
*
* \param[out] vmid Contains the new VMID.
*/
int ipc_get_new_vmid(IDTYPE* vmid);
int ipc_get_new_vmid_callback(IDTYPE src, void* data, uint64_t seq);
struct libos_ipc_cld_exit {
IDTYPE ppid, pid;
IDTYPE uid;
unsigned int exitcode;
unsigned int term_signal;
} __attribute__((packed));
int ipc_cld_exit_send(unsigned int exitcode, unsigned int term_signal);
int ipc_cld_exit_callback(IDTYPE src, void* data, uint64_t seq);
void ipc_child_disconnect_callback(IDTYPE vmid);
#define MAX_RANGE_SIZE 0x20
/*!
* \brief Request a new ID range from the IPC leader.
*
* \param[out] out_start Start of the new ID range.
* \param[out] out_end End of the new ID range.
*
* Sender becomes the owner of the returned ID range.
*/
int ipc_alloc_id_range(IDTYPE* out_start, IDTYPE* out_end);
int ipc_alloc_id_range_callback(IDTYPE src, void* data, uint64_t seq);
/*!
* \brief Release a previously allocated ID range.
*
* \param start Start of the ID range.
* \param end End of the ID range.
*
* \p start and \p end must denote a full range (for details check #ipc_change_id_owner).
*/
int ipc_release_id_range(IDTYPE start, IDTYPE end);
int ipc_release_id_range_callback(IDTYPE src, void* data, uint64_t seq);
/*!
* \brief Change owner of an ID.
*
* \param id ID to change the ownership of.
* \param new_owner New owner of \p id.
*
* This operation effectively splits an existing ID range. Each (if any) of the range parts must be
* later on freed separately. Example:
* - process1 owns range 1..10
* - `ipc_change_id_owner(id=5, new_owner=process2)`
* - now process1 owns ranges 1..4 and 6..10, process2 owns 5..5
* - each of these ranges must be freed separately, e.g.
* `ipc_release_id_range(5, 5); ipc_release_id_range(1, 4); ipc_release_id_range(6, 10);`
* is ok to do, but `ipc_release_id_range(5, 10);` is not.
* Theoretically speaking any process can free any range (as long as each range is freed only once),
* but in the current implementation a process frees only ranges it owns.
*/
int ipc_change_id_owner(IDTYPE id, IDTYPE new_owner);
int ipc_change_id_owner_callback(IDTYPE src, void* data, uint64_t seq);
/*!
* \brief Find the owner of a given id.
*
* \param id ID to find the owner of.
* \param[out] out_owner Contains VMID of the process owning \p id.
*
* If nobody owns \p id then `0` is returned in \p out_owner.
*/
int ipc_get_id_owner(IDTYPE id, IDTYPE* out_owner);
int ipc_get_id_owner_callback(IDTYPE src, void* data, uint64_t seq);
struct libos_ipc_pid_kill {
IDTYPE sender;
IDTYPE pid;
IDTYPE id;
int signum;
enum kill_type type;
};
int ipc_kill_process(IDTYPE sender, IDTYPE target, int sig);
int ipc_kill_thread(IDTYPE sender, IDTYPE dest_pid, IDTYPE target, int sig);
int ipc_kill_pgroup(IDTYPE sender, IDTYPE pgid, int sig);
int ipc_kill_all(IDTYPE sender, int sig);
int ipc_pid_kill_callback(IDTYPE src, void* data, uint64_t seq);
/* PID_GETMETA: get metadata of certain pid */
struct libos_ipc_pid_getmeta {
IDTYPE pid;
enum pid_meta_code code;
} __attribute__((packed));
/* PID_RETMETA: return metadata of certain pid */
struct libos_ipc_pid_retmeta {
size_t datasize;
int ret_val;
char data[];
} __attribute__((packed));
int ipc_pid_getmeta(IDTYPE pid, enum pid_meta_code code, struct libos_ipc_pid_retmeta** data);
int ipc_pid_getmeta_callback(IDTYPE src, void* data, uint64_t seq);
/* SYNC_REQUEST_*, SYNC_CONFIRM_ */
struct libos_ipc_sync {
uint64_t id;
size_t data_size;
int state;
unsigned char data[];
};
int ipc_sync_client_send(int code, uint64_t id, int state, size_t data_size, void* data);
int ipc_sync_server_send(IDTYPE dest, int code, uint64_t id, int state, size_t data_size,
void* data);
int ipc_sync_request_upgrade_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_sync_request_downgrade_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_sync_request_close_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_sync_confirm_upgrade_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_sync_confirm_downgrade_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_sync_confirm_close_callback(IDTYPE src, void* data, unsigned long seq);
/*
* FILE_LOCK_SET: `struct libos_ipc_file_lock` -> `int`
* FILE_LOCK_GET: `struct libos_ipc_file_lock` -> `struct libos_ipc_file_lock_resp`
* FILE_LOCK_CLEAR_PID: `IDTYPE` -> `int`
*/
struct libos_ipc_file_lock {
/* see `struct libos_file_lock` in `libos_fs_lock.h` */
enum libos_file_lock_family family;
int type;
uint64_t start;
uint64_t end;
IDTYPE pid;
uint64_t handle_id;
bool wait;
char path[]; /* null-terminated */
};
struct libos_ipc_file_lock_resp {
int result;
/* see `struct libos_file_lock` in `libos_fs_lock.h` */
enum libos_file_lock_family family;
int type;
uint64_t start;
uint64_t end;
IDTYPE pid;
uint64_t handle_id;
};
struct libos_file_lock;
int ipc_file_lock_set(const char* path, struct libos_file_lock* file_lock, bool wait);
int ipc_file_lock_set_send_response(IDTYPE vmid, unsigned long seq, int result);
int ipc_file_lock_get(const char* path, struct libos_file_lock* file_lock,
struct libos_file_lock* out_file_lock);
int ipc_file_lock_clear_pid(IDTYPE pid);
int ipc_file_lock_set_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_file_lock_get_callback(IDTYPE src, void* data, unsigned long seq);
int ipc_file_lock_clear_pid_callback(IDTYPE src, void* data, unsigned long seq);