MOS Source Code
Loading...
Searching...
No Matches
ipc.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#define pr_fmt(fmt) "ipc: " fmt
4
5#include "mos/ipc/ipc.hpp"
6
11#include "mos/ipc/pipe.hpp"
14#include "mos/syslog/printk.hpp"
16#include "mos/tasks/signal.hpp"
17#include "mos/tasks/wait.hpp"
18
19#include <mos/allocator.hpp>
23#include <mos/mos_global.h>
24#include <mos_stdlib.hpp>
25#include <mos_string.hpp>
26
27#define IPC_SERVER_MAGIC MOS_FOURCC('I', 'P', 'C', 'S')
28
53
83
87
89{
91 spinlock_acquire(&server->lock);
92 // remove the server from the list
93 list_remove(server);
94
95 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) server->key());
96 if (waitlist)
97 {
98 // the waitlist should have been closed when the server was created
99 // and no one should be waiting on it
100 spinlock_acquire(&waitlist->lock);
101 MOS_ASSERT(waitlist->closed);
102 MOS_ASSERT(list_is_empty(&waitlist->list));
103 spinlock_release(&waitlist->lock);
104 waitlist_init(waitlist); // reuse the waitlist
105 }
107
108 // with the server lock held, we reject all pending connections
109 list_foreach(IPCDescriptor, ipc, server->pending)
110 {
111 ipc->buffer_size_npages = 0; // mark the connection as closed
112 // wake up the client
113 waitlist_close(&ipc->client_waitlist);
114 waitlist_wake_all(&ipc->client_waitlist);
115 }
116
117 server->pending_max = 0;
118 waitlist_close(&server->server_waitlist); // close the server's waitlist
119 int n = waitlist_wake_all(&server->server_waitlist); // wake up the server, if it is waiting
120
121 if (n)
122 {
123 spinlock_release(&server->lock);
124 // let the accept() syscall free the server
125 }
126 else
127 {
128 // now we can free the server
129 delete server;
130 }
131}
132
133size_t ipc_client_read(IPCDescriptor *ipc, void *buf, size_t size)
134{
135 return pipe_read(ipc->client_read_pipe, buf, size);
136}
137
138size_t ipc_client_write(IPCDescriptor *ipc, const void *buf, size_t size)
139{
140 return pipe_write(ipc->client_write_pipe, buf, size);
141}
142
143size_t ipc_server_read(IPCDescriptor *ipc, void *buf, size_t size)
144{
145 return pipe_read(ipc->server_read_pipe, buf, size);
146}
147
148size_t ipc_server_write(IPCDescriptor *ipc, const void *buf, size_t size)
149{
150 return pipe_write(ipc->server_write_pipe, buf, size);
151}
152
154{
155 bool r_fullyclosed = pipe_close_one_end(ipc->client_read_pipe);
156 bool w_fullyclosed = pipe_close_one_end(ipc->client_write_pipe);
157 MOS_ASSERT(r_fullyclosed == w_fullyclosed); // both ends should have the same return value
158
159 if (r_fullyclosed || w_fullyclosed)
160 {
161 // now we can free the ipc
162 delete ipc;
163 return;
164 }
165}
166
168{
169 bool r_fullyclosed = pipe_close_one_end(ipc->server_read_pipe);
170 bool w_fullyclosed = pipe_close_one_end(ipc->server_write_pipe);
171 MOS_ASSERT(r_fullyclosed == w_fullyclosed); // both ends should have the same return value
172
173 if (r_fullyclosed || w_fullyclosed)
174 {
175 // now we can free the ipc
176 delete ipc;
177 return;
178 }
179}
180
185
186static inode_t *ipc_sysfs_create_ino(IPCServer *ipc_server);
187
189{
190 pr_dinfo(ipc, "creating ipc server '%s' with max_pending=%zu", name.data(), max_pending);
191 const auto guard = ipc_lock.lock();
193 {
194 if (server->name == name)
195 {
196 pr_dwarn(ipc, "ipc server '%s' already exists", name.data());
197 return -EEXIST;
198 }
199 }
200
201 // we don't need to acquire the lock here because the server is not yet announced
202 const auto server = mos::create<IPCServer>(name, max_pending);
203
204 // now announce the server
206 ipc_sysfs_create_ino(server);
207
208 // check and see if there is a waitlist for this name
209 // if so, wake up all waiters
210 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) server->key());
211 if (waitlist)
212 {
213 pr_dinfo2(ipc, "found waitlist for ipc server '%s'", name.data());
214 // wake up all waiters
215 waitlist_close(waitlist);
216 const size_t n = waitlist_wake_all(waitlist);
217 if (n)
218 pr_dinfo2(ipc, "woken up %zu waiters for ipc server '%s'", n, name.data());
219 }
220
221 return server;
222}
223
225{
226 const auto guard = ipc_lock.lock();
228 {
229 if (server->name == name)
230 return server;
231 }
232
233 return -ENOENT;
234}
235
237{
238 pr_dinfo(ipc, "accepting connection on ipc server '%s'...", ipc_server->name.c_str());
239
240retry_accept:
241 spinlock_acquire(&ipc_server->lock);
242
243 // check if the server is closed
244 if (ipc_server->pending_max == 0)
245 {
246 // now we can free the server
247 pr_dinfo2(ipc, "ipc server '%s' is closed, aborting accept()", ipc_server->name.c_str());
248 delete ipc_server;
249 return -ECONNABORTED;
250 }
251
252 if (ipc_server->pending_n == 0)
253 {
254 // no pending connections, wait for a client to connect
255 pr_dinfo2(ipc, "no pending connections, waiting for a client to connect...");
257 spinlock_release(&ipc_server->lock);
259
260 if (signal_has_pending())
261 {
262 pr_dinfo2(ipc, "woken up by a signal, aborting accept()");
264 return -EINTR;
265 }
266
267 goto retry_accept; // the server has woken us up, try again
268 }
269
270 // get the first pending connection
271 MOS_ASSERT(!list_is_empty(&ipc_server->pending));
273 list_remove(ipc);
274 ipc_server->pending_n--;
275 spinlock_release(&ipc_server->lock);
276
278 pr_dinfo(ipc, "accepted a connection on ipc server '%s' with buffer_size_npages=%zu", ipc_server->name.c_str(), ipc->buffer_size_npages);
279
280 // setup the pipes
281 auto readPipe = pipe_create(ipc->buffer_size_npages);
282 if (readPipe.isErr())
283 {
284 pr_dwarn(ipc, "failed to create read pipe");
285 // TODO: cleanup
286 return readPipe.getErr();
287 }
288 ipc->server_read_pipe = readPipe.get();
289
290 auto writePipe = pipe_create(ipc->buffer_size_npages);
291 if (writePipe.isErr())
292 {
293 pr_dwarn(ipc, "failed to create write pipe");
294 // TODO: cleanup
295 return writePipe.getErr();
296 }
297 ipc->server_write_pipe = writePipe.get();
298
299 // wake up the client
301
302 return ipc;
303}
304
306{
307 if (buffer_size == 0)
308 return -EINVAL; // buffer size must be > 0
309
310 pr_dinfo(ipc, "connecting to ipc server '%s' with buffer_size=%zu", name.data(), buffer_size);
311 buffer_size = ALIGN_UP_TO_PAGE(buffer_size);
312
313check_server:
314 // check if the server exists
316 IPCServer *ipc_server = NULL;
318 {
319 if (server->name == name)
320 {
321 ipc_server = server;
322 // we are holding the ipc_servers_lock, so that the server won't deannounce itself
323 // while we are checking the server list, thus the server won't be freed
324 spinlock_acquire(&ipc_server->lock);
325 pr_dinfo2(ipc, "found ipc server '%s'", ipc_server->name.c_str());
326 break;
327 }
328 }
329
330 // now we have a server, we can create the connection
331 const auto descriptor = mos::create<IPCDescriptor>(name, buffer_size);
332
333 if (!ipc_server)
334 {
335 // no server found, wait for it to be created
336 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) name.data());
337 if (!waitlist)
338 {
339 waitlist = mos::create<waitlist_t>();
340 waitlist_init(waitlist);
341 // the key must be in kernel memory
342 const auto old = (waitlist_t *) hashmap_put(&name_waitlist, (ptr_t) descriptor->server_name.c_str(), waitlist);
343 if (old)
344 {
345 // someone else has created the waitlist, but now we have replaced it
346 // so we have to append the old waitlist to the new one
347 list_foreach(Thread, thread, old->list)
348 {
349 MOS_ASSERT(waitlist_append(waitlist));
350 }
351 }
352 pr_dinfo2(ipc, "created waitlist for ipc server '%s'", name.data());
353 }
354
355 pr_dinfo2(ipc, "no ipc server '%s' found, waiting for it to be created...", name.data());
356 MOS_ASSERT(waitlist_append(waitlist));
359
360 if (signal_has_pending())
361 {
362 pr_dinfo2(ipc, "woken up by a signal, aborting connect()");
363 delete descriptor;
364 return -EINTR;
365 }
366
367 // now check if the server exists again
368 goto check_server;
369 }
371
372 // add the connection to the pending list
373 if (ipc_server->pending_n >= ipc_server->pending_max)
374 {
375 pr_dwarn(ipc, "ipc server '%s' has reached its max pending connections, rejecting connection", ipc_server->name.c_str());
376 spinlock_release(&ipc_server->lock);
377 delete descriptor;
378 return -ECONNREFUSED;
379 }
380
381 list_node_append(&ipc_server->pending, list_node(descriptor)); // add to pending list
382 ipc_server->pending_n++;
383
384 // now wait for the server to accept the connection
385 MOS_ASSERT(waitlist_append(&descriptor->client_waitlist));
386 waitlist_wake(&ipc_server->server_waitlist, 1);
387 spinlock_release(&ipc_server->lock); // now the server can do whatever it wants
388
390 // the server has woken us up and has accepted the connection, or it is closed
391 pr_dinfo2(ipc, "ipc server '%s' woke us up", ipc_server->name.c_str());
392
393 // check if the server has closed
394 if (descriptor->buffer_size_npages == 0)
395 {
396 // the server is closed, don't touch ipc_server pointer anymore
397 pr_dwarn(ipc, "ipc server '%s' has closed", ipc_server->name.c_str());
398 ipc_server = NULL;
399 delete descriptor;
400 return -ECONNREFUSED;
401 }
402
403 // now we have a connection, both the read and write pipes are ready, the io object is also ready
404 // we just need to return the io object
405 pr_dinfo2(ipc, "ipc server '%s' has accepted the connection", ipc_server->name.c_str());
406 return descriptor;
407}
408
409// ! sysfs support
410
412{
413 sysfs_printf(f, "%-20s\t%s\n", "Server Name", "Max Pending Connections");
415 {
416 sysfs_printf(f, "%-20s\t%zu\n", ipc->name.c_str(), ipc->pending_max);
417 }
418
419 return true;
420}
421
423{
424 ipc_server->sysfs_ino = sysfs_create_inode(FILE_TYPE_CHAR_DEVICE, ipc_server);
425 ipc_server->sysfs_ino->perm = PERM_OWNER & (PERM_READ | PERM_WRITE);
426 ipc_server->sysfs_ino->file_ops = &ipc_sysfs_file_ops;
427 return ipc_server->sysfs_ino;
428}
429
431{
432 MOS_UNUSED(item);
433 MOS_UNUSED(d);
434
436 {
437 MOS_ASSERT(ipc_server->sysfs_ino);
438 add_record(state, ipc_server->sysfs_ino->ino, ipc_server->name, ipc_server->sysfs_ino->type);
439 }
440}
441
442static bool ipc_sysfs_lookup_ipc(inode_t *parent_dir, dentry_t *dentry)
443{
444 MOS_UNUSED(parent_dir);
445
446 const auto name = dentry->name;
447 IPCServer *ipc_server = NULL;
449 {
450 if (ipc->name == name)
451 {
452 ipc_server = ipc;
453 break;
454 }
455 }
456
457 if (ipc_server == NULL)
458 return false;
459
460 dentry_attach(dentry, ipc_server->sysfs_ino);
461 return dentry->inode != NULL;
462}
463
464static bool ipc_sysfs_create_server(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
465{
466 MOS_UNUSED(dir);
467 MOS_UNUSED(perm);
468
469 if (type != FILE_TYPE_REGULAR)
470 return false;
471
472 auto ipc_server = ipc_server_create(dentry->name, 1);
473 if (ipc_server.isErr())
474 return false;
475
476 dentry_attach(dentry, ipc_server->sysfs_ino);
477 return true;
478}
479
480static bool ipc_dump_name_waitlist(uintn key, void *value, void *data)
481{
482 MOS_UNUSED(key);
483 waitlist_t *waitlist = (waitlist_t *) value;
484 sysfs_file_t *f = (sysfs_file_t *) data;
485
486 const auto guard = waitlist->lock.lock();
487 sysfs_printf(f, "%s\t%s:\n", (const char *) key, waitlist->closed ? "closed" : "open");
488 list_foreach(Thread, thread, waitlist->list)
489 {
490 sysfs_printf(f, "\t%s\n", thread->name.c_str());
491 }
492
493 return true;
494}
495
497{
498 sysfs_printf(f, "%-20s\t%s\n", "IPC Name", "Status");
499 const auto guard = ipc_lock.lock();
501 return true;
502}
503
509
#define MOS_ASSERT(cond)
Definition assert.hpp:14
#define MOS_PAGE_SIZE
Definition autoconf.h:6
const Char * c_str() const
Definition string.hpp:215
#define PERM_WRITE
Definition fs_types.h:58
#define PERM_READ
Definition fs_types.h:57
u16 file_perm_t
Definition fs_types.h:52
#define PERM_OWNER
Definition fs_types.h:54
file_type_t
Definition fs_types.h:14
@ FILE_TYPE_CHAR_DEVICE
Definition fs_types.h:18
@ FILE_TYPE_REGULAR
Definition fs_types.h:15
void dentry_attach(dentry_t *d, inode_t *inode)
Attach an inode to a dentry.
Definition dentry.cpp:306
bool signal_has_pending(void)
Return true if there's a pending signal.
Definition signal.cpp:296
MOSAPI hash_t __pure hashmap_hash_string(uintn key)
MOSAPI int __pure hashmap_compare_string(uintn key1, uintn key2)
MOSAPI void * hashmap_get(hashmap_t *map, uintn key)
Definition hashmap.cpp:92
MOSAPI void * hashmap_put(hashmap_t *map, uintn key, void *value)
Definition hashmap.cpp:64
MOSAPI void hashmap_init(hashmap_t *map, size_t capacity, hashmap_hash_t hash_func, hashmap_key_compare_t compare_func)
Definition hashmap.cpp:20
MOSAPI void hashmap_foreach(hashmap_t *map, hashmap_foreach_func_t func, void *data)
Definition hashmap.cpp:142
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.cpp:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.cpp:68
#define list_foreach(t, v, h)
Iterate over a list.
Definition list.hpp:89
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.hpp:74
list_node_t list_head
A linked list head.
Definition list.hpp:23
MOSAPI bool list_is_empty(const list_node_t *head)
Definition list.cpp:21
#define list_remove(element)
Definition list.hpp:80
#define list_node_next_entry(node, type)
Get the next list node.
Definition list.hpp:68
void ipc_server_close(IPCServer *server)
Definition ipc.cpp:88
void ipc_server_close_channel(IPCDescriptor *ipc)
Definition ipc.cpp:167
static inode_t * ipc_sysfs_create_ino(IPCServer *ipc_server)
Definition ipc.cpp:422
void ipc_client_close_channel(IPCDescriptor *ipc)
Definition ipc.cpp:153
static bool ipc_sysfs_create_server(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
Definition ipc.cpp:464
static bool ipc_sysfs_dump_name_waitlist(sysfs_file_t *f)
Definition ipc.cpp:496
PtrResult< IPCServer > ipc_get_server(mos::string_view name)
Definition ipc.cpp:224
size_t ipc_client_write(IPCDescriptor *ipc, const void *buf, size_t size)
Definition ipc.cpp:138
PtrResult< IPCDescriptor > ipc_server_accept(IPCServer *ipc_server)
Definition ipc.cpp:236
static void ipc_sysfs_list_ipcs(sysfs_item_t *item, dentry_t *d, vfs_listdir_state_t *state, dentry_iterator_op add_record)
Definition ipc.cpp:430
PtrResult< IPCDescriptor > ipc_connect_to_server(mos::string_view name, size_t buffer_size)
Definition ipc.cpp:305
static bool ipc_dump_name_waitlist(uintn key, void *value, void *data)
Definition ipc.cpp:480
static sysfs_item_t ipc_sysfs_items[]
Definition ipc.cpp:504
static bool ipc_sysfs_servers(sysfs_file_t *f)
Definition ipc.cpp:411
static spinlock_t ipc_lock
protects ipc_servers and name_waitlist
Definition ipc.cpp:86
size_t ipc_server_write(IPCDescriptor *ipc, const void *buf, size_t size)
Definition ipc.cpp:148
static list_head ipc_servers
Definition ipc.cpp:84
static bool ipc_sysfs_lookup_ipc(inode_t *parent_dir, dentry_t *dentry)
Definition ipc.cpp:442
size_t ipc_server_read(IPCDescriptor *ipc, void *buf, size_t size)
Definition ipc.cpp:143
static hashmap_t name_waitlist
waitlist for an IPC server, key = name, value = waitlist_t *
Definition ipc.cpp:85
void ipc_init(void)
Definition ipc.cpp:181
PtrResult< IPCServer > ipc_server_create(mos::string_view name, size_t max_pending)
Definition ipc.cpp:188
size_t ipc_client_read(IPCDescriptor *ipc, void *buf, size_t size)
Definition ipc.cpp:133
const file_ops_t ipc_sysfs_file_ops
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:76
#define MOS_UNUSED(x)
Definition mos_global.h:65
basic_string_view< char > string_view
mos::basic_string< char, mos::default_allocator > string
Definition string.hpp:336
T * create(Args &&...args)
Definition allocator.hpp:10
#define NULL
Definition pb_syshdr.h:46
__nodiscard bool pipe_close_one_end(pipe_t *pipe)
Close one end of the pipe, so that the other end will get EOF.
Definition pipe.cpp:136
PtrResult< pipe_t > pipe_create(size_t bufsize)
Definition pipe.cpp:167
size_t pipe_write(pipe_t *pipe, const void *buf, size_t size)
Definition pipe.cpp:22
size_t pipe_read(pipe_t *pipe, void *buf, size_t size)
Definition pipe.cpp:82
#define pr_dinfo(feat, fmt,...)
Definition printk.hpp:28
#define pr_dinfo2(feat, fmt,...)
Definition printk.hpp:27
#define pr_dwarn(feat, fmt,...)
Definition printk.hpp:30
void blocked_reschedule(void)
Mark the current task as blocked and reschedule.
Definition schedule.cpp:164
size_t size
Definition slab.cpp:34
const char * name
Definition slab.cpp:35
#define spinlock_acquire(lock)
Definition spinlock.hpp:64
#define spinlock_release(lock)
Definition spinlock.hpp:65
pipe_t * server_read_pipe
Definition ipc.cpp:40
IPCDescriptor(mos::string_view name, size_t buffer_size)
Definition ipc.cpp:49
as_linked_list
attached to either pending or established list
Definition ipc.cpp:31
pipe_t * client_write_pipe
Definition ipc.cpp:39
const mos::string server_name
Definition ipc.cpp:32
waitlist_t client_waitlist
client waits here for the server to accept the connection
Definition ipc.cpp:35
size_t buffer_size_npages
Definition ipc.cpp:33
pipe_t * server_write_pipe
Definition ipc.cpp:45
pipe_t * client_read_pipe
Definition ipc.cpp:46
inode_t * sysfs_ino
inode for sysfs
Definition ipc.cpp:59
list_head pending
list of IPCDescriptor
Definition ipc.cpp:62
~IPCServer()
Definition ipc.cpp:78
as_linked_list
Definition ipc.cpp:56
size_t pending_max
Definition ipc.cpp:60
waitlist_t server_waitlist
wake up the server here when a client connects
Definition ipc.cpp:64
size_t established_n
Definition ipc.cpp:61
spinlock_t lock
Definition ipc.cpp:58
const mos::string name
Definition ipc.cpp:57
size_t pending_n
Definition ipc.cpp:60
IPCServer(mos::string_view name, size_t pending_max)
Definition ipc.cpp:71
void * key() const
Definition ipc.cpp:66
mos::string name
inode_t * inode
file_perm_t perm
const file_ops_t * file_ops
SpinLocker lock()
Definition spinlock.hpp:157
bool closed
Definition wait.hpp:21
spinlock_t lock
Definition wait.hpp:22
list_head list
Definition wait.hpp:23
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
Definition sysfs.cpp:74
inode_t * sysfs_create_inode(file_type_t type, void *data)
Definition sysfs.cpp:376
#define SYSFS_RO_ITEM(_name, _show_fn)
Definition sysfs.hpp:42
#define SYSFS_DYN_DIR(_name, _iterate_fn, _lookup_fn, _create_fn)
Definition sysfs.hpp:47
#define SYSFS_AUTOREGISTER(sysfs_name, sysfs_items)
unsigned long uintn
Definition types.h:26
unsigned long ptr_t
Definition types.h:21
void dentry_iterator_op(vfs_listdir_state_t *state, u64 ino, mos::string_view name, file_type_t type)
Definition vfs_types.hpp:61
void waitlist_init(waitlist_t *list)
Definition wait.cpp:15
#define waitlist_wake_all(list)
Definition wait.hpp:34
size_t waitlist_wake(waitlist_t *list, size_t max_wakeups)
Definition wait.cpp:37
void waitlist_remove_me(waitlist_t *waitlist)
Definition wait.cpp:78
void waitlist_close(waitlist_t *list)
Definition wait.cpp:68
__nodiscard bool waitlist_append(waitlist_t *list)
Definition wait.cpp:21