diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-12-09 14:11:15 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-12-09 14:11:15 -0600 |
commit | c0658f766fa3731fc2f0191d42fa9f6c41d36691 (patch) | |
tree | 282e5296409d9cb0edfc637769fe0731099ee493 /nuttx/drivers/mtd/smart.c | |
parent | 246ab986d67e177bb4e60046ad486947e1624774 (diff) | |
download | px4-nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.gz px4-nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.bz2 px4-nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.zip |
SmartFS: Implements wear-leveling in the SmartFS. From Ken Pettit
Diffstat (limited to 'nuttx/drivers/mtd/smart.c')
-rw-r--r-- | nuttx/drivers/mtd/smart.c | 3870 |
1 files changed, 3474 insertions, 396 deletions
diff --git a/nuttx/drivers/mtd/smart.c b/nuttx/drivers/mtd/smart.c index ccb11501e..b99bed493 100644 --- a/nuttx/drivers/mtd/smart.c +++ b/nuttx/drivers/mtd/smart.c @@ -52,6 +52,9 @@ #include <debug.h> #include <errno.h> +#include <crc8.h> +#include <crc16.h> +#include <crc32.h> #include <nuttx/kmalloc.h> #include <nuttx/fs/fs.h> #include <nuttx/fs/ioctl.h> @@ -63,11 +66,21 @@ * Private Definitions ****************************************************************************/ +//#define CONFIG_SMART_LOCAL_CHECKFREE + #define SMART_STATUS_COMMITTED 0x80 #define SMART_STATUS_RELEASED 0x40 +#define SMART_STATUS_CRC 0x20 #define SMART_STATUS_SIZEBITS 0x1C #define SMART_STATUS_VERBITS 0x03 + +#if defined(CONFIG_SMART_CRC_16) +#define SMART_STATUS_VERSION 0x02 +#elif defined(CONFIG_SMART_CRC_32) +#define SMART_STATUS_VERSION 0x03 +#else #define SMART_STATUS_VERSION 0x01 +#endif #define SMART_SECTSIZE_256 0x00 #define SMART_SECTSIZE_512 0x04 @@ -94,11 +107,11 @@ #define SMART_FMT_VERSION_POS (SMART_FMT_POS1 + 4) #define SMART_FMT_NAMESIZE_POS (SMART_FMT_POS1 + 5) #define SMART_FMT_ROOTDIRS_POS (SMART_FMT_POS1 + 6) -#define SMARTFS_FMT_AGING_POS 32 - -#define SMART_FMT_VERSION 1 +#define SMARTFS_FMT_WEAR_POS 36 +#define SMART_WEAR_LEVEL_FORMAT_SIG 32 #define SMART_PARTNAME_SIZE 4 +#define SMART_FIRST_DIR_SECTOR 3 /* First root directory sector */ #define SMART_FIRST_ALLOC_SECTOR 12 /* First logical sector number we will * use for assignment of requested Alloc * sectors. All enries below this are @@ -119,21 +132,103 @@ #define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member)) #endif +#define SMART_MAX_ALLOCS 6 +//#define CONFIG_MTD_SMART_PACK_COUNTS + +#ifndef CONFIG_MTD_SMART_ALLOC_DEBUG +#define smart_malloc(d, b, n) kmm_malloc(b) +#define smart_free(d, p) kmm_free(p) +#endif + +#define SMART_WEAR_FULL_RELOCATE_THRESHOLD 8 +#define SMART_WEAR_REORG_THRESHOLD 14 +#define SMART_WEAR_MIN_LEVEL 5 +#define SMART_WEAR_FORCE_REORG_THRESHOLD 1 +#define SMART_WEAR_BIT_DIVIDE 1 +#define SMART_WEAR_ZERO_MASK 0x0F +#define SMART_WEAR_BLOCK_MASK 0x01 + +/* Bit mapping for wear level bits */ +/* These are defined to allow updating the wear leveling with the minimum + * number of sector relocations / maximum use of 1 --> 0 transitions when + * incrementing the wear level. + * + * 0: 1111 8: 1011 + * 1: 1110 9: 1010 + * 2: 1100 10: 0010 + * 3: 1000 11: 1101 + * 4: 0111 12: 1001 + * 5: 0110 13: 0001 + * 6: 0100 14: 0011 + * 7: 0000 15: 0101 + */ + +static const uint8_t gWearLevelToBitMap4[] = +{ + 0x0F, 0x0E, 0x0C, 0x08, /* Single bit erased (x3) */ + 0x07, 0x06, 0x04, 0x00, /* Single bit erased (x3) */ + 0x0B, 0x0A, 0x02, /* Single bit erased (x2) */ + 0x0D, 0x09, 0x01, /* Single bit erased (x2) */ + 0x03, + 0x05 +}; + +/* Map a Wear Level bit pattern back to the wear level */ + +static const uint8_t gWearBitToLevelMap4[] = +{ + 7, 13, 10, 14, 6, 15, 5, 4, + 3, 12, 9, 8, 2, 11, 1, 0 +}; + /**************************************************************************** * Private Types ****************************************************************************/ +#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM +struct smart_cache_s +{ + uint16_t logical; /* Logical sector number */ + uint16_t physical; /* Associated physical sector */ + uint16_t birth; /* The "birthday" of this entry */ +}; +#endif + +/* When CRC is enabled, we allocate sectors in memory only and only write + * to the device when an actual writesector is performed. If during the + * alloc process we do a physical write, we would either have to hold off on + * writing the CRC value (which creates an invalid state on the device) or + * we would have to perform a write, release re-write every time which would + * increase the wear of the device 2x. + */ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC +struct smart_allocsector_s +{ + struct smart_allocsector_s *next; /* Pointer to next alloc sector */ + uint16_t logical; /* Logical sector number */ + uint16_t physical; /* Associated physical sector */ +}; +#endif + struct smart_struct_s { FAR struct mtd_dev_s *mtd; /* Contained MTD interface */ struct mtd_geometry_s geo; /* Device geometry */ + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + uint32_t unusedsectors; /* Count of unused sectors (i.e. free when erased) */ + uint32_t blockerases; /* Count of unused sectors (i.e. free when erased) */ +#endif uint16_t neraseblocks; /* Number of erase blocks or sub-sectors */ + uint16_t lastallocblock; /* Last block we allocated a sector from */ uint16_t freesectors; /* Total number of free sectors */ + uint16_t releasesectors; /* Total number of released sectors */ uint16_t mtdBlksPerSector; /* Number of MTD blocks per SMART Sector */ uint16_t sectorsPerBlk; /* Number of sectors per erase block */ uint16_t sectorsize; /* Sector size on device */ uint16_t totalsectors; /* Total number of sectors on device */ - FAR uint16_t *sMap; /* Virtual to physical sector map */ + uint32_t erasesize; /* Size of an erase block */ FAR uint8_t *releasecount; /* Count of released sectors per erase block */ FAR uint8_t *freecount; /* Count of free sectors per erase block */ FAR char *rwbuffer; /* Our sector read/write buffer */ @@ -141,12 +236,44 @@ struct smart_struct_s uint8_t formatversion; /* Format version on the device */ uint8_t formatstatus; /* Indicates the status of the device format */ uint8_t namesize; /* Length of filenames on this device */ + uint8_t debuglevel; /* Debug reporting level */ + uint8_t availSectPerBlk; /* Number of usable sectors per erase block */ #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS uint8_t rootdirentries; /* Number of root directory entries */ uint8_t minor; /* Minor number of the block entry */ #endif +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + uint8_t wearflags; /* Indicates force erase of static blocks needed */ + uint8_t minwearlevel; /* Min level in the wear level bits */ + uint8_t maxwearlevel; /* Max level in the wear level bits */ + uint8_t *wearstatus; /* Array of wear leveling bits */ + uint32_t uneven_wearcount; /* Number of times the the wear level has gone over max */ +#endif +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsector; /* Pointer to first alloc sector */ +#endif +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + FAR uint16_t *sMap; /* Virtual to physical sector map */ +#else + FAR uint8_t *sBitMap; /* Virtual sector used bit-map */ + FAR struct smart_cache_s *sCache; /* Sector cache */ + uint16_t cache_entries; /* Number of valid entries in the cache */ + uint16_t cache_lastlog; /* Keep track of the last sector accessed */ + uint16_t cache_lastphys; /* Keep the physical sector number also */ + uint16_t cache_nextbirth; /* Sector cache aging value */ +#endif +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + FAR uint8_t *erasecounts; /* Number of erases for each erase block */ +#endif +#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG + size_t bytesalloc; + struct smart_alloc_s alloc[SMART_MAX_ALLOCS]; /* Array of memory allocations */ +#endif }; +#define SMART_WEARFLAGS_FORCE_REORG 0x01 +#define SMART_WEARFLAGS_WRITE_NEEDED 0x02 + #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS struct smart_multiroot_device_s { @@ -155,21 +282,74 @@ struct smart_multiroot_device_s }; #endif +/* Format 1 sector header definition */ + +#if SMART_STATUS_VERSION == 1 +#define SMART_FMT_VERSION 1 +struct smart_sect_header_s +{ + uint8_t logicalsector[2]; /* The logical sector number */ + uint8_t seq; /* Incrementing sequence number */ + uint8_t crc8; /* CRC-8 or seq number MSB */ + uint8_t status; /* Status of this sector: + * Bit 7: 1 = Not commited + * 0 = commited + * Bit 6: 1 = Not released + * 0 = released + * Bit 5: Sector CRC enable + * Bit 4-2: Sector size on volume + * Bit 1-0: Format version (0x1) */ +}; +typedef uint8_t crc_t; + +/* Format 2 sector header definition. This is for a 16-bit CRC */ + +#elif SMART_STATUS_VERSION == 2 +#define SMART_FMT_VERSION 2 struct smart_sect_header_s { uint8_t logicalsector[2]; /* The logical sector number */ - uint8_t seq[2]; /* Incrementing sequence number */ + uint8_t crc16[2]; /* CRC-16 for this sector */ + uint8_t status; /* Status of this sector: + * Bit 7: 1 = Not commited + * 0 = commited + * Bit 6: 1 = Not released + * 0 = released + * Bit 5: Sector CRC enable + * Bit 4-2: Sector size on volume + * Bit 1-0: Format version (0x2) */ + uint8_t seq; /* Incrementing sequence number */ +}; +typedef uint16_t crc_t; + +/* Format 3 (32-bit) sector header definition. Actually, this format + * isn't used yet and will likely be changed to a format to support + * NAND devices (possibly with an 18-bit sector size, allowing up to + * 256K sectors on a larger NAND device, though this would take a fair + * amount of RAM for management). + */ + +#elif SMART_STATUS_VERSION == 3 +#error "32-Bit mode not supported yet" +#define SMART_FMT_VERSION 3 +struct smart_sect_header_s +{ + uint8_t logicalsector[4]; /* The logical sector number */ + uint8_t crc32[4]; /* CRC-32 for this sector */ uint8_t status; /* Status of this sector: * Bit 7: 1 = Not commited * 0 = commited * Bit 6: 1 = Not released * 0 = released - * Bit 5: Reserved - 1 - * Bit 4: Reserved - 1 - * Bit 3: Reserved - 1 - * Bit 2: Reserved - 1 - * Bit 1-0: Format version */ + * Bit 5: Sector CRC enable + * Bit 4-2: Sector size on volume + * Bit 1-0: Format version (0x3) */ + uint8_t seq; /* Incrementing sequence number */ }; +typedef uint32_t crc_t; + +#endif + /**************************************************************************** * Private Function Prototypes @@ -188,6 +368,21 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer, static int smart_geometry(FAR struct inode *inode, struct geometry *geometry); static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg); +static int smart_findfreephyssector(FAR struct smart_struct_s *dev, uint8_t canrelocate); + +#ifdef CONFIG_FS_WRITABLE +static int smart_writesector(FAR struct smart_struct_s *dev, unsigned long arg); +#endif +static int smart_readsector(FAR struct smart_struct_s *dev, unsigned long arg); + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static int smart_read_wearstatus(FAR struct smart_struct_s *dev); +static int smart_relocate_static_data(FAR struct smart_struct_s *dev, uint16_t block); +#endif + +static int smart_relocate_sector(FAR struct smart_struct_s *dev, + uint16_t oldsector, uint16_t newsector); + /**************************************************************************** * Private Data ****************************************************************************/ @@ -237,6 +432,292 @@ static int smart_close(FAR struct inode *inode) } /**************************************************************************** + * Name: smart_malloc + * + * Description: Perform allocations and keep track of amount of allocated + * memory for this context. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG +FAR static void* smart_malloc(FAR struct smart_struct_s *dev, + size_t bytes, const char *name) +{ + void* ret = kmm_malloc(bytes); + uint8_t x; + + /* Keep track of the total allocation */ + + if (ret != NULL) + { + dev->bytesalloc += bytes; + } + + /* Keep track of individual allocs */ + + for (x = 0; x < SMART_MAX_ALLOCS; x++) + { + if (dev->alloc[x].ptr == NULL) + { + dev->alloc[x].ptr = ret; + dev->alloc[x].size = bytes; + dev->alloc[x].name = name; + break; + } + } + + fdbg("SMART alloc: %ld\n", dev->bytesalloc); + return ret; +} +#endif + +/**************************************************************************** + * Name: smart_free + * + * Description: Perform smart memory free operation. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG +static void smart_free(FAR struct smart_struct_s *dev, FAR void* ptr) +{ + uint8_t x; + + for (x = 0; x < SMART_MAX_ALLOCS; x++) + { + if (dev->alloc[x].ptr == ptr) + { + dev->alloc[x].ptr = NULL; + dev->bytesalloc -= dev->alloc[x].size; + kmm_free(ptr); + break; + } + } +} +#endif + +/**************************************************************************** + * Name: smart_set_count + * + * Description: Set either the freecount or releasecount value for the + * specified eraseblock (depending on which pointer is passed). + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS +static void smart_set_count(FAR struct smart_struct_s *dev, FAR uint8_t* pCount, + uint16_t block, uint8_t count) +{ + if (dev->sectorsPerBlk > 16) + { + pCount[block] = count; + } + else + { + /* Save the lower 4 bits of the count in a shared byte */ + + if (block & 0x01) + { + pCount[block >> 1] = (pCount[block >> 1] & 0xF0) | (count & 0x0F); + } + else + { + pCount[block >> 1] = (pCount[block >> 1] & 0x0F) | ((count & 0x0F) << 4); + } + + /* If we have 16 sectors per block, then the upper bit (representing 16) + * all get packed into shared bytes. + */ + + if (dev->sectorsPerBlk == 16) + { + if (count == 16) + { + pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] |= 1 << (block & 0x07); + } + else + { + pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] &= ~(1 << (block & 0x07)); + } + } + } +} +#endif + +/**************************************************************************** + * Name: smart_get_count + * + * Description: Get either the freecount or releasecount value for the + * specified eraseblock (depending on which pointer is passed). + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS +static uint8_t smart_get_count(FAR struct smart_struct_s *dev, + FAR uint8_t* pCount, uint16_t block) +{ + uint8_t count; + + if (dev->sectorsPerBlk > 16) + { + count = pCount[block]; + } + else + { + /* Save the lower 4 bits of the count in a shared byte */ + + if (block & 0x01) + { + count = pCount[block >> 1] & 0x0F; + } + else + { + count = pCount[block >> 1] >> 4; + } + + /* If we have 16 sectors per block, then the upper bit (representing 16) + * all get packed into shared bytes. + */ + + if (dev->sectorsPerBlk == 16) + { + if (pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] & (1 << (block & 0x07))) + { + count |= 0x10; + } + } + } + + return count; +} +#endif + +/**************************************************************************** + * Name: smart_add_count + * + * Description: Add the specified value to and eraseblock count. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS +static void smart_add_count(struct smart_struct_s *dev, uint8_t* pCount, + uint16_t block, int adder) +{ + int16_t value; + + value = smart_get_count(dev, pCount, block) + adder; + smart_set_count(dev, pCount, block, value); +} +#endif + +/**************************************************************************** + * Name: smart_checkfree + * + * Description: A debug routine for validating the free sector count used + * during development of the wear leveling code. + * + ****************************************************************************/ + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE +int smart_checkfree(FAR struct smart_struct_s *dev, int lineno) +{ + uint16_t x, freecount; +#ifdef CONFIG_DEBUG_FS + uint16_t blockfree, blockrelease; + static uint16_t prev_freesectors = 0; + static uint16_t prev_releasesectors = 0; + static uint8_t *prev_freecount = NULL; + static uint8_t *prev_releasecount = NULL; +#endif + + freecount = 0; + for (x = 0; x < dev->neraseblocks; x++) + { +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + freecount += smart_get_count(dev, dev->freecount, x); +#else + freecount += dev->freecount[x]; +#endif + } + + /* Test if the calculated freesectors equals the reported value */ + +#ifdef CONFIG_DEBUG_FS + if (freecount != dev->freesectors) + { + fdbg("Free count incorrect in line %d! Calculated=%d, dev->freesectors=%d\n", + lineno, freecount, dev->freesectors); + + /* Determine what changed from the last time which caused this error */ + + fdbg(" ... Prev freesectors=%d, prev releasesectors=%d\n", + prev_freesectors, prev_releasesectors); + + if (prev_freecount) + { + for (x = 0; x < dev->neraseblocks; x++) + { +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + blockfree = smart_get_count(dev, dev->freecount, x); + blockrelease = smart_get_count(dev, dev->releasecount, x); +#else + blockfree = dev->freecount[x]; + blockrelease = dev->releasecount[x]; +#endif + if (prev_freecount[x] != blockfree || prev_releasecount[x] != blockrelease) + { + /* This block's values are different from the last time ... report it */ + + fdbg(" ... Block %d: Old Free=%d, old release=%d, New free=%d, new release = %d\n", + x, prev_freecount[x], prev_releasecount[x], blockfree, blockrelease); + } + } + } + + /* Modifiy the freesector count to reflect the actual calculated freecount + to get us back in line. + */ + + dev->freesectors = freecount; + return -EIO; + } + + /* Make a copy of the freecount and releasecount arrays to compare the + * differences between successive calls so we can evaluate what changed + * in the event an error is detected. + */ + + if (prev_freecount == NULL) + { + prev_freecount = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks << 1, "Free backup"); + prev_releasecount = prev_freecount + dev->neraseblocks; + } + + if (prev_freecount != NULL) + { + for (x = 0; x < dev->neraseblocks; x++) + { +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + prev_freecount[x] = smart_get_count(dev, dev->freecount, x); + prev_releasecount[x] = smart_get_count(dev, dev->releasecount, x); +#else + prev_freecount[x] = dev->freecount[x]; + prev_releasecount[x] = dev->releasecount[x]; +#endif + } + } + + /* Save the previous freesectors count */ + + prev_freesectors = dev->freesectors; + prev_releasesectors = dev->releasesectors; +#endif + + return OK; +} +#endif + +/**************************************************************************** * Name: smart_reload * * Description: Read the specified numer of sectors @@ -259,8 +740,8 @@ static ssize_t smart_reload(struct smart_struct_s *dev, FAR uint8_t *buffer, /* Read the full erase block into the buffer */ - fdbg("Read %d blocks starting at block %d\n", mtdBlocks, mtdStartBlock); - nread = MTD_BREAD(dev->mtd, mtdStartBlock, mtdBlocks, buffer); + fvdbg("Read %d blocks starting at block %d\n", mtdBlocks, mtdStartBlock); + nread = MTD_BREAD(dev->mtd, mtdStartBlock, mtdBlocks, buffer); if (nread != mtdBlocks) { fdbg("Read %d blocks starting at block %d failed: %d\n", @@ -301,10 +782,11 @@ static ssize_t smart_read(FAR struct inode *inode, unsigned char *buffer, ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer, - size_t start_sector, unsigned int nsectors) +static ssize_t smart_write(FAR struct inode *inode, + FAR const unsigned char *buffer, + size_t start_sector, unsigned int nsectors) { - struct smart_struct_s *dev; + FAR struct smart_struct_s *dev; off_t alignedblock; off_t mask; off_t blkstowrite; @@ -321,9 +803,9 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer, DEBUGASSERT(inode && inode->i_private); #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS - dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev; + dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev; #else - dev = (struct smart_struct_s *)inode->i_private; + dev = (FAR struct smart_struct_s *)inode->i_private; #endif /* I think maybe we need to lock on a mutex here */ @@ -421,7 +903,7 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer, static int smart_geometry(FAR struct inode *inode, struct geometry *geometry) { - struct smart_struct_s *dev; + FAR struct smart_struct_s *dev; uint32_t erasesize; fvdbg("Entry\n"); @@ -430,9 +912,9 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry) if (geometry) { #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS - dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev; + dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev; #else - dev = (struct smart_struct_s *)inode->i_private; + dev = (FAR struct smart_struct_s *)inode->i_private; #endif geometry->geo_available = true; geometry->geo_mediachanged = false; @@ -445,7 +927,7 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry) erasesize = dev->geo.erasesize; if (erasesize == 0) { - erasesize = 65536; + erasesize = 262144; } geometry->geo_nsectors = dev->geo.neraseblocks * erasesize / @@ -471,10 +953,11 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry) * ****************************************************************************/ -static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size) +static int smart_setsectorsize(FAR struct smart_struct_s *dev, uint16_t size) { uint32_t erasesize; uint32_t totalsectors; + uint32_t allocsize; /* Validate the size isn't zero so we don't divide by zero below */ @@ -483,6 +966,11 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size) size = CONFIG_MTD_SMART_SECTOR_SIZE; } + if (size == dev->sectorsize) + { + return OK; + } + erasesize = dev->geo.erasesize; dev->neraseblocks = dev->geo.neraseblocks; @@ -492,56 +980,263 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size) if (erasesize == 0) { - erasesize = 65536; + erasesize = 262144; } + dev->erasesize = erasesize; dev->sectorsize = size; dev->mtdBlksPerSector = dev->sectorsize / dev->geo.blocksize; - dev->sectorsPerBlk = erasesize / dev->sectorsize; + if (erasesize / dev->sectorsize > 256) + { + /* We can't throw a dbg message here becasue it is too early. + * set the erasesize to zero and exit, then we will detect + * it during mksmartfs or mount. + */ + + dev->erasesize = 0; + dev->sectorsPerBlk = 256; + dev->availSectPerBlk = 255; + } + else + { + /* Set the sectors per erase block and available sectors per erase block */ + + dev->sectorsPerBlk = erasesize / dev->sectorsize; + if (dev->sectorsPerBlk == 256) + { + dev->availSectPerBlk = 255; + } + else + { + dev->availSectPerBlk = dev->sectorsPerBlk; + } + } + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + dev->unusedsectors = 0; + dev->blockerases = 0; +#endif /* Release any existing rwbuffer and sMap */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM if (dev->sMap != NULL) { - kmm_free(dev->sMap); + smart_free(dev, dev->sMap); + dev->sMap = NULL; + } +#else + if (dev->sBitMap != NULL) + { + smart_free(dev, dev->sBitMap); + dev->sBitMap = NULL; } + dev->cache_entries = 0; + dev->cache_lastlog = 0xFFFF; + dev->cache_nextbirth = 0; +#endif + if (dev->rwbuffer != NULL) { - kmm_free(dev->rwbuffer); + smart_free(dev, dev->rwbuffer); + dev->rwbuffer = NULL; } +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + if (dev->wearstatus != NULL) + { + smart_free(dev, dev->wearstatus); + dev->wearstatus = NULL; + } +#endif + /* Allocate a virtual to physical sector map buffer. Also allocate * the storage space for releasecount and freecounts. */ totalsectors = dev->neraseblocks * dev->sectorsPerBlk; + + /* Validate the number of total sectors is small enough for a uint16_t */ + + if (totalsectors > 65536) + { + dbg("Invalid SMART sector count %ld\n", totalsectors); + return -EINVAL; + } + else if (totalsectors == 65536) + { + /* Special case. We allow 65536 sectors and simply waste 2 sectors + * to allow a smaller sector size with almost maximum flash usage. + */ + + totalsectors -= 2; + } + dev->totalsectors = (uint16_t) totalsectors; - dev->sMap = (uint16_t *) kmm_malloc(totalsectors * sizeof(uint16_t) + - (dev->neraseblocks << 1)); +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + allocsize = dev->neraseblocks << 1; + dev->sMap = (FAR uint16_t *) smart_malloc(dev, totalsectors * sizeof(uint16_t) + + allocsize, "Sector map"); if (!dev->sMap) { fdbg("Error allocating SMART virtual map buffer\n"); - kmm_free(dev); - return -EINVAL; + goto errexit; + } + + dev->releasecount = (FAR uint8_t *) dev->sMap + (totalsectors * sizeof(uint16_t)); + dev->freecount = dev->releasecount + dev->neraseblocks; +#else + dev->sBitMap = (FAR uint8_t *) smart_malloc(dev, (totalsectors+7) >> 3, "Sector Bitmap"); + if (dev->sBitMap == NULL) + { + fdbg("Error allocating SMART sector cache\n"); + goto errexit; + } + + /* Calculate the alloc size of the freesector and release sector arrays */ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + if (dev->sectorsPerBlk > 16) + { + allocsize = dev->neraseblocks << 1; } + else if (dev->sectorsPerBlk == 16) + { + allocsize = dev->neraseblocks + (dev->neraseblocks >> 2); + } + else + { + allocsize = dev->neraseblocks; + } + +#else + allocsize = dev->neraseblocks << 1; +#endif - dev->releasecount = (uint8_t *) dev->sMap + (totalsectors * sizeof(uint16_t)); + /* Allocate the sector cache */ + + if (dev->sCache == NULL) + { + dev->sCache = (FAR struct smart_cache_s *) smart_malloc(dev, + CONFIG_MTD_SMART_SECTOR_CACHE_SIZE * sizeof(struct smart_cache_s) + + allocsize, "Sector Cache"); + } + + if (!dev->sCache) + { + fdbg("Error allocating SMART sector cache\n"); + goto errexit; + } + + dev->releasecount = (FAR uint8_t *) dev->sCache + (CONFIG_MTD_SMART_SECTOR_CACHE_SIZE * + sizeof(struct smart_cache_s)); + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + if (dev->sectorsPerBlk > 16) + { + dev->freecount = dev->releasecount + dev->neraseblocks; + } + else if (dev->sectorsPerBlk == 16) + { + dev->freecount = dev->releasecount + (dev->neraseblocks >> 1) + (dev->neraseblocks >> 3); + } + else + { + dev->freecount = dev->releasecount + (dev->neraseblocks >> 1); + } + +#else dev->freecount = dev->releasecount + dev->neraseblocks; +#endif + +#endif /* CONFIG_MTD_SMART_MINIMIZE_RAM */ + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + /* Allocate a buffer to hold the erase counts */ + + if (dev->erasecounts == NULL) + { + dev->erasecounts = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks, "Erase counts"); + } + + if (!dev->erasecounts) + { + fdbg("Error allocating erase count array\n"); + goto errexit; + } + + memset(dev->erasecounts, 0, dev->neraseblocks); +#endif + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + /* Allocate the wear leveling status array */ + + dev->wearstatus = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks >> + SMART_WEAR_BIT_DIVIDE, "Wear status"); + if (!dev->wearstatus) + { + fdbg("Error allocating wear level status array\n"); + goto errexit; + } + + memset(dev->wearstatus, CONFIG_SMARTFS_ERASEDSTATE, dev->neraseblocks >> + SMART_WEAR_BIT_DIVIDE); + dev->wearflags = 0; + dev->uneven_wearcount = 0; +#endif /* Allocate a read/write buffer */ - dev->rwbuffer = (char *) kmm_malloc(size); + dev->rwbuffer = (FAR char *) smart_malloc(dev, size, "RW Buffer"); if (!dev->rwbuffer) { fdbg("Error allocating SMART read/write buffer\n"); - kmm_free(dev->sMap); - kmm_free(dev); - return -EINVAL; + goto errexit; } return OK; + + /* On error for any allocation, we jump here and free anything that had + * previously been allocated. + */ + +errexit: + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + if (dev->sMap) + { + smart_free(dev, dev->sMap); + } +#else + if (dev->sBitMap) + { + smart_free(dev, dev->sBitMap); + } + + if (dev->sCache) + { + smart_free(dev, dev->sCache); + } +#endif + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + if (dev->wearstatus) + { + smart_free(dev, dev->wearstatus); + } +#endif + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + if (dev->erasecounts) + { + smart_free(dev, dev->erasecounts); + } +#endif + + kmm_free(dev); + return -ENOMEM; } /**************************************************************************** @@ -551,12 +1246,12 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size) * MTD device. If the MTD driver supports a direct impl of * write, then it uses it, otherwise it does a read-modify-write * and depends on the architecture of the flash to only program - * bits that acutally changed. + * bits that actually changed. * ****************************************************************************/ -static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset, - int nbytes, const uint8_t *buffer) +static ssize_t smart_bytewrite(FAR struct smart_struct_s *dev, size_t offset, + int nbytes, FAR const uint8_t *buffer) { ssize_t ret; @@ -587,7 +1282,7 @@ static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset, /* Do a block read */ - ret = MTD_BREAD(dev->mtd, startblock, nblocks, (uint8_t *) dev->rwbuffer); + ret = MTD_BREAD(dev->mtd, startblock, nblocks, (FAR uint8_t *) dev->rwbuffer); if (ret < 0) { fdbg("Error %d reading from device\n", -ret); @@ -600,7 +1295,7 @@ static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset, /* Write the data back to the device */ - ret = MTD_BWRITE(dev->mtd, startblock, nblocks, (uint8_t *) dev->rwbuffer); + ret = MTD_BWRITE(dev->mtd, startblock, nblocks, (FAR uint8_t *) dev->rwbuffer); if (ret < 0) { fdbg("Error %d writing to device\n", -ret); @@ -615,6 +1310,451 @@ errout: } /**************************************************************************** + * Name: smart_add_sector_to_cache + * + * Description: Adds a logical to physical sector maaping to the sector + * map cache. The cache is used to minimize RAM by eliminating + * a one-to-one mapping of all logical sectors and only keeping + * a fixed number of mappings per the + * CONFIG_MTD_SMART_SECTOR_CACHE_SIZE parameter. Sectors are + * automatically managed and removed based on the time since + * they were accessed last. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM +static int smart_add_sector_to_cache(FAR struct smart_struct_s *dev, + uint16_t logical, uint16_t physical, int line) +{ + uint16_t index, x; + uint16_t oldest; + + /* If we aren't full yet, just add the sector to the end of the list */ + + index = 1; + if (dev->cache_entries < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE) + { + index = dev->cache_entries++; + } + else + { + /* Cache is full. We must find the least accessed entry and replace it */ + + oldest = 0xFFFF; + for (x = 0; x < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE; x++) + { + /* Never replace cache entries for system sectors */ + + if (dev->sCache[x].logical < SMART_FIRST_ALLOC_SECTOR) + continue; + + /* If the hit count is zero, then choose this entry */ + + if (dev->sCache[x].birth < oldest) + { + oldest = dev->sCache[x].birth; + index = x; + } + } + } + + /* Now add the sector at index */ + + dev->sCache[index].logical = logical; + dev->sCache[index].physical = physical; + dev->sCache[index].birth = dev->cache_nextbirth++; + dev->cache_lastlog = logical; + dev->cache_lastphys = physical; + if (dev->debuglevel > 1) + { + dbg("Add Cache sector: Log=%d, Phys=%d at index %d from line %d\n", + logical, physical, index, line); + } + + /* Test if the birthdays need to be adjusted */ + + if (oldest >= CONFIG_MTD_SMART_SECTOR_CACHE_SIZE + 1024) + { + for (x = 0; x < dev->cache_entries; x++) + { + dev->sCache[x].birth -= 1024; + } + + dev->cache_nextbirth -= 1024; + } + + return index; +} +#endif + +/**************************************************************************** + * Name: smart_cache_lookup + * + * Description: Perform a cache lookup for the requested logical sector. + * If the sector is in the cache, then update the hitcount and + * return the physical mapping. If a cache miss occurs, then + * the routine will scan the volume to find the logical sector + * and add / replace a cache entry with the newly located sector. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM +static uint16_t smart_cache_lookup(FAR struct smart_struct_s *dev, uint16_t logical) +{ + int ret; + uint16_t block, sector; + uint16_t x, physical, logicalsector; + struct smart_sect_header_s header; + size_t readaddress; + + physical = 0xFFFF; + + /* Test if searching for the last sector used */ + + if (logical == dev->cache_lastlog) + { + return dev->cache_lastphys; + } + + /* First search for the entry in the cache */ + + for (x = 0; x < dev->cache_entries; x++) + { + if (dev->sCache[x].logical == logical) + { + /* Entry found in the cache. Grab the physical mapping. */ + + physical = dev->sCache[x].physical; + break; + } + } + + /* If the entry wasn't found in the cache, then we must search the volume + * for it and add it to the cache. + */ + + if (physical == 0xFFFF) + { + /* Now scan the MTD device. Instead of scanning start to end, we + * span the erase blocks and read one sector from each at a time. + * this helps speed up the search on volumes that aren't full + * because of sector allocation scheme will use the lower sector + * numbers in each erase block first. + */ + + for (sector = 0; sector < dev->sectorsPerBlk && physical == 0xFFFF; sector++) + { + /* Now scan across each erase block */ + + for (block = 0; block < dev->geo.neraseblocks; block++) + { + /* Calculate the read address for this sector */ + + readaddress = block * dev->erasesize + + sector * CONFIG_MTD_SMART_SECTOR_SIZE; + + /* Read the header for this sector */ + + ret = MTD_READ(dev->mtd, readaddress, + sizeof(struct smart_sect_header_s), (FAR uint8_t *) &header); + if (ret != sizeof(struct smart_sect_header_s)) + { + goto err_out; + } + + /* Get the logical sector number for this physical sector */ + + logicalsector = *((FAR uint16_t *) header.logicalsector); +#if CONFIG_SMARTFS_ERASEDSTATE == 0x00 + if (logicalsector == 0) + { + continue; + } +#endif + + /* Test if this sector has been committed */ + + if ((header.status & SMART_STATUS_COMMITTED) == + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) + { + continue; + } + + /* Test if this sector has been release and skip it if it has */ + + if ((header.status & SMART_STATUS_RELEASED) != + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)) + { + continue; + } + + if ((header.status & SMART_STATUS_VERBITS) != SMART_STATUS_VERSION) + { + continue; + } + + /* Test if this is the sector we are looking for */ + + if (logicalsector == logical) + { + /* This is the sector we are looking for! Add it to the cache */ + + physical = block * dev->sectorsPerBlk + sector; + smart_add_sector_to_cache(dev, logical, physical, __LINE__ ); + break; + } + } + } + } + + /* Update the last logical sector found variable */ + + dev->cache_lastlog = logical; + dev->cache_lastphys = physical; + +err_out: + return physical; +} +#endif + +/**************************************************************************** + * Name: smart_update_cache + * + * Description: Updates a cache entry (if present) replacing the logical + * sector's physical sector mapping with the new one provided. + * This does not affect the hit count. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM +static void smart_update_cache(FAR struct smart_struct_s *dev, uint16_t + logical, uint16_t physical) +{ + uint16_t x; + + /* Scan through all cache entries and find the logical sector entry */ + + for (x = 0; x < dev->cache_entries; x++) + { + if (dev->sCache[x].logical == logical) + { + /* Entry found. Update it's physical mapping */ + + dev->sCache[x].physical = physical; + + /* If we are freeing a sector, then remove the logical entry from + the cache. + */ + + if (physical == 0xFFFF) + { + dev->sCache[x].logical = dev->sCache[dev->cache_entries-1].logical; + dev->sCache[x].physical = dev->sCache[dev->cache_entries-1].physical; + dev->cache_entries--; + } + + if (dev->debuglevel > 1) + { + dbg("Update Cache: Log=%d, Phys=%d at index %d\n", logical, physical, x); + } + + break; + } + } + + if (dev->cache_lastlog == logical) + { + dev->cache_lastphys = physical; + } +} +#endif + +/**************************************************************************** + * Name: smart_get_wear_level + * + * Description: Gets the wear level of the specified block. Wear levels are + * encoded to minimize the number of zero to one transitions, + * possibly allowing updates to made on NOR devices that have + * no CRC enabled. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static uint8_t smart_get_wear_level(FAR struct smart_struct_s *dev, uint16_t block) +{ + uint8_t bits; + + bits = dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE]; + if (block & 0x01) + { + /* Use the upper nibble */ + + bits >>= 4; + } + else + { + /* Use the lower nibble */ + + bits &= 0x0F; + } + + /* Lookup and return the level using the BitToLevel map */ + + return gWearBitToLevelMap4[bits]; +} +#endif + +/**************************************************************************** + * Name: smart_find_wear_minmax + * + * Description: Find the minimum and maximum wear levels. This is used when + * we increment the wear level of a minimum value block so that + * we can detect if a new minimum exists and perform normalization + * of the wear-levels. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static void smart_find_wear_minmax(FAR struct smart_struct_s *dev) +{ + uint16_t x; + unsigned char level; + + dev->minwearlevel = 15; + dev->maxwearlevel = 0; + + /* Loop through all erase blocks and find min / max level */ + + for (x = 0; x < dev->geo.neraseblocks; x++) + { + /* Find wear level of the minimum worn block */ + + level = smart_get_wear_level(dev, x); + if (level < dev->minwearlevel) + { + dev->minwearlevel = level; + } + + /* Find wear level of the maximum worn block */ + + if (level > dev->maxwearlevel) + { + dev->maxwearlevel = level; + } + } + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + /* Also adjust the erase counts */ + level = 255; + for (x = 0; x < dev->geo.neraseblocks; x++) + { + if (dev->erasecounts[x] < level) + { + level = dev->erasecounts[x]; + } + } + + if (level != 0) + { + for (x = 0; x < dev->geo.neraseblocks; x++) + { + dev->erasecounts[x] -= level; + } + } + +#endif +} +#endif + +/**************************************************************************** + * Name: smart_set_wear_level + * + * Description: Sets the wear level of the specified block. The wear level + * is a 4-bit field packed 2 entries per byte and is mapped to + * a bit field which minimizes the number of 0 to 1 transitions + * such that entries can be updated on a NOR flash withough the + * need to relocated the format sector (assuming CRC is not + * enabled, in which case a relocated is needed for ANY change). + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static int smart_set_wear_level(FAR struct smart_struct_s *dev, uint16_t block, + uint8_t level) +{ + uint8_t bits, oldlevel; + + /* Get the old wear level to test if we need to update min / max */ + + oldlevel = smart_get_wear_level(dev, block); + + /* Get the bit map for this wear level from the static map array */ + + if (level > 15) + { + dbg("Fatal Design Error! Wear level > 15, block=%d\n", block); + + /* This is a design flaw, but we still allow processing, otherwise we + * will corrupt the volume. It's better to have a few blocks that are + * worn a bit more than to create an error condition on the volume. + * + * Set the level to the maximum value and add to the un-even wear count + * to keep track of the number of times this has happened. + */ + + level = 15; + dev->uneven_wearcount++; + } + + bits = gWearLevelToBitMap4[level]; + + if (block & 0x01) + { + /* Use the upper nibble */ + + dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0x0F; + dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits << 4; + } + else + { + /* Use the lower nibble */ + + dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0xF0; + dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits; + } + + /* Mark wear bits as dirty */ + + dev->wearflags |= SMART_WEARFLAGS_WRITE_NEEDED; + + /* Test if min / max need to be updated */ + + if (oldlevel + 1 == level) + { + /* Test if max needs to be updated */ + + if (level > dev->maxwearlevel) + { + dev->maxwearlevel = level; + } + + /* Test if this was the min level. If it was, then + we need to rescan for min. */ + + if (oldlevel == dev->minwearlevel) + { + smart_find_wear_minmax(dev); + + if (oldlevel != dev->minwearlevel) + fvdbg("##### New min wear level = %d\n", dev->minwearlevel); + } + } + + return 0; +} +#endif + +/**************************************************************************** * Name: smart_scan * * Description: Performs a scan of the MTD device searching for format @@ -623,48 +1763,75 @@ errout: * ****************************************************************************/ -static int smart_scan(struct smart_struct_s *dev) +static int smart_scan(FAR struct smart_struct_s *dev) { int sector; int ret; - int offset; uint16_t totalsectors; - uint16_t sectorsize; + uint16_t sectorsize, prerelease; uint16_t logicalsector; + uint16_t loser; + uint32_t readaddress; + uint32_t offset; uint16_t seq1; uint16_t seq2; - size_t readaddress; struct smart_sect_header_s header; +#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM + int dupsector; + uint16_t duplogsector; +#endif #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS int x; char devname[22]; - struct smart_multiroot_device_s *rootdirdev; + FAR struct smart_multiroot_device_s *rootdirdev; #endif fvdbg("Entry\n"); - /* Read the 1st header from the device. We always keep the - * 1st sector's header's sectorsize field accurate, even - * after we erase an MTD block/sector */ + /* Find the sector size on the volume by reading headers from + * sectors of decreasing size. On a formatted volume, the sector + * size is saved in the header status byte of seach sector, so + * by starting with the largest supported sector size and + * decreasing from there, we will be sure to find data that is + * a header and not sector data. + */ - ret = MTD_READ(dev->mtd, 0, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); - if (ret != sizeof(struct smart_sect_header_s)) + sectorsize = 0xFFFF; + offset = 16384; + + while (sectorsize == 0xFFFF) { - goto err_out; - } + readaddress = 0; - /* Now set the sectorsize and other sectorsize derived variables */ + while (readaddress < dev->erasesize * dev->geo.neraseblocks) + { + /* Read the next sector from the device */ - if (header.status == CONFIG_SMARTFS_ERASEDSTATE) - { - sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE; - } - else - { - sectorsize = (header.status & SMART_STATUS_SIZEBITS) << 7; + ret = MTD_READ(dev->mtd, 0, sizeof(struct smart_sect_header_s), + (FAR uint8_t *) &header); + if (ret != sizeof(struct smart_sect_header_s)) + { + goto err_out; + } + + if (header.status != CONFIG_SMARTFS_ERASEDSTATE) + { + sectorsize = (header.status & SMART_STATUS_SIZEBITS) << 7; + break; + } + + readaddress += offset; + } + + offset >>= 1; + if (offset < 256 && sectorsize == 0xFFFF) + { + sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE; + } } + /* Now set the sectorsize and other sectorsize derived variables */ + ret = smart_setsectorsize(dev, sectorsize); if (ret != OK) { @@ -673,24 +1840,45 @@ static int smart_scan(struct smart_struct_s *dev) /* Initialize the device variables */ - totalsectors = dev->neraseblocks * dev->sectorsPerBlk; + totalsectors = dev->totalsectors; dev->formatstatus = SMART_FMT_STAT_NOFMT; - dev->freesectors = totalsectors; + dev->freesectors = dev->availSectPerBlk * dev->geo.neraseblocks; + dev->releasesectors = 0; /* Initialize the freecount and releasecount arrays */ for (sector = 0; sector < dev->neraseblocks; sector++) { - dev->freecount[sector] = dev->sectorsPerBlk; - dev->releasecount[sector] = 0; + if (sector == dev->neraseblocks - 1 && dev->totalsectors == 65534) + { + prerelease = 2; + } + else + { + prerelease = 0; + } + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_set_count(dev, dev->freecount, sector, dev->availSectPerBlk - prerelease); + smart_set_count(dev, dev->releasecount, sector, prerelease); +#else + dev->freecount[sector] = dev->availSectPerBlk - prerelease; + dev->releasecount[sector] = prerelease; +#endif } /* Initialize the sector map */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM for (sector = 0; sector < totalsectors; sector++) { dev->sMap[sector] = -1; } +#else + /* Clear all logical sector used bits */ + + memset(dev->sBitMap, 0, (dev->totalsectors + 7) >> 3); +#endif /* Now scan the MTD device */ @@ -705,7 +1893,7 @@ static int smart_scan(struct smart_struct_s *dev) /* Read the header for this sector */ ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); + (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { goto err_out; @@ -713,7 +1901,7 @@ static int smart_scan(struct smart_struct_s *dev) /* Get the logical sector number for this physical sector */ - logicalsector = *((uint16_t *) header.logicalsector); + logicalsector = *((FAR uint16_t *) header.logicalsector); #if CONFIG_SMARTFS_ERASEDSTATE == 0x00 if (logicalsector == 0) { @@ -733,7 +1921,11 @@ static int smart_scan(struct smart_struct_s *dev) * erase block's freecount. */ +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->freecount, sector / dev->sectorsPerBlk, -1); +#else dev->freecount[sector / dev->sectorsPerBlk]--; +#endif dev->freesectors--; /* Test if this sector has been release and if it has, @@ -743,7 +1935,16 @@ static int smart_scan(struct smart_struct_s *dev) if ((header.status & SMART_STATUS_RELEASED) != (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)) { + /* Keep track of the total number of released sectors and + * released sectors per erase block. + */ + + dev->releasesectors++; +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->releasecount, sector / dev->sectorsPerBlk, 1); +#else dev->releasecount[sector / dev->sectorsPerBlk]++; +#endif continue; } @@ -772,7 +1973,7 @@ static int smart_scan(struct smart_struct_s *dev) /* Read the sector data */ ret = MTD_READ(dev->mtd, readaddress, 32, - (uint8_t*) dev->rwbuffer); + (FAR uint8_t*) dev->rwbuffer); if (ret != 32) { fdbg("Error reading physical sector %d.\n", sector); @@ -792,8 +1993,6 @@ static int smart_scan(struct smart_struct_s *dev) continue; } - /* TODO: May want to validate / save the erase block aging info */ - /* Mark the volume as formatted and set the sector size */ dev->formatstatus = SMART_FMT_STAT_FORMATTED; @@ -824,7 +2023,8 @@ static int smart_scan(struct smart_struct_s *dev) * the SMART device structure and the root directory number. */ - rootdirdev = (struct smart_multiroot_device_s*) kmm_malloc(sizeof(*rootdirdev)); + rootdirdev = (struct smart_multiroot_device_s*) smart_malloc(dev, + sizeof(*rootdirdev), "Root Dir"); if (rootdirdev == NULL) { fdbg("Memory alloc failed\n"); @@ -836,55 +2036,138 @@ static int smart_scan(struct smart_struct_s *dev) rootdirdev->dev = dev; rootdirdev->rootdirnum = x; - (void)register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev); + ret = register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev); /* Inode private data is a reference to the SMART device structure */ - (void)register_blockdriver(devname, &g_bops, 0, rootdirdev); + ret = register_blockdriver(devname, &g_bops, 0, rootdirdev); } #endif } /* Test for duplicate logical sectors on the device */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM if (dev->sMap[logicalsector] != 0xFFFF) +#else + if (dev->sBitMap[logicalsector >> 3] & (1 << (logicalsector & 0x07))) +#endif { /* Uh-oh, we found more than 1 physical sector claiming to be - * the * same logical sector. Use the sequence number information + * the same logical sector. Use the sequence number information * to resolve who wins. */ - uint16_t loser; - seq2 = *((uint16_t *) header.seq); +#if SMART_STATUS_VERSION == 1 + if (header.status & SMART_STATUS_CRC) + { + seq2 = header.seq; + } + else + { + seq2 = *((FAR uint16_t *) &header.seq); + } +#else + seq2 = header.seq; +#endif /* We must re-read the 1st physical sector to get it's seq number */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM readaddress = dev->sMap[logicalsector] * dev->mtdBlksPerSector * dev->geo.blocksize; +#else + /* For minimize RAM, we have to rescan to find the 1st sector claiming to + * be this logical sector. + */ + + for (dupsector = 0; dupsector < sector; dupsector++) + { + /* Calculate the read address for this sector */ + + readaddress = dupsector * dev->mtdBlksPerSector * dev->geo.blocksize; + + /* Read the header for this sector */ + + ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s), + (FAR uint8_t *) &header); + if (ret != sizeof(struct smart_sect_header_s)) + { + goto err_out; + } + + /* Get the logical sector number for this physical sector */ + + duplogsector = *((FAR uint16_t *) header.logicalsector); +#if CONFIG_SMARTFS_ERASEDSTATE == 0x00 + if (duplogsector == 0) + { + duplogsector = -1; + } +#endif + + /* Test if this sector has been committed */ + + if ((header.status & SMART_STATUS_COMMITTED) == + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) + { + continue; + } + + /* Test if this sector has been release and skip it if it has */ + + if ((header.status & SMART_STATUS_RELEASED) != + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)) + { + continue; + } + + if ((header.status & SMART_STATUS_VERBITS) != SMART_STATUS_VERSION) + { + continue; + } + + /* Now compare if this logical sector matches the current sector */ + + if (duplogsector == logicalsector) + { + break; + } + } +#endif + ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); + (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { goto err_out; } - seq1 = *((uint16_t *) header.seq); +#if SMART_STATUS_VERSION == 1 + if (header.status & SMART_STATUS_CRC) + { + seq1 = header.seq; + } + else + { + seq1 = *((FAR uint16_t *) &header.seq); + } +#else + seq1 = header.seq; +#endif /* Now determine who wins */ - if (seq1 > 0xFFF0 && seq2 < 10) + if ((seq1 > 0xFFF0 && seq2 < 10) || seq2 > seq1) { - /* Seq 2 is the winner ... we assume it wrapped */ - - loser = dev->sMap[logicalsector]; - dev->sMap[logicalsector] = sector; - } - else if (seq2 > seq1) - { - /* Seq 2 is bigger, so it's the winner */ + /* Seq 2 is the winner ... bigger or it wrapped */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM loser = dev->sMap[logicalsector]; dev->sMap[logicalsector] = sector; +#else + loser = dupsector; +#endif } else { @@ -897,7 +2180,7 @@ static int smart_scan(struct smart_struct_s *dev) readaddress = loser * dev->mtdBlksPerSector * dev->geo.blocksize; ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); + (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { goto err_out; @@ -917,16 +2200,114 @@ static int smart_scan(struct smart_struct_s *dev) } } +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM /* Update the logical to physical sector map */ dev->sMap[logicalsector] = sector; +#else + /* Mark the logical sector as used in the bitmap */ + dev->sBitMap[logicalsector >> 3] |= 1 << (logicalsector & 0x07); + + if (logicalsector < SMART_FIRST_ALLOC_SECTOR) + { + smart_add_sector_to_cache(dev, logicalsector, sector, __LINE__ ); + } +#endif + } + +#if defined (CONFIG_MTD_SMART_WEAR_LEVEL) && (SMART_STATUS_VERSION == 1) +#ifdef CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT + + /* We need to check if we are converting an older format with incorrect + * wear leveling data in sector zero to the new format. The old format + * put all zeros in the wear level bit locations, but the new (better) + * way is to leave them 0xFF. + */ + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + sector = dev->sMap[0]; +#else + sector = smart_cache_lookup(dev, 0); +#endif + + /* Validate the sector is valid ... may be an unformatted device */ + + if (sector != 0xFFFF) + { + /* Read the sector data */ + + ret = MTD_BREAD(dev->mtd, sector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer); + if (ret != dev->mtdBlksPerSector) + { + fdbg("Error reading physical sector %d.\n", sector); + goto err_out; + } + + + /* Check for old format wear leveling */ + if (dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG] == 0) + { + /* Old format detected. We must relocate sector zero and fill it in with 0xFF */ + + uint16_t newsector = smart_findfreephyssector(dev, FALSE); + if (newsector == 0xFFFF) + { + /* Unable to find a free sector!!! */ + + fdbg("Can't find a free sector for relocation\n"); + ret = -ENOSPC; + goto err_out; + } + + memset(&dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG], 0xFF, + dev->mtdBlksPerSector * dev->geo.blocksize - + SMART_WEAR_LEVEL_FORMAT_SIG); + + smart_relocate_sector(dev, sector, newsector); + + /* Update the free and release sector counts */ + + dev->freesectors--; + dev->releasesectors++; + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + dev->sMap[0] = newsector; + dev->freecount[newsector / dev->sectorsPerBlk]--; + dev->releasecount[sector / dev->sectorsPerBlk]++; +#else + smart_update_cache(dev, 0, newsector); + smart_add_count(dev, dev->freecount, newsector / dev->sectorsPerBlk, -1); + smart_add_count(dev, dev->releasecount, sector / dev->sectorsPerBlk, 1); +#endif + + } } +#endif /* CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT */ +#endif /* CONFIG_MTD_SMART_WEAR_LEVEL && SMART_STATUS_VERSION == 1 */ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + /* Read the wear leveling status bits */ + + smart_read_wearstatus(dev); +#endif + fdbg("SMART Scan\n"); fdbg(" Erase size: %10d\n", dev->sectorsPerBlk * dev->sectorsize); fdbg(" Erase count: %10d\n", dev->neraseblocks); fdbg(" Sect/block: %10d\n", dev->sectorsPerBlk); fdbg(" MTD Blk/Sect: %10d\n", dev->mtdBlksPerSector); +#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG + fdbg(" Allocations:\n"); + for (sector = 0; sector < SMART_MAX_ALLOCS; sector++) + { + if (dev->alloc[sector].ptr != NULL) + { + fdbg(" %s: %d\n", dev->alloc[sector].name, dev->alloc[sector].size); + } + } +#endif ret = OK; @@ -943,16 +2324,15 @@ err_out: ****************************************************************************/ #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS -static inline int smart_getformat(struct smart_struct_s *dev, - struct smart_format_s *fmt, +static inline int smart_getformat(FAR struct smart_struct_s *dev, + FAR struct smart_format_s *fmt, uint8_t rootdirnum) #else -static inline int smart_getformat(struct smart_struct_s *dev, - struct smart_format_s *fmt) +static inline int smart_getformat(FAR struct smart_struct_s *dev, + FAR struct smart_format_s *fmt) #endif { int ret; - int x; fvdbg("Entry\n"); DEBUGASSERT(fmt); @@ -987,7 +2367,8 @@ static inline int smart_getformat(struct smart_struct_s *dev, fmt->sectorsize = dev->sectorsize; fmt->availbytes = dev->sectorsize - sizeof(struct smart_sect_header_s); - fmt->nsectors = dev->neraseblocks * dev->sectorsPerBlk; + fmt->nsectors = dev->totalsectors; + fmt->nfreesectors = dev->freesectors; fmt->namesize = dev->namesize; #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS @@ -997,10 +2378,7 @@ static inline int smart_getformat(struct smart_struct_s *dev, /* Add the released sectors to the reported free sector count */ - for (x = 0; x < dev->neraseblocks; x++) - { - fmt->nfreesectors += dev->releasecount[x]; - } + fmt->nfreesectors += dev->releasesectors; /* Subtract the reserved sector count */ @@ -1013,6 +2391,394 @@ err_out: } /**************************************************************************** + * Name: smart_erase_block_if_empty + * + * Description: Tests the specified erase block if it contains all free or + * released sectors and erases it. + * + ****************************************************************************/ + +static void smart_erase_block_if_empty(FAR struct smart_struct_s *dev, + uint16_t block, uint8_t forceerase) +{ + uint16_t freecount, releasecount, prerelease; + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + releasecount = smart_get_count(dev, dev->releasecount, block); + freecount = smart_get_count(dev, dev->freecount, block); +#else + releasecount = dev->releasecount[block]; + freecount = dev->freecount[block]; +#endif + + if ((freecount + releasecount == dev->availSectPerBlk && freecount < 1) || forceerase) + { + /* Erase the block */ + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + dev->unusedsectors += freecount; + dev->blockerases++; +#endif + MTD_ERASE(dev->mtd, block, 1); + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + if (dev->erasecounts) + { + dev->erasecounts[block]++; + } +#endif + + /* If wear leveling enabled, then we must add one to the wear status */ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1); +#endif + + /* If we have a device with 65534 sectors, then disallow the last two + * physical sector if this is the last erase block on the device. + */ + + if (block == dev->geo.neraseblocks - 1 && dev->totalsectors == 65534) + { + prerelease = 2; + } + else + { + prerelease = 0; + } + + dev->freesectors += dev->availSectPerBlk - prerelease - freecount; + dev->releasesectors -= releasecount - prerelease; + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_set_count(dev, dev->releasecount, block, prerelease); + smart_set_count(dev, dev->freecount, block, dev->availSectPerBlk-prerelease); +#else + dev->releasecount[block] = prerelease; + dev->freecount[block] = dev->availSectPerBlk - prerelease; +#endif /* CONFIG_MTD_SMART_PACK_COUNTS */ + + /* Now that we have erased this block and updated the release / free counts, + * if we are in WEAR LEVELING enabled mode, we must check if this erase block's + * wear level has reached the threshold to warrant moving a minimum wear level + * block's data into it (i.e. relocating static data to this block so it will + * be worn less). + */ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + if (!forceerase) + { + smart_relocate_static_data(dev, block); + } +#endif + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...while eraseing block %d\n", block); + } +#endif + } +} + +/**************************************************************************** + * Name: smart_relocate_static_data + * + * Description: Tests if the specified block has reached the wear threshold + * for static data relocation and if it has, relocates a less + * worn block to it. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static int smart_relocate_static_data(FAR struct smart_struct_s *dev, uint16_t block) +{ + uint16_t freecount, x, sector, minblock; + uint16_t nextsector, newsector, mincount; + int ret; + FAR struct smart_sect_header_s *header; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsector; +#endif + + /* Now that we have erased this block and updated the release / free counts, + * if we are in WEAR LEVELING enabled mode, we must check if this erase block's + * wear level has reached the threshold to warrant moving a minimum wear level + * block's data into it (i.e. relocating static data to this block so it will + * be worn less). + */ + + ret = OK; + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...about to relocate static data %d\n", block); + } +#endif + + if (smart_get_wear_level(dev, block) >= SMART_WEAR_FULL_RELOCATE_THRESHOLD) + { + /* Okay, this block is getting too worn. Move a minimum wear level + * block to it in it's entirity. + */ + + /* Scan all erase blocks (or until we find a minimum wear level block + * with no free + released blocks. + */ + + freecount = dev->sectorsPerBlk + 1; + minblock = dev->geo.neraseblocks; + mincount = 0; + for (x = 0; x < dev->geo.neraseblocks; x++) + { + if (smart_get_wear_level(dev, x) == dev->minwearlevel) + { + /* Don't allow the format sector or directory sector to + * be moved into a worn block. First get the format and + * dir sectors. + */ + + mincount++; + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + if (smart_get_count(dev, dev->releasecount, x) + + smart_get_count(dev, dev->freecount, x) < freecount) + { + freecount = smart_get_count(dev, dev->releasecount, x) + + smart_get_count(dev, dev->freecount, x); + minblock = x; + } +#else + if (dev->freecount[x] + dev->releasecount[x] < freecount) + { + freecount = dev->freecount[x] + dev->releasecount[x]; + minblock = x; + } +#endif + + /* Break if freecount reaches zero */ + + if (freecount == 0) + { + /* We found a minimum wear-level block with no free sectors. + * relocate this block to the more highly worn block. + */ + + break; + } + } + } + + /* Okay, now move block 'x' to block 'block' and erase block 'x' */ + + x = minblock; + + /* We are resuing nextsector and newsector variables here simply as + * variables for displaying debug data. I have learned through my + * years of programming that this is a really good way to create + * spaghetti code, but I didn't want to add stack variables just + * for debug data, and I *know* these variables aren't being used + * yet. + */ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + nextsector = smart_get_count(dev, dev->freecount, x); + newsector = smart_get_count(dev, dev->releasecount, x); +#else + nextsector = dev->freecount[x]; + newsector = dev->releasecount[x]; +#endif + fvdbg("Moving block %d, wear %d, free %d, released %d to block %d, wear %d\n", + x, smart_get_wear_level(dev, x), + nextsector, newsector, + block, smart_get_wear_level(dev, block)); + + nextsector = block * dev->sectorsPerBlk; + for (sector = x * dev->sectorsPerBlk; sector < + x * dev->sectorsPerBlk + dev->availSectPerBlk; sector++) + { + /* Read the next sector from this erase block */ + + ret = MTD_BREAD(dev->mtd, sector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + if (ret != dev->mtdBlksPerSector) + { + fdbg("Error reading sector %d\n", sector); + ret = -EIO; + goto errout; + } + + /* Test if the block is in use */ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + + /* Check if there is a temporary alloc for this physical sector */ + + allocsector = dev->allocsector; + while (allocsector) + { + if (allocsector->physical == sector) + { + break; + } + + allocsector = allocsector->next; + } + + /* If we found a temp allocation, just update the mapped physical + * location and move on to the next block ... there is no data to + * move yet. + */ + + if (allocsector) + { + /* Get next sector from 'block' */ + + newsector = nextsector++; + if (newsector == 0xFFFF) + { + /* Unable to find a free sector!!! */ + + fdbg("Can't find a free sector for relocation\n"); + ret = -ENOSPC; + goto errout; + } + + /* Update the temporary allocation's physical sector */ + + allocsector->physical = newsector; + *((FAR uint16_t *) header->logicalsector) = allocsector->logical; + } + else +#endif + { + if (((header->status & SMART_STATUS_COMMITTED) == + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) || + ((header->status & SMART_STATUS_RELEASED) != + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))) + { + /* This sector doesn't have live data (free or released). + * just continue to the next sector and don't move it. + */ + + continue; + } + + /* Find a new sector where it can live, NOT in this erase block */ + + newsector = nextsector++; + + /* Relocate the sector data */ + + if ((ret = smart_relocate_sector(dev, sector, newsector)) < 0) + { + goto errout; + } + } + + dev->freesectors--; + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + dev->sMap[*((FAR uint16_t *) header->logicalsector)] = newsector; +#else + smart_update_cache(dev, *((FAR uint16_t *) header->logicalsector), newsector); +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->freecount, block, -1); +#else + dev->freecount[block]--; +#endif /* CONFIG_MTD_SMART_PACK_COUNTS */ + } + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...about to erase static block %d\n", block); + } +#endif + + /* Now erase the block we just relocated, force erasing it */ + + smart_erase_block_if_empty(dev, x, TRUE); + } + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...done erasing static block %d\n", block); + } +#endif + +errout: + return ret; +} +#endif + +/**************************************************************************** + * Name: smart_calc_sector_crc + * + * Description: Calculate the CRC value for the sector data in the RW buffer + * based on the configured CRC size. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC +static crc_t smart_calc_sector_crc(FAR struct smart_struct_s *dev) +{ + crc_t crc = 0; + +#ifdef CONFIG_SMART_CRC_8 + + /* Calculate CRC on data region of the sector */ + + crc = crc8((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)], + dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s)); + + /* Add logical sector number and seq to the CRC calculation */ + + crc = crc8part((uint8_t *) dev->rwbuffer, 3, crc); + + /* Add status to the CRC calculation */ + + crc = crc8part((uint8_t *) &dev->rwbuffer[offsetof(struct smart_sect_header_s, + status)], 1, crc); + +#elif defined(CONFIG_SMART_CRC_16) + /* Calculate CRC on data region of the sector */ + + crc = crc16((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)], + dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s)); + + /* Add logical sector number to the CRC calculation */ + + crc = crc16part((uint8_t *) dev->rwbuffer, 2, crc); + + /* Add status and seq to the CRC calculation */ + + crc = crc16part((uint8_t *) &dev->rwbuffer[offsetof(struct smart_sect_header_s, + status)], 2, crc); + +#elif defined(CONFIG_SMART_CRC_32) + /* Calculate CRC on data region of the sector */ + + crc = crc32((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)], + dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s)); + + /* Add logical sector number, status and seq to the CRC calculation */ + + crc = crc32part((uint8_t *) dev->rwbuffer, 6, crc); +#else +#error "Unknown CRC size!" +#endif + + return crc; +} +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + +/**************************************************************************** * Name: smart_llformat * * Description: Performs a low-level format of the flash device. This @@ -1022,17 +2788,38 @@ err_out: ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) +static inline int smart_llformat(FAR struct smart_struct_s *dev, unsigned long arg) { - struct smart_sect_header_s *sectorheader; - size_t wrcount; - size_t totalsectors; - int x; - int ret; - uint8_t sectsize; + FAR struct smart_sect_header_s *sectorheader; + size_t wrcount; + int x; + int ret; + uint8_t sectsize, prerelease; fvdbg("Entry\n"); + smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE); + + /* Check for invalid format */ + if (dev->erasesize == 0) + { + if (dev->geo.erasesize == 0) + { + dev->erasesize = 262144; + } + else + { + dev->erasesize = dev->geo.erasesize; + } + + dbg("ERROR: Invalid geometery ... Sectors per erase block must be 256 or less\n"); + dbg(" Erase block size = %d\n", dev->erasesize); + dbg(" Sector size = %d\n", dev->sectorsize); + dbg(" Sectors/erase block = %d\n", dev->erasesize / dev->sectorsize); + + return -EINVAL; + } + /* Erase the MTD device */ ret = MTD_IOCTL(dev->mtd, MTDIOC_BULKERASE, 0); @@ -1041,28 +2828,47 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) return ret; } - /* Now construct a logical sector zero header to write to the device. - * We fill it with zero so when we add sector aging, all the sector - * ages will already be initialized to zero without needing special - * logic to deal with a 0xFF erased-state value. - */ + /* Now construct a logical sector zero header to write to the device. */ - sectorheader = (struct smart_sect_header_s *) dev->rwbuffer; - memset(dev->rwbuffer, 0, dev->sectorsize); - memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, SMARTFS_FMT_AGING_POS); - *((uint16_t *) sectorheader->seq) = 0; + sectorheader = (FAR struct smart_sect_header_s *) dev->rwbuffer; + memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize); +#if SMART_STATUS_VERSION == 1 +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + /* CRC enabled. Using an 8-bit sequence number */ + + sectorheader->seq = 0; +#else + /* CRC not enabled. Using a 16-bit sequence number */ + + *((FAR uint16_t *) §orheader->seq) = 0; +#endif +#else /* SMART_STATUS_VERSION == 1 */ + sectorheader->seq = 0; +#endif /* SMART_STATUS_VERSION == 1 */ + + /* Set the sector size of this sector */ sectsize = (CONFIG_MTD_SMART_SECTOR_SIZE >> 9) << 2; + + /* Set the sector logical sector to zero and setup the header status */ + #if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF ) - *((uint16_t *) sectorheader->logicalsector) = 0; + *((FAR uint16_t *) sectorheader->logicalsector) = 0; sectorheader->status = (uint8_t) ~(SMART_STATUS_COMMITTED | SMART_STATUS_VERBITS | SMART_STATUS_SIZEBITS) | SMART_STATUS_VERSION | sectsize; -#else - *((uint16_t *) sectorheader->logicalsector) = 0xFFFF; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + sectorheader->status &= ~SMART_STATUS_CRC; +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + +#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */ + *((FAR uint16_t *) sectorheader->logicalsector) = 0xFFFF; sectorheader->status = (uint8_t) (SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize); -#endif +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + sectorheader->status |= SMART_STATUS_CRC; +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ +#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */ /* Now add the format signature to the sector */ @@ -1078,10 +2884,18 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) dev->rwbuffer[SMART_FMT_ROOTDIRS_POS] = (uint8_t) arg; +#ifdef CONFIG_SMART_CRC_8 + sectorheader->crc8 = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_16) + *((uint16_t *) sectorheader->crc16) = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_32) + *((uint32_t *) sectorheader->crc32) = smart_calc_sector_crc(dev); +#endif + /* Write the sector to the flash */ wrcount = MTD_BWRITE(dev->mtd, 0, dev->mtdBlksPerSector, - (uint8_t *) dev->rwbuffer); + (FAR uint8_t *) dev->rwbuffer); if (wrcount != dev->mtdBlksPerSector) { /* The block is not empty!! What to do? */ @@ -1102,30 +2916,56 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) } dev->formatstatus = SMART_FMT_STAT_UNKNOWN; - dev->freesectors = dev->neraseblocks * dev->sectorsPerBlk - 1; + dev->freesectors = dev->availSectPerBlk * dev->geo.neraseblocks - 1; + dev->releasesectors = 0; +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + dev->uneven_wearcount = 0; +#endif + + /* Initialize the released and free counts */ + for (x = 0; x < dev->neraseblocks; x++) { - /* Initialize the released and free counts */ + /* Test for a geometry with 65536 sectors. We allow this, though + we never use the last two sectors in this mode. + */ - dev->releasecount[x] = 0; - dev->freecount[x] = dev->sectorsPerBlk; + if (x == dev->neraseblocks && dev->totalsectors == 65534) + { + prerelease = 2; + } + else + { + prerelease = 0; + } +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_set_count(dev, dev->releasecount, x, prerelease); + smart_set_count(dev, dev->freecount, x, dev->availSectPerBlk-prerelease); +#else + dev->releasecount[x] = prerelease; + dev->freecount[x] = dev->availSectPerBlk-prerelease; +#endif } /* Account for the format sector */ +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_set_count(dev, dev->freecount, 0, dev->availSectPerBlk - 1); +#else dev->freecount[0]--; +#endif /* Now initialize the logical to physical sector map */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM dev->sMap[0] = 0; /* Logical sector zero = physical sector 0 */ - - totalsectors = dev->neraseblocks * dev->sectorsPerBlk; - for (x = 1; x < totalsectors; x++) + for (x = 1; x < dev->totalsectors; x++) { /* Mark all other logical sectors as non-existant */ dev->sMap[x] = -1; } +#endif #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS @@ -1143,6 +2983,380 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) #endif /* CONFIG_FS_WRITABLE */ /**************************************************************************** + * Name: smart_relocate_sector + * + * Description: Relocates the specified sector to the new sector location. + * + ****************************************************************************/ + +static int smart_relocate_sector(FAR struct smart_struct_s *dev, + uint16_t oldsector, uint16_t newsector) +{ + int ret; + size_t offset; + FAR struct smart_sect_header_s *header; + uint8_t newstatus; + + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + + /* Increment the sequence number and clear the "commit" flag */ + +#if SMART_STATUS_VERSION == 1 + if (header->status & SMART_STATUS_CRC) + { +#endif + /* Using 8-bit sequence */ + + header->seq++; + if (header->seq == 0xFF) + { + header->seq = 1; + } +#if SMART_STATUS_VERSION == 1 + } + else + { + /* Using 16-bit sequence and no CRC */ + + (*((FAR uint16_t *) &header->seq))++; + if (*((FAR uint16_t *) &header->seq) == 0xFFFF) + { + *((FAR uint16_t *) &header->seq) = 1; + } + } +#endif + + /* When CRC is enabled, we must pre-commit the sector and also + * calculate an updated CRC for the sector prior to writing + * since we changed the sequence number. + */ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + + /* First pre-commit the sector */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC); +#else + header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC; +#endif + + /* Now calculate the new CRC */ + +#ifdef CONFIG_SMART_CRC_8 + header->crc8 = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_16) + *((uint16_t *) header->crc16) = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_32) + *((uint32_t *) header->crc32) = smart_calc_sector_crc(dev); +#endif + + /* Write the data to the new physical sector location */ + + ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + +#else /* CONFIG_MTD_SMART_ENABLE_CRC */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + header->status |= SMART_STATUS_COMMITTED; +#else + header->status &= ~SMART_STATUS_COMMITTED; +#endif + + /* Write the data to the new physical sector location */ + + ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + + /* Commit the sector */ + + offset = newsector * dev->mtdBlksPerSector * dev->geo.blocksize + + offsetof(struct smart_sect_header_s, status); +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + newstatus = header->status & ~SMART_STATUS_COMMITTED; +#else + newstatus = header->status | SMART_STATUS_COMMITTED; +#endif + ret = smart_bytewrite(dev, offset, 1, &newstatus); + if (ret < 0) + { + fdbg("Error %d committing new sector %d\n" -ret, newsector); + goto errout; + } +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + + /* Release the old physical sector */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + newstatus = header->status & ~(SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED); +#else + newstatus = header->status | SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED; +#endif + offset = oldsector * dev->mtdBlksPerSector * dev->geo.blocksize + + offsetof(struct smart_sect_header_s, status); + ret = smart_bytewrite(dev, offset, 1, &newstatus); + if (ret < 0) + { + fdbg("Error %d releasing old sector %d\n" -ret, oldsector); + } + +#ifndef CONFIG_MTD_SMART_ENABLE_CRC +errout: +#endif + + return ret; +} + +/**************************************************************************** + * Name: smart_relocate_block + * + * Description: Relocates the specified MTD erase block by moving any + * active sectors to a different erase block and then erases + * the selected block. + * + ****************************************************************************/ + +static int smart_relocate_block(FAR struct smart_struct_s *dev, uint16_t block) +{ + uint16_t newsector, oldrelease; + int x; + int ret; + FAR struct smart_sect_header_s *header; + uint8_t prerelease; + uint16_t freecount; +#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS) + uint16_t releasecount; +#endif +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsector; +#endif + + /* Perform collection on block with the most released sectors. + * First mark the block as having no free sectors so we don't + * try to move sectors into the block we are trying to erase. + */ + + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...while relocating block %d, free=%d\n", block, dev->freesectors); + } +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + freecount = smart_get_count(dev, dev->freecount, block); + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) +#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS) + releasecount = smart_get_count(dev, dev->releasecount, block); +#endif +#endif + + /* Ensure we aren't relocating a block containing the only free sectors */ + + if (freecount >= dev->freesectors) + { + fdbg("Program bug! Relocating the only block (%d) with free sectors!\n", block); + ret = -EIO; + goto errout; + } + + smart_set_count(dev, dev->freecount, block, 0); + +#else /* CONFIG_MTD_SMART_PACK_COUNTS */ + + freecount = dev->freecount[block]; +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) +#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS) + releasecount = dev->releasecount[block]; +#endif +#endif + dev->freecount[block] = 0; +#endif + + /* Next move all live data in the block to a new home. */ + + for (x = block * dev->sectorsPerBlk; x < + block * dev->sectorsPerBlk + dev->availSectPerBlk; x++) + { + /* Read the next sector from this erase block */ + + ret = MTD_BREAD(dev->mtd, x * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + if (ret != dev->mtdBlksPerSector) + { + fdbg("Error reading sector %d\n", x); + ret = -EIO; + goto errout; + } + + /* Test if the block is in use */ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + + /* Check if there is a temporary alloc for this physical sector */ + + allocsector = dev->allocsector; + while (allocsector) + { + if (allocsector->physical == x) + break; + allocsector = allocsector->next; + } + + /* If we found a temp allocation, just update the mapped physical + * location and move on to the next block ... there is no data to + * move yet. + */ + + if (allocsector) + { + newsector = smart_findfreephyssector(dev, FALSE); + if (newsector == 0xFFFF) + { + /* Unable to find a free sector!!! */ + + fdbg("Can't find a free sector for relocation\n"); + ret = -ENOSPC; + goto errout; + } + + /* Update the temporary allocation's physical sector */ + + allocsector->physical = newsector; + *((FAR uint16_t *) header->logicalsector) = allocsector->logical; + } + else +#endif + { + if (((header->status & SMART_STATUS_COMMITTED) == + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) || + ((header->status & SMART_STATUS_RELEASED) != + (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))) + { + /* This sector doesn't have live data (free or released). + * just continue to the next sector and don't move it. + */ + + continue; + } + + /* Find a new sector where it can live, NOT in this erase block */ + + newsector = smart_findfreephyssector(dev, FALSE); + if (newsector == 0xFFFF) + { + /* Unable to find a free sector!!! */ + + fdbg("Can't find a free sector for relocation\n"); + ret = -ENOSPC; + goto errout; + } + + /* Relocate the sector data */ + + if ((ret = smart_relocate_sector(dev, x, newsector)) < 0) + goto errout; + } + + /* Update the variables */ + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + dev->sMap[*((FAR uint16_t *) header->logicalsector)] = newsector; +#else + smart_update_cache(dev, *((FAR uint16_t *) header->logicalsector), newsector); +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->freecount, newsector / dev->sectorsPerBlk, -1); +#else + dev->freecount[newsector / dev->sectorsPerBlk]--; +#endif + } + + /* Now erase the erase block */ + + MTD_ERASE(dev->mtd, block, 1); +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + dev->unusedsectors += freecount; + dev->blockerases++; +#endif + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + if (dev->erasecounts) + { + dev->erasecounts[block]++; + } +#endif + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + + /* Update the new wear level count */ + + smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1); +#endif + + /* Update the free and release sectors for this erase block. */ + + if (x == dev->neraseblocks && dev->totalsectors == 65534) + { + /* We can't use the last two sectors on a 65536 sector device, + so "pre-release" them so they never get allocated. + */ + + prerelease = 2; + } + else + { + prerelease = 0; + } + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + oldrelease = smart_get_count(dev, dev->releasecount, block); + dev->freesectors += oldrelease-prerelease; + dev->releasesectors -= oldrelease-prerelease; + smart_set_count(dev, dev->freecount, block, dev->availSectPerBlk-prerelease); + smart_set_count(dev, dev->releasecount, block, prerelease); +#else + oldrelease = dev->releasecount[block]; + dev->freesectors += oldrelease-prerelease; + dev->releasesectors -= oldrelease-prerelease; + dev->freecount[block] = dev->availSectPerBlk-prerelease; + dev->releasecount[block] = prerelease; +#endif + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...while relocating block %d, free=%d, release=%d, oldrelease=%d\n", block, freecount, releasecount, oldrelease); + } +#endif + + /* Test if this erase causes the block to reach the full relocate + * threshold requiring static data relocation. + */ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + smart_relocate_static_data(dev, block); +#endif + + return OK; + +errout: + /* Restore the block's freecount if error */ + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_set_count(dev, dev->freecount, block, freecount); +#else + dev->freecount[block] = freecount; +#endif + return ret; +} + +/**************************************************************************** * Name: smart_findfreephyssector * * Description: Finds a free physical sector based on free and released @@ -1150,12 +3364,17 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg) * ****************************************************************************/ -static int smart_findfreephyssector(struct smart_struct_s *dev) +static int smart_findfreephyssector(FAR struct smart_struct_s *dev, + uint8_t canrelocate) { - uint16_t allocfreecount; - uint16_t allocblock; + uint16_t count, allocfreecount, allocblock; +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + uint16_t wornfreecount, wornblock; + uint8_t wearlevel, wornlevel; + uint8_t maxwearlevel; +#endif uint16_t physicalsector; - uint16_t x; + uint16_t x, block; uint32_t readaddr; struct smart_sect_header_s header; int ret; @@ -1164,58 +3383,211 @@ static int smart_findfreephyssector(struct smart_struct_s *dev) * sector from. This is based on the number of free sectors * available in each erase block. */ +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +retry: +#endif allocfreecount = 0; allocblock = 0xFFFF; +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + wornfreecount = 0; + wornblock = 0xFFFF; + wornlevel = 15; + maxwearlevel = 0; +#endif physicalsector = 0xFFFF; + if (++dev->lastallocblock >= dev->neraseblocks) + { + dev->lastallocblock = 0; + } + + block = dev->lastallocblock; for (x = 0; x < dev->neraseblocks; x++) { /* Test if this block has more free blocks than the - * currently selected block */ + * currently selected block + */ - if (dev->freecount[x] > allocfreecount) +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + count = smart_get_count(dev, dev->freecount, block); +#else + count = dev->freecount[block]; +#endif + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + /* Keep track of the block with the max free sectors that is worn */ + + wearlevel = smart_get_wear_level(dev, block); + if (wearlevel >= SMART_WEAR_FULL_RELOCATE_THRESHOLD) + { + if (wearlevel > maxwearlevel && count > 0) + { + maxwearlevel = wearlevel; + } + + if (count > wornfreecount || (count > 0 && wearlevel < wornlevel)) + { + /* Keep track of this block. If there are only worn blocks with + * free sectors left, then we will use it. + */ + + if (x < dev->neraseblocks - 1 || !wornfreecount) + { + wornfreecount = count; + wornblock = block; + wornlevel = wearlevel; + } + } + } + else +#endif + + if (count > allocfreecount) { /* Assign this block to alloc from */ - allocblock = x; - allocfreecount = dev->freecount[x]; + if (x < dev->neraseblocks - 1 || !allocfreecount) + { + allocblock = block; + allocfreecount = count; + } } + if (++block >= dev->neraseblocks) + block = 0; } /* Check if we found an allocblock. */ if (allocblock == 0xFFFF) { - /* No free sectors found! Bug? */ - return -EIO; + /* No un-worn blocks with free sectors */ + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + + /* If we are allowed to relocate unworn blocks then do so now */ + + if (canrelocate && wornfreecount < (dev->sectorsPerBlk >> 2) && wornlevel == maxwearlevel) + { + /* Relocate up to 8 unworn blocks */ + + block = 0; + for (x = 0; x < 8; ) + { + if (smart_get_wear_level(dev, block) < SMART_WEAR_FORCE_REORG_THRESHOLD) + { + if (smart_relocate_block(dev, block) < 0) + { + fdbg("Error relocating block while finding free phys sector\n"); + return -1; + } + + x++; + } + + block++; + } + if (x > 0) + { + /* Disable relocate for retry */ + + canrelocate = FALSE; + goto retry; + } + } + else + { + dev->wearflags |= SMART_WEARFLAGS_FORCE_REORG; + } + + /* Test if we found a worn block with free sectors */ + + if (wornblock != 0xFFFF) + { + allocblock = wornblock; + } + else +#endif + + { + dbg("Program bug! Expected a free sector, free=%d\n", dev->freesectors); + for (x = 0; x < dev->neraseblocks; x++) + { + printf("%d ", dev->freecount[x]); + } + + /* No free sectors found! Bug? */ + + return -1; + } } /* Now find a free physical sector within this selected * erase block to allocate. */ for (x = allocblock * dev->sectorsPerBlk; - x < (allocblock+1) * dev->sectorsPerBlk; x++) + x < allocblock * dev->sectorsPerBlk + dev->availSectPerBlk; x++) { - /* Check if this physical sector is available */ + /* Check if this physical sector is available. */ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + /* First check if there is a temporary alloc in place */ + + FAR struct smart_allocsector_s* allocsect; + allocsect = dev->allocsector; + + while (allocsect) + { + if (allocsect->physical == x) + break; + allocsect = allocsect->next; + } + + /* If we found this physical sector above, then continue on + * to the next physical sector in this block ... this one has + * a temporary allocation assigned. + */ + + if (allocsect) + { + continue; + } +#endif + + /* Now check on the physical media */ readaddr = x * dev->mtdBlksPerSector * dev->geo.blocksize; ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); + (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { - fvdbg("Error reading phys sector %d\n", physicalsector); - return -EIO; + fdbg("Error reading phys sector %d\n", physicalsector); + return -1; } - if ((*((uint16_t *) header.logicalsector) == 0xFFFF) && - (*((uint16_t *) header.seq) == 0xFFFF) && + if ((*((FAR uint16_t *) header.logicalsector) == 0xFFFF) && +#if SMART_STATUS_VERSION == 1 + (*((FAR uint16_t *) &header.seq) == 0xFFFF) && +#else + (header.seq == CONFIG_SMARTFS_ERASEDSTATE) && +#endif ((header.status & SMART_STATUS_COMMITTED) == (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))) { physicalsector = x; + dev->lastallocblock = allocblock; break; } } + if (physicalsector == 0xFFFF) + { + dbg("Program bug! Expected a free sector\n"); + } + + if (physicalsector >= dev->totalsectors) + { + dbg("Program bug! Selected sector too big!!!\n"); + } + return physicalsector; } @@ -1229,54 +3601,74 @@ static int smart_findfreephyssector(struct smart_struct_s *dev) ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static int smart_garbagecollect(struct smart_struct_s *dev) +static int smart_garbagecollect(FAR struct smart_struct_s *dev) { - uint16_t releasedsectors; uint16_t collectblock; uint16_t releasemax; - uint16_t newsector; bool collect = TRUE; int x; int ret; - size_t offset; - struct smart_sect_header_s *header; - uint8_t newstatus; +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + uint8_t count; +#endif while (collect) { collect = FALSE; - /* Calculate the number of released sectors on the device */ - - releasedsectors = 0; - collectblock = 0xFFFF; - releasemax = 0; - for (x = 0; x < dev->neraseblocks; x++) - { - releasedsectors += dev->releasecount[x]; - if (dev->releasecount[x] > releasemax) - { - releasemax = dev->releasecount[x]; - collectblock = x; - } - } - /* Test if the released sectors count is greater than the * free sectors. If it is, then we will do garbage collection. */ - if (releasedsectors > dev->freesectors) - collect = TRUE; + if (dev->releasesectors > dev->freesectors && dev->freesectors < + (dev->totalsectors >> 5)) + { + collect = TRUE; + } /* Test if we have more reached our reserved free sector limit */ if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4) - collect = TRUE; + { + collect = TRUE; + } /* Test if we need to garbage collect */ if (collect) { + /* Find the block with the most released sectors */ + + collectblock = 0xFFFF; + releasemax = 0; + for (x = 0; x < dev->neraseblocks; x++) + { +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + /* Don't collect blocks that have been worn completely */ + + if (smart_get_wear_level(dev, x) >= SMART_WEAR_REORG_THRESHOLD) + { + continue; + } +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + count = smart_get_count(dev, dev->releasecount, x); + if (count > releasemax) + { + releasemax = count; + collectblock = x; + } +#else + if (dev->releasecount[x] > releasemax) + { + releasemax = dev->releasecount[x]; + collectblock = x; + } +#endif + } + //releasemax = smart_get_count(dev, dev->releasecount, collectblock); + if (collectblock == 0xFFFF) { /* Need to collect, but no sectors with released blocks! */ @@ -1285,167 +3677,402 @@ static int smart_garbagecollect(struct smart_struct_s *dev) goto errout; } - fdbg("Collecting block %d, free=%d released=%d\n", +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) + { + fdbg(" ...before collecting block %d\n", collectblock); + } +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + fvdbg("Collecting block %d, free=%d released=%d, totalfree=%d, totalrelease=%d\n", + collectblock, smart_get_count(dev, dev->freecount, collectblock), + smart_get_count(dev, dev->releasecount, collectblock), dev->freesectors, dev->releasesectors); +#else + fvdbg("Collecting block %d, free=%d released=%d\n", collectblock, dev->freecount[collectblock], dev->releasecount[collectblock]); +#endif - if (dev->freecount[collectblock] == 6) + /* Relocate the active data in the collection block */ + + ret = smart_relocate_block(dev, collectblock); + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + if (smart_checkfree(dev, __LINE__) != OK) { - fdbg("here!\n"); + fdbg(" ...while collecting block %d\n", collectblock); } +#endif - /* Perform collection on block with the most released sectors. - * First mark the block as having no free sectors so we don't - * try to move sectors into the block we are trying to erase. - */ + if (ret != OK) + { + goto errout; + } + } + } - dev->freecount[collectblock] = 0; + return OK; - /* Next move all live data in the block to a new home. */ +errout: + return ret; +} +#endif /* CONFIG_FS_WRITABLE */ - for (x = collectblock * dev->sectorsPerBlk; x < - (collectblock + 1) * dev->sectorsPerBlk; x++) - { - /* Read the next sector from this erase block */ +/**************************************************************************** + * Name: smart_write_wearstatus + * + * Description: Writes the wear leveling status bits to sector zero (and + * possibly others if it doesn't fit) such that is is persisted + * across OS reboots. + * + ****************************************************************************/ - ret = MTD_BREAD(dev->mtd, x * dev->mtdBlksPerSector, - dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer); - if (ret != dev->mtdBlksPerSector) - { - fdbg("Error reading sector %d\n", x); - ret = -EIO; - goto errout; - } +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static int smart_write_wearstatus(struct smart_struct_s *dev) +{ + uint16_t sector; + uint16_t remaining, towrite; + struct smart_read_write_s req; + int ret; + uint8_t buffer[8], write_buffer = 0; - /* Test if if the block is in use */ + sector = 0; + remaining = dev->geo.neraseblocks >> 1; + memset(buffer, 0xFF, sizeof(buffer)); - header = (struct smart_sect_header_s *) dev->rwbuffer; - if (((header->status & SMART_STATUS_COMMITTED) == - (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) || - ((header->status & SMART_STATUS_RELEASED) != - (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))) - { - /* This sector doesn't have live data (free or released). - * just continue to the next sector and don't move it. - */ +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + if (dev->blockerases > 0) + { + *((uint32_t *) buffer) = dev->blockerases; + write_buffer = 1; + } +#endif - continue; - } + /* Write the uneven wear count just prior to the wear bits */ - /* Find a new sector where it can live, NOT in this erase block */ + if (dev->uneven_wearcount != 0) + { + *((uint32_t *) &buffer[4]) = dev->uneven_wearcount; + write_buffer = 1; + } - newsector = smart_findfreephyssector(dev); - if (newsector == 0xFFFF) - { - /* Unable to find a free sector!!! */ + /* Test if we need to write either total block erase count or + uneven wearcount (or both) + */ - fdbg("Can't find a free sector for relocation\n"); - ret = -EIO; - goto errout; - } + if (write_buffer) + { + req.logsector = sector; + req.offset = SMARTFS_FMT_WEAR_POS - 8; + req.count = sizeof(buffer); + req.buffer = buffer; + ret = smart_writesector(dev, (unsigned long) &req); + if (ret != OK) + { + goto errout; + } + } - /* Increment the sequence number and clear the "commit" flag */ + /* Write all wear level bits to logical sector zero, one, two */ - (*((uint16_t *) header->seq))++; - if (*((uint16_t *) header->seq) == 0xFFFF) - { - *((uint16_t *) header->seq) = 1; - } -#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF - header->status |= SMART_STATUS_COMMITTED; -#else - header->status &= ~SMART_STATUS_COMMITTED; -#endif + while (remaining) + { + /* Calculate the number of bytes to write to this sector */ - /* Write the data to the new physical sector location */ + towrite = remaining; + if (towrite > dev->sectorsize - SMARTFS_FMT_WEAR_POS) + { + towrite = dev->sectorsize - SMARTFS_FMT_WEAR_POS; + } - (void)MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector, - dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer); + /* Setup the sector write request (we are our own client) */ - /* Commit the sector */ + req.logsector = sector; + req.offset = SMARTFS_FMT_WEAR_POS; + req.count = towrite; + req.buffer = &dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) - + remaining]; - offset = newsector * dev->mtdBlksPerSector * dev->geo.blocksize + - offsetof(struct smart_sect_header_s, status); -#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF - newstatus = header->status & ~SMART_STATUS_COMMITTED; -#else - newstatus = header->status | SMART_STATUS_COMMITTED; -#endif - ret = smart_bytewrite(dev, offset, 1, &newstatus); - if (ret < 0) - { - fdbg("Error %d committing new sector %d\n" -ret, newsector); - goto errout; - } + /* Write the sector */ - /* Release the old physical sector */ + ret = smart_writesector(dev, (unsigned long) &req); + if (ret != OK) + { + goto errout; + } -#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF - newstatus = header->status & ~SMART_STATUS_RELEASED; -#else - newstatus = header->status | SMART_STATUS_RELEASED; -#endif - offset = x * dev->mtdBlksPerSector * dev->geo.blocksize + - offsetof(struct smart_sect_header_s, status); - ret = smart_bytewrite(dev, offset, 1, &newstatus); - if (ret < 0) - { - fdbg("Error %d releasing old sector %d\n" -ret, x); - goto errout; - } + /* Decrement the remaining count */ - /* Update the variables */ + remaining -= towrite; + if (remaining) + { + /* Data doesn't fit in a single sector. Use the reserved sectors */ - dev->sMap[*((uint16_t *) header->logicalsector)] = newsector; - dev->freecount[newsector / dev->sectorsPerBlk]--; + sector++; + if (sector >= SMART_FIRST_DIR_SECTOR) + { + /* Error, wear status bit too large! */ + fdbg("Invalid geometry - wear level status too large\n"); + ret = -EINVAL; + goto errout; } + } + } - /* Now erase the erase block */ + /* Now clear the NEEDS_WRITE wear status bit */ - MTD_ERASE(dev->mtd, collectblock, 1); + dev->wearflags &= ~SMART_WEARFLAGS_WRITE_NEEDED; + ret = OK; - dev->freesectors += dev->releasecount[collectblock]; - dev->freecount[collectblock] = dev->sectorsPerBlk; - dev->releasecount[collectblock] = 0; +errout: + return ret; +} +#endif - /* If this is block zero, then be sure to write the sector size */ +/**************************************************************************** + * Name: smart_read_wearstatus + * + * Description: Reads the wear leveling status bits from sector zero (and + * possibly others if it doesn't fit) such that is is persisted + * across OS reboots. + * + ****************************************************************************/ - if (collectblock == 0) - { - /* Set the sector size in the 1st header */ +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL +static inline int smart_read_wearstatus(FAR struct smart_struct_s *dev) +{ + uint16_t sector; + uint16_t remaining, toread; + struct smart_read_write_s req; + int ret; + uint8_t buffer[8]; + + /* Prepare to read the total block erases and uneven wearcount values */ + + sector = 0; + req.logsector = sector; + req.offset = SMARTFS_FMT_WEAR_POS - 8; + req.count = sizeof(buffer); + req.buffer = buffer; + ret = smart_readsector(dev, (unsigned long) &req); + if (ret != sizeof(buffer)) + { + goto errout; + } + + /* Get the uneven wearcount value */ + + dev->uneven_wearcount = *((uint32_t *) &buffer[4]); + + /* Check for erased state */ - uint8_t sectsize = dev->sectorsize >> 7; #if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF ) - newstatus = (uint8_t) ~SMART_STATUS_SIZEBITS | sectsize; -#else - newstatus = (uint8_t) sectsize; + if (dev->uneven_wearcount == 0xFFFFFFFF) + { + dev->uneven_wearcount = 0; + } #endif - /* Write the sector size to the device */ - offset = offsetof(struct smart_sect_header_s, status); - ret = smart_bytewrite(dev, offset, 1, &newstatus); - if (ret < 0) - { - fdbg("Error %d setting sector 0 size\n", -ret); - } - } +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + /* Get the block erases count */ + + dev->blockerases = *((uint32_t *) buffer); +#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF ) + if (dev->blockerases == 0xFFFFFFFF) + { + dev->blockerases = 0; + } +#endif +#endif + + /* Read all wear level bits from the flash */ + + remaining = dev->geo.neraseblocks >> 1; + while (remaining) + { + /* Calculate number of bytes to read from this sector */ - /* Update the block aging information in the format signature sector */ + toread = remaining; + if (toread > dev->sectorsize - SMARTFS_FMT_WEAR_POS) + { + toread = dev->sectorsize - SMARTFS_FMT_WEAR_POS; } - else + + /* Setup the sector read request (we are our own client) */ + + req.logsector = sector; + req.offset = SMARTFS_FMT_WEAR_POS; + req.count = toread; + req.buffer = &dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) - + remaining]; + + /* Read the sector */ + + ret = smart_readsector(dev, (unsigned long) &req); + if (ret != toread) { - /* Test for aging sectors and push them to a new location - * so we wear evenly. - */ + goto errout; + } + + /* Decrement the remaining count */ + + remaining -= toread; + if (remaining) + { + /* Data doesn't fit in a single sector. Use the reserved sectors */ + + sector++; + if (sector >= SMART_FIRST_DIR_SECTOR) + { + /* Error, wear status bit too large! */ + + fdbg("Invalid geometry - wear level status too large\n"); + ret = -EINVAL; + goto errout; + } } } - return OK; + /* Now interrogate the status bits */ + + smart_find_wear_minmax(dev); + +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + /* Set the erase counts equal to the wear levels */ + + for (sector = 0; sector < dev->geo.neraseblocks; sector++) + { + dev->erasecounts[sector] = smart_get_wear_level(dev, sector); + } +#endif + + ret = OK; errout: return ret; } -#endif /* CONFIG_FS_WRITABLE */ +#endif + +/**************************************************************************** + * Name: smart_write_alloc_sector + * + * Description: Writes a newly allocated sector's header to the RW buffer + * and updates sector mapping variables. If CRC isn't enabled + * it also writes the header to the device. + * + ****************************************************************************/ + +#ifdef CONFIG_FS_WRITABLE +static int smart_write_alloc_sector(FAR struct smart_struct_s *dev, + uint16_t logical, uint16_t physical) +{ + int ret = 1; + uint8_t sectsize; + FAR struct smart_sect_header_s *header; + + memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize); + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + *((FAR uint16_t *) header->logicalsector) = logical; +#if SMART_STATUS_VERSION == 1 +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + header->seq = 0; +#else + *((FAR uint16_t *) &header->crc8) = 0; +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ +#else + header->seq = 0; +#endif + sectsize = dev->sectorsize >> 7; + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + header->status = ~(SMART_STATUS_COMMITTED | SMART_STATUS_SIZEBITS | + SMART_STATUS_VERBITS) | SMART_STATUS_VERSION | sectsize; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + header->status &= ~SMART_STATUS_CRC; +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ +#else + header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + header->status |= SMART_STATUS_CRC; +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ +#endif + + /* Write the header to the physical sector location */ + +#ifndef CONFIG_MTD_SMART_ENABLE_CRC + fvdbg("Write MTD block %d\n", physical * dev->mtdBlksPerSector); + ret = MTD_BWRITE(dev->mtd, physical * dev->mtdBlksPerSector, 1, + (FAR uint8_t *) dev->rwbuffer); + if (ret != 1) + { + /* The block is not empty!! What to do? */ + + fdbg("Write block %d failed: %d.\n", physical * + dev->mtdBlksPerSector, ret); + + /* Unlock the mutex if we add one */ + + return -EIO; + } +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + + return ret; +} +#endif + +/**************************************************************************** + * Name: smart_validate_crc + * + * Description: Validates the CRC data in the sector's header against the + * data in the sector. Assumes the entire sector has been + * read into the RW buffer already. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_SMART_ENABLE_CRC +static int smart_validate_crc(FAR struct smart_struct_s *dev) +{ + crc_t crc; + FAR struct smart_sect_header_s *header; + + /* Calculate CRC on data region of the sector */ + + crc = smart_calc_sector_crc(dev); + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + +#ifdef CONFIG_SMART_CRC_8 + + /* Test 8-bit CRC */ + + if (crc != header->crc8) + { + return -EIO; + } + +#elif defined(CONFIG_SMART_CRC_16) + + /* Test 16-bit CRC */ + + if (crc != *((uint16_t *) header->crc16)) + { + return -EIO; + } + +#elif defined(CONFIG_SMART_CRC_32) + + if (crc != *((uint32_t *) header->crc32)) + { + return -EIO; + } + +#endif + + /* CRC checkout out okay */ + + return OK; +} +#endif /**************************************************************************** * Name: smart_writesector @@ -1459,20 +4086,26 @@ errout: ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static inline int smart_writesector(struct smart_struct_s *dev, unsigned long arg) +static int smart_writesector(FAR struct smart_struct_s *dev, + unsigned long arg) { - int ret; - uint16_t x; - bool needsrelocate = FALSE; - uint32_t mtdblock; - uint16_t physsector; - struct smart_read_write_s *req; - struct smart_sect_header_s *header; - size_t offset; - uint8_t byte; + int ret; + bool needsrelocate = FALSE; + uint32_t mtdblock; + uint16_t physsector, oldphyssector, block; + FAR struct smart_read_write_s *req; + FAR struct smart_sect_header_s *header; + size_t offset; + uint8_t byte; +#if defined(CONFIG_MTD_SMART_WEAR_LEVEL) || !defined(CONFIG_MTD_SMART_ENABLE_CRC) + uint16_t x; +#endif +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsector; +#endif fvdbg("Entry\n"); - req = (struct smart_read_write_s *) arg; + req = (FAR struct smart_read_write_s *) arg; DEBUGASSERT(req->offset <= dev->sectorsize); DEBUGASSERT(req->offset+req->count <= dev->sectorsize); @@ -1485,8 +4118,38 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar ret = -EINVAL; goto errout; } + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + /* Test if an adjustement to the wear levels is needed */ + + if (dev->minwearlevel >= SMART_WEAR_MIN_LEVEL || + (dev->minwearlevel > 0 && dev->maxwearlevel >= SMART_WEAR_REORG_THRESHOLD)) + { + /* Subtract dev->minwearlevel from all wear levels */ + + offset = dev->minwearlevel; + fvdbg("Reducing wear level bits by %d\n", offset); + for (x = 0; x < dev->geo.neraseblocks; x++) + { + smart_set_wear_level(dev, x, smart_get_wear_level(dev, x) - offset); + } + + dev->minwearlevel -= offset; + dev->maxwearlevel -= offset; + + /* Now write the new wear bits to the flash */ + dev->wearflags &= ~SMART_WEARFLAGS_FORCE_REORG; + dev->wearflags |= SMART_WEARFLAGS_WRITE_NEEDED; + } +#endif + +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM physsector = dev->sMap[req->logsector]; +#else + physsector = smart_cache_lookup(dev, req->logsector); +#endif if (physsector == 0xFFFF) { fdbg("Logical sector %d not allocated\n", req->logsector); @@ -1497,7 +4160,7 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Read the sector data into our buffer */ mtdblock = physsector * dev->mtdBlksPerSector; - ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdBlksPerSector, (uint8_t *) + ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); if (ret != dev->mtdBlksPerSector) { @@ -1508,6 +4171,36 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Test if we need to relocate the sector to perform the write */ +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + allocsector = dev->allocsector; + while (allocsector) + { + /* Test if the requested logical sector is a temp alloc */ + + if (allocsector->logical == req->logsector) + { + break; + } + + allocsector = allocsector->next; + } + + /* When CRC is enabled, then we always have to relocate the sector if + * it is not a temporary alloc (i.e. initial alloc before the very first + * write operation). + */ + + if (!allocsector) + { + needsrelocate = TRUE; + } + +#else + /* When CRC is not enabled, we may be able to simply add the new data to + * the sector if it doesn't conflict with existing data on the device. + * Test if there is a conflict in the data. + */ + for (x = 0; x < req->count; x++) { /* Test if the next byte can be written to the flash */ @@ -1527,12 +4220,20 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar } #endif } +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + + /* If we are not using CRC and on a device that supports re-writing + bits from 1 to 0 without neededing a block erase, such as NOR + FLASH, then we can simply update the data in place and don't need + to relocate the sector. Test if we need to relocate or not. + */ if (needsrelocate) { /* Find a new physical sector to save data to */ - physsector = smart_findfreephyssector(dev); + oldphyssector = physsector; + physsector = smart_findfreephyssector(dev, FALSE); if (physsector == 0xFFFF) { fdbg("Error relocating sector %d\n", req->logsector); @@ -1542,8 +4243,26 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Update the sequence number to indicate the sector was moved */ - header = (struct smart_sect_header_s *) dev->rwbuffer; - (*((uint16_t *) header->seq))++; +#if SMART_STATUS_VERSION == 1 + if (header->status & SMART_STATUS_CRC) + { +#endif + header->seq++; + if (header->seq == 0xFF) + { + header->seq = 0; + } +#if SMART_STATUS_VERSION == 1 + } + else + { + (*((FAR uint16_t *) &header->seq))++; + if (*((FAR uint16_t *) &header->seq) == 0xFFFF) + *((FAR uint16_t *) &header->seq) = 1; + } +#else + header->seq++; +#endif #if CONFIG_SMARTFS_ERASEDSTATE == 0xFF header->status |= SMART_STATUS_COMMITTED; #else @@ -1551,11 +4270,82 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar #endif } +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + /* When CRC is enabled and we have a temp alloc, then fill in the RW buffer + * with the header information prior to copying the write data to the buf. + */ + + if (allocsector) + { + smart_write_alloc_sector(dev, allocsector->logical, allocsector->physical); + + /* Remove allocsector from the list and free the memory */ + + if (dev->allocsector == allocsector) + { + /* We are the head item. Remove ourselves as head */ + + dev->allocsector = allocsector->next; + } + else + { + FAR struct smart_allocsector_s *prev; + + /* Start at head and find our entry */ + + prev = dev->allocsector; + while (prev && prev->next != allocsector) + { + /* Scan the list until we find this entry */ + + prev = prev->next; + } + + if (prev) + { + /* Remove from the list */ + + prev->next = allocsector->next; + } + } + + /* Now free the memory */ + + kmm_free(allocsector); + } + /* Now copy the data to the sector buffer. */ memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset], req->buffer, req->count); + /* Commit the sector ahead of time. The CRC will protect us */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC); +#else + header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC; +#endif + + /* Now calculate the CRC value for the sector */ + +#ifdef CONFIG_SMART_CRC_8 + header->crc8 = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_16) + *((uint16_t *) header->crc16) = smart_calc_sector_crc(dev); +#elif defined(CONFIG_SMART_CRC_32) + *((uint32_t *) header->crc32) = smart_calc_sector_crc(dev); +#endif + +#else /* CONFIG_MTD_SMART_ENABLE_CRC */ + + /* Now copy the data to the sector buffer. */ + + memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset], + req->buffer, req->count); + +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ + /* Now write the sector buffer to the device. */ if (needsrelocate) @@ -1563,7 +4353,7 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Write the entire sector to the new physical location, uncommitted. */ ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdBlksPerSector, - dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer); + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); if (ret != dev->mtdBlksPerSector) { fdbg("Error writing to physical sector %d\n", physsector); @@ -1573,6 +4363,8 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Commit the new physical sector */ +#ifndef CONFIG_MTD_SMART_ENABLE_CRC + #if CONFIG_SMARTFS_ERASEDSTATE == 0xFF byte = header->status & ~SMART_STATUS_COMMITTED; #else @@ -1587,13 +4379,14 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar ret = -EIO; goto errout; } +#endif /* Release the old physical sector */ #if CONFIG_SMARTFS_ERASEDSTATE == 0xFF - byte = header->status & ~SMART_STATUS_RELEASED; + byte = header->status & ~(SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED); #else - byte = header->status | SMART_STATUS_RELEASED; + byte = header->status | SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED; #endif offset = mtdblock * dev->geo.blocksize + offsetof(struct smart_sect_header_s, status); @@ -1602,13 +4395,34 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar /* Update releasecount for released sector and freecount for the * newly allocated physical sector. */ - dev->releasecount[dev->sMap[req->logsector] / dev->sectorsPerBlk]++; + block = oldphyssector / dev->sectorsPerBlk; +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->releasecount, block, 1); + smart_add_count(dev, dev->freecount, physsector / dev->sectorsPerBlk, -1); +#else + dev->releasecount[block]++; dev->freecount[physsector / dev->sectorsPerBlk]--; +#endif dev->freesectors--; + dev->releasesectors++; + +#ifdef CONFIG_SMART_LOCAL_CHECKFREE + /* Perform debug free count checking enabled */ + + smart_checkfree(dev, __LINE__); +#endif /* Update the sector map */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM dev->sMap[req->logsector] = physsector; +#else + smart_update_cache(dev, req->logsector, physsector); +#endif + + /* Test if releasing the sector created an empty erase block */ + + smart_erase_block_if_empty(dev, block, FALSE); /* Since we performed a relocation, do garbage collection to * ensure we don't fill up our flash with released blocks. @@ -1616,14 +4430,47 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar smart_garbagecollect(dev); } - else + else /* needsrelocate */ { +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + /* Write the entire sector to FLASH when CRC enabled */ + + ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + if (ret != dev->mtdBlksPerSector) + { + fdbg("Error writing to physical sector %d\n", physsector); + ret = -EIO; + goto errout; + } + + /* Read the sector back and validate the CRC. */ + + ret = MTD_BREAD(dev->mtd, physsector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + if (ret == dev->mtdBlksPerSector) + { + /* Validate the CRC of the read-back data */ + + ret = smart_validate_crc(dev); + } + + if (ret != OK) + { + /* TODO: Mark this as a bad block! */ + + fdbg("Error validating physical sector %d\n", physsector); + ret = -EIO; + goto errout; + } +#else /* Not relocated. Just write the portion of the sector that needs * to be written. */ offset = mtdblock * dev->geo.blocksize + sizeof(struct smart_sect_header_s) + req->offset; ret = smart_bytewrite(dev, offset, req->count, req->buffer); +#endif } ret = OK; @@ -1641,16 +4488,23 @@ errout: * ****************************************************************************/ -static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg) +static int smart_readsector(FAR struct smart_struct_s *dev, + unsigned long arg) { int ret; - uint32_t readaddr; uint16_t physsector; - struct smart_read_write_s *req; + FAR struct smart_read_write_s *req; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC +#if SMART_STATUS_VERSION == 1 + FAR struct smart_sect_header_s *header; +#endif +#else + uint32_t readaddr; struct smart_sect_header_s header; +#endif fvdbg("Entry\n"); - req = (struct smart_read_write_s *) arg; + req = (FAR struct smart_read_write_s *) arg; DEBUGASSERT(req->offset < dev->sectorsize); DEBUGASSERT(req->offset+req->count < dev->sectorsize); @@ -1664,7 +4518,11 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg goto errout; } +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM physsector = dev->sMap[req->logsector]; +#else + physsector = smart_cache_lookup(dev, req->logsector); +#endif if (physsector == 0xFFFF) { fdbg("Logical sector %d not allocated\n", req->logsector); @@ -1672,10 +4530,62 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg goto errout; } +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + + /* When CRC is enabled, we read the entire sector into RAM so we can + * validate the CRC. + */ + + ret = MTD_BREAD(dev->mtd, physsector * dev->mtdBlksPerSector, + dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer); + if (ret != dev->mtdBlksPerSector) + { + /* TODO: Mark the block bad */ + + fdbg("Error reading phys sector %d\n", physsector); + ret = -EIO; + goto errout; + } + +#if SMART_STATUS_VERSION == 1 + /* Test if this sector has CRC enabled or not */ + + header = (FAR struct smart_sect_header_s *) dev->rwbuffer; + if ((header->status & SMART_STATUS_CRC) == (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC)) + { + /* Format VERSION 1 supports either no CRC or 8-bit CRC. Looks like + * CRC not enabled for this sector, so skip the CRC test. + */ + + } + else +#endif + { + /* Validate the read CRC against the calculated sector CRC */ + + ret = smart_validate_crc(dev); + if (ret != OK) + { + /* TODO: Mark the block bad */ + + fdbg("Error validating sector %d CRC during read\n", physsector); + ret = -EIO; + goto errout; + } + } + + /* Copy data to the output buffer */ + + memmove((FAR char *) req->buffer, &dev->rwbuffer[req->offset + + sizeof(struct smart_sect_header_s)], req->count); + ret = req->count; + +#else /* CONFIG_MTD_SMART_ENABLE_CRC */ + /* Read the sector header data to validate as a sanity check */ ret = MTD_READ(dev->mtd, physsector * dev->mtdBlksPerSector * dev->geo.blocksize, - sizeof(struct smart_sect_header_s), (uint8_t *) &header); + sizeof(struct smart_sect_header_s), (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { fvdbg("Error reading sector %d header\n", physsector); @@ -1685,7 +4595,7 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg /* Do a sanity check on the header data */ - if (((*(uint16_t *) header.logicalsector) != req->logsector) || + if (((*(FAR uint16_t *) header.logicalsector) != req->logsector) || ((header.status & SMART_STATUS_COMMITTED) == (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))) { @@ -1702,7 +4612,7 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg readaddr = (uint32_t) physsector * dev->mtdBlksPerSector * dev->geo.blocksize + req->offset + sizeof(struct smart_sect_header_s);; - ret = MTD_READ(dev->mtd, readaddr, req->count, (uint8_t *) + ret = MTD_READ(dev->mtd, readaddr, req->count, (FAR uint8_t *) req->buffer); if (ret != req->count) { @@ -1711,6 +4621,8 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg goto errout; } +#endif + errout: return ret; } @@ -1724,32 +4636,43 @@ errout: ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long requested) +static inline int smart_allocsector(FAR struct smart_struct_s *dev, + unsigned long requested) { int x; - int ret; uint16_t logsector = 0xFFFF; /* Logical sector number selected */ uint16_t physicalsector; /* The selected physical sector */ - uint16_t releasecount; - struct smart_sect_header_s *header; - uint8_t sectsize; +#ifndef CONFIG_MTD_SMART_ENABLE_CRC + int ret; +#endif /* Validate that we have enough sectors available to perform an * allocation. We have to ensure we keep enough reserved sectors * on hand to do released sector garbage collection. */ - releasecount = 0; - for (x = 0; x < dev->neraseblocks; x++) - { - releasecount += dev->releasecount[x]; - } - if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4) { - /* We are at our free sector limit. Test if we have - * sectors we can release */ + /* Do a garbage collect and then test freesectors again */ + + if (dev->releasesectors + dev->freesectors > dev->availSectPerBlk + 4) + { + + for (x = 0; x < dev->availSectPerBlk; x++) + { + smart_garbagecollect(dev); + + if (dev->freesectors > dev->availSectPerBlk + 4) + break; + } - if (releasecount == 0) + if (dev->freesectors <= (dev->availSectPerBlk << 0) + 4) + { + /* No space left!! */ + + return -ENOSPC; + } + } + else { /* No space left!! */ @@ -1764,9 +4687,33 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re { /* Validate the sector is not already allocated */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM if (dev->sMap[requested] == (uint16_t) -1) +#else + if (!(dev->sBitMap[requested >> 3] & (1 << (requested & 0x07)))) +#endif { - logsector = requested; +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsect; + + /* Ensure this logical sector doesn't have a temporary alloc */ + allocsect = dev->allocsector; + while (allocsect) + { + if (allocsect->logical == requested) + { + break; + } + + allocsect = allocsect->next; + } + + if (allocsect != NULL) + { + } + else +#endif + logsector = requested; } } @@ -1778,8 +4725,41 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re for (x = SMART_FIRST_ALLOC_SECTOR; x < dev->totalsectors; x++) { +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM if (dev->sMap[x] == (uint16_t) -1) +#else + if (!(dev->sBitMap[x >> 3] & (1 << (x & 0x07)))) +#endif { +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + FAR struct smart_allocsector_s *allocsect; + + /* Ensure this logical sector doesn't have a temporary alloc + * when CRC is enabled. With CRC enabled, when a sector is + * allocated, we don't actually update the FLASH until the + * very end when we have all data so the CRC can be calculated. + * Instead, we keep an in-memory linked list of allocated + * sectors until the write sector occurs. + */ + + allocsect = dev->allocsector; + while (allocsect) + { + if (allocsect->logical == x) + { + break; + } + + allocsect = allocsect->next; + } + + if (allocsect != NULL) + { + /* This logical sector has an in-memory temp alloc */ + + continue; + } +#endif /* Unused logical sector found. Use this one */ logsector = x; @@ -1805,7 +4785,7 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re } /* Check if we need to do garbage collection. We have to - * ensure we keep enough reserved free sectors to per garbage + * ensure we keep enough reserved free sectors to perform garbage * collection as it involves moving sectors from blocks with * released sectors into blocks with free sectors, then * erasing the vacated block. */ @@ -1814,47 +4794,64 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re /* Find a free physical sector */ - physicalsector = smart_findfreephyssector(dev); + physicalsector = smart_findfreephyssector(dev, FALSE); fvdbg("Alloc: log=%d, phys=%d, erase block=%d, free=%d, released=%d\n", logsector, physicalsector, physicalsector / dev->sectorsPerBlk, dev->freesectors, releasecount); - /* Create a header to assign the logical sector */ +#ifdef CONFIG_MTD_SMART_ENABLE_CRC - memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize); - header = (struct smart_sect_header_s *) dev->rwbuffer; - *((uint16_t *) header->logicalsector) = logsector; - *((uint16_t *) header->seq) = 0; - sectsize = dev->sectorsize >> 7; + /* When CRC is enabled, we don't write the header to the device until + * the data is written via writesector. Just add the allocation to + * our temporary allocsector list and we'll pick it up later. + */ -#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF - header->status = ~(SMART_STATUS_COMMITTED | SMART_STATUS_SIZEBITS | - SMART_STATUS_VERBITS) | SMART_STATUS_VERSION | sectsize; -#else - header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize; -#endif + { + FAR struct smart_allocsector_s *allocsect = (FAR struct smart_allocsector_s *) + kmm_malloc(sizeof(struct smart_allocsector_s)); + if (allocsect == NULL) + { + fdbg("Out of memory allocting sector\n"); + return -ENOMEM; + } - /* Write the header to the physical sector location */ + /* Fill in the struct and add to the list. We are protected by the + * smartfs layer's mutex, so no locking required. + */ - x = physicalsector * dev->mtdBlksPerSector; + allocsect->logical = logsector; + allocsect->physical = physicalsector; + allocsect->next = dev->allocsector; + dev->allocsector = allocsect; + } - fvdbg("Write MTD block %d\n", x); - ret = MTD_BWRITE(dev->mtd, x, 1, (uint8_t *) dev->rwbuffer); - if (ret != 1) - { - /* The block is not empty!! What to do? */ +#else /* CONFIG_MTD_SMART_ENABLE_CRC */ - fdbg("Write block %d failed: %d.\n", x, ret); + /* Write the logical sector to the flash. We will fill it in with data later. */ - /* Unlock the mutex if we add one */ + ret = smart_write_alloc_sector(dev, logsector, physicalsector); + if (ret != 1) + { + /* Error writing sector, return error */ - return -EIO; + return ret; } +#endif /* CONFIG_MTD_SMART_ENABLE_CRC */ /* Map the sector and update the free sector counts */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM dev->sMap[logsector] = physicalsector; +#else + dev->sBitMap[logsector >> 3] |= (1 << (logsector & 0x07)); + smart_add_sector_to_cache(dev, logsector, physicalsector, __LINE__ ); +#endif + +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->freecount, physicalsector / dev->sectorsPerBlk, -1); +#else dev->freecount[physicalsector / dev->sectorsPerBlk]--; +#endif dev->freesectors--; /* Return the logical sector number */ @@ -1873,8 +4870,8 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re ****************************************************************************/ #ifdef CONFIG_FS_WRITABLE -static inline int smart_freesector(struct smart_struct_s *dev, unsigned long - logicalsector) +static inline int smart_freesector(FAR struct smart_struct_s *dev, + unsigned long logicalsector) { int ret; int readaddr; @@ -1889,7 +4886,11 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long { /* Validate the sector is actually allocated */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM if (dev->sMap[logicalsector] == (uint16_t) -1) +#else + if (!(dev->sBitMap[logicalsector >> 3] & (1 << (logicalsector & 0x07)))) +#endif { fdbg("Invalid release - sector %d not allocated\n", logicalsector); ret = -EINVAL; @@ -1899,10 +4900,14 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long /* Okay to release the sector. Read the sector header info */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM physsector = dev->sMap[logicalsector]; +#else + physsector = smart_cache_lookup(dev, logicalsector); +#endif readaddr = physsector * dev->mtdBlksPerSector * dev->geo.blocksize; ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s), - (uint8_t *) &header); + (FAR uint8_t *) &header); if (ret != sizeof(struct smart_sect_header_s)) { goto errout; @@ -1910,7 +4915,7 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long /* Do a sanity check on the logical sector number */ - if (*((uint16_t *) header.logicalsector) != (uint16_t) logicalsector) + if (*((FAR uint16_t *) header.logicalsector) != (uint16_t) logicalsector) { /* Hmmm... something is wrong. This should always match! Bug in our code? */ @@ -1933,32 +4938,32 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long ret = smart_bytewrite(dev, offset, 1, &header.status); if (ret != 1) { - fdbg("Error updating physicl sector %d status\n", physsector); + fdbg("Error updating physical sector %d status\n", physsector); goto errout; } /* Update the erase block's release count */ + dev->releasesectors++; block = physsector / dev->sectorsPerBlk; +#ifdef CONFIG_MTD_SMART_PACK_COUNTS + smart_add_count(dev, dev->releasecount, block, 1); +#else dev->releasecount[block]++; +#endif /* Unmap this logical sector */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM dev->sMap[logicalsector] = (uint16_t) -1; +#else + dev->sBitMap[logicalsector >> 3] &= ~(1 << (logicalsector & 0x07)); + smart_update_cache(dev, logicalsector, 0xFFFF); +#endif /* If this block has only released blocks, then erase it */ - if (dev->releasecount[block] + dev->freecount[block] == dev->sectorsPerBlk) - { - /* Erase the block */ - - MTD_ERASE(dev->mtd, block, 1); - - dev->freesectors += dev->releasecount[block]; - dev->releasecount[block] = 0; - dev->freecount[block] = dev->sectorsPerBlk; - } - + smart_erase_block_if_empty(dev, block, FALSE); ret = OK; errout: @@ -1975,18 +4980,20 @@ errout: static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) { - struct smart_struct_s *dev ; + FAR struct smart_struct_s *dev ; int ret; - uint32_t sector; - struct mtd_smart_procfs_data_s * procfs_data; +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + FAR struct mtd_smart_procfs_data_s *procfs_data; + FAR struct mtd_smart_debug_data_s *debug_data; +#endif fvdbg("Entry\n"); DEBUGASSERT(inode && inode->i_private); #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS - dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev; + dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev; #else - dev = (struct smart_struct_s *)inode->i_private; + dev = (FAR struct smart_struct_s *)inode->i_private; #endif /* Process the ioctl's we care about first, pass any we don't respond @@ -2019,10 +5026,10 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) /* Return the format information for the device */ #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS - ret = smart_getformat(dev, (struct smart_format_s *) arg, - ((struct smart_multiroot_device_s*) inode->i_private)->rootdirnum); + ret = smart_getformat(dev, (FAR struct smart_format_s *) arg, + ((FAR struct smart_multiroot_device_s*) inode->i_private)->rootdirnum); #else - ret = smart_getformat(dev, (struct smart_format_s *) arg); + ret = smart_getformat(dev, (FAR struct smart_format_s *) arg); #endif goto ok_out; @@ -2059,6 +5066,16 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) /* Write to the sector */ ret = smart_writesector(dev, arg); + +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + if (dev->wearflags & SMART_WEARFLAGS_WRITE_NEEDED) + { + /* Write new wear status bits to the device */ + + smart_write_wearstatus(dev); + } +#endif + goto ok_out; #endif /* CONFIG_FS_WRITABLE */ @@ -2071,21 +5088,19 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) procfs_data->totalsectors = dev->totalsectors; procfs_data->sectorsize = dev->sectorsize; procfs_data->freesectors = dev->freesectors; - procfs_data->releasesectors = 0; - for (sector = 0; sector < dev->neraseblocks; sector++) - { - procfs_data->releasesectors += dev->releasecount[sector]; - } - + procfs_data->releasesectors = dev->releasesectors; procfs_data->namelen = dev->namesize; procfs_data->formatversion = dev->formatversion; - procfs_data->unusedsectors = 0; - procfs_data->blockerases = 0; + procfs_data->unusedsectors = dev->unusedsectors; + procfs_data->blockerases = dev->blockerases; procfs_data->sectorsperblk = dev->sectorsPerBlk; #ifndef CONFIG_MTD_SMART_MINIMIZE_RAM procfs_data->formatsector = dev->sMap[0]; procfs_data->dirsector = dev->sMap[3]; +#else + procfs_data->formatsector = smart_cache_lookup(dev, 0); + procfs_data->dirsector = smart_cache_lookup(dev, 3); #endif #ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG @@ -2096,9 +5111,28 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) procfs_data->allocs = dev->alloc; procfs_data->alloccount = SMART_MAX_ALLOCS; #endif +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + procfs_data->uneven_wearcount = dev->uneven_wearcount; +#endif ret = OK; goto ok_out; #endif + + case BIOC_DEBUGCMD: +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) + debug_data = (FAR struct mtd_smart_debug_data_s *) arg; + switch (debug_data->debugcmd) + { + case SMART_DEBUG_CMD_SET_DEBUG_LEVEL: + dev->debuglevel = debug_data->debugdata; + dbg("Debug level set to %d\n", dev->debuglevel); + + ret = OK; + goto ok_out; + } +#endif + + break; } /* No other block driver ioctl commands are not recognized by this @@ -2133,13 +5167,13 @@ ok_out: * ****************************************************************************/ -int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) +int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, FAR const char *partname) { - struct smart_struct_s *dev; + FAR struct smart_struct_s *dev; int ret = -ENOMEM; uint32_t totalsectors; #ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS - struct smart_multiroot_device_s *rootdirdev; + FAR struct smart_multiroot_device_s *rootdirdev = NULL; #endif /* Sanity check */ @@ -2153,12 +5187,19 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) /* Allocate a SMART device structure */ - dev = (struct smart_struct_s *)kmm_malloc(sizeof(struct smart_struct_s)); + dev = (FAR struct smart_struct_s *)kmm_malloc(sizeof(struct smart_struct_s)); if (dev) { /* Initialize the SMART device structure */ dev->mtd = mtd; +#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG + dev->bytesalloc = 0; + for (totalsectors = 0; totalsectors < SMART_MAX_ALLOCS; totalsectors++) + { + dev->alloc[totalsectors].ptr = NULL; + } +#endif /* Get the device geometry. (casting to uintptr_t first eliminates * complaints on some architectures where the sizeof long is different @@ -2171,33 +5212,52 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) if (ret < 0) { fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret); - kmm_free(dev); goto errout; } /* Set the sector size to the default for now */ +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM dev->sMap = NULL; +#else + dev->sCache = NULL; + dev->sBitMap = NULL; +#endif dev->rwbuffer = NULL; +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + dev->erasecounts = NULL; +#endif +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + dev->wearstatus = NULL; +#endif +#ifdef CONFIG_MTD_SMART_ENABLE_CRC + dev->allocsector = NULL; +#endif + dev->sectorsize = 0; ret = smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE); if (ret != OK) { - kmm_free(dev); goto errout; } /* Calculate the totalsectors on this device and validate */ totalsectors = dev->neraseblocks * dev->sectorsPerBlk; - if (totalsectors > 65534) + if (totalsectors > 65536) { fdbg("SMART Sector size too small for device\n"); - kmm_free(dev); ret = -EINVAL; goto errout; } + else if (totalsectors == 65536) + { + totalsectors -= 2; + } - dev->freesectors = (uint16_t) totalsectors; + dev->totalsectors = (uint16_t) totalsectors; + dev->freesectors = (uint16_t) dev->availSectPerBlk * dev->geo.neraseblocks; + dev->lastallocblock = 0; + dev->debuglevel = 0; /* Mark the device format status an unknown */ @@ -2232,13 +5292,11 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) * the SMART device structure and the root directory number. */ - rootdirdev = (struct smart_multiroot_device_s*) kmm_malloc(sizeof(*rootdirdev)); + rootdirdev = (FAR struct smart_multiroot_device_s*) smart_malloc(dev, + sizeof(*rootdirdev), "Root Dir"); if (rootdirdev == NULL) { fdbg("register_blockdriver failed: %d\n", -ret); - kmm_free(dev->sMap); - kmm_free(dev->rwbuffer); - kmm_free(dev); ret = -ENOMEM; goto errout; } @@ -2267,9 +5325,6 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) if (ret < 0) { fdbg("register_blockdriver failed: %d\n", -ret); - kmm_free(dev->sMap); - kmm_free(dev->rwbuffer); - kmm_free(dev); goto errout; } @@ -2278,6 +5333,29 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname) smart_scan(dev); } + return OK; + errout: +#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM + smart_free(dev, dev->sMap); +#else + smart_free(dev, dev->sBitMap); + smart_free(dev, dev->sCache); +#endif + smart_free(dev, dev->rwbuffer); +#ifdef CONFIG_MTD_SMART_WEAR_LEVEL + smart_free(dev, dev->wearstatus); +#endif +#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG + smart_free(dev, dev->erasecounts); +#endif +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + if (rootdirdev) + { + smart_free(dev,rootdirdev); + } +#endif + + kmm_free(dev); return ret; } |