source: mainline/uspace/lib/c/generic/inet/tcp.c@ 204ba47

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 204ba47 was 204ba47, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Fix data_avail callback. Fix tcp_conn_recv(). Do not generate spurious data_avail callbacks.

  • Property mode set to 100644
File size: 12.8 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 libc
30 * @{
31 */
32/** @file TCP API
33 */
34
35#include <errno.h>
36#include <fibril.h>
37#include <inet/endpoint.h>
38#include <inet/tcp.h>
39#include <ipc/services.h>
40#include <ipc/tcp.h>
41#include <stdlib.h>
42
43static void tcp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
44static int tcp_conn_fibril(void *);
45
46/** Incoming TCP connection info */
47typedef struct {
48 tcp_listener_t *lst;
49 tcp_conn_t *conn;
50} tcp_in_conn_t;
51
52static int tcp_callback_create(tcp_t *tcp)
53{
54 async_exch_t *exch = async_exchange_begin(tcp->sess);
55
56 aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
57 int rc = async_connect_to_me(exch, 0, 0, 0, tcp_cb_conn, tcp);
58 async_exchange_end(exch);
59
60 if (rc != EOK)
61 return rc;
62
63 sysarg_t retval;
64 async_wait_for(req, &retval);
65
66 return retval;
67}
68
69int tcp_create(tcp_t **rtcp)
70{
71 tcp_t *tcp;
72 service_id_t tcp_svcid;
73 int rc;
74
75 tcp = calloc(1, sizeof(tcp_t));
76 if (tcp == NULL) {
77 rc = ENOMEM;
78 goto error;
79 }
80
81 list_initialize(&tcp->conn);
82 list_initialize(&tcp->listener);
83
84 rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
85 IPC_FLAG_BLOCKING);
86 if (rc != EOK) {
87 rc = EIO;
88 goto error;
89 }
90
91 tcp->sess = loc_service_connect(EXCHANGE_SERIALIZE, tcp_svcid,
92 IPC_FLAG_BLOCKING);
93 if (tcp->sess == NULL) {
94 rc = EIO;
95 goto error;
96 }
97
98 rc = tcp_callback_create(tcp);
99 if (rc != EOK) {
100 rc = EIO;
101 goto error;
102 }
103
104 *rtcp = tcp;
105 return EOK;
106error:
107 free(tcp);
108 return rc;
109}
110
111void tcp_destroy(tcp_t *tcp)
112{
113 if (tcp == NULL)
114 return;
115
116 async_hangup(tcp->sess);
117 free(tcp);
118}
119
120static int tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
121 tcp_conn_t **rconn)
122{
123 tcp_conn_t *conn;
124
125 conn = calloc(1, sizeof(tcp_conn_t));
126 if (conn == NULL)
127 return ENOMEM;
128
129 conn->data_avail = false;
130 fibril_mutex_initialize(&conn->lock);
131 fibril_condvar_initialize(&conn->cv);
132
133 conn->tcp = tcp;
134 conn->id = id;
135 conn->cb = cb;
136 conn->cb_arg = arg;
137
138 list_append(&conn->ltcp, &tcp->conn);
139 *rconn = conn;
140
141 return EOK;
142}
143
144int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
145 tcp_conn_t **rconn)
146{
147 async_exch_t *exch;
148 ipc_call_t answer;
149 sysarg_t conn_id;
150
151 exch = async_exchange_begin(tcp->sess);
152 aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
153 sysarg_t rc = async_data_write_start(exch, (void *)epp,
154 sizeof(inet_ep2_t));
155 async_exchange_end(exch);
156
157 if (rc != EOK) {
158 sysarg_t rc_orig;
159 async_wait_for(req, &rc_orig);
160 if (rc_orig != EOK)
161 rc = rc_orig;
162 goto error;
163 }
164
165 async_wait_for(req, &rc);
166 if (rc != EOK)
167 goto error;
168
169 conn_id = IPC_GET_ARG1(answer);
170
171 rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
172 if (rc != EOK)
173 return rc;
174
175 return EOK;
176error:
177 return (int) rc;
178}
179
180void tcp_conn_destroy(tcp_conn_t *conn)
181{
182 async_exch_t *exch;
183
184 if (conn == NULL)
185 return;
186
187 list_remove(&conn->ltcp);
188
189 exch = async_exchange_begin(conn->tcp->sess);
190 sysarg_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
191 async_exchange_end(exch);
192
193 free(conn);
194 (void) rc;
195}
196
197static int tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
198{
199 list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
200 if (conn->id == id) {
201 *rconn = conn;
202 return EOK;
203 }
204 }
205
206 return EINVAL;
207}
208
209void *tcp_conn_userptr(tcp_conn_t *conn)
210{
211 return conn->cb_arg;
212}
213
214int tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
215 void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
216{
217 async_exch_t *exch;
218 tcp_listener_t *lst;
219 ipc_call_t answer;
220
221 lst = calloc(1, sizeof(tcp_listener_t));
222 if (lst == NULL)
223 return ENOMEM;
224
225 exch = async_exchange_begin(tcp->sess);
226 aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
227 sysarg_t rc = async_data_write_start(exch, (void *)ep,
228 sizeof(inet_ep_t));
229 async_exchange_end(exch);
230
231 if (rc != EOK) {
232 sysarg_t rc_orig;
233 async_wait_for(req, &rc_orig);
234 if (rc_orig != EOK)
235 rc = rc_orig;
236 goto error;
237 }
238
239 async_wait_for(req, &rc);
240 if (rc != EOK)
241 goto error;
242
243 lst->tcp = tcp;
244 lst->id = IPC_GET_ARG1(answer);
245 lst->lcb = lcb;
246 lst->lcb_arg = larg;
247 lst->cb = cb;
248 lst->cb_arg = arg;
249
250 list_append(&lst->ltcp, &tcp->listener);
251 *rlst = lst;
252
253 return EOK;
254error:
255 free(lst);
256 return (int) rc;
257}
258
259void tcp_listener_destroy(tcp_listener_t *lst)
260{
261 async_exch_t *exch;
262
263 if (lst == NULL)
264 return;
265
266 list_remove(&lst->ltcp);
267
268 exch = async_exchange_begin(lst->tcp->sess);
269 sysarg_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
270 async_exchange_end(exch);
271
272 free(lst);
273 (void) rc;
274}
275
276static int tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
277{
278 list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
279 if (lst->id == id) {
280 *rlst = lst;
281 return EOK;
282 }
283 }
284
285 return EINVAL;
286}
287
288void *tcp_listener_userptr(tcp_listener_t *lst)
289{
290 return lst->lcb_arg;
291}
292
293int tcp_conn_wait_connected(tcp_conn_t *conn)
294{
295 fibril_mutex_lock(&conn->lock);
296 while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
297 fibril_condvar_wait(&conn->cv, &conn->lock);
298
299 if (conn->connected) {
300 fibril_mutex_unlock(&conn->lock);
301 return EOK;
302 } else {
303 assert(conn->conn_failed || conn->conn_reset);
304 fibril_mutex_unlock(&conn->lock);
305 return EIO;
306 }
307}
308
309int tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
310{
311 async_exch_t *exch;
312 sysarg_t rc;
313
314 exch = async_exchange_begin(conn->tcp->sess);
315 aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
316
317 rc = async_data_write_start(exch, data, bytes);
318 if (rc != EOK) {
319 async_forget(req);
320 return rc;
321 }
322
323 async_exchange_end(exch);
324
325 if (rc != EOK) {
326 async_forget(req);
327 return rc;
328 }
329
330 async_wait_for(req, &rc);
331 return rc;
332}
333
334
335int tcp_conn_send_fin(tcp_conn_t *conn)
336{
337 async_exch_t *exch;
338
339 exch = async_exchange_begin(conn->tcp->sess);
340 sysarg_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
341 async_exchange_end(exch);
342
343 return rc;
344}
345
346int tcp_conn_push(tcp_conn_t *conn)
347{
348 async_exch_t *exch;
349
350 exch = async_exchange_begin(conn->tcp->sess);
351 sysarg_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
352 async_exchange_end(exch);
353
354 return rc;
355}
356
357int tcp_conn_reset(tcp_conn_t *conn)
358{
359 async_exch_t *exch;
360
361 exch = async_exchange_begin(conn->tcp->sess);
362 sysarg_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
363 async_exchange_end(exch);
364
365 return rc;
366}
367
368int tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
369{
370 async_exch_t *exch;
371 ipc_call_t answer;
372
373 fibril_mutex_lock(&conn->lock);
374 if (!conn->data_avail) {
375 fibril_mutex_unlock(&conn->lock);
376 return EAGAIN;
377 }
378
379 exch = async_exchange_begin(conn->tcp->sess);
380 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
381 int rc = async_data_read_start(exch, buf, bsize);
382 async_exchange_end(exch);
383
384 if (rc != EOK) {
385 async_forget(req);
386 fibril_mutex_unlock(&conn->lock);
387 return rc;
388 }
389
390 sysarg_t retval;
391 async_wait_for(req, &retval);
392 if (retval != EOK) {
393 fibril_mutex_unlock(&conn->lock);
394 return retval;
395 }
396
397 *nrecv = IPC_GET_ARG1(answer);
398 fibril_mutex_unlock(&conn->lock);
399 return EOK;
400}
401
402int tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
403{
404 async_exch_t *exch;
405 ipc_call_t answer;
406
407again:
408 fibril_mutex_lock(&conn->lock);
409 while (!conn->data_avail) {
410 fibril_condvar_wait(&conn->cv, &conn->lock);
411 }
412
413 exch = async_exchange_begin(conn->tcp->sess);
414 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
415 int rc = async_data_read_start(exch, buf, bsize);
416 async_exchange_end(exch);
417
418 if (rc != EOK) {
419 async_forget(req);
420 if (rc == EAGAIN) {
421 conn->data_avail = false;
422 fibril_mutex_unlock(&conn->lock);
423 goto again;
424 }
425 fibril_mutex_unlock(&conn->lock);
426 return rc;
427 }
428
429 sysarg_t retval;
430 async_wait_for(req, &retval);
431 if (retval != EOK) {
432 if (rc == EAGAIN) {
433 conn->data_avail = false;
434 }
435 fibril_mutex_unlock(&conn->lock);
436 return retval;
437 }
438
439 *nrecv = IPC_GET_ARG1(answer);
440 fibril_mutex_unlock(&conn->lock);
441 return EOK;
442}
443
444static void tcp_ev_connected(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
445{
446 tcp_conn_t *conn;
447 sysarg_t conn_id;
448 int rc;
449
450 conn_id = IPC_GET_ARG1(*icall);
451
452 rc = tcp_conn_get(tcp, conn_id, &conn);
453 if (rc != EOK) {
454 async_answer_0(iid, ENOENT);
455 return;
456 }
457
458 fibril_mutex_lock(&conn->lock);
459 conn->connected = true;
460 fibril_condvar_broadcast(&conn->cv);
461 fibril_mutex_unlock(&conn->lock);
462
463 async_answer_0(iid, EOK);
464}
465
466static void tcp_ev_conn_failed(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
467{
468 tcp_conn_t *conn;
469 sysarg_t conn_id;
470 int rc;
471
472 conn_id = IPC_GET_ARG1(*icall);
473
474 rc = tcp_conn_get(tcp, conn_id, &conn);
475 if (rc != EOK) {
476 async_answer_0(iid, ENOENT);
477 return;
478 }
479
480 fibril_mutex_lock(&conn->lock);
481 conn->conn_failed = true;
482 fibril_condvar_broadcast(&conn->cv);
483 fibril_mutex_unlock(&conn->lock);
484
485 async_answer_0(iid, EOK);
486}
487
488static void tcp_ev_conn_reset(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
489{
490 tcp_conn_t *conn;
491 sysarg_t conn_id;
492 int rc;
493
494 conn_id = IPC_GET_ARG1(*icall);
495
496 rc = tcp_conn_get(tcp, conn_id, &conn);
497 if (rc != EOK) {
498 async_answer_0(iid, ENOENT);
499 return;
500 }
501
502 fibril_mutex_lock(&conn->lock);
503 conn->conn_reset = true;
504 fibril_condvar_broadcast(&conn->cv);
505 fibril_mutex_unlock(&conn->lock);
506
507 async_answer_0(iid, EOK);
508}
509
510static void tcp_ev_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
511{
512 tcp_conn_t *conn;
513 sysarg_t conn_id;
514 int rc;
515
516 conn_id = IPC_GET_ARG1(*icall);
517
518 rc = tcp_conn_get(tcp, conn_id, &conn);
519 if (rc != EOK) {
520 async_answer_0(iid, ENOENT);
521 return;
522 }
523
524 conn->data_avail = true;
525 fibril_condvar_broadcast(&conn->cv);
526
527 if (conn->cb != NULL && conn->cb->data_avail != NULL)
528 conn->cb->data_avail(conn);
529
530 async_answer_0(iid, EOK);
531}
532
533static void tcp_ev_urg_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
534{
535 async_answer_0(iid, ENOTSUP);
536}
537
538static void tcp_ev_new_conn(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
539{
540 tcp_listener_t *lst;
541 tcp_conn_t *conn;
542 sysarg_t lst_id;
543 sysarg_t conn_id;
544 fid_t fid;
545 tcp_in_conn_t *cinfo;
546 int rc;
547
548 lst_id = IPC_GET_ARG1(*icall);
549 conn_id = IPC_GET_ARG2(*icall);
550
551 rc = tcp_listener_get(tcp, lst_id, &lst);
552 if (rc != EOK) {
553 async_answer_0(iid, ENOENT);
554 return;
555 }
556
557 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
558 if (rc != EOK) {
559 async_answer_0(iid, ENOMEM);
560 return;
561 }
562
563 if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
564 cinfo = calloc(1, sizeof(tcp_in_conn_t));
565 if (cinfo == NULL) {
566 async_answer_0(iid, ENOMEM);
567 return;
568 }
569
570 cinfo->lst = lst;
571 cinfo->conn = conn;
572
573 fid = fibril_create(tcp_conn_fibril, cinfo);
574 if (fid == 0) {
575 async_answer_0(iid, ENOMEM);
576 }
577
578 fibril_add_ready(fid);
579 }
580
581 async_answer_0(iid, EOK);
582}
583
584static void tcp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
585{
586 tcp_t *tcp = (tcp_t *)arg;
587
588 async_answer_0(iid, EOK);
589
590 while (true) {
591 ipc_call_t call;
592 ipc_callid_t callid = async_get_call(&call);
593
594 if (!IPC_GET_IMETHOD(call)) {
595 /* TODO: Handle hangup */
596 return;
597 }
598
599 switch (IPC_GET_IMETHOD(call)) {
600 case TCP_EV_CONNECTED:
601 tcp_ev_connected(tcp, callid, &call);
602 break;
603 case TCP_EV_CONN_FAILED:
604 tcp_ev_conn_failed(tcp, callid, &call);
605 break;
606 case TCP_EV_CONN_RESET:
607 tcp_ev_conn_reset(tcp, callid, &call);
608 break;
609 case TCP_EV_DATA:
610 tcp_ev_data(tcp, callid, &call);
611 break;
612 case TCP_EV_URG_DATA:
613 tcp_ev_urg_data(tcp, callid, &call);
614 break;
615 case TCP_EV_NEW_CONN:
616 tcp_ev_new_conn(tcp, callid, &call);
617 break;
618 default:
619 async_answer_0(callid, ENOTSUP);
620 break;
621 }
622 }
623}
624
625/** Fibril for handling incoming TCP connection in background */
626static int tcp_conn_fibril(void *arg)
627{
628 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
629
630 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
631 tcp_conn_destroy(cinfo->conn);
632
633 return EOK;
634}
635
636/** @}
637 */
Note: See TracBrowser for help on using the repository browser.