Contiki 2.6
|
00001 /** 00002 * \addtogroup rimequeuebuf 00003 * @{ 00004 */ 00005 00006 /* 00007 * Copyright (c) 2006, Swedish Institute of Computer Science. 00008 * All rights reserved. 00009 * 00010 * Redistribution and use in source and binary forms, with or without 00011 * modification, are permitted provided that the following conditions 00012 * are met: 00013 * 1. Redistributions of source code must retain the above copyright 00014 * notice, this list of conditions and the following disclaimer. 00015 * 2. Redistributions in binary form must reproduce the above copyright 00016 * notice, this list of conditions and the following disclaimer in the 00017 * documentation and/or other materials provided with the distribution. 00018 * 3. Neither the name of the Institute nor the names of its contributors 00019 * may be used to endorse or promote products derived from this software 00020 * without specific prior written permission. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 00023 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00024 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00025 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 00026 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00028 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00029 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00032 * SUCH DAMAGE. 00033 * 00034 * This file is part of the Contiki operating system. 00035 * 00036 * $Id: queuebuf.c,v 1.5 2010/11/25 08:43:59 adamdunkels Exp $ 00037 */ 00038 00039 /** 00040 * \file 00041 * Implementation of the Rime queue buffers 00042 * \author 00043 * Adam Dunkels <adam@sics.se> 00044 */ 00045 00046 #include "contiki-net.h" 00047 #if WITH_SWAP 00048 #include "cfs/cfs.h" 00049 #endif 00050 00051 #include <string.h> /* for memcpy() */ 00052 00053 #ifdef QUEUEBUF_CONF_REF_NUM 00054 #define QUEUEBUF_REF_NUM QUEUEBUF_CONF_REF_NUM 00055 #else 00056 #define QUEUEBUF_REF_NUM 2 00057 #endif 00058 00059 /* Structure pointing to a buffer either stored 00060 in RAM or swapped in CFS */ 00061 struct queuebuf { 00062 #if QUEUEBUF_DEBUG 00063 struct queuebuf *next; 00064 const char *file; 00065 int line; 00066 clock_time_t time; 00067 #endif /* QUEUEBUF_DEBUG */ 00068 #if WITH_SWAP 00069 enum {IN_RAM, IN_CFS} location; 00070 union { 00071 #endif 00072 struct queuebuf_data *ram_ptr; 00073 #if WITH_SWAP 00074 int swap_id; 00075 }; 00076 #endif 00077 }; 00078 00079 /* The actual queuebuf data */ 00080 struct queuebuf_data { 00081 uint16_t len; 00082 uint8_t data[PACKETBUF_SIZE]; 00083 struct packetbuf_attr attrs[PACKETBUF_NUM_ATTRS]; 00084 struct packetbuf_addr addrs[PACKETBUF_NUM_ADDRS]; 00085 }; 00086 00087 struct queuebuf_ref { 00088 uint16_t len; 00089 uint8_t *ref; 00090 uint8_t hdr[PACKETBUF_HDR_SIZE]; 00091 uint8_t hdrlen; 00092 }; 00093 00094 MEMB(bufmem, struct queuebuf, QUEUEBUF_NUM); 00095 MEMB(refbufmem, struct queuebuf_ref, QUEUEBUF_REF_NUM); 00096 MEMB(buframmem, struct queuebuf_data, QUEUEBUFRAM_NUM); 00097 00098 #if WITH_SWAP 00099 00100 /* Swapping allows to store up to QUEUEBUF_NUM - QUEUEBUFRAM_NUM 00101 queuebufs in CFS. The swap is made of several large CFS files. 00102 Every buffer stored in CFS has a swap id, referring to a specific 00103 offset in one of these files. */ 00104 #define NQBUF_FILES 4 00105 #define NQBUF_PER_FILE 256 00106 #define QBUF_FILE_SIZE (NQBUF_PER_FILE*sizeof(struct queuebuf_data)) 00107 #define NQBUF_ID (NQBUF_PER_FILE * NQBUF_FILES) 00108 00109 struct qbuf_file { 00110 int fd; 00111 int usage; 00112 int renewable; 00113 }; 00114 00115 /* A statically allocated queuebuf used as a cache for swapped qbufs */ 00116 static struct queuebuf_data tmpdata; 00117 /* A pointer to the qbuf associated to the data in tmpdata */ 00118 static struct queuebuf *tmpdata_qbuf = NULL; 00119 /* The swap id counter */ 00120 static int next_swap_id = 0; 00121 /* The swap files */ 00122 static struct qbuf_file qbuf_files[NQBUF_FILES]; 00123 /* The timer used to renew files during inactivity periods */ 00124 static struct ctimer renew_timer; 00125 00126 #endif 00127 00128 #if QUEUEBUF_DEBUG 00129 #include "lib/list.h" 00130 LIST(queuebuf_list); 00131 #endif /* QUEUEBUF_DEBUG */ 00132 00133 #define DEBUG 0 00134 #if DEBUG 00135 #include <stdio.h> 00136 #define PRINTF(...) printf(__VA_ARGS__) 00137 #else 00138 #define PRINTF(...) 00139 #endif 00140 00141 #ifdef QUEUEBUF_CONF_STATS 00142 #define QUEUEBUF_STATS QUEUEBUF_CONF_STATS 00143 #else 00144 #define QUEUEBUF_STATS 0 00145 #endif /* QUEUEBUF_CONF_STATS */ 00146 00147 #if QUEUEBUF_STATS 00148 uint8_t queuebuf_len, queuebuf_ref_len, queuebuf_max_len; 00149 #endif /* QUEUEBUF_STATS */ 00150 00151 #if WITH_SWAP 00152 /*---------------------------------------------------------------------------*/ 00153 static void 00154 qbuf_renew_file(int file) 00155 { 00156 int ret; 00157 char name[2]; 00158 name[0] = 'a' + file; 00159 name[1] = '\0'; 00160 if(qbuf_files[file].renewable == 1) { 00161 PRINTF("qbuf_renew_file: removing file %d\n", file); 00162 cfs_remove(name); 00163 } 00164 ret = cfs_open(name, CFS_READ | CFS_WRITE); 00165 if(ret == -1) { 00166 PRINTF("qbuf_renew_file: cfs open error\n"); 00167 } 00168 qbuf_files[file].fd = ret; 00169 qbuf_files[file].usage = 0; 00170 qbuf_files[file].renewable = 0; 00171 } 00172 /*---------------------------------------------------------------------------*/ 00173 /* Renews every file with renewable flag set */ 00174 static void 00175 qbuf_renew_all(void *unused) 00176 { 00177 int i; 00178 for(i=0; i<NQBUF_FILES; i++) { 00179 if(qbuf_files[i].renewable == 1) { 00180 qbuf_renew_file(i); 00181 } 00182 } 00183 } 00184 /*---------------------------------------------------------------------------*/ 00185 /* Removes a queuebuf from its swap file */ 00186 static void 00187 queuebuf_remove_from_file(int swap_id) 00188 { 00189 int fileid; 00190 if(swap_id != -1) { 00191 fileid = swap_id / NQBUF_PER_FILE; 00192 qbuf_files[fileid].usage--; 00193 00194 /* The file is full but doesn't contain any more queuebuf, mark it as renewable */ 00195 if(qbuf_files[fileid].usage == 0 && fileid != next_swap_id / NQBUF_PER_FILE) { 00196 qbuf_files[fileid].renewable = 1; 00197 /* This file is renewable, set a timer to renew files */ 00198 ctimer_set(&renew_timer, 0, qbuf_renew_all, NULL); 00199 } 00200 00201 if(tmpdata_qbuf->swap_id == swap_id) { 00202 tmpdata_qbuf->swap_id = -1; 00203 } 00204 } 00205 } 00206 /*---------------------------------------------------------------------------*/ 00207 static int 00208 get_new_swap_id(void) 00209 { 00210 int fileid; 00211 int swap_id = next_swap_id; 00212 fileid = swap_id / NQBUF_PER_FILE; 00213 if(swap_id % NQBUF_PER_FILE == 0) { /* This is the first id in the file */ 00214 if(qbuf_files[fileid].renewable) { 00215 qbuf_renew_file(fileid); 00216 } 00217 if(qbuf_files[fileid].usage>0) { 00218 return -1; 00219 } 00220 } 00221 qbuf_files[fileid].usage++; 00222 next_swap_id = (next_swap_id+1) % NQBUF_ID; 00223 return swap_id; 00224 } 00225 /*---------------------------------------------------------------------------*/ 00226 /* Flush tmpdata to CFS */ 00227 static int 00228 queuebuf_flush_tmpdata(void) 00229 { 00230 int fileid, fd, ret; 00231 cfs_offset_t offset; 00232 if(tmpdata_qbuf) { 00233 queuebuf_remove_from_file(tmpdata_qbuf->swap_id); 00234 tmpdata_qbuf->swap_id = get_new_swap_id(); 00235 if(tmpdata_qbuf->swap_id == -1) { 00236 return -1; 00237 } 00238 fileid = tmpdata_qbuf->swap_id / NQBUF_PER_FILE; 00239 offset = (tmpdata_qbuf->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data); 00240 fd = qbuf_files[fileid].fd; 00241 ret = cfs_seek(fd, offset, CFS_SEEK_SET); 00242 if(ret == -1) { 00243 PRINTF("queuebuf_flush_tmpdata: cfs seek error\n"); 00244 return -1; 00245 } 00246 ret = cfs_write(fd, &tmpdata, sizeof(struct queuebuf_data)); 00247 if(ret == -1) { 00248 PRINTF("queuebuf_flush_tmpdata: cfs write error\n"); 00249 return -1; 00250 } 00251 } 00252 return 0; 00253 } 00254 /*---------------------------------------------------------------------------*/ 00255 /* If the queuebuf is in CFS, load it to tmpdata */ 00256 static struct queuebuf_data * 00257 queuebuf_load_to_ram(struct queuebuf *b) 00258 { 00259 int fileid, fd, ret; 00260 cfs_offset_t offset; 00261 if(b->location == IN_RAM) { /* the qbuf is loacted in RAM */ 00262 return b->ram_ptr; 00263 } else { /* the qbuf is located in CFS */ 00264 if(tmpdata_qbuf && tmpdata_qbuf->swap_id == b->swap_id) { /* the qbuf is already in tmpdata */ 00265 return &tmpdata; 00266 } else { /* the qbuf needs to be loaded from CFS */ 00267 tmpdata_qbuf = b; 00268 /* read the qbuf from CFS */ 00269 fileid = b->swap_id / NQBUF_PER_FILE; 00270 offset = (b->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data); 00271 fd = qbuf_files[fileid].fd; 00272 ret = cfs_seek(fd, offset, CFS_SEEK_SET); 00273 if(ret == -1) { 00274 PRINTF("queuebuf_load_to_ram: cfs seek error\n"); 00275 } 00276 ret = cfs_read(fd, &tmpdata, sizeof(struct queuebuf_data)); 00277 if(ret == -1) { 00278 PRINTF("queuebuf_load_to_ram: cfs read error\n"); 00279 } 00280 return &tmpdata; 00281 } 00282 } 00283 } 00284 #else /* WITH_SWAP */ 00285 /*---------------------------------------------------------------------------*/ 00286 static struct queuebuf_data * 00287 queuebuf_load_to_ram(struct queuebuf *b) 00288 { 00289 return b->ram_ptr; 00290 } 00291 #endif /* WITH_SWAP */ 00292 /*---------------------------------------------------------------------------*/ 00293 void 00294 queuebuf_init(void) 00295 { 00296 #if WITH_SWAP 00297 int i; 00298 for(i=0; i<NQBUF_FILES; i++) { 00299 qbuf_files[i].renewable = 1; 00300 qbuf_renew_file(i); 00301 } 00302 #endif 00303 memb_init(&buframmem); 00304 memb_init(&bufmem); 00305 memb_init(&refbufmem); 00306 #if QUEUEBUF_STATS 00307 queuebuf_max_len = QUEUEBUF_NUM; 00308 #endif /* QUEUEBUF_STATS */ 00309 } 00310 /*---------------------------------------------------------------------------*/ 00311 #if QUEUEBUF_DEBUG 00312 struct queuebuf * 00313 queuebuf_new_from_packetbuf_debug(const char *file, int line) 00314 #else /* QUEUEBUF_DEBUG */ 00315 struct queuebuf * 00316 queuebuf_new_from_packetbuf(void) 00317 #endif /* QUEUEBUF_DEBUG */ 00318 { 00319 struct queuebuf *buf; 00320 struct queuebuf_ref *rbuf; 00321 00322 if(packetbuf_is_reference()) { 00323 rbuf = memb_alloc(&refbufmem); 00324 if(rbuf != NULL) { 00325 #if QUEUEBUF_STATS 00326 ++queuebuf_ref_len; 00327 #endif /* QUEUEBUF_STATS */ 00328 rbuf->len = packetbuf_datalen(); 00329 rbuf->ref = packetbuf_reference_ptr(); 00330 rbuf->hdrlen = packetbuf_copyto_hdr(rbuf->hdr); 00331 } else { 00332 PRINTF("queuebuf_new_from_packetbuf: could not allocate a reference queuebuf\n"); 00333 } 00334 return (struct queuebuf *)rbuf; 00335 } else { 00336 struct queuebuf_data *buframptr; 00337 buf = memb_alloc(&bufmem); 00338 if(buf != NULL) { 00339 #if QUEUEBUF_DEBUG 00340 list_add(queuebuf_list, buf); 00341 buf->file = file; 00342 buf->line = line; 00343 buf->time = clock_time(); 00344 #endif /* QUEUEBUF_DEBUG */ 00345 buf->ram_ptr = memb_alloc(&buframmem); 00346 #if WITH_SWAP 00347 /* If the allocation failed, store the qbuf in swap files */ 00348 if(buf->ram_ptr != NULL) { 00349 buf->location = IN_RAM; 00350 buframptr = buf->ram_ptr; 00351 } else { 00352 buf->location = IN_CFS; 00353 buf->swap_id = -1; 00354 tmpdata_qbuf = buf; 00355 buframptr = &tmpdata; 00356 } 00357 #else 00358 if(buf->ram_ptr == NULL) { 00359 PRINTF("queuebuf_new_from_packetbuf: could not queuebuf data\n"); 00360 return NULL; 00361 } 00362 buframptr = buf->ram_ptr; 00363 #endif 00364 00365 buframptr->len = packetbuf_copyto(buframptr->data); 00366 packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs); 00367 00368 #if WITH_SWAP 00369 if(buf->location == IN_CFS) { 00370 if(queuebuf_flush_tmpdata() == -1) { 00371 /* We were unable to write the data in the swap */ 00372 memb_free(&bufmem, buf); 00373 return NULL; 00374 } 00375 } 00376 #endif 00377 00378 #if QUEUEBUF_STATS 00379 ++queuebuf_len; 00380 PRINTF("queuebuf len %d\n", queuebuf_len); 00381 printf("#A q=%d\n", queuebuf_len); 00382 if(queuebuf_len == queuebuf_max_len + 1) { 00383 memb_free(&bufmem, buf); 00384 queuebuf_len--; 00385 return NULL; 00386 } 00387 #endif /* QUEUEBUF_STATS */ 00388 00389 } else { 00390 PRINTF("queuebuf_new_from_packetbuf: could not allocate a queuebuf\n"); 00391 } 00392 return buf; 00393 } 00394 } 00395 /*---------------------------------------------------------------------------*/ 00396 void 00397 queuebuf_update_attr_from_packetbuf(struct queuebuf *buf) 00398 { 00399 struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf); 00400 packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs); 00401 #if WITH_SWAP 00402 if(buf->location == IN_CFS) { 00403 queuebuf_flush_tmpdata(); 00404 } 00405 #endif 00406 } 00407 /*---------------------------------------------------------------------------*/ 00408 void 00409 queuebuf_free(struct queuebuf *buf) 00410 { 00411 if(memb_inmemb(&bufmem, buf)) { 00412 #if WITH_SWAP 00413 if(buf->location == IN_RAM) { 00414 memb_free(&buframmem, buf->ram_ptr); 00415 } else { 00416 queuebuf_remove_from_file(buf->swap_id); 00417 } 00418 #else 00419 memb_free(&buframmem, buf->ram_ptr); 00420 #endif 00421 memb_free(&bufmem, buf); 00422 #if QUEUEBUF_STATS 00423 --queuebuf_len; 00424 printf("#A q=%d\n", queuebuf_len); 00425 #endif /* QUEUEBUF_STATS */ 00426 #if QUEUEBUF_DEBUG 00427 list_remove(queuebuf_list, buf); 00428 #endif /* QUEUEBUF_DEBUG */ 00429 } else if(memb_inmemb(&refbufmem, buf)) { 00430 memb_free(&refbufmem, buf); 00431 #if QUEUEBUF_STATS 00432 --queuebuf_ref_len; 00433 #endif /* QUEUEBUF_STATS */ 00434 } 00435 } 00436 /*---------------------------------------------------------------------------*/ 00437 void 00438 queuebuf_to_packetbuf(struct queuebuf *b) 00439 { 00440 struct queuebuf_ref *r; 00441 if(memb_inmemb(&bufmem, b)) { 00442 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); 00443 packetbuf_copyfrom(buframptr->data, buframptr->len); 00444 packetbuf_attr_copyfrom(buframptr->attrs, buframptr->addrs); 00445 } else if(memb_inmemb(&refbufmem, b)) { 00446 r = (struct queuebuf_ref *)b; 00447 packetbuf_clear(); 00448 packetbuf_copyfrom(r->ref, r->len); 00449 packetbuf_hdralloc(r->hdrlen); 00450 memcpy(packetbuf_hdrptr(), r->hdr, r->hdrlen); 00451 } 00452 } 00453 /*---------------------------------------------------------------------------*/ 00454 void * 00455 queuebuf_dataptr(struct queuebuf *b) 00456 { 00457 struct queuebuf_ref *r; 00458 00459 if(memb_inmemb(&bufmem, b)) { 00460 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); 00461 return buframptr->data; 00462 } else if(memb_inmemb(&refbufmem, b)) { 00463 r = (struct queuebuf_ref *)b; 00464 return r->ref; 00465 } 00466 return NULL; 00467 } 00468 /*---------------------------------------------------------------------------*/ 00469 int 00470 queuebuf_datalen(struct queuebuf *b) 00471 { 00472 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); 00473 return buframptr->len; 00474 } 00475 /*---------------------------------------------------------------------------*/ 00476 rimeaddr_t * 00477 queuebuf_addr(struct queuebuf *b, uint8_t type) 00478 { 00479 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); 00480 return &buframptr->addrs[type - PACKETBUF_ADDR_FIRST].addr; 00481 } 00482 /*---------------------------------------------------------------------------*/ 00483 packetbuf_attr_t 00484 queuebuf_attr(struct queuebuf *b, uint8_t type) 00485 { 00486 struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); 00487 return buframptr->attrs[type].val; 00488 } 00489 /*---------------------------------------------------------------------------*/ 00490 void 00491 queuebuf_debug_print(void) 00492 { 00493 #if QUEUEBUF_DEBUG 00494 struct queuebuf *q; 00495 printf("queuebuf_list: "); 00496 for(q = list_head(queuebuf_list); q != NULL; 00497 q = list_item_next(q)) { 00498 printf("%s,%d,%lu ", q->file, q->line, q->time); 00499 } 00500 printf("\n"); 00501 #endif /* QUEUEBUF_DEBUG */ 00502 } 00503 /*---------------------------------------------------------------------------*/ 00504 /** @} */