summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/jffs2/src/fs-rtems.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libfs/src/jffs2/src/fs-rtems.c')
-rw-r--r--cpukit/libfs/src/jffs2/src/fs-rtems.c243
1 files changed, 208 insertions, 35 deletions
diff --git a/cpukit/libfs/src/jffs2/src/fs-rtems.c b/cpukit/libfs/src/jffs2/src/fs-rtems.c
index aae208ccef..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);
}
@@ -1118,10 +1145,9 @@ static int rtems_jffs2_statvfs(
return 0;
}
-static int rtems_jffs2_utime(
+static int rtems_jffs2_utimens(
const rtems_filesystem_location_info_t *loc,
- time_t actime,
- time_t modtime
+ struct timespec times[2]
)
{
struct _inode *inode = rtems_jffs2_get_inode_by_location(loc);
@@ -1129,8 +1155,8 @@ static int rtems_jffs2_utime(
int eno;
iattr.ia_valid = ATTR_ATIME | ATTR_MTIME | ATTR_CTIME;
- iattr.ia_atime = actime;
- iattr.ia_mtime = modtime;
+ iattr.ia_atime = times[0].tv_sec;
+ iattr.ia_mtime = times[1].tv_sec;
iattr.ia_ctime = get_seconds();
eno = -jffs2_do_setattr(inode, &iattr);
@@ -1186,7 +1212,7 @@ static const rtems_filesystem_operations_table rtems_jffs2_ops = {
.mount_h = rtems_filesystem_default_mount,
.unmount_h = rtems_filesystem_default_unmount,
.fsunmount_me_h = rtems_jffs2_fsunmount,
- .utime_h = rtems_jffs2_utime,
+ .utimens_h = rtems_jffs2_utimens,
.symlink_h = rtems_jffs2_symlink,
.readlink_h = rtems_jffs2_readlink,
.rename_h = rtems_jffs2_rename,
@@ -1213,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
@@ -1236,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;
@@ -1264,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);
}
@@ -1297,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);
@@ -1317,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)
{
@@ -1367,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) {
@@ -1483,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)
@@ -1568,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);