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