[rtems commit] cpukit/jffs2: Add support for NAND under JFFS2

Joel Sherrill joel at rtems.org
Wed Mar 15 18:11:53 UTC 2023


Module:    rtems
Branch:    master
Commit:    5db68a5859e2ec04b6e49ed2a04243a1b78358eb
Changeset: http://git.rtems.org/rtems/commit/?id=5db68a5859e2ec04b6e49ed2a04243a1b78358eb

Author:    Kinsey Moore <kinsey.moore at oarcorp.com>
Date:      Fri Oct  4 16:11:30 2019 -0500

cpukit/jffs2: Add support for NAND under JFFS2

This adds write buffer and bad block support required for JFFS2
operation on NAND devices. This also adds the minor modifications
necessary for RTEMS support in the Linux header stubs and in wbuf.c.
Memory and NOR backed applications should experience no difference in
operation since they do not expose the callbacks required for write
buffer support.

---

 cpukit/include/rtems/confdefs/libio.h              |  10 ++
 cpukit/include/rtems/jffs2.h                       | 147 +++++++++++++++++++
 cpukit/libfs/src/jffs2/include/linux/fs.h          |   3 +
 cpukit/libfs/src/jffs2/include/linux/jiffies.h     |   1 +
 cpukit/libfs/src/jffs2/include/linux/list.h        |   8 +
 cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h     |  37 +++++
 cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h |  26 ++++
 cpukit/libfs/src/jffs2/include/linux/mutex.h       |  33 ++---
 cpukit/libfs/src/jffs2/include/linux/rwsem.h       |  16 +-
 cpukit/libfs/src/jffs2/include/linux/slab.h        |   1 +
 cpukit/libfs/src/jffs2/include/linux/workqueue.h   |  28 +++-
 cpukit/libfs/src/jffs2/include/linux/writeback.h   |   1 +
 cpukit/libfs/src/jffs2/src/flashio.c               |  75 +++++++++-
 cpukit/libfs/src/jffs2/src/fs-rtems.c              | 161 +++++++++++++++++++++
 cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h           |   2 +
 cpukit/libfs/src/jffs2/src/os-rtems.h              |  69 +++++++--
 cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h    |   1 +
 cpukit/libfs/src/jffs2/src/wbuf.c                  |   2 +
 spec/build/cpukit/libjffs2.yml                     |   1 +
 testsuites/fstests/jffs2_support/fs_support.c      |   2 +-
 20 files changed, 583 insertions(+), 41 deletions(-)

diff --git a/cpukit/include/rtems/confdefs/libio.h b/cpukit/include/rtems/confdefs/libio.h
index 1b84f8c20f..0027a86f50 100644
--- a/cpukit/include/rtems/confdefs/libio.h
+++ b/cpukit/include/rtems/confdefs/libio.h
@@ -145,6 +145,16 @@
 
 #ifdef CONFIGURE_FILESYSTEM_JFFS2
 #include <rtems/jffs2.h>
+
+#ifndef CONFIGURE_JFFS2_DELAYED_WRITE_TASK_PRIORITY
+  #define CONFIGURE_JFFS2_DELAYED_WRITE_TASK_PRIORITY \
+    RTEMS_JFFS2_DELAYED_WRITE_TASK_PRIORITY_DEFAULT
+#endif
+
+const rtems_jffs2_config jffs2_config = {
+  CONFIGURE_JFFS2_DELAYED_WRITE_TASK_PRIORITY,
+};
+
 #endif
 
 #ifdef CONFIGURE_FILESYSTEM_NFS
diff --git a/cpukit/include/rtems/jffs2.h b/cpukit/include/rtems/jffs2.h
index 1cf9a01c6e..e1dee9b582 100644
--- a/cpukit/include/rtems/jffs2.h
+++ b/cpukit/include/rtems/jffs2.h
@@ -229,6 +229,99 @@ typedef int (*rtems_jffs2_flash_erase)(
   uint32_t offset
 );
 
+/**
+ * @brief Flash bad block check operation.
+ *
+ * This operation checks whether a block is bad.
+ *
+ * @param[in, out] self The flash control.
+ * @param[in] offset The offset in bytes of the block to check.
+ * @param[out] The result of the bad block check.
+ *
+ * @retval 0 Successful operation.
+ * @retval -EIO An error occurred.  Please note that the value is negative.
+ * @retval other All other values are reserved and must not be used.
+ */
+typedef int (*rtems_jffs2_flash_block_is_bad)(
+  rtems_jffs2_flash_control *self,
+  uint32_t offset,
+  bool *bad
+);
+
+/**
+ * @brief Flash bad block mark operation.
+ *
+ * This operation marks a block bad.
+ *
+ * @param[in, out] self The flash control.
+ * @param[in] offset The offset in bytes of the block to mark bad.
+ *
+ * @retval 0 Successful operation.
+ * @retval -EIO An error occurred.  Please note that the value is negative.
+ * @retval other All other values are reserved and must not be used.
+ */
+typedef int (*rtems_jffs2_flash_block_mark_bad)(
+  rtems_jffs2_flash_control *self,
+  uint32_t offset
+);
+
+/**
+ * @brief Flash oob write.
+ *
+ * This operation writes the out-of-band/spare bytes for the block matching
+ * the given offset in bytes.
+ *
+ * @param[in, out] self The flash control.
+ * @param[in] offset The offset to erase from the flash begin in bytes.
+ * @param[in] pointer to the buffer which will be written to the oob/spare bytes.
+ * @param[in] length of the buffer which will be written to the oob/spare bytes.
+ *
+ * @retval 0 Successful operation.
+ * @retval -EIO An error occurred.  Please note that the value is negative.
+ * @retval other All other values are reserved and must not be used.
+ */
+typedef int (*rtems_jffs2_flash_oob_write)(
+  rtems_jffs2_flash_control *self,
+  uint32_t offset,
+  uint8_t *oobbuf,
+  uint32_t obblen
+);
+
+/**
+ * @brief Flash oob read.
+ *
+ * This operation reads the out-of-band/spare bytes for the block matching
+ * the given offset in bytes.
+ *
+ * @param[in, out] self The flash control.
+ * @param[in] offset The offset to erase from the flash begin in bytes.
+ * @param[out] pointer to the buffer which will have the oob/spare bytes data written to it.
+ * @param[in] length of the buffer which will hold the oob/spare bytes.
+ *
+ * @retval 0 Successful operation.
+ * @retval -EIO An error occurred.  Please note that the value is negative.
+ * @retval other All other values are reserved and must not be used.
+ */
+typedef int (*rtems_jffs2_flash_oob_read)(
+  rtems_jffs2_flash_control *self,
+  uint32_t offset,
+  uint8_t *oobbuf,
+  uint32_t obblen
+);
+
+/**
+ * @brief Flash get oob size.
+ *
+ * This operation gets the size of the out-of-band/spare bytes for each page.
+ *
+ * @param[in, out] self The flash control.
+ *
+ * @retval The size of the OOB/spare area available to each page
+ */
+typedef uint32_t (*rtems_jffs2_flash_get_oob_size)(
+  rtems_jffs2_flash_control *self
+);
+
 /**
  * @brief Flash destroy operation.
  *
@@ -273,6 +366,14 @@ struct rtems_jffs2_flash_control {
    */
   uint32_t flash_size;
 
+  /**
+   * @brief The size in bytes of the minimum write size for the flash device.
+   *
+   * It must be an integral divisor into the block size. This is only applicable
+   * for NAND devices.
+   */
+  uint32_t write_size;
+
   /**
    * @brief Read from flash operation.
    */
@@ -288,6 +389,31 @@ struct rtems_jffs2_flash_control {
    */
   rtems_jffs2_flash_erase erase;
 
+  /**
+   * @brief Flash bad block check operation.
+   */
+  rtems_jffs2_flash_block_is_bad block_is_bad;
+
+  /**
+   * @brief Flash bad block mark operation.
+   */
+  rtems_jffs2_flash_block_mark_bad block_mark_bad;
+
+  /**
+   * @brief Flash oob bytes write operation.
+   */
+  rtems_jffs2_flash_oob_write oob_write;
+
+  /**
+   * @brief Flash oob bytes read operation.
+   */
+  rtems_jffs2_flash_oob_read oob_read;
+
+  /**
+   * @brief Flash get oob bytes per page operation.
+   */
+  rtems_jffs2_flash_get_oob_size get_oob_size;
+
   /**
    * @brief Flash destroy operation.
    *
@@ -608,6 +734,27 @@ typedef struct {
  */
 #define RTEMS_JFFS2_FORCE_GARBAGE_COLLECTION _IO('F', 3)
 
+/**
+ * Default delayed-write servicing task priority.
+ */
+#define RTEMS_JFFS2_DELAYED_WRITE_TASK_PRIORITY_DEFAULT 15
+
+/**
+ * JFFS2 configuration definition. See confdefs.h for support on using this
+ * structure.
+ */
+typedef struct rtems_jffs2_config {
+  rtems_task_priority delayed_write_priority; /**< Priority of the delayed write
+                                               * task. */
+} rtems_jffs2_config;
+
+/**
+ * External reference to the configuration.
+ *
+ * The configuration is provided by the application.
+ */
+extern const rtems_jffs2_config jffs2_config;
+
 /** @} */
 
 #ifdef __cplusplus
diff --git a/cpukit/libfs/src/jffs2/include/linux/fs.h b/cpukit/libfs/src/jffs2/include/linux/fs.h
index a638e7b6bf..9e40d105dc 100644
--- a/cpukit/libfs/src/jffs2/include/linux/fs.h
+++ b/cpukit/libfs/src/jffs2/include/linux/fs.h
@@ -34,4 +34,7 @@ struct iattr {
 	time_t		ia_ctime;
 };
 
+#define SB_RDONLY 1
+#define sb_rdonly(sb) ((sb)->s_flags & SB_RDONLY)
+
 #endif /* __LINUX_FS_H__ */
diff --git a/cpukit/libfs/src/jffs2/include/linux/jiffies.h b/cpukit/libfs/src/jffs2/include/linux/jiffies.h
new file mode 100644
index 0000000000..77debb5d27
--- /dev/null
+++ b/cpukit/libfs/src/jffs2/include/linux/jiffies.h
@@ -0,0 +1 @@
+#include <linux/workqueue.h>
diff --git a/cpukit/libfs/src/jffs2/include/linux/list.h b/cpukit/libfs/src/jffs2/include/linux/list.h
index 4dc8a5a6b7..efb7fb8019 100644
--- a/cpukit/libfs/src/jffs2/include/linux/list.h
+++ b/cpukit/libfs/src/jffs2/include/linux/list.h
@@ -139,6 +139,14 @@ list_empty( struct list_head *list )
     (_ent_) != (_list_);                 \
     (_ent_) = (_ent_)->next )
 
+/* list_for_each_safe - using _ent_, iterate through list _list_
+   while protecting against removal of list elements */
+
+#define list_for_each_safe( _ent_, _tmp_, _list_ )           \
+    for ( (_ent_) = (_list_)->next, (_tmp_) = (_ent_)->next; \
+    (_ent_) != (_list_);                                     \
+    (_ent_) = (_tmp_), (_tmp_) = (_ent_)->next )
+
 /*
  * list_for_each_entry - this function can be use to iterate over all
  * items in a list* _list_ with it's head at _head_ and link _item_
diff --git a/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h b/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h
index bcf0a9aa70..548201483f 100644
--- a/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h
+++ b/cpukit/libfs/src/jffs2/include/linux/mtd/mtd.h
@@ -6,6 +6,14 @@
 
 #define MTD_FAIL_ADDR_UNKNOWN -1LL
 
+struct mtd_info {
+  uint32_t oobavail;
+  uint32_t oobsize;
+  uint32_t size;
+  uint32_t erasesize;
+  uint32_t writesize;
+};
+
 static inline int do_mtd_point(size_t *retlen, void **ebuf)
 {
 	*retlen = 0;
@@ -20,4 +28,33 @@ static inline int do_mtd_point(size_t *retlen, void **ebuf)
 
 #define mtd_kmalloc_up_to(a, b) kmalloc(*(b), GFP_KERNEL)
 
+struct mtd_oob_ops {
+  uint32_t mode;
+  size_t len;
+  size_t retlen;
+  size_t ooblen;
+  size_t oobretlen;
+  uint32_t ooboffs;
+  uint8_t *datbuf;
+  uint8_t *oobbuf;
+};
+
+#define EUCLEAN EAGAIN
+static inline int mtd_is_bitflip(int err) { return (err == -EUCLEAN); }
+
+#define mtd_block_isbad(mtd_sp, offset) ({ \
+  bool bad; \
+  int sc = jffs2_flash_block_is_bad(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), offset, &bad); \
+  if (sc) { \
+    return sc; \
+  } \
+  bad; \
+})
+#define mtd_block_markbad(mtd_sp, offset) \
+  jffs2_flash_block_mark_bad(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), offset)
+#define mtd_write(mtd_sp, ofs, len, retlen, buf) \
+  jffs2_flash_direct_write(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), ofs, len, retlen, buf)
+#define mtd_read(mtd_sp, ofs, len, retlen, buf) \
+  jffs2_flash_direct_read(RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd), ofs, len, retlen, buf)
+
 #endif /* __LINUX_MTD_MTD_H__ */
diff --git a/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h b/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h
new file mode 100644
index 0000000000..fe47ba6fbf
--- /dev/null
+++ b/cpukit/libfs/src/jffs2/include/linux/mtd/rawnand.h
@@ -0,0 +1,26 @@
+#ifndef __LINUX_MTD_RAWNAND_H__
+#define __LINUX_MTD_RAWNAND_H__
+
+#define mtd_read_oob(mtd_sp, offset, ops) ({ \
+  struct jffs2_sb_info *sb_info = RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd); \
+  int sc = jffs2_flash_oob_read(sb_info, offset, (ops)->oobbuf, (ops)->ooblen); \
+  if (sc) { \
+    sc = -EIO; \
+  } else { \
+    (ops)->oobretlen = (ops)->ooblen; \
+  } \
+  sc; \
+})
+#define mtd_write_oob(mtd_sp, offset, ops) ({ \
+  struct jffs2_sb_info *sb_info = RTEMS_CONTAINER_OF(&(mtd_sp), struct jffs2_sb_info, mtd); \
+  int sc = jffs2_flash_oob_write(sb_info, offset, (ops)->oobbuf, (ops)->ooblen); \
+  if (sc != RTEMS_SUCCESSFUL) { \
+    sc = -EIO; \
+  } else { \
+    (ops)->oobretlen = (ops)->ooblen; \
+  } \
+  sc; \
+})
+#define MTD_OPS_AUTO_OOB 1
+
+#endif /* __LINUX_MTD_RAWNAND_H__ */
diff --git a/cpukit/libfs/src/jffs2/include/linux/mutex.h b/cpukit/libfs/src/jffs2/include/linux/mutex.h
index f5e1efb1af..be8709f125 100644
--- a/cpukit/libfs/src/jffs2/include/linux/mutex.h
+++ b/cpukit/libfs/src/jffs2/include/linux/mutex.h
@@ -1,29 +1,20 @@
 #ifndef __LINUX_MUTEX_H
 #define __LINUX_MUTEX_H
 
-struct mutex { };
+#include <rtems/thread.h>
+
+struct mutex { rtems_mutex r_m; };
 
 #define DEFINE_MUTEX(m) struct mutex m
 
-static inline void mutex_init(struct mutex *m)
-{
-	(void) m;
-}
-
-static inline void mutex_lock(struct mutex *m)
-{
-	(void) m;
-}
-
-static inline int mutex_lock_interruptible(struct mutex *m)
-{
-	(void) m;
-	return 0;
-}
-
-static inline void mutex_unlock(struct mutex *m)
-{
-	(void) m;
-}
+#define mutex_init(m) rtems_mutex_init(&(m)->r_m, "JFFS2 Mutex");
+
+#define mutex_lock(m) rtems_mutex_lock(&(m)->r_m);
+
+#define mutex_lock_interruptible(m) ({ mutex_lock(m); 0; })
+
+#define mutex_unlock(m) rtems_mutex_unlock(&(m)->r_m);
+
+#define mutex_is_locked(m) 1
 
 #endif
diff --git a/cpukit/libfs/src/jffs2/include/linux/rwsem.h b/cpukit/libfs/src/jffs2/include/linux/rwsem.h
index 57bfdf13b7..9db6d45ad2 100644
--- a/cpukit/libfs/src/jffs2/include/linux/rwsem.h
+++ b/cpukit/libfs/src/jffs2/include/linux/rwsem.h
@@ -1,6 +1,20 @@
 #ifndef __LINUX_RWSEM_H__
 #define __LINUX_RWSEM_H__
 
-struct rw_semaphore;
+#include <pthread.h>
+
+struct rw_semaphore {
+  pthread_rwlock_t lock;
+};
+
+#define init_rwsem(rwsem) pthread_rwlock_init(&(rwsem)->lock, NULL)
+
+#define down_read(rwsem) pthread_rwlock_rdlock(&(rwsem)->lock)
+
+#define down_write(rwsem) pthread_rwlock_wrlock(&(rwsem)->lock)
+
+#define up_read(rwsem) pthread_rwlock_unlock(&(rwsem)->lock)
+
+#define up_write(rwsem) pthread_rwlock_unlock(&(rwsem)->lock)
 
 #endif /* __LINUX_RWSEM_H__ */
diff --git a/cpukit/libfs/src/jffs2/include/linux/slab.h b/cpukit/libfs/src/jffs2/include/linux/slab.h
index 532586e210..d664f50dfb 100644
--- a/cpukit/libfs/src/jffs2/include/linux/slab.h
+++ b/cpukit/libfs/src/jffs2/include/linux/slab.h
@@ -7,6 +7,7 @@
 
 #define kzalloc(x, y) calloc(1, x)
 #define kmalloc(x, y) malloc(x)
+#define kmalloc_array(x, y, z) kmalloc((x * y), z)
 #define kfree(x) free(x)
 #define kvfree(x) free(x)
 #define vmalloc(x) malloc(x)
diff --git a/cpukit/libfs/src/jffs2/include/linux/workqueue.h b/cpukit/libfs/src/jffs2/include/linux/workqueue.h
index 90d98288af..45a2942bfc 100644
--- a/cpukit/libfs/src/jffs2/include/linux/workqueue.h
+++ b/cpukit/libfs/src/jffs2/include/linux/workqueue.h
@@ -1,10 +1,30 @@
 #ifndef __LINUX_WORKQUEUE_H__
 #define __LINUX_WORKQUEUE_H__
 
-struct work_struct { } ;
+#include <rtems/chain.h>
 
-#define INIT_WORK(x,y,z) /* */
-#define schedule_work(x) do { } while(0)
-#define flush_scheduled_work() do { } while(0)
+struct work_struct { rtems_chain_node node; };
+
+#define queue_delayed_work(workqueue, delayed_work, delay_ms) ({ \
+  jffs2_queue_delayed_work(delayed_work, delay_ms); \
+  0; \
+})
+
+#define INIT_DELAYED_WORK(delayed_work, delayed_workqueue_callback) ({ \
+  _Chain_Initialize_node(&(delayed_work)->work.node); \
+  (delayed_work)->callback = delayed_workqueue_callback; \
+})
+
+#define msecs_to_jiffies(a) (a)
+
+typedef void (*work_callback_t)(struct work_struct *work);
+struct delayed_work {
+	struct work_struct work;
+	uint64_t execution_time;
+	work_callback_t callback;
+};
+
+#define to_delayed_work(work) RTEMS_CONTAINER_OF(work, struct delayed_work, work)
+void jffs2_queue_delayed_work(struct delayed_work *work, int delay_ms);
 
 #endif /* __LINUX_WORKQUEUE_H__ */
diff --git a/cpukit/libfs/src/jffs2/include/linux/writeback.h b/cpukit/libfs/src/jffs2/include/linux/writeback.h
new file mode 100644
index 0000000000..9899205383
--- /dev/null
+++ b/cpukit/libfs/src/jffs2/include/linux/writeback.h
@@ -0,0 +1 @@
+/* This file intentionally left empty as a stub for wbuf.c */
diff --git a/cpukit/libfs/src/jffs2/src/flashio.c b/cpukit/libfs/src/jffs2/src/flashio.c
index 7b689a5a7c..6b0a9433fa 100644
--- a/cpukit/libfs/src/jffs2/src/flashio.c
+++ b/cpukit/libfs/src/jffs2/src/flashio.c
@@ -17,8 +17,23 @@
 #include <linux/kernel.h>
 #include "nodelist.h"
 
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
 int jffs2_flash_read(struct jffs2_sb_info * c,
-			  cyg_uint32 read_buffer_offset, const size_t size,
+			  loff_t read_buffer_offset, const size_t size,
+			  size_t * return_size, unsigned char *write_buffer)
+{
+	return jffs2_flash_direct_read(c, read_buffer_offset, size, return_size, write_buffer);
+}
+
+int jffs2_flash_write(struct jffs2_sb_info * c,
+			   loff_t write_buffer_offset, size_t size,
+			   size_t * return_size, const unsigned char *read_buffer)
+{
+	return jffs2_flash_direct_write(c, write_buffer_offset, size, return_size, read_buffer);
+}
+#endif
+int jffs2_flash_direct_read(struct jffs2_sb_info * c,
+			  loff_t read_buffer_offset, const size_t size,
 			  size_t * return_size, unsigned char *write_buffer)
 {
 	const struct super_block *sb = OFNI_BS_2SFFJ(c);
@@ -29,9 +44,9 @@ int jffs2_flash_read(struct jffs2_sb_info * c,
 	return (*fc->read)(fc, read_buffer_offset, write_buffer, size);
 }
 
-int jffs2_flash_write(struct jffs2_sb_info * c,
-			   cyg_uint32 write_buffer_offset, const size_t size,
-			   size_t * return_size, unsigned char *read_buffer)
+int jffs2_flash_direct_write(struct jffs2_sb_info * c,
+			   loff_t write_buffer_offset, const size_t size,
+			   size_t * return_size, const unsigned char *read_buffer)
 {
 	const struct super_block *sb = OFNI_BS_2SFFJ(c);
 	rtems_jffs2_flash_control *fc = sb->s_flash_control;
@@ -133,3 +148,55 @@ int jffs2_flash_erase(struct jffs2_sb_info * c,
 	return (*fc->erase)(fc, jeb->offset);
 }
 
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+int jffs2_flash_block_is_bad(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset, bool *bad)
+{
+	const struct super_block *sb = OFNI_BS_2SFFJ(c);
+	rtems_jffs2_flash_control *fc = sb->s_flash_control;
+
+	return (*fc->block_is_bad)(fc, block_offset, bad);
+}
+
+int jffs2_flash_block_mark_bad(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset)
+{
+	const struct super_block *sb = OFNI_BS_2SFFJ(c);
+	rtems_jffs2_flash_control *fc = sb->s_flash_control;
+
+	return (*fc->block_mark_bad)(fc, block_offset);
+}
+
+int jffs2_flash_oob_write(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset,
+			uint8_t *oobbuf,
+			uint32_t ooblen)
+{
+	const struct super_block *sb = OFNI_BS_2SFFJ(c);
+	rtems_jffs2_flash_control *fc = sb->s_flash_control;
+
+	return (*fc->oob_write)(fc, block_offset, oobbuf, ooblen);
+}
+
+int jffs2_flash_oob_read(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset,
+			uint8_t *oobbuf,
+			uint32_t ooblen)
+{
+	const struct super_block *sb = OFNI_BS_2SFFJ(c);
+	rtems_jffs2_flash_control *fc = sb->s_flash_control;
+
+	return (*fc->oob_read)(fc, block_offset, oobbuf, ooblen);
+}
+
+int jffs2_flash_get_oob_size(struct jffs2_sb_info * c)
+{
+	const struct super_block *sb = OFNI_BS_2SFFJ(c);
+	rtems_jffs2_flash_control *fc = sb->s_flash_control;
+
+	if (fc->get_oob_size == NULL) {
+		return 0;
+	}
+	return (*fc->get_oob_size)(fc);
+}
+#endif
diff --git a/cpukit/libfs/src/jffs2/src/fs-rtems.c b/cpukit/libfs/src/jffs2/src/fs-rtems.c
index 3be142a75f..bff7d59ba1 100644
--- a/cpukit/libfs/src/jffs2/src/fs-rtems.c
+++ b/cpukit/libfs/src/jffs2/src/fs-rtems.c
@@ -23,12 +23,14 @@
 #include "nodelist.h"
 #include <linux/pagemap.h>
 #include <linux/crc32.h>
+#include <linux/mtd/mtd.h>
 #include "compr.h"
 #include <errno.h>
 #include <string.h>
 #include <assert.h>
 #include <rtems/libio.h>
 #include <rtems/libio_.h>
+#include <rtems/sysinit.h>
 
 /* Ensure that the JFFS2 values are identical to the POSIX defines */
 
@@ -1049,10 +1051,25 @@ static void rtems_jffs2_freenode(const rtems_filesystem_location_info_t *loc)
 	jffs2_iput(inode);
 }
 
+static void jffs2_remove_delayed_work(struct delayed_work *dwork);
+
 static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry)
 {
 	rtems_jffs2_fs_info *fs_info = mt_entry->fs_info;
 	struct _inode *root_i = mt_entry->mt_fs_root->location.node_access;
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(&fs_info->sb);
+
+	/* Remove wbuf delayed work */
+	jffs2_remove_delayed_work(&c->wbuf_dwork);
+
+	/* Flush any pending writes */
+	if (!sb_rdonly(&fs_info->sb)) {
+		jffs2_flush_wbuf_gc(c, 0);
+	}
+#endif
+
+	jffs2_sum_exit(c);
 
 	icache_evict(root_i, NULL);
 	assert(root_i->i_cache_next == NULL);
@@ -1062,6 +1079,12 @@ static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry
 	rtems_jffs2_free_directory_entries(root_i);
 	free(root_i);
 
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+	jffs2_nand_flash_cleanup(c);
+	free(c->mtd);
+	c->mtd = NULL;
+#endif
+
 	rtems_jffs2_free_fs_info(fs_info, true);
 }
 
@@ -1212,6 +1235,113 @@ static int calculate_inocache_hashsize(uint32_t flash_size)
 	return hashsize;
 }
 
+/* Chain for holding delayed work */
+rtems_chain_control delayed_work_chain;
+
+/* Lock for protecting the delayed work chain */
+struct mutex delayed_work_mutex;
+
+void jffs2_queue_delayed_work(struct delayed_work *work, int delay_ms)
+{
+	mutex_lock(&delayed_work_mutex);
+	if (rtems_chain_is_node_off_chain(&work->work.node)) {
+		work->execution_time = rtems_clock_get_uptime_nanoseconds() + delay_ms*1000000;
+		rtems_chain_append(&delayed_work_chain, &work->work.node);
+	}
+	mutex_unlock(&delayed_work_mutex);
+}
+
+static void jffs2_remove_delayed_work(struct delayed_work *dwork)
+{
+	struct delayed_work* work;
+	rtems_chain_node*    node;
+
+	mutex_lock(&delayed_work_mutex);
+	if (rtems_chain_is_node_off_chain(&dwork->work.node)) {
+		mutex_unlock(&delayed_work_mutex);
+		return;
+	}
+
+	node = rtems_chain_first(&delayed_work_chain);
+	while (!rtems_chain_is_tail(&delayed_work_chain, node)) {
+		work = (struct delayed_work*) node;
+		rtems_chain_node* next_node = rtems_chain_next(node);
+		if (work == dwork) {
+			rtems_chain_extract(node);
+			mutex_unlock(&delayed_work_mutex);
+			return;
+		}
+		node = next_node;
+	}
+	mutex_unlock(&delayed_work_mutex);
+}
+
+static void process_delayed_work(void)
+{
+	struct delayed_work* work;
+	rtems_chain_node*    node;
+
+	mutex_lock(&delayed_work_mutex);
+
+	if (rtems_chain_is_empty(&delayed_work_chain)) {
+		mutex_unlock(&delayed_work_mutex);
+		return;
+	}
+
+	node = rtems_chain_first(&delayed_work_chain);
+	while (!rtems_chain_is_tail(&delayed_work_chain, node)) {
+		work = (struct delayed_work*) node;
+		rtems_chain_node* next_node = rtems_chain_next(node);
+		if (rtems_clock_get_uptime_nanoseconds() >= work->execution_time) {
+			rtems_chain_extract(node);
+			work->callback(&work->work);
+		}
+		node = next_node;
+	}
+	mutex_unlock(&delayed_work_mutex);
+}
+/* Task for processing delayed work */
+static rtems_task delayed_work_task(
+  rtems_task_argument unused
+)
+{
+	(void)unused;
+	while (1) {
+		process_delayed_work();
+		sleep(1);
+	}
+}
+
+rtems_id delayed_work_task_id;
+
+static void jffs2_init_delayed_work_task(void)
+{
+	/* Initialize chain for delayed work */
+	rtems_chain_initialize_empty(&delayed_work_chain);
+
+	/* Initialize lock for delayed work */
+	mutex_init(&delayed_work_mutex);
+
+	/* Create task for delayed work */
+	rtems_status_code err = rtems_task_create(
+		rtems_build_name( 'J', 'F', 'F', 'S' ),
+		jffs2_config.delayed_write_priority,
+		RTEMS_MINIMUM_STACK_SIZE,
+		RTEMS_DEFAULT_MODES,
+		RTEMS_DEFAULT_ATTRIBUTES,
+		&delayed_work_task_id
+	);
+	if (err != RTEMS_SUCCESSFUL) {
+		printk("JFFS2 delayed work task processor creation failed\n");
+	}
+}
+
+RTEMS_SYSINIT_ITEM(
+  jffs2_init_delayed_work_task,
+  RTEMS_SYSINIT_LIBIO,
+  RTEMS_SYSINIT_ORDER_MIDDLE
+);
+
 int rtems_jffs2_initialize(
 	rtems_filesystem_mount_table_entry_t *mt_entry,
 	const void *data
@@ -1237,11 +1367,21 @@ int rtems_jffs2_initialize(
 
 	sb = &fs_info->sb;
 	c = JFFS2_SB_INFO(sb);
+	c->mtd = NULL;
 
 	if (err == 0) {
 		rtems_recursive_mutex_init(&sb->s_mutex, RTEMS_FILESYSTEM_TYPE_JFFS2);
 	}
 
+	/* Start task for delayed work if it hasn't already been started */
+	if (err == 0) {
+		err = rtems_task_start( delayed_work_task_id, delayed_work_task, 0 );
+		/* Task already running from previous mount */
+		if (err == RTEMS_INCORRECT_STATE) {
+			err = 0;
+		}
+	}
+
 	if (err == 0) {
 		uint32_t blocks = fc->flash_size / fc->block_size;
 
@@ -1263,12 +1403,31 @@ int rtems_jffs2_initialize(
 		sb->s_flash_control = fc;
 		sb->s_compressor_control = jffs2_mount_data->compressor_control;
 
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+		c->mtd = malloc(sizeof(struct mtd_info));
+		if (!c->mtd) {
+			err = -ENOMEM;
+		}
+	}
+
+	if (err == 0) {
+		c->mtd->oobavail = jffs2_flash_get_oob_size(c);
+		c->mtd->oobsize = c->mtd->oobavail;
+		c->mtd->size = fc->flash_size;
+		c->mtd->erasesize = fc->block_size;
+		c->mtd->writesize = fc->write_size;
+#endif
 		c->inocache_hashsize = inocache_hashsize;
 		c->inocache_list = &fs_info->inode_cache[0];
 		c->sector_size = fc->block_size;
 		c->flash_size = fc->flash_size;
 		c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+		err = jffs2_nand_flash_setup(c);
+	}
 
+	if (err == 0) {
+#endif
 		err = jffs2_do_mount_fs(c);
 	}
 
@@ -1296,6 +1455,8 @@ int rtems_jffs2_initialize(
 		return 0;
 	} else {
 		if (fs_info != NULL) {
+			free(c->mtd);
+			c->mtd = NULL;
 			rtems_jffs2_free_fs_info(fs_info, do_mount_fs_was_successful);
 		} else {
 			rtems_jffs2_flash_control_destroy(fc);
diff --git a/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h b/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h
index 5a7091746f..4ee43a6f30 100644
--- a/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h
+++ b/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h
@@ -1,3 +1,5 @@
+#include "rtems-jffs2-config.h"
+
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
diff --git a/cpukit/libfs/src/jffs2/src/os-rtems.h b/cpukit/libfs/src/jffs2/src/os-rtems.h
index d9e4330371..bda4fc9d46 100644
--- a/cpukit/libfs/src/jffs2/src/os-rtems.h
+++ b/cpukit/libfs/src/jffs2/src/os-rtems.h
@@ -52,8 +52,7 @@ static inline unsigned int full_name_hash(const void *salt, const unsigned char
 	return hash;
 }
 
-/* NAND flash not currently supported on RTEMS */
-#define jffs2_can_mark_obsolete(c) (1)
+#define container_of(a, b, c) RTEMS_CONTAINER_OF(a, b, c)
 
 #define JFFS2_INODE_INFO(i) (&(i)->jffs2_i)
 #define OFNI_EDONI_2SFFJ(f) RTEMS_CONTAINER_OF(f, struct _inode, jffs2_i)
@@ -108,6 +107,7 @@ struct super_block {
 	unsigned char		s_gc_buffer[PAGE_CACHE_SIZE]; // Avoids malloc when user may be under memory pressure
 	rtems_recursive_mutex	s_mutex;
 	char			s_name_buf[JFFS2_MAX_NAME_LEN];
+	uint32_t		s_flags;
 };
 
 #define sleep_on_spinunlock(wq, sl) spin_unlock(sl)
@@ -151,13 +151,15 @@ static inline uint32_t jffs2_to_os_mode (uint32_t jmode)
 
 
 /* flashio.c */
-int jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset,
-			  const size_t size, size_t * return_size, unsigned char * write_buffer);
-int jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset,
-			   const size_t size, size_t * return_size, unsigned char * read_buffer);
+int jffs2_flash_read(struct jffs2_sb_info *c, loff_t read_buffer_offset,
+			  size_t size, size_t * return_size, unsigned char * write_buffer);
+int jffs2_flash_write(struct jffs2_sb_info *c, loff_t write_buffer_offset,
+			   size_t size, size_t * return_size, const unsigned char * read_buffer);
 int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
 			      unsigned long count, loff_t to, size_t *retlen);
 int jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
+int jffs2_flash_direct_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
 
 // dir-rtems.c
 struct _inode *jffs2_lookup(struct _inode *dir_i, const unsigned char *name, size_t namelen);
@@ -173,8 +175,9 @@ int jffs2_rename (struct _inode *old_dir_i, struct _inode *d_inode, const unsign
 static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
 { }
 
-#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
 #define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
+
 #define jffs2_can_mark_obsolete(c) (1)
 #define jffs2_is_writebuffered(c) (0)
 #define jffs2_cleanmarker_oob(c) (0)
@@ -191,9 +194,55 @@ static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 #define jffs2_nor_ecc(c) (0)
-#else
-#error no nand yet
-#endif
+#else /* CONFIG_JFFS2_FS_WRITEBUFFER */
+/* dirty_writeback_interval is in centiseconds, 500cs == 5s */
+#define dirty_writeback_interval 500
+#define MTD_BIT_WRITEABLE 0x800
+#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
+
+#define jffs2_can_mark_obsolete(c) (OFNI_BS_2SFFJ(c)->s_flash_control->block_is_bad == NULL)
+
+#define jffs2_cleanmarker_oob(c) (OFNI_BS_2SFFJ(c)->s_flash_control->block_is_bad != NULL)
+
+#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
+
+/* wbuf.c */
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
+int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
+void jffs2_wbuf_timeout(unsigned long data);
+void jffs2_wbuf_process(void *data);
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
+int jffs2_flash_block_is_bad(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset,
+			bool *bad);
+int jffs2_flash_block_mark_bad(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset);
+int jffs2_flash_oob_write(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset,
+			uint8_t *oobbuf,
+			uint32_t ooblen);
+int jffs2_flash_oob_read(struct jffs2_sb_info * c,
+			cyg_uint32 block_offset,
+			uint8_t *oobbuf,
+			uint32_t ooblen);
+int jffs2_flash_get_oob_size(struct jffs2_sb_info * c);
+
+int jffs2_dataflash_setup(struct jffs2_sb_info *c);
+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
+int jffs2_ubivol_setup(struct jffs2_sb_info *c);
+void jffs2_ubivol_cleanup(struct jffs2_sb_info *c);
+
+int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
+void jffs2_dirty_trigger(struct jffs2_sb_info *c);
+
+#endif /* CONFIG_JFFS2_FS_WRITEBUFFER */
 
 #ifndef BUG_ON
 #define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0)
diff --git a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h
index 56395e1528..c56cbfeb8a 100644
--- a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h
+++ b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h
@@ -34,3 +34,4 @@
 #define __ECOS 1
 #define KBUILD_MODNAME "JFFS2"
 #define fallthrough __attribute__((__fallthrough__))
+#define CONFIG_JFFS2_FS_WRITEBUFFER
diff --git a/cpukit/libfs/src/jffs2/src/wbuf.c b/cpukit/libfs/src/jffs2/src/wbuf.c
index 2e939c5ef0..04e2b03efc 100644
--- a/cpukit/libfs/src/jffs2/src/wbuf.c
+++ b/cpukit/libfs/src/jffs2/src/wbuf.c
@@ -1,3 +1,5 @@
+#include "rtems-jffs2-config.h"
+
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
diff --git a/spec/build/cpukit/libjffs2.yml b/spec/build/cpukit/libjffs2.yml
index bc57baa68b..d41db4a2c9 100644
--- a/spec/build/cpukit/libjffs2.yml
+++ b/spec/build/cpukit/libjffs2.yml
@@ -33,6 +33,7 @@ source:
 - cpukit/libfs/src/jffs2/src/read.c
 - cpukit/libfs/src/jffs2/src/readinode.c
 - cpukit/libfs/src/jffs2/src/scan.c
+- cpukit/libfs/src/jffs2/src/wbuf.c
 - cpukit/libfs/src/jffs2/src/write.c
 target: jffs2
 type: build
diff --git a/testsuites/fstests/jffs2_support/fs_support.c b/testsuites/fstests/jffs2_support/fs_support.c
index 2aa04a27c6..c2f2dcaf8e 100644
--- a/testsuites/fstests/jffs2_support/fs_support.c
+++ b/testsuites/fstests/jffs2_support/fs_support.c
@@ -163,7 +163,7 @@ void test_shutdown_filesystem(void)
 
 #define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 40
 
-#define CONFIGURE_MAXIMUM_TASKS 1
+#define CONFIGURE_MAXIMUM_TASKS 2
 
 #define CONFIGURE_INIT_TASK_STACK_SIZE (32 * 1024)
 #define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT



More information about the vc mailing list