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