source: mainline/uspace/lib/c/generic/net/packet.c@ 49bd793b

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

networking fixes

  • use sysarg_t for packet_id_t to avoid potential overflow
  • fix the confusion in the order of arguments for nil_received_msg(), this fixes the strange ping timeout on 127.0.0.1
  • Property mode set to 100644
File size: 10.8 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
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
33/** @file
34 * Packet map and queue implementation.
35 * This file has to be compiled with both the packet server and the client.
36 */
37
38#include <assert.h>
39#include <malloc.h>
40#include <mem.h>
41#include <fibril_synch.h>
42#include <unistd.h>
43#include <errno.h>
44
45#include <sys/mman.h>
46
47#include <adt/hash_table.h>
48#include <net/packet.h>
49#include <net/packet_header.h>
50
51/** Packet hash table size. */
52#define PACKET_HASH_TABLE_SIZE 128
53
54/** Packet map global data. */
55static struct {
56 /** Safety lock. */
57 fibril_rwlock_t lock;
58 /** Packet map. */
59 hash_table_t packet_map;
60 /** Packet map operations */
61 hash_table_operations_t operations;
62} pm_globals;
63
64typedef struct {
65 link_t link;
66 packet_t *packet;
67} pm_entry_t;
68
69/**
70 * Hash function for the packet mapping hash table
71 */
72static hash_index_t pm_hash(unsigned long key[])
73{
74 return (hash_index_t) key[0] % PACKET_HASH_TABLE_SIZE;
75}
76
77/**
78 * Key compare function for the packet mapping hash table
79 */
80static int pm_compare(unsigned long key[], hash_count_t keys, link_t *link)
81{
82 pm_entry_t *entry = list_get_instance(link, pm_entry_t, link);
83 return entry->packet->packet_id == key[0];
84}
85
86/**
87 * Remove callback for the packet mapping hash table
88 */
89static void pm_remove_callback(link_t *link)
90{
91 pm_entry_t *entry = list_get_instance(link, pm_entry_t, link);
92 free(entry);
93}
94
95/**
96 * Wrapper used when destroying the whole table
97 */
98static void pm_free_wrapper(link_t *link, void *ignored)
99{
100 pm_entry_t *entry = list_get_instance(link, pm_entry_t, link);
101 free(entry);
102}
103
104
105/** Initializes the packet map.
106 *
107 * @return EOK on success.
108 * @return ENOMEM if there is not enough memory left.
109 */
110int pm_init(void)
111{
112 int rc = EOK;
113
114 fibril_rwlock_initialize(&pm_globals.lock);
115
116 fibril_rwlock_write_lock(&pm_globals.lock);
117
118 pm_globals.operations.hash = pm_hash;
119 pm_globals.operations.compare = pm_compare;
120 pm_globals.operations.remove_callback = pm_remove_callback;
121
122 if (!hash_table_create(&pm_globals.packet_map, PACKET_HASH_TABLE_SIZE, 1,
123 &pm_globals.operations))
124 rc = ENOMEM;
125
126 fibril_rwlock_write_unlock(&pm_globals.lock);
127
128 return rc;
129}
130
131/** Finds the packet mapping.
132 *
133 * @param[in] packet_id Packet identifier to be found.
134 *
135 * @return The found packet reference.
136 * @return NULL if the mapping does not exist.
137 *
138 */
139packet_t *pm_find(packet_id_t packet_id)
140{
141 if (!packet_id)
142 return NULL;
143
144 fibril_rwlock_read_lock(&pm_globals.lock);
145
146 unsigned long key = packet_id;
147 link_t *link = hash_table_find(&pm_globals.packet_map, &key);
148
149 packet_t *packet;
150 if (link != NULL) {
151 pm_entry_t *entry =
152 hash_table_get_instance(link, pm_entry_t, link);
153 packet = entry->packet;
154 } else
155 packet = NULL;
156
157 fibril_rwlock_read_unlock(&pm_globals.lock);
158 return packet;
159}
160
161/** Adds the packet mapping.
162 *
163 * @param[in] packet Packet to be remembered.
164 *
165 * @return EOK on success.
166 * @return EINVAL if the packet is not valid.
167 * @return ENOMEM if there is not enough memory left.
168 *
169 */
170int pm_add(packet_t *packet)
171{
172 if (!packet_is_valid(packet))
173 return EINVAL;
174
175 fibril_rwlock_write_lock(&pm_globals.lock);
176
177 pm_entry_t *entry = malloc(sizeof(pm_entry_t));
178 if (entry == NULL) {
179 fibril_rwlock_write_unlock(&pm_globals.lock);
180 return ENOMEM;
181 }
182
183 entry->packet = packet;
184
185 unsigned long key = packet->packet_id;
186 hash_table_insert(&pm_globals.packet_map, &key, &entry->link);
187
188 fibril_rwlock_write_unlock(&pm_globals.lock);
189
190 return EOK;
191}
192
193/** Remove the packet mapping
194 *
195 * @param[in] packet The packet to be removed
196 *
197 */
198void pm_remove(packet_t *packet)
199{
200 assert(packet_is_valid(packet));
201
202 fibril_rwlock_write_lock(&pm_globals.lock);
203
204 unsigned long key = packet->packet_id;
205 hash_table_remove(&pm_globals.packet_map, &key, 1);
206
207 fibril_rwlock_write_unlock(&pm_globals.lock);
208}
209
210/** Release the packet map. */
211void pm_destroy(void)
212{
213 fibril_rwlock_write_lock(&pm_globals.lock);
214 hash_table_apply(&pm_globals.packet_map, pm_free_wrapper, NULL);
215 hash_table_destroy(&pm_globals.packet_map);
216 /* Leave locked */
217}
218
219/** Add packet to the sorted queue.
220 *
221 * The queue is sorted in the ascending order.
222 * The packet is inserted right before the packets of the same order value.
223 *
224 * @param[in,out] first First packet of the queue. Sets the first
225 * packet of the queue. The original first packet
226 * may be shifted by the new packet.
227 * @param[in] packet Packet to be added.
228 * @param[in] order Packet order value.
229 * @param[in] metric Metric value of the packet.
230 *
231 * @return EOK on success.
232 * @return EINVAL if the first parameter is NULL.
233 * @return EINVAL if the packet is not valid.
234 *
235 */
236int pq_add(packet_t **first, packet_t *packet, size_t order, size_t metric)
237{
238 if ((!first) || (!packet_is_valid(packet)))
239 return EINVAL;
240
241 pq_set_order(packet, order, metric);
242 if (packet_is_valid(*first)) {
243 packet_t *cur = *first;
244
245 do {
246 if (cur->order < order) {
247 if (cur->next)
248 cur = pm_find(cur->next);
249 else {
250 cur->next = packet->packet_id;
251 packet->previous = cur->packet_id;
252
253 return EOK;
254 }
255 } else {
256 packet->previous = cur->previous;
257 packet->next = cur->packet_id;
258
259 cur->previous = packet->packet_id;
260 cur = pm_find(packet->previous);
261
262 if (cur)
263 cur->next = packet->packet_id;
264 else
265 *first = packet;
266
267 return EOK;
268 }
269 } while (packet_is_valid(cur));
270 }
271
272 *first = packet;
273 return EOK;
274}
275
276/** Finds the packet with the given order.
277 *
278 * @param[in] first The first packet of the queue.
279 * @param[in] order The packet order value.
280 * @return The packet with the given order.
281 * @return NULL if the first packet is not valid.
282 * @return NULL if the packet is not found.
283 */
284packet_t *pq_find(packet_t *packet, size_t order)
285{
286 packet_t *item;
287
288 if (!packet_is_valid(packet))
289 return NULL;
290
291 item = packet;
292 do {
293 if (item->order == order)
294 return item;
295
296 item = pm_find(item->next);
297 } while (item && (item != packet) && packet_is_valid(item));
298
299 return NULL;
300}
301
302/** Inserts packet after the given one.
303 *
304 * @param[in] packet The packet in the queue.
305 * @param[in] new_packet The new packet to be inserted.
306 * @return EOK on success.
307 * @return EINVAL if etiher of the packets is invalid.
308 */
309int pq_insert_after(packet_t *packet, packet_t *new_packet)
310{
311 packet_t *item;
312
313 if (!packet_is_valid(packet) || !packet_is_valid(new_packet))
314 return EINVAL;
315
316 new_packet->previous = packet->packet_id;
317 new_packet->next = packet->next;
318 item = pm_find(packet->next);
319 if (item)
320 item->previous = new_packet->packet_id;
321 packet->next = new_packet->packet_id;
322
323 return EOK;
324}
325
326/** Detach the packet from the queue.
327 *
328 * @param[in] packet The packet to be detached.
329 * @return The next packet in the queue. If the packet is the first
330 * one of the queue, this becomes the new first one.
331 * @return NULL if there is no packet left.
332 * @return NULL if the packet is not valid.
333 */
334packet_t *pq_detach(packet_t *packet)
335{
336 packet_t *next;
337 packet_t *previous;
338
339 if (!packet_is_valid(packet))
340 return NULL;
341
342 next = pm_find(packet->next);
343 if (next)
344 next->previous = packet->previous;
345
346 previous = pm_find(packet->previous);
347 if (previous)
348 previous->next = packet->next ;
349
350 packet->previous = 0;
351 packet->next = 0;
352 return next;
353}
354
355/** Sets the packet order and metric attributes.
356 *
357 * @param[in] packeti The packet to be set.
358 * @param[in] order The packet order value.
359 * @param[in] metric The metric value of the packet.
360 * @return EOK on success.
361 * @return EINVAL if the packet is invalid.
362 */
363int pq_set_order(packet_t *packet, size_t order, size_t metric)
364{
365 if (!packet_is_valid(packet))
366 return EINVAL;
367
368 packet->order = order;
369 packet->metric = metric;
370 return EOK;
371}
372
373/** Sets the packet order and metric attributes.
374 *
375 * @param[in] packet The packet to be set.
376 * @param[out] order The packet order value.
377 * @param[out] metric The metric value of the packet.
378 * @return EOK on success.
379 * @return EINVAL if the packet is invalid.
380 */
381int pq_get_order(packet_t *packet, size_t *order, size_t *metric)
382{
383 if (!packet_is_valid(packet))
384 return EINVAL;
385
386 if (order)
387 *order = packet->order;
388
389 if (metric)
390 *metric = packet->metric;
391
392 return EOK;
393}
394
395/** Releases the whole queue.
396 *
397 * Detaches all packets of the queue and calls the packet_release() for each of
398 * them.
399 *
400 * @param[in] first The first packet of the queue.
401 * @param[in] packet_release The releasing function called for each of the
402 * packets after its detachment.
403 */
404void pq_destroy(packet_t *first, void (*packet_release)(packet_t *packet))
405{
406 packet_t *actual;
407 packet_t *next;
408
409 actual = first;
410 while (packet_is_valid(actual)) {
411 next = pm_find(actual->next);
412 actual->next = 0;
413 actual->previous = 0;
414 if(packet_release)
415 packet_release(actual);
416 actual = next;
417 }
418}
419
420/** Returns the next packet in the queue.
421 *
422 * @param[in] packet The packet queue member.
423 * @return The next packet in the queue.
424 * @return NULL if there is no next packet.
425 * @return NULL if the packet is not valid.
426 */
427packet_t *pq_next(packet_t *packet)
428{
429 if (!packet_is_valid(packet))
430 return NULL;
431
432 return pm_find(packet->next);
433}
434
435/** Returns the previous packet in the queue.
436 *
437 * @param[in] packet The packet queue member.
438 * @return The previous packet in the queue.
439 * @return NULL if there is no previous packet.
440 * @return NULL if the packet is not valid.
441 */
442packet_t *pq_previous(packet_t *packet)
443{
444 if (!packet_is_valid(packet))
445 return NULL;
446
447 return pm_find(packet->previous);
448}
449
450/** @}
451 */
Note: See TracBrowser for help on using the repository browser.