Contiki 2.5
storage_mmem.c
Go to the documentation of this file.
1 /**
2  * \addtogroup bundle_storage
3  * @{
4  */
5 
6 /**
7  * \defgroup bundle_storage_mmem MMEM-based temporary Storage
8  *
9  * @{
10  */
11 
12 /**
13  * \file
14  * \author Georg von Zengen <vonzeng@ibr.cs.tu-bs.de>
15  * \author Daniel Willmann <daniel@totalueberwachung.de>
16  * \author Wolf-Bastian Poettner <poettner@ibr.cs.tu-bs.de>
17  */
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 
23 #include "contiki.h"
24 #include "lib/mmem.h"
25 #include "lib/list.h"
26 #include "logging.h"
27 
28 #include "bundle.h"
29 #include "sdnv.h"
30 #include "agent.h"
31 #include "statusreport.h"
32 #include "profiling.h"
33 #include "statistics.h"
34 #include "hash.h"
35 
36 #include "storage.h"
37 
38 // defined in mmem.c, no function to access it though
39 extern unsigned int avail_memory;
40 
41 /**
42  * Internal representation of a bundle
43  *
44  * The layout is quite fixed - the next pointer and the bundle_num have to go first because this struct
45  * has to be compatible with the struct storage_entry_t in storage.h!
46  */
47 struct bundle_list_entry_t {
48  /** pointer to the next list element */
49  struct bundle_list_entry_t * next;
50 
51  /** copy of the bundle number - necessary to have
52  * a static address that we can pass on as an
53  * argument to an event
54  */
55  uint32_t bundle_num;
56 
57  /** Flags */
58  uint8_t flags;
59 
60  /** pointer to the actual bundle stored in MMEM */
61  struct mmem *bundle;
62 };
63 
64 /**
65  * Flags for the storage
66  */
67 #define STORAGE_MMEM_FLAGS_LOCKED 0x1
68 
69 // List and memory blocks for the bundles
70 LIST(bundle_list);
71 MEMB(bundle_mem, struct bundle_list_entry_t, BUNDLE_STORAGE_SIZE);
72 
73 // global, internal variables
74 /** Counts the number of bundles in storage */
75 static uint16_t bundles_in_storage;
76 
77 /** Is used to periodically traverse all bundles and delete those that are expired */
78 static struct ctimer r_store_timer;
79 
80 /**
81  * "Internal" functions
82  */
83 void storage_mmem_prune();
84 void storage_mmem_reinit(void);
85 uint16_t storage_mmem_delete_bundle(uint32_t bundle_number, uint8_t reason);
87 
88 /**
89  * \brief internal function to send statistics to statistics module
90  */
92  statistics_storage_bundles(bundles_in_storage);
93  statistics_storage_memory(avail_memory);
94 }
95 
96 /**
97  * \brief called by agent at startup
98  */
100 {
101  LOG(LOGD_DTN, LOG_STORE, LOGL_INF, "storage_mmem init");
102 
103  // Initialize the bundle list
104  list_init(bundle_list);
105 
106  // Initialize the bundle memory block
107  memb_init(&bundle_mem);
108 
109  // Initialize MMEM for the binary bundle storage
110  mmem_init();
111 
112  bundles_in_storage = 0;
113 
116 
117  ctimer_set(&r_store_timer, CLOCK_SECOND*5, storage_mmem_prune, NULL);
118 }
119 
120 /**
121  * \brief deletes expired bundles from storage
122  */
124 {
125  uint32_t elapsed_time;
126  struct bundle_list_entry_t * entry = NULL;
127  struct bundle_t *bundle = NULL;
128 
129  // Delete expired bundles from storage
130  for(entry = list_head(bundle_list);
131  entry != NULL;
132  entry = list_item_next(entry)) {
133  bundle = (struct bundle_t *) MMEM_PTR(entry->bundle);
134  elapsed_time = clock_seconds() - bundle->rec_time;
135 
136  if( bundle->lifetime < elapsed_time ) {
137  LOG(LOGD_DTN, LOG_STORE, LOGL_INF, "bundle lifetime expired of bundle %lu", entry->bundle_num);
138  storage_mmem_delete_bundle(bundle->bundle_num, REASON_LIFETIME_EXPIRED);
139  }
140  }
141 
142  ctimer_restart(&r_store_timer);
143 }
144 
145 /**
146  * \brief Sets the storage to its initial state
147  */
149 {
150  struct bundle_list_entry_t * entry = NULL;
151  struct bundle_t *bundle = NULL;
152 
153  // Delete all bundles from storage
154  for(entry = list_head(bundle_list);
155  entry != NULL;
156  entry = list_item_next(entry)) {
157  bundle = (struct bundle_t *) MMEM_PTR(entry->bundle);
158 
159  storage_mmem_delete_bundle(bundle->bundle_num, REASON_NO_INFORMATION);
160  }
161 }
162 
163 /**
164  * \brief This function delete as many bundles from the storage as necessary to have at least one slot and the number of required of memory free
165  * \param bundlemem Pointer to the MMEM struct containing the bundle
166  * \return 1 on success, 0 if no room could be made free
167  */
168 uint8_t storage_mmem_make_room(struct mmem * bundlemem)
169 {
170  /* Now delete expired bundles */
172 
173  /* If we do not have a pointer, we cannot compare - do nothing */
174  if( bundlemem == NULL ) {
175  return 0;
176  }
177 
178 #if BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DO_NOT_DELETE
179  /* We do not delete at all. If storage is used up, we sit there and wait */
180  if( bundles_in_storage >= BUNDLE_STORAGE_SIZE ) {
181  return 0;
182  }
183 #elif (BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_OLDEST || BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_YOUNGEST )
184  struct bundle_t * bundle_new = NULL;
185  struct bundle_t * bundle_old = NULL;
186  struct bundle_list_entry_t * entry = NULL;
187 
188  /* Keep deleting bundles until we have enough slots */
189  while( bundles_in_storage >= BUNDLE_STORAGE_SIZE) {
190  unsigned long comparator = 0;
191  struct bundle_list_entry_t * deletor = NULL;
192 
193  /* Obtain the new pointer each time, since the address may change */
194  bundle_new = (struct bundle_t *) MMEM_PTR(bundlemem);
195 
196  for( entry = list_head(bundle_list);
197  entry != NULL;
198  entry = list_item_next(entry) ) {
199 
200  bundle_old = (struct bundle_t *) MMEM_PTR(entry->bundle);
201 
202  /* Never delete locked bundles */
203  if( entry->flags & STORAGE_MMEM_FLAGS_LOCKED ) {
204  continue;
205  }
206 
207  /* Always keep bundles with higher priority */
208  if( (bundle_old->flags & BUNDLE_PRIORITY_MASK) > (bundle_new->flags & BUNDLE_PRIORITY_MASK ) ) {
209  continue;
210  }
211 
212 #if BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_OLDEST
213  if( (clock_seconds() - bundle->rec_time) > comparator || comparator == 0) {
214  comparator = clock_seconds() - bundle_old->rec_time;
215  deletor = entry;
216  }
217 #elif BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_YOUNGEST
218  if( (clock_seconds() - bundle->rec_time) < comparator || comparator == 0) {
219  comparator = clock_seconds() - bundle_old->rec_time;
220  deletor = entry;
221  }
222 #endif
223  }
224 
225  /* Either the for loop did nothing or did not break */
226  if( entry == NULL ) {
227  /* We do not have deletable bundles in storage, stop deleting them */
228  return 0;
229  }
230 
231  /* Delete Bundle */
232  storage_mmem_delete_bundle(entry->bundle_num, REASON_DEPLETED_STORAGE);
233  }
234 #elif (BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_OLDER || BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_YOUNGER )
235  struct bundle_t * bundle_new = NULL;
236  struct bundle_t * bundle_old = NULL;
237  struct bundle_list_entry_t * entry = NULL;
238 
239  /* Keep deleting bundles until we have enough slots */
240  while( bundles_in_storage >= BUNDLE_STORAGE_SIZE) {
241  /* Obtain the new pointer each time, since the address may change */
242  bundle_new = (struct bundle_t *) MMEM_PTR(bundlemem);
243 
244  /* We need this double-loop because otherwise we would be modifying the list
245  * while iterating through it
246  */
247  for( entry = list_head(bundle_list);
248  entry != NULL;
249  entry = list_item_next(entry) ) {
250  bundle_old = (struct bundle_t *) MMEM_PTR(entry->bundle);
251 
252  /* Never delete locked bundles */
253  if( entry->flags & STORAGE_MMEM_FLAGS_LOCKED ) {
254  continue;
255  }
256 
257  /* Always keep bundles with higher priority */
258  if( (bundle_old->flags & BUNDLE_PRIORITY_MASK) > (bundle_new->flags & BUNDLE_PRIORITY_MASK ) ) {
259  continue;
260  }
261 
262 #if BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_OLDER
263  /* If the new bundle has a longer lifetime than the bundle in our storage,
264  * delete the bundle from storage to make room
265  */
266  if( bundle_new->lifetime - (clock_seconds() - bundle_new->rec_time) >= bundle_old->lifetime - (clock_seconds() - bundle_old->rec_time) ) {
267  break;
268  }
269 #elif BUNDLE_STORAGE_BEHAVIOUR == BUNDLE_STORAGE_BEHAVIOUR_DELETE_YOUNGER
270  /* Delete youngest bundle in storage */
271  if( bundle_new->lifetime - (clock_seconds() - bundle_new->rec_time) >= bundle_old->lifetime - (clock_seconds() - bundle_old->rec_time) ) {
272  break;
273  }
274 #endif
275  }
276 
277  /* Either the for loop did nothing or did not break */
278  if( entry == NULL ) {
279  /* We do not have deletable bundles in storage, stop deleting them */
280  return 0;
281  }
282 
283  /* Delete Bundle */
284  storage_mmem_delete_bundle(entry->bundle_num, REASON_DEPLETED_STORAGE);
285  }
286 #else
287 #error No Bundle Deletion Strategy defined
288 #endif
289 
290  return 1;
291 }
292 
293 /**
294  * \brief saves a bundle in storage
295  * \param bundlemem pointer to the bundle
296  * \param bundle_number_ptr pointer where the bundle number will be stored (on success)
297  * \return 0 on error, 1 on success
298  */
299 uint8_t storage_mmem_save_bundle(struct mmem * bundlemem, uint32_t ** bundle_number_ptr)
300 {
301  struct bundle_t *entrybdl = NULL,
302  *bundle = NULL;
303  struct bundle_list_entry_t * entry = NULL;
304 
305  if( bundlemem == NULL ) {
306  LOG(LOGD_DTN, LOG_STORE, LOGL_WRN, "storage_mmem_save_bundle with invalid pointer %p", bundlemem);
307  return 0;
308  }
309 
310  // Get the pointer to our bundle
311  bundle = (struct bundle_t *) MMEM_PTR(bundlemem);
312 
313  if( bundle == NULL ) {
314  LOG(LOGD_DTN, LOG_STORE, LOGL_ERR, "storage_mmem_save_bundle with invalid MMEM structure");
315  bundle_decrement(bundlemem);
316  return 0;
317  }
318 
319  // Look for duplicates in the storage
320  for(entry = list_head(bundle_list);
321  entry != NULL;
322  entry = list_item_next(entry)) {
323  entrybdl = (struct bundle_t *) MMEM_PTR(entry->bundle);
324 
325  if( bundle->bundle_num == entrybdl->bundle_num ) {
326  LOG(LOGD_DTN, LOG_STORE, LOGL_DBG, "%lu is the same bundle", entry->bundle_num);
327  *bundle_number_ptr = &entry->bundle_num;
328  bundle_decrement(bundlemem);
329  return 1;
330  }
331  }
332 
333  if( !storage_mmem_make_room(bundlemem) ) {
334  LOG(LOGD_DTN, LOG_STORE, LOGL_ERR, "Cannot store bundle, no room");
335 
336  /* Throw away bundle to not take up RAM */
337  bundle_decrement(bundlemem);
338 
339  return 0;
340  }
341 
342  // Now we have to update the pointer to our bundle, because MMEM may have been modified (freed) and thus the pointer may have changed
343  bundle = (struct bundle_t *) MMEM_PTR(bundlemem);
344 
345  entry = memb_alloc(&bundle_mem);
346  if( entry == NULL ) {
347  LOG(LOGD_DTN, LOG_STORE, LOGL_ERR, "unable to allocate struct, cannot store bundle");
348  bundle_decrement(bundlemem);
349  return 0;
350  }
351 
352  // Clear the memory area
353  memset(entry, 0, sizeof(struct bundle_list_entry_t));
354 
355  // we copy the reference to the bundle, therefore we have to increase the reference counter
356  entry->bundle = bundlemem;
357  bundle_increment(bundlemem);
358  bundles_in_storage++;
359 
360  // Set all required fields
361  entry->bundle_num = bundle->bundle_num;
362 
363  LOG(LOGD_DTN, LOG_STORE, LOGL_INF, "New Bundle %lu (%lu), Src %lu, Dest %lu, Seq %lu", bundle->bundle_num, entry->bundle_num, bundle->src_node, bundle->dst_node, bundle->tstamp_seq);
364 
365  // Notify the statistics module
367 
368  // Add bundle to the list
369  list_add(bundle_list, entry);
370 
371  // Now we have to (virtually) free the incoming bundle slot
372  // This should do nothing, as we have incremented the reference counter before
373  bundle_decrement(bundlemem);
374 
375  // Now copy over the STATIC pointer to the bundle number, so that
376  // the caller can stick it into an event
377  *bundle_number_ptr = &entry->bundle_num;
378 
379  return 1;
380 }
381 
382 /**
383  * \brief deletes a bundle from storage
384  * \param bundle_number bundle number to be deleted
385  * \param reason reason code
386  * \return 1 on success or 0 on error
387  */
388 uint16_t storage_mmem_delete_bundle(uint32_t bundle_number, uint8_t reason)
389 {
390  struct bundle_t * bundle = NULL;
391  struct bundle_list_entry_t * entry = NULL;
392 
393  LOG(LOGD_DTN, LOG_STORE, LOGL_INF, "Deleting Bundle %lu with reason %u", bundle_number, reason);
394 
395  // Look for the bundle we are talking about
396  for(entry = list_head(bundle_list);
397  entry != NULL;
398  entry = list_item_next(entry)) {
399  bundle = (struct bundle_t *) MMEM_PTR(entry->bundle);
400 
401  if( bundle->bundle_num == bundle_number ) {
402  break;
403  }
404  }
405 
406  if( entry == NULL ) {
407  LOG(LOGD_DTN, LOG_STORE, LOGL_ERR, "Could not find bundle %lu on storage_mmem_delete_bundle", bundle_number);
408  return 0;
409  }
410 
411  // Figure out the source to send status report
412  bundle = (struct bundle_t *) MMEM_PTR(entry->bundle);
413  bundle->del_reason = reason;
414 
415  if( reason != REASON_DELIVERED ) {
416  if( (bundle->flags & BUNDLE_FLAG_CUST_REQ ) || (bundle->flags & BUNDLE_FLAG_REP_DELETE) ){
417  if (bundle->src_node != dtn_node_id){
418  STATUSREPORT.send(entry->bundle, 16, bundle->del_reason);
419  }
420  }
421  }
422 
423  // Notified the agent, that a bundle has been deleted
424  agent_delete_bundle(bundle_number);
425 
426  bundle_decrement(entry->bundle);
427  bundle = NULL;
428 
429  // Remove the bundle from the list
430  list_remove(bundle_list, entry);
431 
432  bundles_in_storage--;
433 
434  // Notify the statistics module
436 
437  // Free the storage struct
438  memb_free(&bundle_mem, entry);
439 
440  return 1;
441 }
442 
443 /**
444  * \brief reads a bundle from storage
445  * \param bundle_num bundle number to read
446  * \return pointer to the MMEM struct, NULL on error
447  */
448 struct mmem *storage_mmem_read_bundle(uint32_t bundle_num)
449 {
450  struct bundle_list_entry_t * entry = NULL;
451  struct bundle_t * bundle = NULL;
452 
453  // Look for the bundle we are talking about
454  for(entry = list_head(bundle_list);
455  entry != NULL;
456  entry = list_item_next(entry)) {
457  bundle = (struct bundle_t *) MMEM_PTR(entry->bundle);
458 
459  if( bundle->bundle_num == bundle_num ) {
460  break;
461  }
462  }
463 
464  if( entry == NULL ) {
465  LOG(LOGD_DTN, LOG_STORE, LOGL_WRN, "Could not find bundle %lu in storage_mmem_read_bundle", bundle_num);
466  return 0;
467  }
468 
469  if( entry->bundle->size == 0 ) {
470  LOG(LOGD_DTN, LOG_STORE, LOGL_WRN, "Found bundle %lu but file size is %u", bundle_num, entry->bundle->size);
471  return 0;
472  }
473 
474  // Someone requested the bundle, he will have to decrease the reference counter again
475  bundle_increment(entry->bundle);
476 
477  /* How long did this bundle rot in our storage? */
478  uint32_t elapsed_time = clock_seconds() - bundle->rec_time;
479 
480  /* Update lifetime of bundle */
481  if( bundle->lifetime < elapsed_time ) {
482  bundle->lifetime = 0;
483  bundle->rec_time = clock_seconds();
484  } else {
485  bundle->lifetime = bundle->lifetime - elapsed_time;
486  bundle->rec_time = clock_seconds();
487  }
488 
489  return entry->bundle;
490 }
491 
492 /**
493  * \brief checks if there is space for a bundle
494  * \param bundlemem pointer to a bundle struct (not used here)
495  * \return number of free slots
496  */
497 uint16_t storage_mmem_get_free_space(struct mmem * bundlemem)
498 {
499  return BUNDLE_STORAGE_SIZE - bundles_in_storage;
500 }
501 
502 /**
503  * \brief Get the number of slots available in storage
504  * \returns the number of free slots
505  */
507  return bundles_in_storage;
508 }
509 
510 /**
511  * \brief Get the bundle list
512  * \returns pointer to first bundle list entry
513  */
515 {
516  return (struct storage_entry_t *) list_head(bundle_list);
517 }
518 
519 /**
520  * \brief Mark a bundle as locked so that it will not be deleted even if we are running out of space
521  *
522  * \param bundle_num Bundle number
523  * \return 1 on success or 0 on error
524  */
526 {
527  struct bundle_list_entry_t * entry = NULL;
528 
529  // Look for the bundle we are talking about
530  for(entry = list_head(bundle_list);
531  entry != NULL;
532  entry = list_item_next(entry)) {
533  if( entry->bundle_num == bundle_num ) {
534  break;
535  }
536  }
537 
538  if( entry == NULL ) {
539  return 0;
540  }
541 
542  entry->flags |= STORAGE_MMEM_FLAGS_LOCKED;
543 
544  return 1;
545 }
546 
547 /**
548  * \brief Mark a bundle as unlocked after being locked previously
549  */
550 void storage_mmem_unlock_bundle(uint32_t bundle_num)
551 {
552  struct bundle_list_entry_t * entry = NULL;
553 
554  // Look for the bundle we are talking about
555  for(entry = list_head(bundle_list);
556  entry != NULL;
557  entry = list_item_next(entry)) {
558  if( entry->bundle_num == bundle_num ) {
559  break;
560  }
561  }
562 
563  if( entry == NULL ) {
564  return;
565  }
566 
567  entry->flags &= ~STORAGE_MMEM_FLAGS_LOCKED;
568 }
569 
570 
571 const struct storage_driver storage_mmem = {
572  "STORAGE_MMEM",
583 };
584 
585 /** @} */
586 /** @} */