Repository: zephyrproject-rtos/zephyr (C)

Issue:
fs: fcb: Add option to disable CRC for FCB entries
**Is your enhancement proposal related to a problem? Please describe.**
The `fcb_append_finish()` takes a long time to process as it is reading back the content written to flash to calculate the CRC for the FCB entry. This severely impacts the throughput when writing to the FCB. 

**Describe the solution you'd like**
For some applications, the CRC check is not needed, for instance if this is done at a higher level, or there are other mechanisms for error detection in place. Disabling the CRC, hereby removing the need to read back the written content in `fcb_append_finish()`, has shown to improve the write throughput significantly for my application, increasing the write throughput by around 75% (675 kb/s -> 1180 kb/s).  

**Describe alternatives you've considered**
An alternative solution could be to calculate the CRC when writing the content to flash. This would remove the need to read back the content in `crc_append_finish()`, but would still spend resources computing the CRC. The FCB does not have a function for writing content data, but is instead relying on e.g. `flash_area_write()` for this.  The API would therefore need to change for this, e.g. by adding a dedicated `fcb_content_write()` function that can calculate the CRC while writing to flash. As a  side note, this would also enable error checking in the write-phase, if the underlying write driver does not support it, as the pre-calculated CRC can be compared with calculating the CRC of the flash content. This change would require more work, API changes, and it would also does not increase the write throughput as much as the proposed solution (a solution calculating the CRC based on the written data resulted in a write throughput of 1080 kb/s). Therefore it is left as a suggestion for further improvements to the FCB.

**Additional context**
None




Fix (patch):
diff --git a/doc/releases/release-notes-3.3.rst b/doc/releases/release-notes-3.3.rst
index 0a5a5caf7e8bd..3e35a11e7ad1f 100644
--- a/doc/releases/release-notes-3.3.rst
+++ b/doc/releases/release-notes-3.3.rst
@@ -504,6 +504,9 @@ Libraries / Subsystems
   * Added new API call `fs_mkfs`.
   * Added new sample `samples/subsys/fs/format`.
   * FAT FS driver has been updated to version 0.15 w/patch1.
+  * Added the option to disable CRC checking in :ref:`fcb_api` by enabling the
+    Kconfig option :kconfig:option:`CONFIG_FCB_ALLOW_FIXED_ENDMARKER`
+    and setting the `FCB_FLAGS_CRC_DISABLED` flag in the :c:struct:`fcb` struct.
 
 * Management
 
diff --git a/include/zephyr/fs/fcb.h b/include/zephyr/fs/fcb.h
index ff620ea73f50b..9e9cddf007953 100644
--- a/include/zephyr/fs/fcb.h
+++ b/include/zephyr/fs/fcb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020 Nordic Semiconductor ASA
+ * Copyright (c) 2017-2023 Nordic Semiconductor ASA
  * Copyright (c) 2015 Runtime Inc
  *
  * SPDX-License-Identifier: Apache-2.0
@@ -14,6 +14,7 @@
 #include <limits.h>
 
 #include <zephyr/storage/flash_map.h>
+#include <zephyr/sys/util_macro.h>
 
 #include <zephyr/kernel.h>
 
@@ -76,6 +77,11 @@ struct fcb_entry_ctx {
 	/**< Flash area where the entry is placed */
 };
 
+/**
+ * @brief Flag to disable CRC for the fcb_entries in flash.
+ */
+#define FCB_FLAGS_CRC_DISABLED BIT(0)
+
 /**
  * @brief FCB instance structure
  *
@@ -129,6 +135,10 @@ struct fcb {
 	/**< The value flash takes when it is erased. This is read from
 	 * flash parameters and initialized upon call to fcb_init.
 	 */
+#ifdef CONFIG_FCB_ALLOW_FIXED_ENDMARKER
+	const uint8_t f_flags;
+	/**< Flags for configuring the FCB. */
+#endif
 };
 
 /**
diff --git a/subsys/fs/fcb/Kconfig b/subsys/fs/fcb/Kconfig
index c6605bce9195b..50ddb7009b254 100644
--- a/subsys/fs/fcb/Kconfig
+++ b/subsys/fs/fcb/Kconfig
@@ -1,6 +1,6 @@
 # Flash Circular Buffer module
 
-# Copyright (c) 2017 Nordic Semiconductor ASA
+# Copyright (c) 2017-2023 Nordic Semiconductor ASA
 # SPDX-License-Identifier: Apache-2.0
 
 #
@@ -13,3 +13,13 @@ config FCB
 	select CRC
 	help
 	  Enable support of Flash Circular Buffer.
+
+if FCB
+
+config FCB_ALLOW_FIXED_ENDMARKER
+	bool "Allow FCB instances to have a fixed endmarker"
+	help
+	  This allows the FCB instances to disable CRC checks in
+	  favor of increased write throughput.
+
+endif
diff --git a/subsys/fs/fcb/fcb_append.c b/subsys/fs/fcb/fcb_append.c
index 681a5825d381f..2630eb9263e32 100644
--- a/subsys/fs/fcb/fcb_append.c
+++ b/subsys/fs/fcb/fcb_append.c
@@ -121,18 +121,18 @@ int
 fcb_append_finish(struct fcb *fcb, struct fcb_entry *loc)
 {
 	int rc;
-	uint8_t crc8[fcb->f_align];
+	uint8_t em[fcb->f_align];
 	off_t off;
 
-	(void)memset(crc8, 0xFF, sizeof(crc8));
+	(void)memset(em, 0xFF, sizeof(em));
 
-	rc = fcb_elem_crc8(fcb, loc, &crc8[0]);
+	rc = fcb_elem_endmarker(fcb, loc, &em[0]);
 	if (rc) {
 		return rc;
 	}
 	off = loc->fe_data_off + fcb_len_in_flash(fcb, loc->fe_data_len);
 
-	rc = fcb_flash_write(fcb, loc->fe_sector, off, crc8, fcb->f_align);
+	rc = fcb_flash_write(fcb, loc->fe_sector, off, em, fcb->f_align);
 	if (rc) {
 		return -EIO;
 	}
diff --git a/subsys/fs/fcb/fcb_elem_info.c b/subsys/fs/fcb/fcb_elem_info.c
index 11d8c9cdd04a2..3bb6031ec2127 100644
--- a/subsys/fs/fcb/fcb_elem_info.c
+++ b/subsys/fs/fcb/fcb_elem_info.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Nordic Semiconductor ASA
+ * Copyright (c) 2017-2023 Nordic Semiconductor ASA
  * Copyright (c) 2015 Runtime Inc
  *
  * SPDX-License-Identifier: Apache-2.0
@@ -10,12 +10,14 @@
 #include <zephyr/fs/fcb.h>
 #include "fcb_priv.h"
 
+#define FCB_FIXED_ENDMARKER 0xab
+
 /*
  * Given offset in flash sector, fill in rest of the fcb_entry, and crc8 over
  * the data.
  */
-int
-fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, uint8_t *c8p)
+static int
+fcb_elem_crc8(struct fcb *_fcb, struct fcb_entry *loc, uint8_t *c8p)
 {
 	uint8_t tmp_str[FCB_TMP_BUF_SZ];
 	int cnt;
@@ -29,16 +31,17 @@ fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, uint8_t *c8p)
 	if (loc->fe_elem_off + 2 > loc->fe_sector->fs_size) {
 		return -ENOTSUP;
 	}
-	rc = fcb_flash_read(fcb, loc->fe_sector, loc->fe_elem_off, tmp_str, 2);
+
+	rc = fcb_flash_read(_fcb, loc->fe_sector, loc->fe_elem_off, tmp_str, 2);
 	if (rc) {
 		return -EIO;
 	}
 
-	cnt = fcb_get_len(fcb, tmp_str, &len);
+	cnt = fcb_get_len(_fcb, tmp_str, &len);
 	if (cnt < 0) {
 		return cnt;
 	}
-	loc->fe_data_off = loc->fe_elem_off + fcb_len_in_flash(fcb, cnt);
+	loc->fe_data_off = loc->fe_elem_off + fcb_len_in_flash(_fcb, cnt);
 	loc->fe_data_len = len;
 
 	crc8 = CRC8_CCITT_INITIAL_VALUE;
@@ -52,7 +55,7 @@ fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, uint8_t *c8p)
 			blk_sz = sizeof(tmp_str);
 		}
 
-		rc = fcb_flash_read(fcb, loc->fe_sector, off, tmp_str, blk_sz);
+		rc = fcb_flash_read(_fcb, loc->fe_sector, off, tmp_str, blk_sz);
 		if (rc) {
 			return -EIO;
 		}
@@ -63,25 +66,83 @@ fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, uint8_t *c8p)
 	return 0;
 }
 
-int fcb_elem_info(struct fcb *fcb, struct fcb_entry *loc)
+#if IS_ENABLED(CONFIG_FCB_ALLOW_FIXED_ENDMARKER)
+/* Given the offset in flash sector, calculate the FCB entry data offset and size, and set
+ * the fixed endmarker.
+ */
+static int
+fcb_elem_endmarker_fixed(struct fcb *_fcb, struct fcb_entry *loc, uint8_t *em)
 {
+	uint8_t tmp_str[2];
+	int cnt;
+	uint16_t len;
 	int rc;
-	uint8_t crc8;
-	uint8_t fl_crc8;
+
+	if (loc->fe_elem_off + 2 > loc->fe_sector->fs_size) {
+		return -ENOTSUP;
+	}
+
+	rc = fcb_flash_read(_fcb, loc->fe_sector, loc->fe_elem_off, tmp_str, 2);
+	if (rc) {
+		return -EIO;
+	}
+
+	cnt = fcb_get_len(_fcb, tmp_str, &len);
+	if (cnt < 0) {
+		return cnt;
+	}
+	loc->fe_data_off = loc->fe_elem_off + fcb_len_in_flash(_fcb, cnt);
+	loc->fe_data_len = len;
+
+	*em = FCB_FIXED_ENDMARKER;
+	return 0;
+}
+#endif /* IS_ENABLED(CONFIG_FCB_ALLOW_FIXED_ENDMARKER) */
+
+/* Given the offset in flash sector, calculate the FCB entry data offset and size, and calculate
+ * the expected endmarker.
+ */
+int
+fcb_elem_endmarker(struct fcb *_fcb, struct fcb_entry *loc, uint8_t *em)
+{
+#if IS_ENABLED(CONFIG_FCB_ALLOW_FIXED_ENDMARKER)
+	if (_fcb->f_flags & FCB_FLAGS_CRC_DISABLED) {
+		return fcb_elem_endmarker_fixed(_fcb, loc, em);
+	}
+#endif /* IS_ENABLED(CONFIG_FCB_ALLOW_FIXED_ENDMARKER) */
+
+	return fcb_elem_crc8(_fcb, loc, em);
+}
+
+/* Given the offset in flash sector, calculate the FCB entry data offset and size, and verify that
+ * the FCB entry endmarker is correct.
+ */
+int fcb_elem_info(struct fcb *_fcb, struct fcb_entry *loc)
+{
+	int rc;
+	uint8_t em;
+	uint8_t fl_em;
 	off_t off;
 
-	rc = fcb_elem_crc8(fcb, loc, &crc8);
+	rc = fcb_elem_endmarker(_fcb, loc, &em);
 	if (rc) {
 		return rc;
 	}
-	off = loc->fe_data_off + fcb_len_in_flash(fcb, loc->fe_data_len);
+	off = loc->fe_data_off + fcb_len_in_flash(_fcb, loc->fe_data_len);
 
-	rc = fcb_flash_read(fcb, loc->fe_sector, off, &fl_crc8, sizeof(fl_crc8));
+	rc = fcb_flash_read(_fcb, loc->fe_sector, off, &fl_em, sizeof(fl_em));
 	if (rc) {
 		return -EIO;
 	}
 
-	if (fl_crc8 != crc8) {
+	if (IS_ENABLED(CONFIG_FCB_ALLOW_FIXED_ENDMARKER) && (fl_em != em)) {
+		rc = fcb_elem_crc8(_fcb, loc, &em);
+		if (rc) {
+			return rc;
+		}
+	}
+
+	if (fl_em != em) {
 		return -EBADMSG;
 	}
 	return 0;
diff --git a/subsys/fs/fcb/fcb_priv.h b/subsys/fs/fcb/fcb_priv.h
index ea2643569a018..79dd878d462a1 100644
--- a/subsys/fs/fcb/fcb_priv.h
+++ b/subsys/fs/fcb/fcb_priv.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020 Nordic Semiconductor ASA
+ * Copyright (c) 2017-2023 Nordic Semiconductor ASA
  * Copyright (c) 2015 Runtime Inc
  *
  * SPDX-License-Identifier: Apache-2.0
@@ -70,7 +70,7 @@ struct flash_sector *fcb_getnext_sector(struct fcb *fcb,
 int fcb_getnext_nolock(struct fcb *fcb, struct fcb_entry *loc);
 
 int fcb_elem_info(struct fcb *fcb, struct fcb_entry *loc);
-int fcb_elem_crc8(struct fcb *fcb, struct fcb_entry *loc, uint8_t *crc8p);
+int fcb_elem_endmarker(struct fcb *fcb, struct fcb_entry *loc, uint8_t *crc8p);
 
 int fcb_sector_hdr_init(struct fcb *fcb, struct flash_sector *sector, uint16_t id);
 int fcb_sector_hdr_read(struct fcb *fcb, struct flash_sector *sector,