dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_flashfs_logfs.c
Go to the documentation of this file.
1 
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>
24  */
25 
26 /* Project Includes */
27 #include "pios.h"
28 
29 #include "pios_flash.h" /* PIOS_FLASH_* */
30 #include "pios_flashfs_logfs_priv.h" /* Internal API */
31 
32 #include <stdbool.h>
33 #include <stddef.h> /* NULL */
34 
35 #define MIN(x,y) ((x) < (y) ? (x) : (y))
36 
37 /*
38  * Filesystem state data tracked in RAM
39  */
40 
43 };
44 
45 struct logfs_state {
47  const struct flashfs_logfs_cfg *cfg;
48  bool mounted;
49  uint8_t active_arena_id;
50 
51  /* NOTE: num_active_slots + num_free_slots will not typically add
52  * up to the number of slots in the arena since some of the
53  * slots will be obsolete or otherwise invalidated
54  */
55  uint16_t num_free_slots; /* slots in free state */
56  uint16_t num_active_slots; /* slots in active state */
57 
58  /* Underlying flash partition handle */
59  uintptr_t partition_id;
60  uint32_t partition_size;
61 };
62 
63 /*
64  * Internal Utility functions
65  */
66 
71 static uintptr_t logfs_get_addr(const struct logfs_state *logfs, uint8_t arena_id, uint16_t slot_id)
72 {
73  PIOS_Assert(arena_id < (logfs->partition_size / logfs->cfg->arena_size));
74  PIOS_Assert(slot_id < (logfs->cfg->arena_size / logfs->cfg->slot_size));
75 
76  return ((arena_id * logfs->cfg->arena_size) +
77  (slot_id * logfs->cfg->slot_size));
78 }
79 
80 /*
81  * The bits within these enum values must progress ONLY
82  * from 1 -> 0 so that we can write later ones on top
83  * of earlier ones in NOR flash without an erase cycle.
84  */
86  /*
87  * The STM32F30X flash subsystem is only capable of
88  * writing words or halfwords. In this case we use halfwords.
89  * In addition to that it is only capable to write to erased
90  * cells (0xffff) or write a cell from anything to (0x0000).
91  * To cope with this, the F3 needs carefully crafted enum values.
92  * For this to work the underlying flash driver has to
93  * check each halfword if it has changed before writing.
94  */
95  ARENA_STATE_ERASED = 0xFFFFFFFF,
96  ARENA_STATE_RESERVED = 0xE6E6FFFF,
97  ARENA_STATE_ACTIVE = 0xE6E66666,
98  ARENA_STATE_OBSOLETE = 0x00000000,
99 } __attribute__((packed));
100 
101 struct arena_header {
102  uint32_t magic;
104 } __attribute__((packed));
105 
106 
107 /****************************************
108  * Arena life-cycle transition functions
109  ****************************************/
110 
116 static int32_t logfs_erase_arena(const struct logfs_state *logfs, uint8_t arena_id)
117 {
118  uintptr_t arena_addr = logfs_get_addr (logfs, arena_id, 0);
119 
120  /* Erase all of the sectors in the arena */
121  if (PIOS_FLASH_erase_range(logfs->partition_id, arena_addr, logfs->cfg->arena_size) != 0) {
122  return -1;
123  }
124 
125  /* Mark this arena as fully erased */
126  struct arena_header arena_hdr = {
127  .magic = logfs->cfg->fs_magic,
128  .state = ARENA_STATE_ERASED,
129  };
130 
132  arena_addr,
133  (uint8_t *)&arena_hdr,
134  sizeof(arena_hdr)) != 0) {
135  return -2;
136  }
137 
138  /* Arena is ready to be activated */
139  return 0;
140 }
141 
148 static int32_t logfs_reserve_arena (const struct logfs_state *logfs, uint8_t arena_id)
149 {
150  uintptr_t arena_addr = logfs_get_addr (logfs, arena_id, 0);
151 
152  /* Read in the current arena header */
153  struct arena_header arena_hdr;
155  arena_addr,
156  (uint8_t *)&arena_hdr,
157  sizeof(arena_hdr)) != 0) {
158  return -1;
159  }
160  if (arena_hdr.state != ARENA_STATE_ERASED) {
161  /* Arena was not erased, can't reserve it */
162  return -2;
163  }
164 
165  /* Set the arena state to reserved */
166  arena_hdr.state = ARENA_STATE_RESERVED;
167 
168  /* Write the arena header back to flash */
170  arena_addr,
171  (uint8_t *)&arena_hdr,
172  sizeof(arena_hdr)) != 0) {
173  return -3;
174  }
175 
176  /* Arena is ready to be filled */
177  return 0;
178 }
179 
185 static int32_t logfs_erase_all_arenas(const struct logfs_state *logfs)
186 {
187  uint16_t num_arenas = logfs->partition_size / logfs->cfg->arena_size;
188 
189  for (uint16_t arena = 0; arena < num_arenas; arena++) {
190  if (logfs_erase_arena(logfs, arena) != 0)
191  return -1;
192  }
193 
194  return 0;
195 }
196 
203 static int32_t logfs_activate_arena(const struct logfs_state *logfs, uint8_t arena_id)
204 {
205  uintptr_t arena_addr = logfs_get_addr(logfs, arena_id, 0);
206 
207  /* Make sure this arena has been previously erased */
208  struct arena_header arena_hdr;
210  arena_addr,
211  (uint8_t *)&arena_hdr,
212  sizeof (arena_hdr)) != 0) {
213  /* Failed to read arena header */
214  return -1;
215  }
216  if ((arena_hdr.state != ARENA_STATE_RESERVED) &&
217  (arena_hdr.state != ARENA_STATE_ERASED)) {
218  /* Arena was not erased or reserved, can't activate it */
219  return -2;
220  }
221 
222  /* Mark this arena as active */
223  arena_hdr.state = ARENA_STATE_ACTIVE;
225  arena_addr,
226  (uint8_t *)&arena_hdr,
227  sizeof(arena_hdr)) != 0) {
228  return -3;
229  }
230 
231  /* The arena is now activated and the log may be mounted */
232  return 0;
233 }
234 
241 static int32_t logfs_obsolete_arena(const struct logfs_state *logfs, uint8_t arena_id)
242 {
243  uintptr_t arena_addr = logfs_get_addr (logfs, arena_id, 0);
244 
245  /* We shouldn't be retiring the currently active arena */
246  PIOS_Assert(!logfs->mounted);
247 
248  /* Make sure this arena was previously active */
249  struct arena_header arena_hdr;
251  arena_addr,
252  (uint8_t *)&arena_hdr,
253  sizeof (arena_hdr)) != 0) {
254  /* Failed to read arena header */
255  return -1;
256  }
257 
258  if (arena_hdr.state != ARENA_STATE_ACTIVE) {
259  /* Arena was not previously active, can't obsolete it */
260  return -2;
261  }
262 
263  /* Mark this arena as obsolete */
264  arena_hdr.state = ARENA_STATE_OBSOLETE;
266  arena_addr,
267  (uint8_t *)&arena_hdr,
268  sizeof(arena_hdr)) != 0) {
269  return -3;
270  }
271 
272  /* Arena is now obsoleted */
273  return 0;
274 }
275 
283 static int32_t logfs_find_active_arena(const struct logfs_state *logfs)
284 {
285  /* Search for the lowest numbered active arena */
286  for (uint8_t arena_id = 0;
287  arena_id < logfs->partition_size / logfs->cfg->arena_size;
288  arena_id++) {
289  uintptr_t arena_addr = logfs_get_addr (logfs, arena_id, 0);
290  /* Load the arena header */
291  struct arena_header arena_hdr;
293  arena_addr,
294  (uint8_t *)&arena_hdr,
295  sizeof (arena_hdr)) != 0) {
296  return -2;
297  }
298  if ((arena_hdr.state == ARENA_STATE_ACTIVE) &&
299  (arena_hdr.magic == logfs->cfg->fs_magic)) {
300  /* This is the first active arena */
301  return arena_id;
302  }
303  }
304 
305  /* Didn't find an active arena */
306  return -1;
307 }
308 
309 /*
310  * The bits within these enum values must progress ONLY
311  * from 1 -> 0 so that we can write later ones on top
312  * of earlier ones in NOR flash without an erase cycle.
313  */
315  /*
316  * The STM32F30X flash subsystem is only capable of
317  * writing words or halfwords. In this case we use halfwords.
318  * In addition to that it is only capable to write to erased
319  * cells (0xffff) or write a cell from anything to (0x0000).
320  * To cope with this, the F3 needs carfully crafted enum values.
321  * For this to work the underlying flash driver has to
322  * check each halfword if it has changed before writing.
323  */
324  SLOT_STATE_EMPTY = 0xFFFFFFFF,
325  SLOT_STATE_RESERVED = 0xFAFAFFFF,
326  SLOT_STATE_ACTIVE = 0xFAFAAAAA,
327  SLOT_STATE_OBSOLETE = 0x00000000,
328 } __attribute__((packed));
329 
330 struct slot_header {
332  uint32_t obj_id;
333  uint16_t obj_inst_id;
334  uint16_t obj_size;
335 } __attribute__((packed));
336 
337 /* NOTE: Must be called while holding the flash transaction lock */
338 static int32_t logfs_raw_copy_bytes (const struct logfs_state *logfs, uintptr_t src_addr, uint16_t src_size, uintptr_t dst_addr)
339 {
340 #define RAW_COPY_BLOCK_SIZE 16
341  uint8_t data_block[RAW_COPY_BLOCK_SIZE];
342 
343  while (src_size) {
344  uint16_t blk_size;
345  if (src_size >= RAW_COPY_BLOCK_SIZE) {
346  /* Copy a full block */
347  blk_size = RAW_COPY_BLOCK_SIZE;
348  } else {
349  /* Copy the remainder */
350  blk_size = src_size;
351  }
352 
353  /* Read a block of data from source */
355  src_addr,
356  data_block,
357  blk_size) != 0) {
358  /* Failed to read next chunk from source */
359  return -1;
360  }
361 
362  /* Write a block of data to destination */
364  dst_addr,
365  data_block,
366  blk_size) != 0) {
367  /* Failed to write chunk to destination */
368  return -2;
369  }
370 
371  /* Update the src/dst pointers */
372  src_size -= blk_size;
373  src_addr += blk_size;
374  dst_addr += blk_size;
375  }
376 
377  return 0;
378 }
379 
380 /*
381  * Is the entire filesystem full?
382  * true = all slots in the arena are in the ACTIVE state (ie. garbage collection won't free anything)
383  * false = some slots in the arena are either currently free or could be free'd by garbage collection
384  */
385 static bool logfs_fs_is_full(const struct logfs_state *logfs)
386 {
387  return (logfs->num_active_slots == (logfs->cfg->arena_size / logfs->cfg->slot_size) - 1);
388 }
389 
390 /*
391  * Is the log full?
392  * true = there are no unwritten slots left in the log (garbage collection may or may not help)
393  * false = there are still some entirely unused slots left in the log
394  */
395 static bool logfs_log_is_full(const struct logfs_state *logfs)
396 {
397  return (logfs->num_free_slots == 0);
398 }
399 
400 static int32_t logfs_unmount_log(struct logfs_state *logfs)
401 {
402  PIOS_Assert (logfs->mounted);
403 
404  logfs->num_active_slots = 0;
405  logfs->num_free_slots = 0;
406  logfs->mounted = false;
407 
408  return 0;
409 }
410 
411 static int32_t logfs_mount_log(struct logfs_state *logfs, uint8_t arena_id)
412 {
413  PIOS_Assert (!logfs->mounted);
414 
415  logfs->num_active_slots = 0;
416  logfs->num_free_slots = 0;
417  logfs->active_arena_id = arena_id;
418 
419  /* Scan the log to find out how full it is */
420  for (uint16_t slot_id = 1;
421  slot_id < (logfs->cfg->arena_size / logfs->cfg->slot_size);
422  slot_id++) {
423  struct slot_header slot_hdr;
424  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, slot_id);
426  slot_addr,
427  (uint8_t *)&slot_hdr,
428  sizeof (slot_hdr)) != 0) {
429  return -1;
430  }
431 
432  /*
433  * Empty slots must be in a continguous block at the
434  * end of the arena.
435  */
436  PIOS_Assert (slot_hdr.state == SLOT_STATE_EMPTY ||
437  logfs->num_free_slots == 0);
438 
439  switch (slot_hdr.state) {
440  case SLOT_STATE_EMPTY:
441  logfs->num_free_slots++;
442  break;
443  case SLOT_STATE_ACTIVE:
444  logfs->num_active_slots++;
445  break;
446  case SLOT_STATE_RESERVED:
447  case SLOT_STATE_OBSOLETE:
448  break;
449  }
450  }
451 
452  /* Scan is complete, mark the arena mounted */
453  logfs->active_arena_id = arena_id;
454  logfs->mounted = true;
455 
456  return 0;
457 }
458 
459 static bool PIOS_FLASHFS_Logfs_validate(const struct logfs_state *logfs)
460 {
461  return (logfs && (logfs->magic == PIOS_FLASHFS_LOGFS_DEV_MAGIC));
462 }
463 
465 {
466  struct logfs_state *logfs;
467 
468  logfs = (struct logfs_state *)PIOS_malloc_no_dma(sizeof(*logfs));
469  if (!logfs) return (NULL);
470 
472  return(logfs);
473 }
474 static void PIOS_FLASHFS_Logfs_free(struct logfs_state *logfs)
475 {
476  /* Invalidate the magic */
478  PIOS_free(logfs);
479 }
480 
485 int32_t PIOS_FLASHFS_Logfs_Init(uintptr_t *fs_id, const struct flashfs_logfs_cfg *cfg, enum pios_flash_partition_labels partition_label)
486 {
487  PIOS_Assert(cfg);
488 
489  /* Find the partition id for the requested partition label */
490  uintptr_t partition_id;
491  if (PIOS_FLASH_find_partition_id(partition_label, &partition_id) != 0) {
492  return -1;
493  }
494 
495  /* Query the total partition size */
496  uint32_t partition_size;
497  if (PIOS_FLASH_get_partition_size(partition_id, &partition_size) != 0) {
498  return -1;
499  }
500 
501  /* We must have at least 2 arenas for garbage collection to work */
502  PIOS_Assert((partition_size / cfg->arena_size > 1));
503 
504  /* arena_size must exactly divide the partition size */
505  PIOS_Assert((partition_size % cfg->arena_size) == 0);
506 
507  int8_t rc;
508 
509  struct logfs_state *logfs;
510 
511  logfs = (struct logfs_state *) PIOS_FLASHFS_Logfs_alloc();
512  if (!logfs) {
513  rc = -1;
514  goto out_exit;
515  }
516 
517  /* Bind configuration parameters to this filesystem instance */
518  logfs->cfg = cfg; /* filesystem configuration */
519  logfs->partition_id = partition_id; /* underlying partition */
520  logfs->partition_size = partition_size; /* size of underlying partition */
521  logfs->mounted = false;
522 
523  if (PIOS_FLASH_start_transaction(logfs->partition_id) != 0) {
524  rc = -1;
525  goto out_exit;
526  }
527 
528  bool found = false;
529  int32_t arena_id;
530  for (uint8_t try = 0; !found && try < 2; try++) {
531  /* Find the active arena */
532  arena_id = logfs_find_active_arena(logfs);
533  if (arena_id >= 0) {
534  /* Found the active arena */
535  found = true;
536  break;
537  } else {
538  /* No active arena found, erase and activate arena 0 */
539  if (logfs_erase_arena(logfs, 0) != 0)
540  break;
541  if (logfs_activate_arena(logfs, 0) != 0)
542  break;
543  }
544  }
545 
546  if (!found) {
547  /* Still no active arena, something is broken */
548  rc = -2;
549  goto out_end_trans;
550  }
551 
552  /* We've found an active arena, mount it */
553  if (logfs_mount_log(logfs, arena_id) != 0) {
554  /* Failed to mount the log, something is broken */
555  rc = -3;
556  goto out_end_trans;
557  }
558 
559  /* Log has been mounted */
560  rc = 0;
561 
562  *fs_id = (uintptr_t) logfs;
563 
564 out_end_trans:
566 
567 out_exit:
568  return rc;
569 }
570 
571 int32_t PIOS_FLASHFS_Logfs_Destroy(uintptr_t fs_id)
572 {
573  int32_t rc;
574 
575  struct logfs_state *logfs = (struct logfs_state *)fs_id;
576 
577  if (!PIOS_FLASHFS_Logfs_validate(logfs)) {
578  rc = -1;
579  goto out_exit;
580  }
581 
583  rc = 0;
584 
585 out_exit:
586  return rc;
587 }
588 
589 /* NOTE: Must be called while holding the flash transaction lock */
590 static int32_t logfs_garbage_collect (struct logfs_state *logfs) {
591  PIOS_Assert (logfs->mounted);
592 
593  /* Source arena is the active arena */
594  uint8_t src_arena_id = logfs->active_arena_id;
595 
596  /* Compute destination arena */
597  uint8_t dst_arena_id = (logfs->active_arena_id + 1) % (logfs->partition_size / logfs->cfg->arena_size);
598 
599  /* Erase destination arena */
600  if (logfs_erase_arena (logfs, dst_arena_id) != 0) {
601  return -1;
602  }
603 
604  /* Reserve the destination arena so we can start filling it */
605  if (logfs_reserve_arena (logfs, dst_arena_id) != 0) {
606  /* Unable to reserve the arena */
607  return -2;
608  }
609 
610  /* Copy active slots from active arena to destination arena */
611  uint16_t dst_slot_id = 1;
612  for (uint16_t src_slot_id = 1;
613  src_slot_id < (logfs->cfg->arena_size / logfs->cfg->slot_size);
614  src_slot_id++) {
615  struct slot_header slot_hdr;
616  uintptr_t src_addr = logfs_get_addr (logfs, src_arena_id, src_slot_id);
618  src_addr,
619  (uint8_t *)&slot_hdr,
620  sizeof (slot_hdr)) != 0) {
621  return -3;
622  }
623 
624  if (slot_hdr.state == SLOT_STATE_ACTIVE) {
625  uintptr_t dst_addr = logfs_get_addr (logfs, dst_arena_id, dst_slot_id);
626  if (logfs_raw_copy_bytes(logfs,
627  src_addr,
628  sizeof(slot_hdr) + slot_hdr.obj_size,
629  dst_addr) != 0) {
630  /* Failed to copy all bytes */
631  return -4;
632  }
633  dst_slot_id++;
634  }
635  }
636 
637  /* Activate the destination arena */
638  if (logfs_activate_arena (logfs, dst_arena_id) != 0) {
639  return -5;
640  }
641 
642  /* Unmount the source arena */
643  if (logfs_unmount_log (logfs) != 0) {
644  return -6;
645  }
646 
647  /* Obsolete the source arena */
648  if (logfs_obsolete_arena (logfs, src_arena_id) != 0) {
649  return -7;
650  }
651 
652  /* Mount the new arena */
653  if (logfs_mount_log (logfs, dst_arena_id) != 0) {
654  return -8;
655  }
656 
657  return 0;
658 }
659 
660 /* NOTE: Must be called while holding the flash transaction lock */
661 static int16_t logfs_object_find_next (const struct logfs_state *logfs, struct slot_header *slot_hdr, uint16_t *curr_slot, uint32_t obj_id, uint16_t obj_inst_id)
662 {
663  PIOS_Assert(slot_hdr);
664  PIOS_Assert(curr_slot);
665 
666  /* First slot in the arena is reserved for arena header, skip it. */
667  if (*curr_slot == 0) *curr_slot = 1;
668 
669  for (uint16_t slot_id = *curr_slot;
670  slot_id < (logfs->cfg->arena_size / logfs->cfg->slot_size);
671  slot_id++) {
672  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, slot_id);
673 
675  slot_addr,
676  (uint8_t *)slot_hdr,
677  sizeof (*slot_hdr)) != 0) {
678  return -2;
679  }
680  if (slot_hdr->state == SLOT_STATE_EMPTY) {
681  /* We hit the end of the log */
682  break;
683  }
684  if (slot_hdr->state == SLOT_STATE_ACTIVE &&
685  slot_hdr->obj_id == obj_id &&
686  slot_hdr->obj_inst_id == obj_inst_id) {
687  /* Found what we were looking for */
688  *curr_slot = slot_id;
689  return 0;
690  }
691  }
692 
693  /* No matching entry was found */
694  return -1;
695 }
696 
697 /* NOTE: Must be called while holding the flash transaction lock */
698 /* OPTIMIZE: could trust that there is at most one active version of every object and terminate the search when we find one */
699 static int8_t logfs_delete_object (struct logfs_state *logfs, uint32_t obj_id, uint16_t obj_inst_id)
700 {
701  int8_t rc;
702 
703  bool more = true;
704  uint16_t curr_slot_id = 0;
705  do {
706  struct slot_header slot_hdr;
707  switch (logfs_object_find_next (logfs, &slot_hdr, &curr_slot_id, obj_id, obj_inst_id)) {
708  case 0:
709  /* Found a matching slot. Obsolete it. */
710  slot_hdr.state = SLOT_STATE_OBSOLETE;
711  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, curr_slot_id);
712 
714  slot_addr,
715  (uint8_t *)&slot_hdr,
716  sizeof(slot_hdr)) != 0) {
717  rc = -2;
718  goto out_exit;
719  }
720  /* Object has been successfully obsoleted and is no longer active */
721  logfs->num_active_slots--;
722  break;
723  case -1:
724  /* Search completed, object not found */
725  more = false;
726  rc = 0;
727  break;
728  default:
729  /* Error occurred during search */
730  rc = -1;
731  goto out_exit;
732  }
733  } while (more);
734 
735 out_exit:
736  return rc;
737 }
738 
739 /* NOTE: Must be called while holding the flash transaction lock */
740 static int8_t logfs_reserve_free_slot (struct logfs_state *logfs, uint16_t *slot_id, struct slot_header *slot_hdr, uint32_t obj_id, uint16_t obj_inst_id, uint16_t obj_size)
741 {
742  PIOS_Assert(slot_id);
743  PIOS_Assert(slot_hdr);
744 
745  if (logfs->num_free_slots < 1) {
746  /* No free slots to allocate */
747  return -1;
748  }
749 
750  if (obj_size > (logfs->cfg->slot_size - sizeof (slot_hdr))) {
751  /* This object is too big for the slot */
752  return -2;
753  }
754 
755  uint16_t candidate_slot_id = (logfs->cfg->arena_size / logfs->cfg->slot_size) - logfs->num_free_slots;
756  PIOS_Assert(candidate_slot_id > 0);
757 
758  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, candidate_slot_id);
759 
761  slot_addr,
762  (uint8_t *)slot_hdr,
763  sizeof (*slot_hdr)) != 0) {
764  /* Failed to read slot header for candidate slot */
765  return -3;
766  }
767 
768  if (slot_hdr->state != SLOT_STATE_EMPTY) {
769  /* Candidate slot isn't empty! Something is broken. */
771  return -4;
772  }
773 
774  /* Mark this slot as RESERVED */
775  slot_hdr->state = SLOT_STATE_RESERVED;
776  slot_hdr->obj_id = obj_id;
777  slot_hdr->obj_inst_id = obj_inst_id;
778  slot_hdr->obj_size = obj_size;
779 
781  slot_addr,
782  (uint8_t *)slot_hdr,
783  sizeof(*slot_hdr)) != 0) {
784  /* Failed to write the slot header */
785  return -5;
786  }
787 
788  /* FIXME: If the header write (above) failed, may have partially written data, thus corrupting that slot but we would have missed decrementing this counter */
789  logfs->num_free_slots--;
790 
791  *slot_id = candidate_slot_id;
792  return 0;
793 }
794 
795 /* NOTE: Must be called while holding the flash transaction lock */
796 static int8_t logfs_append_to_log (struct logfs_state *logfs, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
797 {
798  /* Reserve a free slot for our new object */
799  uint16_t free_slot_id;
800  struct slot_header slot_hdr;
801  if (logfs_reserve_free_slot (logfs, &free_slot_id, &slot_hdr, obj_id, obj_inst_id, obj_size) != 0) {
802  /* Failed to reserve a free slot */
803  return -1;
804  }
805 
806  /* Compute slot address */
807  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, free_slot_id);
808 
809  /* Write the data into the reserved slot, starting after the slot header */
810  if (obj_size > 0) {
811  uintptr_t slot_offset = sizeof(slot_hdr);
812 
814  slot_addr + slot_offset,
815  obj_data,
816  obj_size) != 0) {
817  /* Failed to write the object data to the slot */
818  return -2;
819  }
820  }
821 
822  /* Mark this slot active in one atomic step */
823  slot_hdr.state = SLOT_STATE_ACTIVE;
825  slot_addr,
826  (uint8_t *)&slot_hdr,
827  sizeof(slot_hdr)) != 0) {
828  /* Failed to mark the slot active */
829  return -4;
830  }
831 
832  /* Object has been successfully written to the slot */
833  logfs->num_active_slots++;
834  return 0;
835 }
836 
837 
838 /**********************************
839  *
840  * Provide a PIOS_FLASHFS_* driver
841  *
842  *********************************/
843 #include "pios_flashfs.h" /* API for flash filesystem */
844 
861 int32_t PIOS_FLASHFS_ObjSave(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
862 {
863  int8_t rc;
864 
865  struct logfs_state *logfs = (struct logfs_state *)fs_id;
866 
867  if (!PIOS_FLASHFS_Logfs_validate(logfs)) {
868  rc = -1;
869  goto out_exit;
870  }
871 
872  PIOS_Assert(obj_size <= (logfs->cfg->slot_size - sizeof(struct slot_header)));
873 
874  if (PIOS_FLASH_start_transaction(logfs->partition_id) != 0) {
875  rc = -2;
876  goto out_exit;
877  }
878 
879  if (logfs_delete_object (logfs, obj_id, obj_inst_id) != 0) {
880  rc = -3;
881  goto out_end_trans;
882  }
883 
884  /*
885  * All old versions of this object + instance have been invalidated.
886  * Write the new object.
887  */
888 
889  /* Check if the arena is entirely full. */
890  if (logfs_fs_is_full(logfs)) {
891  /* Note: Filesystem Full means we're full of *active* records so gc won't help at all. */
892  rc = -4;
893  goto out_end_trans;
894  }
895 
896  /* Is garbage collection required? */
897  if (logfs_log_is_full(logfs)) {
898  /* Note: Log Full means the log is full but may contain obsolete slots so gc may free some space */
899  if (logfs_garbage_collect(logfs) != 0) {
900  rc = -5;
901  goto out_end_trans;
902  }
903  /* Check one more time just to be sure we actually free'd some space */
904  if (logfs_log_is_full(logfs)) {
905  /*
906  * Log is still full even after gc!
907  * NOTE: This should not happen since the filesystem wasn't full
908  * when we checked above so gc should have helped.
909  */
911  rc = -6;
912  goto out_end_trans;
913  }
914  }
915 
916  /* We have room for our new object. Append it to the log. */
917  if (logfs_append_to_log(logfs, obj_id, obj_inst_id, obj_data, obj_size) != 0) {
918  /* Error during append */
919  rc = -7;
920  goto out_end_trans;
921  }
922 
923  /* Object successfully written to the log */
924  rc = 0;
925 
926 out_end_trans:
928 
929 out_exit:
930  return rc;
931 }
932 
947 int32_t PIOS_FLASHFS_ObjLoad(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
948 {
949  int8_t rc;
950 
951  struct logfs_state *logfs = (struct logfs_state *)fs_id;
952 
953  if (!PIOS_FLASHFS_Logfs_validate(logfs)) {
954  rc = -1;
955  goto out_exit;
956  }
957 
958  PIOS_Assert(obj_size <= (logfs->cfg->slot_size - sizeof(struct slot_header)));
959 
960  if (PIOS_FLASH_start_transaction(logfs->partition_id) != 0) {
961  rc = -2;
962  goto out_exit;
963  }
964 
965  /* Find the object in the log */
966  uint16_t slot_id = 0;
967  struct slot_header slot_hdr;
968  if (logfs_object_find_next (logfs, &slot_hdr, &slot_id, obj_id, obj_inst_id) != 0) {
969  /* Object does not exist in fs */
970  rc = -3;
971  goto out_end_trans;
972  }
973 
974  /* Sanity check what we've found */
975  if (slot_hdr.obj_size != obj_size) {
976  /* Object sizes don't match. Not safe to copy contents. */
977  rc = -4;
978  goto out_end_trans;
979  }
980 
981  /* Read the contents of the object from the log */
982  if (obj_size > 0) {
983  uintptr_t slot_addr = logfs_get_addr (logfs, logfs->active_arena_id, slot_id);
985  slot_addr + sizeof(slot_hdr),
986  (uint8_t *)obj_data,
987  obj_size) != 0) {
988  /* Failed to read object data from the log */
989  rc = -5;
990  goto out_end_trans;
991  }
992  }
993 
994  /* Object successfully loaded */
995  rc = 0;
996 
997 out_end_trans:
999 
1000 out_exit:
1001  return rc;
1002 }
1003 
1014 int32_t PIOS_FLASHFS_ObjDelete(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id)
1015 {
1016  int8_t rc;
1017 
1018  struct logfs_state *logfs = (struct logfs_state *)fs_id;
1019 
1020  if (!PIOS_FLASHFS_Logfs_validate(logfs)) {
1021  rc = -1;
1022  goto out_exit;
1023  }
1024 
1025  if (PIOS_FLASH_start_transaction(logfs->partition_id) != 0) {
1026  rc = -2;
1027  goto out_exit;
1028  }
1029 
1030  if (logfs_delete_object (logfs, obj_id, obj_inst_id) != 0) {
1031  rc = -3;
1032  goto out_end_trans;
1033  }
1034 
1035  /* Object successfully deleted from the log */
1036  rc = 0;
1037 
1038 out_end_trans:
1040 
1041 out_exit:
1042  return rc;
1043 }
1044 
1055 int32_t PIOS_FLASHFS_Format(uintptr_t fs_id)
1056 {
1057  int32_t rc;
1058 
1059  struct logfs_state *logfs = (struct logfs_state *)fs_id;
1060 
1061  if (!PIOS_FLASHFS_Logfs_validate(logfs)) {
1062  rc = -1;
1063  goto out_exit;
1064  }
1065 
1066  if (logfs->mounted) {
1067  logfs_unmount_log(logfs);
1068  }
1069 
1070  if (PIOS_FLASH_start_transaction(logfs->partition_id) != 0) {
1071  rc = -2;
1072  goto out_exit;
1073  }
1074 
1075  if (logfs_erase_all_arenas(logfs) != 0) {
1076  rc = -3;
1077  goto out_end_trans;
1078  }
1079 
1080  /* Reinitialize arena 0 */
1081  if (logfs_activate_arena(logfs, 0) != 0) {
1082  rc = -4;
1083  goto out_end_trans;
1084  }
1085 
1086  /* Mount arena 0 */
1087  if (logfs_mount_log(logfs, 0) != 0) {
1088  rc = -5;
1089  goto out_end_trans;
1090  }
1091 
1092  /* Chip erased and log remounted successfully */
1093  rc = 0;
1094 
1095 out_end_trans:
1097 
1098 out_exit:
1099  return rc;
1100 }
1101 
SLOT_STATE_ACTIVE
int32_t PIOS_FLASH_find_partition_id(enum pios_flash_partition_labels label, uintptr_t *partition_id)
uint8_t active_arena_id
Main PiOS header to include all the compiled in PiOS options.
static int32_t logfs_raw_copy_bytes(const struct logfs_state *logfs, uintptr_t src_addr, uint16_t src_size, uintptr_t dst_addr)
enum slot_state state
static int32_t logfs_erase_all_arenas(const struct logfs_state *logfs)
Erases all arenas available to this filesystem instance.
SLOT_STATE_EMPTY
SLOT_STATE_OBSOLETE
uint16_t obj_inst_id
int32_t PIOS_FLASH_get_partition_size(uintptr_t partition_id, uint32_t *partition_size)
static int32_t logfs_erase_arena(const struct logfs_state *logfs, uint8_t arena_id)
Erases all sectors within the given arena and sets arena to erased state.
#define RAW_COPY_BLOCK_SIZE
#define PIOS_DEBUG_Assert(test)
Definition: pios_debug.h:51
static void PIOS_FLASHFS_Logfs_free(struct logfs_state *logfs)
ARENA_STATE_OBSOLETE
static int8_t logfs_delete_object(struct logfs_state *logfs, uint32_t obj_id, uint16_t obj_inst_id)
uintptr_t partition_id
uint16_t obj_size
int32_t PIOS_FLASHFS_Logfs_Init(uintptr_t *fs_id, const struct flashfs_logfs_cfg *cfg, enum pios_flash_partition_labels partition_label)
Initialize the flash object setting FS.
void * PIOS_malloc_no_dma(size_t size)
Definition: pios_heap.c:166
int32_t PIOS_FLASHFS_Logfs_Destroy(uintptr_t fs_id)
ARENA_STATE_ERASED
uint32_t partition_size
int32_t PIOS_FLASH_write_data(uintptr_t partition_id, uint32_t offset, const uint8_t *data, uint16_t len)
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
int32_t PIOS_FLASH_end_transaction(uintptr_t partition_id)
int32_t PIOS_FLASHFS_ObjDelete(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id)
Delete one instance of an object from the filesystem.
static int32_t logfs_reserve_arena(const struct logfs_state *logfs, uint8_t arena_id)
Marks the given arena as reserved so it can be filled.
static int16_t logfs_object_find_next(const struct logfs_state *logfs, struct slot_header *slot_hdr, uint16_t *curr_slot, uint32_t obj_id, uint16_t obj_inst_id)
static bool PIOS_FLASHFS_Logfs_validate(const struct logfs_state *logfs)
static struct logfs_state * PIOS_FLASHFS_Logfs_alloc(void)
static int8_t logfs_reserve_free_slot(struct logfs_state *logfs, uint16_t *slot_id, struct slot_header *slot_hdr, uint32_t obj_id, uint16_t obj_inst_id, uint16_t obj_size)
SLOT_STATE_RESERVED
static bool logfs_log_is_full(const struct logfs_state *logfs)
static int32_t logfs_find_active_arena(const struct logfs_state *logfs)
Find the first active arena in flash.
static int8_t logfs_append_to_log(struct logfs_state *logfs, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
pios_flashfs_logfs_dev_magic
uint16_t obj_inst_id
static uintptr_t logfs_get_addr(const struct logfs_state *logfs, uint8_t arena_id, uint16_t slot_id)
Return the offset in flash of a particular slot within an arena.
uint32_t obj_id
int32_t PIOS_FLASH_erase_range(uintptr_t partition_id, uint32_t start_offset, uint32_t size)
void PIOS_free(void *buf)
Definition: pios_heap.c:174
enum arena_state state
static int32_t logfs_garbage_collect(struct logfs_state *logfs)
int32_t PIOS_FLASHFS_ObjSave(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
Saves one object instance to the filesystem.
int32_t PIOS_FLASHFS_ObjLoad(uintptr_t fs_id, uint32_t obj_id, uint16_t obj_inst_id, uint8_t *obj_data, uint16_t obj_size)
Load one object instance from the filesystem.
static int32_t logfs_activate_arena(const struct logfs_state *logfs, uint8_t arena_id)
Marks the given arena as active so it can be mounted.
static bool logfs_fs_is_full(const struct logfs_state *logfs)
uint16_t num_free_slots
enum arena_state __attribute__((packed))
Definition: serial_4way.h:38
pios_flash_partition_labels
Definition: pios_flash.h:31
int32_t PIOS_FLASH_read_data(uintptr_t partition_id, uint32_t offset, uint8_t *data, uint16_t len)
enum pios_flashfs_logfs_dev_magic magic
int32_t PIOS_FLASH_start_transaction(uintptr_t partition_id)
static int32_t logfs_obsolete_arena(const struct logfs_state *logfs, uint8_t arena_id)
Marks the given arena as obsolete.
static int32_t logfs_mount_log(struct logfs_state *logfs, uint8_t arena_id)
const struct flashfs_logfs_cfg * cfg
int32_t PIOS_FLASHFS_Format(uintptr_t fs_id)
Erases all filesystem arenas and activate the first arena.
#define PIOS_Assert(test)
Definition: pios_debug.h:52
arena_state
ARENA_STATE_RESERVED
static int32_t logfs_unmount_log(struct logfs_state *logfs)
ARENA_STATE_ACTIVE
uint16_t num_active_slots