Contiki-NG
Loading...
Searching...
No Matches
queuebuf.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2006, Swedish Institute of Computer Science.
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 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 */
32
33/**
34 * \file
35 * Implementation of the Packet queue buffers
36 * \author
37 * Adam Dunkels <adam@sics.se>
38 */
39
40/**
41 * \addtogroup queuebuf
42 * @{
43 */
44
45#include "contiki-net.h"
46#include "net/queuebuf.h"
47
48#if WITH_SWAP
49#include "cfs/cfs.h"
50#endif
51
52#include <string.h> /* for memcpy() */
53
54/* Structure pointing to a buffer either stored
55 in RAM or swapped in CFS */
56struct queuebuf {
57#if QUEUEBUF_DEBUG
58 struct queuebuf *next;
59 const char *file;
60 int line;
61 clock_time_t time;
62#endif /* QUEUEBUF_DEBUG */
63#if WITH_SWAP
64 enum {IN_RAM, IN_CFS} location;
65 union {
66#endif
67 struct queuebuf_data *ram_ptr;
68#if WITH_SWAP
69 int swap_id;
70 };
71#endif
72};
73
74/* The actual queuebuf data */
75struct queuebuf_data {
76 uint8_t data[PACKETBUF_SIZE];
77 uint16_t len;
78 struct packetbuf_attr attrs[PACKETBUF_NUM_ATTRS];
79 struct packetbuf_addr addrs[PACKETBUF_NUM_ADDRS];
80};
81
82MEMB(bufmem, struct queuebuf, QUEUEBUF_NUM);
83MEMB(buframmem, struct queuebuf_data, QUEUEBUFRAM_NUM);
84
85#if WITH_SWAP
86
87/* Swapping allows to store up to QUEUEBUF_NUM - QUEUEBUFRAM_NUM
88 queuebufs in CFS. The swap is made of several large CFS files.
89 Every buffer stored in CFS has a swap id, referring to a specific
90 offset in one of these files. */
91#define NQBUF_FILES 4
92#define NQBUF_PER_FILE 256
93#define QBUF_FILE_SIZE (NQBUF_PER_FILE*sizeof(struct queuebuf_data))
94#define NQBUF_ID (NQBUF_PER_FILE * NQBUF_FILES)
95
96struct qbuf_file {
97 int fd;
98 int usage;
99 int renewable;
100};
101
102/* A statically allocated queuebuf used as a cache for swapped qbufs */
103static struct queuebuf_data tmpdata;
104/* A pointer to the qbuf associated to the data in tmpdata */
105static struct queuebuf *tmpdata_qbuf = NULL;
106/* The swap id counter */
107static int next_swap_id = 0;
108/* The swap files */
109static struct qbuf_file qbuf_files[NQBUF_FILES];
110/* The timer used to renew files during inactivity periods */
111static struct ctimer renew_timer;
112
113#endif
114
115#if QUEUEBUF_DEBUG
116#include "lib/list.h"
117LIST(queuebuf_list);
118#endif /* QUEUEBUF_DEBUG */
119
120#define DEBUG 0
121#if DEBUG
122#include <stdio.h>
123#define PRINTF(...) printf(__VA_ARGS__)
124#else
125#define PRINTF(...)
126#endif
127
128#ifdef QUEUEBUF_CONF_STATS
129#define QUEUEBUF_STATS QUEUEBUF_CONF_STATS
130#else
131#define QUEUEBUF_STATS 0
132#endif /* QUEUEBUF_CONF_STATS */
133
134#if QUEUEBUF_STATS
135uint8_t queuebuf_len, queuebuf_max_len;
136#endif /* QUEUEBUF_STATS */
137
138#if WITH_SWAP
139/*---------------------------------------------------------------------------*/
140static void
141qbuf_renew_file(int file)
142{
143 int ret;
144 char name[2];
145 name[0] = 'a' + file;
146 name[1] = '\0';
147 if(qbuf_files[file].renewable == 1) {
148 PRINTF("qbuf_renew_file: removing file %d\n", file);
149 cfs_remove(name);
150 }
151 ret = cfs_open(name, CFS_READ | CFS_WRITE);
152 if(ret == -1) {
153 PRINTF("qbuf_renew_file: cfs open error\n");
154 }
155 qbuf_files[file].fd = ret;
156 qbuf_files[file].usage = 0;
157 qbuf_files[file].renewable = 0;
158}
159/*---------------------------------------------------------------------------*/
160/* Renews every file with renewable flag set */
161static void
162qbuf_renew_all(void *unused)
163{
164 int i;
165 for(i=0; i<NQBUF_FILES; i++) {
166 if(qbuf_files[i].renewable == 1) {
167 qbuf_renew_file(i);
168 }
169 }
170}
171/*---------------------------------------------------------------------------*/
172/* Removes a queuebuf from its swap file */
173static void
174queuebuf_remove_from_file(int swap_id)
175{
176 int fileid;
177 if(swap_id != -1) {
178 fileid = swap_id / NQBUF_PER_FILE;
179 qbuf_files[fileid].usage--;
180
181 /* The file is full but doesn't contain any more queuebuf, mark it as renewable */
182 if(qbuf_files[fileid].usage == 0 && fileid != next_swap_id / NQBUF_PER_FILE) {
183 qbuf_files[fileid].renewable = 1;
184 /* This file is renewable, set a timer to renew files */
185 ctimer_set(&renew_timer, 0, qbuf_renew_all, NULL);
186 }
187
188 if(tmpdata_qbuf->swap_id == swap_id) {
189 tmpdata_qbuf->swap_id = -1;
190 }
191 }
192}
193/*---------------------------------------------------------------------------*/
194static int
195get_new_swap_id(void)
196{
197 int fileid;
198 int swap_id = next_swap_id;
199 fileid = swap_id / NQBUF_PER_FILE;
200 if(swap_id % NQBUF_PER_FILE == 0) { /* This is the first id in the file */
201 if(qbuf_files[fileid].renewable) {
202 qbuf_renew_file(fileid);
203 }
204 if(qbuf_files[fileid].usage>0) {
205 return -1;
206 }
207 }
208 qbuf_files[fileid].usage++;
209 next_swap_id = (next_swap_id+1) % NQBUF_ID;
210 return swap_id;
211}
212/*---------------------------------------------------------------------------*/
213/* Flush tmpdata to CFS */
214static int
215queuebuf_flush_tmpdata(void)
216{
217 int fileid, fd, ret;
218 cfs_offset_t offset;
219 if(tmpdata_qbuf) {
220 queuebuf_remove_from_file(tmpdata_qbuf->swap_id);
221 tmpdata_qbuf->swap_id = get_new_swap_id();
222 if(tmpdata_qbuf->swap_id == -1) {
223 return -1;
224 }
225 fileid = tmpdata_qbuf->swap_id / NQBUF_PER_FILE;
226 offset = (tmpdata_qbuf->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data);
227 fd = qbuf_files[fileid].fd;
228 ret = cfs_seek(fd, offset, CFS_SEEK_SET);
229 if(ret == -1) {
230 PRINTF("queuebuf_flush_tmpdata: cfs seek error\n");
231 return -1;
232 }
233 ret = cfs_write(fd, &tmpdata, sizeof(struct queuebuf_data));
234 if(ret == -1) {
235 PRINTF("queuebuf_flush_tmpdata: cfs write error\n");
236 return -1;
237 }
238 }
239 return 0;
240}
241/*---------------------------------------------------------------------------*/
242/* If the queuebuf is in CFS, load it to tmpdata */
243static struct queuebuf_data *
244queuebuf_load_to_ram(struct queuebuf *b)
245{
246 int fileid, fd, ret;
247 cfs_offset_t offset;
248 if(b->location == IN_RAM) { /* the qbuf is loacted in RAM */
249 return b->ram_ptr;
250 } else { /* the qbuf is located in CFS */
251 if(tmpdata_qbuf && tmpdata_qbuf->swap_id == b->swap_id) { /* the qbuf is already in tmpdata */
252 return &tmpdata;
253 } else { /* the qbuf needs to be loaded from CFS */
254 tmpdata_qbuf = b;
255 /* read the qbuf from CFS */
256 fileid = b->swap_id / NQBUF_PER_FILE;
257 offset = (b->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data);
258 fd = qbuf_files[fileid].fd;
259 ret = cfs_seek(fd, offset, CFS_SEEK_SET);
260 if(ret == -1) {
261 PRINTF("queuebuf_load_to_ram: cfs seek error\n");
262 }
263 ret = cfs_read(fd, &tmpdata, sizeof(struct queuebuf_data));
264 if(ret == -1) {
265 PRINTF("queuebuf_load_to_ram: cfs read error\n");
266 }
267 return &tmpdata;
268 }
269 }
270}
271#else /* WITH_SWAP */
272/*---------------------------------------------------------------------------*/
273static struct queuebuf_data *
274queuebuf_load_to_ram(struct queuebuf *b)
275{
276 return b->ram_ptr;
277}
278#endif /* WITH_SWAP */
279/*---------------------------------------------------------------------------*/
280void
281queuebuf_init(void)
282{
283#if WITH_SWAP
284 int i;
285 for(i=0; i<NQBUF_FILES; i++) {
286 qbuf_files[i].renewable = 1;
287 qbuf_renew_file(i);
288 }
289#endif
290 memb_init(&buframmem);
291 memb_init(&bufmem);
292#if QUEUEBUF_STATS
293 queuebuf_max_len = 0;
294#endif /* QUEUEBUF_STATS */
295}
296/*---------------------------------------------------------------------------*/
297size_t
298queuebuf_numfree(void)
299{
300 return memb_numfree(&bufmem);
301}
302/*---------------------------------------------------------------------------*/
303#if QUEUEBUF_DEBUG
304struct queuebuf *
305queuebuf_new_from_packetbuf_debug(const char *file, int line)
306#else /* QUEUEBUF_DEBUG */
307struct queuebuf *
308queuebuf_new_from_packetbuf(void)
309#endif /* QUEUEBUF_DEBUG */
310{
311 struct queuebuf *buf;
312
313 struct queuebuf_data *buframptr;
314 buf = memb_alloc(&bufmem);
315 if(buf != NULL) {
316#if QUEUEBUF_DEBUG
317 list_add(queuebuf_list, buf);
318 buf->file = file;
319 buf->line = line;
320 buf->time = clock_time();
321#endif /* QUEUEBUF_DEBUG */
322 buf->ram_ptr = memb_alloc(&buframmem);
323#if WITH_SWAP
324 /* If the allocation failed, store the qbuf in swap files */
325 if(buf->ram_ptr != NULL) {
326 buf->location = IN_RAM;
327 buframptr = buf->ram_ptr;
328 } else {
329 buf->location = IN_CFS;
330 buf->swap_id = -1;
331 tmpdata_qbuf = buf;
332 buframptr = &tmpdata;
333 }
334#else
335 if(buf->ram_ptr == NULL) {
336 PRINTF("queuebuf_new_from_packetbuf: could not queuebuf data\n");
337 memb_free(&bufmem, buf);
338 return NULL;
339 }
340 buframptr = buf->ram_ptr;
341#endif
342
343 buframptr->len = packetbuf_copyto(buframptr->data);
344 packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
345
346#if WITH_SWAP
347 if(buf->location == IN_CFS) {
348 if(queuebuf_flush_tmpdata() == -1) {
349 /* We were unable to write the data in the swap */
350 memb_free(&bufmem, buf);
351 return NULL;
352 }
353 }
354#endif
355
356#if QUEUEBUF_STATS
357 ++queuebuf_len;
358 PRINTF("#A q=%d\n", queuebuf_len);
359 if(queuebuf_len > queuebuf_max_len) {
360 queuebuf_max_len = queuebuf_len;
361 }
362#endif /* QUEUEBUF_STATS */
363
364 } else {
365 PRINTF("queuebuf_new_from_packetbuf: could not allocate a queuebuf\n");
366 }
367 return buf;
368}
369/*---------------------------------------------------------------------------*/
370void
371queuebuf_update_attr_from_packetbuf(struct queuebuf *buf)
372{
373 struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf);
374 packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
375#if WITH_SWAP
376 if(buf->location == IN_CFS) {
377 queuebuf_flush_tmpdata();
378 }
379#endif
380}
381/*---------------------------------------------------------------------------*/
382void
383queuebuf_update_from_packetbuf(struct queuebuf *buf)
384{
385 struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf);
386 packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
387 buframptr->len = packetbuf_copyto(buframptr->data);
388#if WITH_SWAP
389 if(buf->location == IN_CFS) {
390 queuebuf_flush_tmpdata();
391 }
392#endif
393}
394/*---------------------------------------------------------------------------*/
395void
396queuebuf_free(struct queuebuf *buf)
397{
398 if(memb_inmemb(&bufmem, buf)) {
399#if WITH_SWAP
400 if(buf->location == IN_RAM) {
401 memb_free(&buframmem, buf->ram_ptr);
402 } else {
403 queuebuf_remove_from_file(buf->swap_id);
404 }
405#else
406 memb_free(&buframmem, buf->ram_ptr);
407#endif
408 memb_free(&bufmem, buf);
409#if QUEUEBUF_STATS
410 --queuebuf_len;
411 PRINTF("#A q=%d\n", queuebuf_len);
412#endif /* QUEUEBUF_STATS */
413#if QUEUEBUF_DEBUG
414 list_remove(queuebuf_list, buf);
415#endif /* QUEUEBUF_DEBUG */
416 }
417}
418/*---------------------------------------------------------------------------*/
419void
420queuebuf_to_packetbuf(struct queuebuf *b)
421{
422 if(memb_inmemb(&bufmem, b)) {
423 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
424 packetbuf_copyfrom(buframptr->data, buframptr->len);
425 packetbuf_attr_copyfrom(buframptr->attrs, buframptr->addrs);
426 }
427}
428/*---------------------------------------------------------------------------*/
429void *
430queuebuf_dataptr(struct queuebuf *b)
431{
432 if(memb_inmemb(&bufmem, b)) {
433 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
434 return buframptr->data;
435 }
436 return NULL;
437}
438/*---------------------------------------------------------------------------*/
439int
440queuebuf_datalen(struct queuebuf *b)
441{
442 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
443 return buframptr->len;
444}
445/*---------------------------------------------------------------------------*/
446linkaddr_t *
447queuebuf_addr(struct queuebuf *b, uint8_t type)
448{
449 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
450 return &buframptr->addrs[type - PACKETBUF_ADDR_FIRST].addr;
451}
452/*---------------------------------------------------------------------------*/
453packetbuf_attr_t
454queuebuf_attr(struct queuebuf *b, uint8_t type)
455{
456 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
457 return buframptr->attrs[type].val;
458}
459/*---------------------------------------------------------------------------*/
460void
461queuebuf_debug_print(void)
462{
463#if QUEUEBUF_DEBUG
464 struct queuebuf *q;
465 printf("queuebuf_list: ");
466 for(q = list_head(queuebuf_list); q != NULL;
467 q = list_item_next(q)) {
468 printf("%s,%d,%lu ", q->file, q->line, q->time);
469 }
470 printf("\n");
471#endif /* QUEUEBUF_DEBUG */
472}
473/*---------------------------------------------------------------------------*/
474/** @} */
CFS header file.
clock_time_t clock_time(void)
Get the current clock time.
Definition clock.c:118
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition cfs.h:95
int cfs_read(int f, void *buf, unsigned int len)
Read data from an open file.
Definition cfs-cooja.c:85
int cfs_remove(const char *name)
Remove a file.
Definition cfs-cooja.c:147
int cfs_open(const char *n, int f)
Open a file.
Definition cfs-cooja.c:58
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file.
Definition cfs.h:132
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition cfs.h:109
cfs_offset_t cfs_seek(int f, cfs_offset_t o, int w)
Seek to a specified position in an open file.
Definition cfs-cooja.c:126
int cfs_write(int f, const void *buf, unsigned int len)
Write data to an open file.
Definition cfs-cooja.c:102
int cfs_offset_t
CFS directory entry name length.
Definition cfs.h:65
static void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition ctimer.h:137
#define LIST(name)
Declare a linked list.
Definition list.h:90
static void * list_item_next(const void *item)
Get the next item following this item.
Definition list.h:294
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition list.c:71
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition list.c:134
static void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition list.h:169
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition memb.c:78
size_t memb_numfree(struct memb *m)
Count free memory blocks.
Definition memb.c:108
int memb_inmemb(struct memb *m, void *ptr)
Check if a given address is within a memory area previously declared with MEMB().
Definition memb.c:101
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition memb.c:59
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition memb.c:52
#define MEMB(name, structure, num)
Declare a memory block.
Definition memb.h:91
int packetbuf_copyfrom(const void *from, uint16_t len)
Copy from external data into the packetbuf.
Definition packetbuf.c:84
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition packetbuf.h:67
int packetbuf_copyto(void *to)
Copy the entire packetbuf to an external buffer.
Definition packetbuf.c:96
Linked list manipulation routines.
Header file for the Packet queue buffer management.