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#include "mos/ipc/ipc.hpp"
4
11#include "mos/ipc/pipe.hpp"
15#include "mos/tasks/signal.hpp"
16#include "mos/tasks/wait.hpp"
17
18#include <mos/allocator.hpp>
22#include <mos/mos_global.h>
23#include <mos_stdlib.hpp>
24#include <mos_string.hpp>
25
26#define IPC_SERVER_MAGIC MOS_FOURCC('I', 'P', 'C', 'S')
27
52
82
86
88{
90 spinlock_acquire(&server->lock);
91 // remove the server from the list
92 list_remove(server);
93
94 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) server->key());
95 if (waitlist)
96 {
97 // the waitlist should have been closed when the server was created
98 // and no one should be waiting on it
99 spinlock_acquire(&waitlist->lock);
100 MOS_ASSERT(waitlist->closed);
101 MOS_ASSERT(list_is_empty(&waitlist->list));
102 spinlock_release(&waitlist->lock);
103 waitlist_init(waitlist); // reuse the waitlist
104 }
106
107 // with the server lock held, we reject all pending connections
108 list_foreach(IpcDescriptor, ipc, server->pending)
109 {
110 ipc->buffer_size_npages = 0; // mark the connection as closed
111 // wake up the client
112 waitlist_close(&ipc->client_waitlist);
113 waitlist_wake_all(&ipc->client_waitlist);
114 }
115
116 server->pending_max = 0;
117 waitlist_close(&server->server_waitlist); // close the server's waitlist
118 int n = waitlist_wake_all(&server->server_waitlist); // wake up the server, if it is waiting
119
120 if (n)
121 {
122 spinlock_release(&server->lock);
123 // let the accept() syscall free the server
124 }
125 else
126 {
128 // now we can free the server
129 const auto dparent = ipc_get_sysfs_dir();
130
131 const auto dentry = dentry_get_from_parent(server->sysfs_ino->superblock, dparent, server->name);
132 if (dentry->inode == nullptr)
133 dentry_attach(dentry, server->sysfs_ino); // fixup, as lookup may not have been called
134 inode_unlink(server->sysfs_ino, dentry);
135 dentry_unref(dentry); // it won't release dentry because dentry->inode is still valid
136 dentry_detach(dentry);
137 dentry_try_release(dentry);
138 server->sysfs_ino = NULL;
139 delete server;
140 }
141}
142
143size_t ipc_client_read(IpcDescriptor *ipc, void *buf, size_t size)
144{
145 return pipe_read(ipc->client_read_pipe, buf, size);
146}
147
148size_t ipc_client_write(IpcDescriptor *ipc, const void *buf, size_t size)
149{
150 return pipe_write(ipc->client_write_pipe, buf, size);
151}
152
153size_t ipc_server_read(IpcDescriptor *ipc, void *buf, size_t size)
154{
155 return pipe_read(ipc->server_read_pipe, buf, size);
156}
157
158size_t ipc_server_write(IpcDescriptor *ipc, const void *buf, size_t size)
159{
160 return pipe_write(ipc->server_write_pipe, buf, size);
161}
162
164{
165 bool r_fullyclosed = pipe_close_one_end(ipc->client_read_pipe);
166 bool w_fullyclosed = pipe_close_one_end(ipc->client_write_pipe);
167 MOS_ASSERT(r_fullyclosed == w_fullyclosed); // both ends should have the same return value
168
169 if (r_fullyclosed || w_fullyclosed)
170 {
171 // now we can free the ipc
172 delete ipc;
173 return;
174 }
175}
176
178{
179 bool r_fullyclosed = pipe_close_one_end(ipc->server_read_pipe);
180 bool w_fullyclosed = pipe_close_one_end(ipc->server_write_pipe);
181 MOS_ASSERT(r_fullyclosed == w_fullyclosed); // both ends should have the same return value
182
183 if (r_fullyclosed || w_fullyclosed)
184 {
185 // now we can free the ipc
186 delete ipc;
187 return;
188 }
189}
190
195
196static inode_t *ipc_sysfs_create_ino(IPCServer *ipc_server);
197
199{
200 dInfo<ipc> << "creating ipc server '" << name << "' with max_pending=" << max_pending;
201 const auto guard = ipc_lock.lock();
203 {
204 if (server->name == name)
205 {
206 dWarn<ipc> << "ipc server '" << name << "' already exists";
207 return -EEXIST;
208 }
209 }
210
211 // we don't need to acquire the lock here because the server is not yet announced
212 const auto server = mos::create<IPCServer>(name, max_pending);
213
214 // now announce the server
216 ipc_sysfs_create_ino(server);
217
218 // check and see if there is a waitlist for this name
219 // if so, wake up all waiters
220 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) server->key());
221 if (waitlist)
222 {
223 dInfo<ipc> << "found waitlist for ipc server '" << name << "'";
224 // wake up all waiters
225 waitlist_close(waitlist);
226 const size_t n = waitlist_wake_all(waitlist);
227 if (n)
228 dInfo<ipc> << "woken up " << n << " waiters for ipc server '" << name << "'";
229 }
230
231 return server;
232}
233
235{
236 const auto guard = ipc_lock.lock();
238 {
239 if (server->name == name)
240 return server;
241 }
242
243 return -ENOENT;
244}
245
247{
248 dInfo<ipc> << "accepting connection on ipc server '" << ipc_server->name << "'...";
249
250retry_accept:
251 spinlock_acquire(&ipc_server->lock);
252
253 // check if the server is closed
254 if (ipc_server->pending_max == 0)
255 {
256 // now we can free the server
257 dInfo<ipc> << "ipc server '" << ipc_server->name << "' is closed, aborting accept()";
258 delete ipc_server;
259 return -ECONNABORTED;
260 }
261
262 if (ipc_server->pending_n == 0)
263 {
264 // no pending connections, wait for a client to connect
265 dInfo<ipc> << "no pending connections, waiting for a client to connect...";
267 spinlock_release(&ipc_server->lock);
269
270 if (signal_has_pending())
271 {
272 dInfo<ipc> << "woken up by a signal, aborting accept()";
274 return -EINTR;
275 }
276
277 goto retry_accept; // the server has woken us up, try again
278 }
279
280 // get the first pending connection
281 MOS_ASSERT(!list_is_empty(&ipc_server->pending));
283 list_remove(desc);
284 ipc_server->pending_n--;
285 spinlock_release(&ipc_server->lock);
286
288 dInfo<ipc> << "accepted a connection on ipc server '" << ipc_server->name << "' with buffer_size_npages=" << desc->buffer_size_npages;
289
290 // setup the pipes
291 auto readPipe = pipe_create(desc->buffer_size_npages);
292 if (readPipe.isErr())
293 {
294 dWarn<ipc> << "failed to create read pipe";
295 // TODO: cleanup
296 return readPipe.getErr();
297 }
298 desc->server_read_pipe = readPipe.get();
299
300 auto writePipe = pipe_create(desc->buffer_size_npages);
301 if (writePipe.isErr())
302 {
303 dWarn<ipc> << "failed to create write pipe";
304 // TODO: cleanup
305 return writePipe.getErr();
306 }
307 desc->server_write_pipe = writePipe.get();
308
309 // wake up the client
311
312 return desc;
313}
314
316{
317 if (buffer_size == 0)
318 return -EINVAL; // buffer size must be > 0
319
320 dInfo<ipc> << "connecting to ipc server '" << name << "' with buffer_size=" << buffer_size;
321 buffer_size = ALIGN_UP_TO_PAGE(buffer_size);
322
323check_server:
324 // check if the server exists
326 IPCServer *ipc_server = NULL;
328 {
329 if (server->name == name)
330 {
331 ipc_server = server;
332 // we are holding the ipc_servers_lock, so that the server won't deannounce itself
333 // while we are checking the server list, thus the server won't be freed
334 spinlock_acquire(&ipc_server->lock);
335 dInfo<ipc> << "found ipc server '" << ipc_server->name << "'";
336 break;
337 }
338 }
339
340 // now we have a server, we can create the connection
341 const auto descriptor = mos::create<IpcDescriptor>(name, buffer_size);
342
343 if (!ipc_server)
344 {
345 // no server found, wait for it to be created
346 waitlist_t *waitlist = (waitlist_t *) hashmap_get(&name_waitlist, (ptr_t) name.data());
347 if (!waitlist)
348 {
349 waitlist = mos::create<waitlist_t>();
350 waitlist_init(waitlist);
351 // the key must be in kernel memory
352 const auto old = (waitlist_t *) hashmap_put(&name_waitlist, (ptr_t) descriptor->server_name.c_str(), waitlist);
353 if (old)
354 {
355 // someone else has created the waitlist, but now we have replaced it
356 // so we have to append the old waitlist to the new one
357 list_foreach(Thread, thread, old->list)
358 {
359 MOS_ASSERT(waitlist_append(waitlist));
360 }
361 }
362 dInfo<ipc> << "created waitlist for ipc server '" << name << "'";
363 }
364
365 dInfo<ipc> << "no ipc server '" << name << "' found, waiting for it to be created...";
366 MOS_ASSERT(waitlist_append(waitlist));
369
370 if (signal_has_pending())
371 {
372 dInfo<ipc> << "woken up by a signal, aborting connect()";
373 delete descriptor;
374 return -EINTR;
375 }
376
377 // now check if the server exists again
378 goto check_server;
379 }
381
382 // add the connection to the pending list
383 if (ipc_server->pending_n >= ipc_server->pending_max)
384 {
385 dWarn<ipc> << "ipc server '" << ipc_server->name << "' has reached its max pending connections, rejecting connection";
386 spinlock_release(&ipc_server->lock);
387 delete descriptor;
388 return -ECONNREFUSED;
389 }
390
391 list_node_append(&ipc_server->pending, list_node(descriptor)); // add to pending list
392 ipc_server->pending_n++;
393
394 // now wait for the server to accept the connection
395 MOS_ASSERT(waitlist_append(&descriptor->client_waitlist));
396 waitlist_wake(&ipc_server->server_waitlist, 1);
397 spinlock_release(&ipc_server->lock); // now the server can do whatever it wants
398
400 // the server has woken us up and has accepted the connection, or it is closed
401 dInfo<ipc> << "ipc server '" << ipc_server->name << "' woke us up";
402
403 // check if the server has closed
404 if (descriptor->buffer_size_npages == 0)
405 {
406 // the server is closed, don't touch ipc_server pointer anymore
407 dWarn<ipc> << "ipc server '" << ipc_server->name << "' has closed";
408 ipc_server = NULL;
409 delete descriptor;
410 return -ECONNREFUSED;
411 }
412
413 // now we have a connection, both the read and write pipes are ready, the io object is also ready
414 // we just need to return the io object
415 dInfo<ipc> << "ipc server '" << ipc_server->name << "' has accepted the connection";
416 return descriptor;
417}
418
419// ! sysfs support
420
422{
423 sysfs_printf(f, "%-40s\t%s\n", "Server Name", "Max Pending Connections");
425 {
426 sysfs_printf(f, "%-40s\t%zu\n", ipc->name.c_str(), ipc->pending_max);
427 }
428
429 return true;
430}
431
433{
434 ipc_server->sysfs_ino = sysfs_create_inode(FILE_TYPE_CHAR_DEVICE, ipc_server);
435 ipc_server->sysfs_ino->perm = PERM_OWNER & (PERM_READ | PERM_WRITE);
436 ipc_server->sysfs_ino->file_ops = &ipc_sysfs_file_ops;
437 return ipc_server->sysfs_ino;
438}
439
441{
442 MOS_UNUSED(item);
443 MOS_UNUSED(d);
444
446 {
447 MOS_ASSERT(ipc_server->sysfs_ino);
448 add_record(state, ipc_server->sysfs_ino->ino, ipc_server->name, ipc_server->sysfs_ino->type);
449 }
450}
451
452static bool ipc_sysfs_lookup_ipc(inode_t *parent_dir, dentry_t *dentry)
453{
454 MOS_UNUSED(parent_dir);
455
456 const auto name = dentry->name;
457 IPCServer *ipc_server = NULL;
459 {
460 if (ipc->name == name)
461 {
462 ipc_server = ipc;
463 break;
464 }
465 }
466
467 if (ipc_server == NULL)
468 return false;
469
470 dentry_attach(dentry, ipc_server->sysfs_ino);
471 return dentry->inode != NULL;
472}
473
474static bool ipc_sysfs_create_server(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
475{
476 MOS_UNUSED(dir);
477 MOS_UNUSED(perm);
478
479 if (type != FILE_TYPE_REGULAR)
480 return false;
481
482 auto ipc_server = ipc_server_create(dentry->name, 1);
483 if (ipc_server.isErr())
484 return false;
485
486 dentry_attach(dentry, ipc_server->sysfs_ino);
487 return true;
488}
489
490static bool ipc_dump_name_waitlist(uintn key, void *value, void *data)
491{
492 MOS_UNUSED(key);
493 waitlist_t *waitlist = (waitlist_t *) value;
494 sysfs_file_t *f = (sysfs_file_t *) data;
495
496 const auto guard = waitlist->lock.lock();
497 sysfs_printf(f, "%s\t%s:\n", (const char *) key, waitlist->closed ? "closed" : "open");
498 list_foreach(Thread, thread, waitlist->list)
499 {
500 sysfs_printf(f, "\t%s\n", thread->name.c_str());
501 }
502
503 return true;
504}
505
507{
508 sysfs_printf(f, "%-20s\t%s\n", "IPC Name", "Status");
509 const auto guard = ipc_lock.lock();
511 return true;
512}
513
519
521
523{
524 return __sysfs_ipc._dentry;
525}
#define MOS_ASSERT(cond)
Definition assert.hpp:19
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#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:279
void dentry_detach(dentry_t *d)
Detach the inode from a dentry.
Definition dentry.cpp:292
void dentry_try_release(dentry_t *dentry)
void dentry_unref(dentry_t *dentry)
Decrement the reference count of a dentry.
bool signal_has_pending(void)
Return true if there's a pending signal.
Definition signal.cpp:307
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
bool inode_unlink(inode_t *dir, dentry_t *dentry)
Unlink a dentry from its parent inode.
Definition inode.cpp:80
void ipc_server_close(IPCServer *server)
Definition ipc.cpp:87
void ipc_server_close_channel(IpcDescriptor *ipc)
Definition ipc.cpp:177
dentry_t * ipc_get_sysfs_dir()
Definition ipc.cpp:522
static inode_t * ipc_sysfs_create_ino(IPCServer *ipc_server)
Definition ipc.cpp:432
size_t ipc_server_read(IpcDescriptor *ipc, void *buf, size_t size)
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:474
static bool ipc_sysfs_dump_name_waitlist(sysfs_file_t *f)
Definition ipc.cpp:506
PtrResult< IPCServer > ipc_get_server(mos::string_view name)
Definition ipc.cpp:234
PtrResult< IpcDescriptor > ipc_connect_to_server(mos::string_view name, size_t buffer_size)
Definition ipc.cpp:315
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:440
static bool ipc_dump_name_waitlist(uintn key, void *value, void *data)
Definition ipc.cpp:490
static sysfs_item_t ipc_sysfs_items[]
Definition ipc.cpp:514
static bool ipc_sysfs_servers(sysfs_file_t *f)
Definition ipc.cpp:421
static spinlock_t ipc_lock
protects ipc_servers and name_waitlist
Definition ipc.cpp:85
size_t ipc_client_read(IpcDescriptor *ipc, void *buf, size_t size)
Definition ipc.cpp:143
void ipc_client_close_channel(IpcDescriptor *ipc)
Definition ipc.cpp:163
PtrResult< IpcDescriptor > ipc_server_accept(IPCServer *ipc_server)
Definition ipc.cpp:246
static list_head ipc_servers
Definition ipc.cpp:83
size_t ipc_server_write(IpcDescriptor *ipc, const void *buf, size_t size)
Definition ipc.cpp:158
static bool ipc_sysfs_lookup_ipc(inode_t *parent_dir, dentry_t *dentry)
Definition ipc.cpp:452
size_t ipc_client_write(IpcDescriptor *ipc, const void *buf, size_t size)
Definition ipc.cpp:148
static hashmap_t name_waitlist
waitlist for an IPC server, key = name, value = waitlist_t *
Definition ipc.cpp:84
void ipc_init(void)
Definition ipc.cpp:191
PtrResult< IPCServer > ipc_server_create(mos::string_view name, size_t max_pending)
Definition ipc.cpp:198
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
T * create(Args &&...args)
Definition allocator.hpp:12
mos::basic_string< char > string
Definition string.hpp:395
#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:134
PtrResult< pipe_t > pipe_create(size_t bufsize)
Definition pipe.cpp:165
size_t pipe_write(pipe_t *pipe, const void *buf, size_t size)
Definition pipe.cpp:20
size_t pipe_read(pipe_t *pipe, void *buf, size_t size)
Definition pipe.cpp:80
void blocked_reschedule(void)
Mark the current task as blocked and reschedule.
Definition schedule.cpp:163
size_t size
Definition slab.cpp:32
const char * name
Definition slab.cpp:33
#define spinlock_acquire(lock)
Definition spinlock.hpp:64
#define spinlock_release(lock)
Definition spinlock.hpp:65
inode_t * sysfs_ino
inode for sysfs
Definition ipc.cpp:58
list_head pending
list of IPCDescriptor
Definition ipc.cpp:61
~IPCServer()
Definition ipc.cpp:77
as_linked_list
Definition ipc.cpp:55
size_t pending_max
Definition ipc.cpp:59
waitlist_t server_waitlist
wake up the server here when a client connects
Definition ipc.cpp:63
size_t established_n
Definition ipc.cpp:60
spinlock_t lock
Definition ipc.cpp:57
const mos::string name
Definition ipc.cpp:56
size_t pending_n
Definition ipc.cpp:59
IPCServer(mos::string_view name, size_t pending_max)
Definition ipc.cpp:70
void * key() const
Definition ipc.cpp:65
pipe_t * server_read_pipe
Definition ipc.cpp:39
IpcDescriptor(mos::string_view name, size_t buffer_size)
Definition ipc.cpp:48
as_linked_list
attached to either pending or established list
Definition ipc.cpp:30
pipe_t * client_write_pipe
Definition ipc.cpp:38
const mos::string server_name
Definition ipc.cpp:31
waitlist_t client_waitlist
client waits here for the server to accept the connection
Definition ipc.cpp:34
size_t buffer_size_npages
Definition ipc.cpp:32
pipe_t * server_write_pipe
Definition ipc.cpp:44
pipe_t * client_read_pipe
Definition ipc.cpp:45
mos::string name
inode_t * inode
superblock_t * superblock
file_perm_t perm
const file_ops_t * file_ops
SpinLocker lock()
Definition spinlock.hpp:180
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:73
inode_t * sysfs_create_inode(file_type_t type, void *data)
Definition sysfs.cpp:375
#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)
#define f(_fmt)
Definition syslog.hpp:160
constexpr auto dInfo
Definition syslog.hpp:152
constexpr auto dWarn
Definition syslog.hpp:154
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:64
dentry_t * dentry_get_from_parent(superblock_t *sb, dentry_t *parent, mos::string_view name)
Create a new dentry with the given name and parent.
Definition vfs_utils.cpp:37
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