source: mainline/uspace/srv/net/udp/assoc.c@ 648e2ac

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 648e2ac was 498ced1, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Unify reference counting and remove some unnecessary instances of <atomic.h>

  • Property mode set to 100644
File size: 11.0 KB
RevLine 
[ee603c4]1/*
[2f19103]2 * Copyright (c) 2015 Jiri Svoboda
[ee603c4]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 UDP associations
35 */
36
37#include <adt/list.h>
[fab2746]38#include <errno.h>
[3e6a98c5]39#include <stdbool.h>
[ee603c4]40#include <fibril_synch.h>
[58e9dec]41#include <inet/endpoint.h>
[58e8646]42#include <inet/inet.h>
[ee603c4]43#include <io/log.h>
[2989c7e]44#include <nettl/amap.h>
[ee603c4]45#include <stdlib.h>
46
47#include "assoc.h"
48#include "msg.h"
49#include "pdu.h"
50#include "udp_inet.h"
51#include "udp_type.h"
52
[2989c7e]53static LIST_INITIALIZE(assoc_list);
54static FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
55static amap_t *amap;
[ee603c4]56
[2f19103]57static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
[b7fd2a0]58static errno_t udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
[92b42442]59
[2989c7e]60/** Initialize associations. */
[b7fd2a0]61errno_t udp_assocs_init(void)
[2989c7e]62{
[b7fd2a0]63 errno_t rc;
[2989c7e]64
65 rc = amap_create(&amap);
66 if (rc != EOK) {
67 assert(rc == ENOMEM);
68 return ENOMEM;
69 }
70
71 return EOK;
72}
73
[ee603c4]74/** Create new association structure.
75 *
[2f19103]76 * @param epp Endpoint pair (will be copied)
77 * @param cb Callbacks
78 * @param cb_arg Callback argument
[ee603c4]79 * @return New association or NULL
80 */
[2f19103]81udp_assoc_t *udp_assoc_new(inet_ep2_t *epp, udp_assoc_cb_t *cb, void *cb_arg)
[ee603c4]82{
83 udp_assoc_t *assoc = NULL;
84
[92b42442]85 /* Allocate association structure */
[ee603c4]86 assoc = calloc(1, sizeof(udp_assoc_t));
87 if (assoc == NULL)
88 goto error;
89
90 fibril_mutex_initialize(&assoc->lock);
91
92 /* One for the user */
[498ced1]93 refcount_init(&assoc->refcnt);
[ee603c4]94
95 /* Initialize receive queue */
96 list_initialize(&assoc->rcv_queue);
97 fibril_condvar_initialize(&assoc->rcv_queue_cv);
98
[2f19103]99 if (epp != NULL)
100 assoc->ident = *epp;
[ee603c4]101
[fab2746]102 assoc->cb = cb;
103 assoc->cb_arg = cb_arg;
[ee603c4]104 return assoc;
105error:
106 return NULL;
107}
108
109/** Destroy association structure.
110 *
111 * Association structure should be destroyed when the folowing conditions
112 * are met:
113 * (1) user has deleted the association
114 * (2) nobody is holding references to the association
115 *
116 * This happens when @a assoc->refcnt is zero as we count (1)
117 * as an extra reference.
118 *
[92b42442]119 * @param assoc Association
[ee603c4]120 */
121static void udp_assoc_free(udp_assoc_t *assoc)
122{
[a1a101d]123 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_free(%p)", assoc->name, assoc);
[ee603c4]124
125 while (!list_empty(&assoc->rcv_queue)) {
126 link_t *link = list_first(&assoc->rcv_queue);
[92b42442]127 udp_rcv_queue_entry_t *rqe = list_get_instance(link,
128 udp_rcv_queue_entry_t, link);
[ee603c4]129 list_remove(link);
[92b42442]130
131 udp_msg_delete(rqe->msg);
132 free(rqe);
[ee603c4]133 }
134
135 free(assoc);
136}
137
138/** Add reference to association.
139 *
140 * Increase association reference count by one.
141 *
142 * @param assoc Association
143 */
144void udp_assoc_addref(udp_assoc_t *assoc)
145{
[a1a101d]146 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: upd_assoc_addref(%p)", assoc->name, assoc);
[498ced1]147 refcount_up(&assoc->refcnt);
[ee603c4]148}
149
150/** Remove reference from association.
151 *
152 * Decrease association reference count by one.
153 *
154 * @param assoc Association
155 */
156void udp_assoc_delref(udp_assoc_t *assoc)
157{
[a1a101d]158 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delref(%p)", assoc->name, assoc);
[ee603c4]159
[498ced1]160 if (refcount_down(&assoc->refcnt))
[ee603c4]161 udp_assoc_free(assoc);
162}
163
164/** Delete association.
165 *
166 * The caller promises not make no further references to @a assoc.
167 * UDP will free @a assoc eventually.
168 *
169 * @param assoc Association
170 */
171void udp_assoc_delete(udp_assoc_t *assoc)
172{
[a1a101d]173 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delete(%p)", assoc->name, assoc);
[ee603c4]174
175 assert(assoc->deleted == false);
176 udp_assoc_delref(assoc);
177 assoc->deleted = true;
178}
179
180/** Enlist association.
181 *
182 * Add association to the association map.
183 */
[b7fd2a0]184errno_t udp_assoc_add(udp_assoc_t *assoc)
[ee603c4]185{
[2989c7e]186 inet_ep2_t aepp;
[b7fd2a0]187 errno_t rc;
[2989c7e]188
[ee603c4]189 udp_assoc_addref(assoc);
190 fibril_mutex_lock(&assoc_list_lock);
[2989c7e]191
192 rc = amap_insert(amap, &assoc->ident, assoc, af_allow_system, &aepp);
193 if (rc != EOK) {
194 udp_assoc_delref(assoc);
195 fibril_mutex_unlock(&assoc_list_lock);
196 return rc;
197 }
198
199 assoc->ident = aepp;
[ee603c4]200 list_append(&assoc->link, &assoc_list);
201 fibril_mutex_unlock(&assoc_list_lock);
[2989c7e]202
203 return EOK;
[ee603c4]204}
205
206/** Delist association.
207 *
208 * Remove association from the association map.
209 */
210void udp_assoc_remove(udp_assoc_t *assoc)
211{
212 fibril_mutex_lock(&assoc_list_lock);
[2989c7e]213 amap_remove(amap, &assoc->ident);
[ee603c4]214 list_remove(&assoc->link);
215 fibril_mutex_unlock(&assoc_list_lock);
216 udp_assoc_delref(assoc);
217}
218
[695b6ff]219/** Set IP link in association.
220 *
221 * @param assoc Association
222 * @param iplink IP link
223 */
224void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
225{
226 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
227 assoc, iplink);
228 fibril_mutex_lock(&assoc->lock);
[2f19103]229 assoc->ident.local_link = iplink;
[695b6ff]230 fibril_mutex_unlock(&assoc->lock);
231}
232
[ee603c4]233/** Send message to association.
234 *
235 * @param assoc Association
[2f19103]236 * @param remote Remote endpoint or NULL not to override @a assoc
[ee603c4]237 * @param msg Message
238 *
239 * @return EOK on success
[2f19103]240 * EINVAL if remote endpoint is not set
[ee603c4]241 * ENOMEM if out of resources
242 * EIO if no route to destination exists
243 */
[b7fd2a0]244errno_t udp_assoc_send(udp_assoc_t *assoc, inet_ep_t *remote, udp_msg_t *msg)
[ee603c4]245{
246 udp_pdu_t *pdu;
[2f19103]247 inet_ep2_t epp;
[b7fd2a0]248 errno_t rc;
[ee603c4]249
[c3f7d37]250 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send(%p, %p, %p)",
[2f19103]251 assoc, remote, msg);
[92b42442]252
[2f19103]253 /* @a remote can be used to override the remote endpoint */
254 epp = assoc->ident;
255 if (remote != NULL)
256 epp.remote = *remote;
[ee603c4]257
[c3f7d37]258 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check addr any");
[fab2746]259
[2f19103]260 if ((inet_addr_is_any(&epp.remote.addr)) ||
[58e9dec]261 (epp.remote.port == inet_port_any))
[ee603c4]262 return EINVAL;
263
[58e8646]264 /* This association has no local address set. Need to determine one. */
265 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check no local addr");
266 if (inet_addr_is_any(&epp.local.addr) && !assoc->nolocal) {
267 log_msg(LOG_DEFAULT, LVL_DEBUG, "Determine local address.");
268 rc = inet_get_srcaddr(&epp.remote.addr, 0, &epp.local.addr);
269 if (rc != EOK) {
270 log_msg(LOG_DEFAULT, LVL_DEBUG, "Cannot determine "
271 "local address.");
272 return EINVAL;
273 }
274 }
275
[c3f7d37]276 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check version");
[fab2746]277
[58e8646]278 if (!inet_addr_is_any(&epp.local.addr) &&
279 epp.remote.addr.version != epp.local.addr.version)
[fab2746]280 return EINVAL;
281
[c3f7d37]282 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - encode pdu");
[fab2746]283
[2f19103]284 rc = udp_pdu_encode(&epp, msg, &pdu);
[ee603c4]285 if (rc != EOK)
286 return ENOMEM;
287
[c3f7d37]288 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - transmit");
[fab2746]289
[ee603c4]290 rc = udp_transmit_pdu(pdu);
[071a2c60]291 udp_pdu_delete(pdu);
292
[ee603c4]293 if (rc != EOK)
294 return EIO;
295
[c3f7d37]296 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - success");
[ee603c4]297 return EOK;
298}
299
[92b42442]300/** Get a received message.
301 *
302 * Pull one message from the association's receive queue.
303 */
[b7fd2a0]304errno_t udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, inet_ep_t *remote)
[92b42442]305{
306 link_t *link;
307 udp_rcv_queue_entry_t *rqe;
308
[c3f7d37]309 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv()");
[92b42442]310
311 fibril_mutex_lock(&assoc->lock);
[141a20d]312 while (list_empty(&assoc->rcv_queue) && !assoc->reset) {
[a1a101d]313 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
[92b42442]314 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
315 }
316
[141a20d]317 if (assoc->reset) {
[a1e2df13]318 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - association was reset");
[141a20d]319 fibril_mutex_unlock(&assoc->lock);
[fab2746]320 return ENXIO;
[141a20d]321 }
322
[c3f7d37]323 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - got a message");
[92b42442]324 link = list_first(&assoc->rcv_queue);
325 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
326 list_remove(link);
327 fibril_mutex_unlock(&assoc->lock);
328
329 *msg = rqe->msg;
[2f19103]330 *remote = rqe->epp.remote;
[7094e196]331 free(rqe);
[92b42442]332
333 return EOK;
334}
335
336/** Message received.
337 *
338 * Find the association to which the message belongs and queue it.
339 */
[2f19103]340void udp_assoc_received(inet_ep2_t *repp, udp_msg_t *msg)
[92b42442]341{
342 udp_assoc_t *assoc;
[b7fd2a0]343 errno_t rc;
[92b42442]344
[c3f7d37]345 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_received(%p, %p)", repp, msg);
[92b42442]346
[2f19103]347 assoc = udp_assoc_find_ref(repp);
[92b42442]348 if (assoc == NULL) {
[c3f7d37]349 log_msg(LOG_DEFAULT, LVL_DEBUG, "No association found. Message dropped.");
[92b42442]350 /* XXX Generate ICMP error. */
351 /* XXX Might propagate error directly by error return. */
[071a2c60]352 udp_msg_delete(msg);
[92b42442]353 return;
354 }
355
[fab2746]356 if (0) {
[2f19103]357 rc = udp_assoc_queue_msg(assoc, repp, msg);
[fab2746]358 if (rc != EOK) {
359 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
[ae7d03c]360 /* XXX Generate ICMP error? */
[fab2746]361 }
[92b42442]362 }
[fab2746]363
[c3f7d37]364 log_msg(LOG_DEFAULT, LVL_DEBUG, "call assoc->cb->recv_msg");
[2f19103]365 assoc->cb->recv_msg(assoc->cb_arg, repp, msg);
[99ea91b2]366 udp_assoc_delref(assoc);
[92b42442]367}
368
[141a20d]369/** Reset association.
370 *
371 * This causes any pendingreceive operations to return immediately with
372 * UDP_ERESET.
373 */
374void udp_assoc_reset(udp_assoc_t *assoc)
375{
376 fibril_mutex_lock(&assoc->lock);
377 assoc->reset = true;
378 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
379 fibril_mutex_unlock(&assoc->lock);
380}
381
[b7fd2a0]382static errno_t udp_assoc_queue_msg(udp_assoc_t *assoc, inet_ep2_t *epp,
[92b42442]383 udp_msg_t *msg)
384{
385 udp_rcv_queue_entry_t *rqe;
386
[a1a101d]387 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
[2f19103]388 assoc, epp, msg);
[92b42442]389
390 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
391 if (rqe == NULL)
392 return ENOMEM;
393
394 link_initialize(&rqe->link);
[2f19103]395 rqe->epp = *epp;
[92b42442]396 rqe->msg = msg;
397
398 fibril_mutex_lock(&assoc->lock);
399 list_append(&rqe->link, &assoc->rcv_queue);
400 fibril_mutex_unlock(&assoc->lock);
401
402 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
403
404 return EOK;
405}
406
[2f19103]407/** Find association structure for specified endpoint pair.
[92b42442]408 *
[2f19103]409 * An association is uniquely identified by an endpoint pair. Look up our
410 * association map and return association structure based on endpoint pair.
[92b42442]411 * The association reference count is bumped by one.
412 *
[2f19103]413 * @param epp Endpoint pair
[92b42442]414 * @return Association structure or NULL if not found.
415 */
[2f19103]416static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *epp)
[92b42442]417{
[b7fd2a0]418 errno_t rc;
[8d48c7e]419 void *arg;
420 udp_assoc_t *assoc;
[fab2746]421
[c3f7d37]422 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_find_ref(%p)", epp);
[92b42442]423 fibril_mutex_lock(&assoc_list_lock);
[2f19103]424
[8d48c7e]425 rc = amap_find_match(amap, epp, &arg);
426 if (rc != EOK) {
[443a0bc]427 assert(rc == ENOENT);
[8d48c7e]428 fibril_mutex_unlock(&assoc_list_lock);
429 return NULL;
[92b42442]430 }
[2f19103]431
[8d48c7e]432 assoc = (udp_assoc_t *)arg;
433 udp_assoc_addref(assoc);
434
[92b42442]435 fibril_mutex_unlock(&assoc_list_lock);
[8d48c7e]436 return assoc;
[92b42442]437}
438
[ee603c4]439/**
440 * @}
441 */
Note: See TracBrowser for help on using the repository browser.