You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/08/22 02:37:45 UTC

[GitHub] [incubator-nuttx-apps] XinStellaris opened a new pull request, #1288: testing:add MTD Fail-safe config-data tests

XinStellaris opened a new pull request, #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288

   Signed-off-by: 田昕 <ti...@xiaomi.com>
   
   ## Summary
   testcases for fail-safe MTD config data.
   Fail-safe here means the new config data system is resilient to power loss.
   
   ## Impact
   Only on the fail-safe MTD config data.
   
   ## Testing
   Tested on Risc-v/Esp32c3
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on a diff in pull request #1288: testing:add MTD Fail-safe config-data tests

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288#discussion_r959340975


##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2039 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdconfig_fs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  ret = ioctl(fd, MTDIOC_BULKERASE, NULL);
+  if (ret < 0)
+    {
+      printf("%s:clear failed, ret=%d\n", __func__, ret);
+      return -errno;
+    }
+
+  close(fd);
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      return ret;
+    }
+
+  return ret;
+}
+
+extern int find_mtddriver(FAR const char *pathname,
+                        FAR struct inode **ppinode);
+
+/****************************************************************************
+ * Name: setup
+ ****************************************************************************/
+
+static int setup(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  FAR struct inode *sys_node;
+
+  ret = find_mtddriver(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME,
+    &sys_node);
+  if (ret < 0)
+    {
+      printf("ERROR: open %s failed: %d\n",
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, ret);
+      return -errno;
+    }
+
+  ret = mtdconfig_register(sys_node->u.i_mtd);
+  if (ret < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, ret);
+      return ret;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_mount
+ ****************************************************************************/
+
+void test_nvs_mount(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: execute_long_pattern_write
+ ****************************************************************************/
+
+static int execute_long_pattern_write(const char *key)
+{
+  struct config_data_s data;
+  int i;
+  int fd;
+  int ret;
+  uint8_t rd_buf[512];
+  uint8_t wr_buf[512];
+  char pattern[4];
+
+  pattern[0] = 0xde;
+  pattern[1] = 0xad;
+  pattern[2] = 0xbe;
+  pattern[3] = 0xef;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  strlcpy(data.name, key, sizeof(data.name));
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != -1 || errno != ENOENT)
+    {
+      printf("%s:CFGDIOC_GETCONFIG unexpected failure: %d\n",
+        __func__, ret);
+      goto err_fd;
+    }
+
+  for (i = 0; i < sizeof(wr_buf); i += sizeof(pattern))
+    {
+      memcpy(wr_buf + i, pattern, sizeof(pattern));
+    }
+
+  data.configdata = wr_buf;
+  data.len = sizeof(wr_buf);
+
+  ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+  if (memcmp(wr_buf, rd_buf, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:RD buff should be equal to the WR buff\n", __func__);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+err_fd:
+  close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_write
+ ****************************************************************************/
+
+void test_nvs_write(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = execute_long_pattern_write(TEST_KEY1);
+  if (ret < 0)
+    {
+      printf("%s:execute_long_pattern_write failed, ret=%d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupt_expire
+ * Description: Test that startup correctly handles invalid expire.
+ ****************************************************************************/
+
+void test_nvs_corrupt_expire(struct mtdnvs_ctx_s *ctx)
+{
+  struct nvs_ate ate;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int ret;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct config_data_s data;
+  char rd_buf[50];
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* write valid data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write valid data again, simulate overwrite data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA2, strlen(TEST_DATA2) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write data2 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = 2 * (strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2);
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* Write ate */
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA2) + 1,
+    strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write close ate, mark section 0 as closed */
+
+  fill_close_ate(&ate,
+    CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write close ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_FIRSTCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_FIRSTCONFIG unexpected failure: %d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, TEST_DATA2, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:unexpected value\n", __func__);
+      goto test_fail;
+    }
+
+  ret = ioctl(nvs_fd, CFGDIOC_NEXTCONFIG, &data);
+  if (ret != -1 || errno != ENOENT)
+    {
+      printf("%s:CFGDIOC_NEXTCONFIG should return ENOENT, but: %d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupted_write
+ ****************************************************************************/
+
+void test_nvs_corrupted_write(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  char rd_buf[512];
+  char wr_buf_1[] = TEST_DATA1;
+  char wr_buf_2[] = TEST_DATA2;
+  char key1[] = TEST_KEY1;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct nvs_ate ate;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* lets simulate loss of part of data */
+
+  /* write valid data */
+
+  ret = write(mtd_fd, key1, sizeof(key1));
+  if (ret != sizeof(key1))
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, wr_buf_1, sizeof(wr_buf_1));
+  if (ret != sizeof(wr_buf_1))
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* power loss occurs after we write data */
+
+  ret = write(mtd_fd, key1, sizeof(key1));
+  if (ret != sizeof(key1))
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, wr_buf_2, sizeof(wr_buf_1));
+  if (ret != sizeof(wr_buf_2))
+    {
+      printf("%s:write data2 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = 2 * (sizeof(key1) + sizeof(wr_buf_1));
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 3 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write ate */
+
+  fill_ate(&ate, key1, sizeof(wr_buf_1), 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  /* now start up */
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret < 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, wr_buf_1, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:failed, RD buff should be equal to the first WR buff "
+        "because subsequent write operation has failed\n",
+        __func__);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+  if (nvs_fd > 0)
+    {
+      close(nvs_fd);
+    }
+
+  if (mtd_fd > 0)
+    {
+      close(mtd_fd);
+    }
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc
+ ****************************************************************************/
+
+void test_nvs_gc(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  int fd = -1;
+  uint8_t buf[44];
+  uint8_t rd_buf[44];
+  struct config_data_s data;
+  uint16_t i;
+  uint16_t id;
+  const uint16_t max_id = 10;
+
+  /* 4096 * 2 / (44 + 4 + 16)  = 128, 129 write will trigger GC. */
+
+  const uint16_t max_writes = 129;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  for (i = 0; i < max_writes; i++)
+    {
+      id = (i % max_id);
+      uint8_t id_data = id + max_id * (i / max_id);
+
+      memset(buf, id_data, sizeof(buf));
+
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = buf;
+      data.len = sizeof(buf);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+    }
+
+  for (id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  fd = -1;
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  for (id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+  if (fd >= 0)
+    close(fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: write_content
+ ****************************************************************************/
+
+static int write_content(uint16_t max_id, uint16_t begin, uint16_t end)
+{
+  uint8_t buf[44];
+  int fd = -1;
+  int ret;
+  struct config_data_s data;
+  uint16_t i;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  for (i = begin; i < end; i++)
+    {
+      uint8_t id = (i % max_id);
+      uint8_t id_data = id + max_id * (i / max_id);
+
+      memset(buf, id_data, sizeof(buf));
+
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = buf;
+      data.len = sizeof(buf);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  return ret;
+
+test_fail:
+  if (fd >= 0)
+    close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: check_content
+ ****************************************************************************/
+
+static int check_content(uint16_t max_id)
+{
+  uint8_t rd_buf[44];
+  uint8_t buf[44];
+  int fd = -1;
+  int ret;
+  struct config_data_s data;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  for (uint16_t id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (uint16_t i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  return ret;
+
+test_fail:
+
+  if (fd >= 0)
+    close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_3sectors
+ * Description: Full round of GC over 3 sectors
+ ****************************************************************************/
+
+void test_nvs_gc_3sectors(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  const uint16_t max_id = 64;
+
+  /* 4096 * 2 / (44 + 4 + 16)  = 128, 129 write will trigger GC. */
+
+  const uint16_t max_writes = 129;
+
+  /* 4096 / (44 + 4 + 16) = 64, 129 + 64  write will trigger 2st GC. */
+
+  const uint16_t max_writes_2 = 129 + 64;
+  const uint16_t max_writes_3 = 129 + 64 + 64;
+  const uint16_t max_writes_4 = 129 + 64 + 64 + 64;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 1st GC */
+
+  ret = write_content(max_id, 0, max_writes);
+  if (ret < 0)
+    {
+      printf("%s:1st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:1st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:1st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:1st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:1st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 2nd GC */
+
+  ret = write_content(max_id, max_writes, max_writes_2);
+  if (ret < 0)
+    {
+      printf("%s:2st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:2st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:2st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:2st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:2st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 3rd GC */
+
+  ret = write_content(max_id, max_writes_2, max_writes_3);
+  if (ret < 0)
+    {
+      printf("%s:3st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:3st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:3st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:3st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:3st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 4th GC */
+
+  ret = write_content(max_id, max_writes_3, max_writes_4);
+  if (ret < 0)
+    {
+      printf("%s:4st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:4st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:4st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:4st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:4st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupted_sector_close_operation
+ ****************************************************************************/
+
+void test_nvs_corrupted_sector_close_operation(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  uint8_t rd_buf[512];
+  uint8_t wr_buf[512];
+  char key1[] = TEST_KEY1;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int loop_section;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct nvs_ate ate;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* lets simulate loss of close at the beginning of gc */
+
+  for (loop_section = 0;
+    loop_section <
+      CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 1;
+    loop_section++)
+    {
+      /* write valid data */
+
+      for (i = 0; i < 2 ; i++)
+        {
+          ret = write(mtd_fd, key1, sizeof(key1));
+          if (ret != sizeof(key1))
+            {
+              printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+
+          memset(wr_buf, 'A' + i, sizeof(wr_buf));
+          wr_buf[sizeof(wr_buf) - 1] = '\0';
+
+          ret = write(mtd_fd, wr_buf, sizeof(wr_buf));
+          if (ret != sizeof(wr_buf))
+            {
+              printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+        }
+
+      /* set unused flash to 0xff */
+
+      for (i = 2 * (sizeof(key1) + sizeof(wr_buf));
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16;
+        i++)
+        {
+          ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+          if (ret != sizeof(erase_value))
+            {
+              printf("%s:erase failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+        }
+
+      /* write ate 2 times */
+
+      fill_ate(&ate, key1, sizeof(wr_buf), sizeof(wr_buf) + sizeof(key1),
+        (loop_section ==
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2)
+        ? false : true);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      fill_ate(&ate, key1, sizeof(wr_buf), 0, true);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      /* write gc_done ate */
+
+      fill_gc_done_ate(&ate);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      if (loop_section ==
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2)
+        {
+          fill_corrupted_close_ate(&ate);
+        }
+      else
+        {
+          fill_close_ate(&ate,
+            CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16);
+        }
+
+      ret =  write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write close ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  if (memcmp(rd_buf, wr_buf, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:strncmp failed\n", __func__);
+      ret = -EACCES;
+      goto test_fail;
+    }
+
+  /* Ensure that the NVS is able to store new content. */
+
+  if (execute_long_pattern_write(TEST_KEY2) != 0)
+    {
+      printf("%s:write again failed\n", __func__);
+      ret = -EACCES;
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_full_sector
+ * Description: Test case when storage become full,
+ *              so only deletion is possible.
+ ****************************************************************************/
+
+void test_nvs_full_sector(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  uint16_t filling_id = 0;
+  uint16_t i;
+  uint16_t data_read;
+  int fd = -1;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  while (1)
+    {
+      sprintf(data.name, "k%04x", filling_id);
+      data.configdata = (FAR uint8_t *)&filling_id;
+      data.len = sizeof(filling_id);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret == -1 && errno == ENOSPC)
+        {
+          break;
+        }
+      else if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      filling_id++;
+    }
+
+  /* check whether can delete whatever from full storage */
+
+  sprintf(data.name, "k%04x", 1);
+  data.configdata = NULL;
+  data.len = 0;
+
+  ret = ioctl(fd, CFGDIOC_DELCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_DELCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  /* the last sector is full now, test re-initialization */
+
+  close(fd);
+  fd = -1;
+  mtdconfig_unregister();
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  sprintf(data.name, "k%04x", filling_id);
+  data.configdata = (FAR uint8_t *)&filling_id;
+  data.len = sizeof(filling_id);
+
+  ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  /* sanitycheck on NVS content */
+
+  for (i = 0; i <= filling_id; i++)
+    {
+      sprintf(data.name, "k%04x", i);
+      data.configdata = (FAR uint8_t *)&data_read;
+      data.len = sizeof(data_read);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+
+      if (i == 1)
+        {
+          if (ret != -1 || errno != ENOENT)
+            {
+              printf("%s:shouldn't found the entry: %d\n", __func__, i);
+              ret = -EIO;
+              goto test_fail;
+            }
+        }
+      else if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+      else
+        {
+          if (data_read != i)
+            {
+              printf("%s:read data %d \n", __func__, data_read);
+              printf("%s:read expected  %d\n", __func__, i);
+              printf("%s:read unexpected data: %d instead of %d\n",
+                __func__, data_read, i);
+              ret = -EIO;
+              goto test_fail;
+            }
+        }
+    }
+
+  close(fd);
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (fd >= 0)
+    close(fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_corrupt_close_ate
+ * Description: Test that garbage-collection can
+ *      recover all ate's even when the last ate, ie close_ate,
+ *      is corrupt. In this test the close_ate is set to point to the
+ *      last ate at -5. A valid ate is however present at -6.
+ *      Since the close_ate has an invalid crc8, the offset
+ *      should not be used and a recover of the
+ *      last ate should be done instead.
+ ****************************************************************************/
+
+void test_nvs_gc_corrupt_close_ate(struct mtdnvs_ctx_s *ctx)
+{
+  struct nvs_ate ate;
+  struct nvs_ate close_ate;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int ret;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct config_data_s data;
+  char rd_buf[50];
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* write valid data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret != strlen(TEST_KEY1) + 1)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1);
+  if (ret != strlen(TEST_DATA1) + 1)
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 6 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* Write valid ate at -6 */
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 5 * 16;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 2 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write invalid close ate, mark section 0 as closed */
+
+  fill_corrupted_close_ate(&close_ate);
+  ret = write(mtd_fd, &close_ate, sizeof(close_ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff in section 1 */
+
+  for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE -  16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write invalid close ate, mark section 1 as closed */
+
+  fill_corrupted_close_ate(&close_ate);
+  ret = write(mtd_fd, &close_ate, sizeof(close_ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:NVSIOC_READ unexpected failure: %d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, TEST_DATA1, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:unexpected value\n", __func__);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_corrupt_ate
+ * Description: Test that garbage-collection correctly handles corrupt ate's.
+ ****************************************************************************/
+
+void test_nvs_gc_corrupt_ate(struct mtdnvs_ctx_s *ctx)

Review Comment:
   add static for all function except main



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx-apps] XinStellaris commented on a diff in pull request #1288: testing:add MTD Fail-safe config-data tests

Posted by GitBox <gi...@apache.org>.
XinStellaris commented on code in PR #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288#discussion_r959427669


##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2039 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;           /* data id */
+  uint16_t offset;       /* data offset within sector */
+  uint16_t len;          /* data len within sector */
+  uint16_t key_len;      /* key string len */
+  uint8_t  part;         /* part of a multipart data - future extension */
+  uint8_t  crc8;         /* crc8 check of the ate entry */
+  uint8_t  expired;      /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved[3];  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t nvs_fnv_hash(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= *input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = nvs_fnv_hash((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdconfig_fs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  ret = ioctl(fd, MTDIOC_BULKERASE, NULL);
+  if (ret < 0)
+    {
+      printf("%s:clear failed, ret=%d\n", __func__, ret);
+      return -errno;
+    }
+
+  close(fd);
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      return ret;
+    }
+
+  return ret;
+}
+
+extern int find_mtddriver(FAR const char *pathname,
+                        FAR struct inode **ppinode);
+
+/****************************************************************************
+ * Name: setup
+ ****************************************************************************/
+
+static int setup(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  FAR struct inode *sys_node;
+
+  ret = find_mtddriver(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME,
+    &sys_node);
+  if (ret < 0)
+    {
+      printf("ERROR: open %s failed: %d\n",
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, ret);
+      return -errno;
+    }
+
+  ret = mtdconfig_register(sys_node->u.i_mtd);
+  if (ret < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, ret);
+      return ret;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_mount
+ ****************************************************************************/
+
+void test_nvs_mount(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: execute_long_pattern_write
+ ****************************************************************************/
+
+static int execute_long_pattern_write(const char *key)
+{
+  struct config_data_s data;
+  int i;
+  int fd;
+  int ret;
+  uint8_t rd_buf[512];
+  uint8_t wr_buf[512];
+  char pattern[4];
+
+  pattern[0] = 0xde;
+  pattern[1] = 0xad;
+  pattern[2] = 0xbe;
+  pattern[3] = 0xef;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  strlcpy(data.name, key, sizeof(data.name));
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != -1 || errno != ENOENT)
+    {
+      printf("%s:CFGDIOC_GETCONFIG unexpected failure: %d\n",
+        __func__, ret);
+      goto err_fd;
+    }
+
+  for (i = 0; i < sizeof(wr_buf); i += sizeof(pattern))
+    {
+      memcpy(wr_buf + i, pattern, sizeof(pattern));
+    }
+
+  data.configdata = wr_buf;
+  data.len = sizeof(wr_buf);
+
+  ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+  if (memcmp(wr_buf, rd_buf, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:RD buff should be equal to the WR buff\n", __func__);
+      ret = -EIO;
+      goto err_fd;
+    }
+
+err_fd:
+  close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_write
+ ****************************************************************************/
+
+void test_nvs_write(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_register failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = execute_long_pattern_write(TEST_KEY1);
+  if (ret < 0)
+    {
+      printf("%s:execute_long_pattern_write failed, ret=%d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupt_expire
+ * Description: Test that startup correctly handles invalid expire.
+ ****************************************************************************/
+
+void test_nvs_corrupt_expire(struct mtdnvs_ctx_s *ctx)
+{
+  struct nvs_ate ate;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int ret;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct config_data_s data;
+  char rd_buf[50];
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* write valid data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write valid data again, simulate overwrite data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA2, strlen(TEST_DATA2) + 1);
+  if (ret < 0)
+    {
+      printf("%s:write data2 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = 2 * (strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2);
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* Write ate */
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA2) + 1,
+    strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write close ate, mark section 0 as closed */
+
+  fill_close_ate(&ate,
+    CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write close ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_FIRSTCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_FIRSTCONFIG unexpected failure: %d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, TEST_DATA2, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:unexpected value\n", __func__);
+      goto test_fail;
+    }
+
+  ret = ioctl(nvs_fd, CFGDIOC_NEXTCONFIG, &data);
+  if (ret != -1 || errno != ENOENT)
+    {
+      printf("%s:CFGDIOC_NEXTCONFIG should return ENOENT, but: %d\n",
+        __func__, ret);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupted_write
+ ****************************************************************************/
+
+void test_nvs_corrupted_write(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  char rd_buf[512];
+  char wr_buf_1[] = TEST_DATA1;
+  char wr_buf_2[] = TEST_DATA2;
+  char key1[] = TEST_KEY1;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct nvs_ate ate;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* lets simulate loss of part of data */
+
+  /* write valid data */
+
+  ret = write(mtd_fd, key1, sizeof(key1));
+  if (ret != sizeof(key1))
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, wr_buf_1, sizeof(wr_buf_1));
+  if (ret != sizeof(wr_buf_1))
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* power loss occurs after we write data */
+
+  ret = write(mtd_fd, key1, sizeof(key1));
+  if (ret != sizeof(key1))
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, wr_buf_2, sizeof(wr_buf_1));
+  if (ret != sizeof(wr_buf_2))
+    {
+      printf("%s:write data2 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = 2 * (sizeof(key1) + sizeof(wr_buf_1));
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 3 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write ate */
+
+  fill_ate(&ate, key1, sizeof(wr_buf_1), 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  /* now start up */
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret < 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, wr_buf_1, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:failed, RD buff should be equal to the first WR buff "
+        "because subsequent write operation has failed\n",
+        __func__);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+  if (nvs_fd > 0)
+    {
+      close(nvs_fd);
+    }
+
+  if (mtd_fd > 0)
+    {
+      close(mtd_fd);
+    }
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc
+ ****************************************************************************/
+
+void test_nvs_gc(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  int fd = -1;
+  uint8_t buf[44];
+  uint8_t rd_buf[44];
+  struct config_data_s data;
+  uint16_t i;
+  uint16_t id;
+  const uint16_t max_id = 10;
+
+  /* 4096 * 2 / (44 + 4 + 16)  = 128, 129 write will trigger GC. */
+
+  const uint16_t max_writes = 129;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  for (i = 0; i < max_writes; i++)
+    {
+      id = (i % max_id);
+      uint8_t id_data = id + max_id * (i / max_id);
+
+      memset(buf, id_data, sizeof(buf));
+
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = buf;
+      data.len = sizeof(buf);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+    }
+
+  for (id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  fd = -1;
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  for (id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+  if (fd >= 0)
+    close(fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: write_content
+ ****************************************************************************/
+
+static int write_content(uint16_t max_id, uint16_t begin, uint16_t end)
+{
+  uint8_t buf[44];
+  int fd = -1;
+  int ret;
+  struct config_data_s data;
+  uint16_t i;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  for (i = begin; i < end; i++)
+    {
+      uint8_t id = (i % max_id);
+      uint8_t id_data = id + max_id * (i / max_id);
+
+      memset(buf, id_data, sizeof(buf));
+
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = buf;
+      data.len = sizeof(buf);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  return ret;
+
+test_fail:
+  if (fd >= 0)
+    close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: check_content
+ ****************************************************************************/
+
+static int check_content(uint16_t max_id)
+{
+  uint8_t rd_buf[44];
+  uint8_t buf[44];
+  int fd = -1;
+  int ret;
+  struct config_data_s data;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -errno;
+    }
+
+  for (uint16_t id = 0; id < max_id; id++)
+    {
+      /* 4 byte key */
+
+      sprintf(data.name, "k%02d", id);
+      data.configdata = rd_buf;
+      data.len = sizeof(rd_buf);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+      if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      for (uint16_t i = 0; i < sizeof(rd_buf); i++)
+        {
+          rd_buf[i] = rd_buf[i] % max_id;
+          buf[i] = id;
+        }
+
+      if (memcmp(buf, rd_buf, sizeof(rd_buf)) != 0)
+        {
+          printf("RD buff should be equal to the WR buff\n");
+          goto test_fail;
+        }
+    }
+
+  close(fd);
+  return ret;
+
+test_fail:
+
+  if (fd >= 0)
+    close(fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_3sectors
+ * Description: Full round of GC over 3 sectors
+ ****************************************************************************/
+
+void test_nvs_gc_3sectors(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  const uint16_t max_id = 64;
+
+  /* 4096 * 2 / (44 + 4 + 16)  = 128, 129 write will trigger GC. */
+
+  const uint16_t max_writes = 129;
+
+  /* 4096 / (44 + 4 + 16) = 64, 129 + 64  write will trigger 2st GC. */
+
+  const uint16_t max_writes_2 = 129 + 64;
+  const uint16_t max_writes_3 = 129 + 64 + 64;
+  const uint16_t max_writes_4 = 129 + 64 + 64 + 64;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 1st GC */
+
+  ret = write_content(max_id, 0, max_writes);
+  if (ret < 0)
+    {
+      printf("%s:1st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:1st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:1st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:1st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:1st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 2nd GC */
+
+  ret = write_content(max_id, max_writes, max_writes_2);
+  if (ret < 0)
+    {
+      printf("%s:2st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:2st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:2st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:2st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:2st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 3rd GC */
+
+  ret = write_content(max_id, max_writes_2, max_writes_3);
+  if (ret < 0)
+    {
+      printf("%s:3st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:3st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:3st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:3st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:3st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* Trigger 4th GC */
+
+  ret = write_content(max_id, max_writes_3, max_writes_4);
+  if (ret < 0)
+    {
+      printf("%s:4st GC write failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:4st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:4st mtdconfig_unregister failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:4st setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = check_content(max_id);
+  if (ret < 0)
+    {
+      printf("%s:4st GC check failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_corrupted_sector_close_operation
+ ****************************************************************************/
+
+void test_nvs_corrupted_sector_close_operation(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  uint8_t rd_buf[512];
+  uint8_t wr_buf[512];
+  char key1[] = TEST_KEY1;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int loop_section;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct nvs_ate ate;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* lets simulate loss of close at the beginning of gc */
+
+  for (loop_section = 0;
+    loop_section <
+      CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 1;
+    loop_section++)
+    {
+      /* write valid data */
+
+      for (i = 0; i < 2 ; i++)
+        {
+          ret = write(mtd_fd, key1, sizeof(key1));
+          if (ret != sizeof(key1))
+            {
+              printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+
+          memset(wr_buf, 'A' + i, sizeof(wr_buf));
+          wr_buf[sizeof(wr_buf) - 1] = '\0';
+
+          ret = write(mtd_fd, wr_buf, sizeof(wr_buf));
+          if (ret != sizeof(wr_buf))
+            {
+              printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+        }
+
+      /* set unused flash to 0xff */
+
+      for (i = 2 * (sizeof(key1) + sizeof(wr_buf));
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16;
+        i++)
+        {
+          ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+          if (ret != sizeof(erase_value))
+            {
+              printf("%s:erase failed, ret=%d\n", __func__, ret);
+              goto test_fail;
+            }
+        }
+
+      /* write ate 2 times */
+
+      fill_ate(&ate, key1, sizeof(wr_buf), sizeof(wr_buf) + sizeof(key1),
+        (loop_section ==
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2)
+        ? false : true);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      fill_ate(&ate, key1, sizeof(wr_buf), 0, true);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      /* write gc_done ate */
+
+      fill_gc_done_ate(&ate);
+      ret = write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+
+      if (loop_section ==
+        CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_COUNT - 2)
+        {
+          fill_corrupted_close_ate(&ate);
+        }
+      else
+        {
+          fill_close_ate(&ate,
+            CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 4 * 16);
+        }
+
+      ret =  write(mtd_fd, &ate, sizeof(ate));
+      if (ret != sizeof(ate))
+        {
+          printf("%s:write close ate failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  if (memcmp(rd_buf, wr_buf, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:strncmp failed\n", __func__);
+      ret = -EACCES;
+      goto test_fail;
+    }
+
+  /* Ensure that the NVS is able to store new content. */
+
+  if (execute_long_pattern_write(TEST_KEY2) != 0)
+    {
+      printf("%s:write again failed\n", __func__);
+      ret = -EACCES;
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_full_sector
+ * Description: Test case when storage become full,
+ *              so only deletion is possible.
+ ****************************************************************************/
+
+void test_nvs_full_sector(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  uint16_t filling_id = 0;
+  uint16_t i;
+  uint16_t data_read;
+  int fd = -1;
+  struct config_data_s data;
+
+  printf("%s: test begin\n", __func__);
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  while (1)
+    {
+      sprintf(data.name, "k%04x", filling_id);
+      data.configdata = (FAR uint8_t *)&filling_id;
+      data.len = sizeof(filling_id);
+
+      ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+      if (ret == -1 && errno == ENOSPC)
+        {
+          break;
+        }
+      else if (ret != 0)
+        {
+          printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+
+      filling_id++;
+    }
+
+  /* check whether can delete whatever from full storage */
+
+  sprintf(data.name, "k%04x", 1);
+  data.configdata = NULL;
+  data.len = 0;
+
+  ret = ioctl(fd, CFGDIOC_DELCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_DELCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  /* the last sector is full now, test re-initialization */
+
+  close(fd);
+  fd = -1;
+  mtdconfig_unregister();
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      goto test_fail;
+    }
+
+  sprintf(data.name, "k%04x", filling_id);
+  data.configdata = (FAR uint8_t *)&filling_id;
+  data.len = sizeof(filling_id);
+
+  ret = ioctl(fd, CFGDIOC_SETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:CFGDIOC_SETCONFIG failed, ret=%d\n", __func__, ret);
+      ret = -EIO;
+      goto test_fail;
+    }
+
+  /* sanitycheck on NVS content */
+
+  for (i = 0; i <= filling_id; i++)
+    {
+      sprintf(data.name, "k%04x", i);
+      data.configdata = (FAR uint8_t *)&data_read;
+      data.len = sizeof(data_read);
+
+      ret = ioctl(fd, CFGDIOC_GETCONFIG, &data);
+
+      if (i == 1)
+        {
+          if (ret != -1 || errno != ENOENT)
+            {
+              printf("%s:shouldn't found the entry: %d\n", __func__, i);
+              ret = -EIO;
+              goto test_fail;
+            }
+        }
+      else if (ret != 0)
+        {
+          printf("%s:CFGDIOC_GETCONFIG failed, ret=%d\n", __func__, ret);
+          ret = -EIO;
+          goto test_fail;
+        }
+      else
+        {
+          if (data_read != i)
+            {
+              printf("%s:read data %d \n", __func__, data_read);
+              printf("%s:read expected  %d\n", __func__, i);
+              printf("%s:read unexpected data: %d instead of %d\n",
+                __func__, data_read, i);
+              ret = -EIO;
+              goto test_fail;
+            }
+        }
+    }
+
+  close(fd);
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (fd >= 0)
+    close(fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_corrupt_close_ate
+ * Description: Test that garbage-collection can
+ *      recover all ate's even when the last ate, ie close_ate,
+ *      is corrupt. In this test the close_ate is set to point to the
+ *      last ate at -5. A valid ate is however present at -6.
+ *      Since the close_ate has an invalid crc8, the offset
+ *      should not be used and a recover of the
+ *      last ate should be done instead.
+ ****************************************************************************/
+
+void test_nvs_gc_corrupt_close_ate(struct mtdnvs_ctx_s *ctx)
+{
+  struct nvs_ate ate;
+  struct nvs_ate close_ate;
+  int mtd_fd = -1;
+  int nvs_fd = -1;
+  int ret;
+  int i;
+  uint8_t erase_value = CONFIG_MTD_CONFIG_ERASEDVALUE;
+  struct config_data_s data;
+  char rd_buf[50];
+
+  printf("%s: test begin\n", __func__);
+
+  mtd_fd = open(CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_MOUNTPT_NAME, O_RDWR);
+  if (mtd_fd < 0)
+    {
+      printf("%s:mtdnvs_register failed, ret=%d\n", __func__, mtd_fd);
+      goto test_fail;
+    }
+
+  /* write valid data */
+
+  ret = write(mtd_fd, TEST_KEY1, strlen(TEST_KEY1) + 1);
+  if (ret != strlen(TEST_KEY1) + 1)
+    {
+      printf("%s:write key1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  ret = write(mtd_fd, TEST_DATA1, strlen(TEST_DATA1) + 1);
+  if (ret != strlen(TEST_DATA1) + 1)
+    {
+      printf("%s:write data1 failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = strlen(TEST_KEY1) + strlen(TEST_DATA1) + 2;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 6 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* Write valid ate at -6 */
+
+  fill_ate(&ate, TEST_KEY1, strlen(TEST_DATA1) + 1, 0, false);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff */
+
+  for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 5 * 16;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE - 2 * 16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write gc_done ate */
+
+  fill_gc_done_ate(&ate);
+  ret = write(mtd_fd, &ate, sizeof(ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* write invalid close ate, mark section 0 as closed */
+
+  fill_corrupted_close_ate(&close_ate);
+  ret = write(mtd_fd, &close_ate, sizeof(close_ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  /* set unused flash to 0xff in section 1 */
+
+  for (i = CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE;
+        i < CONFIG_TESTING_MTD_CONFIG_FAIL_SAFE_FLASH_SECTION_SIZE -  16;
+        i++)
+    {
+      ret = write(mtd_fd, &erase_value, sizeof(erase_value));
+      if (ret != sizeof(erase_value))
+        {
+          printf("%s:erase failed, ret=%d\n", __func__, ret);
+          goto test_fail;
+        }
+    }
+
+  /* write invalid close ate, mark section 1 as closed */
+
+  fill_corrupted_close_ate(&close_ate);
+  ret = write(mtd_fd, &close_ate, sizeof(close_ate));
+  if (ret != sizeof(ate))
+    {
+      printf("%s:write gc_done ate failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  close(mtd_fd);
+  mtd_fd = -1;
+
+  ret = setup(ctx);
+  if (ret < 0)
+    {
+      printf("%s:setup failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  nvs_fd = open("/dev/config", 0);
+  if (nvs_fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, nvs_fd);
+      goto test_fail;
+    }
+
+  strlcpy(data.name, TEST_KEY1, sizeof(data.name));
+  data.configdata = (FAR uint8_t *)rd_buf;
+  data.len = sizeof(rd_buf);
+
+  ret = ioctl(nvs_fd, CFGDIOC_GETCONFIG, &data);
+  if (ret != 0)
+    {
+      printf("%s:NVSIOC_READ unexpected failure: %d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  if (strncmp(rd_buf, TEST_DATA1, sizeof(rd_buf)) != 0)
+    {
+      printf("%s:unexpected value\n", __func__);
+      goto test_fail;
+    }
+
+  close(nvs_fd);
+  nvs_fd = -1;
+
+  /* at the end of test, erase all blocks */
+
+  ret = teardown();
+  if (ret < 0)
+    {
+      printf("%s:teardown failed, ret=%d\n", __func__, ret);
+      goto test_fail;
+    }
+
+  printf("%s: success\n", __func__);
+
+  return;
+
+test_fail:
+
+  if (nvs_fd >= 0)
+    close(nvs_fd);
+
+  if (mtd_fd >= 0)
+    close(mtd_fd);
+
+  printf("%s: failed\n", __func__);
+
+  return;
+}
+
+/****************************************************************************
+ * Name: test_nvs_gc_corrupt_ate
+ * Description: Test that garbage-collection correctly handles corrupt ate's.
+ ****************************************************************************/
+
+void test_nvs_gc_corrupt_ate(struct mtdnvs_ctx_s *ctx)

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on a diff in pull request #1288: testing:add MTD Fail-safe config-data tests

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288#discussion_r956598043


##########
testing/mtd_config_fs/Makefile:
##########
@@ -0,0 +1,34 @@
+############################################################################
+# apps/testing/mtd_nvs/Makefile

Review Comment:
   ```suggestion
   # apps/testing/mtd_config_fs/Makefile
   ```



##########
testing/mtd_config_fs/Kconfig:
##########
@@ -0,0 +1,49 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config TESTING_FAILSAFE_MTD_CONFIG

Review Comment:
   let's change prefix from TESTING_FAILSAFE_MTD_CONFIG_ to TESTING_MTD_CONFIG_FAILSAFE_



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;         /* data id */
+  uint16_t offset;     /* data offset within sector */
+  uint16_t len;        /* data len within sector */
+  uint16_t key_len;    /* key string len */
+  uint8_t  part;       /* part of a multipart data - future extension */
+  uint8_t  crc8;       /* crc8 check of the ate entry */
+  uint8_t  expired;    /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved;   /* for future extension */
+  uint16_t reserved2;  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t fnv_hash_32(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= (uint32_t)*input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdnvs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -ENOENT;
+    }
+
+  ret = ioctl(fd, MTDIOC_BULKERASE, NULL);
+  if (ret < 0)
+    {
+      printf("%s:clear failed, ret=%d\n", __func__, ret);
+      return -EACCES;
+    }
+
+  close(fd);
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdnvs_unregister failed, ret=%d\n", __func__, ret);
+      return ret;
+    }
+
+  return ret;
+}
+
+extern int find_mtddriver(FAR const char *pathname,
+                        FAR struct inode **ppinode);
+
+/****************************************************************************
+ * Name: setup
+ ****************************************************************************/
+
+static int setup(struct mtdnvs_ctx_s *ctx)
+{
+  int ret;
+  FAR struct inode *sys_node;
+
+  ret = find_mtddriver(CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME,
+    &sys_node);
+  if (ret < 0)
+    {
+      printf("ERROR: open %s failed: %d\n",
+        CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME, ret);
+      return -ENOENT;

Review Comment:
   ```suggestion
         return ret;
   ```



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;         /* data id */
+  uint16_t offset;     /* data offset within sector */
+  uint16_t len;        /* data len within sector */
+  uint16_t key_len;    /* key string len */
+  uint8_t  part;       /* part of a multipart data - future extension */
+  uint8_t  crc8;       /* crc8 check of the ate entry */
+  uint8_t  expired;    /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved;   /* for future extension */
+  uint16_t reserved2;  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t fnv_hash_32(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= (uint32_t)*input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdnvs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -ENOENT;

Review Comment:
   ```suggestion
         return -errno;
   ```



##########
testing/mtd_config_fs/Kconfig:
##########
@@ -0,0 +1,49 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config TESTING_FAILSAFE_MTD_CONFIG
+	tristate "MTD Config fail-safe storage test"
+	default n

Review Comment:
   depends on MTD_CONFIG_FAIL_SAFE



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;         /* data id */
+  uint16_t offset;     /* data offset within sector */
+  uint16_t len;        /* data len within sector */
+  uint16_t key_len;    /* key string len */
+  uint8_t  part;       /* part of a multipart data - future extension */
+  uint8_t  crc8;       /* crc8 check of the ate entry */
+  uint8_t  expired;    /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved;   /* for future extension */
+  uint16_t reserved2;  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t fnv_hash_32(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= (uint32_t)*input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdnvs_test [OPTION [ARG]] ...\n");

Review Comment:
   ```suggestion
     printf("Usage : mtdconfig_fs_test [OPTION [ARG]] ...\n");
   ```



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;         /* data id */
+  uint16_t offset;     /* data offset within sector */
+  uint16_t len;        /* data len within sector */
+  uint16_t key_len;    /* key string len */
+  uint8_t  part;       /* part of a multipart data - future extension */
+  uint8_t  crc8;       /* crc8 check of the ate entry */
+  uint8_t  expired;    /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved;   /* for future extension */
+  uint16_t reserved2;  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t fnv_hash_32(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= (uint32_t)*input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdnvs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -ENOENT;
+    }
+
+  ret = ioctl(fd, MTDIOC_BULKERASE, NULL);
+  if (ret < 0)
+    {
+      printf("%s:clear failed, ret=%d\n", __func__, ret);
+      return -EACCES;

Review Comment:
   ```suggestion
         return -errno;
   ```



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/mtd/configdata.h>
+
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/crc8.h>
+#include <debug.h>
+#include <assert.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define TEST_KEY1       "testkey1"
+#define TEST_KEY2       "testkey2"
+#define TEST_DATA1      "abc"
+#define TEST_DATA2      "def"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Allocation Table Entry */
+
+begin_packed_struct struct nvs_ate
+{
+  uint32_t id;         /* data id */
+  uint16_t offset;     /* data offset within sector */
+  uint16_t len;        /* data len within sector */
+  uint16_t key_len;    /* key string len */
+  uint8_t  part;       /* part of a multipart data - future extension */
+  uint8_t  crc8;       /* crc8 check of the ate entry */
+  uint8_t  expired;    /* 0xFF-newest entry, others-old entry */
+  uint8_t  reserved;   /* for future extension */
+  uint16_t reserved2;  /* for future extension */
+} end_packed_struct;
+
+/* Pre-allocated simulated flash */
+
+struct mtdnvs_ctx_s
+{
+  char mountdir[CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_MAXNAME];
+  struct mallinfo mmbefore;
+  struct mallinfo mmprevious;
+  struct mallinfo mmafter;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fnv_hash_32
+ ****************************************************************************/
+
+static uint32_t fnv_hash_32(FAR const uint8_t *input, uint32_t len)
+{
+  uint32_t i = 0;
+  uint32_t hval = 2166136261;
+
+  /* FNV-1 hash each octet in the buffer */
+
+  while (i++ < len)
+    {
+      /* multiply by the 32 bit FNV magic prime mod 2^32 */
+
+      hval *= 0x01000193;
+
+      /* xor the bottom with the current octet */
+
+      hval ^= (uint32_t)*input++;
+    }
+
+  return hval;
+}
+
+/****************************************************************************
+ * Name: fill_crc8_update
+ ****************************************************************************/
+
+static void fill_crc8_update(FAR struct nvs_ate *entry)
+{
+  uint8_t ate_crc;
+
+  ate_crc = crc8part((FAR const uint8_t *)entry,
+                     offsetof(struct nvs_ate, crc8), 0xff);
+  entry->crc8 = ate_crc;
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_corrupted_close_ate(FAR struct nvs_ate *close_ate)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+}
+
+/****************************************************************************
+ * Name: fill_close_ate
+ ****************************************************************************/
+
+static void fill_close_ate(FAR struct nvs_ate *close_ate, int offset)
+{
+  memset((FAR void *)close_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  close_ate->id = 0xffffffff;
+  close_ate->len = 0U;
+  close_ate->offset = offset;
+  fill_crc8_update(close_ate);
+}
+
+/****************************************************************************
+ * Name: fill_gc_done_ate
+ ****************************************************************************/
+
+static void fill_gc_done_ate(FAR struct nvs_ate *gc_done_ate)
+{
+  memset((FAR void *)gc_done_ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  gc_done_ate->id = 0xffffffff;
+  gc_done_ate->len = 0U;
+  fill_crc8_update(gc_done_ate);
+}
+
+/****************************************************************************
+ * Name: fill_ate
+ ****************************************************************************/
+
+static void fill_ate(FAR struct nvs_ate *ate, FAR const char *key,
+                    uint16_t len, uint16_t offset, bool expired)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+  fill_crc8_update(ate);
+  ate->expired = expired ? 0x7f : 0xff;
+}
+
+/****************************************************************************
+ * Name: fill_corrupted_ate
+ ****************************************************************************/
+
+static void fill_corrupted_ate(FAR struct nvs_ate *ate,
+                FAR const char *key, uint16_t len, uint16_t offset)
+{
+  memset((FAR void *)ate, CONFIG_MTD_CONFIG_ERASEDVALUE,
+    sizeof(struct nvs_ate));
+  ate->id = fnv_hash_32((FAR const uint8_t *)key, strlen(key) + 1)
+    % 0xfffffffd + 1;
+  ate->len = len;
+  ate->offset = offset;
+  ate->key_len = strlen(key) + 1;
+}
+
+/****************************************************************************
+ * Name: mtdnvs_showmemusage
+ ****************************************************************************/
+
+static void mtdnvs_showmemusage(struct mallinfo *mmbefore,
+                                struct mallinfo *mmafter)
+{
+  printf("VARIABLE  BEFORE   AFTER    DELTA\n");
+  printf("======== ======== ======== ========\n");
+  printf("arena    %8x %8x %8x\n", mmbefore->arena   , mmafter->arena,
+                                   mmafter->arena    - mmbefore->arena);
+  printf("ordblks  %8d %8d %8d\n", mmbefore->ordblks , mmafter->ordblks,
+                                   mmafter->ordblks  - mmbefore->ordblks);
+  printf("mxordblk %8x %8x %8x\n", mmbefore->mxordblk, mmafter->mxordblk,
+                                   mmafter->mxordblk - mmbefore->mxordblk);
+  printf("uordblks %8x %8x %8x\n", mmbefore->uordblks, mmafter->uordblks,
+                                   mmafter->uordblks - mmbefore->uordblks);
+  printf("fordblks %8x %8x %8x\n", mmbefore->fordblks, mmafter->fordblks,
+                                   mmafter->fordblks - mmbefore->fordblks);
+}
+
+/****************************************************************************
+ * Name: mtdnvs_endmemusage
+ ****************************************************************************/
+
+static void mtdnvs_endmemusage(FAR struct mtdnvs_ctx_s *ctx)
+{
+  ctx->mmafter = mallinfo();
+
+  printf("\nFinal memory usage:\n");
+  mtdnvs_showmemusage(&ctx->mmbefore, &ctx->mmafter);
+}
+
+/****************************************************************************
+ * Name: show_useage
+ ****************************************************************************/
+
+static void show_useage(void)
+{
+  printf("Usage : mtdnvs_test [OPTION [ARG]] ...\n");
+  printf("-h    show this help statement\n");
+  printf("-m    mount point to be tested e.g. [%s]\n",
+          CONFIG_TESTING_FAILSAFE_MTD_CONFIG_MOUNTPT_NAME);
+}
+
+/****************************************************************************
+ * Name: teardown
+ ****************************************************************************/
+
+static int teardown(void)
+{
+  int fd;
+  int ret;
+
+  fd = open("/dev/config", 0);
+  if (fd < 0)
+    {
+      printf("%s:open failed, ret=%d\n", __func__, fd);
+      return -ENOENT;
+    }
+
+  ret = ioctl(fd, MTDIOC_BULKERASE, NULL);
+  if (ret < 0)
+    {
+      printf("%s:clear failed, ret=%d\n", __func__, ret);
+      return -EACCES;
+    }
+
+  close(fd);
+
+  ret = mtdconfig_unregister();
+  if (ret < 0)
+    {
+      printf("%s:mtdnvs_unregister failed, ret=%d\n", __func__, ret);

Review Comment:
   ```suggestion
         printf("%s:mtdconfig_unregister failed, ret=%d\n", __func__, ret);
   ```



##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>

Review Comment:
   don't need include mtd.h?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx-apps] XinStellaris commented on a diff in pull request #1288: testing:add MTD Fail-safe config-data tests

Posted by GitBox <gi...@apache.org>.
XinStellaris commented on code in PR #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288#discussion_r958241651


##########
testing/mtd_config_fs/mtd_config_fs_test_main.c:
##########
@@ -0,0 +1,2040 @@
+/****************************************************************************
+ * apps/testing/mtd_config_fs/mtd_config_fs_test_main.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/mtd/mtd.h>

Review Comment:
   it is needed , cause we need  MTDIOC_BULKERASE to erase all for testing. And MTDIOC_BULKERASE is defined in <nuttx/mtd/mtd.h>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx-apps] xiaoxiang781216 merged pull request #1288: testing:add MTD Fail-safe config-data tests

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #1288:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1288


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org