diff options
Diffstat (limited to 'cpukit/libfs/src/jffs2')
33 files changed, 1959 insertions, 119 deletions
diff --git a/cpukit/libfs/src/jffs2/VERSION b/cpukit/libfs/src/jffs2/VERSION index 7d9c9f41b2..7302f10ef6 100644 --- a/cpukit/libfs/src/jffs2/VERSION +++ b/cpukit/libfs/src/jffs2/VERSION @@ -1,9 +1,9 @@ -This directory contains a port of the JFFS2 file system from Linux v4.17. +This directory contains a port of the JFFS2 file system from Linux v5.9. To update to a newer Linux version use this command in a Git clone of Linux to generate the relevant patches: - git format-patch v4.17..v9.99 -- + git format-patch v6.1..v9.99 -- \ include/uapi/linux/jffs2.h \ fs/jffs2/LICENCE \ fs/jffs2/acl.h \ @@ -26,6 +26,7 @@ generate the relevant patches: fs/jffs2/readinode.c \ fs/jffs2/scan.c \ fs/jffs2/summary.h \ + fs/jffs2/wbuf.c \ fs/jffs2/write.c \ fs/jffs2/xattr.h 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/jffs2.h b/cpukit/libfs/src/jffs2/include/linux/jffs2.h index a18b719f49..637ee4a793 100644 --- a/cpukit/libfs/src/jffs2/include/linux/jffs2.h +++ b/cpukit/libfs/src/jffs2/include/linux/jffs2.h @@ -77,11 +77,6 @@ #define JFFS2_ACL_VERSION 0x0001 -// Maybe later... -//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) -//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) - - #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at mount time, don't wait for it to happen later */ @@ -128,7 +123,7 @@ struct jffs2_raw_dirent __u8 unused[2]; jint32_t node_crc; jint32_t name_crc; - __u8 name[0]; + __u8 name[]; }; /* The JFFS2 raw inode structure: Used for storage on physical media. */ @@ -160,7 +155,7 @@ struct jffs2_raw_inode jint16_t flags; /* See JFFS2_INO_FLAG_* */ jint32_t data_crc; /* CRC for the (compressed) data. */ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ - __u8 data[0]; + __u8 data[]; }; struct jffs2_raw_xattr { @@ -175,7 +170,7 @@ struct jffs2_raw_xattr { jint16_t value_len; jint32_t data_crc; jint32_t node_crc; - __u8 data[0]; + __u8 data[]; } __attribute__((packed)); struct jffs2_raw_xref @@ -201,7 +196,7 @@ struct jffs2_raw_summary jint32_t padded; /* sum of the size of padding nodes */ jint32_t sum_crc; /* summary information crc */ jint32_t node_crc; /* node crc */ - jint32_t sum[0]; /* inode summary info */ + jint32_t sum[]; /* inode summary info */ }; union jffs2_node_union 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/kernel-rtems.h b/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h index eb5f422116..ed46ce2391 100644 --- a/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h +++ b/cpukit/libfs/src/jffs2/include/linux/kernel-rtems.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 embedded brains GmbH. All rights reserved. + * Copyright (c) 2013 embedded brains GmbH & Co. KG * * Copyright 2016 Chris Johns <chrisj@rtems.org> * 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..cc82a3f17a 100644 --- a/cpukit/libfs/src/jffs2/include/linux/mutex.h +++ b/cpukit/libfs/src/jffs2/include/linux/mutex.h @@ -26,4 +26,6 @@ static inline void mutex_unlock(struct mutex *m) (void) m; } +#define mutex_is_locked(m) 1 + #endif diff --git a/cpukit/libfs/src/jffs2/include/linux/printk.h b/cpukit/libfs/src/jffs2/include/linux/printk.h new file mode 100644 index 0000000000..515188faa1 --- /dev/null +++ b/cpukit/libfs/src/jffs2/include/linux/printk.h @@ -0,0 +1,6 @@ +#ifndef __LINUX_PRINTK_H__ +#define __LINUX_PRINTK_H__ + +#define no_printk(fmt, ...) do { } while (0) + +#endif /* __LINUX_PRINTK_H__ */ diff --git a/cpukit/libfs/src/jffs2/include/linux/rwsem.h b/cpukit/libfs/src/jffs2/include/linux/rwsem.h index 57bfdf13b7..e59e1cede3 100644 --- a/cpukit/libfs/src/jffs2/include/linux/rwsem.h +++ b/cpukit/libfs/src/jffs2/include/linux/rwsem.h @@ -1,6 +1,16 @@ #ifndef __LINUX_RWSEM_H__ #define __LINUX_RWSEM_H__ -struct rw_semaphore; +struct rw_semaphore {}; + +#define init_rwsem(rwsem) + +#define down_read(rwsem) + +#define down_write(rwsem) + +#define up_read(rwsem) + +#define up_write(rwsem) #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..9811c7cd3e 100644 --- a/cpukit/libfs/src/jffs2/include/linux/workqueue.h +++ b/cpukit/libfs/src/jffs2/include/linux/workqueue.h @@ -1,10 +1,34 @@ #ifndef __LINUX_WORKQUEUE_H__ #define __LINUX_WORKQUEUE_H__ -struct work_struct { } ; +#include <rtems/chain.h> +#include <linux/mutex.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) ({ \ + (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; + struct mutex dw_mutex; + volatile bool pending; + volatile uint64_t execution_time; + work_callback_t callback; + /* Superblock provided for locking */ + struct super_block *sb; +}; + +#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/acl.h b/cpukit/libfs/src/jffs2/src/acl.h index 2e2b5745c3..9d9fb7cf09 100644 --- a/cpukit/libfs/src/jffs2/src/acl.h +++ b/cpukit/libfs/src/jffs2/src/acl.h @@ -22,12 +22,14 @@ struct jffs2_acl_entry_short { struct jffs2_acl_header { jint32_t a_version; + struct jffs2_acl_entry a_entries[]; }; #ifdef CONFIG_JFFS2_FS_POSIX_ACL -struct posix_acl *jffs2_get_acl(struct inode *inode, int type); -int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type); +struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu); +int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *acl, int type); extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *); extern int jffs2_init_acl_post(struct inode *); diff --git a/cpukit/libfs/src/jffs2/src/build.c b/cpukit/libfs/src/jffs2/src/build.c index d35bc83bbb..12e2ef820b 100644 --- a/cpukit/libfs/src/jffs2/src/build.c +++ b/cpukit/libfs/src/jffs2/src/build.c @@ -417,13 +417,15 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); ret = -EIO; - goto out_free; + goto out_sum_exit; } jffs2_calc_trigger_levels(c); return 0; + out_sum_exit: + jffs2_sum_exit(c); out_free: kvfree(c->blocks); diff --git a/cpukit/libfs/src/jffs2/src/compr.c b/cpukit/libfs/src/jffs2/src/compr.c index a9e821a6a1..19605754de 100644 --- a/cpukit/libfs/src/jffs2/src/compr.c +++ b/cpukit/libfs/src/jffs2/src/compr.c @@ -7,7 +7,7 @@ * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, * University of Szeged, Hungary - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by Arjan van de Ven <arjan@infradead.org> * diff --git a/cpukit/libfs/src/jffs2/src/compr_rtime.c b/cpukit/libfs/src/jffs2/src/compr_rtime.c index c8313ed234..e3c761dcfe 100644 --- a/cpukit/libfs/src/jffs2/src/compr_rtime.c +++ b/cpukit/libfs/src/jffs2/src/compr_rtime.c @@ -44,6 +44,9 @@ uint16_t rtems_jffs2_compressor_rtime_compress( (void) self; + if (*dstlen <= 3) + return JFFS2_COMPR_NONE; + memset(positions,0,sizeof(positions)); while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { diff --git a/cpukit/libfs/src/jffs2/src/debug.h b/cpukit/libfs/src/jffs2/src/debug.h index 6c905b3854..6b4afd4e75 100644 --- a/cpukit/libfs/src/jffs2/src/debug.h +++ b/cpukit/libfs/src/jffs2/src/debug.h @@ -13,6 +13,7 @@ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ +#include <linux/printk.h> #include <linux/sched.h> #ifndef CONFIG_JFFS2_FS_DEBUG @@ -117,73 +118,73 @@ do { \ #ifdef JFFS2_DBG_READINODE_MESSAGES #define dbg_readinode(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_readinode(fmt, ...) +#define dbg_readinode(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif #ifdef JFFS2_DBG_READINODE2_MESSAGES #define dbg_readinode2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_readinode2(fmt, ...) +#define dbg_readinode2(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES #define dbg_fragtree(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fragtree(fmt, ...) +#define dbg_fragtree(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif #ifdef JFFS2_DBG_FRAGTREE2_MESSAGES #define dbg_fragtree2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fragtree2(fmt, ...) +#define dbg_fragtree2(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Directory entry list manilulation debugging messages */ #ifdef JFFS2_DBG_DENTLIST_MESSAGES #define dbg_dentlist(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_dentlist(fmt, ...) +#define dbg_dentlist(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Print the messages about manipulating node_refs */ #ifdef JFFS2_DBG_NODEREF_MESSAGES #define dbg_noderef(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_noderef(fmt, ...) +#define dbg_noderef(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Manipulations with the list of inodes (JFFS2 inocache) */ #ifdef JFFS2_DBG_INOCACHE_MESSAGES #define dbg_inocache(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_inocache(fmt, ...) +#define dbg_inocache(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Summary debugging messages */ #ifdef JFFS2_DBG_SUMMARY_MESSAGES #define dbg_summary(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_summary(fmt, ...) +#define dbg_summary(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* File system build messages */ #ifdef JFFS2_DBG_FSBUILD_MESSAGES #define dbg_fsbuild(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_fsbuild(fmt, ...) +#define dbg_fsbuild(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Watch the object allocations */ #ifdef JFFS2_DBG_MEMALLOC_MESSAGES #define dbg_memalloc(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_memalloc(fmt, ...) +#define dbg_memalloc(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* Watch the XATTR subsystem */ #ifdef JFFS2_DBG_XATTR_MESSAGES #define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else -#define dbg_xattr(fmt, ...) +#define dbg_xattr(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* "Sanity" checks */ diff --git a/cpukit/libfs/src/jffs2/src/dir-rtems.c b/cpukit/libfs/src/jffs2/src/dir-rtems.c index 9fa70bcec4..91906bac94 100644 --- a/cpukit/libfs/src/jffs2/src/dir-rtems.c +++ b/cpukit/libfs/src/jffs2/src/dir-rtems.c @@ -6,11 +6,11 @@ * Copyright © 2001-2003 Free Software Foundation, Inc. * Copyright © 2001-2007 Red Hat, Inc. * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -214,6 +214,8 @@ int jffs2_mknod( f = JFFS2_INODE_INFO(inode); + mutex_lock(&f->sem); + inode->i_size = datalen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); diff --git a/cpukit/libfs/src/jffs2/src/erase.c b/cpukit/libfs/src/jffs2/src/erase.c index 9ffffafaea..6966ed9b0a 100644 --- a/cpukit/libfs/src/jffs2/src/erase.c +++ b/cpukit/libfs/src/jffs2/src/erase.c @@ -45,9 +45,9 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n", __func__, jeb->offset, jeb->offset, jeb->offset + c->sector_size); - instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + instr = kzalloc(sizeof(struct erase_info), GFP_KERNEL); if (!instr) { - pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); + pr_warn("kzalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); @@ -59,8 +59,6 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, return; } - memset(instr, 0, sizeof(*instr)); - instr->addr = jeb->offset; instr->len = c->sector_size; @@ -403,7 +401,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb { size_t retlen; int ret; - uint32_t uninitialized_var(bad_offset); + uint32_t bad_offset; switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { case -EAGAIN: goto refile; 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 8bc3d85cc3..029cba6618 100644 --- a/cpukit/libfs/src/jffs2/src/fs-rtems.c +++ b/cpukit/libfs/src/jffs2/src/fs-rtems.c @@ -6,12 +6,12 @@ * Copyright © 2001-2003 Free Software Foundation, Inc. * Copyright © 2001-2007 Red Hat, Inc. * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> - * Copyright © 2013, 2016 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright (C) 2013, 2016 embedded brains GmbH & Co. KG * * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com> * Contributors: David Woodhouse, Nick Garnett, Richard Panton. * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -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 */ @@ -577,6 +579,9 @@ static int rtems_jffs2_ioctl( break; case RTEMS_JFFS2_FORCE_GARBAGE_COLLECTION: eno = -jffs2_garbage_collect_pass(&inode->i_sb->jffs2_sb); + if (!eno) { + eno = -jffs2_flush_wbuf_pad(&inode->i_sb->jffs2_sb); + } break; default: eno = EINVAL; @@ -1049,10 +1054,26 @@ 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); + jffs2_flush_wbuf_pad(c); + } +#endif + + jffs2_sum_exit(c); icache_evict(root_i, NULL); assert(root_i->i_cache_next == NULL); @@ -1062,6 +1083,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 +1239,127 @@ 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; + +/* + * All delayed work structs are initialized and added to the chain during FS + * init. Must be called with no locks held + */ +static void add_delayed_work_to_chain(struct delayed_work *work) +{ + /* Initialize delayed work */ + mutex_init(&work->dw_mutex); + work->pending = false; + _Chain_Initialize_node(&work->work.node); \ + work->callback = NULL; + + mutex_lock(&delayed_work_mutex); + rtems_chain_append_unprotected(&delayed_work_chain, &work->work.node); + mutex_unlock(&delayed_work_mutex); +} + +void jffs2_queue_delayed_work(struct delayed_work *work, int delay_ms) +{ + mutex_lock(&work->dw_mutex); + if (!work->pending) { + work->execution_time = rtems_clock_get_uptime_nanoseconds(); + work->execution_time += delay_ms*1000000; + work->pending = true; + } + mutex_unlock(&work->dw_mutex); +} + +/* Clean up during FS unmount */ +static void jffs2_remove_delayed_work(struct delayed_work *dwork) +{ + mutex_lock(&delayed_work_mutex); + rtems_chain_extract_unprotected(&dwork->work.node); + mutex_unlock(&delayed_work_mutex); + /* Don't run pending delayed work, this will happen during unmount */ +} + +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; + node = rtems_chain_next(node); + + if (!work->pending) { + continue; + } + + if (rtems_clock_get_uptime_nanoseconds() < work->execution_time) { + continue; + } + + mutex_lock(&work->dw_mutex); + work->pending = false; + mutex_unlock(&work->dw_mutex); + + rtems_jffs2_do_lock(work->sb); + work->callback(&work->work); + rtems_jffs2_do_unlock(work->sb); + } + 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(); + usleep(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 @@ -1235,13 +1383,29 @@ int rtems_jffs2_initialize( err = -ENOMEM; } - sb = &fs_info->sb; - c = JFFS2_SB_INFO(sb); if (err == 0) { + sb = &fs_info->sb; + c = JFFS2_SB_INFO(sb); +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + c->wbuf_dwork.sb = sb; + add_delayed_work_to_chain(&c->wbuf_dwork); +#endif + spin_lock_init(&c->erase_completion_lock); + spin_lock_init(&c->inocache_lock); + c->mtd = NULL; 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 +1427,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 +1479,11 @@ int rtems_jffs2_initialize( return 0; } else { if (fs_info != NULL) { +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + jffs2_remove_delayed_work(&c->wbuf_dwork); +#endif + 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); @@ -1316,30 +1504,6 @@ int rtems_jffs2_initialize( // //========================================================================== -unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, - struct jffs2_inode_info *f, - unsigned long offset, - unsigned long *priv) -{ - int ret; - struct super_block *sb = OFNI_BS_2SFFJ(c); - unsigned char *gc_buffer = &sb->s_gc_buffer[0]; - - ret = jffs2_read_inode_range(c, f, gc_buffer, - offset & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE); - if (ret) - return ERR_PTR(ret); - - return gc_buffer; -} - -void jffs2_gc_release_page(struct jffs2_sb_info *c, - unsigned char *ptr, - unsigned long *priv) -{ - /* Do nothing */ -} - static struct _inode *new_inode(struct super_block *sb) { @@ -1366,6 +1530,8 @@ static struct _inode *new_inode(struct super_block *sb) inode->i_cache_next = NULL; // Newest inode, about to be cached + mutex_init(&JFFS2_INODE_INFO(inode)->sem); + // Add to the icache for (cached_inode = sb->s_root; cached_inode != NULL; cached_inode = cached_inode->i_cache_next) { @@ -1482,7 +1648,14 @@ void jffs2_iput(struct _inode *i) static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) { - memset(f, 0, sizeof(*f)); + /* These must be set manually to preserve other members */ + f->highest_version = 0; + f->fragtree = RB_ROOT; + f->metadata = NULL; + f->dents = NULL; + f->target = NULL; + f->flags = 0; + f->usercompr = 0; } static void jffs2_clear_inode (struct _inode *inode) @@ -1567,6 +1740,7 @@ static int jffs2_read_inode (struct _inode *inode) c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); + mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); diff --git a/cpukit/libfs/src/jffs2/src/gc.c b/cpukit/libfs/src/jffs2/src/gc.c index f557075ab8..3d0d8c56d9 100644 --- a/cpukit/libfs/src/jffs2/src/gc.c +++ b/cpukit/libfs/src/jffs2/src/gc.c @@ -1173,12 +1173,17 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { +#ifndef __rtems__ + struct inode *inode = OFNI_EDONI_2SFFJ(f); +#endif /* __rtems__ */ struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; uint32_t alloclen, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; - unsigned long pg; +#ifndef __rtems__ + struct page *page; +#endif /* __rtems__ */ unsigned char *pg_ptr; memset(&ri, 0, sizeof(ri)); @@ -1333,15 +1338,26 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era * end up here trying to GC the *same* page that jffs2_write_begin() is * trying to write out, read_cache_page() will not deadlock. */ mutex_unlock(&f->sem); - pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); - mutex_lock(&f->sem); - - if (IS_ERR(pg_ptr)) { +#ifndef __rtems__ + page = read_cache_page(inode->i_mapping, start >> PAGE_SHIFT, + __jffs2_read_folio, NULL); + if (IS_ERR(page)) { pr_warn("read_cache_page() returned error: %ld\n", - PTR_ERR(pg_ptr)); - return PTR_ERR(pg_ptr); + PTR_ERR(page)); + mutex_lock(&f->sem); + return PTR_ERR(page); } + pg_ptr = kmap(page); +#else /* __rtems__ */ + pg_ptr = &OFNI_BS_2SFFJ(c)->s_gc_buffer[0]; + ret = jffs2_read_inode_range(c, f, pg_ptr, + start & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE); + if (ret) + return ret; +#endif /* __rtems__ */ + mutex_lock(&f->sem); + offset = start; while(offset < orig_end) { uint32_t datalen; @@ -1404,6 +1420,9 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era } } - jffs2_gc_release_page(c, pg_ptr, &pg); +#ifndef __rtems__ + kunmap(page); + put_page(page); +#endif /* __rtems__ */ return ret; } diff --git a/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h b/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h index 6b6050e9eb..64ff8abc01 100644 --- a/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h +++ b/cpukit/libfs/src/jffs2/src/jffs2_fs_i.h @@ -18,11 +18,11 @@ #include <linux/mutex.h> struct jffs2_inode_info { - /* We need an internal mutex similar to inode->i_mutex. + /* We need an internal mutex similar to inode->i_rwsem. Unfortunately, we can't used the existing one, because either the GC would deadlock, or we'd have to release it before letting GC proceed. Or we'd have to put ugliness - into the GC code so it didn't attempt to obtain the i_mutex + into the GC code so it didn't attempt to obtain the i_rwsem for the inode(s) which are already locked */ struct mutex sem; diff --git a/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h b/cpukit/libfs/src/jffs2/src/jffs2_fs_sb.h index 778275f48a..7960f92f85 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. * @@ -38,6 +40,7 @@ struct jffs2_mount_opts { * users. This is implemented simply by means of not allowing the * latter users to write to the file system if the amount if the * available space is less then 'rp_size'. */ + bool set_rp_size; unsigned int rp_size; }; @@ -72,6 +75,9 @@ struct jffs2_sb_info { uint32_t bad_size; uint32_t sector_size; uint32_t unchecked_size; +#ifdef __rtems__ + uint32_t obsolete_size; +#endif uint32_t nr_free_blocks; uint32_t nr_erasing_blocks; diff --git a/cpukit/libfs/src/jffs2/src/nodelist.h b/cpukit/libfs/src/jffs2/src/nodelist.h index 143f60dbcb..b30aa5bbf6 100644 --- a/cpukit/libfs/src/jffs2/src/nodelist.h +++ b/cpukit/libfs/src/jffs2/src/nodelist.h @@ -259,7 +259,7 @@ struct jffs2_full_dirent uint32_t ino; /* == zero for unlink */ unsigned int nhash; unsigned char type; - unsigned char name[0]; + unsigned char name[]; }; /* @@ -351,14 +351,14 @@ static inline struct jffs2_node_frag *frag_last(struct rb_root *root) #define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) #define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) -#define frag_erase(frag, list) rb_erase(&frag->rb, list); +#define frag_erase(frag, list) rb_erase(&frag->rb, list) #define tn_next(tn) rb_entry(rb_next(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_prev(tn) rb_entry(rb_prev(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_parent(tn) rb_entry(rb_parent(&(tn)->rb), struct jffs2_tmp_dnode_info, rb) #define tn_left(tn) rb_entry((tn)->rb.rb_left, struct jffs2_tmp_dnode_info, rb) #define tn_right(tn) rb_entry((tn)->rb.rb_right, struct jffs2_tmp_dnode_info, rb) -#define tn_erase(tn, list) rb_erase(&tn->rb, list); +#define tn_erase(tn, list) rb_erase(&tn->rb, list) #define tn_last(list) rb_entry(rb_last(list), struct jffs2_tmp_dnode_info, rb) #define tn_first(list) rb_entry(rb_first(list), struct jffs2_tmp_dnode_info, rb) diff --git a/cpukit/libfs/src/jffs2/src/os-rtems.h b/cpukit/libfs/src/jffs2/src/os-rtems.h index db1be61e67..4bc6f5df13 100644 --- a/cpukit/libfs/src/jffs2/src/os-rtems.h +++ b/cpukit/libfs/src/jffs2/src/os-rtems.h @@ -2,11 +2,11 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2002-2003 Free Software Foundation, Inc. - * Copyright © 2013 embedded brains GmbH <rtems@embedded-brains.de> + * Copyright © 2013 embedded brains GmbH & Co. KG * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * - * Port to the RTEMS by embedded brains GmbH. + * Port to the RTEMS by embedded brains GmbH & Co. KG * * For licensing information, see the file 'LICENCE' in this directory. * @@ -52,11 +52,10 @@ 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) ((struct _inode *) ( ((char *)f) - ((char *)(&((struct _inode *)NULL)->jffs2_i)) ) ) +#define OFNI_EDONI_2SFFJ(f) RTEMS_CONTAINER_OF(f, struct _inode, jffs2_i) #define ITIME(sec) (sec) #define I_SEC(tv) (tv) @@ -101,6 +100,10 @@ struct _inode { struct super_block { struct jffs2_sb_info jffs2_sb; + /* + * If granular locking is ever enabled for JFFS2, the inode cache + * (s_root) needs to be protected due to NAND delayed writes. + */ struct _inode * s_root; rtems_jffs2_flash_control *s_flash_control; rtems_jffs2_compressor_control *s_compressor_control; @@ -108,6 +111,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) @@ -136,9 +140,6 @@ struct _inode *jffs2_iget(struct super_block *sb, cyg_uint32 ino); void jffs2_iput(struct _inode * i); void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink); -unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - unsigned long offset, unsigned long *priv); -void jffs2_gc_release_page(struct jffs2_sb_info *c, unsigned char *pg, unsigned long *priv); /* Avoid polluting RTEMS namespace with names not starting in jffs2_ */ #define os_to_jffs2_mode(x) jffs2_from_os_mode(x) @@ -154,13 +155,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); @@ -176,8 +179,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) @@ -194,9 +198,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/readinode.c b/cpukit/libfs/src/jffs2/src/readinode.c index e6c9452c03..9f554f8644 100644 --- a/cpukit/libfs/src/jffs2/src/readinode.c +++ b/cpukit/libfs/src/jffs2/src/readinode.c @@ -693,6 +693,22 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r jffs2_free_full_dirent(fd); return -EIO; } + +#ifdef CONFIG_JFFS2_SUMMARY + /* + * we use CONFIG_JFFS2_SUMMARY because without it, we + * have checked it while mounting + */ + crc = crc32(0, fd->name, rd->nsize); + if (unlikely(crc != je32_to_cpu(rd->name_crc))) { + JFFS2_NOTICE("name CRC failed on dirent node at" + "%#08x: read %#08x,calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + jffs2_mark_node_obsolete(c, ref); + jffs2_free_full_dirent(fd); + return 0; + } +#endif } fd->nhash = full_name_hash(NULL, fd->name, rd->nsize); @@ -1294,7 +1310,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, dbg_readinode("symlink's target '%s' cached\n", f->target); } - /* fall through... */ + fallthrough; case S_IFBLK: case S_IFCHR: @@ -1434,11 +1450,12 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) } jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - +#ifdef __rtems__ if (f->target) { kfree(f->target); f->target = NULL; } +#endif /* __rtems__ */ fds = f->dents; while(fds) { diff --git a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h index 1017a71c1f..0e88d81989 100644 --- a/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h +++ b/cpukit/libfs/src/jffs2/src/rtems-jffs2-config.h @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2018 embedded brains Gmbh (http://www.embedded-brains.de) + * Copyright (C) 2018 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,3 +33,5 @@ #define __ECOS 1 #define KBUILD_MODNAME "JFFS2" +#define fallthrough __attribute__((__fallthrough__)) +#define CONFIG_JFFS2_FS_WRITEBUFFER diff --git a/cpukit/libfs/src/jffs2/src/scan.c b/cpukit/libfs/src/jffs2/src/scan.c index 177a0cdd3f..8ac4a40414 100644 --- a/cpukit/libfs/src/jffs2/src/scan.c +++ b/cpukit/libfs/src/jffs2/src/scan.c @@ -139,7 +139,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); ret = -ENOMEM; - goto out; + goto out_buf; } } @@ -264,26 +264,47 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) } #endif if (c->nr_erasing_blocks) { - if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { +#ifdef __rtems__ + if (c->obsolete_size != c->dirty_size) { +#endif + if (!c->used_size && !c->unchecked_size && + ((c->nr_free_blocks+empty_blocks+bad_blocks) != c->nr_blocks || bad_blocks == c->nr_blocks)) { pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n", empty_blocks, bad_blocks, c->nr_blocks); +#ifdef __rtems__ + pr_notice("nr_erasing_blocks %d, used 0x%x, dirty 0x%x, wasted 0x%x, free 0x%x, erasing 0x%x, bad 0x%x, obsolete 0x%x, unchecked 0x%x\n", + c->nr_erasing_blocks, + c->used_size, + c->dirty_size, + c->wasted_size, + c->free_size, + c->erasing_size, + c->bad_size, + c->obsolete_size, + c->unchecked_size); +#endif ret = -EIO; goto out; } +#ifdef __rtems__ + } +#endif spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); spin_unlock(&c->erase_completion_lock); } ret = 0; out: + jffs2_sum_reset_collected(s); + kfree(s); + out_buf: if (buf_size) kfree(flashbuf); #ifndef __ECOS else mtd_unpoint(c->mtd, 0, c->mtd->size); #endif - kfree(s); return ret; } @@ -530,8 +551,11 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo err = jffs2_fill_scan_buf(c, sumptr, jeb->offset + c->sector_size - sumlen, sumlen - buf_len); - if (err) + if (err) { + if (sumlen > buf_size) + kfree(sumptr); return err; + } } } @@ -640,6 +664,9 @@ scan_more: sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node)); +#ifdef __rtems__ + c->obsolete_size += (jeb->offset + c->sector_size - ofs); +#endif if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs))) return err; break; @@ -790,6 +817,9 @@ scan_more: if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); +#ifdef __rtems__ + c->obsolete_size += PAD(je32_to_cpu(node->totlen)); +#endif continue; } @@ -1078,7 +1108,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo memcpy(&fd->name, rd->name, checkedlen); fd->name[checkedlen] = 0; - crc = crc32(0, fd->name, rd->nsize); + crc = crc32(0, fd->name, checkedlen); if (crc != je32_to_cpu(rd->name_crc)) { pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", __func__, ofs, je32_to_cpu(rd->name_crc), crc); diff --git a/cpukit/libfs/src/jffs2/src/summary.h b/cpukit/libfs/src/jffs2/src/summary.h index 60207a2ae9..36d9a12807 100644 --- a/cpukit/libfs/src/jffs2/src/summary.h +++ b/cpukit/libfs/src/jffs2/src/summary.h @@ -61,7 +61,7 @@ struct jffs2_sum_dirent_flash jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ - uint8_t name[0]; /* dirent name */ + uint8_t name[]; /* dirent name */ } __attribute__((packed)); struct jffs2_sum_xattr_flash @@ -117,7 +117,7 @@ struct jffs2_sum_dirent_mem jint32_t ino; /* == zero for unlink */ uint8_t nsize; /* dirent name size */ uint8_t type; /* dirent type */ - uint8_t name[0]; /* dirent name */ + uint8_t name[]; /* dirent name */ } __attribute__((packed)); struct jffs2_sum_xattr_mem @@ -194,18 +194,18 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb #define jffs2_sum_active() (0) #define jffs2_sum_init(a) (0) -#define jffs2_sum_exit(a) +#define jffs2_sum_exit(a) do { } while (0) #define jffs2_sum_disable_collecting(a) #define jffs2_sum_is_disabled(a) (0) -#define jffs2_sum_reset_collected(a) +#define jffs2_sum_reset_collected(a) do { } while (0) #define jffs2_sum_add_kvec(a,b,c,d) (0) -#define jffs2_sum_move_collected(a,b) +#define jffs2_sum_move_collected(a,b) do { } while (0) #define jffs2_sum_write_sumnode(a) (0) -#define jffs2_sum_add_padding_mem(a,b) -#define jffs2_sum_add_inode_mem(a,b,c) -#define jffs2_sum_add_dirent_mem(a,b,c) -#define jffs2_sum_add_xattr_mem(a,b,c) -#define jffs2_sum_add_xref_mem(a,b,c) +#define jffs2_sum_add_padding_mem(a,b) do { } while (0) +#define jffs2_sum_add_inode_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_dirent_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_xattr_mem(a,b,c) do { } while (0) +#define jffs2_sum_add_xref_mem(a,b,c) do { } while (0) #define jffs2_sum_scan_sumnode(a,b,c,d,e) (0) #endif /* CONFIG_JFFS2_SUMMARY */ diff --git a/cpukit/libfs/src/jffs2/src/wbuf.c b/cpukit/libfs/src/jffs2/src/wbuf.c new file mode 100644 index 0000000000..04e2b03efc --- /dev/null +++ b/cpukit/libfs/src/jffs2/src/wbuf.c @@ -0,0 +1,1352 @@ +#include "rtems-jffs2-config.h" + +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de> + * + * Created by David Woodhouse <dwmw2@infradead.org> + * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/crc32.h> +#include <linux/mtd/rawnand.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/writeback.h> + +#include "nodelist.h" + +/* For testing write failures */ +#undef BREAKME +#undef BREAKMEHEADER + +#ifdef BREAKME +static unsigned char *brokenbuf; +#endif + +#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) ) +#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) ) + +/* max. erase failures before we mark a block bad */ +#define MAX_ERASE_FAILURES 2 + +struct jffs2_inodirty { + uint32_t ino; + struct jffs2_inodirty *next; +}; + +static struct jffs2_inodirty inodirty_nomem; + +static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) +{ + struct jffs2_inodirty *this = c->wbuf_inodes; + + /* If a malloc failed, consider _everything_ dirty */ + if (this == &inodirty_nomem) + return 1; + + /* If ino == 0, _any_ non-GC writes mean 'yes' */ + if (this && !ino) + return 1; + + /* Look to see if the inode in question is pending in the wbuf */ + while (this) { + if (this->ino == ino) + return 1; + this = this->next; + } + return 0; +} + +static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) +{ + struct jffs2_inodirty *this; + + this = c->wbuf_inodes; + + if (this != &inodirty_nomem) { + while (this) { + struct jffs2_inodirty *next = this->next; + kfree(this); + this = next; + } + } + c->wbuf_inodes = NULL; +} + +static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) +{ + struct jffs2_inodirty *new; + + /* Schedule delayed write-buffer write-out */ + jffs2_dirty_trigger(c); + + if (jffs2_wbuf_pending_for_ino(c, ino)) + return; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + jffs2_dbg(1, "No memory to allocate inodirty. Fallback to all considered dirty\n"); + jffs2_clear_wbuf_ino_list(c); + c->wbuf_inodes = &inodirty_nomem; + return; + } + new->ino = ino; + new->next = c->wbuf_inodes; + c->wbuf_inodes = new; + return; +} + +static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) +{ + struct list_head *this, *next; + static int n; + + if (list_empty(&c->erasable_pending_wbuf_list)) + return; + + list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + jffs2_dbg(1, "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", + jeb->offset); + list_del(this); + if ((jiffies + (n++)) & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + jffs2_dbg(1, "...and adding to erase_pending_list\n"); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + jffs2_dbg(1, "...and adding to erasable_list\n"); + list_add_tail(&jeb->list, &c->erasable_list); + } + } +} + +#define REFILE_NOTEMPTY 0 +#define REFILE_ANYWAY 1 + +static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) +{ + jffs2_dbg(1, "About to refile bad block at %08x\n", jeb->offset); + + /* File the existing block on the bad_used_list.... */ + if (c->nextblock == jeb) + c->nextblock = NULL; + else /* Not sure this should ever happen... need more coffee */ + list_del(&jeb->list); + if (jeb->first_node) { + jffs2_dbg(1, "Refiling block at %08x to bad_used_list\n", + jeb->offset); + list_add(&jeb->list, &c->bad_used_list); + } else { + BUG_ON(allow_empty == REFILE_NOTEMPTY); + /* It has to have had some nodes or we couldn't be here */ + jffs2_dbg(1, "Refiling block at %08x to erase_pending_list\n", + jeb->offset); + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } + + if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { + uint32_t oldfree = jeb->free_size; + + jffs2_link_node_ref(c, jeb, + (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE, + oldfree, NULL); + /* convert to wasted */ + c->wasted_size += oldfree; + jeb->wasted_size += oldfree; + c->dirty_size -= oldfree; + jeb->dirty_size -= oldfree; + } + + jffs2_dbg_dump_block_lists_nolock(c); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); +} + +static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_raw_node_ref *raw, + union jffs2_node_union *node) +{ + struct jffs2_node_frag *frag; + struct jffs2_full_dirent *fd; + + dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n", + node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype)); + + BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 && + je16_to_cpu(node->u.magic) != 0); + + switch (je16_to_cpu(node->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + if (f->metadata && f->metadata->raw == raw) { + dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata); + return &f->metadata->raw; + } + frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset)); + BUG_ON(!frag); + /* Find a frag which refers to the full_dnode we want to modify */ + while (!frag->node || frag->node->raw != raw) { + frag = frag_next(frag); + BUG_ON(!frag); + } + dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node); + return &frag->node->raw; + + case JFFS2_NODETYPE_DIRENT: + for (fd = f->dents; fd; fd = fd->next) { + if (fd->raw == raw) { + dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd); + return &fd->raw; + } + } + BUG(); + + default: + dbg_noderef("Don't care about replacing raw for nodetype %x\n", + je16_to_cpu(node->u.nodetype)); + break; + } + return NULL; +} + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY +static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, + uint32_t ofs) +{ + int ret; + size_t retlen; + char *eccstr; + + ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); + if (ret && ret != -EUCLEAN && ret != -EBADMSG) { + pr_warn("%s(): Read back of page at %08x failed: %d\n", + __func__, c->wbuf_ofs, ret); + return ret; + } else if (retlen != c->wbuf_pagesize) { + pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d\n", + __func__, ofs, retlen, c->wbuf_pagesize); + return -EIO; + } + if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) + return 0; + + if (ret == -EUCLEAN) + eccstr = "corrected"; + else if (ret == -EBADMSG) + eccstr = "correction failed"; + else + eccstr = "OK or unused"; + + pr_warn("Write verify error (ECC %s) at %08x. Wrote:\n", + eccstr, c->wbuf_ofs); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf, c->wbuf_pagesize, 0); + + pr_warn("Read back:\n"); + print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, + c->wbuf_verify, c->wbuf_pagesize, 0); + + return -EIO; +} +#else +#define jffs2_verify_write(c,b,o) (0) +#endif + +/* Recover from failure to write wbuf. Recover the nodes up to the + * wbuf, not the one which we were starting to try to write. */ + +static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb, *new_jeb; + struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL; + size_t retlen; + int ret; + int nr_refile = 0; + unsigned char *buf; + uint32_t start, end, ofs, len; + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + spin_lock(&c->erase_completion_lock); + if (c->wbuf_ofs % c->mtd->erasesize) + jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); + else + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + spin_unlock(&c->erase_completion_lock); + + BUG_ON(!ref_obsolete(jeb->last_node)); + + /* Find the first node to be recovered, by skipping over every + node which ends before the wbuf starts, or which is obsolete. */ + for (next = raw = jeb->first_node; next; raw = next) { + next = ref_next(raw); + + if (ref_obsolete(raw) || + (next && ref_offset(next) <= c->wbuf_ofs)) { + dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw)), + c->wbuf_ofs); + continue; + } + dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw))); + + first_raw = raw; + break; + } + + if (!first_raw) { + /* All nodes were obsolete. Nothing to recover. */ + jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad\n"); + c->wbuf_len = 0; + return; + } + + start = ref_offset(first_raw); + end = ref_offset(jeb->last_node); + nr_refile = 1; + + /* Count the number of refs which need to be copied */ + while ((raw = ref_next(raw)) != jeb->last_node) + nr_refile++; + + dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); + + buf = NULL; + if (start < c->wbuf_ofs) { + /* First affected node was already partially written. + * Attempt to reread the old data into our buffer. */ + + buf = kmalloc(end - start, GFP_KERNEL); + if (!buf) { + pr_crit("Malloc failure in wbuf recovery. Data loss ensues.\n"); + + goto read_failed; + } + + /* Do the read... */ + ret = mtd_read(c->mtd, start, c->wbuf_ofs - start, &retlen, + buf); + + /* ECC recovered ? */ + if ((ret == -EUCLEAN || ret == -EBADMSG) && + (retlen == c->wbuf_ofs - start)) + ret = 0; + + if (ret || retlen != c->wbuf_ofs - start) { + pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.\n"); + + kfree(buf); + buf = NULL; + read_failed: + first_raw = ref_next(first_raw); + nr_refile--; + while (first_raw && ref_obsolete(first_raw)) { + first_raw = ref_next(first_raw); + nr_refile--; + } + + /* If this was the only node to be recovered, give up */ + if (!first_raw) { + c->wbuf_len = 0; + return; + } + + /* It wasn't. Go on and try to recover nodes complete in the wbuf */ + start = ref_offset(first_raw); + dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); + + } else { + /* Read succeeded. Copy the remaining data from the wbuf */ + memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); + } + } + /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. + Either 'buf' contains the data, or we find it in the wbuf */ + + /* ... and get an allocation of space from a shiny new block instead */ + ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.\n"); + kfree(buf); + return; + } + + /* The summary is not recovered, so it must be disabled for this erase block */ + jffs2_sum_disable_collecting(c->summary); + + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile); + if (ret) { + pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.\n"); + kfree(buf); + return; + } + + ofs = write_ofs(c); + + if (end-start >= c->wbuf_pagesize) { + /* Need to do another write immediately, but it's possible + that this is just because the wbuf itself is completely + full, and there's nothing earlier read back from the + flash. Hence 'buf' isn't necessarily what we're writing + from. */ + unsigned char *rewrite_buf = buf?:c->wbuf; + uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); + + jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover\n", + towrite, ofs); + +#ifdef BREAKMEHEADER + static int breakme; + if (breakme++ == 20) { + pr_notice("Faking write error at 0x%08x\n", ofs); + breakme = 0; + mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf); + ret = -EIO; + } else +#endif + ret = mtd_write(c->mtd, ofs, towrite, &retlen, + rewrite_buf); + + if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { + /* Argh. We tried. Really we did. */ + pr_crit("Recovery of wbuf failed due to a second write error\n"); + kfree(buf); + + if (retlen) + jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL); + + return; + } + pr_notice("Recovery of wbuf succeeded to %08x\n", ofs); + + c->wbuf_len = (end - start) - towrite; + c->wbuf_ofs = ofs + towrite; + memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); + /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ + } else { + /* OK, now we're left with the dregs in whichever buffer we're using */ + if (buf) { + memcpy(c->wbuf, buf, end-start); + } else { + memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); + } + c->wbuf_ofs = ofs; + c->wbuf_len = end - start; + } + + /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ + new_jeb = &c->blocks[ofs / c->sector_size]; + + spin_lock(&c->erase_completion_lock); + for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) { + uint32_t rawlen = ref_totlen(c, jeb, raw); + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *new_ref; + struct jffs2_raw_node_ref **adjust_ref = NULL; + struct jffs2_inode_info *f = NULL; + + jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x\n", + rawlen, ref_offset(raw), ref_flags(raw), ofs); + + ic = jffs2_raw_ref_to_ic(raw); + + /* Ick. This XATTR mess should be fixed shortly... */ + if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) { + struct jffs2_xattr_datum *xd = (void *)ic; + BUG_ON(xd->node != raw); + adjust_ref = &xd->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) { + struct jffs2_xattr_datum *xr = (void *)ic; + BUG_ON(xr->node != raw); + adjust_ref = &xr->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) { + struct jffs2_raw_node_ref **p = &ic->nodes; + + /* Remove the old node from the per-inode list */ + while (*p && *p != (void *)ic) { + if (*p == raw) { + (*p) = (raw->next_in_ino); + raw->next_in_ino = NULL; + break; + } + p = &((*p)->next_in_ino); + } + + if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) { + /* If it's an in-core inode, then we have to adjust any + full_dirent or full_dnode structure to point to the + new version instead of the old */ + f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink); + if (IS_ERR(f)) { + /* Should never happen; it _must_ be present */ + JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n", + ic->ino, PTR_ERR(f)); + BUG(); + } + /* We don't lock f->sem. There's a number of ways we could + end up in here with it already being locked, and nobody's + going to modify it on us anyway because we hold the + alloc_sem. We're only changing one ->raw pointer too, + which we can get away with without upsetting readers. */ + adjust_ref = jffs2_incore_replace_raw(c, f, raw, + (void *)(buf?:c->wbuf) + (ref_offset(raw) - start)); + } else if (unlikely(ic->state != INO_STATE_PRESENT && + ic->state != INO_STATE_CHECKEDABSENT && + ic->state != INO_STATE_GC)) { + JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state); + BUG(); + } + } + + new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic); + + if (adjust_ref) { + BUG_ON(*adjust_ref != raw); + *adjust_ref = new_ref; + } + if (f) + jffs2_gc_release_inode(c, f); + + if (!ref_obsolete(raw)) { + jeb->dirty_size += rawlen; + jeb->used_size -= rawlen; + c->dirty_size += rawlen; + c->used_size -= rawlen; + raw->flash_offset = ref_offset(raw) | REF_OBSOLETE; + BUG_ON(raw->next_in_ino); + } + ofs += rawlen; + } + + kfree(buf); + + /* Fix up the original jeb now it's on the bad_list */ + if (first_raw == jeb->first_node) { + jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list\n", + jeb->offset); + list_move(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_garbage_collect_trigger(c); + } + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + + jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); + + spin_unlock(&c->erase_completion_lock); + + jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", + c->wbuf_ofs, c->wbuf_len); + +} + +/* Meaning of pad argument: + 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. + 1: Pad, do not adjust nextblock free_size + 2: Pad, adjust nextblock free_size +*/ +#define NOPAD 0 +#define PAD_NOACCOUNT 1 +#define PAD_ACCOUNTING 2 + +static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) +{ + struct jffs2_eraseblock *wbuf_jeb; + int ret; + size_t retlen; + + /* Nothing to do if not write-buffering the flash. In particular, we shouldn't + del_timer() the timer we never initialised. */ + if (!jffs2_is_writebuffered(c)) + return 0; + + if (!mutex_is_locked(&c->alloc_sem)) { + pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!\n"); + BUG(); + } + + if (!c->wbuf_len) /* already checked c->wbuf above */ + return 0; + + wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1)) + return -ENOMEM; + + /* claim remaining space on the page + this happens, if we have a change to a new block, + or if fsync forces us to flush the writebuffer. + if we have a switch to next page, we will not have + enough remaining space for this. + */ + if (pad ) { + c->wbuf_len = PAD(c->wbuf_len); + + /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR + with 8 byte page size */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); + + if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { + struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); + padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); + padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); + padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); + } + } + /* else jffs2_flash_writev has actually filled in the rest of the + buffer for us, and will deal with the node refs etc. later. */ + +#ifdef BREAKME + static int breakme; + if (breakme++ == 20) { + pr_notice("Faking write error at 0x%08x\n", c->wbuf_ofs); + breakme = 0; + mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, + brokenbuf); + ret = -EIO; + } else +#endif + + ret = mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, + &retlen, c->wbuf); + + if (ret) { + pr_warn("jffs2_flush_wbuf(): Write failed with %d\n", ret); + goto wfail; + } else if (retlen != c->wbuf_pagesize) { + pr_warn("jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", + retlen, c->wbuf_pagesize); + ret = -EIO; + goto wfail; + } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) { + wfail: + jffs2_wbuf_recover(c); + + return ret; + } + + /* Adjust free size of the block if we padded. */ + if (pad) { + uint32_t waste = c->wbuf_pagesize - c->wbuf_len; + + jffs2_dbg(1, "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", + (wbuf_jeb == c->nextblock) ? "next" : "", + wbuf_jeb->offset); + + /* wbuf_pagesize - wbuf_len is the amount of space that's to be + padded. If there is less free space in the block than that, + something screwed up */ + if (wbuf_jeb->free_size < waste) { + pr_crit("jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", + c->wbuf_ofs, c->wbuf_len, waste); + pr_crit("jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", + wbuf_jeb->offset, wbuf_jeb->free_size); + BUG(); + } + + spin_lock(&c->erase_completion_lock); + + jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL); + /* FIXME: that made it count as dirty. Convert to wasted */ + wbuf_jeb->dirty_size -= waste; + c->dirty_size -= waste; + wbuf_jeb->wasted_size += waste; + c->wasted_size += waste; + } else + spin_lock(&c->erase_completion_lock); + + /* Stick any now-obsoleted blocks on the erase_pending_list */ + jffs2_refile_wbuf_blocks(c); + jffs2_clear_wbuf_ino_list(c); + spin_unlock(&c->erase_completion_lock); + + memset(c->wbuf,0xff,c->wbuf_pagesize); + /* adjust write buffer offset, else we get a non contiguous write bug */ + c->wbuf_ofs += c->wbuf_pagesize; + c->wbuf_len = 0; + return 0; +} + +/* Trigger garbage collection to flush the write-buffer. + If ino arg is zero, do it if _any_ real (i.e. not GC) writes are + outstanding. If ino arg non-zero, do it only if a write for the + given inode is outstanding. */ +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) +{ + uint32_t old_wbuf_ofs; + uint32_t old_wbuf_len; + int ret = 0; + + jffs2_dbg(1, "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino); + + if (!c->wbuf) + return 0; + + mutex_lock(&c->alloc_sem); + if (!jffs2_wbuf_pending_for_ino(c, ino)) { + jffs2_dbg(1, "Ino #%d not pending in wbuf. Returning\n", ino); + mutex_unlock(&c->alloc_sem); + return 0; + } + + old_wbuf_ofs = c->wbuf_ofs; + old_wbuf_len = c->wbuf_len; + + if (c->unchecked_size) { + /* GC won't make any progress for a while */ + jffs2_dbg(1, "%s(): padding. Not finished checking\n", + __func__); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); + } else while (old_wbuf_len && + old_wbuf_ofs == c->wbuf_ofs) { + + mutex_unlock(&c->alloc_sem); + + jffs2_dbg(1, "%s(): calls gc pass\n", __func__); + + ret = jffs2_garbage_collect_pass(c); + if (ret) { + /* GC failed. Flush it with padding instead */ + mutex_lock(&c->alloc_sem); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + /* retry flushing wbuf in case jffs2_wbuf_recover + left some data in the wbuf */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); + break; + } + mutex_lock(&c->alloc_sem); + } + + jffs2_dbg(1, "%s(): ends...\n", __func__); + + mutex_unlock(&c->alloc_sem); + return ret; +} + +/* Pad write-buffer to end and write it, wasting space. */ +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) +{ + int ret; + + if (!c->wbuf) + return 0; + + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + /* retry - maybe wbuf recover left some data in wbuf. */ + if (ret) + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + up_write(&c->wbuf_sem); + + return ret; +} + +static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf, + size_t len) +{ + if (len && !c->wbuf_len && (len >= c->wbuf_pagesize)) + return 0; + + if (len > (c->wbuf_pagesize - c->wbuf_len)) + len = c->wbuf_pagesize - c->wbuf_len; + memcpy(c->wbuf + c->wbuf_len, buf, len); + c->wbuf_len += (uint32_t) len; + return len; +} + +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, + unsigned long count, loff_t to, size_t *retlen, + uint32_t ino) +{ + struct jffs2_eraseblock *jeb; + size_t wbuf_retlen, donelen = 0; + uint32_t outvec_to = to; + int ret, invec; + + /* If not writebuffered flash, don't bother */ + if (!jffs2_is_writebuffered(c)) + return jffs2_flash_direct_writev(c, invecs, count, to, retlen); + + down_write(&c->wbuf_sem); + + /* If wbuf_ofs is not initialized, set it to target address */ + if (c->wbuf_ofs == 0xFFFFFFFF) { + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + memset(c->wbuf,0xff,c->wbuf_pagesize); + } + + /* + * Sanity checks on target address. It's permitted to write + * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to + * write at the beginning of a new erase block. Anything else, + * and you die. New block starts at xxx000c (0-b = block + * header) + */ + if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { + /* It's a write to a new block */ + if (c->wbuf_len) { + jffs2_dbg(1, "%s(): to 0x%lx causes flush of wbuf at 0x%08x\n", + __func__, (unsigned long)to, c->wbuf_ofs); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + if (ret) + goto outerr; + } + /* set pointer to new block */ + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + } + + if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { + /* We're not writing immediately after the writebuffer. Bad. */ + pr_crit("%s(): Non-contiguous write to %08lx\n", + __func__, (unsigned long)to); + if (c->wbuf_len) + pr_crit("wbuf was previously %08x-%08x\n", + c->wbuf_ofs, c->wbuf_ofs + c->wbuf_len); + BUG(); + } + + /* adjust alignment offset */ + if (c->wbuf_len != PAGE_MOD(to)) { + c->wbuf_len = PAGE_MOD(to); + /* take care of alignment to next page */ + if (!c->wbuf_len) { + c->wbuf_len = c->wbuf_pagesize; + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + } + + for (invec = 0; invec < count; invec++) { + int vlen = invecs[invec].iov_len; + uint8_t *v = invecs[invec].iov_base; + + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); + + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; + donelen += wbuf_retlen; + v += wbuf_retlen; + + if (vlen >= c->wbuf_pagesize) { + ret = mtd_write(c->mtd, outvec_to, PAGE_DIV(vlen), + &wbuf_retlen, v); + if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen)) + goto outfile; + + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; + c->wbuf_ofs = outvec_to; + donelen += wbuf_retlen; + v += wbuf_retlen; + } + + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } + + outvec_to += wbuf_retlen; + donelen += wbuf_retlen; + } + + /* + * If there's a remainder in the wbuf and it's a non-GC write, + * remember that the wbuf affects this ino + */ + *retlen = donelen; + + if (jffs2_sum_active()) { + int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to); + if (res) + return res; + } + + if (c->wbuf_len && ino) + jffs2_wbuf_dirties_inode(c, ino); + + ret = 0; + up_write(&c->wbuf_sem); + return ret; + +outfile: + /* + * At this point we have no problem, c->wbuf is empty. However + * refile nextblock to avoid writing again to same address. + */ + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[outvec_to / c->sector_size]; + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + + spin_unlock(&c->erase_completion_lock); + +outerr: + *retlen = 0; + up_write(&c->wbuf_sem); + return ret; +} + +/* + * This is the entry for flash write. + * Check, if we work on NAND FLASH, if so build an kvec and write it via vritev +*/ +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf) +{ + struct kvec vecs[1]; + + if (!jffs2_is_writebuffered(c)) + return jffs2_flash_direct_write(c, ofs, len, retlen, buf); + + vecs[0].iov_base = (unsigned char *) buf; + vecs[0].iov_len = len; + return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); +} + +/* + Handle readback from writebuffer and ECC failure return +*/ +int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +{ + loff_t orbf = 0, owbf = 0, lwbf = 0; + int ret; + + if (!jffs2_is_writebuffered(c)) + return mtd_read(c->mtd, ofs, len, retlen, buf); + + /* Read flash */ + down_read(&c->wbuf_sem); + ret = mtd_read(c->mtd, ofs, len, retlen, buf); + + if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) { + if (ret == -EBADMSG) + pr_warn("mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", + len, ofs); + /* + * We have the raw data without ECC correction in the buffer, + * maybe we are lucky and all data or parts are correct. We + * check the node. If data are corrupted node check will sort + * it out. We keep this block, it will fail on write or erase + * and the we mark it bad. Or should we do that now? But we + * should give him a chance. Maybe we had a system crash or + * power loss before the ecc write or a erase was completed. + * So we return success. :) + */ + ret = 0; + } + + /* if no writebuffer available or write buffer empty, return */ + if (!c->wbuf_pagesize || !c->wbuf_len) + goto exit; + + /* if we read in a different block, return */ + if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs)) + goto exit; + + if (ofs >= c->wbuf_ofs) { + owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ + if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ + goto exit; + lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ + if (lwbf > len) + lwbf = len; + } else { + orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ + if (orbf > len) /* is write beyond write buffer ? */ + goto exit; + lwbf = len - orbf; /* number of bytes to copy */ + if (lwbf > c->wbuf_len) + lwbf = c->wbuf_len; + } + if (lwbf > 0) + memcpy(buf+orbf,c->wbuf+owbf,lwbf); + +exit: + up_read(&c->wbuf_sem); + return ret; +} + +#define NR_OOB_SCAN_PAGES 4 + +/* For historical reasons we use only 8 bytes for OOB clean marker */ +#define OOB_CM_SIZE 8 + +static const struct jffs2_unknown_node oob_cleanmarker = +{ + .magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = constant_cpu_to_je32(8) +}; + +/* + * Check, if the out of band area is empty. This function knows about the clean + * marker and if it is present in OOB, treats the OOB as empty anyway. + */ +int jffs2_check_oob_empty(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int mode) +{ + int i, ret; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + struct mtd_oob_ops ops = { }; + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; + ops.oobbuf = c->oobbuf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_read_oob(c->mtd, jeb->offset, &ops); + if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) { + pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret || mtd_is_bitflip(ret)) + ret = -EIO; + return ret; + } + + for(i = 0; i < ops.ooblen; i++) { + if (mode && i < cmlen) + /* Yeah, we know about the cleanmarker */ + continue; + + if (ops.oobbuf[i] != 0xFF) { + jffs2_dbg(2, "Found %02x at %x in OOB for " + "%08x\n", ops.oobbuf[i], i, jeb->offset); + return 1; + } + } + + return 0; +} + +/* + * Check for a valid cleanmarker. + * Returns: 0 if a valid cleanmarker was found + * 1 if no cleanmarker was found + * negative error code if an error occurred + */ +int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + struct mtd_oob_ops ops = { }; + int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = cmlen; + ops.oobbuf = c->oobbuf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_read_oob(c->mtd, jeb->offset, &ops); + if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) { + pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret || mtd_is_bitflip(ret)) + ret = -EIO; + return ret; + } + + return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); +} + +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + int ret; + struct mtd_oob_ops ops = { }; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); + + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooblen = cmlen; + ops.oobbuf = (uint8_t *)&oob_cleanmarker; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = NULL; + + ret = mtd_write_oob(c->mtd, jeb->offset, &ops); + if (ret || ops.oobretlen != ops.ooblen) { + pr_err("cannot write OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; + return ret; + } + + return 0; +} + +/* + * On NAND we try to mark this block bad. If the block was erased more + * than MAX_ERASE_FAILURES we mark it finally bad. + * Don't care about failures. This block remains on the erase-pending + * or badblock list as long as nobody manipulates the flash with + * a bootloader or something like that. + */ + +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) +{ + int ret; + + /* if the count is < max, we try to write the counter to the 2nd page oob area */ + if( ++jeb->bad_count < MAX_ERASE_FAILURES) + return 0; + + pr_warn("marking eraseblock at %08x as bad\n", bad_offset); + ret = mtd_block_markbad(c->mtd, bad_offset); + + if (ret) { + jffs2_dbg(1, "%s(): Write failed for block at %08x: error %d\n", + __func__, jeb->offset, ret); + return ret; + } + return 1; +} + +static struct jffs2_sb_info *work_to_sb(struct work_struct *work) +{ + struct delayed_work *dwork; + + dwork = to_delayed_work(work); + return container_of(dwork, struct jffs2_sb_info, wbuf_dwork); +} + +static void delayed_wbuf_sync(struct work_struct *work) +{ + struct jffs2_sb_info *c = work_to_sb(work); + struct super_block *sb = OFNI_BS_2SFFJ(c); + + if (!sb_rdonly(sb)) { + jffs2_dbg(1, "%s()\n", __func__); + jffs2_flush_wbuf_gc(c, 0); + } +} + +void jffs2_dirty_trigger(struct jffs2_sb_info *c) +{ + struct super_block *sb = OFNI_BS_2SFFJ(c); + unsigned long delay; + + if (sb_rdonly(sb)) + return; + + delay = msecs_to_jiffies(dirty_writeback_interval * 10); + if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay)) + jffs2_dbg(1, "%s()\n", __func__); +} + +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +{ + if (!c->mtd->oobsize) + return 0; + + /* Cleanmarker is out-of-band, so inline size zero */ + c->cleanmarker_size = 0; + + if (c->mtd->oobavail == 0) { + pr_err("inconsistent device description\n"); + return -EINVAL; + } + + jffs2_dbg(1, "using OOB on NAND\n"); + + c->oobavail = c->mtd->oobavail; + + /* Initialise write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + c->oobbuf = kmalloc_array(NR_OOB_SCAN_PAGES, c->oobavail, GFP_KERNEL); + if (!c->oobbuf) { + kfree(c->wbuf); + return -ENOMEM; + } + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif + return 0; +} + +void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) +{ +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); + kfree(c->oobbuf); +} + +int jffs2_dataflash_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; /* No cleanmarkers needed */ + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + c->wbuf_pagesize = c->mtd->erasesize; + + /* Find a suitable c->sector_size + * - Not too much sectors + * - Sectors have to be at least 4 K + some bytes + * - All known dataflashes have erase sizes of 528 or 1056 + * - we take at least 8 eraseblocks and want to have at least 8K size + * - The concatenation should be a power of 2 + */ + + c->sector_size = 8 * c->mtd->erasesize; + + while (c->sector_size < 8192) { + c->sector_size *= 2; + } + + /* It may be necessary to adjust the flash size */ + c->flash_size = c->mtd->size; + + if ((c->flash_size % c->sector_size) != 0) { + c->flash_size = (c->flash_size / c->sector_size) * c->sector_size; + pr_warn("flash size adjusted to %dKiB\n", c->flash_size); + } + + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->wbuf); + return -ENOMEM; + } +#endif + + pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n", + c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); +} + +int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker currently occupies whole programming regions, + * either one or 2 for 8Byte STMicro flashes. */ + c->cleanmarker_size = max(16u, c->mtd->writesize); + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->wbuf); + return -ENOMEM; + } +#endif + return 0; +} + +void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif + kfree(c->wbuf); +} + +int jffs2_ubivol_setup(struct jffs2_sb_info *c) { + c->cleanmarker_size = 0; + + if (c->mtd->writesize == 1) + /* We do not need write-buffer */ + return 0; + + init_rwsem(&c->wbuf_sem); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + + c->wbuf_pagesize = c->mtd->writesize; + c->wbuf_ofs = 0xFFFFFFFF; + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n", + c->wbuf_pagesize, c->sector_size); + + return 0; +} + +void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} |