source: mainline/uspace/lib/c/generic/async/server.c@ f92b315

Last change on this file since f92b315 was 012dd8e, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

taskman: Handle INIT_TASKS as tasks spawned by loader

  • everyone is connected to its spawner, except for INIT_TASKS, they are connected to taskman (first binary)
  • taskman is now aware even of INIT_TASKS and taskman itself
  • refactored taskman handshake — NS session is created lazily
  • refactored async.c with usage of create_session
  • changed EINVAL to EINTR on lost waits
  • removed TODOs from taskman and related libc TODOs

Conflicts:

abi/include/abi/ipc/methods.h
boot/Makefile.common
uspace/lib/c/generic/async.c
uspace/lib/c/generic/libc.c
uspace/lib/c/generic/loader.c
uspace/lib/c/generic/ns.c
uspace/lib/c/generic/private/async.h
uspace/lib/c/generic/private/taskman.h
uspace/lib/c/generic/task.c
uspace/lib/c/include/async.h
uspace/lib/c/include/task.h
uspace/srv/loader/main.c
uspace/srv/ns/ns.c

  • Property mode set to 100644
File size: 45.4 KB
RevLine 
[06502f7d]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[06502f7d]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[b2951e2]27 */
28
[a46da63]29/** @addtogroup libc
[b2951e2]30 * @{
31 */
32/** @file
[c07544d3]33 */
[06502f7d]34
[80649a91]35/**
36 * Asynchronous library
37 *
[c07544d3]38 * The aim of this library is to provide a facility for writing programs which
39 * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
40 * programming.
[80649a91]41 *
[79ae36dd]42 * You should be able to write very simple multithreaded programs. The async
43 * framework will automatically take care of most of the synchronization
44 * problems.
[80649a91]45 *
[9591265]46 * Example of use (pseudo C):
[c07544d3]47 *
[80649a91]48 * 1) Multithreaded client application
[9591265]49 *
[c07544d3]50 * fibril_create(fibril1, ...);
51 * fibril_create(fibril2, ...);
52 * ...
53 *
54 * int fibril1(void *arg)
55 * {
[79ae36dd]56 * conn = async_connect_me_to(...);
57 *
58 * exch = async_exchange_begin(conn);
59 * c1 = async_send(exch);
60 * async_exchange_end(exch);
61 *
62 * exch = async_exchange_begin(conn);
63 * c2 = async_send(exch);
64 * async_exchange_end(exch);
65 *
[c07544d3]66 * async_wait_for(c1);
67 * async_wait_for(c2);
68 * ...
69 * }
[80649a91]70 *
71 *
72 * 2) Multithreaded server application
73 *
[c07544d3]74 * main()
75 * {
76 * async_manager();
77 * }
78 *
[984a9ba]79 * port_handler(ipc_call_t *icall)
[c07544d3]80 * {
81 * if (want_refuse) {
[984a9ba]82 * async_answer_0(icall, ELIMIT);
[c07544d3]83 * return;
84 * }
[80649a91]85 *
[984a9ba]86 * async_answer_0(icall, EOK);
[53ca318]87 *
[984a9ba]88 * async_get_call(&call);
89 * somehow_handle_the_call(&call);
90 * async_answer_2(&call, 1, 2, 3);
91 *
92 * async_get_call(&call);
[c07544d3]93 * ...
94 * }
[a2cd194]95 *
[80649a91]96 */
[9591265]97
[4805495]98#define _LIBC_ASYNC_C_
[64d2b10]99#include <ipc/ipc.h>
[80649a91]100#include <async.h>
[49a796f1]101#include "../private/async.h"
[4805495]102#undef _LIBC_ASYNC_C_
[64d2b10]103
[8820544]104#include <ipc/irq.h>
105#include <ipc/event.h>
[bc1f1c2]106#include <fibril.h>
[d9c8c81]107#include <adt/hash_table.h>
[853802e]108#include <adt/hash.h>
[d9c8c81]109#include <adt/list.h>
[80649a91]110#include <assert.h>
111#include <errno.h>
[bd41ac52]112#include <time.h>
[3e6a98c5]113#include <stdbool.h>
[38d150e]114#include <stdlib.h>
[79ae36dd]115#include <mem.h>
116#include <stdlib.h>
[e2ab36f1]117#include <macros.h>
[514d561]118#include <str_error.h>
[101516d]119#include <as.h>
[ae6021d]120#include <abi/mm/as.h>
[49a796f1]121#include "../private/libc.h"
[d73d992]122#include "../private/fibril.h"
[5da7199]123
[8dab988]124#define DPRINTF(...) ((void) 0)
125
[79ae36dd]126/* Client connection data */
[c80fdd0]127typedef struct {
[062d900]128 ht_link_t link;
[a35b458]129
[649f087]130 task_id_t in_task_id;
[498ced1]131 int refcnt;
[c80fdd0]132 void *data;
133} client_t;
134
[79ae36dd]135/* Server connection data */
[80649a91]136typedef struct {
[514d561]137 /** Fibril handling the connection. */
138 fid_t fid;
[a35b458]139
[e70bfa5]140 /** Hash table link. */
[062d900]141 ht_link_t link;
[a35b458]142
[e2ab36f1]143 /** Incoming client task ID. */
144 task_id_t in_task_id;
[a35b458]145
[23882034]146 /** Link to the client tracking structure. */
147 client_t *client;
[a35b458]148
[1de92fb0]149 /** Channel for messages that should be delivered to this fibril. */
150 mpsc_t *msg_channel;
[a35b458]151
[e70bfa5]152 /** Call data of the opening call. */
[80649a91]153 ipc_call_t call;
[a35b458]154
[e70bfa5]155 /** Fibril function that will be used to handle the connection. */
[b688fd8]156 async_port_handler_t handler;
[a35b458]157
[b688fd8]158 /** Client data */
159 void *data;
[80649a91]160} connection_t;
161
[8dab988]162/* Member of notification_t::msg_list. */
163typedef struct {
164 link_t link;
165 ipc_call_t calldata;
166} notification_msg_t;
167
[8820544]168/* Notification data */
169typedef struct {
[3b1cc8d]170 /** notification_hash_table link */
171 ht_link_t htlink;
172
173 /** notification_queue link */
174 link_t qlink;
[a35b458]175
[8820544]176 /** Notification method */
177 sysarg_t imethod;
[a35b458]178
[8820544]179 /** Notification handler */
180 async_notification_handler_t handler;
[a35b458]181
[3b1cc8d]182 /** Notification handler argument */
183 void *arg;
184
[8dab988]185 /** List of arrived notifications. */
186 list_t msg_list;
[8820544]187} notification_t;
188
[bc1f1c2]189/** Identifier of the incoming connection handled by the current fibril. */
[79ae36dd]190static fibril_local connection_t *fibril_connection;
[e70bfa5]191
[46eec3b]192static void *default_client_data_constructor(void)
193{
194 return NULL;
195}
196
197static void default_client_data_destructor(void *data)
198{
199}
200
201static async_client_data_ctor_t async_client_data_create =
202 default_client_data_constructor;
203static async_client_data_dtor_t async_client_data_destroy =
204 default_client_data_destructor;
205
206void async_set_client_data_constructor(async_client_data_ctor_t ctor)
207{
[f302586]208 assert(async_client_data_create == default_client_data_constructor);
[46eec3b]209 async_client_data_create = ctor;
210}
211
212void async_set_client_data_destructor(async_client_data_dtor_t dtor)
213{
[f302586]214 assert(async_client_data_destroy == default_client_data_destructor);
[46eec3b]215 async_client_data_destroy = dtor;
216}
217
[0a8f070]218static async_client_conn_t implicit_connection = NULL;
[45c8eea]219static fibril_rmutex_t client_mutex;
[c80fdd0]220static hash_table_t client_hash_table;
[3b1cc8d]221
222// TODO: lockfree notification_queue?
[45c8eea]223static fibril_rmutex_t notification_mutex;
[8820544]224static hash_table_t notification_hash_table;
[3b1cc8d]225static LIST_INITIALIZE(notification_queue);
226static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
227
[8dab988]228static LIST_INITIALIZE(notification_freelist);
229static long notification_freelist_total = 0;
230static long notification_freelist_used = 0;
231
[8820544]232static sysarg_t notification_avail = 0;
233
[5e801dc]234static size_t client_key_hash(const void *key)
[c80fdd0]235{
[5e801dc]236 const task_id_t *in_task_id = key;
237 return *in_task_id;
[c80fdd0]238}
239
[062d900]240static size_t client_hash(const ht_link_t *item)
[c80fdd0]241{
[062d900]242 client_t *client = hash_table_get_inst(item, client_t, link);
243 return client_key_hash(&client->in_task_id);
[c80fdd0]244}
245
[5e801dc]246static bool client_key_equal(const void *key, const ht_link_t *item)
[c80fdd0]247{
[5e801dc]248 const task_id_t *in_task_id = key;
[062d900]249 client_t *client = hash_table_get_inst(item, client_t, link);
[5e801dc]250 return *in_task_id == client->in_task_id;
[c80fdd0]251}
252
253/** Operations for the client hash table. */
[062d900]254static hash_table_ops_t client_hash_table_ops = {
[c80fdd0]255 .hash = client_hash,
[062d900]256 .key_hash = client_key_hash,
257 .key_equal = client_key_equal,
[4e00f87]258 .equal = NULL,
259 .remove_callback = NULL
[c80fdd0]260};
[80649a91]261
[9ef495f]262static client_t *async_client_get(task_id_t client_id, bool create)
263{
264 client_t *client = NULL;
[a35b458]265
[9bde0d5]266 fibril_rmutex_lock(&client_mutex);
[9ef495f]267 ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
268 if (link) {
269 client = hash_table_get_inst(link, client_t, link);
[498ced1]270 client->refcnt++;
[9ef495f]271 } else if (create) {
[3bd1d7d4]272 // TODO: move the malloc out of critical section
[9bde0d5]273 /* malloc() is rmutex safe. */
[9ef495f]274 client = malloc(sizeof(client_t));
275 if (client) {
276 client->in_task_id = client_id;
277 client->data = async_client_data_create();
[a35b458]278
[498ced1]279 client->refcnt = 1;
[9ef495f]280 hash_table_insert(&client_hash_table, &client->link);
281 }
282 }
[a35b458]283
[9bde0d5]284 fibril_rmutex_unlock(&client_mutex);
[9ef495f]285 return client;
286}
287
288static void async_client_put(client_t *client)
289{
290 bool destroy;
[a35b458]291
[9bde0d5]292 fibril_rmutex_lock(&client_mutex);
[a35b458]293
[498ced1]294 if (--client->refcnt == 0) {
[9ef495f]295 hash_table_remove(&client_hash_table, &client->in_task_id);
296 destroy = true;
297 } else
298 destroy = false;
[a35b458]299
[9bde0d5]300 fibril_rmutex_unlock(&client_mutex);
[a35b458]301
[9ef495f]302 if (destroy) {
303 if (client->data)
304 async_client_data_destroy(client->data);
[a35b458]305
[9ef495f]306 free(client);
307 }
308}
309
310/** Wrapper for client connection fibril.
311 *
312 * When a new connection arrives, a fibril with this implementing
313 * function is created.
314 *
315 * @param arg Connection structure pointer.
316 *
317 * @return Always zero.
318 *
319 */
[b7fd2a0]320static errno_t connection_fibril(void *arg)
[9ef495f]321{
322 assert(arg);
[a35b458]323
[9ef495f]324 /*
325 * Setup fibril-local connection pointer.
326 */
327 fibril_connection = (connection_t *) arg;
[a35b458]328
[6769005]329 mpsc_t *c = fibril_connection->msg_channel;
330
[9ef495f]331 /*
332 * Add our reference for the current connection in the client task
333 * tracking structure. If this is the first reference, create and
334 * hash in a new tracking structure.
335 */
[a35b458]336
[9ef495f]337 client_t *client = async_client_get(fibril_connection->in_task_id, true);
338 if (!client) {
[061274f]339 ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
[6769005]340 goto out;
[9ef495f]341 }
[a35b458]342
[9ef495f]343 fibril_connection->client = client;
[a35b458]344
[9ef495f]345 /*
346 * Call the connection handler function.
347 */
[984a9ba]348 fibril_connection->handler(&fibril_connection->call,
349 fibril_connection->data);
[a35b458]350
[9ef495f]351 /*
352 * Remove the reference for this client task connection.
353 */
354 async_client_put(client);
[a35b458]355
[1de92fb0]356 /*
357 * Close the channel, if it isn't closed already.
358 */
359 mpsc_close(c);
360
[9ef495f]361 /*
362 * Answer all remaining messages with EHANGUP.
363 */
[1de92fb0]364 ipc_call_t call;
365 while (mpsc_receive(c, &call, NULL) == EOK)
366 ipc_answer_0(call.cap_handle, EHANGUP);
[a35b458]367
[9ef495f]368 /*
[1de92fb0]369 * Clean up memory.
[9ef495f]370 */
[6769005]371out:
[1de92fb0]372 mpsc_destroy(c);
[9ef495f]373 free(fibril_connection);
[d5c1051]374 return EOK;
[9ef495f]375}
376
[6769005]377/** Return label usable during replies to IPC_M_CONNECT_ME_TO. */
378sysarg_t async_get_label(void)
379{
[53ee7a0]380 return (sysarg_t) fibril_connection;
[6769005]381}
382
[9ef495f]383/** Create a new fibril for a new connection.
384 *
[01c3bb4]385 * Create new fibril for connection, fill in connection structures and insert it
386 * into the hash table, so that later we can easily do routing of messages to
387 * particular fibrils.
[9ef495f]388 *
[6769005]389 * @param conn Pointer to the connection structure. Will be used as the
390 * label of the connected phone and request_label of incoming
391 * calls routed through that phone.
392 * @param in_task_id Identification of the incoming connection.
[012dd8e]393 * @param call Call data of the opening call. If call is NULL, it's
394 * either a callback connection that was opened by
395 * accepting the IPC_M_CONNECT_TO_ME call.
396 * Alternatively, it is zero when we are opening
397 * implicit connection.
[6769005]398 * @param handler Connection handler.
399 * @param data Client argument to pass to the connection handler.
[9ef495f]400 *
[01c3bb4]401 * @return New fibril id or NULL on failure.
[9ef495f]402 *
403 */
[6769005]404static fid_t async_new_connection(connection_t *conn, task_id_t in_task_id,
[061274f]405 ipc_call_t *call, async_port_handler_t handler, void *data)
[9ef495f]406{
407 conn->in_task_id = in_task_id;
[1de92fb0]408 conn->msg_channel = mpsc_create(sizeof(ipc_call_t));
[9ef495f]409 conn->handler = handler;
410 conn->data = data;
[a35b458]411
[6769005]412 if (!conn->msg_channel)
413 goto error;
414
[9ef495f]415 if (call)
416 conn->call = *call;
[061274f]417 else
418 conn->call.cap_handle = CAP_NIL;
[a35b458]419
[9ef495f]420 /* We will activate the fibril ASAP */
[514d561]421 conn->fid = fibril_create(connection_fibril, conn);
[a35b458]422
[6769005]423 if (conn->fid == 0)
424 goto error;
[a35b458]425
[6769005]426 fibril_start(conn->fid);
[a35b458]427
[6769005]428 return conn->fid;
[a35b458]429
[6769005]430error:
431 if (conn->msg_channel)
432 mpsc_destroy(conn->msg_channel);
433 free(conn);
[a35b458]434
[6769005]435 if (call)
436 ipc_answer_0(call->cap_handle, ENOMEM);
[a35b458]437
[6769005]438 return (fid_t) NULL;
[9ef495f]439}
440
[78bb04b]441/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
442 *
443 * Ask through phone for a new connection to some service.
444 *
445 * @param exch Exchange for sending the message.
446 * @param iface Callback interface.
447 * @param arg1 User defined argument.
448 * @param arg2 User defined argument.
449 * @param handler Callback handler.
450 * @param data Handler data.
451 * @param port_id ID of the newly created port.
452 *
[cde999a]453 * @return Zero on success or an error code.
[78bb04b]454 *
455 */
[b7fd2a0]456errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
[78bb04b]457 sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
458{
459 if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
460 return EINVAL;
[a35b458]461
[78bb04b]462 if (exch == NULL)
463 return ENOENT;
[a35b458]464
[6769005]465 connection_t *conn = calloc(1, sizeof(*conn));
466 if (!conn)
467 return ENOMEM;
468
[78bb04b]469 ipc_call_t answer;
[6769005]470 aid_t req = async_send_5(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
471 0, (sysarg_t) conn, &answer);
[a35b458]472
[fda19b8]473 errno_t rc;
474 async_wait_for(req, &rc);
[6769005]475 if (rc != EOK) {
476 free(conn);
[fda19b8]477 return rc;
[6769005]478 }
[a35b458]479
[fda19b8]480 rc = async_create_port_internal(iface, handler, data, port_id);
[6769005]481 if (rc != EOK) {
482 free(conn);
[fda19b8]483 return rc;
[6769005]484 }
[a35b458]485
[6769005]486 fid_t fid = async_new_connection(conn, answer.task_id, NULL, handler,
487 data);
[514d561]488 if (fid == (fid_t) NULL)
[78bb04b]489 return ENOMEM;
[a35b458]490
[78bb04b]491 return EOK;
492}
493
[5e801dc]494static size_t notification_key_hash(const void *key)
[8820544]495{
[5e801dc]496 const sysarg_t *id = key;
497 return *id;
[8820544]498}
499
500static size_t notification_hash(const ht_link_t *item)
501{
502 notification_t *notification =
[3b1cc8d]503 hash_table_get_inst(item, notification_t, htlink);
[8820544]504 return notification_key_hash(&notification->imethod);
505}
506
[5e801dc]507static bool notification_key_equal(const void *key, const ht_link_t *item)
[8820544]508{
[5e801dc]509 const sysarg_t *id = key;
[8820544]510 notification_t *notification =
[3b1cc8d]511 hash_table_get_inst(item, notification_t, htlink);
[5e801dc]512 return *id == notification->imethod;
[8820544]513}
514
515/** Operations for the notification hash table. */
516static hash_table_ops_t notification_hash_table_ops = {
517 .hash = notification_hash,
518 .key_hash = notification_key_hash,
519 .key_equal = notification_key_equal,
520 .equal = NULL,
521 .remove_callback = NULL
522};
523
[e70bfa5]524/** Try to route a call to an appropriate connection fibril.
[80649a91]525 *
[36c9234]526 * If the proper connection fibril is found, a message with the call is added to
527 * its message queue. If the fibril was not active, it is activated and all
528 * timeouts are unregistered.
529 *
[061274f]530 * @param call Data of the incoming call.
[c07544d3]531 *
[1de92fb0]532 * @return EOK if the call was successfully passed to the respective fibril.
533 * @return ENOENT if the call doesn't match any connection.
534 * @return Other error code if routing failed for other reasons.
[36c9234]535 *
[80649a91]536 */
[1de92fb0]537static errno_t route_call(ipc_call_t *call)
[450cd3a]538{
[79ae36dd]539 assert(call);
[a35b458]540
[6769005]541 connection_t *conn = (connection_t *) call->request_label;
[a35b458]542
[6769005]543 if (!conn)
[1de92fb0]544 return ENOENT;
[a35b458]545
[6769005]546 assert(conn->msg_channel);
[a35b458]547
[1de92fb0]548 errno_t rc = mpsc_send(conn->msg_channel, call);
[a35b458]549
[fafb8e5]550 if (ipc_get_imethod(call) == IPC_M_PHONE_HUNGUP) {
[1de92fb0]551 /* Close the channel, but let the connection fibril answer. */
552 mpsc_close(conn->msg_channel);
553 // FIXME: Ideally, we should be able to discard/answer the
554 // hungup message here and just close the channel without
555 // passing it out. Unfortunatelly, somehow that breaks
556 // handling of CPU exceptions.
557 }
[a35b458]558
[1de92fb0]559 return rc;
[c07544d3]560}
[80649a91]561
[3b1cc8d]562/** Function implementing the notification handler fibril. Never returns. */
563static errno_t notification_fibril_func(void *arg)
564{
565 (void) arg;
566
567 while (true) {
568 fibril_semaphore_down(&notification_semaphore);
569
[9bde0d5]570 fibril_rmutex_lock(&notification_mutex);
[3b1cc8d]571
572 /*
573 * The semaphore ensures that if we get this far,
574 * the queue must be non-empty.
575 */
576 assert(!list_empty(&notification_queue));
577
578 notification_t *notification = list_get_instance(
579 list_first(&notification_queue), notification_t, qlink);
580
581 async_notification_handler_t handler = notification->handler;
582 void *arg = notification->arg;
583
[8dab988]584 notification_msg_t *m = list_pop(&notification->msg_list,
585 notification_msg_t, link);
586 assert(m);
587 ipc_call_t calldata = m->calldata;
588
589 notification_freelist_used--;
590
591 if (notification_freelist_total > 64 &&
592 notification_freelist_total > 2 * notification_freelist_used) {
593 /* Going to free the structure if we have too much. */
594 notification_freelist_total--;
595 } else {
596 /* Otherwise add to freelist. */
597 list_append(&m->link, &notification_freelist);
598 m = NULL;
599 }
[3b1cc8d]600
[8dab988]601 if (list_empty(&notification->msg_list))
602 list_remove(&notification->qlink);
[3b1cc8d]603
[9bde0d5]604 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]605
606 if (handler)
607 handler(&calldata, arg);
[8dab988]608
609 free(m);
[3b1cc8d]610 }
611
612 /* Not reached. */
613 return EOK;
614}
615
616/**
617 * Creates a new dedicated fibril for handling notifications.
618 * By default, there is one such fibril. This function can be used to
619 * create more in order to increase the number of notification that can
620 * be processed concurrently.
621 *
622 * Currently, there is no way to destroy those fibrils after they are created.
623 */
624errno_t async_spawn_notification_handler(void)
625{
626 fid_t f = fibril_create(notification_fibril_func, NULL);
627 if (f == 0)
628 return ENOMEM;
629
630 fibril_add_ready(f);
631 return EOK;
632}
633
634/** Queue notification.
[c07544d3]635 *
[c170438]636 * @param call Data of the incoming call.
[58563585]637 *
[c07544d3]638 */
[3b1cc8d]639static void queue_notification(ipc_call_t *call)
[c07544d3]640{
[c170438]641 assert(call);
[a35b458]642
[9bde0d5]643 fibril_rmutex_lock(&notification_mutex);
[a35b458]644
[8dab988]645 notification_msg_t *m = list_pop(&notification_freelist,
646 notification_msg_t, link);
647
648 if (!m) {
[9bde0d5]649 fibril_rmutex_unlock(&notification_mutex);
[8dab988]650 m = malloc(sizeof(notification_msg_t));
651 if (!m) {
652 DPRINTF("Out of memory.\n");
653 abort();
654 }
655
[9bde0d5]656 fibril_rmutex_lock(&notification_mutex);
[8dab988]657 notification_freelist_total++;
658 }
659
[95a47b0]660 sysarg_t imethod = ipc_get_imethod(call);
661 ht_link_t *link = hash_table_find(&notification_hash_table, &imethod);
[3b1cc8d]662 if (!link) {
663 /* Invalid notification. */
664 // TODO: Make sure this can't happen and turn it into assert.
[8dab988]665 notification_freelist_total--;
[9bde0d5]666 fibril_rmutex_unlock(&notification_mutex);
[8dab988]667 free(m);
[3b1cc8d]668 return;
[8820544]669 }
[a35b458]670
[3b1cc8d]671 notification_t *notification =
672 hash_table_get_inst(link, notification_t, htlink);
673
[8dab988]674 notification_freelist_used++;
675 m->calldata = *call;
676 list_append(&m->link, &notification->msg_list);
[3b1cc8d]677
[8dab988]678 if (!link_in_use(&notification->qlink))
679 list_append(&notification->qlink, &notification_queue);
[3b1cc8d]680
[9bde0d5]681 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]682
683 fibril_semaphore_up(&notification_semaphore);
684}
685
686/**
687 * Creates a new notification structure and inserts it into the hash table.
688 *
689 * @param handler Function to call when notification is received.
690 * @param arg Argument for the handler function.
691 * @return The newly created notification structure.
692 */
693static notification_t *notification_create(async_notification_handler_t handler, void *arg)
694{
695 notification_t *notification = calloc(1, sizeof(notification_t));
696 if (!notification)
697 return NULL;
698
699 notification->handler = handler;
700 notification->arg = arg;
701
[8dab988]702 list_initialize(&notification->msg_list);
703
[3b1cc8d]704 fid_t fib = 0;
705
[9bde0d5]706 fibril_rmutex_lock(&notification_mutex);
[3b1cc8d]707
708 if (notification_avail == 0) {
709 /* Attempt to create the first handler fibril. */
710 fib = fibril_create(notification_fibril_func, NULL);
711 if (fib == 0) {
[9bde0d5]712 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]713 free(notification);
714 return NULL;
715 }
716 }
717
718 sysarg_t imethod = notification_avail;
719 notification_avail++;
720
721 notification->imethod = imethod;
722 hash_table_insert(&notification_hash_table, &notification->htlink);
723
[9bde0d5]724 fibril_rmutex_unlock(&notification_mutex);
[3b1cc8d]725
726 if (imethod == 0) {
727 assert(fib);
728 fibril_add_ready(fib);
729 }
[a35b458]730
[3b1cc8d]731 return notification;
[80649a91]732}
733
[8820544]734/** Subscribe to IRQ notification.
735 *
736 * @param inr IRQ number.
737 * @param handler Notification handler.
738 * @param data Notification handler client data.
739 * @param ucode Top-half pseudocode handler.
740 *
[071a1ddb]741 * @param[out] handle IRQ capability handle on success.
742 *
[cde999a]743 * @return An error code.
[8820544]744 *
745 */
[b7fd2a0]746errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
[eadaeae8]747 void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
[8820544]748{
[3b1cc8d]749 notification_t *notification = notification_create(handler, data);
[8820544]750 if (!notification)
751 return ENOMEM;
[a35b458]752
[eadaeae8]753 cap_irq_handle_t ihandle;
[3b1cc8d]754 errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
755 &ihandle);
[071a1ddb]756 if (rc == EOK && handle != NULL) {
[eadaeae8]757 *handle = ihandle;
[9233e9d]758 }
[071a1ddb]759 return rc;
[8820544]760}
761
762/** Unsubscribe from IRQ notification.
763 *
[eadaeae8]764 * @param handle IRQ capability handle.
[8820544]765 *
[cde999a]766 * @return Zero on success or an error code.
[8820544]767 *
768 */
[eadaeae8]769errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
[8820544]770{
771 // TODO: Remove entry from hash table
772 // to avoid memory leak
[a35b458]773
[eadaeae8]774 return ipc_irq_unsubscribe(ihandle);
[8820544]775}
776
777/** Subscribe to event notifications.
778 *
779 * @param evno Event type to subscribe.
780 * @param handler Notification handler.
781 * @param data Notification handler client data.
782 *
[cde999a]783 * @return Zero on success or an error code.
[8820544]784 *
785 */
[b7fd2a0]786errno_t async_event_subscribe(event_type_t evno,
[8820544]787 async_notification_handler_t handler, void *data)
788{
[3b1cc8d]789 notification_t *notification = notification_create(handler, data);
[8820544]790 if (!notification)
791 return ENOMEM;
[a35b458]792
[3b1cc8d]793 return ipc_event_subscribe(evno, notification->imethod);
[8820544]794}
795
796/** Subscribe to task event notifications.
797 *
798 * @param evno Event type to subscribe.
799 * @param handler Notification handler.
800 * @param data Notification handler client data.
801 *
[cde999a]802 * @return Zero on success or an error code.
[8820544]803 *
804 */
[b7fd2a0]805errno_t async_event_task_subscribe(event_task_type_t evno,
[8820544]806 async_notification_handler_t handler, void *data)
807{
[3b1cc8d]808 notification_t *notification = notification_create(handler, data);
[8820544]809 if (!notification)
810 return ENOMEM;
[a35b458]811
[3b1cc8d]812 return ipc_event_task_subscribe(evno, notification->imethod);
[8820544]813}
814
815/** Unmask event notifications.
816 *
817 * @param evno Event type to unmask.
818 *
819 * @return Value returned by the kernel.
820 *
821 */
[b7fd2a0]822errno_t async_event_unmask(event_type_t evno)
[8820544]823{
824 return ipc_event_unmask(evno);
825}
826
827/** Unmask task event notifications.
828 *
829 * @param evno Event type to unmask.
830 *
831 * @return Value returned by the kernel.
832 *
833 */
[b7fd2a0]834errno_t async_event_task_unmask(event_task_type_t evno)
[8820544]835{
836 return ipc_event_task_unmask(evno);
837}
838
[e70bfa5]839/** Return new incoming message for the current (fibril-local) connection.
840 *
[984a9ba]841 * @param call Storage where the incoming call data will be stored.
842 * @param usecs Timeout in microseconds. Zero denotes no timeout.
843 *
844 * @return If no timeout was specified, then true is returned.
845 * @return If a timeout is specified, then true is returned unless
846 * the timeout expires prior to receiving a message.
[e70bfa5]847 *
848 */
[bd41ac52]849bool async_get_call_timeout(ipc_call_t *call, usec_t usecs)
[80649a91]850{
[79ae36dd]851 assert(call);
852 assert(fibril_connection);
[a35b458]853
[bd41ac52]854 struct timespec ts;
855 struct timespec *expires = NULL;
[49d072e]856 if (usecs) {
[bd41ac52]857 getuptime(&ts);
858 ts_add_diff(&ts, USEC2NSEC(usecs));
859 expires = &ts;
[514d561]860 }
861
[1de92fb0]862 errno_t rc = mpsc_receive(fibril_connection->msg_channel,
863 call, expires);
[a35b458]864
[1de92fb0]865 if (rc == ETIMEOUT)
866 return false;
[a35b458]867
[1de92fb0]868 if (rc != EOK) {
869 /*
870 * The async_get_call_timeout() interface doesn't support
871 * propagating errors. Return a null call instead.
872 */
[514d561]873
[1de92fb0]874 memset(call, 0, sizeof(ipc_call_t));
[fafb8e5]875 ipc_set_imethod(call, IPC_M_PHONE_HUNGUP);
[de9a18e]876 call->cap_handle = CAP_NIL;
[450cd3a]877 }
[a35b458]878
[984a9ba]879 return true;
[80649a91]880}
881
[4f13e19]882bool async_get_call(ipc_call_t *call)
883{
884 return async_get_call_timeout(call, 0);
885}
886
[455f190]887void *async_get_client_data(void)
888{
889 assert(fibril_connection);
890 return fibril_connection->client->data;
891}
892
[e2ab36f1]893void *async_get_client_data_by_id(task_id_t client_id)
[455f190]894{
[e2ab36f1]895 client_t *client = async_client_get(client_id, false);
[455f190]896 if (!client)
897 return NULL;
[a35b458]898
[455f190]899 if (!client->data) {
900 async_client_put(client);
901 return NULL;
902 }
[a35b458]903
[455f190]904 return client->data;
905}
906
[e2ab36f1]907void async_put_client_data_by_id(task_id_t client_id)
[455f190]908{
[e2ab36f1]909 client_t *client = async_client_get(client_id, false);
[a35b458]910
[455f190]911 assert(client);
912 assert(client->data);
[a35b458]913
[cdc8ee2d]914 /* Drop the reference we got in async_get_client_data_by_hash(). */
915 async_client_put(client);
[a35b458]916
[cdc8ee2d]917 /* Drop our own reference we got at the beginning of this function. */
[455f190]918 async_client_put(client);
919}
920
[36c9234]921/** Handle a call that was received.
922 *
923 * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
924 * Otherwise the call is routed to its connection fibril.
925 *
[061274f]926 * @param call Data of the incoming call.
[6b21292]927 *
[36c9234]928 */
[061274f]929static void handle_call(ipc_call_t *call)
[80649a91]930{
[79ae36dd]931 assert(call);
[a35b458]932
[d054ad3]933 if (call->flags & IPC_CALL_ANSWERED) {
934 /* Answer to a call made by us. */
935 async_reply_received(call);
[e768aea]936 return;
[d054ad3]937 }
[e768aea]938
[061274f]939 if (call->cap_handle == CAP_NIL) {
[e768aea]940 if (call->flags & IPC_CALL_NOTIF) {
941 /* Kernel notification */
942 queue_notification(call);
943 }
[47b7006]944 return;
[6b21292]945 }
[a35b458]946
[566992e1]947 /* New connection */
[fafb8e5]948 if (ipc_get_imethod(call) == IPC_M_CONNECT_ME_TO) {
[6769005]949 connection_t *conn = calloc(1, sizeof(*conn));
950 if (!conn) {
951 ipc_answer_0(call->cap_handle, ENOMEM);
952 return;
953 }
954
[fafb8e5]955 iface_t iface = (iface_t) ipc_get_arg1(call);
[a35b458]956
[49a796f1]957 // TODO: Currently ignores all ports but the first one.
958 void *data;
959 async_port_handler_t handler =
960 async_get_port_handler(iface, 0, &data);
[a35b458]961
[6769005]962 async_new_connection(conn, call->task_id, call, handler, data);
[566992e1]963 return;
964 }
[a35b458]965
[6769005]966 /* Route the call according to its request label */
[1de92fb0]967 errno_t rc = route_call(call);
[0a8f070]968 if (rc == EOK) {
[47b7006]969 return;
[0a8f070]970 } else if (implicit_connection != NULL) {
971 async_new_connection(call->in_task_id, call->in_phone_hash,
972 callid, call, implicit_connection, NULL);
973 return;
974 }
[a35b458]975
[1de92fb0]976 // TODO: Log the error.
977
978 if (call->cap_handle != CAP_NIL)
979 /* Unknown call from unknown phone - hang it up */
980 ipc_answer_0(call->cap_handle, EHANGUP);
[450cd3a]981}
982
[36c9234]983/** Endless loop dispatching incoming calls and answers.
984 *
[c07544d3]985 * @return Never returns.
986 *
[36c9234]987 */
[b7fd2a0]988static errno_t async_manager_worker(void)
[80649a91]989{
[514d561]990 ipc_call_t call;
991 errno_t rc;
[a35b458]992
[514d561]993 while (true) {
994 rc = fibril_ipc_wait(&call, NULL);
[acf6b55]995 if (rc == EOK)
996 handle_call(&call);
[80649a91]997 }
[01c3bb4]998
[a46da63]999 return 0;
[80649a91]1000}
1001
[36c9234]1002/** Function to start async_manager as a standalone fibril.
[c07544d3]1003 *
[36c9234]1004 * When more kernel threads are used, one async manager should exist per thread.
1005 *
[c07544d3]1006 * @param arg Unused.
1007 * @return Never returns.
[36c9234]1008 *
[a2cd194]1009 */
[b7fd2a0]1010static errno_t async_manager_fibril(void *arg)
[80649a91]1011{
[085bd54]1012 async_manager_worker();
[a46da63]1013 return 0;
[80649a91]1014}
[450cd3a]1015
[36c9234]1016/** Add one manager to manager list. */
[fec7ba0]1017static fid_t async_create_manager(void)
[450cd3a]1018{
[c170438]1019 fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
[514d561]1020 fibril_start(fid);
1021 return fid;
[80649a91]1022}
1023
[36c9234]1024/** Initialize the async framework.
1025 *
1026 */
[49a796f1]1027void __async_server_init(void)
[80649a91]1028{
[45c8eea]1029 if (fibril_rmutex_initialize(&client_mutex) != EOK)
1030 abort();
1031 if (fibril_rmutex_initialize(&notification_mutex) != EOK)
1032 abort();
1033
[062d900]1034 if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
[47b7006]1035 abort();
[a35b458]1036
[8820544]1037 if (!hash_table_create(&notification_hash_table, 0, 0,
1038 &notification_hash_table_ops))
1039 abort();
[514d561]1040
1041 async_create_manager();
[49a796f1]1042}
[a35b458]1043
[25f6bddb]1044void __async_server_fini(void)
1045{
1046 fibril_rmutex_destroy(&client_mutex);
1047 fibril_rmutex_destroy(&notification_mutex);
1048}
1049
[beb83c1]1050errno_t async_accept_0(ipc_call_t *call)
1051{
[d57c7c2]1052 cap_call_handle_t chandle = call->cap_handle;
1053 assert(chandle != CAP_NIL);
1054 call->cap_handle = CAP_NIL;
1055 return ipc_answer_5(chandle, EOK, 0, 0, 0, 0, async_get_label());
[beb83c1]1056}
1057
[984a9ba]1058errno_t async_answer_0(ipc_call_t *call, errno_t retval)
[49a796f1]1059{
[d57c7c2]1060 cap_call_handle_t chandle = call->cap_handle;
1061 assert(chandle != CAP_NIL);
1062 call->cap_handle = CAP_NIL;
1063 return ipc_answer_0(chandle, retval);
[450cd3a]1064}
[01ff41c]1065
[984a9ba]1066errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
[01ff41c]1067{
[d57c7c2]1068 cap_call_handle_t chandle = call->cap_handle;
1069 assert(chandle != CAP_NIL);
1070 call->cap_handle = CAP_NIL;
1071 return ipc_answer_1(chandle, retval, arg1);
[49a796f1]1072}
[a35b458]1073
[984a9ba]1074errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1075 sysarg_t arg2)
1076{
[d57c7c2]1077 cap_call_handle_t chandle = call->cap_handle;
1078 assert(chandle != CAP_NIL);
1079 call->cap_handle = CAP_NIL;
1080 return ipc_answer_2(chandle, retval, arg1, arg2);
[49a796f1]1081}
[a35b458]1082
[984a9ba]1083errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1084 sysarg_t arg2, sysarg_t arg3)
1085{
[d57c7c2]1086 cap_call_handle_t chandle = call->cap_handle;
1087 assert(chandle != CAP_NIL);
1088 call->cap_handle = CAP_NIL;
1089 return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
[49a796f1]1090}
[a35b458]1091
[984a9ba]1092errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1093 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1094{
[d57c7c2]1095 cap_call_handle_t chandle = call->cap_handle;
1096 assert(chandle != CAP_NIL);
1097 call->cap_handle = CAP_NIL;
1098 return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
[49a796f1]1099}
[a35b458]1100
[984a9ba]1101errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
[49a796f1]1102 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
1103{
[d57c7c2]1104 cap_call_handle_t chandle = call->cap_handle;
1105 assert(chandle != CAP_NIL);
1106 call->cap_handle = CAP_NIL;
1107 return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
[49a796f1]1108}
[a35b458]1109
[4f13e19]1110static errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1111 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1112{
[984a9ba]1113 assert(call);
1114
[d57c7c2]1115 cap_call_handle_t chandle = call->cap_handle;
1116 assert(chandle != CAP_NIL);
1117 call->cap_handle = CAP_NIL;
1118
[49a796f1]1119 if (exch == NULL)
1120 return ENOENT;
[a35b458]1121
[d57c7c2]1122 return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2,
1123 mode);
[49a796f1]1124}
[a35b458]1125
[4f13e19]1126static errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
[49a796f1]1127 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1128 sysarg_t arg4, sysarg_t arg5, unsigned int mode)
1129{
[984a9ba]1130 assert(call);
1131
[d57c7c2]1132 cap_call_handle_t chandle = call->cap_handle;
1133 assert(chandle != CAP_NIL);
1134 call->cap_handle = CAP_NIL;
1135
[49a796f1]1136 if (exch == NULL)
1137 return ENOENT;
[a35b458]1138
[d57c7c2]1139 return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
1140 arg4, arg5, mode);
[01ff41c]1141}
1142
[4f13e19]1143errno_t async_forward_0(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1144 unsigned int mode)
1145{
1146 return async_forward_fast(call, exch, imethod, 0, 0, mode);
1147}
1148
1149errno_t async_forward_1(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1150 sysarg_t arg1, unsigned int mode)
1151{
1152 return async_forward_fast(call, exch, imethod, arg1, 0, mode);
1153}
1154
1155errno_t async_forward_2(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1156 sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1157{
1158 return async_forward_fast(call, exch, imethod, arg1, arg2, mode);
1159}
1160
1161errno_t async_forward_3(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1162 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, unsigned int mode)
1163{
1164 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, 0, 0,
1165 mode);
1166}
1167
1168errno_t async_forward_4(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1169 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1170 unsigned int mode)
1171{
1172 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
1173 0, mode);
1174}
1175
1176errno_t async_forward_5(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
1177 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
1178 unsigned int mode)
1179{
1180 return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
1181 arg5, mode);
1182}
1183
[49a796f1]1184/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
[36c9234]1185 *
[49a796f1]1186 * Ask through phone for a new connection to some service.
[01ff41c]1187 *
[9b1baac]1188 * @param exch Exchange for sending the message.
1189 * @param iface Callback interface.
1190 * @param arg2 User defined argument.
1191 * @param arg3 User defined argument.
[c07544d3]1192 *
[49a796f1]1193 * @return Zero on success or an error code.
[36c9234]1194 *
[01ff41c]1195 */
[9b1baac]1196errno_t async_connect_to_me(async_exch_t *exch, iface_t iface, sysarg_t arg2,
[49a796f1]1197 sysarg_t arg3)
[01ff41c]1198{
[79ae36dd]1199 if (exch == NULL)
[49a796f1]1200 return ENOENT;
[a35b458]1201
[6769005]1202 sysarg_t label = 0;
1203 errno_t rc = async_req_5_0(exch, IPC_M_CONNECT_TO_ME, iface, arg2, arg3,
1204 0, label);
[a35b458]1205
[6769005]1206 return rc;
[49a796f1]1207}
[a35b458]1208
[49a796f1]1209/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
[0da4e41]1210 *
[47b7006]1211 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN
1212 * calls so that the user doesn't have to remember the meaning of each IPC
1213 * argument.
[0da4e41]1214 *
1215 * So far, this wrapper is to be used from within a connection fibril.
1216 *
[984a9ba]1217 * @param call Storage for the data of the IPC_M_SHARE_IN call.
1218 * @param size Destination address space area size.
[47b7006]1219 *
1220 * @return True on success, false on failure.
[0da4e41]1221 *
1222 */
[984a9ba]1223bool async_share_in_receive(ipc_call_t *call, size_t *size)
[0da4e41]1224{
[984a9ba]1225 assert(call);
[0da4e41]1226 assert(size);
[a35b458]1227
[984a9ba]1228 async_get_call(call);
[a35b458]1229
[fafb8e5]1230 if (ipc_get_imethod(call) != IPC_M_SHARE_IN)
[47b7006]1231 return false;
[a35b458]1232
[fafb8e5]1233 *size = (size_t) ipc_get_arg1(call);
[47b7006]1234 return true;
[0da4e41]1235}
1236
1237/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
1238 *
[fbcdeb8]1239 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
[47b7006]1240 * calls so that the user doesn't have to remember the meaning of each IPC
1241 * argument.
[0da4e41]1242 *
[f8048d1]1243 * @param call IPC_M_SHARE_IN call to answer.
[984a9ba]1244 * @param src Source address space base.
1245 * @param flags Flags to be used for sharing. Bits can be only cleared.
[47b7006]1246 *
1247 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1248 *
1249 */
[984a9ba]1250errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
[0da4e41]1251{
[984a9ba]1252 assert(call);
1253
[d57c7c2]1254 cap_call_handle_t chandle = call->cap_handle;
1255 assert(chandle != CAP_NIL);
1256 call->cap_handle = CAP_NIL;
1257
1258 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) flags);
[0da4e41]1259}
1260
1261/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
1262 *
[47b7006]1263 * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT
1264 * calls so that the user doesn't have to remember the meaning of each IPC
1265 * argument.
[0da4e41]1266 *
1267 * So far, this wrapper is to be used from within a connection fibril.
1268 *
[984a9ba]1269 * @param call Storage for the data of the IPC_M_SHARE_OUT call.
1270 * @param size Storage for the source address space area size.
1271 * @param flags Storage for the sharing flags.
[47b7006]1272 *
1273 * @return True on success, false on failure.
[0da4e41]1274 *
1275 */
[984a9ba]1276bool async_share_out_receive(ipc_call_t *call, size_t *size,
[01c3bb4]1277 unsigned int *flags)
[0da4e41]1278{
[984a9ba]1279 assert(call);
[0da4e41]1280 assert(size);
1281 assert(flags);
[a35b458]1282
[984a9ba]1283 async_get_call(call);
[a35b458]1284
[fafb8e5]1285 if (ipc_get_imethod(call) != IPC_M_SHARE_OUT)
[47b7006]1286 return false;
[a35b458]1287
[fafb8e5]1288 *size = (size_t) ipc_get_arg2(call);
1289 *flags = (unsigned int) ipc_get_arg3(call);
[47b7006]1290 return true;
[0da4e41]1291}
1292
1293/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
1294 *
[47b7006]1295 * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT
1296 * calls so that the user doesn't have to remember the meaning of each IPC
1297 * argument.
[0da4e41]1298 *
[f8048d1]1299 * @param call IPC_M_SHARE_OUT call to answer.
[984a9ba]1300 * @param dst Address of the storage for the destination address space area
1301 * base address.
[47b7006]1302 *
[01c3bb4]1303 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1304 *
1305 */
[984a9ba]1306errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
[0da4e41]1307{
[984a9ba]1308 assert(call);
[0da4e41]1309
[d57c7c2]1310 cap_call_handle_t chandle = call->cap_handle;
1311 assert(chandle != CAP_NIL);
1312 call->cap_handle = CAP_NIL;
1313
1314 return ipc_answer_2(chandle, EOK, (sysarg_t) __progsymbols.end,
[984a9ba]1315 (sysarg_t) dst);
[d768d4c8]1316}
1317
1318/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
1319 *
1320 * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
1321 * calls so that the user doesn't have to remember the meaning of each IPC
1322 * argument.
1323 *
1324 * So far, this wrapper is to be used from within a connection fibril.
1325 *
[984a9ba]1326 * @param call Storage for the data of the IPC_M_DATA_READ.
1327 * @param size Storage for the maximum size. Can be NULL.
[d768d4c8]1328 *
1329 * @return True on success, false on failure.
1330 *
1331 */
[984a9ba]1332bool async_data_read_receive(ipc_call_t *call, size_t *size)
[0da4e41]1333{
[984a9ba]1334 assert(call);
[a35b458]1335
[984a9ba]1336 async_get_call(call);
[a35b458]1337
[fafb8e5]1338 if (ipc_get_imethod(call) != IPC_M_DATA_READ)
[47b7006]1339 return false;
[a35b458]1340
[0da4e41]1341 if (size)
[fafb8e5]1342 *size = (size_t) ipc_get_arg2(call);
[a35b458]1343
[47b7006]1344 return true;
[0da4e41]1345}
1346
1347/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
1348 *
[47b7006]1349 * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ
1350 * calls so that the user doesn't have to remember the meaning of each IPC
1351 * argument.
[0da4e41]1352 *
[984a9ba]1353 * @param call IPC_M_DATA_READ call to answer.
1354 * @param src Source address for the IPC_M_DATA_READ call.
1355 * @param size Size for the IPC_M_DATA_READ call. Can be smaller than
1356 * the maximum size announced by the sender.
[47b7006]1357 *
[01c3bb4]1358 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1359 *
1360 */
[984a9ba]1361errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
[0da4e41]1362{
[984a9ba]1363 assert(call);
1364
[d57c7c2]1365 cap_call_handle_t chandle = call->cap_handle;
1366 assert(chandle != CAP_NIL);
1367 call->cap_handle = CAP_NIL;
1368
1369 return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
[0da4e41]1370}
1371
[b4cbef1]1372/** Wrapper for forwarding any read request
1373 *
1374 */
[4f13e19]1375static errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
[79ae36dd]1376 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1377 ipc_call_t *dataptr)
[b4cbef1]1378{
[79ae36dd]1379 if (exch == NULL)
1380 return ENOENT;
[a35b458]1381
[984a9ba]1382 ipc_call_t call;
1383 if (!async_data_read_receive(&call, NULL)) {
1384 async_answer_0(&call, EINVAL);
[b4cbef1]1385 return EINVAL;
1386 }
[a35b458]1387
[4f13e19]1388 aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1389 dataptr);
1390 if (msg == 0) {
[984a9ba]1391 async_answer_0(&call, EINVAL);
[b4cbef1]1392 return EINVAL;
1393 }
[a35b458]1394
[984a9ba]1395 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1396 IPC_FF_ROUTE_FROM_ME);
1397 if (retval != EOK) {
[ab9f443]1398 async_forget(msg);
[984a9ba]1399 async_answer_0(&call, retval);
[b4cbef1]1400 return retval;
1401 }
[a35b458]1402
[b7fd2a0]1403 errno_t rc;
[b4cbef1]1404 async_wait_for(msg, &rc);
[a35b458]1405
[b7fd2a0]1406 return (errno_t) rc;
[b4cbef1]1407}
1408
[4f13e19]1409errno_t async_data_read_forward_0_0(async_exch_t *exch, sysarg_t imethod)
1410{
1411 return async_data_read_forward_fast(exch, imethod, 0, 0, 0, 0, NULL);
1412}
1413
1414errno_t async_data_read_forward_1_0(async_exch_t *exch, sysarg_t imethod,
1415 sysarg_t arg1)
1416{
1417 return async_data_read_forward_fast(exch, imethod, arg1, 0, 0, 0, NULL);
1418}
1419
1420errno_t async_data_read_forward_2_0(async_exch_t *exch, sysarg_t imethod,
1421 sysarg_t arg1, sysarg_t arg2)
1422{
1423 return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
1424 0, NULL);
1425}
1426
1427errno_t async_data_read_forward_3_0(async_exch_t *exch, sysarg_t imethod,
1428 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1429{
1430 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1431 0, NULL);
1432}
1433
1434errno_t async_data_read_forward_4_0(async_exch_t *exch, sysarg_t imethod,
1435 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1436{
1437 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1438 arg4, NULL);
1439}
1440
1441errno_t async_data_read_forward_0_1(async_exch_t *exch, sysarg_t imethod,
1442 ipc_call_t *dataptr)
1443{
1444 return async_data_read_forward_fast(exch, imethod, 0, 0, 0,
1445 0, dataptr);
1446}
1447
1448errno_t async_data_read_forward_1_1(async_exch_t *exch, sysarg_t imethod,
1449 sysarg_t arg1, ipc_call_t *dataptr)
1450{
1451 return async_data_read_forward_fast(exch, imethod, arg1, 0, 0,
1452 0, dataptr);
1453}
1454
1455errno_t async_data_read_forward_2_1(async_exch_t *exch, sysarg_t imethod,
1456 sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
1457{
1458 return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
1459 0, dataptr);
1460}
1461
1462errno_t async_data_read_forward_3_1(async_exch_t *exch, sysarg_t imethod,
1463 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
1464{
1465 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1466 0, dataptr);
1467}
1468
1469errno_t async_data_read_forward_4_1(async_exch_t *exch, sysarg_t imethod,
1470 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1471 ipc_call_t *dataptr)
1472{
1473 return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
1474 arg4, dataptr);
1475}
1476
[0da4e41]1477/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
1478 *
[47b7006]1479 * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
1480 * calls so that the user doesn't have to remember the meaning of each IPC
1481 * argument.
[0da4e41]1482 *
1483 * So far, this wrapper is to be used from within a connection fibril.
1484 *
[984a9ba]1485 * @param call Storage for the data of the IPC_M_DATA_WRITE.
1486 * @param size Storage for the suggested size. May be NULL.
[5ae1c51]1487 *
1488 * @return True on success, false on failure.
1489 *
1490 */
[984a9ba]1491bool async_data_write_receive(ipc_call_t *call, size_t *size)
[0da4e41]1492{
[984a9ba]1493 assert(call);
[a35b458]1494
[984a9ba]1495 async_get_call(call);
[a35b458]1496
[fafb8e5]1497 if (ipc_get_imethod(call) != IPC_M_DATA_WRITE)
[47b7006]1498 return false;
[a35b458]1499
[0da4e41]1500 if (size)
[fafb8e5]1501 *size = (size_t) ipc_get_arg2(call);
[a35b458]1502
[47b7006]1503 return true;
[0da4e41]1504}
1505
1506/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
1507 *
[47b7006]1508 * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE
1509 * calls so that the user doesn't have to remember the meaning of each IPC
1510 * argument.
[0da4e41]1511 *
[984a9ba]1512 * @param call IPC_M_DATA_WRITE call to answer.
1513 * @param dst Final destination address for the IPC_M_DATA_WRITE call.
1514 * @param size Final size for the IPC_M_DATA_WRITE call.
[b4cbef1]1515 *
[01c3bb4]1516 * @return Zero on success or a value from @ref errno.h on failure.
[0da4e41]1517 *
1518 */
[984a9ba]1519errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
[0da4e41]1520{
[984a9ba]1521 assert(call);
1522
1523 return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
[0da4e41]1524}
1525
[eda925a]1526/** Wrapper for receiving binary data or strings
[8aa42e3]1527 *
1528 * This wrapper only makes it more comfortable to use async_data_write_*
[eda925a]1529 * functions to receive binary data or strings.
[8aa42e3]1530 *
[472c09d]1531 * @param data Pointer to data pointer (which should be later disposed
1532 * by free()). If the operation fails, the pointer is not
1533 * touched.
[eda925a]1534 * @param nullterm If true then the received data is always zero terminated.
1535 * This also causes to allocate one extra byte beyond the
1536 * raw transmitted data.
[b4cbef1]1537 * @param min_size Minimum size (in bytes) of the data to receive.
[472c09d]1538 * @param max_size Maximum size (in bytes) of the data to receive. 0 means
1539 * no limit.
[eda925a]1540 * @param granulariy If non-zero then the size of the received data has to
[472c09d]1541 * be divisible by this value.
1542 * @param received If not NULL, the size of the received data is stored here.
[8aa42e3]1543 *
1544 * @return Zero on success or a value from @ref errno.h on failure.
1545 *
1546 */
[b7fd2a0]1547errno_t async_data_write_accept(void **data, const bool nullterm,
[eda925a]1548 const size_t min_size, const size_t max_size, const size_t granularity,
1549 size_t *received)
[8aa42e3]1550{
[79ae36dd]1551 assert(data);
[a35b458]1552
[984a9ba]1553 ipc_call_t call;
[8aa42e3]1554 size_t size;
[984a9ba]1555 if (!async_data_write_receive(&call, &size)) {
1556 async_answer_0(&call, EINVAL);
[8aa42e3]1557 return EINVAL;
1558 }
[a35b458]1559
[b4cbef1]1560 if (size < min_size) {
[984a9ba]1561 async_answer_0(&call, EINVAL);
[b4cbef1]1562 return EINVAL;
1563 }
[a35b458]1564
[8aa42e3]1565 if ((max_size > 0) && (size > max_size)) {
[984a9ba]1566 async_answer_0(&call, EINVAL);
[8aa42e3]1567 return EINVAL;
1568 }
[a35b458]1569
[472c09d]1570 if ((granularity > 0) && ((size % granularity) != 0)) {
[984a9ba]1571 async_answer_0(&call, EINVAL);
[472c09d]1572 return EINVAL;
1573 }
[a35b458]1574
[57dea62]1575 void *arg_data;
[a35b458]1576
[eda925a]1577 if (nullterm)
[57dea62]1578 arg_data = malloc(size + 1);
[eda925a]1579 else
[57dea62]1580 arg_data = malloc(size);
[a35b458]1581
[57dea62]1582 if (arg_data == NULL) {
[984a9ba]1583 async_answer_0(&call, ENOMEM);
[8aa42e3]1584 return ENOMEM;
1585 }
[a35b458]1586
[984a9ba]1587 errno_t rc = async_data_write_finalize(&call, arg_data, size);
[8aa42e3]1588 if (rc != EOK) {
[57dea62]1589 free(arg_data);
[8aa42e3]1590 return rc;
1591 }
[a35b458]1592
[eda925a]1593 if (nullterm)
[57dea62]1594 ((char *) arg_data)[size] = 0;
[a35b458]1595
[57dea62]1596 *data = arg_data;
[472c09d]1597 if (received != NULL)
1598 *received = size;
[a35b458]1599
[8aa42e3]1600 return EOK;
1601}
1602
[b4cbef1]1603/** Wrapper for voiding any data that is about to be received
1604 *
1605 * This wrapper can be used to void any pending data
1606 *
1607 * @param retval Error value from @ref errno.h to be returned to the caller.
1608 *
1609 */
[b7fd2a0]1610void async_data_write_void(errno_t retval)
[b4cbef1]1611{
[984a9ba]1612 ipc_call_t call;
1613 async_data_write_receive(&call, NULL);
1614 async_answer_0(&call, retval);
[b4cbef1]1615}
1616
1617/** Wrapper for forwarding any data that is about to be received
1618 *
1619 */
[4f13e19]1620static errno_t async_data_write_forward_fast(async_exch_t *exch,
1621 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
1622 sysarg_t arg4, ipc_call_t *dataptr)
[b4cbef1]1623{
[79ae36dd]1624 if (exch == NULL)
1625 return ENOENT;
[a35b458]1626
[984a9ba]1627 ipc_call_t call;
1628 if (!async_data_write_receive(&call, NULL)) {
1629 async_answer_0(&call, EINVAL);
[b4cbef1]1630 return EINVAL;
1631 }
[a35b458]1632
[4f13e19]1633 aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
[b4cbef1]1634 dataptr);
1635 if (msg == 0) {
[984a9ba]1636 async_answer_0(&call, EINVAL);
[b4cbef1]1637 return EINVAL;
1638 }
[a35b458]1639
[984a9ba]1640 errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
[b4cbef1]1641 IPC_FF_ROUTE_FROM_ME);
1642 if (retval != EOK) {
[ab9f443]1643 async_forget(msg);
[984a9ba]1644 async_answer_0(&call, retval);
[b4cbef1]1645 return retval;
1646 }
[a35b458]1647
[b7fd2a0]1648 errno_t rc;
[b4cbef1]1649 async_wait_for(msg, &rc);
[a35b458]1650
[b7fd2a0]1651 return (errno_t) rc;
[b4cbef1]1652}
1653
[4f13e19]1654errno_t async_data_write_forward_0_0(async_exch_t *exch, sysarg_t imethod)
1655{
1656 return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
1657 0, NULL);
1658}
1659
1660errno_t async_data_write_forward_1_0(async_exch_t *exch, sysarg_t imethod,
1661 sysarg_t arg1)
1662{
1663 return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
1664 0, NULL);
1665}
1666
1667errno_t async_data_write_forward_2_0(async_exch_t *exch, sysarg_t imethod,
1668 sysarg_t arg1, sysarg_t arg2)
1669{
1670 return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
1671 0, NULL);
1672}
1673
1674errno_t async_data_write_forward_3_0(async_exch_t *exch, sysarg_t imethod,
1675 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
1676{
1677 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1678 0, NULL);
1679}
1680
1681errno_t async_data_write_forward_4_0(async_exch_t *exch, sysarg_t imethod,
1682 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1683{
1684 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1685 arg4, NULL);
1686}
1687
1688errno_t async_data_write_forward_0_1(async_exch_t *exch, sysarg_t imethod,
1689 ipc_call_t *dataptr)
1690{
1691 return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
1692 0, dataptr);
1693}
1694
1695errno_t async_data_write_forward_1_1(async_exch_t *exch, sysarg_t imethod,
1696 sysarg_t arg1, ipc_call_t *dataptr)
1697{
1698 return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
1699 0, dataptr);
1700}
1701
1702errno_t async_data_write_forward_2_1(async_exch_t *exch, sysarg_t imethod,
1703 sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
1704{
1705 return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
1706 0, dataptr);
1707}
1708
1709errno_t async_data_write_forward_3_1(async_exch_t *exch, sysarg_t imethod,
1710 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
1711{
1712 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1713 0, dataptr);
1714}
1715
1716errno_t async_data_write_forward_4_1(async_exch_t *exch, sysarg_t imethod,
1717 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
1718 ipc_call_t *dataptr)
1719{
1720 return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
1721 arg4, dataptr);
1722}
1723
[79ae36dd]1724/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1725 *
1726 * If the current call is IPC_M_CONNECT_TO_ME then a new
1727 * async session is created for the accepted phone.
1728 *
1729 * @param mgmt Exchange management style.
1730 *
[8869f7b]1731 * @return New async session.
1732 * @return NULL on failure.
[79ae36dd]1733 *
1734 */
1735async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
1736{
1737 /* Accept the phone */
1738 ipc_call_t call;
[984a9ba]1739 async_get_call(&call);
1740
[fafb8e5]1741 cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(&call);
[a35b458]1742
[fafb8e5]1743 if ((ipc_get_imethod(&call) != IPC_M_CONNECT_TO_ME) ||
[bb97118]1744 !cap_handle_valid((phandle))) {
[984a9ba]1745 async_answer_0(&call, EINVAL);
[79ae36dd]1746 return NULL;
1747 }
[a35b458]1748
[012dd8e]1749 async_sess_t *sess = create_session(phandle, mgmt, 0, 0, 0);
[79ae36dd]1750 if (sess == NULL) {
[012dd8e]1751 ipc_hangup(phone);
1752 async_answer_0(&call, errno);
1753 } else {
1754 /* Acknowledge the connected phone */
1755 async_answer_0(&call, EOK);
[79ae36dd]1756 }
[a35b458]1757
[79ae36dd]1758 return sess;
1759}
1760
[8869f7b]1761/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
1762 *
1763 * If the call is IPC_M_CONNECT_TO_ME then a new
1764 * async session is created. However, the phone is
1765 * not accepted automatically.
1766 *
1767 * @param mgmt Exchange management style.
1768 * @param call Call data.
1769 *
1770 * @return New async session.
1771 * @return NULL on failure.
1772 * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
1773 *
1774 */
1775async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
1776{
[fafb8e5]1777 cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(call);
[a35b458]1778
[fafb8e5]1779 if ((ipc_get_imethod(call) != IPC_M_CONNECT_TO_ME) ||
[bb97118]1780 !cap_handle_valid((phandle)))
[8869f7b]1781 return NULL;
[a35b458]1782
[012dd8e]1783 return create_session(phandle, mgmt, 0, 0, 0);
[8869f7b]1784}
1785
[984a9ba]1786bool async_state_change_receive(ipc_call_t *call)
[2c4aa39]1787{
[984a9ba]1788 assert(call);
[a35b458]1789
[984a9ba]1790 async_get_call(call);
[a35b458]1791
[fafb8e5]1792 if (ipc_get_imethod(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
[2c4aa39]1793 return false;
[a35b458]1794
[2c4aa39]1795 return true;
1796}
1797
[984a9ba]1798errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
[2c4aa39]1799{
[984a9ba]1800 assert(call);
1801
[bb97118]1802 return async_answer_1(call, EOK, cap_handle_raw(other_exch->phone));
[2c4aa39]1803}
1804
[6b96dc06]1805__noreturn void async_manager(void)
[d73d992]1806{
[514d561]1807 fibril_event_t ever = FIBRIL_EVENT_INIT;
1808 fibril_wait_for(&ever);
[d73d992]1809 __builtin_unreachable();
1810}
1811
[a46da63]1812/** @}
[b2951e2]1813 */
Note: See TracBrowser for help on using the repository browser.