static int spiffs_gc_clean()

in fs/spiffs/src/spiffs_gc.c [449:970]


static int spiffs_gc_clean(FAR struct spiffs_s *fs, int16_t blkndx)
{
  const int entries_per_page = (SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t));
  int ret = OK;

  /* This is the global localizer being pushed and popped */

  struct spiffs_gc_s gc;                 /* Our stack frame/state */
  FAR int16_t *objlu_buf;
  FAR struct spiffs_pgobj_ndxheader_s *objhdr;
  FAR struct spiffs_page_objndx_s *objndx;
  int16_t cur_pgndx      = 0;
  int cur_entry          = 0;

  spiffs_gcinfo("Cleaning block %04x\n", blkndx);

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

  objlu_buf = (FAR int16_t *)fs->lu_work;
  objhdr    = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
  objndx    = (struct spiffs_page_objndx_s *)fs->work;

  if (fs->free_blkndx == blkndx)
    {
      /* Move free cursor to next block, cannot use free pages from the block
       * we want to clean
       */

      fs->free_blkndx = (blkndx + 1) % SPIFFS_GEO_BLOCK_COUNT(fs);
      fs->free_entry = 0;
      spiffs_gcinfo("Move free cursor to block=%04x\n", fs->free_blkndx);
    }

  while (ret >= 0 && gc.state != FINISHED)
    {
      int obj_lookup_page;
      uint8_t scan;

      spiffs_gcinfo("state=%d entry=%d\n", gc.state, cur_entry);

      gc.objid_found  = false;  /* Reset (to no found data page) */

      /* Scan through lookup pages */

      obj_lookup_page = cur_entry / entries_per_page;
      scan            = 1;

      /* Check each object lookup page */

      while (scan && ret >= 0 &&
             obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
        {
          int entry_offset = obj_lookup_page * entries_per_page;

          ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                                  0, blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
                                  SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                                  SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work);

          /* Check each object lookup entry */

          while (scan && ret >= 0 &&
                 cur_entry - entry_offset < entries_per_page &&
                 cur_entry <
                 (int)(SPIFFS_GEO_PAGES_PER_BLOCK(fs) -
                       SPIFFS_OBJ_LOOKUP_PAGES(fs)))
            {
              int16_t id = objlu_buf[cur_entry - entry_offset];

              cur_pgndx =
                SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, cur_entry);

              /* Act upon object id depending on gc state */

              switch (gc.state)
                {
                case FIND_OBJ_DATA:

                  /* Find a data page. */

                  if (id != SPIFFS_OBJID_DELETED &&
                      id != SPIFFS_OBJID_FREE &&
                      ((id & SPIFFS_OBJID_NDXFLAG) == 0))
                    {
                      /* Found a data page, stop scanning and handle in
                       * switch case below
                       */

                      spiffs_gcinfo(
                        "Found data page, state=%d, objid=%04x\n",
                        gc.state, id);

                      gc.objid_found    = true;
                      gc.cur_objid      = id;
                      gc.cur_data_pgndx = cur_pgndx;
                      scan              = 0;
                    }
                  break;

                case MOVE_OBJ_DATA:

                  /* Evacuate found data pages for corresponding object index
                   * we have in memory, update memory representation
                   */

                  if (id == gc.cur_objid)
                    {
                      struct spiffs_page_header_s phdr;

                      ret = spiffs_cache_read(fs,
                                      SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                                      0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
                                      sizeof(struct spiffs_page_header_s),
                                      (FAR uint8_t *)&phdr);
                      if (ret < 0)
                        {
                          ferr("ERROR: spiffs_cache_read() failed: %d\n",
                               ret);
                          return ret;
                        }

                      spiffs_gcinfo("Found data page %04x:%04x @%04x\n",
                                    gc.cur_objid, phdr.spndx, cur_pgndx);

                      if (SPIFFS_OBJNDX_ENTRY_SPNDX(fs, phdr.spndx) !=
                          gc.cur_objndx_spndx)
                        {
                          spiffs_gcinfo(
                            "No objndx spndx match, take in another run\n");
                        }
                      else
                        {
                          int16_t new_data_pgndx;
                          if (phdr.flags & SPIFFS_PH_FLAG_DELET)
                            {
                              /* Move page */

                              ret = spiffs_page_move(fs, 0, 0, id, &phdr,
                                                 cur_pgndx, &new_data_pgndx);

                              spiffs_gcinfo(
                                "Move objndx=%04x:%04x page=%04x to %04x\n",
                                gc.cur_objid, phdr.spndx,
                                cur_pgndx, new_data_pgndx);

                              if (ret < 0)
                                {
                                  ferr("ERROR: spiffs_page_move(): %d\n",
                                       ret);
                                  return ret;
                                }

                              /* Move wipes obj_lu, reload it */

                              ret =
                                spiffs_cache_read(fs,
                                   SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                                   0,
                                   blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
                                   SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                                   SPIFFS_GEO_PAGE_SIZE(fs),
                                   fs->lu_work);
                              if (ret < 0)
                                {
                                  ferr("ERROR: spiffs_cache_read(): %d\n",
                                       ret);
                                  return ret;
                                }
                            }
                          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_gcinfo(
                                "Wipe objndx=%04x:%04x page=%04x\n",
                                id, phdr.spndx, cur_pgndx);

                              ret = spiffs_page_delete(fs, cur_pgndx);
                              if (ret < 0)
                                {
                                  ferr("ERROR: spiffs_page_delete(): %d\n",
                                       ret);
                                  return ret;
                                }

                              new_data_pgndx = SPIFFS_OBJID_FREE;
                            }

                          /* Update memory representation of object index
                           * page with new data page
                           */

                          if (gc.cur_objndx_spndx == 0)
                            {
                              /* Update object index header page */

                              ((FAR int16_t *)((FAR uint8_t *)objhdr +
                                 sizeof(struct spiffs_pgobj_ndxheader_s)))
                                 [phdr.spndx] = new_data_pgndx;

                              spiffs_gcinfo(
                                "Wrote page=%04x to objhdr entry=%04x\n",
                                new_data_pgndx,
                                (int)SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx));
                            }
                          else
                            {
                              /* Update object index page */

                              ((FAR int16_t *)((FAR uint8_t *)objndx +
                                sizeof(struct spiffs_page_objndx_s)))
                                  [SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx)] =
                                    new_data_pgndx;

                              spiffs_gcinfo(
                                "Wrote page=%04x to objndx entry=%04x\n",
                                new_data_pgndx,
                                (int)SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx));
                            }
                        }
                    }
                  break;

                case MOVE_OBJ_NDX:

                  /* Find and evacuate object index pages */

                  if (id != SPIFFS_OBJID_DELETED &&
                      id != SPIFFS_OBJID_FREE &&
                      (id & SPIFFS_OBJID_NDXFLAG) != 0)
                    {
                      /* Found an index object id */

                      struct spiffs_page_header_s phdr;
                      int16_t new_pgndx;

                      /* Load header */

                      ret = spiffs_cache_read(fs,
                                      SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                                      0,
                                      SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
                                      sizeof(struct spiffs_page_header_s),
                                      (FAR uint8_t *)&phdr);
                      if (ret < 0)
                        {
                          ferr("ERROR: spiffs_cache_read() failed: %d\n",
                               ret);
                          return ret;
                        }

                      if (phdr.flags & SPIFFS_PH_FLAG_DELET)
                        {
                          /* Move page */

                          ret = spiffs_page_move(fs, 0, 0, id, &phdr,
                                                 cur_pgndx, &new_pgndx);

                          spiffs_gcinfo(
                            "Move objndx=%04x:%04x page=%04x to %04x\n",
                            id, phdr.spndx, cur_pgndx, new_pgndx);

                          if (ret < 0)
                            {
                              ferr("ERROR: spiffs_page_move() failed: %d\n",
                                   ret);
                              return ret;
                            }

                          spiffs_fobj_event(fs,
                                    (FAR struct spiffs_page_objndx_s *)&phdr,
                                    SPIFFS_EV_NDXMOV, id,
                                    phdr.spndx, new_pgndx, 0);

                          /* Move wipes obj_lu, reload it */

                          ret =
                            spiffs_cache_read(fs,
                                   SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                                   0,
                                   blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
                                   SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                                   SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work);
                          if (ret < 0)
                            {
                              ferr("ERROR: spiffs_cache_read() failed: %d\n",
                                   ret);
                              return ret;
                            }
                        }
                      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_gcinfo("Wipe objndx=%04x:%04x page=%04x\n",
                                        id, phdr.spndx, cur_pgndx);

                          ret = spiffs_page_delete(fs, cur_pgndx);
                          if (ret < 0)
                            {
                              ferr("ERROR: spiffs_fobj_event() failed: %d\n",
                                   ret);
                              return ret;
                            }

                          spiffs_fobj_event(fs, NULL,
                                            SPIFFS_EV_NDXDEL, id,
                                            phdr.spndx, cur_pgndx, 0);
                        }
                    }
                  break;

                default:
                  scan = 0;
                  break;
                }

              cur_entry++;
            }

          obj_lookup_page++;    /* No need to check scan variable here,
                                 * obj_lookup_page is set in start of loop
                                 */
        }

      if (ret < 0)
        {
          break;
        }

      /* State finalization and switch */

      switch (gc.state)
        {
        case FIND_OBJ_DATA:
          if (gc.objid_found)
            {
              struct spiffs_page_header_s phdr;
              int16_t objndx_pgndx;

              /* Handle found data page.  Find out corresponding objndx page
               * and load it to memory
               */

              gc.stored_scan_entry_index = cur_entry; /* Push cursor */
              cur_entry                  = 0;         /* Sestart scan from start */
              gc.state                   = MOVE_OBJ_DATA;

              ret = spiffs_cache_read(fs,
                                      SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                                      0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
                                      sizeof(struct spiffs_page_header_s),
                                      (FAR uint8_t *)&phdr);
              if (ret < 0)
                {
                  ferr("ERROR: spiffs_fobj_event() failed: %d\n", ret);
                  return ret;
                }

              gc.cur_objndx_spndx =
                SPIFFS_OBJNDX_ENTRY_SPNDX(fs, phdr.spndx);

              spiffs_gcinfo("Find objndx spndx=%04x\n", gc.cur_objndx_spndx);

              ret = spiffs_objlu_find_id_and_span(fs,
                                         gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
                                         gc.cur_objndx_spndx, 0,
                                         &objndx_pgndx);
              if (ret == -ENOENT)
                {
                  /* 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_gcinfo("objndx not found! Wipe page %04x\n",
                                gc.cur_data_pgndx);

                  ret = spiffs_page_delete(fs, gc.cur_data_pgndx);
                  if (ret < 0)
                    {
                      ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
                      return ret;
                    }

                  /* 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;
                }

              if (ret < 0)
                {
                  return ret;
                }

              spiffs_gcinfo("Found object index at page=%04x\n",
                            objndx_pgndx);

              ret = spiffs_cache_read(fs,
                                      SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                                      0,
                                      SPIFFS_PAGE_TO_PADDR(fs, objndx_pgndx),
                                      SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
              if (ret < 0)
                {
                  ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
                  return ret;
                }

              /* Cannot allow a gc if the presumed index in fact is no
               * index, a check must run or lot of data may be lost
               */

              ret = spiffs_validate_objndx(&objndx->phdr,
                                         gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
                                         gc.cur_objndx_spndx);
              if (ret < 0)
                {
                  ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
                  return ret;
                }

              gc.cur_objndx_pgndx = objndx_pgndx;
            }
          else
            {
              /* No more data pages found, passed through all block, start
               * evacuating object indices
               */

              gc.state  = MOVE_OBJ_NDX;
              cur_entry = 0;    /* Restart entry scan index */
            }
          break;

        case MOVE_OBJ_DATA:
          {
            int16_t new_objndx_pgndx;

            /* Store modified objndx (hdr) page residing in memory now that
             * all data pages belonging to this object index and residing in
             * the block we want to evacuate
             */

            gc.state  = FIND_OBJ_DATA;
            cur_entry = gc.stored_scan_entry_index;     /* pop cursor */

            if (gc.cur_objndx_spndx == 0)
              {
                /* Store object index header page */

                ret = spiffs_fobj_update_ndxhdr(fs, 0,
                                         gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
                                         gc.cur_objndx_pgndx, fs->work, 0,
                                         0, &new_objndx_pgndx);

                spiffs_gcinfo("Store modified objhdr page=%04x:%04x\n",
                              new_objndx_pgndx, 0);

                if (ret < 0)
                  {
                    ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
                         ret);
                    return ret;
                  }
              }
            else
              {
                /* Store object index page */

                ret =  spiffs_page_move(fs, 0, fs->work,
                                        gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
                                        0, gc.cur_objndx_pgndx,
                                        &new_objndx_pgndx);

                spiffs_gcinfo("Store modified objndx page=%04x:%04x\n",
                              new_objndx_pgndx, objndx->phdr.spndx);

                if (ret < 0)
                  {
                    ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
                    return ret;
                  }

                spiffs_fobj_event(fs,
                                 (FAR struct spiffs_page_objndx_s *)fs->work,
                                 SPIFFS_EV_NDXUPD, gc.cur_objid,
                                 objndx->phdr.spndx,
                                 new_objndx_pgndx, 0);
              }
          }
          break;

        case MOVE_OBJ_NDX:
          /* Scanned through all blocks, no more object indices found - our
           * work here is done
           */

          gc.state = FINISHED;
          break;

        default:
          cur_entry = 0;
          break;
        }

      spiffs_gcinfo("state=%d\n", gc.state);
    }

  return ret;
}