Exemplo n.º 1
0
s32_t ICACHE_FLASH_ATTR SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
    u8_t *fd_space, u32_t fd_space_size,
    void *cache, u32_t cache_size,
    spiffs_check_callback check_cb_f) {
  SPIFFS_LOCK(fs);
  memset(fs, 0, sizeof(spiffs));
  memcpy(&fs->cfg, config, sizeof(spiffs_config));
  fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  fs->work = &work[0];
  fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
  memset(fd_space, 0, fd_space_size);
  // align fd_space pointer to pointer size byte boundary, below is safe
  u8_t ptr_size = sizeof(void*);
// #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
  u8_t addr_lsb = (u8_t)(((u32_t)fd_space) & (ptr_size-1));
// #pragma GCC diagnostic pop
  if (addr_lsb) {
    fd_space += (ptr_size-addr_lsb);
    fd_space_size -= (ptr_size-addr_lsb);
  }
  fs->fd_space = fd_space;
  fs->fd_count = (fd_space_size/sizeof(spiffs_fd));

  // align cache pointer to 4 byte boundary, below is safe
// #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
  addr_lsb = (u8_t)(((u32_t)cache) & (ptr_size-1));
// #pragma GCC diagnostic pop
  if (addr_lsb) {
    cache = (u8_t *)cache + (ptr_size-addr_lsb);
    cache_size -= (ptr_size-addr_lsb);
  }
  if (cache_size & (ptr_size-1)) {
    cache_size -= (cache_size & (ptr_size-1));
  }
#if SPIFFS_CACHE
  fs->cache = cache;
  fs->cache_size = cache_size;
  spiffs_cache_init(fs);
#endif

  s32_t res = spiffs_obj_lu_scan(fs);
  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);

  SPIFFS_DBG("page index byte len:         %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
  SPIFFS_DBG("object lookup pages:         %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
  SPIFFS_DBG("page pages per block:        %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
  SPIFFS_DBG("page header length:          %i\n", sizeof(spiffs_page_header));
  SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
  SPIFFS_DBG("object index entries:        %i\n", SPIFFS_OBJ_IX_LEN(fs));
  SPIFFS_DBG("available file descriptors:  %i\n", fs->fd_count);
  SPIFFS_DBG("free blocks:                 %i\n", fs->free_blocks);

  fs->check_cb_f = check_cb_f;

  SPIFFS_UNLOCK(fs);

  return 0;
}
Exemplo n.º 2
0
// frees cached page
static s32_t ICACHE_FLASH_ATTR
spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
  s32_t res = SPIFFS_OK;
  spiffs_cache *cache = spiffs_get_cache(fs);
  spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
  if (cache->cpage_use_map & (1<<ix)) {
    if (write_back &&
        (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
        (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
      u8_t *mem =  spiffs_get_cache_page(fs, cache, ix);
      res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
    }

    cp->flags = 0;
    cache->cpage_use_map &= ~(1 << ix);

    if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %d objid %04x\n", ix, cp->obj_id);
    } else {
      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %d pix %04x\n", ix, cp->pix);
    }
  }

  return res;
}
Exemplo n.º 3
0
// Updates page statistics for a block that is about to be erased
s32_t spiffs_gc_erase_page_stats(
    spiffs *fs,
    spiffs_block_ix bix) {
  s32_t res = SPIFFS_OK;
  int obj_lookup_page = 0;
  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  int cur_entry = 0;
  u32_t dele = 0;
  u32_t allo = 0;

  // check each object lookup page
  while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
    int entry_offset = obj_lookup_page * entries_per_page;
    res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
        0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
    // check each entry
    while (res == SPIFFS_OK &&
        cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
      spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
      if (obj_id == SPIFFS_OBJ_ID_FREE) {
      } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
        dele++;
      } else {
        allo++;
      }
      cur_entry++;
    } // per entry
    obj_lookup_page++;
  } // per object lookup page
  SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
  fs->stats_p_allocated -= allo;
  fs->stats_p_deleted -= dele;
  return res;
}
Exemplo n.º 4
0
// reads from spi flash or the cache
s32_t spiffs_phys_rd(
    spiffs *fs,
    u8_t op,
    spiffs_file fh,
    u32_t addr,
    u32_t len,
    u8_t *dst) {
  (void)fh;
  s32_t res = SPIFFS_OK;
  spiffs_cache *cache = spiffs_get_cache(fs);
  spiffs_cache_page *cp =  spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
  cache->last_access++;
  if (cp) {
    // we've already got one, you see
#if SPIFFS_CACHE_STATS
    fs->cache_hits++;
#endif
    cp->last_access = cache->last_access;
    u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
    memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
  } else {
    if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
      // for second layer lookup functions, we do not cache in order to prevent shredding
      return SPIFFS_HAL_READ(fs, addr, len, dst);
    }
#if SPIFFS_CACHE_STATS
    fs->cache_misses++;
#endif
    // this operation will always free one cache page (unless all already free),
    // the result code stems from the write operation of the possibly freed cache page
    res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);

    cp = spiffs_cache_page_allocate(fs);
    if (cp) {
      cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
      cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);

      s32_t res2 = SPIFFS_HAL_READ(fs,
          addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
          SPIFFS_CFG_LOG_PAGE_SZ(fs),
          spiffs_get_cache_page(fs, cache, cp->ix));
      if (res2 != SPIFFS_OK) {
        // honor read failure before possible write failure (bad idea?)
        res = res2;
      }
      u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
      memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
    } else {
      // this will never happen, last resort for sake of symmetry
      s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
      if (res2 != SPIFFS_OK) {
        // honor read failure before possible write failure (bad idea?)
        res = res2;
      }
    }
  }
  return res;
}
Exemplo n.º 5
0
// reads from spi flash or the cache
s32_t ICACHE_FLASH_ATTR
spiffs_phys_rd(
    spiffs *fs,
    u8_t op,
    spiffs_file fh,
    u32_t addr,
    u32_t len,
    u8_t *dst) {
  (void)fh;
  s32_t res = SPIFFS_OK;
  spiffs_cache *cache = spiffs_get_cache(fs);
  spiffs_cache_page *cp =  spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
  cache->last_access++;
  if (cp) {
#if SPIFFS_CACHE_STATS
    fs->cache_hits++;
#endif
    cp->last_access = cache->last_access;
  } else {
    if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
      // for second layer lookup functions, we do not cache in order to prevent shredding
      return fs->cfg.hal_read_f(
          addr ,
          len,
          dst);
    }
#if SPIFFS_CACHE_STATS
    fs->cache_misses++;
#endif
    res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
    cp = spiffs_cache_page_allocate(fs);
    if (cp) {
      cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
      cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
    }

    s32_t res2 = fs->cfg.hal_read_f(
        addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
        SPIFFS_CFG_LOG_PAGE_SZ(fs),
        spiffs_get_cache_page(fs, cache, cp->ix));
    if (res2 != SPIFFS_OK) {
      res = res2;
    }
  }
  u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
  memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
  return res;
}
Exemplo n.º 6
0
// Empties given block by moving all data into free pages of another block
// Strategy:
//   loop:
//   scan object lookup for object data pages
//   for first found id, check spix and load corresponding object index page to memory
//   push object scan lookup entry index
//     rescan object lookup, find data pages with same id and referenced by same object index
//     move data page, update object index in memory
//     when reached end of lookup, store updated object index
//   pop object scan lookup entry index
//   repeat loop until end of object lookup
//   scan object lookup again for remaining object index pages, move to new page in other block
//
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
  s32_t res = SPIFFS_OK;
  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  int cur_entry = 0;
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  spiffs_gc gc;
  spiffs_page_ix cur_pix = 0;
  spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
  spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;

  SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);

  c_memset(&gc, 0, sizeof(spiffs_gc));
  gc.state = FIND_OBJ_DATA;

  if (fs->free_cursor_block_ix == bix) {
    // move free cursor to next block, cannot use free pages from the block we want to clean
    fs->free_cursor_block_ix = (bix+1)%fs->block_count;
    fs->free_cursor_obj_lu_entry = 0;
    SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
  }

  while (res == SPIFFS_OK && gc.state != FINISHED) {
    SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
    gc.obj_id_found = 0;

    // scan through lookup pages
    int obj_lookup_page = cur_entry / entries_per_page;
    u8_t scan = 1;
    // check each object lookup page
    while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
      int entry_offset = obj_lookup_page * entries_per_page;
      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
          0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
          SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
      // check each entry
      while (scan && res == SPIFFS_OK &&
          cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
        spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
        cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);

        // act upon object id depending on gc state
        switch (gc.state) {
        case FIND_OBJ_DATA:
          if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
              ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
            SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
            gc.obj_id_found = 1;
            gc.cur_obj_id = obj_id;
            scan = 0;
          }
          break;
        case MOVE_OBJ_DATA:
          if (obj_id == gc.cur_obj_id) {
            spiffs_page_header p_hdr;
            res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
            SPIFFS_CHECK_RES(res);
            SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
            if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
              SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
            } else {
              spiffs_page_ix new_data_pix;
              if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
                // move page
                res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
                SPIFFS_CHECK_RES(res);
                // move wipes obj_lu, reload it
                res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                    0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                    SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
                SPIFFS_CHECK_RES(res);
              } else {
                // page is deleted but not deleted in lookup, scrap it
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
                res = spiffs_page_delete(fs, cur_pix);
                SPIFFS_CHECK_RES(res);
                new_data_pix = SPIFFS_OBJ_ID_FREE;
              }
              // update memory representation of object index page with new data page
              if (gc.cur_objix_spix == 0) {
                // update object index header page
                ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
              } else {
                // update object index page
                ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
              }
            }
          }
          break;
        case MOVE_OBJ_IX:
          if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
              (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
            // found an index object id
            spiffs_page_header p_hdr;
            spiffs_page_ix new_pix;
            // load header
            res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
            SPIFFS_CHECK_RES(res);
            if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
              // move page
              res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
              SPIFFS_CHECK_RES(res);
              spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
              // move wipes obj_lu, reload it
              res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                  0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                  SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
              SPIFFS_CHECK_RES(res);
            } else {
              // page is deleted but not deleted in lookup, scrap it
              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
              res = spiffs_page_delete(fs, cur_pix);
              if (res == SPIFFS_OK) {
                spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
              }
            }
            SPIFFS_CHECK_RES(res);
          }
          break;
        default:
          scan = 0;
          break;
        }
        cur_entry++;
      } // per entry
      obj_lookup_page++;
    } // per object lookup page

    if (res != SPIFFS_OK) break;

    // state finalization and switch
    switch (gc.state) {
    case FIND_OBJ_DATA:
      if (gc.obj_id_found) {
        // find out corresponding obj ix page and load it to memory
        spiffs_page_header p_hdr;
        spiffs_page_ix objix_pix;
        gc.stored_scan_entry_index = cur_entry;
        cur_entry = 0;
        gc.state = MOVE_OBJ_DATA;
        res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
            0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
        SPIFFS_CHECK_RES(res);
        gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
        SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
        res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
        SPIFFS_CHECK_RES(res);
        SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
        res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
            0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
        SPIFFS_CHECK_RES(res);
        SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
        gc.cur_objix_pix = objix_pix;
      } else {
        gc.state = MOVE_OBJ_IX;
        cur_entry = 0; // restart entry scan index
      }
      break;
    case MOVE_OBJ_DATA: {
      // store modified objix (hdr) page
      spiffs_page_ix new_objix_pix;
      gc.state = FIND_OBJ_DATA;
      cur_entry = gc.stored_scan_entry_index;
      if (gc.cur_objix_spix == 0) {
        // store object index header page
        res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
        SPIFFS_CHECK_RES(res);
      } else {
        // store object index page
        res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
        SPIFFS_CHECK_RES(res);
        spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
      }
    }
    break;
    case MOVE_OBJ_IX:
      gc.state = FINISHED;
      break;
    default:
      cur_entry = 0;
      break;
    }
    SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
  } // while state != FINISHED


  return res;
}
Exemplo n.º 7
0
// Searches for blocks where all entries are deleted - if one is found,
// the block is erased. Compared to the non-quick gc, the quick one ensures
// that no updates are needed on existing objects on pages that are erased.
s32_t spiffs_gc_quick(
    spiffs *fs, u16_t max_free_pages) {
  s32_t res = SPIFFS_OK;
  u32_t blocks = fs->block_count;
  spiffs_block_ix cur_block = 0;
  u32_t cur_block_addr = 0;
  int cur_entry = 0;
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;

  SPIFFS_GC_DBG("gc_quick: running\n", cur_block);
#if SPIFFS_GC_STATS
  fs->stats_gc_runs++;
#endif

  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));

  // find fully deleted blocks
  // check each block
  while (res == SPIFFS_OK && blocks--) {
    u16_t deleted_pages_in_block = 0;
    u16_t free_pages_in_block = 0;

    int obj_lookup_page = 0;
    // check each object lookup page
    while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
      int entry_offset = obj_lookup_page * entries_per_page;
      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
          0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
      // check each entry
      while (res == SPIFFS_OK &&
          cur_entry - entry_offset < entries_per_page &&
          cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
        spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
        if (obj_id == SPIFFS_OBJ_ID_DELETED) {
          deleted_pages_in_block++;
        } else if (obj_id == SPIFFS_OBJ_ID_FREE) {
          // kill scan, go for next block
          free_pages_in_block++;
          if (free_pages_in_block > max_free_pages) {
            obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
            res = 1; // kill object lu loop
            break;
          }
        }  else {
          // kill scan, go for next block
          obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
          res = 1; // kill object lu loop
          break;
        }
        cur_entry++;
      } // per entry
      obj_lookup_page++;
    } // per object lookup page
    if (res == 1) res = SPIFFS_OK;

    if (res == SPIFFS_OK &&
        deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
        free_pages_in_block <= max_free_pages) {
      // found a fully deleted block
      fs->stats_p_deleted -= deleted_pages_in_block;
      res = spiffs_gc_erase_block(fs, cur_block);
      return res;
    }

    cur_entry = 0;
    cur_block++;
    cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  } // per block

  if (res == SPIFFS_OK) {
    res = SPIFFS_ERR_NO_DELETED_BLOCKS;
  }
  return res;
}
Exemplo n.º 8
0
// Finds block candidates to erase
s32_t spiffs_gc_find_candidate(
    spiffs *fs,
    spiffs_block_ix **block_candidates,
    int *candidate_count,
    char fs_crammed) {
  s32_t res = SPIFFS_OK;
  u32_t blocks = fs->block_count;
  spiffs_block_ix cur_block = 0;
  u32_t cur_block_addr = 0;
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  int cur_entry = 0;

  // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
  int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
  *candidate_count = 0;
  c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));

  // divide up work area into block indices and scores
  // todo alignment?
  spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
  s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));

  *block_candidates = cand_blocks;

  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));

  // check each block
  while (res == SPIFFS_OK && blocks--) {
    u16_t deleted_pages_in_block = 0;
    u16_t used_pages_in_block = 0;

    int obj_lookup_page = 0;
    // check each object lookup page
    while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
      int entry_offset = obj_lookup_page * entries_per_page;
      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
          0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
      // check each entry
      while (res == SPIFFS_OK &&
          cur_entry - entry_offset < entries_per_page &&
          cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
        spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
        if (obj_id == SPIFFS_OBJ_ID_FREE) {
          // when a free entry is encountered, scan logic ensures that all following entries are free also
          res = 1; // kill object lu loop
          break;
        } else  if (obj_id == SPIFFS_OBJ_ID_DELETED) {
          deleted_pages_in_block++;
        } else {
          used_pages_in_block++;
        }
        cur_entry++;
      } // per entry
      obj_lookup_page++;
    } // per object lookup page
    if (res == 1) res = SPIFFS_OK;

    // calculate score and insert into candidate table
    // stoneage sort, but probably not so many blocks
    if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
      // read erase count
      spiffs_obj_id erase_count;
      res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
          SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
          sizeof(spiffs_obj_id), (u8_t *)&erase_count);
      SPIFFS_CHECK_RES(res);

      spiffs_obj_id erase_age;
      if (fs->max_erase_count > erase_count) {
        erase_age = fs->max_erase_count - erase_count;
      } else {
        erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
      }

      s32_t score =
          deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
          used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
          erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
      int cand_ix = 0;
      SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
      while (cand_ix < max_candidates) {
        if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
          cand_blocks[cand_ix] = cur_block;
          cand_scores[cand_ix] = score;
          break;
        } else if (cand_scores[cand_ix] < score) {
          int reorder_cand_ix = max_candidates - 2;
          while (reorder_cand_ix >= cand_ix) {
            cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
            cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
            reorder_cand_ix--;
          }
          cand_blocks[cand_ix] = cur_block;
          cand_scores[cand_ix] = score;
          break;
        }
        cand_ix++;
      }
      (*candidate_count)++;
    }

    cur_entry = 0;
    cur_block++;
    cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  } // per block

  return res;
}
Exemplo n.º 9
0
s32_t SPIFFS_vis(spiffs *fs) {
  s32_t res = SPIFFS_OK;
  SPIFFS_API_CHECK_MOUNT(fs);
  SPIFFS_LOCK(fs);

  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  spiffs_block_ix bix = 0;

  while (bix < fs->block_count) {
    // check each object lookup page
    int obj_lookup_page = 0;
    int cur_entry = 0;

    while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
      int entry_offset = obj_lookup_page * entries_per_page;
      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
          0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
      // check each entry
      while (res == SPIFFS_OK &&
          cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
        spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
        if (cur_entry == 0) {
          spiffs_printf("%4i ", bix);
        } else if ((cur_entry & 0x3f) == 0) {
          spiffs_printf("     ");
        }
        if (obj_id == SPIFFS_OBJ_ID_FREE) {
          spiffs_printf(SPIFFS_TEST_VIS_FREE_STR);
        } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
          spiffs_printf(SPIFFS_TEST_VIS_DELE_STR);
        } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){
          spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id));
        } else {
          spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id));
        }
        cur_entry++;
        if ((cur_entry & 0x3f) == 0) {
          spiffs_printf("\n");
        }
      } // per entry
      obj_lookup_page++;
    } // per object lookup page

    spiffs_obj_id erase_count;
    res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
        SPIFFS_ERASE_COUNT_PADDR(fs, bix),
        sizeof(spiffs_obj_id), (u8_t *)&erase_count);
    SPIFFS_CHECK_RES(res);

    if (erase_count != (spiffs_obj_id)-1) {
      spiffs_printf("\tera_cnt: %d\n", erase_count);
    } else {
      spiffs_printf("\tera_cnt: N/A\n");
    }

    bix++;
  } // per block

  spiffs_printf("era_cnt_max: %d\n", fs->max_erase_count);
  spiffs_printf("last_errno:  %d\n", fs->errno);
  spiffs_printf("blocks:      %d\n", fs->block_count);
  spiffs_printf("free_blocks: %d\n", fs->free_blocks);
  spiffs_printf("page_alloc:  %d\n", fs->stats_p_allocated);
  spiffs_printf("page_delet:  %d\n", fs->stats_p_deleted);

  SPIFFS_UNLOCK(fs);
  return res;
}
Exemplo n.º 10
0
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
  SPIFFS_API_CHECK_MOUNT(fs);
  SPIFFS_LOCK(fs);

  spiffs_fd *fd;
  s32_t res;
  u32_t offset;

  res = spiffs_fd_get(fs, fh, &fd);
  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);

  if ((fd->flags & SPIFFS_WRONLY) == 0) {
    res = SPIFFS_ERR_NOT_WRITABLE;
    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
  }

  offset = fd->fdoffset;

#if SPIFFS_CACHE_WR
  if (fd->cache_page == 0) {
    // see if object id is associated with cache already
    fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
  }
#endif
  if (fd->flags & SPIFFS_APPEND) {
    if (fd->size == SPIFFS_UNDEFINED_LEN) {
      offset = 0;
    } else {
      offset = fd->size;
    }
#if SPIFFS_CACHE_WR
    if (fd->cache_page) {
      offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size);
    }
#endif
  }

  SPIFFS_DBG("SPIFFS_write %d %04x offs:%d len %d\n", fh, fd->obj_id, offset, len);

#if SPIFFS_CACHE_WR
  if ((fd->flags & SPIFFS_DIRECT) == 0) {
    if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
      // small write, try to cache it
      u8_t alloc_cpage = 1;
      if (fd->cache_page) {
        // have a cached page for this fd already, check cache page boundaries
        if (offset < fd->cache_page->offset || // writing before cache
            offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache
            offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
        {
          // boundary violation, write back cache first and allocate new
          SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:&04x, boundary viol, offs:%d size:%d\n",
              fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
          res = spiffs_hydro_write(fs, fd,
              spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
              fd->cache_page->offset, fd->cache_page->size);
          spiffs_cache_fd_release(fs, fd->cache_page);
        } else {
          // writing within cache
          alloc_cpage = 0;
        }
      }

      if (alloc_cpage) {
        fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd);
        if (fd->cache_page) {
          fd->cache_page->offset = offset;
          fd->cache_page->size = 0;
          SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %d for fd %d:%04x\n",
              fd->cache_page->ix, fd->file_nbr, fd->obj_id);
        }
      }

      if (fd->cache_page) {
        u32_t offset_in_cpage = offset - fd->cache_page->offset;
        SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %d for fd %d:%04x, offs %d:%d len %d\n",
            fd->cache_page->ix, fd->file_nbr, fd->obj_id,
            offset, offset_in_cpage, len);
        spiffs_cache *cache = spiffs_get_cache(fs);
        u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix);
        c_memcpy(&cpage_data[offset_in_cpage], buf, len);
        fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len);
        fd->fdoffset += len;
        SPIFFS_UNLOCK(fs);
        return len;
      } else {
        res = spiffs_hydro_write(fs, fd, buf, offset, len);
        SPIFFS_API_CHECK_RES(fs, res);
        fd->fdoffset += len;
        SPIFFS_UNLOCK(fs);
        return res;
      }
    } else {
      // big write, no need to cache it - but first check if there is a cached write already
      if (fd->cache_page) {
        // write back cache first
        SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:%04x, big write, offs:%d size:%d\n",
            fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
        res = spiffs_hydro_write(fs, fd,
            spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
            fd->cache_page->offset, fd->cache_page->size);
        spiffs_cache_fd_release(fs, fd->cache_page);
        res = spiffs_hydro_write(fs, fd, buf, offset, len);
        SPIFFS_API_CHECK_RES(fs, res);
      }
    }
  }
#endif

  res = spiffs_hydro_write(fs, fd, buf, offset, len);
  SPIFFS_API_CHECK_RES(fs, res);
  fd->fdoffset += len;

  SPIFFS_UNLOCK(fs);

  return res;
}
Exemplo n.º 11
0
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) {
  return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs));
}
Exemplo n.º 12
0
#include <esp8266.h>
#include "roffs.h"

#ifdef SPIFFS

#include "spiffs.h"

static u8_t spiffs_work_buf[SPIFFS_CFG_LOG_PAGE_SZ(x)*2];
static u8_t spiffs_fds[4*32];
static spiffs fs;

int ICACHE_FLASH_ATTR roffs_mount(uint32_t flashAddress)
{
    spiffs_config cfg;
    s32_t spiffs_hal_read(u32_t addr, u32_t size, u8_t *dst);
    s32_t spiffs_hal_write(u32_t addr, u32_t size, u8_t *src);
    s32_t spiffs_hal_erase(u32_t addr, u32_t size);
    cfg.hal_read_f = spiffs_hal_read;
    cfg.hal_write_f = spiffs_hal_write;
    cfg.hal_erase_f = spiffs_hal_erase;
    int res = SPIFFS_mount(&fs,
                           &cfg,
                           spiffs_work_buf,
                           spiffs_fds, sizeof(spiffs_fds),
                           NULL, 0,
                           NULL);
    if (res != SPIFFS_OK) {
        os_printf("Formatting flash filesystem\n");
        res = SPIFFS_format(&fs);
        if (res == SPIFFS_OK) {
            res = SPIFFS_mount(&fs,
Exemplo n.º 13
0
// Empties given block by moving all data into free pages of another block
// Strategy:
//   loop:
//   scan object lookup for object data pages
//   for first found id, check spix and load corresponding object index page to memory
//   push object scan lookup entry index
//     rescan object lookup, find data pages with same id and referenced by same object index
//     move data page, update object index in memory
//     when reached end of lookup, store updated object index
//   pop object scan lookup entry index
//   repeat loop until end of object lookup
//   scan object lookup again for remaining object index pages, move to new page in other block
//
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
  s32_t res = SPIFFS_OK;
  const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  // this is the global localizer being pushed and popped
  int cur_entry = 0;
  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  spiffs_gc gc; // our stack frame/state
  spiffs_page_ix cur_pix = 0;
  spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
  spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;

  SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);

  memset(&gc, 0, sizeof(spiffs_gc));
  gc.state = FIND_OBJ_DATA;

  if (fs->free_cursor_block_ix == bix) {
    // move free cursor to next block, cannot use free pages from the block we want to clean
    fs->free_cursor_block_ix = (bix+1)%fs->block_count;
    fs->free_cursor_obj_lu_entry = 0;
    SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
  }

  while (res == SPIFFS_OK && gc.state != FINISHED) {
    SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
    gc.obj_id_found = 0; // reset (to no found data page)

    // scan through lookup pages
    int obj_lookup_page = cur_entry / entries_per_page;
    u8_t scan = 1;
    // check each object lookup page
    while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
      int entry_offset = obj_lookup_page * entries_per_page;
      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
          0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
          SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
      // check each object lookup entry
      while (scan && res == SPIFFS_OK &&
          cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
        spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
        cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);

        // act upon object id depending on gc state
        switch (gc.state) {
        case FIND_OBJ_DATA:
          // find a data page
          if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
              ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
            // found a data page, stop scanning and handle in switch case below
            SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
            gc.obj_id_found = 1;
            gc.cur_obj_id = obj_id;
            gc.cur_data_pix = cur_pix;
            scan = 0;
          }
          break;
        case MOVE_OBJ_DATA:
          // evacuate found data pages for corresponding object index we have in memory,
          // update memory representation
          if (obj_id == gc.cur_obj_id) {
            spiffs_page_header p_hdr;
            res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
            SPIFFS_CHECK_RES(res);
            SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
            if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
              SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
            } else {
              spiffs_page_ix new_data_pix;
              if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
                // move page
                res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
                SPIFFS_CHECK_RES(res);
                // move wipes obj_lu, reload it
                res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                    0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                    SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
                SPIFFS_CHECK_RES(res);
              } else {
                // page is deleted but not deleted in lookup, scrap it -
                // might seem unnecessary as we will erase this block, but
                // we might get aborted
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
                res = spiffs_page_delete(fs, cur_pix);
                SPIFFS_CHECK_RES(res);
                new_data_pix = SPIFFS_OBJ_ID_FREE;
              }
              // update memory representation of object index page with new data page
              if (gc.cur_objix_spix == 0) {
                // update object index header page
                ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
              } else {
                // update object index page
                ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
              }
            }
          }
          break;
        case MOVE_OBJ_IX:
          // find and evacuate object index pages
          if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
              (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
            // found an index object id
            spiffs_page_header p_hdr;
            spiffs_page_ix new_pix;
            // load header
            res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
            SPIFFS_CHECK_RES(res);
            if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
              // move page
              res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
              SPIFFS_CHECK_RES(res);
              spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
                  SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
              // move wipes obj_lu, reload it
              res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                  0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                  SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
              SPIFFS_CHECK_RES(res);
            } else {
              // page is deleted but not deleted in lookup, scrap it -
              // might seem unnecessary as we will erase this block, but
              // we might get aborted
              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
              res = spiffs_page_delete(fs, cur_pix);
              if (res == SPIFFS_OK) {
                spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
                    SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
              }
            }
            SPIFFS_CHECK_RES(res);
          }
          break;
        default:
          scan = 0;
          break;
        } // switch gc state
        cur_entry++;
      } // per entry
      obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
    } // per object lookup page
    if (res != SPIFFS_OK) break;

    // state finalization and switch
    switch (gc.state) {
    case FIND_OBJ_DATA:
      if (gc.obj_id_found) {
        // handle found data page -
        // find out corresponding obj ix page and load it to memory
        spiffs_page_header p_hdr;
        spiffs_page_ix objix_pix;
        gc.stored_scan_entry_index = cur_entry; // push cursor
        cur_entry = 0; // restart scan from start
        gc.state = MOVE_OBJ_DATA;
        res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
            0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
        SPIFFS_CHECK_RES(res);
        gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
        SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
        res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
        if (res == SPIFFS_ERR_NOT_FOUND) {
          // on borked systems we might get an ERR_NOT_FOUND here -
          // this is handled by simply deleting the page as it is not referenced
          // from anywhere
          SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
          res = spiffs_page_delete(fs, gc.cur_data_pix);
          SPIFFS_CHECK_RES(res);
          // then we restore states and continue scanning for data pages
          cur_entry = gc.stored_scan_entry_index; // pop cursor
          gc.state = FIND_OBJ_DATA;
          break; // done
        }
        SPIFFS_CHECK_RES(res);
        SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
        res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
            0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
        SPIFFS_CHECK_RES(res);
        // cannot allow a gc if the presumed index in fact is no index, a
        // check must run or lot of data may be lost
        SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
        gc.cur_objix_pix = objix_pix;
      } else {
        // no more data pages found, passed thru all block, start evacuating object indices
        gc.state = MOVE_OBJ_IX;
        cur_entry = 0; // restart entry scan index
      }
      break;
    case MOVE_OBJ_DATA: {
      // store modified objix (hdr) page residing in memory now that all
      // data pages belonging to this object index and residing in the block
      // we want to evacuate
      spiffs_page_ix new_objix_pix;
      gc.state = FIND_OBJ_DATA;
      cur_entry = gc.stored_scan_entry_index; // pop cursor
      if (gc.cur_objix_spix == 0) {
        // store object index header page
        res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix);
        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
        SPIFFS_CHECK_RES(res);
      } else {
        // store object index page
        res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
        SPIFFS_CHECK_RES(res);
        spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
            SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
      }
    }
    break;
    case MOVE_OBJ_IX:
      // scanned thru all block, no more object indices found - our work here is done
      gc.state = FINISHED;
      break;
    default:
      cur_entry = 0;
      break;
    } // switch gc.state
    SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
  } // while state != FINISHED


  return res;
}
Exemplo n.º 14
0
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
    u8_t *fd_space, u32_t fd_space_size,
    void *cache, u32_t cache_size,
    spiffs_check_callback check_cb_f) {
  void *user_data;
  SPIFFS_LOCK(fs);
  user_data = fs->user_data;
  memset(fs, 0, sizeof(spiffs));
  memcpy(&fs->cfg, config, sizeof(spiffs_config));
  fs->user_data = user_data;
  fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  fs->work = &work[0];
  fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
  memset(fd_space, 0, fd_space_size);
  // align fd_space pointer to pointer size byte boundary
  u8_t ptr_size = sizeof(void*);
  u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1);
  if (addr_lsb) {
    fd_space += (ptr_size-addr_lsb);
    fd_space_size -= (ptr_size-addr_lsb);
  }
  fs->fd_space = fd_space;
  fs->fd_count = (fd_space_size/sizeof(spiffs_fd));

  // align cache pointer to 4 byte boundary
  addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1);
  if (addr_lsb) {
    u8_t *cache_8 = (u8_t *)cache;
    cache_8 += (ptr_size-addr_lsb);
    cache = cache_8;
    cache_size -= (ptr_size-addr_lsb);
  }
  if (cache_size & (ptr_size-1)) {
    cache_size -= (cache_size & (ptr_size-1));
  }

#if SPIFFS_CACHE
  fs->cache = cache;
  fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size;
  spiffs_cache_init(fs);
#endif

  s32_t res;

#if SPIFFS_USE_MAGIC
  res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE;
  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
#endif

  fs->config_magic = SPIFFS_CONFIG_MAGIC;

  res = spiffs_obj_lu_scan(fs);
  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);

  SPIFFS_DBG("page index byte len:         %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
  SPIFFS_DBG("object lookup pages:         %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
  SPIFFS_DBG("page pages per block:        %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
  SPIFFS_DBG("page header length:          %i\n", sizeof(spiffs_page_header));
  SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
  SPIFFS_DBG("object index entries:        %i\n", SPIFFS_OBJ_IX_LEN(fs));
  SPIFFS_DBG("available file descriptors:  %i\n", fs->fd_count);
  SPIFFS_DBG("free blocks:                 %i\n", fs->free_blocks);

  fs->check_cb_f = check_cb_f;

  fs->mounted = 1;

  SPIFFS_UNLOCK(fs);

  return 0;
}