Contiki-Inga 3.x
queuebuf.c
Go to the documentation of this file.
1 /**
2  * \addtogroup rimequeuebuf
3  * @{
4  */
5 
6 /*
7  * Copyright (c) 2006, Swedish Institute of Computer Science.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the Institute nor the names of its contributors
19  * may be used to endorse or promote products derived from this software
20  * without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * This file is part of the Contiki operating system.
35  *
36  */
37 
38 /**
39  * \file
40  * Implementation of the Rime queue buffers
41  * \author
42  * Adam Dunkels <adam@sics.se>
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  uint16_t len;
81  uint8_t data[PACKETBUF_SIZE];
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 #if QUEUEBUF_DEBUG
311 struct queuebuf *
312 queuebuf_new_from_packetbuf_debug(const char *file, int line)
313 #else /* QUEUEBUF_DEBUG */
314 struct queuebuf *
315 queuebuf_new_from_packetbuf(void)
316 #endif /* QUEUEBUF_DEBUG */
317 {
318  struct queuebuf *buf;
319  struct queuebuf_ref *rbuf;
320 
321  if(packetbuf_is_reference()) {
322  rbuf = memb_alloc(&refbufmem);
323  if(rbuf != NULL) {
324 #if QUEUEBUF_STATS
325  ++queuebuf_ref_len;
326 #endif /* QUEUEBUF_STATS */
327  rbuf->len = packetbuf_datalen();
328  rbuf->ref = packetbuf_reference_ptr();
329  rbuf->hdrlen = packetbuf_copyto_hdr(rbuf->hdr);
330  } else {
331  PRINTF("queuebuf_new_from_packetbuf: could not allocate a reference queuebuf\n");
332  }
333  return (struct queuebuf *)rbuf;
334  } else {
335  struct queuebuf_data *buframptr;
336  buf = memb_alloc(&bufmem);
337  if(buf != NULL) {
338 #if QUEUEBUF_DEBUG
339  list_add(queuebuf_list, buf);
340  buf->file = file;
341  buf->line = line;
342  buf->time = clock_time();
343 #endif /* QUEUEBUF_DEBUG */
344  buf->ram_ptr = memb_alloc(&buframmem);
345 #if WITH_SWAP
346  /* If the allocation failed, store the qbuf in swap files */
347  if(buf->ram_ptr != NULL) {
348  buf->location = IN_RAM;
349  buframptr = buf->ram_ptr;
350  } else {
351  buf->location = IN_CFS;
352  buf->swap_id = -1;
353  tmpdata_qbuf = buf;
354  buframptr = &tmpdata;
355  }
356 #else
357  if(buf->ram_ptr == NULL) {
358  PRINTF("queuebuf_new_from_packetbuf: could not queuebuf data\n");
359  return NULL;
360  }
361  buframptr = buf->ram_ptr;
362 #endif
363 
364  buframptr->len = packetbuf_copyto(buframptr->data);
365  packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
366 
367 #if WITH_SWAP
368  if(buf->location == IN_CFS) {
369  if(queuebuf_flush_tmpdata() == -1) {
370  /* We were unable to write the data in the swap */
371  memb_free(&bufmem, buf);
372  return NULL;
373  }
374  }
375 #endif
376 
377 #if QUEUEBUF_STATS
378  ++queuebuf_len;
379  PRINTF("queuebuf len %d\n", queuebuf_len);
380  printf("#A q=%d\n", queuebuf_len);
381  if(queuebuf_len == queuebuf_max_len + 1) {
382  memb_free(&bufmem, buf);
383  queuebuf_len--;
384  return NULL;
385  }
386 #endif /* QUEUEBUF_STATS */
387 
388  } else {
389  PRINTF("queuebuf_new_from_packetbuf: could not allocate a queuebuf\n");
390  }
391  return buf;
392  }
393 }
394 /*---------------------------------------------------------------------------*/
395 void
396 queuebuf_update_attr_from_packetbuf(struct queuebuf *buf)
397 {
398  struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf);
399  packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs);
400 #if WITH_SWAP
401  if(buf->location == IN_CFS) {
402  queuebuf_flush_tmpdata();
403  }
404 #endif
405 }
406 /*---------------------------------------------------------------------------*/
407 void
408 queuebuf_free(struct queuebuf *buf)
409 {
410  if(memb_inmemb(&bufmem, buf)) {
411 #if WITH_SWAP
412  if(buf->location == IN_RAM) {
413  memb_free(&buframmem, buf->ram_ptr);
414  } else {
415  queuebuf_remove_from_file(buf->swap_id);
416  }
417 #else
418  memb_free(&buframmem, buf->ram_ptr);
419 #endif
420  memb_free(&bufmem, buf);
421 #if QUEUEBUF_STATS
422  --queuebuf_len;
423  printf("#A q=%d\n", queuebuf_len);
424 #endif /* QUEUEBUF_STATS */
425 #if QUEUEBUF_DEBUG
426  list_remove(queuebuf_list, buf);
427 #endif /* QUEUEBUF_DEBUG */
428  } else if(memb_inmemb(&refbufmem, buf)) {
429  memb_free(&refbufmem, buf);
430 #if QUEUEBUF_STATS
431  --queuebuf_ref_len;
432 #endif /* QUEUEBUF_STATS */
433  }
434 }
435 /*---------------------------------------------------------------------------*/
436 void
437 queuebuf_to_packetbuf(struct queuebuf *b)
438 {
439  struct queuebuf_ref *r;
440  if(memb_inmemb(&bufmem, b)) {
441  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
442  packetbuf_copyfrom(buframptr->data, buframptr->len);
443  packetbuf_attr_copyfrom(buframptr->attrs, buframptr->addrs);
444  } else if(memb_inmemb(&refbufmem, b)) {
445  r = (struct queuebuf_ref *)b;
446  packetbuf_clear();
447  packetbuf_copyfrom(r->ref, r->len);
448  packetbuf_hdralloc(r->hdrlen);
449  memcpy(packetbuf_hdrptr(), r->hdr, r->hdrlen);
450  }
451 }
452 /*---------------------------------------------------------------------------*/
453 void *
454 queuebuf_dataptr(struct queuebuf *b)
455 {
456  struct queuebuf_ref *r;
457 
458  if(memb_inmemb(&bufmem, b)) {
459  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
460  return buframptr->data;
461  } else if(memb_inmemb(&refbufmem, b)) {
462  r = (struct queuebuf_ref *)b;
463  return r->ref;
464  }
465  return NULL;
466 }
467 /*---------------------------------------------------------------------------*/
468 int
469 queuebuf_datalen(struct queuebuf *b)
470 {
471  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
472  return buframptr->len;
473 }
474 /*---------------------------------------------------------------------------*/
475 linkaddr_t *
476 queuebuf_addr(struct queuebuf *b, uint8_t type)
477 {
478  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
479  return &buframptr->addrs[type - PACKETBUF_ADDR_FIRST].addr;
480 }
481 /*---------------------------------------------------------------------------*/
482 packetbuf_attr_t
483 queuebuf_attr(struct queuebuf *b, uint8_t type)
484 {
485  struct queuebuf_data *buframptr = queuebuf_load_to_ram(b);
486  return buframptr->attrs[type].val;
487 }
488 /*---------------------------------------------------------------------------*/
489 void
490 queuebuf_debug_print(void)
491 {
492 #if QUEUEBUF_DEBUG
493  struct queuebuf *q;
494  printf("queuebuf_list: ");
495  for(q = list_head(queuebuf_list); q != NULL;
496  q = list_item_next(q)) {
497  printf("%s,%d,%lu ", q->file, q->line, q->time);
498  }
499  printf("\n");
500 #endif /* QUEUEBUF_DEBUG */
501 }
502 /*---------------------------------------------------------------------------*/
503 /** @} */