Contiki-NG
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 */
56 struct 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 */
75 struct 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 
82 MEMB(bufmem, struct queuebuf, QUEUEBUF_NUM);
83 MEMB(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 
96 struct 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 */
103 static struct queuebuf_data tmpdata;
104 /* A pointer to the qbuf associated to the data in tmpdata */
105 static struct queuebuf *tmpdata_qbuf = NULL;
106 /* The swap id counter */
107 static int next_swap_id = 0;
108 /* The swap files */
109 static struct qbuf_file qbuf_files[NQBUF_FILES];
110 /* The timer used to renew files during inactivity periods */
111 static struct ctimer renew_timer;
112 
113 #endif
114 
115 #if QUEUEBUF_DEBUG
116 #include "lib/list.h"
117 LIST(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
135 uint8_t queuebuf_len, queuebuf_max_len;
136 #endif /* QUEUEBUF_STATS */
137 
138 #if WITH_SWAP
139 /*---------------------------------------------------------------------------*/
140 static void
141 qbuf_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 */
161 static void
162 qbuf_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 */
173 static void
174 queuebuf_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 /*---------------------------------------------------------------------------*/
194 static int
195 get_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 */
214 static int
215 queuebuf_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 */
243 static struct queuebuf_data *
244 queuebuf_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 /*---------------------------------------------------------------------------*/
273 static struct queuebuf_data *
274 queuebuf_load_to_ram(struct queuebuf *b)
275 {
276  return b->ram_ptr;
277 }
278 #endif /* WITH_SWAP */
279 /*---------------------------------------------------------------------------*/
280 void
281 queuebuf_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 /*---------------------------------------------------------------------------*/
297 int
298 queuebuf_numfree(void)
299 {
300  return memb_numfree(&bufmem);
301 }
302 /*---------------------------------------------------------------------------*/
303 #if QUEUEBUF_DEBUG
304 struct queuebuf *
305 queuebuf_new_from_packetbuf_debug(const char *file, int line)
306 #else /* QUEUEBUF_DEBUG */
307 struct queuebuf *
308 queuebuf_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 /*---------------------------------------------------------------------------*/
370 void
371 queuebuf_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 /*---------------------------------------------------------------------------*/
382 void
383 queuebuf_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 /*---------------------------------------------------------------------------*/
395 void
396 queuebuf_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 /*---------------------------------------------------------------------------*/
419 void
420 queuebuf_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 /*---------------------------------------------------------------------------*/
429 void *
430 queuebuf_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 /*---------------------------------------------------------------------------*/
439 int
440 queuebuf_datalen(struct queuebuf *b)
441 {
442  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
443  return buframptr->len;
444 }
445 /*---------------------------------------------------------------------------*/
446 linkaddr_t *
447 queuebuf_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 /*---------------------------------------------------------------------------*/
453 packetbuf_attr_t
454 queuebuf_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 /*---------------------------------------------------------------------------*/
460 void
461 queuebuf_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 /** @} */
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:78
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-sdcard.c:41
int packetbuf_copyto(void *to)
Copy the entire packetbuf to an external buffer.
Definition: packetbuf.c:96
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
int cfs_remove(const char *name)
Remove a file.
Definition: cfs-cooja.c:150
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-sdcard.c:94
Header file for the Packet queue buffer management
Linked list manipulation routines.
int cfs_write(int fd, const void *buf, unsigned int len)
Write data to an open file.
Definition: cfs-sdcard.c:86
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition: packetbuf.h:67
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
CFS header file.
int packetbuf_copyfrom(const void *from, uint16_t len)
Copy from external data into the packetbuf.
Definition: packetbuf.c:84
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
int cfs_read(int fd, void *buf, unsigned int len)
Read data from an open file.
Definition: cfs-sdcard.c:78
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:92
#define LIST(name)
Declare a linked list.
Definition: list.h:89
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:129
int memb_numfree(struct memb *m)
Count free memory blocks.
Definition: memb.c:108
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition: cfs.h:106
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:90