source: mainline/uspace/lib/c/generic/net/packet.c@ 16ac756

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 16ac756 was 16ac756, checked in by Jakub Jermar <jakub@…>, 15 years ago

Get rid of the ERROR_CODE madness in libc/net.

  • 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 <malloc.h>
39#include <mem.h>
40#include <fibril_synch.h>
41#include <unistd.h>
42#include <errno.h>
43
44#include <sys/mman.h>
45
46#include <adt/generic_field.h>
47#include <net/packet.h>
48#include <net/packet_header.h>
49
50/** Packet map page size. */
51#define PACKET_MAP_SIZE 100
52
53/** Returns the packet map page index.
54 * @param[in] packet_id The packet identifier.
55 */
56#define PACKET_MAP_PAGE(packet_id) (((packet_id) - 1) / PACKET_MAP_SIZE)
57
58/** Returns the packet index in the corresponding packet map page.
59 * @param[in] packet_id The packet identifier.
60 */
61#define PACKET_MAP_INDEX(packet_id) (((packet_id) - 1) % PACKET_MAP_SIZE)
62
63/** Type definition of the packet map page. */
64typedef packet_t packet_map_t[PACKET_MAP_SIZE];
65
66/** Type definition of the packet map page pointer. */
67typedef packet_map_t * packet_map_ref;
68
69/** Packet map.
70 * Maps packet identifiers to the packet references.
71 * @see generic_field.h
72 */
73GENERIC_FIELD_DECLARE(gpm, packet_map_t);
74
75/** Packet map global data. */
76static struct {
77 /** Safety lock. */
78 fibril_rwlock_t lock;
79 /** Packet map. */
80 gpm_t packet_map;
81} pm_globals;
82
83GENERIC_FIELD_IMPLEMENT(gpm, packet_map_t);
84
85/** Initializes the packet map.
86 *
87 * @returns EOK on success.
88 * @returns ENOMEM if there is not enough memory left.
89 */
90int pm_init(void)
91{
92 int rc;
93
94 fibril_rwlock_initialize(&pm_globals.lock);
95
96 fibril_rwlock_write_lock(&pm_globals.lock);
97 rc = gpm_initialize(&pm_globals.packet_map);
98 fibril_rwlock_write_unlock(&pm_globals.lock);
99
100 return rc;
101}
102
103/** Finds the packet mapping.
104 *
105 * @param[in] packet_id The packet identifier to be found.
106 * @returns The found packet reference.
107 * @returns NULL if the mapping does not exist.
108 */
109packet_t pm_find(packet_id_t packet_id)
110{
111 packet_map_ref map;
112 packet_t packet;
113
114 if (!packet_id)
115 return NULL;
116
117 fibril_rwlock_read_lock(&pm_globals.lock);
118 if (packet_id > PACKET_MAP_SIZE * gpm_count(&pm_globals.packet_map)) {
119 fibril_rwlock_read_unlock(&pm_globals.lock);
120 return NULL;
121 }
122 map = gpm_get_index(&pm_globals.packet_map, PACKET_MAP_PAGE(packet_id));
123 if (!map) {
124 fibril_rwlock_read_unlock(&pm_globals.lock);
125 return NULL;
126 }
127 packet = (*map) [PACKET_MAP_INDEX(packet_id)];
128 fibril_rwlock_read_unlock(&pm_globals.lock);
129 return packet;
130}
131
132/** Adds the packet mapping.
133 *
134 * @param[in] packet The packet to be remembered.
135 * @returns EOK on success.
136 * @returns EINVAL if the packet is not valid.
137 * @returns EINVAL if the packet map is not initialized.
138 * @returns ENOMEM if there is not enough memory left.
139 */
140int pm_add(packet_t packet)
141{
142 packet_map_ref map;
143 int rc;
144
145 if (!packet_is_valid(packet))
146 return EINVAL;
147
148 fibril_rwlock_write_lock(&pm_globals.lock);
149
150 if (PACKET_MAP_PAGE(packet->packet_id) <
151 gpm_count(&pm_globals.packet_map)) {
152 map = gpm_get_index(&pm_globals.packet_map,
153 PACKET_MAP_PAGE(packet->packet_id));
154 } else {
155 do {
156 map = (packet_map_ref) malloc(sizeof(packet_map_t));
157 if (!map) {
158 fibril_rwlock_write_unlock(&pm_globals.lock);
159 return ENOMEM;
160 }
161 bzero(map, sizeof(packet_map_t));
162 rc = gpm_add(&pm_globals.packet_map, map);
163 if (rc < 0) {
164 fibril_rwlock_write_unlock(&pm_globals.lock);
165 free(map);
166 return rc;
167 }
168 } while (PACKET_MAP_PAGE(packet->packet_id) >=
169 gpm_count(&pm_globals.packet_map));
170 }
171
172 (*map) [PACKET_MAP_INDEX(packet->packet_id)] = packet;
173 fibril_rwlock_write_unlock(&pm_globals.lock);
174 return EOK;
175}
176
177/** Releases the packet map. */
178void pm_destroy(void)
179{
180 int count;
181 int index;
182 packet_map_ref map;
183 packet_t packet;
184
185 fibril_rwlock_write_lock(&pm_globals.lock);
186 count = gpm_count(&pm_globals.packet_map);
187 while (count > 0) {
188 map = gpm_get_index(&pm_globals.packet_map, count - 1);
189 for (index = PACKET_MAP_SIZE - 1; index >= 0; --index) {
190 packet = (*map)[index];
191 if (packet_is_valid(packet))
192 munmap(packet, packet->length);
193 }
194 }
195 gpm_destroy(&pm_globals.packet_map);
196 // leave locked
197}
198
199/** Add packet to the sorted queue.
200 *
201 * The queue is sorted in the ascending order.
202 * The packet is inserted right before the packets of the same order value.
203 *
204 * @param[in,out] first The first packet of the queue. Sets the first packet of
205 * the queue. The original first packet may be shifted by
206 * the new packet.
207 * @param[in] packet The packet to be added.
208 * @param[in] order The packet order value.
209 * @param[in] metric The metric value of the packet.
210 * @returns EOK on success.
211 * @returns EINVAL if the first parameter is NULL.
212 * @returns EINVAL if the packet is not valid.
213 */
214int pq_add(packet_t * first, packet_t packet, size_t order, size_t metric)
215{
216 packet_t item;
217
218 if (!first || !packet_is_valid(packet))
219 return EINVAL;
220
221 pq_set_order(packet, order, metric);
222 if (packet_is_valid(*first)) {
223 item = * first;
224 do {
225 if (item->order < order) {
226 if (item->next) {
227 item = pm_find(item->next);
228 } else {
229 item->next = packet->packet_id;
230 packet->previous = item->packet_id;
231 return EOK;
232 }
233 } else {
234 packet->previous = item->previous;
235 packet->next = item->packet_id;
236 item->previous = packet->packet_id;
237 item = pm_find(packet->previous);
238 if (item)
239 item->next = packet->packet_id;
240 else
241 *first = packet;
242 return EOK;
243 }
244 } while (packet_is_valid(item));
245 }
246 *first = packet;
247 return EOK;
248}
249
250/** Finds the packet with the given order.
251 *
252 * @param[in] first The first packet of the queue.
253 * @param[in] order The packet order value.
254 * @returns The packet with the given order.
255 * @returns NULL if the first packet is not valid.
256 * @returns NULL if the packet is not found.
257 */
258packet_t pq_find(packet_t packet, size_t order)
259{
260 packet_t item;
261
262 if (!packet_is_valid(packet))
263 return NULL;
264
265 item = packet;
266 do {
267 if (item->order == order)
268 return item;
269
270 item = pm_find(item->next);
271 } while (item && (item != packet) && packet_is_valid(item));
272
273 return NULL;
274}
275
276/** Inserts packet after the given one.
277 *
278 * @param[in] packet The packet in the queue.
279 * @param[in] new_packet The new packet to be inserted.
280 * @returns EOK on success.
281 * @returns EINVAL if etiher of the packets is invalid.
282 */
283int pq_insert_after(packet_t packet, packet_t new_packet)
284{
285 packet_t item;
286
287 if (!packet_is_valid(packet) || !packet_is_valid(new_packet))
288 return EINVAL;
289
290 new_packet->previous = packet->packet_id;
291 new_packet->next = packet->next;
292 item = pm_find(packet->next);
293 if (item)
294 item->previous = new_packet->packet_id;
295 packet->next = new_packet->packet_id;
296
297 return EOK;
298}
299
300/** Detach the packet from the queue.
301 *
302 * @param[in] packet The packet to be detached.
303 * @returns The next packet in the queue. If the packet is the first
304 * one of the queue, this becomes the new first one.
305 * @returns NULL if there is no packet left.
306 * @returns NULL if the packet is not valid.
307 */
308packet_t pq_detach(packet_t packet)
309{
310 packet_t next;
311 packet_t previous;
312
313 if (!packet_is_valid(packet))
314 return NULL;
315
316 next = pm_find(packet->next);
317 if (next) {
318 next->previous = packet->previous;
319 previous = pm_find(next->previous);
320 if (previous)
321 previous->next = next->packet_id;
322 }
323 packet->previous = 0;
324 packet->next = 0;
325 return next;
326}
327
328/** Sets the packet order and metric attributes.
329 *
330 * @param[in] packeti The packet to be set.
331 * @param[in] order The packet order value.
332 * @param[in] metric The metric value of the packet.
333 * @returns EOK on success.
334 * @returns EINVAL if the packet is invalid.
335 */
336int pq_set_order(packet_t packet, size_t order, size_t metric)
337{
338 if (!packet_is_valid(packet))
339 return EINVAL;
340
341 packet->order = order;
342 packet->metric = metric;
343 return EOK;
344}
345
346/** Sets the packet order and metric attributes.
347 *
348 * @param[in] packet The packet to be set.
349 * @param[out] order The packet order value.
350 * @param[out] metric The metric value of the packet.
351 * @returns EOK on success.
352 * @returns EINVAL if the packet is invalid.
353 */
354int pq_get_order(packet_t packet, size_t *order, size_t *metric)
355{
356 if (!packet_is_valid(packet))
357 return EINVAL;
358
359 if (order)
360 *order = packet->order;
361
362 if (metric)
363 *metric = packet->metric;
364
365 return EOK;
366}
367
368/** Releases the whole queue.
369 *
370 * Detaches all packets of the queue and calls the packet_release() for each of
371 * them.
372 *
373 * @param[in] first The first packet of the queue.
374 * @param[in] packet_release The releasing function called for each of the
375 * packets after its detachment.
376 */
377void pq_destroy(packet_t first, void (*packet_release)(packet_t packet))
378{
379 packet_t actual;
380 packet_t next;
381
382 actual = first;
383 while (packet_is_valid(actual)) {
384 next = pm_find(actual->next);
385 actual->next = 0;
386 actual->previous = 0;
387 if(packet_release)
388 packet_release(actual);
389 actual = next;
390 }
391}
392
393/** Returns the next packet in the queue.
394 *
395 * @param[in] packet The packet queue member.
396 * @returns The next packet in the queue.
397 * @returns NULL if there is no next packet.
398 * @returns NULL if the packet is not valid.
399 */
400packet_t pq_next(packet_t packet)
401{
402 if (!packet_is_valid(packet))
403 return NULL;
404
405 return pm_find(packet->next);
406}
407
408/** Returns the previous packet in the queue.
409 *
410 * @param[in] packet The packet queue member.
411 * @returns The previous packet in the queue.
412 * @returns NULL if there is no previous packet.
413 * @returns NULL if the packet is not valid.
414 */
415packet_t pq_previous(packet_t packet)
416{
417 if (!packet_is_valid(packet))
418 return NULL;
419
420 return pm_find(packet->previous);
421}
422
423/** @}
424 */
Note: See TracBrowser for help on using the repository browser.