Contiki 3.x
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 Rime queue buffers
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  */
39 
40 /**
41  * \addtogroup rimequeuebuf
42  * @{
43  */
44 
45 #include "contiki-net.h"
46 #if WITH_SWAP
47 #include "cfs/cfs.h"
48 #endif
49 
50 #include <string.h> /* for memcpy() */
51 
52 #ifdef QUEUEBUF_CONF_REF_NUM
53 #define QUEUEBUF_REF_NUM QUEUEBUF_CONF_REF_NUM
54 #else
55 #define QUEUEBUF_REF_NUM 2
56 #endif
57 
58 /* Structure pointing to a buffer either stored
59  in RAM or swapped in CFS */
60 struct queuebuf {
61 #if QUEUEBUF_DEBUG
62  struct queuebuf *next;
63  const char *file;
64  int line;
65  clock_time_t time;
66 #endif /* QUEUEBUF_DEBUG */
67 #if WITH_SWAP
68  enum {IN_RAM, IN_CFS} location;
69  union {
70 #endif
71  struct queuebuf_data *ram_ptr;
72 #if WITH_SWAP
73  int swap_id;
74  };
75 #endif
76 };
77 
78 /* The actual queuebuf data */
79 struct queuebuf_data {
80  uint8_t data[PACKETBUF_SIZE];
81  uint16_t len;
82  struct packetbuf_attr attrs[PACKETBUF_NUM_ATTRS];
83  struct packetbuf_addr addrs[PACKETBUF_NUM_ADDRS];
84 };
85 
86 struct queuebuf_ref {
87  uint16_t len;
88  uint8_t *ref;
89  uint8_t hdr[PACKETBUF_HDR_SIZE];
90  uint8_t hdrlen;
91 };
92 
93 MEMB(bufmem, struct queuebuf, QUEUEBUF_NUM);
94 MEMB(refbufmem, struct queuebuf_ref, QUEUEBUF_REF_NUM);
95 MEMB(buframmem, struct queuebuf_data, QUEUEBUFRAM_NUM);
96 
97 #if WITH_SWAP
98 
99 /* Swapping allows to store up to QUEUEBUF_NUM - QUEUEBUFRAM_NUM
100  queuebufs in CFS. The swap is made of several large CFS files.
101  Every buffer stored in CFS has a swap id, referring to a specific
102  offset in one of these files. */
103 #define NQBUF_FILES 4
104 #define NQBUF_PER_FILE 256
105 #define QBUF_FILE_SIZE (NQBUF_PER_FILE*sizeof(struct queuebuf_data))
106 #define NQBUF_ID (NQBUF_PER_FILE * NQBUF_FILES)
107 
108 struct qbuf_file {
109  int fd;
110  int usage;
111  int renewable;
112 };
113 
114 /* A statically allocated queuebuf used as a cache for swapped qbufs */
115 static struct queuebuf_data tmpdata;
116 /* A pointer to the qbuf associated to the data in tmpdata */
117 static struct queuebuf *tmpdata_qbuf = NULL;
118 /* The swap id counter */
119 static int next_swap_id = 0;
120 /* The swap files */
121 static struct qbuf_file qbuf_files[NQBUF_FILES];
122 /* The timer used to renew files during inactivity periods */
123 static struct ctimer renew_timer;
124 
125 #endif
126 
127 #if QUEUEBUF_DEBUG
128 #include "lib/list.h"
129 LIST(queuebuf_list);
130 #endif /* QUEUEBUF_DEBUG */
131 
132 #define DEBUG 0
133 #if DEBUG
134 #include <stdio.h>
135 #define PRINTF(...) printf(__VA_ARGS__)
136 #else
137 #define PRINTF(...)
138 #endif
139 
140 #ifdef QUEUEBUF_CONF_STATS
141 #define QUEUEBUF_STATS QUEUEBUF_CONF_STATS
142 #else
143 #define QUEUEBUF_STATS 0
144 #endif /* QUEUEBUF_CONF_STATS */
145 
146 #if QUEUEBUF_STATS
147 uint8_t queuebuf_len, queuebuf_ref_len, queuebuf_max_len;
148 #endif /* QUEUEBUF_STATS */
149 
150 #if WITH_SWAP
151 /*---------------------------------------------------------------------------*/
152 static void
153 qbuf_renew_file(int file)
154 {
155  int ret;
156  char name[2];
157  name[0] = 'a' + file;
158  name[1] = '\0';
159  if(qbuf_files[file].renewable == 1) {
160  PRINTF("qbuf_renew_file: removing file %d\n", file);
161  cfs_remove(name);
162  }
163  ret = cfs_open(name, CFS_READ | CFS_WRITE);
164  if(ret == -1) {
165  PRINTF("qbuf_renew_file: cfs open error\n");
166  }
167  qbuf_files[file].fd = ret;
168  qbuf_files[file].usage = 0;
169  qbuf_files[file].renewable = 0;
170 }
171 /*---------------------------------------------------------------------------*/
172 /* Renews every file with renewable flag set */
173 static void
174 qbuf_renew_all(void *unused)
175 {
176  int i;
177  for(i=0; i<NQBUF_FILES; i++) {
178  if(qbuf_files[i].renewable == 1) {
179  qbuf_renew_file(i);
180  }
181  }
182 }
183 /*---------------------------------------------------------------------------*/
184 /* Removes a queuebuf from its swap file */
185 static void
186 queuebuf_remove_from_file(int swap_id)
187 {
188  int fileid;
189  if(swap_id != -1) {
190  fileid = swap_id / NQBUF_PER_FILE;
191  qbuf_files[fileid].usage--;
192 
193  /* The file is full but doesn't contain any more queuebuf, mark it as renewable */
194  if(qbuf_files[fileid].usage == 0 && fileid != next_swap_id / NQBUF_PER_FILE) {
195  qbuf_files[fileid].renewable = 1;
196  /* This file is renewable, set a timer to renew files */
197  ctimer_set(&renew_timer, 0, qbuf_renew_all, NULL);
198  }
199 
200  if(tmpdata_qbuf->swap_id == swap_id) {
201  tmpdata_qbuf->swap_id = -1;
202  }
203  }
204 }
205 /*---------------------------------------------------------------------------*/
206 static int
207 get_new_swap_id(void)
208 {
209  int fileid;
210  int swap_id = next_swap_id;
211  fileid = swap_id / NQBUF_PER_FILE;
212  if(swap_id % NQBUF_PER_FILE == 0) { /* This is the first id in the file */
213  if(qbuf_files[fileid].renewable) {
214  qbuf_renew_file(fileid);
215  }
216  if(qbuf_files[fileid].usage>0) {
217  return -1;
218  }
219  }
220  qbuf_files[fileid].usage++;
221  next_swap_id = (next_swap_id+1) % NQBUF_ID;
222  return swap_id;
223 }
224 /*---------------------------------------------------------------------------*/
225 /* Flush tmpdata to CFS */
226 static int
227 queuebuf_flush_tmpdata(void)
228 {
229  int fileid, fd, ret;
230  cfs_offset_t offset;
231  if(tmpdata_qbuf) {
232  queuebuf_remove_from_file(tmpdata_qbuf->swap_id);
233  tmpdata_qbuf->swap_id = get_new_swap_id();
234  if(tmpdata_qbuf->swap_id == -1) {
235  return -1;
236  }
237  fileid = tmpdata_qbuf->swap_id / NQBUF_PER_FILE;
238  offset = (tmpdata_qbuf->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data);
239  fd = qbuf_files[fileid].fd;
240  ret = cfs_seek(fd, offset, CFS_SEEK_SET);
241  if(ret == -1) {
242  PRINTF("queuebuf_flush_tmpdata: cfs seek error\n");
243  return -1;
244  }
245  ret = cfs_write(fd, &tmpdata, sizeof(struct queuebuf_data));
246  if(ret == -1) {
247  PRINTF("queuebuf_flush_tmpdata: cfs write error\n");
248  return -1;
249  }
250  }
251  return 0;
252 }
253 /*---------------------------------------------------------------------------*/
254 /* If the queuebuf is in CFS, load it to tmpdata */
255 static struct queuebuf_data *
256 queuebuf_load_to_ram(struct queuebuf *b)
257 {
258  int fileid, fd, ret;
259  cfs_offset_t offset;
260  if(b->location == IN_RAM) { /* the qbuf is loacted in RAM */
261  return b->ram_ptr;
262  } else { /* the qbuf is located in CFS */
263  if(tmpdata_qbuf && tmpdata_qbuf->swap_id == b->swap_id) { /* the qbuf is already in tmpdata */
264  return &tmpdata;
265  } else { /* the qbuf needs to be loaded from CFS */
266  tmpdata_qbuf = b;
267  /* read the qbuf from CFS */
268  fileid = b->swap_id / NQBUF_PER_FILE;
269  offset = (b->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data);
270  fd = qbuf_files[fileid].fd;
271  ret = cfs_seek(fd, offset, CFS_SEEK_SET);
272  if(ret == -1) {
273  PRINTF("queuebuf_load_to_ram: cfs seek error\n");
274  }
275  ret = cfs_read(fd, &tmpdata, sizeof(struct queuebuf_data));
276  if(ret == -1) {
277  PRINTF("queuebuf_load_to_ram: cfs read error\n");
278  }
279  return &tmpdata;
280  }
281  }
282 }
283 #else /* WITH_SWAP */
284 /*---------------------------------------------------------------------------*/
285 static struct queuebuf_data *
286 queuebuf_load_to_ram(struct queuebuf *b)
287 {
288  return b->ram_ptr;
289 }
290 #endif /* WITH_SWAP */
291 /*---------------------------------------------------------------------------*/
292 void
293 queuebuf_init(void)
294 {
295 #if WITH_SWAP
296  int i;
297  for(i=0; i<NQBUF_FILES; i++) {
298  qbuf_files[i].renewable = 1;
299  qbuf_renew_file(i);
300  }
301 #endif
302  memb_init(&buframmem);
303  memb_init(&bufmem);
304  memb_init(&refbufmem);
305 #if QUEUEBUF_STATS
306  queuebuf_max_len = QUEUEBUF_NUM;
307 #endif /* QUEUEBUF_STATS */
308 }
309 /*---------------------------------------------------------------------------*/
310 int
311 queuebuf_numfree(void)
312 {
313  if(packetbuf_is_reference()) {
314  return memb_numfree(&refbufmem);
315  } else {
316  return memb_numfree(&bufmem);
317  }
318 }
319 /*---------------------------------------------------------------------------*/
320 #if QUEUEBUF_DEBUG
321 struct queuebuf *
322 queuebuf_new_from_packetbuf_debug(const char *file, int line)
323 #else /* QUEUEBUF_DEBUG */
324 struct queuebuf *
325 queuebuf_new_from_packetbuf(void)
326 #endif /* QUEUEBUF_DEBUG */
327 {
328  struct queuebuf *buf;
329  struct queuebuf_ref *rbuf;
330 
331  if(packetbuf_is_reference()) {
332  rbuf = memb_alloc(&refbufmem);
333  if(rbuf != NULL) {
334 #if QUEUEBUF_STATS
335  ++queuebuf_ref_len;
336 #endif /* QUEUEBUF_STATS */
337  rbuf->len = packetbuf_datalen();
338  rbuf->ref = packetbuf_reference_ptr();
339  rbuf->hdrlen = packetbuf_copyto_hdr(rbuf->hdr);
340  } else {
341  PRINTF("queuebuf_new_from_packetbuf: could not allocate a reference queuebuf\n");
342  }
343  return (struct queuebuf *)rbuf;
344  } else {
345  struct queuebuf_data *buframptr;
346  buf = memb_alloc(&bufmem);
347  if(buf != NULL) {
348 #if QUEUEBUF_DEBUG
349  list_add(queuebuf_list, buf);
350  buf->file = file;
351  buf->line = line;
352  buf->time = clock_time();
353 #endif /* QUEUEBUF_DEBUG */
354  buf->ram_ptr = memb_alloc(&buframmem);
355 #if WITH_SWAP
356  /* If the allocation failed, store the qbuf in swap files */
357  if(buf->ram_ptr != NULL) {
358  buf->location = IN_RAM;
359  buframptr = buf->ram_ptr;
360  } else {
361  buf->location = IN_CFS;
362  buf->swap_id = -1;
363  tmpdata_qbuf = buf;
364  buframptr = &tmpdata;
365  }
366 #else
367  if(buf->ram_ptr == NULL) {
368  PRINTF("queuebuf_new_from_packetbuf: could not queuebuf data\n");
369  memb_free(&bufmem, buf);
370  return NULL;
371  }
372  buframptr = buf->ram_ptr;
373 #endif
374 
375  buframptr->len = packetbuf_copyto(buframptr->data);
376  packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
377 
378 #if WITH_SWAP
379  if(buf->location == IN_CFS) {
380  if(queuebuf_flush_tmpdata() == -1) {
381  /* We were unable to write the data in the swap */
382  memb_free(&bufmem, buf);
383  return NULL;
384  }
385  }
386 #endif
387 
388 #if QUEUEBUF_STATS
389  ++queuebuf_len;
390  PRINTF("queuebuf len %d\n", queuebuf_len);
391  printf("#A q=%d\n", queuebuf_len);
392  if(queuebuf_len == queuebuf_max_len + 1) {
393  queuebuf_free(buf);
394  queuebuf_len--;
395  return NULL;
396  }
397 #endif /* QUEUEBUF_STATS */
398 
399  } else {
400  PRINTF("queuebuf_new_from_packetbuf: could not allocate a queuebuf\n");
401  }
402  return buf;
403  }
404 }
405 /*---------------------------------------------------------------------------*/
406 void
407 queuebuf_update_attr_from_packetbuf(struct queuebuf *buf)
408 {
409  struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf);
410  packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
411 #if WITH_SWAP
412  if(buf->location == IN_CFS) {
413  queuebuf_flush_tmpdata();
414  }
415 #endif
416 }
417 /*---------------------------------------------------------------------------*/
418 void
419 queuebuf_update_from_packetbuf(struct queuebuf *buf)
420 {
421  struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf);
422  packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
423  buframptr->len = packetbuf_copyto(buframptr->data);
424 #if WITH_SWAP
425  if(buf->location == IN_CFS) {
426  queuebuf_flush_tmpdata();
427  }
428 #endif
429 }
430 /*---------------------------------------------------------------------------*/
431 void
432 queuebuf_free(struct queuebuf *buf)
433 {
434  if(memb_inmemb(&bufmem, buf)) {
435 #if WITH_SWAP
436  if(buf->location == IN_RAM) {
437  memb_free(&buframmem, buf->ram_ptr);
438  } else {
439  queuebuf_remove_from_file(buf->swap_id);
440  }
441 #else
442  memb_free(&buframmem, buf->ram_ptr);
443 #endif
444  memb_free(&bufmem, buf);
445 #if QUEUEBUF_STATS
446  --queuebuf_len;
447  printf("#A q=%d\n", queuebuf_len);
448 #endif /* QUEUEBUF_STATS */
449 #if QUEUEBUF_DEBUG
450  list_remove(queuebuf_list, buf);
451 #endif /* QUEUEBUF_DEBUG */
452  } else if(memb_inmemb(&refbufmem, buf)) {
453  memb_free(&refbufmem, buf);
454 #if QUEUEBUF_STATS
455  --queuebuf_ref_len;
456 #endif /* QUEUEBUF_STATS */
457  }
458 }
459 /*---------------------------------------------------------------------------*/
460 void
461 queuebuf_to_packetbuf(struct queuebuf *b)
462 {
463  struct queuebuf_ref *r;
464  if(memb_inmemb(&bufmem, b)) {
465  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
466  packetbuf_copyfrom(buframptr->data, buframptr->len);
467  packetbuf_attr_copyfrom(buframptr->attrs, buframptr->addrs);
468  } else if(memb_inmemb(&refbufmem, b)) {
469  r = (struct queuebuf_ref *)b;
470  packetbuf_clear();
471  packetbuf_copyfrom(r->ref, r->len);
472  packetbuf_hdralloc(r->hdrlen);
473  memcpy(packetbuf_hdrptr(), r->hdr, r->hdrlen);
474  }
475 }
476 /*---------------------------------------------------------------------------*/
477 void *
478 queuebuf_dataptr(struct queuebuf *b)
479 {
480  struct queuebuf_ref *r;
481 
482  if(memb_inmemb(&bufmem, b)) {
483  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
484  return buframptr->data;
485  } else if(memb_inmemb(&refbufmem, b)) {
486  r = (struct queuebuf_ref *)b;
487  return r->ref;
488  }
489  return NULL;
490 }
491 /*---------------------------------------------------------------------------*/
492 int
493 queuebuf_datalen(struct queuebuf *b)
494 {
495  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
496  return buframptr->len;
497 }
498 /*---------------------------------------------------------------------------*/
499 linkaddr_t *
500 queuebuf_addr(struct queuebuf *b, uint8_t type)
501 {
502  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
503  return &buframptr->addrs[type - PACKETBUF_ADDR_FIRST].addr;
504 }
505 /*---------------------------------------------------------------------------*/
506 packetbuf_attr_t
507 queuebuf_attr(struct queuebuf *b, uint8_t type)
508 {
509  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
510  return buframptr->attrs[type].val;
511 }
512 /*---------------------------------------------------------------------------*/
513 void
514 queuebuf_debug_print(void)
515 {
516 #if QUEUEBUF_DEBUG
517  struct queuebuf *q;
518  printf("queuebuf_list: ");
519  for(q = list_head(queuebuf_list); q != NULL;
520  q = list_item_next(q)) {
521  printf("%s,%d,%lu ", q->file, q->line, q->time);
522  }
523  printf("\n");
524 #endif /* QUEUEBUF_DEBUG */
525 }
526 /*---------------------------------------------------------------------------*/
527 /** @} */
Linked list manipulation routines.
int packetbuf_hdralloc(int size)
Extend the header of the packetbuf, for outbound packets.
Definition: packetbuf.c:172
int packetbuf_copyfrom(const void *from, uint16_t len)
Copy from external data into the packetbuf.
Definition: packetbuf.c:93
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:996
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-coffee.c:1042
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition: cfs.h:104
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define NULL
The null pointer.
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition: packetbuf.c:239
CCIF clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:41
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
int cfs_remove(const char *name)
Remove a file.
Definition: cfs-coffee.c:1074
void * packetbuf_hdrptr(void)
Get a pointer to the header in the packetbuf, for outbound packets.
Definition: packetbuf.c:213
void * packetbuf_reference_ptr(void)
Get a pointer to external data referenced by the packetbuf.
Definition: packetbuf.c:233
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
#define PACKETBUF_HDR_SIZE
The size of the packetbuf header, in bytes.
Definition: packetbuf.h:74
int packetbuf_copyto(void *to)
Copy the entire packetbuf to an external buffer.
Definition: packetbuf.c:140
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
int packetbuf_copyto_hdr(uint8_t *to)
Copy the header portion of the packetbuf to an external buffer.
Definition: packetbuf.c:123
#define LIST(name)
Declare a linked list.
Definition: list.h:86
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition: packetbuf.c:77
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition: packetbuf.h:65
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:127
int packetbuf_is_reference(void)
Check if the packetbuf references external data.
Definition: packetbuf.c:227
CFS header file.