source: mainline/uspace/lib/c/generic/net/packet.c@ 99b65d2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 99b65d2 was 5fe7692, checked in by Petr Koupy <petr.koupy@…>, 14 years ago

Removed side effects from map ADTs.

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