source: mainline/uspace/srv/net/udp/service.c@ abf2dfd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since abf2dfd was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
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.
27 */
28
29/** @addtogroup udp
30 * @{
31 */
32
33/**
34 * @file HelenOS service implementation
35 */
36
37#include <async.h>
38#include <errno.h>
39#include <inet/endpoint.h>
40#include <io/log.h>
41#include <ipc/services.h>
42#include <ipc/udp.h>
43#include <loc.h>
44#include <macros.h>
45#include <stdlib.h>
46
47#include "assoc.h"
48#include "msg.h"
49#include "service.h"
50#include "udp_type.h"
51
52#define NAME "udp"
53
54/** Maximum message size */
55#define MAX_MSG_SIZE DATA_XFER_LIMIT
56
57static void udp_cassoc_recv_msg(void *, inet_ep2_t *, udp_msg_t *);
58
59/** Callbacks to tie us to association layer */
60static udp_assoc_cb_t udp_cassoc_cb = {
61 .recv_msg = udp_cassoc_recv_msg
62};
63
64/** Add message to client receive queue.
65 *
66 * @param cassoc Client association
67 * @param epp Endpoint pair on which message was received
68 * @param msg Message
69 *
70 * @return EOK on success, ENOMEM if out of memory
71 */
72static int udp_cassoc_queue_msg(udp_cassoc_t *cassoc, inet_ep2_t *epp,
73 udp_msg_t *msg)
74{
75 udp_crcv_queue_entry_t *rqe;
76
77 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_cassoc_queue_msg(%p, %p, %p)",
78 cassoc, epp, msg);
79
80 rqe = calloc(1, sizeof(udp_crcv_queue_entry_t));
81 if (rqe == NULL)
82 return ENOMEM;
83
84 link_initialize(&rqe->link);
85 rqe->epp = *epp;
86 rqe->msg = msg;
87 rqe->cassoc = cassoc;
88
89// fibril_mutex_lock(&assoc->lock);
90 list_append(&rqe->link, &cassoc->client->crcv_queue);
91// fibril_mutex_unlock(&assoc->lock);
92
93// fibril_condvar_broadcast(&assoc->rcv_queue_cv);
94
95 return EOK;
96}
97
98/** Send 'data' event to client.
99 *
100 * @param client Client
101 */
102static void udp_ev_data(udp_client_t *client)
103{
104 async_exch_t *exch;
105
106 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ev_data()");
107
108 exch = async_exchange_begin(client->sess);
109 aid_t req = async_send_0(exch, UDP_EV_DATA, NULL);
110 async_exchange_end(exch);
111
112 async_forget(req);
113}
114
115/** Create client association.
116 *
117 * This effectively adds an association into a client's namespace.
118 *
119 * @param client Client
120 * @param assoc Association
121 * @param rcassoc Place to store pointer to new client association
122 *
123 * @return EOK on soccess, ENOMEM if out of memory
124 */
125static int udp_cassoc_create(udp_client_t *client, udp_assoc_t *assoc,
126 udp_cassoc_t **rcassoc)
127{
128 udp_cassoc_t *cassoc;
129 sysarg_t id;
130
131 cassoc = calloc(1, sizeof(udp_cassoc_t));
132 if (cassoc == NULL)
133 return ENOMEM;
134
135 /* Allocate new ID */
136 id = 0;
137 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
138 if (cassoc->id >= id)
139 id = cassoc->id + 1;
140 }
141
142 cassoc->id = id;
143 cassoc->client = client;
144 cassoc->assoc = assoc;
145
146 list_append(&cassoc->lclient, &client->cassoc);
147 *rcassoc = cassoc;
148 return EOK;
149}
150
151/** Destroy client association.
152 *
153 * @param cassoc Client association
154 */
155static void udp_cassoc_destroy(udp_cassoc_t *cassoc)
156{
157 list_remove(&cassoc->lclient);
158 free(cassoc);
159}
160
161/** Get client association by ID.
162 *
163 * @param client Client
164 * @param id Client association ID
165 * @param rcassoc Place to store pointer to client association
166 *
167 * @return EOK on success, ENOENT if no client association with the given ID
168 * is found.
169 */
170static int udp_cassoc_get(udp_client_t *client, sysarg_t id,
171 udp_cassoc_t **rcassoc)
172{
173 list_foreach (client->cassoc, lclient, udp_cassoc_t, cassoc) {
174 if (cassoc->id == id) {
175 *rcassoc = cassoc;
176 return EOK;
177 }
178 }
179
180 return ENOENT;
181}
182
183/** Message received on client association.
184 *
185 * Used as udp_assoc_cb.recv_msg callback.
186 *
187 * @param arg Callback argument, client association
188 * @param epp Endpoint pair where message was received
189 * @param msg Message
190 */
191static void udp_cassoc_recv_msg(void *arg, inet_ep2_t *epp, udp_msg_t *msg)
192{
193 udp_cassoc_t *cassoc = (udp_cassoc_t *) arg;
194
195 udp_cassoc_queue_msg(cassoc, epp, msg);
196 udp_ev_data(cassoc->client);
197}
198
199/** Create association.
200 *
201 * Handle client request to create association (with parameters unmarshalled).
202 *
203 * @param client UDP client
204 * @param epp Endpoint pair
205 * @param rassoc_id Place to store ID of new association
206 *
207 * @return EOK on success or negative error code
208 */
209static int udp_assoc_create_impl(udp_client_t *client, inet_ep2_t *epp,
210 sysarg_t *rassoc_id)
211{
212 udp_assoc_t *assoc;
213 udp_cassoc_t *cassoc;
214 int rc;
215
216 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
217
218 assoc = udp_assoc_new(epp, NULL, NULL);
219 if (assoc == NULL)
220 return EIO;
221
222 if (epp->local_link != 0)
223 udp_assoc_set_iplink(assoc, epp->local_link);
224
225 rc = udp_cassoc_create(client, assoc, &cassoc);
226 if (rc != EOK) {
227 assert(rc == ENOMEM);
228 udp_assoc_delete(assoc);
229 return ENOMEM;
230 }
231
232 assoc->cb = &udp_cassoc_cb;
233 assoc->cb_arg = cassoc;
234
235 rc = udp_assoc_add(assoc);
236 if (rc != EOK) {
237 udp_cassoc_destroy(cassoc);
238 udp_assoc_delete(assoc);
239 return rc;
240 }
241
242 *rassoc_id = cassoc->id;
243 return EOK;
244}
245
246/** Destroy association.
247 *
248 * Handle client request to destroy association (with parameters unmarshalled).
249 *
250 * @param client UDP client
251 * @param assoc_id Association ID
252 * @return EOK on success, ENOENT if no such association is found
253 */
254static int udp_assoc_destroy_impl(udp_client_t *client, sysarg_t assoc_id)
255{
256 udp_cassoc_t *cassoc;
257 int rc;
258
259 rc = udp_cassoc_get(client, assoc_id, &cassoc);
260 if (rc != EOK) {
261 assert(rc == ENOENT);
262 return ENOENT;
263 }
264
265 udp_assoc_remove(cassoc->assoc);
266 udp_assoc_reset(cassoc->assoc);
267 udp_assoc_delete(cassoc->assoc);
268 udp_cassoc_destroy(cassoc);
269 return EOK;
270}
271
272/** Send message via association.
273 *
274 * Handle client request to send message (with parameters unmarshalled).
275 *
276 * @param client UDP client
277 * @param assoc_id Association ID
278 * @param dest Destination endpoint or @c NULL to use the default from
279 * association
280 * @param data Message data
281 * @param size Message size
282 *
283 * @return EOK on success or negative error code
284 */
285static int udp_assoc_send_msg_impl(udp_client_t *client, sysarg_t assoc_id,
286 inet_ep_t *dest, void *data, size_t size)
287{
288 udp_msg_t msg;
289 udp_cassoc_t *cassoc;
290 int rc;
291
292 rc = udp_cassoc_get(client, assoc_id, &cassoc);
293 if (rc != EOK)
294 return rc;
295
296 msg.data = data;
297 msg.data_size = size;
298 rc = udp_assoc_send(cassoc->assoc, dest, &msg);
299 if (rc != EOK)
300 return rc;
301
302 return EOK;
303}
304
305/** Create callback session.
306 *
307 * Handle client request to create callback session.
308 *
309 * @param client UDP client
310 * @param iid Async request ID
311 * @param icall Async request data
312 */
313static void udp_callback_create_srv(udp_client_t *client, ipc_callid_t iid,
314 ipc_call_t *icall)
315{
316 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_callback_create_srv()");
317
318 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
319 if (sess == NULL) {
320 async_answer_0(iid, ENOMEM);
321 return;
322 }
323
324 client->sess = sess;
325 async_answer_0(iid, EOK);
326}
327
328/** Create association.
329 *
330 * Handle client request to create association.
331 *
332 * @param client UDP client
333 * @param iid Async request ID
334 * @param icall Async request data
335 */
336static void udp_assoc_create_srv(udp_client_t *client, ipc_callid_t iid,
337 ipc_call_t *icall)
338{
339 ipc_callid_t callid;
340 size_t size;
341 inet_ep2_t epp;
342 sysarg_t assoc_id;
343 int rc;
344
345 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_srv()");
346
347 if (!async_data_write_receive(&callid, &size)) {
348 async_answer_0(callid, EREFUSED);
349 async_answer_0(iid, EREFUSED);
350 return;
351 }
352
353 if (size != sizeof(inet_ep2_t)) {
354 async_answer_0(callid, EINVAL);
355 async_answer_0(iid, EINVAL);
356 return;
357 }
358
359 rc = async_data_write_finalize(callid, &epp, size);
360 if (rc != EOK) {
361 async_answer_0(callid, rc);
362 async_answer_0(iid, rc);
363 return;
364 }
365
366 rc = udp_assoc_create_impl(client, &epp, &assoc_id);
367 if (rc != EOK) {
368 async_answer_0(iid, rc);
369 return;
370 }
371
372 async_answer_1(iid, EOK, assoc_id);
373}
374
375/** Destroy association.
376 *
377 * Handle client request to destroy association.
378 *
379 * @param client UDP client
380 * @param iid Async request ID
381 * @param icall Async request data
382 */
383static void udp_assoc_destroy_srv(udp_client_t *client, ipc_callid_t iid,
384 ipc_call_t *icall)
385{
386 sysarg_t assoc_id;
387 int rc;
388
389 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_destroy_srv()");
390
391 assoc_id = IPC_GET_ARG1(*icall);
392 rc = udp_assoc_destroy_impl(client, assoc_id);
393 async_answer_0(iid, rc);
394}
395
396/** Send message via association.
397 *
398 * Handle client request to send message.
399 *
400 * @param client UDP client
401 * @param iid Async request ID
402 * @param icall Async request data
403 */
404static void udp_assoc_send_msg_srv(udp_client_t *client, ipc_callid_t iid,
405 ipc_call_t *icall)
406{
407 ipc_callid_t callid;
408 size_t size;
409 inet_ep_t dest;
410 sysarg_t assoc_id;
411 void *data;
412 int rc;
413
414 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send_msg_srv()");
415
416 /* Receive dest */
417
418 if (!async_data_write_receive(&callid, &size)) {
419 async_answer_0(callid, EREFUSED);
420 async_answer_0(iid, EREFUSED);
421 return;
422 }
423
424 if (size != sizeof(inet_ep_t)) {
425 async_answer_0(callid, EINVAL);
426 async_answer_0(iid, EINVAL);
427 return;
428 }
429
430 rc = async_data_write_finalize(callid, &dest, size);
431 if (rc != EOK) {
432 async_answer_0(callid, rc);
433 async_answer_0(iid, rc);
434 return;
435 }
436
437 /* Receive message data */
438
439 if (!async_data_write_receive(&callid, &size)) {
440 async_answer_0(callid, EREFUSED);
441 async_answer_0(iid, EREFUSED);
442 return;
443 }
444
445 if (size > MAX_MSG_SIZE) {
446 async_answer_0(callid, EINVAL);
447 async_answer_0(iid, EINVAL);
448 return;
449 }
450
451 data = malloc(size);
452 if (data == NULL) {
453 async_answer_0(callid, ENOMEM);
454 async_answer_0(iid, ENOMEM);
455 }
456
457 rc = async_data_write_finalize(callid, data, size);
458 if (rc != EOK) {
459 async_answer_0(callid, rc);
460 async_answer_0(iid, rc);
461 free(data);
462 return;
463 }
464
465 assoc_id = IPC_GET_ARG1(*icall);
466
467 rc = udp_assoc_send_msg_impl(client, assoc_id, &dest, data, size);
468 if (rc != EOK) {
469 async_answer_0(iid, rc);
470 free(data);
471 return;
472 }
473
474 async_answer_0(iid, EOK);
475 free(data);
476}
477
478/** Get next received message.
479 *
480 * @param client UDP Client
481 * @return Pointer to queue entry for next received message
482 */
483static udp_crcv_queue_entry_t *udp_rmsg_get_next(udp_client_t *client)
484{
485 link_t *link;
486
487 link = list_first(&client->crcv_queue);
488 if (link == NULL)
489 return NULL;
490
491 return list_get_instance(link, udp_crcv_queue_entry_t, link);
492}
493
494/** Get info on first received message.
495 *
496 * Handle client request to get information on received message.
497 *
498 * @param client UDP client
499 * @param iid Async request ID
500 * @param icall Async request data
501 */
502static void udp_rmsg_info_srv(udp_client_t *client, ipc_callid_t iid,
503 ipc_call_t *icall)
504{
505 ipc_callid_t callid;
506 size_t size;
507 udp_crcv_queue_entry_t *enext;
508 sysarg_t assoc_id;
509 int rc;
510
511 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv()");
512 enext = udp_rmsg_get_next(client);
513
514 if (!async_data_read_receive(&callid, &size)) {
515 async_answer_0(callid, EREFUSED);
516 async_answer_0(iid, EREFUSED);
517 return;
518 }
519
520 if (enext == NULL) {
521 async_answer_0(callid, ENOENT);
522 async_answer_0(iid, ENOENT);
523 return;
524 }
525
526 rc = async_data_read_finalize(callid, &enext->epp.remote,
527 max(size, (ssize_t)sizeof(inet_ep_t)));
528 if (rc != EOK) {
529 async_answer_0(iid, rc);
530 return;
531 }
532
533 assoc_id = enext->cassoc->id;
534 size = enext->msg->data_size;
535
536 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_info_srv(): assoc_id=%zu, "
537 "size=%zu", assoc_id, size);
538 async_answer_2(iid, EOK, assoc_id, size);
539}
540
541/** Read data from first received message.
542 *
543 * Handle client request to read data from first received message.
544 *
545 * @param client UDP client
546 * @param iid Async request ID
547 * @param icall Async request data
548 */
549static void udp_rmsg_read_srv(udp_client_t *client, ipc_callid_t iid,
550 ipc_call_t *icall)
551{
552 ipc_callid_t callid;
553 ssize_t msg_size;
554 udp_crcv_queue_entry_t *enext;
555 void *data;
556 size_t size;
557 ssize_t off;
558 int rc;
559
560 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv()");
561 off = IPC_GET_ARG1(*icall);
562
563 enext = udp_rmsg_get_next(client);
564
565 if (!async_data_read_receive(&callid, &size)) {
566 async_answer_0(callid, EREFUSED);
567 async_answer_0(iid, EREFUSED);
568 return;
569 }
570
571 if (enext == NULL) {
572 async_answer_0(callid, ENOENT);
573 async_answer_0(iid, ENOENT);
574 return;
575 }
576
577 data = enext->msg->data + off;
578 msg_size = enext->msg->data_size;
579
580 rc = async_data_read_finalize(callid, data, max(msg_size - off,
581 (ssize_t)size));
582 if (rc != EOK) {
583 async_answer_0(iid, rc);
584 return;
585 }
586
587 async_answer_0(iid, EOK);
588 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_read_srv(): OK");
589}
590
591/** Discard first received message.
592 *
593 * Handle client request to discard first received message, advancing
594 * to the next one.
595 *
596 * @param client UDP client
597 * @param iid Async request ID
598 * @param icall Async request data
599 */
600static void udp_rmsg_discard_srv(udp_client_t *client, ipc_callid_t iid,
601 ipc_call_t *icall)
602{
603 udp_crcv_queue_entry_t *enext;
604
605 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_rmsg_discard_srv()");
606
607 enext = udp_rmsg_get_next(client);
608 if (enext == NULL) {
609 log_msg(LOG_DEFAULT, LVL_DEBUG, "usg_rmsg_discard_srv: enext==NULL");
610 async_answer_0(iid, ENOENT);
611 return;
612 }
613
614 list_remove(&enext->link);
615 udp_msg_delete(enext->msg);
616 free(enext);
617 async_answer_0(iid, EOK);
618}
619
620/** Handle UDP client connection.
621 *
622 * @param iid Connect call ID
623 * @param icall Connect call data
624 * @param arg Connection argument
625 */
626static void udp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
627{
628 udp_client_t client;
629 size_t n;
630
631 /* Accept the connection */
632 async_answer_0(iid, EOK);
633
634 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn()");
635
636 client.sess = NULL;
637 list_initialize(&client.cassoc);
638 list_initialize(&client.crcv_queue);
639
640 while (true) {
641 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: wait req");
642 ipc_call_t call;
643 ipc_callid_t callid = async_get_call(&call);
644 sysarg_t method = IPC_GET_IMETHOD(call);
645
646 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: method=%d",
647 (int)method);
648 if (!method) {
649 /* The other side has hung up */
650 async_answer_0(callid, EOK);
651 break;
652 }
653
654 switch (method) {
655 case UDP_CALLBACK_CREATE:
656 udp_callback_create_srv(&client, callid, &call);
657 break;
658 case UDP_ASSOC_CREATE:
659 udp_assoc_create_srv(&client, callid, &call);
660 break;
661 case UDP_ASSOC_DESTROY:
662 udp_assoc_destroy_srv(&client, callid, &call);
663 break;
664 case UDP_ASSOC_SEND_MSG:
665 udp_assoc_send_msg_srv(&client, callid, &call);
666 break;
667 case UDP_RMSG_INFO:
668 udp_rmsg_info_srv(&client, callid, &call);
669 break;
670 case UDP_RMSG_READ:
671 udp_rmsg_read_srv(&client, callid, &call);
672 break;
673 case UDP_RMSG_DISCARD:
674 udp_rmsg_discard_srv(&client, callid, &call);
675 break;
676 default:
677 async_answer_0(callid, ENOTSUP);
678 break;
679 }
680 }
681
682 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_client_conn: terminated");
683
684 n = list_count(&client.cassoc);
685 if (n != 0) {
686 log_msg(LOG_DEFAULT, LVL_WARN, "udp_client_conn: "
687 "Client with %zu active associations closed session.", n);
688 /* XXX Clean up */
689 }
690
691 /* XXX Clean up client receive queue */
692
693 if (client.sess != NULL)
694 async_hangup(client.sess);
695}
696
697/** Initialize UDP service.
698 *
699 * @return EOK on success or negative error code.
700 */
701int udp_service_init(void)
702{
703 int rc;
704 service_id_t sid;
705
706 async_set_fallback_port_handler(udp_client_conn, NULL);
707
708 rc = loc_server_register(NAME);
709 if (rc != EOK) {
710 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
711 return EIO;
712 }
713
714 rc = loc_service_register(SERVICE_NAME_UDP, &sid);
715 if (rc != EOK) {
716 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
717 return EIO;
718 }
719
720 return EOK;
721}
722
723/**
724 * @}
725 */
Note: See TracBrowser for help on using the repository browser.