| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 | 
							- /*
 
-  * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
 
-  * This program is free software. It comes without any warranty, to the extent
 
-  * permitted by applicable law. You can redistribute it and/or modify it under
 
-  * the terms of the Do What The Fuck You Want To Public License, Version 2, as
 
-  * published by Sam Hocevar. See the COPYING file for more details.
 
-  */
 
- /**
 
-  * @defgroup ringfs_impl RingFS implementation
 
-  * @details
 
-  *
 
-  * @{
 
-  */
 
- #include <ringfs.h>
 
- #include <inttypes.h>
 
- #include <stdbool.h>
 
- #include <stddef.h>
 
- #include "main.h"
 
- /**
 
-  * @defgroup sector
 
-  * @{
 
-  */
 
- enum sector_status {
 
-     SECTOR_ERASED     = 0xFFFFFFFF, /**< Default state after NOR flash erase. */
 
-     SECTOR_FREE       = 0xFFFFFF00, /**< Sector erased. */
 
-     SECTOR_IN_USE     = 0xFFFF0000, /**< Sector contains valid data. */
 
-     SECTOR_ERASING    = 0xFF000000, /**< Sector should be erased. */
 
-     SECTOR_FORMATTING = 0x00000000, /**< The entire partition is being formatted. */
 
- };
 
- struct sector_header {
 
-     uint32_t status;
 
-     uint32_t version;
 
- };
 
- static int _sector_address(struct ringfs *fs, int sector_offset)
 
- {
 
-     return (fs->flash->sector_offset + sector_offset) * fs->flash->sector_size;
 
- }
 
- static int _sector_get_status(struct ringfs *fs, int sector, uint32_t *status)
 
- {
 
-     return fs->flash->read(fs->flash,
 
-             _sector_address(fs, sector) + offsetof(struct sector_header, status),
 
-             status, sizeof(*status));
 
- }
 
- static int _sector_set_status(struct ringfs *fs, int sector, uint32_t status)
 
- {
 
-     return fs->flash->program(fs->flash,
 
-             _sector_address(fs, sector) + offsetof(struct sector_header, status),
 
-             &status, sizeof(status));
 
- }
 
- static int _sector_free(struct ringfs *fs, int sector)
 
- {
 
-     int sector_addr = _sector_address(fs, sector);
 
-     _sector_set_status(fs, sector, SECTOR_ERASING);
 
-     fs->flash->sector_erase(fs->flash, sector_addr);
 
-     fs->flash->program(fs->flash,
 
-             sector_addr + offsetof(struct sector_header, version),
 
-             &fs->version, sizeof(fs->version));
 
-     _sector_set_status(fs, sector, SECTOR_FREE);
 
-     return 0;
 
- }
 
- /**
 
-  * @}
 
-  * @defgroup slot
 
-  * @{
 
-  */
 
- enum slot_status {
 
-     SLOT_ERASED   = 0xFFFFFFFF, /**< Default state after NOR flash erase. */
 
-     SLOT_RESERVED = 0xFFFFFF00, /**< Write started but not yet committed. */
 
-     SLOT_VALID    = 0xFFFF0000, /**< Write committed, slot contains valid data. */
 
-     SLOT_GARBAGE  = 0xFF000000, /**< Slot contents discarded and no longer valid. */
 
- };
 
- struct slot_header {
 
-     uint32_t status;
 
- };
 
- static int _slot_address(struct ringfs *fs, struct ringfs_loc *loc)
 
- {
 
-     return _sector_address(fs, loc->sector) +
 
-            sizeof(struct sector_header) +
 
-            (sizeof(struct slot_header) + fs->object_size) * loc->slot;
 
- }
 
- static int _slot_get_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t *status)
 
- {
 
-     return fs->flash->read(fs->flash,
 
-             _slot_address(fs, loc) + offsetof(struct slot_header, status),
 
-             status, sizeof(*status));
 
- }
 
- static int _slot_set_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t status)
 
- {
 
-     return fs->flash->program(fs->flash, 
 
-             _slot_address(fs, loc) + offsetof(struct slot_header, status),
 
-             &status, sizeof(status));
 
- }
 
- /**
 
-  * @}
 
-  * @defgroup loc
 
-  * @{
 
-  */
 
- static bool _loc_equal(struct ringfs_loc *a, struct ringfs_loc *b)
 
- {
 
-     return (a->sector == b->sector) && (a->slot == b->slot);
 
- }
 
- /** Advance a location to the beginning of the next sector. */
 
- static void _loc_advance_sector(struct ringfs *fs, struct ringfs_loc *loc)
 
- {
 
-     loc->slot = 0;
 
-     loc->sector++;
 
-     if (loc->sector >= fs->flash->sector_count)
 
-         loc->sector = 0;
 
- }
 
- /** Advance a location to the next slot, advancing the sector too if needed. */
 
- static void _loc_advance_slot(struct ringfs *fs, struct ringfs_loc *loc)
 
- {
 
-     loc->slot++;
 
-     if (loc->slot >= fs->slots_per_sector)
 
-         _loc_advance_sector(fs, loc);
 
- }
 
- /**
 
-  * @}
 
-  */
 
- /* And here we go. */
 
- int ringfs_init(struct ringfs *fs, struct ringfs_flash_partition *flash, uint32_t version, int object_size)
 
- {
 
-     /* Copy arguments to instance. */
 
-     fs->flash = flash;
 
-     fs->version = version;
 
-     fs->object_size = object_size;
 
-     /* Precalculate commonly used values. */
 
-     fs->slots_per_sector = (fs->flash->sector_size - sizeof(struct sector_header)) /
 
-                            (sizeof(struct slot_header) + fs->object_size);
 
-     return 0;
 
- }
 
- int ringfs_format(struct ringfs *fs)
 
- {
 
-     /* Mark all sectors to prevent half-erased filesystems. */
 
-     for (int sector=0; sector<fs->flash->sector_count; sector++)
 
-         _sector_set_status(fs, sector, SECTOR_FORMATTING);
 
-     /* Erase, update version, mark as free. */
 
-     for (int sector=0; sector<fs->flash->sector_count; sector++)
 
-         _sector_free(fs, sector);
 
-     /* Start reading & writing at the first sector. */
 
-     fs->read.sector = 0;
 
-     fs->read.slot = 0;
 
-     fs->write.sector = 0;
 
-     fs->write.slot = 0;
 
-     fs->cursor.sector = 0;
 
-     fs->cursor.slot = 0;
 
-     return 0;
 
- }
 
- int ringfs_scan(struct ringfs *fs)
 
- {
 
-     uint32_t previous_sector_status = SECTOR_FREE;
 
-     /* The read sector is the first IN_USE sector *after* a FREE sector
 
-      * (or the first one). */
 
-     int read_sector = 0;
 
-     /* The write sector is the last IN_USE sector *before* a FREE sector
 
-      * (or the last one). */
 
-     int write_sector = fs->flash->sector_count - 1;
 
-     /* There must be at least one FREE sector available at all times. */
 
-     bool free_seen = false;
 
-     /* If there's no IN_USE sector, we start at the first one. */
 
-     bool used_seen = false;
 
-     bool err_sector = true;
 
-     /* Iterate over sectors. */
 
-     for (int sector=0; sector<fs->flash->sector_count; sector++) {
 
-         int addr = _sector_address(fs, sector);
 
-         /* Read sector header. */
 
-         struct sector_header header;
 
-         fs->flash->read(fs->flash, addr, &header, sizeof(header));
 
-         /* Detect partially-formatted partitions. */
 
-         if (header.status == SECTOR_FORMATTING) {
 
-         	DBG printf("ringfs_scan: partially formatted partition\r\n");
 
-         	if(err_sector){
 
- 				err_sector = false;
 
- 				_sector_free(fs, addr);
 
- 				//fs->flash->read(fs->flash, addr, &header, sizeof(header));
 
- 				header.status = SECTOR_FREE;
 
- 				header.version = fs->version;
 
- 			}else{
 
- 				return -1;
 
- 			}
 
-         }
 
-         /* Detect and fix partially erased sectors. */
 
-         if (header.status == SECTOR_ERASING || header.status == SECTOR_ERASED) {
 
-             _sector_free(fs, addr);
 
-             header.status = SECTOR_FREE;
 
-         }
 
-         /* Detect corrupted sectors. */
 
-         if (header.status != SECTOR_FREE && header.status != SECTOR_IN_USE) {
 
-         	DBG printf("ringfs_scan: corrupted sector %d\r\n", sector);
 
-         	if(err_sector){
 
- 				err_sector = false;
 
- 				_sector_free(fs, addr);
 
- 				//fs->flash->read(fs->flash, addr, &header, sizeof(header));
 
- 				header.status = SECTOR_FREE;
 
- 				header.version = fs->version;
 
- 			}else{
 
- 				return -1;
 
- 			}
 
-         }
 
-         /* Detect obsolete versions. We can't do this earlier because the version
 
-          * could have been invalid due to a partial erase. */
 
-         if (header.version != fs->version) {
 
-         	DBG printf("ringfs_scan: corrupted sector %d\r\n", sector);
 
-         	DBG printf("ringfs_scan: incompatible version 0x%08"PRIx32"\r\n", header.version);
 
-             if(err_sector){
 
-             	err_sector = false;
 
-             	_sector_free(fs, addr);
 
-             	//fs->flash->read(fs->flash, addr, &header, sizeof(header));
 
-             	DBG printf("ringfs_scan: incompatible version 0x%08"PRIx32"\r\n", header.version);
 
-             	header.status = SECTOR_FREE;
 
-             	header.version = fs->version;
 
-             }else{
 
-             	return -1;
 
-             }
 
-         }
 
-         /* Record the presence of a FREE sector. */
 
-         if (header.status == SECTOR_FREE)
 
-             free_seen = true;
 
-         /* Record the presence of a IN_USE sector. */
 
-         if (header.status == SECTOR_IN_USE)
 
-             used_seen = true;
 
-         /* Update read & write sectors according to the above rules. */
 
-         if (header.status == SECTOR_IN_USE && previous_sector_status == SECTOR_FREE)
 
-             read_sector = sector;
 
-         if (header.status == SECTOR_FREE && previous_sector_status == SECTOR_IN_USE)
 
-             write_sector = sector-1;
 
-         previous_sector_status = header.status;
 
-     }
 
-     /* Detect the lack of a FREE sector. */
 
-     if (!free_seen) {
 
-     	DBG printf("ringfs_scan: invariant violated: no FREE sector found\r\n");
 
-         return -1;
 
-     }
 
-     /* Start writing at the first sector if the filesystem is empty. */
 
-     if (!used_seen) {
 
-         write_sector = 0;
 
-     }
 
-     /* Scan the write sector and skip all occupied slots at the beginning. */
 
-     fs->write.sector = write_sector;
 
-     fs->write.slot = 0;
 
-     while (fs->write.sector == write_sector) {
 
-         uint32_t status;
 
-         _slot_get_status(fs, &fs->write, &status);
 
-         if (status == SLOT_ERASED)
 
-             break;
 
-         _loc_advance_slot(fs, &fs->write);
 
-     }
 
-     /* If the sector was full, we're at the beginning of a FREE sector now. */
 
-     /* Position the read head at the start of the first IN_USE sector, then skip
 
-      * over garbage/invalid slots until something of value is found or we reach
 
-      * the write head which means there's no data. */
 
-     fs->read.sector = read_sector;
 
-     fs->read.slot = 0;
 
-     while (!_loc_equal(&fs->read, &fs->write)) {
 
-         uint32_t status;
 
-         _slot_get_status(fs, &fs->read, &status);
 
-         if (status == SLOT_VALID)
 
-             break;
 
-         _loc_advance_slot(fs, &fs->read);
 
-     }
 
-     /* Move the read cursor to the read head position. */
 
-     ringfs_rewind(fs);
 
-     return 0;
 
- }
 
- int ringfs_capacity(struct ringfs *fs)
 
- {
 
-     return fs->slots_per_sector * (fs->flash->sector_count - 1);
 
- }
 
- int ringfs_count_estimate(struct ringfs *fs)
 
- {
 
-     int sector_diff = (fs->write.sector - fs->read.sector + fs->flash->sector_count) %
 
-         fs->flash->sector_count;
 
-     return sector_diff * fs->slots_per_sector + fs->write.slot - fs->read.slot;
 
- }
 
- int ringfs_count_exact(struct ringfs *fs)
 
- {
 
-     int count = 0;
 
-     /* Use a temporary loc for iteration. */
 
-     struct ringfs_loc loc = fs->read;
 
-     while (!_loc_equal(&loc, &fs->write)) {
 
-         uint32_t status;
 
-         _slot_get_status(fs, &loc, &status);
 
-         
 
-         if (status == SLOT_VALID)
 
-             count++;
 
-         _loc_advance_slot(fs, &loc);
 
-     }
 
-     return count;
 
- }
 
- int ringfs_cursor_position(struct ringfs *fs) {
 
-     return fs->cursor_position;
 
- }
 
- int ringfs_append(struct ringfs *fs, const void *object)
 
- {
 
-     uint32_t status;
 
-     /*
 
-      * There are three sectors involved in appending a value:
 
-      * - the sector where the append happens: it has to be writable
 
-      * - the next sector: it must be free (invariant)
 
-      * - the next-next sector: read & cursor heads are moved there if needed
 
-      */
 
-     /* Make sure the next sector is free. */
 
-     int next_sector = (fs->write.sector+1) % fs->flash->sector_count;
 
-     _sector_get_status(fs, next_sector, &status);
 
-     if (status != SECTOR_FREE) {
 
-         /* Next sector must be freed. But first... */
 
-         /* Move the read & cursor heads out of the way. */
 
-         if (fs->read.sector == next_sector)
 
-             _loc_advance_sector(fs, &fs->read);
 
-         if (fs->cursor.sector == next_sector)
 
-             _loc_advance_sector(fs, &fs->cursor);
 
-         /* Free the next sector. */
 
-         _sector_free(fs, next_sector);
 
-     }
 
-     /* Now we can make sure the current write sector is writable. */
 
-     _sector_get_status(fs, fs->write.sector, &status);
 
-     if (status == SECTOR_FREE) {
 
-         /* Free sector. Mark as used. */
 
-         _sector_set_status(fs, fs->write.sector, SECTOR_IN_USE);
 
-     } else if (status != SECTOR_IN_USE) {
 
-         printf("ringfs_append: corrupted filesystem\r\n");
 
-         return -1;
 
-     }
 
-     /* Preallocate slot. */
 
-     _slot_set_status(fs, &fs->write, SLOT_RESERVED);
 
-     /* Write object. */
 
-     fs->flash->program(fs->flash,
 
-             _slot_address(fs, &fs->write) + sizeof(struct slot_header),
 
-             object, fs->object_size);
 
-     /* Commit write. */
 
-     _slot_set_status(fs, &fs->write, SLOT_VALID);
 
-     /* Advance the write head. */
 
-     _loc_advance_slot(fs, &fs->write);
 
-     return 0;
 
- }
 
- int ringfs_fetch(struct ringfs *fs, void *object)
 
- {
 
-     /* Advance forward in search of a valid slot. */
 
-     while (!_loc_equal(&fs->cursor, &fs->write)) {
 
-         uint32_t status;
 
-         _slot_get_status(fs, &fs->cursor, &status);
 
-         if (status == SLOT_VALID) {
 
-             fs->flash->read(fs->flash,
 
-                     _slot_address(fs, &fs->cursor) + sizeof(struct slot_header),
 
-                     object, fs->object_size);
 
-             _loc_advance_slot(fs, &fs->cursor);
 
-             fs->cursor_position++;
 
-             return 0;
 
-         }
 
-         _loc_advance_slot(fs, &fs->cursor);
 
-     }
 
-     return -1;
 
- }
 
- int ringfs_discard(struct ringfs *fs)
 
- {
 
-     while (!_loc_equal(&fs->read, &fs->cursor)) {
 
-         _slot_set_status(fs, &fs->read, SLOT_GARBAGE);
 
-         _loc_advance_slot(fs, &fs->read);
 
-     }
 
-     fs->cursor_position = 0;
 
-     return 0;
 
- }
 
- int ringfs_rewind(struct ringfs *fs)
 
- {
 
-     fs->cursor = fs->read;
 
-     fs->cursor_position = 0;
 
-     return 0;
 
- }
 
- /**
 
-  * @}
 
-  */
 
- /* vim: set ts=4 sw=4 et: */
 
 
  |