Commit 4af8e944 authored by David Woodhouse's avatar David Woodhouse

JFFS2 file system update

 - Improved support for NAND flash
 - More generic compression support, allowing for extra compressors
 - Fix potential deadlock with kupdated
parent 987817ef
......@@ -1097,17 +1097,15 @@ config JFFS_FS_VERBOSE
config JFFS_PROC_FS
bool "JFFS stats available in /proc filesystem"
depends on JFFS_FS && PROC
depends on JFFS_FS && PROC_FS
help
Enabling this option will cause statistics from mounted JFFS file systems
to be made available to the user in the /proc/fs/jffs/ directory.
config JFFS2_FS
tristate "Journalling Flash File System v2 (JFFS2) support"
depends on MTD
select CRC32
select ZLIB_INFLATE
select ZLIB_DEFLATE
depends on MTD
help
JFFS2 is the second generation of the Journalling Flash File System
for use on diskless embedded devices. It provides improved wear
......@@ -1151,6 +1149,82 @@ config JFFS2_FS_NAND
Say 'N' unless you have NAND flash and you are willing to test and
develop JFFS2 support for it.
config JFFS2_COMPRESSION_OPTIONS
bool "Advanced compression options for JFFS2"
default n
help
Enabling this option allows you to explicitly choose which
compression modules, if any, are enabled in JFFS2. Removing
compressors and mean you cannot read existing file systems,
and enabling experimental compressors can mean that you
write a file system which cannot be read by a standard kernel.
If unsure, you should _definitely_ say 'N'.
config JFFS2_ZLIB
bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS
select ZLIB_INFLATE
select ZLIB_DEFLATE
depends on JFFS2_FS
default y
help
Zlib is designed to be a free, general-purpose, legally unencumbered,
lossless data-compression library for use on virtually any computer
hardware and operating system. See http://www.gzip.org/zlib/ for
further information.
Say 'Y' if unsure.
config JFFS2_RTIME
bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default y
help
Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
config JFFS2_RUBIN
bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default n
help
RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
choice
prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
default JFFS2_CMODE_PRIORITY
depends on JFFS2_FS
help
You can set here the default compression mode of JFFS2 from
the avaiable compression modes. Don't touch if unsure.
config JFFS2_CMODE_NONE
bool "no compression"
help
Uses no compression.
config JFFS2_CMODE_PRIORITY
bool "priority"
help
Tries the compressors in a predefinied order and chooses the first
successful one.
config JFFS2_CMODE_SIZE
bool "size (EXPERIMENTAL)"
help
Tries all compressors and chooses the one which has the smallest
result.
endchoice
config JFFS2_PROC
bool "JFFS2 proc interface support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS && PROC_FS
default n
help
You can read some statistics and set the compression mode and
compressor priorities with this interface.
config CRAMFS
tristate "Compressed ROM file system support"
select ZLIB_INFLATE
......
#
# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
# $Id: Makefile,v 1.34 2002/03/08 11:27:59 dwmw2 Exp $
# $Id: Makefile.common,v 1.5 2004/07/15 16:06:41 dwmw2 Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
jffs2-y := compr.o compr_rubin.o compr_rtime.o compr_zlib.o
jffs2-y += dir.o file.o ioctl.o nodelist.o malloc.o
jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o
jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
jffs2-$(CONFIG_JFFS2_PROC) += proc.o
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: background.c,v 1.44 2003/10/08 13:29:55 dwmw2 Exp $
* $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $
*
*/
......@@ -20,12 +20,11 @@
static int jffs2_garbage_collect_thread(void *);
static int thread_should_wake(struct jffs2_sb_info *c);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
spin_lock(&c->erase_completion_lock);
if (c->gc_task && thread_should_wake(c))
if (c->gc_task && jffs2_thread_should_wake(c))
send_sig(SIGHUP, c->gc_task, 1);
spin_unlock(&c->erase_completion_lock);
}
......@@ -84,11 +83,11 @@ static int jffs2_garbage_collect_thread(void *_c)
for (;;) {
allow_signal(SIGHUP);
if (!thread_should_wake(c)) {
if (!jffs2_thread_should_wake(c)) {
set_current_state (TASK_INTERRUPTIBLE);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
/* Yes, there's a race here; we checked thread_should_wake() before
setting current->state to TASK_INTERRUPTIBLE. But it doesn't
/* Yes, there's a race here; we checked jffs2_thread_should_wake()
before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
matter - We don't care if we miss a wakeup, because the GC thread
is only an optimisation anyway. */
schedule();
......@@ -144,34 +143,3 @@ static int jffs2_garbage_collect_thread(void *_c)
spin_unlock(&c->erase_completion_lock);
complete_and_exit(&c->gc_thread_exit, 0);
}
static int thread_should_wake(struct jffs2_sb_info *c)
{
int ret = 0;
uint32_t dirty;
if (c->unchecked_size) {
D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
c->unchecked_size, c->checked_ino));
return 1;
}
/* dirty_size contains blocks on erase_pending_list
* those blocks are counted in c->nr_erasing_blocks.
* If one block is actually erased, it is not longer counted as dirty_space
* but it is counted in c->nr_erasing_blocks, so we add it and subtract it
* with c->nr_erasing_blocks * c->sector_size again.
* Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
* This helps us to force gc and pick eventually a clean block to spread the load.
*/
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
(dirty > c->nospc_dirty_size))
ret = 1;
D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
return ret;
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: build.c,v 1.52 2003/10/09 00:38:38 dwmw2 Exp $
* $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
*
*/
......@@ -16,8 +16,7 @@
#include <linux/slab.h>
#include "nodelist.h"
int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
static inline struct jffs2_inode_cache *
first_inode_chain(int *i, struct jffs2_sb_info *c)
......@@ -44,6 +43,41 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
ic; \
ic = next_inode(&i, ic, (c)))
static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
/* For each child, increase nlink */
for(fd = ic->scan_dents; fd; fd = fd->next) {
struct jffs2_inode_cache *child_ic;
if (!fd->ino)
continue;
/* XXX: Can get high latency here with huge directories */
child_ic = jffs2_get_ino_cache(c, fd->ino);
if (!child_ic) {
printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
fd->name, fd->ino, ic->ino);
continue;
}
if (child_ic->nlink++ && fd->type == DT_DIR) {
printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
if (fd->ino == 1 && ic->ino == 1) {
printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
}
/* What do we do about it? */
}
D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
/* Can't free them. We might need them in pass 2 */
}
}
/* Scan plan:
- Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
- Scan directory tree from top down, setting nlink in inocaches
......@@ -54,6 +88,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
int ret;
int i;
struct jffs2_inode_cache *ic;
struct jffs2_full_dirent *dead_fds = NULL;
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
......@@ -71,47 +106,51 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
ret = jffs2_build_inode_pass1(c, ic);
if (ret) {
D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
return ret;
D1(BUG_ON(ic->ino > c->highest_ino));
if (ic->scan_dents) {
jffs2_build_inode_pass1(c, ic);
cond_resched();
}
cond_resched();
}
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
D1(jffs2_dump_block_lists(c));
/* Next, scan for inodes with nlink == 0 and remove them. If
they were directories, then decrement the nlink of their
children too, and repeat the scan. As that's going to be
a fairly uncommon occurrence, it's not so evil to do it this
way. Recursion bad. */
do {
D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
ret = 0;
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
if (ic->nlink)
continue;
D1(printk(KERN_DEBUG "Pass 2 starting\n"));
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
if (ic->nlink)
continue;
/* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
cond_resched();
}
ret = jffs2_build_remove_unlinked_inode(c, ic);
if (ret)
break;
/* -EAGAIN means the inode's nlink was zero, so we deleted it,
and furthermore that it had children and their nlink has now
gone to zero too. So we have to restart the scan. */
}
D1(jffs2_dump_block_lists(c));
D1(printk(KERN_DEBUG "Pass 2a starting\n"));
cond_resched();
} while(ret == -EAGAIN);
while (dead_fds) {
struct jffs2_inode_cache *ic;
struct jffs2_full_dirent *fd = dead_fds;
dead_fds = fd->next;
ic = jffs2_get_ino_cache(c, fd->ino);
D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
if (ic)
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
jffs2_free_full_dirent(fd);
}
D1(printk(KERN_DEBUG "Pass 2 complete\n"));
/* Finally, we can scan again and free the dirent nodes and scan_info structs */
/* Finally, we can scan again and free the dirent structs */
for_each_inode(i, c, ic) {
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
......@@ -133,49 +172,10 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
return ret;
}
int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
if (ic->ino > c->highest_ino)
c->highest_ino = ic->ino;
/* For each child, increase nlink */
for(fd=ic->scan_dents; fd; fd = fd->next) {
struct jffs2_inode_cache *child_ic;
if (!fd->ino)
continue;
/* XXX: Can get high latency here with huge directories */
child_ic = jffs2_get_ino_cache(c, fd->ino);
if (!child_ic) {
printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
fd->name, fd->ino, ic->ino);
continue;
}
if (child_ic->nlink++ && fd->type == DT_DIR) {
printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
if (fd->ino == 1 && ic->ino == 1) {
printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
}
/* What do we do about it? */
}
D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
/* Can't free them. We might need them in pass 2 */
}
return 0;
}
int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
int ret = 0;
D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
......@@ -214,18 +214,29 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod
jffs2_free_full_dirent(fd);
continue;
}
jffs2_free_full_dirent(fd);
/* Reduce nlink of the child. If it's now zero, stick it on the
dead_fds list to be cleaned up later. Else just free the fd */
child_ic->nlink--;
if (!child_ic->nlink) {
D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
fd->ino, fd->name));
fd->next = *dead_fds;
*dead_fds = fd;
} else {
D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
fd->ino, fd->name, child_ic->nlink));
jffs2_free_full_dirent(fd);
}
}
ret = -EAGAIN;
}
/*
We don't delete the inocache from the hash list and free it yet.
The erase code will do that, when all the nodes are completely gone.
*/
return ret;
}
static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
......
This diff is collapsed.
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
* $Id: compr.h,v 1.5 2004/06/23 16:34:39 havasi Exp $
*
*/
#ifndef __JFFS2_COMPR_H__
#define __JFFS2_COMPR_H__
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/jffs2.h>
#include <linux/jffs2_fs_i.h>
#include <linux/jffs2_fs_sb.h>
#include "nodelist.h"
#define JFFS2_RUBINMIPS_PRIORITY 10
#define JFFS2_DYNRUBIN_PRIORITY 20
#define JFFS2_LZARI_PRIORITY 30
#define JFFS2_LZO_PRIORITY 40
#define JFFS2_RTIME_PRIORITY 50
#define JFFS2_ZLIB_PRIORITY 60
#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
#define JFFS2_COMPR_MODE_NONE 0
#define JFFS2_COMPR_MODE_PRIORITY 1
#define JFFS2_COMPR_MODE_SIZE 2
void jffs2_set_compression_mode(int mode);
int jffs2_get_compression_mode(void);
struct jffs2_compressor {
struct list_head list;
int priority; /* used by prirority comr. mode */
char *name;
char compr; /* JFFS2_COMPR_XXX */
int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *srclen, uint32_t *destlen, void *model);
int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
uint32_t cdatalen, uint32_t datalen, void *model);
int usecount;
int disabled; /* if seted the compressor won't compress */
unsigned char *compr_buf; /* used by size compr. mode */
uint32_t compr_buf_size; /* used by size compr. mode */
uint32_t stat_compr_orig_size;
uint32_t stat_compr_new_size;
uint32_t stat_compr_blocks;
uint32_t stat_decompr_blocks;
};
int jffs2_register_compressor(struct jffs2_compressor *comp);
int jffs2_unregister_compressor(struct jffs2_compressor *comp);
int jffs2_compressors_init(void);
int jffs2_compressors_exit(void);
uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *data_in, unsigned char **cpage_out,
uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
#ifdef CONFIG_JFFS2_PROC
int jffs2_enable_compressor_name(const char *name);
int jffs2_disable_compressor_name(const char *name);
int jffs2_set_compression_mode_name(const char *mode_name);
char *jffs2_get_compression_mode_name(void);
int jffs2_set_compressor_priority(const char *mode_name, int priority);
char *jffs2_list_compressors(void);
char *jffs2_stats(void);
#endif
/* Compressor modules */
/* These functions will be called by jffs2_compressors_init/exit */
#ifdef CONFIG_JFFS2_RUBIN
int jffs2_rubinmips_init(void);
void jffs2_rubinmips_exit(void);
int jffs2_dynrubin_init(void);
void jffs2_dynrubin_exit(void);
#endif
#ifdef CONFIG_JFFS2_RTIME
int jffs2_rtime_init(void);
void jffs2_rtime_exit(void);
#endif
#ifdef CONFIG_JFFS2_ZLIB
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
#endif
#ifdef CONFIG_JFFS2_LZARI
int jffs2_lzari_init(void);
void jffs2_lzari_exit(void);
#endif
#ifdef CONFIG_JFFS2_LZO
int jffs2_lzo_init(void);
void jffs2_lzo_exit(void);
#endif
/* Prototypes from proc.c */
int jffs2_proc_init(void);
int jffs2_proc_exit(void);
#endif /* __JFFS2_COMPR_H__ */
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $
*
*
* Very simple lz77-ish encoder.
......@@ -25,10 +25,12 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/jffs2.h>
#include "compr.h"
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
short positions[256];
int outpos = 0;
......@@ -67,8 +69,8 @@ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
}
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, void *model)
{
short positions[256];
int outpos = 0;
......@@ -98,7 +100,29 @@ void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
outpos+=repeat;
}
}
}
}
return 0;
}
static struct jffs2_compressor jffs2_rtime_comp = {
.priority = JFFS2_RTIME_PRIORITY,
.name = "rtime",
.compr = JFFS2_COMPR_RTIME,
.compress = &jffs2_rtime_compress,
.decompress = &jffs2_rtime_decompress,
#ifdef JFFS2_RTIME_DISABLED
.disabled = 1,
#else
.disabled = 0,
#endif
};
int jffs2_rtime_init(void)
{
return jffs2_register_compressor(&jffs2_rtime_comp);
}
void jffs2_rtime_exit(void)
{
jffs2_unregister_compressor(&jffs2_rtime_comp);
}
......@@ -7,17 +7,17 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $
* $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $
*
*/
#include <linux/string.h>
#include <linux/types.h>
#include <linux/jffs2.h>
#include "compr_rubin.h"
#include "histo_mips.h"
#include "compr.h"
static void init_rubin(struct rubin_state *rs, int div, int *bits)
{
......@@ -223,13 +223,13 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
#if 0
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
#endif
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
int bits[8];
unsigned char histo[256];
......@@ -306,14 +306,15 @@ static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata
}
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen, void *model)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
return 0;
}
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen)
int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen, void *model)
{
int bits[8];
int c;
......@@ -322,4 +323,51 @@ void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
bits[c] = data_in[c];
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
return 0;
}
static struct jffs2_compressor jffs2_rubinmips_comp = {
.priority = JFFS2_RUBINMIPS_PRIORITY,
.name = "rubinmips",
.compr = JFFS2_COMPR_DYNRUBIN,
.compress = NULL, /*&jffs2_rubinmips_compress,*/
.decompress = &jffs2_rubinmips_decompress,
#ifdef JFFS2_RUBINMIPS_DISABLED
.disabled = 1,
#else
.disabled = 0,
#endif
};
int jffs2_rubinmips_init(void)
{
return jffs2_register_compressor(&jffs2_rubinmips_comp);
}
void jffs2_rubinmips_exit(void)
{
jffs2_unregister_compressor(&jffs2_rubinmips_comp);
}
static struct jffs2_compressor jffs2_dynrubin_comp = {
.priority = JFFS2_DYNRUBIN_PRIORITY,
.name = "dynrubin",
.compr = JFFS2_COMPR_RUBINMIPS,
.compress = jffs2_dynrubin_compress,
.decompress = &jffs2_dynrubin_decompress,
#ifdef JFFS2_DYNRUBIN_DISABLED
.disabled = 1,
#else
.disabled = 0,
#endif
};
int jffs2_dynrubin_init(void)
{
return jffs2_register_compressor(&jffs2_dynrubin_comp);
}
void jffs2_dynrubin_exit(void)
{
jffs2_unregister_compressor(&jffs2_dynrubin_comp);
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $
*
*/
......@@ -17,13 +17,12 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/zutil.h>
#include <asm/semaphore.h>
#include "nodelist.h"
#include "compr.h"
/* Plan: call deflate() with avail_in == *sourcelen,
avail_out = *dstlen - 12 and flush == Z_FINISH.
......@@ -39,7 +38,10 @@ static DECLARE_MUTEX(inflate_sem);
static z_stream inf_strm, def_strm;
#ifdef __KERNEL__ /* Linux-only */
int __init jffs2_zlib_init(void)
#include <linux/vmalloc.h>
#include <linux/init.h>
static int __init alloc_workspaces(void)
{
def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
if (!def_strm.workspace) {
......@@ -57,15 +59,18 @@ int __init jffs2_zlib_init(void)
return 0;
}
void jffs2_zlib_exit(void)
static void free_workspaces(void)
{
vfree(def_strm.workspace);
vfree(inf_strm.workspace);
}
#else
#define alloc_workspaces() (0)
#define free_workspaces() do { } while(0)
#endif /* __KERNEL__ */
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
int ret;
......@@ -130,8 +135,8 @@ int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
return ret;
}
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, void *model)
{
int ret;
int wbits = MAX_WBITS;
......@@ -165,7 +170,7 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
printk(KERN_WARNING "inflateInit failed\n");
up(&inflate_sem);
return;
return 1;
}
while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
......@@ -175,4 +180,39 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
}
zlib_inflateEnd(&inf_strm);
up(&inflate_sem);
return 0;
}
static struct jffs2_compressor jffs2_zlib_comp = {
.priority = JFFS2_ZLIB_PRIORITY,
.name = "zlib",
.compr = JFFS2_COMPR_ZLIB,
.compress = &jffs2_zlib_compress,
.decompress = &jffs2_zlib_decompress,
#ifdef JFFS2_ZLIB_DISABLED
.disabled = 1,
#else
.disabled = 0,
#endif
};
int __init jffs2_zlib_init(void)
{
int ret;
ret = alloc_workspaces();
if (ret)
return ret;
ret = jffs2_register_compressor(&jffs2_zlib_comp);
if (ret)
free_workspaces();
return ret;
}
void jffs2_zlib_exit(void)
{
jffs2_unregister_compressor(&jffs2_zlib_comp);
free_workspaces();
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $
* $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $
*
*/
......@@ -28,7 +28,7 @@ struct erase_priv_struct {
#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *);
#endif
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
......@@ -36,6 +36,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
int ret;
uint32_t bad_offset;
#ifdef __ECOS
ret = jffs2_flash_erase(c, jeb);
if (!ret) {
......@@ -65,18 +66,16 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
instr->len = c->sector_size;
instr->callback = jffs2_erase_callback;
instr->priv = (unsigned long)(&instr[1]);
instr->fail_addr = 0xffffffff;
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
((struct erase_priv_struct *)instr->priv)->c = c;
/* NAND , read out the fail counter, if possible */
if (!jffs2_can_mark_obsolete(c))
jffs2_nand_read_failcnt(c,jeb);
ret = c->mtd->erase(c->mtd, instr);
if (!ret)
return;
bad_offset = instr->fail_addr;
kfree(instr);
#endif /* __ECOS */
......@@ -98,10 +97,10 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
else
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
jffs2_erase_failed(c, jeb);
jffs2_erase_failed(c, jeb, bad_offset);
}
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
{
struct jffs2_eraseblock *jeb;
......@@ -118,6 +117,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
spin_unlock(&c->erase_completion_lock);
jffs2_mark_erased_block(c, jeb);
if (!--count) {
D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
goto done;
}
} else if (!list_empty(&c->erase_pending_list)) {
jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
......@@ -144,6 +148,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
}
spin_unlock(&c->erase_completion_lock);
done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
up(&c->erase_free_sem);
......@@ -160,16 +165,34 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo
jffs2_erase_pending_trigger(c);
}
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
{
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_del(&jeb->list);
list_add(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
/* For NAND, if the failure did not occur at the device level for a
specific physical page, don't bother updating the bad block table. */
if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
/* We had a device-level failure to erase. Let's see if we've
failed too many times. */
if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
/* We'd like to give this block another try. */
spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
return;
}
}
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_del(&jeb->list);
list_add(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
}
#ifndef __ECOS
......@@ -179,7 +202,7 @@ static void jffs2_erase_callback(struct erase_info *instr)
if(instr->state != MTD_ERASE_DONE) {
printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
jffs2_erase_failed(priv->c, priv->jeb);
jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
} else {
jffs2_erase_succeeded(priv->c, priv->jeb);
}
......@@ -277,17 +300,13 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_erase
jeb->last_node = NULL;
}
void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
{
OFNI_BS_2SFFJ(c)->s_dirt = 1;
}
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *marker_ref = NULL;
unsigned char *ebuf;
size_t retlen;
int ret;
uint32_t bad_offset;
if (!jffs2_cleanmarker_oob(c)) {
marker_ref = jffs2_alloc_raw_node_ref();
......@@ -312,6 +331,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
int i;
bad_offset = ofs;
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
......@@ -325,22 +346,21 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
/* It's OK. We know it's properly aligned */
unsigned long datum = *(unsigned long *)(&ebuf[i]);
if (datum + 1) {
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
bad_offset += i;
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
bad:
if (!jffs2_cleanmarker_oob(c))
jffs2_free_raw_node_ref(marker_ref);
else
jffs2_write_nand_badblock( c ,jeb );
kfree(ebuf);
bad2:
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_add_tail(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
/* Stick it on a list (any list) so
erase_failed can take it right off
again. Silly, but shouldn't happen
often. */
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
jffs2_erase_failed(c, jeb, bad_offset);
return;
}
}
......@@ -349,7 +369,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
}
kfree(ebuf);
}
bad_offset = jeb->offset;
/* Write the erase complete marker */
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
if (jffs2_cleanmarker_oob(c)) {
......@@ -370,29 +392,30 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
.totlen = cpu_to_je32(c->cleanmarker_size)
};
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4));
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker);
/* We only write the header; the rest was noise or padding anyway */
ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
goto bad2;
}
if (retlen != je32_to_cpu(marker.totlen)) {
if (retlen != sizeof(marker)) {
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
jeb->offset, je32_to_cpu(marker.totlen), retlen);
jeb->offset, sizeof(marker), retlen);
goto bad2;
}
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
marker_ref->totlen = PAD(je32_to_cpu(marker.totlen));
marker_ref->__totlen = c->cleanmarker_size;
jeb->first_node = jeb->last_node = marker_ref;
jeb->free_size = c->sector_size - marker_ref->totlen;
jeb->used_size = marker_ref->totlen;
jeb->free_size = c->sector_size - c->cleanmarker_size;
jeb->used_size = c->cleanmarker_size;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
}
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: file.c,v 1.96 2003/10/11 11:47:23 dwmw2 Exp $
* $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $
*
*/
......@@ -72,7 +72,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
unsigned char *pg_buf;
int ret;
D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
if (!PageLocked(pg))
PAGE_BUG(pg);
......@@ -93,7 +93,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
flush_dcache_page(pg);
kunmap(pg);
D1(printk(KERN_DEBUG "readpage finished\n"));
D2(printk(KERN_DEBUG "readpage finished\n"));
return 0;
}
......@@ -207,6 +207,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_raw_inode *ri;
unsigned aligned_start = start & ~3;
int ret = 0;
uint32_t writtenlen = 0;
......@@ -240,9 +241,9 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
hurt to do it again. The alternative is ifdefs, which are ugly. */
kmap(pg);
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
(pg->index << PAGE_CACHE_SHIFT) + start,
end - start, &writtenlen);
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
(pg->index << PAGE_CACHE_SHIFT) + aligned_start,
end - aligned_start, &writtenlen);
kunmap(pg);
......@@ -250,6 +251,12 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
/* There was an error writing. */
SetPageError(pg);
}
/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
if (writtenlen < (start&3))
writtenlen = 0;
else
writtenlen -= (start&3);
if (writtenlen) {
if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs.c,v 1.32 2003/10/11 11:47:23 dwmw2 Exp $
* $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $
*
*/
......@@ -58,7 +58,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
mdata = kmalloc(f->metadata->size, GFP_USER);
if (!mdata)
return -ENOMEM;
ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
if (ret) {
kfree(mdata);
return ret;
......@@ -145,10 +145,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
old_metadata = f->metadata;
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
vmtruncate(inode, iattr->ia_size);
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
}
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
......@@ -166,6 +164,14 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
up(&f->sem);
jffs2_complete_reservation(c);
/* We have to do the vmtruncate() without f->sem held, since
some pages may be locked and waiting for it in readpage().
We are protected from a simultaneous write() extending i_size
back past iattr->ia_size, because do_truncate() holds the
generic inode semaphore. */
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
vmtruncate(inode, iattr->ia_size);
return 0;
}
......@@ -287,7 +293,7 @@ void jffs2_read_inode (struct inode *inode)
case S_IFCHR:
/* Read the device numbers from the media */
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
/* Eep */
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
up(&f->sem);
......@@ -317,7 +323,7 @@ void jffs2_dirty_inode(struct inode *inode)
struct iattr iattr;
if (!(inode->i_state & I_DIRTY_DATASYNC)) {
D1(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
return;
}
......@@ -343,9 +349,14 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
/* We stop if it was running, then restart if it needs to.
This also catches the case where it was stopped and this
is just a remount to restart it */
if (!(sb->s_flags & MS_RDONLY))
is just a remount to restart it.
Flush the writebuffer, if neccecary, else we loose it */
if (!(sb->s_flags & MS_RDONLY)) {
jffs2_stop_garbage_collect_thread(c);
down(&c->alloc_sem);
jffs2_flush_wbuf_pad(c);
up(&c->alloc_sem);
}
if (!(*flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
......@@ -365,7 +376,7 @@ void jffs2_write_super (struct super_block *sb)
D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
jffs2_garbage_collect_trigger(c);
jffs2_erase_pending_blocks(c);
jffs2_erase_pending_blocks(c, 0);
jffs2_flush_wbuf_gc(c, 0);
}
......@@ -437,17 +448,35 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
c = JFFS2_SB_INFO(sb);
#ifndef CONFIG_JFFS2_FS_NAND
if (c->mtd->type == MTD_NANDFLASH) {
printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
return -EINVAL;
}
#endif
c->flash_size = c->mtd->size;
/*
* Check, if we have to concatenate physical blocks to larger virtual blocks
* to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
*/
blocks = c->flash_size / c->mtd->erasesize;
while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
c->sector_size = c->mtd->erasesize;
blocks = c->flash_size / c->sector_size;
while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
blocks >>= 1;
c->sector_size <<= 1;
}
c->sector_size = c->flash_size / blocks;
/*
* Size alignment check
*/
if ((c->sector_size * blocks) != c->flash_size) {
c->flash_size = c->sector_size * blocks;
printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
c->flash_size / 1024);
}
if (c->sector_size != c->mtd->erasesize)
printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
c->mtd->erasesize / 1024, c->sector_size / 1024);
......@@ -460,12 +489,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
/* Joern -- stick alignment for weird 8-byte-page flash here */
if (jffs2_cleanmarker_oob(c)) {
/* NAND (or other bizarre) flash... do setup accordingly */
ret = jffs2_nand_flash_setup(c);
if (ret)
return ret;
}
/* NAND (or other bizarre) flash... do setup accordingly */
ret = jffs2_flash_setup(c);
if (ret)
return ret;
c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
if (!c->inocache_list) {
......@@ -510,7 +537,126 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
out_inohash:
kfree(c->inocache_list);
out_wbuf:
jffs2_nand_flash_cleanup(c);
jffs2_flash_cleanup(c);
return ret;
}
void jffs2_gc_release_inode(struct jffs2_sb_info *c,
struct jffs2_inode_info *f)
{
iput(OFNI_EDONI_2SFFJ(f));
}
struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
int inum, int nlink)
{
struct inode *inode;
struct jffs2_inode_cache *ic;
if (!nlink) {
/* The inode has zero nlink but its nodes weren't yet marked
obsolete. This has to be because we're still waiting for
the final (close() and) iput() to happen.
There's a possibility that the final iput() could have
happened while we were contemplating. In order to ensure
that we don't cause a new read_inode() (which would fail)
for the inode in question, we use ilookup() in this case
instead of iget().
The nlink can't _become_ zero at this point because we're
holding the alloc_sem, and jffs2_do_unlink() would also
need that while decrementing nlink on any inode.
*/
inode = ilookup(OFNI_BS_2SFFJ(c), inum);
if (!inode) {
D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
inum));
spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, inum);
if (!ic) {
D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
spin_unlock(&c->inocache_lock);
return NULL;
}
if (ic->state != INO_STATE_CHECKEDABSENT) {
/* Wait for progress. Don't just loop */
D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
ic->ino, ic->state));
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
} else {
spin_unlock(&c->inocache_lock);
}
return NULL;
}
} else {
/* Inode has links to it still; they're not going away because
jffs2_do_unlink() would need the alloc_sem and we have it.
Just iget() it, and if read_inode() is necessary that's OK.
*/
inode = iget(OFNI_BS_2SFFJ(c), inum);
if (!inode)
return ERR_PTR(-ENOMEM);
}
if (is_bad_inode(inode)) {
printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
inum, nlink);
/* NB. This will happen again. We need to do something appropriate here. */
iput(inode);
return ERR_PTR(-EIO);
}
return JFFS2_INODE_INFO(inode);
}
unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
struct jffs2_inode_info *f,
unsigned long offset,
unsigned long *priv)
{
struct inode *inode = OFNI_EDONI_2SFFJ(f);
struct page *pg;
pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
(void *)jffs2_do_readpage_unlock, inode);
if (IS_ERR(pg))
return (void *)pg;
*priv = (unsigned long)pg;
return kmap(pg);
}
void jffs2_gc_release_page(struct jffs2_sb_info *c,
unsigned char *ptr,
unsigned long *priv)
{
struct page *pg = (void *)*priv;
kunmap(pg);
page_cache_release(pg);
}
int jffs2_flash_setup(struct jffs2_sb_info *c) {
int ret = 0;
if (jffs2_cleanmarker_oob(c)) {
/* NAND flash... do setup accordingly */
ret = jffs2_nand_flash_setup(c);
if (ret)
return ret;
}
/* add setups for other bizarre flashes here... */
return ret;
}
void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
if (jffs2_cleanmarker_oob(c)) {
jffs2_nand_flash_cleanup(c);
}
/* add cleanups for other bizarre flashes here... */
}
This diff is collapsed.
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001 Red Hat, Inc.
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: ioctl.c,v 1.7 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $
*
*/
......@@ -18,6 +18,6 @@ int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
{
/* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
will include compression support etc. */
return -EINVAL;
return -ENOTTY;
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: malloc.c,v 1.25 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $
*
*/
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.c,v 1.80 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $
*
*/
......@@ -58,7 +58,7 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new
/* Put a new tmp_dnode_info into the list, keeping the list in
order of increasing version
*/
void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
{
struct jffs2_tmp_dnode_info **prev = list;
......@@ -133,7 +133,9 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
cond_resched();
/* FIXME: point() */
err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
err = jffs2_flash_read(c, (ref_offset(ref)),
min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
&retlen, (void *)&node);
if (err) {
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
goto free_out;
......@@ -141,7 +143,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* Check we've managed to read at least the common node header */
if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
printk(KERN_WARNING "short read in get_inode_nodes()\n");
err = -EIO;
goto free_out;
......@@ -246,7 +248,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* If we've never checked the CRCs on this node, check them now. */
if (ref_flags(ref) == REF_UNCHECKED) {
uint32_t crc;
uint32_t crc, len;
struct jffs2_eraseblock *jeb;
crc = crc32(0, &node, sizeof(node.i)-8);
......@@ -321,10 +323,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
len = ref_totlen(c, jeb, ref);
jeb->used_size += len;
jeb->unchecked_size -= len;
c->used_size += len;
c->unchecked_size -= len;
/* If node covers at least a whole page, or if it starts at the
beginning of a page and runs to the end of the file, or if
......@@ -377,6 +381,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
default:
if (ref_flags(ref) == REF_UNCHECKED) {
struct jffs2_eraseblock *jeb;
uint32_t len;
printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
je16_to_cpu(node.u.nodetype), ref_offset(ref));
......@@ -384,10 +389,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
len = ref_totlen(c, jeb, ref);
jeb->used_size += len;
jeb->unchecked_size -= len;
c->used_size += len;
c->unchecked_size -= len;
mark_ref_normal(ref);
spin_unlock(&c->erase_completion_lock);
......@@ -631,6 +638,8 @@ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
jffs2_free_node_frag(frag);
frag = parent;
cond_resched();
}
}
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.h,v 1.104 2003/10/08 11:45:11 dwmw2 Exp $
* $Id: nodelist.h,v 1.119 2004/05/26 12:28:12 gleixner Exp $
*
*/
......@@ -44,6 +44,39 @@
#define D2(x)
#endif
#define JFFS2_NATIVE_ENDIAN
/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
whatever OS we're actually running on here too. */
#if defined(JFFS2_NATIVE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){x})
#define cpu_to_je32(x) ((jint32_t){x})
#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
#define je16_to_cpu(x) ((x).v16)
#define je32_to_cpu(x) ((x).v32)
#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
#elif defined(JFFS2_BIG_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
#define je16_to_cpu(x) (be16_to_cpu(x.v16))
#define je32_to_cpu(x) (be32_to_cpu(x.v32))
#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
#elif defined(JFFS2_LITTLE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
#define je16_to_cpu(x) (le16_to_cpu(x.v16))
#define je32_to_cpu(x) (le32_to_cpu(x.v32))
#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
#else
#error wibble
#endif
/*
This is all we need to keep in-core for each raw node during normal
operation. As and when we do read_inode on a particular inode, we can
......@@ -59,13 +92,12 @@ struct jffs2_raw_node_ref
word so you know when you've got there :) */
struct jffs2_raw_node_ref *next_phys;
uint32_t flash_offset;
uint32_t totlen;
uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
};
/* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
to play with. So we set the least significant bit to 1 to
signify that the node is obsoleted by later nodes.
*/
to play with, which indicate the node's status; see below: */
#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
#define REF_PRISTINE 2 /* Completely clean. GC without looking */
......@@ -74,7 +106,6 @@ struct jffs2_raw_node_ref
#define ref_offset(ref) ((ref)->flash_offset & ~3)
#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
};
/*
Used for keeping track of deletion nodes &c, which can only be marked
......@@ -246,9 +277,9 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
BUG(); \
} \
if (ref_flags(ref2) == REF_UNCHECKED) \
my_unchecked_size += ref2->totlen; \
my_unchecked_size += ref_totlen(c, jeb, ref2); \
else if (!ref_obsolete(ref2)) \
my_used_size += ref2->totlen; \
my_used_size += ref_totlen(c, jeb, ref2); \
if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
......@@ -268,6 +299,57 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
} \
} while(0)
/* Calculate totlen from surrounding nodes or eraseblock */
static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *ref)
{
uint32_t ref_end;
if (ref->next_phys)
ref_end = ref_offset(ref->next_phys);
else {
if (!jeb)
jeb = &c->blocks[ref->flash_offset / c->sector_size];
/* Last node in block. Use free_space */
BUG_ON(ref != jeb->last_node);
ref_end = jeb->offset + c->sector_size - jeb->free_size;
}
return ref_end - ref_offset(ref);
}
static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *ref)
{
uint32_t ret;
D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
BUG();
})
#if 1
ret = ref->__totlen;
#else
/* This doesn't actually work yet */
ret = __ref_totlen(c, jeb, ref);
if (ret != ref->__totlen) {
printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
ret, ref->__totlen);
if (!jeb)
jeb = &c->blocks[ref->flash_offset / c->sector_size];
paranoia_failed_dump(jeb);
BUG();
}
#endif
return ret;
}
#define ALLOC_NORMAL 0 /* Normal allocation */
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
......@@ -281,13 +363,13 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
#define PAD(x) (((x)+3)&~3)
static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
{
while(raw->next_in_ino) {
raw = raw->next_in_ino;
}
return ((struct jffs2_inode_cache *)raw)->ino;
return ((struct jffs2_inode_cache *)raw);
}
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
......@@ -311,7 +393,6 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
/* nodelist.c */
D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
......@@ -330,6 +411,7 @@ struct rb_node *rb_prev(struct rb_node *);
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
/* nodemgmt.c */
int jffs2_thread_should_wake(struct jffs2_sb_info *c);
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
......@@ -383,18 +465,13 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *);
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
/* read.c */
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_full_dnode *fd, unsigned char *buf,
int ofs, int len);
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *buf, uint32_t offset, uint32_t len);
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
/* compr.c */
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
/* scan.c */
int jffs2_scan_medium(struct jffs2_sb_info *c);
void jffs2_rotate_lists(struct jffs2_sb_info *c);
......@@ -404,8 +481,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c);
/* erase.c */
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
#ifdef CONFIG_JFFS2_FS_NAND
/* wbuf.c */
......@@ -413,11 +489,6 @@ 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_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_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
#endif
/* compr_zlib.c */
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
#endif /* __JFFS2_NODELIST_H__ */
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $
* $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
*
*/
......@@ -209,8 +209,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
if (list_empty(&c->free_list)) {
DECLARE_WAITQUEUE(wait, current);
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_list)) {
struct jffs2_eraseblock *ejeb;
......@@ -243,30 +241,12 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
return -ENOSPC;
}
/* Make sure this can't deadlock. Someone has to start the erases
of erase_pending blocks */
#ifdef __ECOS
/* In eCos, we don't have a handy kernel thread doing the erases for
us. We do them ourselves right now. */
jffs2_erase_pending_blocks(c);
#else
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&c->erase_wait, &wait);
D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
if (!list_empty(&c->erase_pending_list)) {
D1(printk(KERN_DEBUG "Triggering pending erases\n"));
jffs2_erase_pending_trigger(c);
}
spin_unlock(&c->erase_completion_lock);
schedule();
remove_wait_queue(&c->erase_wait, &wait);
/* Don't wait for it; just erase one right now */
jffs2_erase_pending_blocks(c, 1);
spin_lock(&c->erase_completion_lock);
if (signal_pending(current)) {
return -EINTR;
}
#endif
/* An erase may have failed, decreasing the
amount of free space available. So we must
restart from the beginning */
......@@ -321,9 +301,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
{
struct jffs2_eraseblock *jeb;
uint32_t len = new->totlen;
uint32_t len;
jeb = &c->blocks[new->flash_offset / c->sector_size];
len = ref_totlen(c, jeb, new);
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
......@@ -420,31 +402,31 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_lock(&c->erase_completion_lock);
if (ref_flags(ref) == REF_UNCHECKED) {
D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
jeb->unchecked_size -= ref->totlen;
c->unchecked_size -= ref->totlen;
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
jeb->unchecked_size -= ref_totlen(c, jeb, ref);
c->unchecked_size -= ref_totlen(c, jeb, ref);
} else {
D1(if (unlikely(jeb->used_size < ref->totlen)) {
D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
jeb->used_size -= ref->totlen;
c->used_size -= ref->totlen;
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
jeb->used_size -= ref_totlen(c, jeb, ref);
c->used_size -= ref_totlen(c, jeb, ref);
}
// Take care, that wasted size is taken into concern
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
addedsize = ref->totlen;
jeb->dirty_size += ref->totlen;
c->dirty_size += ref->totlen;
addedsize = ref_totlen(c, jeb, ref);
jeb->dirty_size += ref_totlen(c, jeb, ref);
c->dirty_size += ref_totlen(c, jeb, ref);
/* Convert wasted space to dirty, if not a bad block */
if (jeb->wasted_size) {
......@@ -465,8 +447,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
} else {
D1(printk("Wasting\n"));
addedsize = 0;
jeb->wasted_size += ref->totlen;
c->wasted_size += ref->totlen;
jeb->wasted_size += ref_totlen(c, jeb, ref);
c->wasted_size += ref_totlen(c, jeb, ref);
}
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
......@@ -497,30 +479,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
if (jffs2_wbuf_dirty(c)) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
#if 0 /* This check was added to allow us to find places where we added nodes to the lists
after dropping the alloc_sem, and it did that just fine. But it also caused us to
lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
have needed to. So I suspect it's outlived its usefulness. Thomas? */
/* We've changed the rules slightly. After
writing a node you now mustn't drop the
alloc_sem before you've finished all the
list management - this is so that when we
get here, we know that no other nodes have
been written, and the above check on wbuf
is valid - wbuf_len is nonzero IFF the node
which obsoletes this node is still in the
wbuf.
So we BUG() if that new rule is broken, to
make sure we catch it and fix it.
*/
if (!down_trylock(&c->alloc_sem)) {
up(&c->alloc_sem);
printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
BUG();
}
#endif
} else {
if (jiffies & 127) {
/* Most of the time, we just erase it immediately. Otherwise we
......@@ -572,12 +530,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
return;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
return;
}
/* XXX FIXME: This is ugly now */
......@@ -750,3 +708,34 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c)
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
int jffs2_thread_should_wake(struct jffs2_sb_info *c)
{
int ret = 0;
uint32_t dirty;
if (c->unchecked_size) {
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
c->unchecked_size, c->checked_ino));
return 1;
}
/* dirty_size contains blocks on erase_pending_list
* those blocks are counted in c->nr_erasing_blocks.
* If one block is actually erased, it is not longer counted as dirty_space
* but it is counted in c->nr_erasing_blocks, so we add it and subtract it
* with c->nr_erasing_blocks * c->sector_size again.
* Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
* This helps us to force gc and pick eventually a clean block to spread the load.
*/
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
(dirty > c->nospc_dirty_size))
ret = 1;
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
return ret;
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: os-linux.h,v 1.37 2003/10/11 11:47:23 dwmw2 Exp $
* $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $
*
*/
......@@ -23,6 +23,9 @@
#define kstatfs statfs
#endif
struct kstatfs;
struct kvec;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
......@@ -69,14 +72,6 @@
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
#endif
/* Hmmm. P'raps generic code should only ever see versions of signal
functions which do the locking automatically? */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) && !defined(__rh_config_h__)
#define current_sig_lock current->sigmask_lock
#else
#define current_sig_lock current->sighand->siglock
#endif
#define sleep_on_spinunlock(wq, s) \
do { \
DECLARE_WAITQUEUE(__wait, current); \
......@@ -113,8 +108,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
#define jffs2_nand_flash_setup(c) (0)
#define jffs2_nand_flash_cleanup(c) do {} while(0)
#define jffs2_wbuf_dirty(c) (0)
......@@ -130,9 +124,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
struct kstatfs;
struct kvec;
/* 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_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
......@@ -140,13 +132,19 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
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);
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_nand_flash_setup(struct jffs2_sb_info *c);
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
#endif /* NAND */
/* erase.c */
static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
{
OFNI_BS_2SFFJ(c)->s_dirt = 1;
}
/* background.c */
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
......@@ -184,13 +182,26 @@ int jffs2_statfs (struct super_block *, struct kstatfs *);
void jffs2_write_super (struct super_block *);
int jffs2_remount_fs (struct super_block *, int *, char *);
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
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);
int jffs2_flash_setup(struct jffs2_sb_info *c);
void jffs2_flash_cleanup(struct jffs2_sb_info *c);
/* writev.c */
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen);
/* super.c */
#endif /* __JFFS2_OS_LINUX_H__ */
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: proc.c,v 1.3 2004/06/24 09:51:38 havasi Exp $
*
* Files in /proc/fs/jffs2 directory:
* compr_list
* read: shows the list of the loaded compressors
* (name, priority, enadbled/disabled)
* write: compressors can be enabled/disabled and
* the priority of them can be changed,
* required formats:
* enable COMPRESSOR_NAME
* disble COMPRESSOR_NAME
* priority NEW_PRIORITY COMPRESSOR_NAME
* compr_mode
* read: shows the name of the actual compression mode
* write: sets the actual comperession mode
* compr_stat
* read: shows compression statistics
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/jffs.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include "compr.h"
extern struct proc_dir_entry *jffs_proc_root;
/* Structure for top-level entry in '/proc/fs' directory */
static struct proc_dir_entry *jffs2_proc_root;
/* Structure for files in /proc/fs/jffs2 directory */
static struct proc_dir_entry *jffs2_proc_compr_stat;
static struct proc_dir_entry *jffs2_proc_compr_mode;
/* Read the JFFS2 'compr_stat' file */
static int jffs2_proc_stat_read (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0,i;
char *stat = jffs2_stats();
if (strlen(stat)<off) {
*eof = 1;
kfree(stat);
return len;
}
for (i=off;((stat[i]!=0)&&(len<count));i++,len++) {
page[len]=stat[i];
}
if (off+len>=strlen(stat)) *eof = 1;
else *eof = 0;
kfree(stat);
return len;
}
/* Read the JFFS2 'compr_mode' file */
static int jffs2_proc_mode_read (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
if (strlen(jffs2_get_compression_mode_name())+1>count) {
/* it should not happen */
*eof = 1;
return 0;
}
len += sprintf(page, "%s\n",jffs2_get_compression_mode_name());
*eof = 1;
return len;
}
/* Write the JFFS2 'compr_mode' file
* sets the actual compression mode
*/
static int jffs2_proc_mode_write(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char *compr_name;
/* collect the name of the compression mode and set it */
compr_name = kmalloc(count+1,GFP_KERNEL);
if (sscanf(buffer,"%s",compr_name)>0) {
if (jffs2_set_compression_mode_name(compr_name)) {
printk(KERN_WARNING "JFFS2: error switching compression mode. Invalid parameter (%s)?\n",compr_name);
}
}
else {
printk(KERN_WARNING "JFFS2: error: parameter missing\n");
}
kfree(compr_name);
return count;
}
/* Read the JFFS2 'compr_list' file */
static int jffs2_proc_list_read (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
char *list = jffs2_list_compressors();
if (strlen(list)+1>count) {
/* it should not happen */
*eof = 1;
kfree(list);
return 0;
}
len += sprintf(page,"%s",list);
*eof = 1;
kfree(list);
return len;
}
/* Write the JFFS2 'compr_list' file
* enable/disable a compressor or set the priority of it
*/
static int jffs2_proc_list_write(struct file *file, const char *buffer,
unsigned long count, void *data)
{
int prior;
char *compr_name,*compr_cmd;
compr_name = kmalloc(count+1,GFP_KERNEL);
compr_cmd = kmalloc(count+1,GFP_KERNEL);
if (!compr_name) {
printk(KERN_WARNING "JFFS2: unable to allocate memory\n");
goto list_write_end;
}
compr_name[0] = 0;
if (sscanf(buffer,"priority %d %s",&prior,compr_name)>1) {
jffs2_set_compressor_priority(compr_name, prior);
goto list_write_end;
}
if (sscanf(buffer,"enable %s",compr_name)>0) {
jffs2_enable_compressor_name(compr_name);
goto list_write_end;
}
if (sscanf(buffer,"disable %s",compr_name)>0) {
jffs2_disable_compressor_name(compr_name);
goto list_write_end;
}
printk(KERN_WARNING "JFFS2: usage of /proc/fs/jffs2/compr_list:\n"
" echo \"enable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n"
" echo \"disable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n"
" echo \"priority NEW_PRIORITY COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n");
list_write_end:
kfree(compr_cmd);
kfree(compr_name);
return count;
}
/* Register a JFFS2 proc directory */
int jffs2_proc_init(void)
{
jffs2_proc_root = proc_mkdir("jffs2", proc_root_fs);
/* create entry for 'compr_stat' file */
if ((jffs2_proc_compr_stat = create_proc_entry ("compr_stat", 0, jffs2_proc_root))) {
jffs2_proc_compr_stat->read_proc = jffs2_proc_stat_read;
}
else {
return -ENOMEM;
}
/* create entry for 'compr_mode' file */
if ((jffs2_proc_compr_mode = create_proc_entry ("compr_mode", 0, jffs2_proc_root))) {
jffs2_proc_compr_mode->read_proc = jffs2_proc_mode_read;
jffs2_proc_compr_mode->write_proc = jffs2_proc_mode_write;
}
else {
return -ENOMEM;
}
/* create entry for 'compr_list' file */
if ((jffs2_proc_compr_mode = create_proc_entry ("compr_list", 0, jffs2_proc_root))) {
jffs2_proc_compr_mode->read_proc = jffs2_proc_list_read;
jffs2_proc_compr_mode->write_proc = jffs2_proc_list_write;
}
else {
return -ENOMEM;
}
return 0;
}
/* Unregister a JFFS2 proc directory */
int jffs2_proc_exit(void)
{
#if LINUX_VERSION_CODE < 0x020300
remove_proc_entry ("compr_stat", &jffs2_proc_root);
remove_proc_entry ("compr_mode", &jffs2_proc_root);
remove_proc_entry ("compr_list", &jffs2_proc_root);
remove_proc_entry ("jffs2", &proc_root_fs);
#else
remove_proc_entry ("compr_stat", jffs2_proc_root);
remove_proc_entry ("compr_mode", jffs2_proc_root);
remove_proc_entry ("compr_list", jffs2_proc_root);
remove_proc_entry ("jffs2", proc_root_fs);
#endif
return 0;
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $
*
*/
......@@ -18,8 +18,11 @@
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include "nodelist.h"
#include "compr.h"
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_full_dnode *fd, unsigned char *buf,
int ofs, int len)
{
struct jffs2_raw_inode *ri;
size_t readlen;
......@@ -127,7 +130,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
if (ri->compr != JFFS2_COMPR_NONE) {
D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
if (ret) {
printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
goto out_decomprbuf;
......@@ -195,7 +198,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
frag->ofs+fragofs, frag->ofs+fragofs+readlen,
ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
D2(printk(KERN_DEBUG "node read done\n"));
if (ret) {
D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
......@@ -231,7 +234,7 @@ char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
}
buf[f->metadata->size]=0;
ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
up(&f->sem);
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.107 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $
*
*/
......@@ -56,6 +56,66 @@ void jffs2_print_frag_list(struct jffs2_inode_info *f)
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
}
}
static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
{
struct jffs2_node_frag *frag;
int bitched = 0;
for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
struct jffs2_full_dnode *fn = frag->node;
if (!fn || !fn->raw)
continue;
if (ref_flags(fn->raw) == REF_PRISTINE) {
if (fn->frags > 1) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
bitched = 1;
}
/* A hole node which isn't multi-page should be garbage-collected
and merged anyway, so we just check for the frag size here,
rather than mucking around with actually reading the node
and checking the compression type, which is the real way
to tell a hole node. */
if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
ref_offset(fn->raw));
bitched = 1;
}
if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
bitched = 1;
}
}
}
if (bitched) {
struct jffs2_node_frag *thisfrag;
printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
thisfrag = frag_first(&f->fragtree);
while (thisfrag) {
if (!thisfrag->node) {
printk("Frag @0x%x-0x%x; node-less hole\n",
thisfrag->ofs, thisfrag->size + thisfrag->ofs);
} else if (!thisfrag->node->raw) {
printk("Frag @0x%x-0x%x; raw-less hole\n",
thisfrag->ofs, thisfrag->size + thisfrag->ofs);
} else {
printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
thisfrag->ofs, thisfrag->size + thisfrag->ofs,
ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
}
thisfrag = frag_next(thisfrag);
}
}
return bitched;
}
#endif /* D1 */
static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
......@@ -130,6 +190,11 @@ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_in
mark_ref_normal(next->node->raw);
}
}
D2(if (jffs2_sanitycheck_fragtree(f)) {
printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
return 0;
})
D2(jffs2_print_frag_list(f));
return 0;
}
......@@ -384,6 +449,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
}
}
spin_unlock(&c->inocache_lock);
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
......@@ -460,7 +526,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
fn = tn->fn;
if (f->metadata) {
if (tn->version > mdata_ver) {
if (likely(tn->version >= mdata_ver)) {
D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
......@@ -468,10 +534,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
mdata_ver = 0;
} else {
D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n",
ref_offset(f->metadata->raw), tn->version, mdata_ver));
/* This should never happen. */
printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
/* Fill in latest_node from the metadata, not this one we're about to free... */
fn = f->metadata;
goto next_tn;
}
}
......@@ -488,6 +557,8 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
tn_list = tn->next;
jffs2_free_tmp_dnode_info(tn);
}
D1(jffs2_sanitycheck_fragtree(f));
if (!fn) {
/* No data nodes for this inode. */
if (f->inocache->ino != 1) {
......@@ -594,24 +665,10 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
struct jffs2_full_dirent *fd, *fds;
/* I don't think we care about the potential race due to reading this
without f->sem. It can never get undeleted. */
int deleted = f->inocache && !f->inocache->nlink;
/* If it's a deleted inode, grab the alloc_sem. This prevents
jffs2_garbage_collect_pass() from deciding that it wants to
garbage collect one of the nodes we're just about to mark
obsolete -- by the time we drop alloc_sem and return, all
the nodes are marked obsolete, and jffs2_g_c_pass() won't
call iget() for the inode in question.
We also used to do this to keep the temporary BUG() in
jffs2_mark_node_obsolete() from triggering.
*/
if(deleted)
down(&c->alloc_sem);
int deleted;
down(&f->sem);
deleted = f->inocache && !f->inocache->nlink;
if (f->metadata) {
if (deleted)
......@@ -633,7 +690,4 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
up(&f->sem);
if(deleted)
up(&c->alloc_sem);
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.104 2003/10/11 14:52:48 dwmw2 Exp $
* $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $
*
*/
#include <linux/kernel.h>
......@@ -285,8 +285,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
uint32_t hdr_crc, buf_ofs, buf_len;
int err;
int noise = 0;
int wasempty = 0;
uint32_t empty_start = 0;
#ifdef CONFIG_JFFS2_FS_NAND
int cleanmarkerfound = 0;
#endif
......@@ -339,8 +337,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
switch (ret) {
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
case 1: return BLK_STATE_ALLDIRTY;
case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
......@@ -359,6 +355,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
noise = 10;
scan_more:
while(ofs < jeb->offset + c->sector_size) {
D1(ACCT_PARANOIA_CHECK(jeb));
......@@ -398,42 +395,52 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
uint32_t inbuf_ofs = ofs - buf_ofs + 4;
uint32_t scanend;
uint32_t inbuf_ofs;
uint32_t empty_start;
empty_start = ofs;
ofs += 4;
/* If scanning empty space after only a cleanmarker, don't
bother scanning the whole block */
if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
else
scanend = buf_len;
D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
while (inbuf_ofs < scanend) {
if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
goto emptyends;
more_empty:
inbuf_ofs = ofs - buf_ofs;
while (inbuf_ofs < buf_len) {
if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
empty_start, ofs);
DIRTY_SPACE(ofs-empty_start);
goto scan_more;
}
inbuf_ofs+=4;
ofs += 4;
}
/* Ran off end. */
D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size)
/* If we're only checking the beginning of a block with a cleanmarker,
bail now */
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
return BLK_STATE_CLEANMARKER;
wasempty = 1;
continue;
} else if (wasempty) {
emptyends:
printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
DIRTY_SPACE(ofs-empty_start);
wasempty = 0;
continue;
}
/* See how much more there is to read in this eraseblock... */
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
if (!buf_len) {
/* No more to read. Break out of main loop without marking
this range of empty space as dirty (because it's not) */
D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
empty_start));
break;
}
D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
goto more_empty;
}
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
......@@ -554,7 +561,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = ofs | REF_NORMAL;
marker_ref->totlen = c->cleanmarker_size;
marker_ref->__totlen = c->cleanmarker_size;
jeb->first_node = jeb->last_node = marker_ref;
USED_SPACE(PAD(c->cleanmarker_size));
......@@ -610,7 +617,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
}
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
&& (!jeb->first_node || jeb->first_node->next_in_ino) )
&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
return BLK_STATE_CLEANMARKER;
/* move blocks with max 4 byte dirty space to cleanlist */
......@@ -634,6 +641,9 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info
if (ic)
return ic;
if (ino > c->highest_ino)
c->highest_ino = ino;
ic = jffs2_alloc_inode_cache();
if (!ic) {
printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
......@@ -645,7 +655,7 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info
ic->nodes = (void *)ic;
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink=1;
ic->nlink = 1;
return ic;
}
......@@ -698,7 +708,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
/* Wheee. It worked */
raw->flash_offset = ofs | REF_UNCHECKED;
raw->totlen = PAD(je32_to_cpu(ri->totlen));
raw->__totlen = PAD(je32_to_cpu(ri->totlen));
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
......@@ -775,7 +785,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
return -ENOMEM;
}
raw->totlen = PAD(je32_to_cpu(rd->totlen));
raw->__totlen = PAD(je32_to_cpu(rd->totlen));
raw->flash_offset = ofs | REF_PRISTINE;
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $
* $Id: super.c,v 1.96 2004/07/13 08:57:30 dwmw2 Exp $
*
*/
......@@ -24,6 +24,7 @@
#include <linux/mtd/mtd.h>
#include <linux/ctype.h>
#include <linux/namei.h>
#include "compr.h"
#include "nodelist.h"
static void jffs2_put_super(struct super_block *);
......@@ -266,7 +267,7 @@ static void jffs2_put_super (struct super_block *sb)
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
jffs2_nand_flash_cleanup(c);
jffs2_flash_cleanup(c);
kfree(c->inocache_list);
if (c->mtd->sync)
c->mtd->sync(c->mtd);
......@@ -294,7 +295,7 @@ static int __init init_jffs2_fs(void)
int ret;
printk(KERN_INFO "JFFS2 version 2.2."
#ifdef CONFIG_FS_JFFS2_NAND
#ifdef CONFIG_JFFS2_FS_NAND
" (NAND)"
#endif
" (C) 2001-2003 Red Hat, Inc.\n");
......@@ -307,15 +308,22 @@ static int __init init_jffs2_fs(void)
printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
return -ENOMEM;
}
ret = jffs2_zlib_init();
#ifdef CONFIG_JFFS2_PROC
ret = jffs2_proc_init();
if (ret) {
printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
printk(KERN_ERR "JFFS2 error: Failed to initialise proc interface\n");
goto out;
}
#endif
ret = jffs2_compressors_init();
if (ret) {
printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
goto out;
}
ret = jffs2_create_slab_caches();
if (ret) {
printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
goto out_zlib;
goto out_compressors;
}
ret = register_filesystem(&jffs2_fs_type);
if (ret) {
......@@ -326,8 +334,11 @@ static int __init init_jffs2_fs(void)
out_slab:
jffs2_destroy_slab_caches();
out_zlib:
jffs2_zlib_exit();
out_compressors:
jffs2_compressors_exit();
#ifdef CONFIG_JFFS2_PROC
jffs2_proc_exit();
#endif
out:
return ret;
}
......@@ -336,7 +347,10 @@ static void __exit exit_jffs2_fs(void)
{
unregister_filesystem(&jffs2_fs_type);
jffs2_destroy_slab_caches();
jffs2_zlib_exit();
jffs2_compressors_exit();
#ifdef CONFIG_JFFS2_PROC
jffs2_proc_exit();
#endif
kmem_cache_destroy(jffs2_inode_cachep);
}
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $
* $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $
*
*/
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: write.c,v 1.75 2003/10/08 11:45:11 dwmw2 Exp $
* $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
......@@ -18,6 +18,7 @@
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
#include "compr.h"
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
......@@ -31,7 +32,6 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
memset(ic, 0, sizeof(*ic));
init_MUTEX_LOCKED(&f->sem);
f->inocache = ic;
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
......@@ -133,7 +133,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
fn->raw = raw;
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*ri)+datalen);
raw->__totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
......@@ -275,11 +275,11 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
fd->raw = raw;
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*rd)+namelen);
raw->__totlen = PAD(sizeof(*rd)+namelen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
(alloc_mode==ALLOC_GC)?0:fd->ino);
(alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
if (ret || (retlen != sizeof(*rd) + namelen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
......@@ -359,7 +359,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
while(writelen) {
struct jffs2_full_dnode *fn;
unsigned char *comprbuf = NULL;
unsigned char comprtype = JFFS2_COMPR_NONE;
uint16_t comprtype = JFFS2_COMPR_NONE;
uint32_t phys_ofs, alloclen;
uint32_t datalen, cdatalen;
int retried = 0;
......@@ -373,24 +373,10 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
break;
}
down(&f->sem);
datalen = writelen;
cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
comprbuf = kmalloc(cdatalen, GFP_KERNEL);
if (comprbuf) {
comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen);
}
if (comprtype == JFFS2_COMPR_NONE) {
/* Either compression failed, or the allocation of comprbuf failed */
if (comprbuf)
kfree(comprbuf);
comprbuf = buf;
datalen = cdatalen;
}
/* Now comprbuf points to the data to be written, be it compressed or not.
comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
that the comprbuf doesn't need to be kfree()d.
*/
comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
......@@ -403,14 +389,14 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
ri->offset = cpu_to_je32(offset);
ri->csize = cpu_to_je32(cdatalen);
ri->dsize = cpu_to_je32(datalen);
ri->compr = comprtype;
ri->compr = comprtype & 0xff;
ri->usercompr = (comprtype >> 8 ) & 0xff;
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
if (comprtype != JFFS2_COMPR_NONE)
kfree(comprbuf);
jffs2_free_comprbuf(comprbuf, buf);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
......@@ -559,48 +545,75 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
uint32_t alloclen, phys_ofs;
int ret;
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
if (1 /* alternative branch needs testing */ ||
!jffs2_can_mark_obsolete(c)) {
/* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
if (ret) {
rd = jffs2_alloc_raw_dirent();
if (!rd)
return -ENOMEM;
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
if (ret) {
jffs2_free_raw_dirent(rd);
return ret;
}
down(&dir_f->sem);
/* Build a deletion node */
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
rd->pino = cpu_to_je32(dir_f->inocache->ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = cpu_to_je32(0);
rd->mctime = cpu_to_je32(get_seconds());
rd->nsize = namelen;
rd->type = DT_UNKNOWN;
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
jffs2_free_raw_dirent(rd);
return ret;
}
down(&dir_f->sem);
if (IS_ERR(fd)) {
jffs2_complete_reservation(c);
up(&dir_f->sem);
return PTR_ERR(fd);
}
/* Build a deletion node */
rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
/* File it. This will mark the old one obsolete. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
up(&dir_f->sem);
} else {
struct jffs2_full_dirent **prev = &dir_f->dents;
uint32_t nhash = full_name_hash(name, namelen);
rd->pino = cpu_to_je32(dir_f->inocache->ino);
rd->version = cpu_to_je32(++dir_f->highest_version);
rd->ino = cpu_to_je32(0);
rd->mctime = cpu_to_je32(get_seconds());
rd->nsize = namelen;
rd->type = DT_UNKNOWN;
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
down(&dir_f->sem);
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
jffs2_free_raw_dirent(rd);
while ((*prev) && (*prev)->nhash <= nhash) {
if ((*prev)->nhash == nhash &&
!memcmp((*prev)->name, name, namelen) &&
!(*prev)->name[namelen]) {
struct jffs2_full_dirent *this = *prev;
if (IS_ERR(fd)) {
jffs2_complete_reservation(c);
D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
this->ino, ref_offset(this->raw)));
*prev = this->next;
jffs2_mark_node_obsolete(c, (this->raw));
jffs2_free_full_dirent(this);
break;
}
prev = &((*prev)->next);
}
up(&dir_f->sem);
return PTR_ERR(fd);
}
/* File it. This will mark the old one obsolete. */
jffs2_add_fd_to_list(c, fd, &dir_f->dents);
up(&dir_f->sem);
/* dead_f is NULL if this was a rename not a real unlink */
/* Also catch the !f->inocache case, where there was a dirent
pointing to an inode which didn't exist. */
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $
* $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
......
......@@ -8,7 +8,7 @@
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
* $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $
* $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $
*
*/
......@@ -43,6 +43,8 @@
#define JFFS2_COMPR_COPY 0x04
#define JFFS2_COMPR_DYNRUBIN 0x05
#define JFFS2_COMPR_ZLIB 0x06
#define JFFS2_COMPR_LZO 0x07
#define JFFS2_COMPR_LZARI 0x08
/* Compatibility flags. */
#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
#define JFFS2_NODE_ACCURATE 0x2000
......@@ -87,39 +89,6 @@ typedef struct {
uint16_t v16;
} __attribute__((packed)) jint16_t;
#define JFFS2_NATIVE_ENDIAN
/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
whatever OS we're actually running on here too. */
#if defined(JFFS2_NATIVE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){x})
#define cpu_to_je32(x) ((jint32_t){x})
#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
#define je16_to_cpu(x) ((x).v16)
#define je32_to_cpu(x) ((x).v32)
#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
#elif defined(JFFS2_BIG_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
#define je16_to_cpu(x) (be16_to_cpu(x.v16))
#define je32_to_cpu(x) (be32_to_cpu(x.v32))
#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
#elif defined(JFFS2_LITTLE_ENDIAN)
#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
#define je16_to_cpu(x) (le16_to_cpu(x.v16))
#define je32_to_cpu(x) (le32_to_cpu(x.v32))
#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
#else
#error wibble
#endif
struct jffs2_unknown_node
{
/* All start like this */
......
/* $Id: jffs2_fs_i.h,v 1.15 2002/11/12 09:42:49 dwmw2 Exp $ */
/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */
#ifndef _JFFS2_FS_I
#define _JFFS2_FS_I
......@@ -36,9 +36,11 @@ struct jffs2_inode_info {
uint16_t flags;
uint8_t usercompr;
#if !defined (__ECOS)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
struct inode vfs_inode;
#endif
#endif
};
#endif /* _JFFS2_FS_I */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment