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)
......
......@@ -2,137 +2,473 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* 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: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $
*
*/
#if defined(__KERNEL__) || defined (__ECOS)
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/types.h>
#else
#define KERN_DEBUG
#define KERN_NOTICE
#define KERN_WARNING
#define printk printf
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#endif
#include "compr.h"
static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
/* Available compressors are on this list */
static LIST_HEAD(jffs2_compressor_list);
/* Actual compression mode */
static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
#include <linux/jffs2.h>
void jffs2_set_compression_mode(int mode)
{
jffs2_compression_mode = mode;
}
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
int jffs2_get_compression_mode(void)
{
return jffs2_compression_mode;
}
/* Statistics for blocks stored without compression */
static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
/* jffs2_compress:
* @data: Pointer to uncompressed data
* @cdata: Pointer to buffer for compressed data
* @cdata: Pointer to returned pointer to buffer for compressed data
* @datalen: On entry, holds the amount of data available for compression.
* On exit, expected to hold the amount of data actually compressed.
* @cdatalen: On entry, holds the amount of space available for compressed
* data. On exit, expected to hold the actual size of the compressed
* data.
*
* Returns: Byte to be stored with data indicating compression type used.
* Returns: Lower byte to be stored with data indicating compression type used.
* Zero is used to show that the data could not be compressed - the
* compressed version was actually larger than the original.
* Upper byte will be used later. (soon)
*
* If the cdata buffer isn't large enough to hold all the uncompressed data,
* jffs2_compress should compress as much as will fit, and should set
* *datalen accordingly to show the amount of data which were compressed.
*/
unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *datalen, uint32_t *cdatalen)
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 ret;
int ret = JFFS2_COMPR_NONE;
int compr_ret;
struct jffs2_compressor *this, *best=NULL;
unsigned char *output_buf = NULL, *tmp_buf;
uint32_t orig_slen, orig_dlen;
uint32_t best_slen=0, best_dlen=0;
ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_ZLIB;
}
#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_DYNRUBIN;
}
#endif
#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RUBINMIPS;
}
#endif
/* rtime does manage to recompress already-compressed data */
ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
if (!ret) {
return JFFS2_COMPR_RTIME;
}
#if 0
/* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
/* If we get here, no compression is going to work */
/* But we might want to use the fragmentation part -- Arjan */
memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
if (*datalen > *cdatalen)
*datalen = *cdatalen;
#endif
return JFFS2_COMPR_NONE; /* We failed to compress */
switch (jffs2_compression_mode) {
case JFFS2_COMPR_MODE_NONE:
break;
case JFFS2_COMPR_MODE_PRIORITY:
output_buf = kmalloc(*cdatalen,GFP_KERNEL);
if (!output_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
goto out;
}
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
ret = this->compr;
this->stat_compr_blocks++;
this->stat_compr_orig_size += *datalen;
this->stat_compr_new_size += *cdatalen;
break;
}
}
spin_unlock(&jffs2_compressor_list_lock);
if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
break;
case JFFS2_COMPR_MODE_SIZE:
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
/* Allocating memory for output buffer if necessary */
if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
spin_unlock(&jffs2_compressor_list_lock);
kfree(this->compr_buf);
spin_lock(&jffs2_compressor_list_lock);
this->compr_buf_size=0;
this->compr_buf=NULL;
}
if (!this->compr_buf) {
spin_unlock(&jffs2_compressor_list_lock);
tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
spin_lock(&jffs2_compressor_list_lock);
if (!tmp_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
continue;
}
else {
this->compr_buf = tmp_buf;
this->compr_buf_size = orig_dlen;
}
}
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
if ((!best_dlen)||(best_dlen>*cdatalen)) {
best_dlen = *cdatalen;
best_slen = *datalen;
best = this;
}
}
}
if (best_dlen) {
*cdatalen = best_dlen;
*datalen = best_slen;
output_buf = best->compr_buf;
best->compr_buf = NULL;
best->compr_buf_size = 0;
best->stat_compr_blocks++;
best->stat_compr_orig_size += best_slen;
best->stat_compr_new_size += best_dlen;
ret = best->compr;
}
spin_unlock(&jffs2_compressor_list_lock);
break;
default:
printk(KERN_ERR "JFFS2: unknow compression mode.\n");
}
out:
if (ret == JFFS2_COMPR_NONE) {
*cpage_out = data_in;
*datalen = *cdatalen;
none_stat_compr_blocks++;
none_stat_compr_size += *datalen;
}
else {
*cpage_out = output_buf;
}
return ret;
}
int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
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)
{
switch (comprtype) {
struct jffs2_compressor *this;
int ret;
switch (comprtype & 0xff) {
case JFFS2_COMPR_NONE:
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy(data_out, cdata_in, datalen);
none_stat_decompr_blocks++;
break;
case JFFS2_COMPR_ZERO:
memset(data_out, 0, datalen);
break;
default:
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (comprtype == this->compr) {
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
if (ret) {
printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
}
else {
this->stat_decompr_blocks++;
}
this->usecount--;
spin_unlock(&jffs2_compressor_list_lock);
return ret;
}
}
printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype);
spin_unlock(&jffs2_compressor_list_lock);
return -EIO;
}
return 0;
}
case JFFS2_COMPR_ZLIB:
jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
break;
int jffs2_register_compressor(struct jffs2_compressor *comp)
{
struct jffs2_compressor *this;
case JFFS2_COMPR_RTIME:
jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
break;
if (!comp->name) {
printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
return -1;
}
comp->compr_buf_size=0;
comp->compr_buf=NULL;
comp->usecount=0;
comp->stat_compr_orig_size=0;
comp->stat_compr_new_size=0;
comp->stat_compr_blocks=0;
comp->stat_decompr_blocks=0;
D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (this->priority < comp->priority) {
list_add(&comp->list, this->list.prev);
goto out;
}
}
list_add_tail(&comp->list, &jffs2_compressor_list);
out:
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
int jffs2_unregister_compressor(struct jffs2_compressor *comp)
{
D2(struct jffs2_compressor *this;)
D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
spin_lock(&jffs2_compressor_list_lock);
if (comp->usecount) {
spin_unlock(&jffs2_compressor_list_lock);
printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
return -1;
}
list_del(&comp->list);
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
#ifdef CONFIG_JFFS2_PROC
#define JFFS2_STAT_BUF_SIZE 16000
char *jffs2_list_compressors(void)
{
struct jffs2_compressor *this;
char *buf, *act_buf;
act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
list_for_each_entry(this, &jffs2_compressor_list, list) {
act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
if ((this->disabled)||(!this->compress))
act_buf += sprintf(act_buf,"disabled");
else
act_buf += sprintf(act_buf,"enabled");
act_buf += sprintf(act_buf,"\n");
}
return buf;
}
char *jffs2_stats(void)
{
struct jffs2_compressor *this;
char *buf, *act_buf;
act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
act_buf += sprintf(act_buf,"%10s ","none");
act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks,
none_stat_compr_size, none_stat_decompr_blocks);
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
act_buf += sprintf(act_buf,"%10s ",this->name);
if ((this->disabled)||(!this->compress))
act_buf += sprintf(act_buf,"- ");
else
act_buf += sprintf(act_buf,"+ ");
act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks,
this->stat_compr_new_size, this->stat_compr_orig_size,
this->stat_decompr_blocks);
act_buf += sprintf(act_buf,"\n");
}
spin_unlock(&jffs2_compressor_list_lock);
return buf;
}
char *jffs2_get_compression_mode_name(void)
{
switch (jffs2_compression_mode) {
case JFFS2_COMPR_MODE_NONE:
return "none";
case JFFS2_COMPR_MODE_PRIORITY:
return "priority";
case JFFS2_COMPR_MODE_SIZE:
return "size";
}
return "unkown";
}
int jffs2_set_compression_mode_name(const char *name)
{
if (!strcmp("none",name)) {
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
return 0;
}
if (!strcmp("priority",name)) {
jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
return 0;
}
if (!strcmp("size",name)) {
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
return 0;
}
return 1;
}
static int jffs2_compressor_Xable(const char *name, int disabled)
{
struct jffs2_compressor *this;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (!strcmp(this->name, name)) {
this->disabled = disabled;
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
}
spin_unlock(&jffs2_compressor_list_lock);
printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
return 1;
}
int jffs2_enable_compressor_name(const char *name)
{
return jffs2_compressor_Xable(name, 0);
}
int jffs2_disable_compressor_name(const char *name)
{
return jffs2_compressor_Xable(name, 1);
}
int jffs2_set_compressor_priority(const char *name, int priority)
{
struct jffs2_compressor *this,*comp;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (!strcmp(this->name, name)) {
this->priority = priority;
comp = this;
goto reinsert;
}
}
spin_unlock(&jffs2_compressor_list_lock);
printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
return 1;
reinsert:
/* list is sorted in the order of priority, so if
we change it we have to reinsert it into the
good place */
list_del(&comp->list);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (this->priority < comp->priority) {
list_add(&comp->list, this->list.prev);
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
}
list_add_tail(&comp->list, &jffs2_compressor_list);
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
case JFFS2_COMPR_RUBINMIPS:
#if 0 /* Disabled 23/9/1 */
jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
#else
printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
#endif
break;
case JFFS2_COMPR_DYNRUBIN:
#if 1 /* Phase this one out */
jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
{
if (orig != comprbuf)
kfree(comprbuf);
}
int jffs2_compressors_init(void)
{
/* Registering compressors */
#ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_init();
#endif
#ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_init();
#endif
#ifdef CONFIG_JFFS2_RUBIN
jffs2_rubinmips_init();
jffs2_dynrubin_init();
#endif
#ifdef CONFIG_JFFS2_LZARI
jffs2_lzari_init();
#endif
#ifdef CONFIG_JFFS2_LZO
jffs2_lzo_init();
#endif
/* Setting default compression mode */
#ifdef CONFIG_JFFS2_CMODE_NONE
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
#else
printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
#ifdef CONFIG_JFFS2_CMODE_SIZE
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
#else
D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
#endif
break;
#endif
return 0;
}
default:
printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
return -EIO;
}
return 0;
int jffs2_compressors_exit(void)
{
/* Unregistering compressors */
#ifdef CONFIG_JFFS2_LZO
jffs2_lzo_exit();
#endif
#ifdef CONFIG_JFFS2_LZARI
jffs2_lzari_exit();
#endif
#ifdef CONFIG_JFFS2_RUBIN
jffs2_dynrubin_exit();
jffs2_rubinmips_exit();
#endif
#ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_exit();
#endif
#ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_exit();
#endif
return 0;
}
/*
* 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... */
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: gc.c,v 1.114 2003/10/09 13:53:35 dwmw2 Exp $
* $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $
*
*/
......@@ -19,6 +19,7 @@
#include <linux/compiler.h>
#include <linux/stat.h>
#include "nodelist.h"
#include "compr.h"
static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
struct jffs2_inode_cache *ic,
......@@ -36,7 +37,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic);
struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
/* Called with erase_completion_lock held */
static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
......@@ -80,7 +81,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
nextlist = &c->erasable_list;
} else {
/* Eep. All were empty */
printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
return NULL;
}
......@@ -112,11 +113,11 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{
struct jffs2_inode_info *f;
struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
uint32_t inum;
int ret = 0;
int ret = 0, inum, nlink;
if (down_interruptible(&c->alloc_sem))
return -EINTR;
......@@ -186,7 +187,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic->state = INO_STATE_CHECKING;
spin_unlock(&c->inocache_lock);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
ret = jffs2_do_crccheck_inode(c, ic);
if (ret)
......@@ -204,7 +205,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
jeb = jffs2_find_gc_block(c);
if (!jeb) {
printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -EIO;
......@@ -223,17 +224,21 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
while(ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
jeb->gc_node = raw = raw->next_phys;
if (!raw) {
raw = raw->next_phys;
if (unlikely(!raw)) {
printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
jeb->gc_node = raw;
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
BUG();
}
}
jeb->gc_node = raw;
D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
if (!raw->next_in_ino) {
/* Inode-less node. Clean marker, snapshot or something like that */
/* FIXME: If it's something that needs to be copied, including something
......@@ -243,13 +248,17 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
up(&c->alloc_sem);
goto eraseit_lock;
}
inum = jffs2_raw_ref_to_inum(raw);
D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
ic = jffs2_raw_ref_to_ic(raw);
/* We need to hold the inocache. Either the erase_completion_lock or
the inocache_lock are sufficient; we trade down since the inocache_lock
causes less contention. */
spin_lock(&c->inocache_lock);
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
/* Three possibilities:
1. Inode is already in-core. We must iget it and do proper
......@@ -259,11 +268,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
3. Inode is not in-core, node is not pristine. We must iget()
and take the slow path.
*/
spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, inum);
/* This should never fail unless I'm particularly stupid.
So we don't check before dereferencing it */
switch(ic->state) {
case INO_STATE_CHECKEDABSENT:
......@@ -275,28 +279,28 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic->state = INO_STATE_GC;
else {
D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
inum));
ic->ino));
}
break;
case INO_STATE_PRESENT:
case INO_STATE_UNCHECKED:
/* It's in-core or hasn't been checked. GC must iget() it. */
/* It's in-core. GC must iget() it. */
break;
case INO_STATE_UNCHECKED:
case INO_STATE_CHECKING:
case INO_STATE_GC:
/* Should never happen. We should have finished checking
by the time we actually start doing any GC. */
by the time we actually start doing any GC, and since
we're holding the alloc_sem, no other garbage collection
can happen.
*/
printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
ic->ino, ic->state);
up(&c->alloc_sem);
spin_unlock(&c->inocache_lock);
BUG();
case INO_STATE_GC:
/* Should never happen. We are holding the alloc_sem,
no other garbage collection can happen. Note that we
do depend on this later when deciding to do a simple
node copy */
BUG();
case INO_STATE_READING:
/* Someone's currently trying to read it. We must wait for
them to finish and then go through the full iget() route
......@@ -306,7 +310,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
up(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
inum, ic->state));
ic->ino, ic->state));
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
/* And because we dropped the alloc_sem we must start again from the
beginning. Ponder chance of livelock here -- we're returning success
......@@ -319,27 +323,50 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
A: Small enough that I don't care :)
*/
return 0;
}
spin_unlock(&c->inocache_lock);
/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
node intact, and we don't have to muck about with the fragtree etc.
because we know it's not in-core. If it _was_ in-core, we go through
all the iget() crap anyway */
if (ic->state == INO_STATE_GC) {
spin_unlock(&c->inocache_lock);
ret = jffs2_garbage_collect_pristine(c, ic, raw);
jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
if (ret != -EBADFD)
spin_lock(&c->inocache_lock);
ic->state = INO_STATE_CHECKEDABSENT;
wake_up(&c->inocache_wq);
if (ret != -EBADFD) {
spin_unlock(&c->inocache_lock);
goto release_sem;
}
/* Fall through if it wanted us to */
/* Fall through if it wanted us to, with inocache_lock held */
}
ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
/* Prevent the fairly unlikely race where the gcblock is
entirely obsoleted by the final close of a file which had
the only valid nodes in the block, followed by erasure,
followed by freeing of the ic because the erased block(s)
held _all_ the nodes of that inode.... never been seen but
it's vaguely possible. */
inum = ic->ino;
nlink = ic->nlink;
spin_unlock(&c->inocache_lock);
f = jffs2_gc_fetch_inode(c, inum, nlink);
if (IS_ERR(f))
return PTR_ERR(f);
if (!f)
return 0;
ret = jffs2_garbage_collect_live(c, jeb, raw, f);
jffs2_gc_release_inode(c, f);
release_sem:
up(&c->alloc_sem);
......@@ -362,38 +389,35 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
return ret;
}
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
{
struct jffs2_inode_info *f;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *fn = NULL;
struct jffs2_full_dirent *fd;
uint32_t start = 0, end = 0, nrfrags = 0;
struct inode *inode;
int ret = 0;
inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
if (is_bad_inode(inode)) {
printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
/* NB. This will happen again. We need to do something appropriate here. */
up(&c->alloc_sem);
iput(inode);
return -EIO;
}
f = JFFS2_INODE_INFO(inode);
down(&f->sem);
/* Now we have the lock for this inode. Check that it's still the one at the head
of the list. */
spin_lock(&c->erase_completion_lock);
if (c->gcblock != jeb) {
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
goto upnout;
}
if (ref_obsolete(raw)) {
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
/* They'll call again */
goto upnout;
}
spin_unlock(&c->erase_completion_lock);
/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
if (f->metadata && f->metadata->raw == raw) {
fn = f->metadata;
......@@ -406,30 +430,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era
if (frag->node && frag->node->raw == raw) {
fn = frag->node;
end = frag->ofs + frag->size;
#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */
if (!nrfrags && 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(raw), fn->frags);
mark_ref_normal(raw);
}
/* 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) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
ref_offset(raw));
mark_ref_normal(raw);
}
if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
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(raw), frag->ofs, frag->ofs+frag->size);
mark_ref_normal(raw);
}
}
#endif
if (!nrfrags++)
start = frag->ofs;
if (nrfrags == frag->node->frags)
......@@ -438,10 +438,10 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era
}
if (fn) {
if (ref_flags(raw) == REF_PRISTINE) {
ret = jffs2_garbage_collect_pristine(c, ic, raw);
ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
if (!ret) {
/* Urgh. Return it sensibly. */
frag->node->raw = ic->nodes;
frag->node->raw = f->inocache->nodes;
}
if (ret != -EBADFD)
goto upnout;
......@@ -478,7 +478,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era
}
upnout:
up(&f->sem);
iput(inode);
return ret;
}
......@@ -492,30 +491,32 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
size_t retlen;
int ret;
uint32_t phys_ofs, alloclen;
uint32_t crc;
uint32_t crc, rawlen;
int retried = 0;
D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
rawlen = ref_totlen(c, c->gcblock, raw);
/* Ask for a small amount of space (or the totlen if smaller) because we
don't want to force wastage of the end of a block if splitting would
work. */
ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
&phys_ofs, &alloclen);
ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN,
rawlen), &phys_ofs, &alloclen);
if (ret)
return ret;
if (alloclen < raw->totlen) {
if (alloclen < rawlen) {
/* Doesn't fit untouched. We'll go the old route and split it */
return -EBADFD;
}
node = kmalloc(raw->totlen, GFP_KERNEL);
node = kmalloc(rawlen, GFP_KERNEL);
if (!node)
return -ENOMEM;
ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
if (!ret && retlen != raw->totlen)
ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
if (!ret && retlen != rawlen)
ret = -EIO;
if (ret)
goto out_node;
......@@ -578,14 +579,14 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
/* OK, all the CRCs are good; this node can just be copied as-is. */
retry:
nraw->flash_offset = phys_ofs;
nraw->totlen = raw->totlen;
nraw->__totlen = rawlen;
nraw->next_phys = NULL;
ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
if (ret || (retlen != raw->totlen)) {
if (ret || (retlen != rawlen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
raw->totlen, phys_ofs, ret, retlen);
rawlen, phys_ofs, ret, retlen);
if (retlen) {
/* Doesn't belong to any inode */
nraw->next_in_ino = NULL;
......@@ -609,7 +610,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
ret = jffs2_reserve_space_gc(c, raw->totlen, &phys_ofs, &dummy);
ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
if (!ret) {
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
......@@ -674,7 +675,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
return -ENOMEM;
}
ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
if (ret) {
printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
kfree(mdata);
......@@ -779,13 +780,17 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
delete a 'real' dirent with the same name that's still
somewhere else on the flash. */
if (!jffs2_can_mark_obsolete(c)) {
struct jffs2_raw_dirent rd;
struct jffs2_raw_dirent *rd;
struct jffs2_raw_node_ref *raw;
int ret;
size_t retlen;
int name_len = strlen(fd->name);
uint32_t name_crc = crc32(0, fd->name, name_len);
char *namebuf = NULL;
uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
rd = kmalloc(rawlen, GFP_KERNEL);
if (!rd)
return -ENOMEM;
/* Prevent the erase code from nicking the obsolete node refs while
we're looking at them. I really don't like this extra lock but
......@@ -793,91 +798,66 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
down(&c->erase_free_sem);
for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
/* We only care about obsolete ones */
if (!(ref_obsolete(raw)))
continue;
/* Any dirent with the same name is going to have the same length... */
if (ref_totlen(c, NULL, raw) != rawlen)
continue;
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
if ((raw->flash_offset & ~(c->sector_size-1)) ==
(fd->raw->flash_offset & ~(c->sector_size-1)))
continue;
/* This is an obsolete node belonging to the same directory */
ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
/* This is an obsolete node belonging to the same directory, and it's of the right
length. We need to take a closer look...*/
ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw));
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continue to obsolete it. Continue */
continue;
}
if (retlen != sizeof(struct jffs2_unknown_node)) {
if (retlen != rawlen) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
retlen, rawlen, ref_offset(raw));
continue;
}
if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT ||
PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len))
continue;
/* OK, it's a dirent node, it's the right length. We have to take a
closer look at it... */
ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continune to obsolete it. Continue */
if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
continue;
}
if (retlen != sizeof(rd)) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
retlen, sizeof(rd), ref_offset(raw));
continue;
}
/* If the name CRC doesn't match, skip */
if (je32_to_cpu(rd.name_crc) != name_crc)
if (je32_to_cpu(rd->name_crc) != name_crc)
continue;
/* If the name length doesn't match, or it's another deletion dirent, skip */
if (rd.nsize != name_len || !je32_to_cpu(rd.ino))
if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
continue;
/* OK, check the actual name now */
if (!namebuf) {
namebuf = kmalloc(name_len + 1, GFP_KERNEL);
if (!namebuf) {
up(&c->erase_free_sem);
return -ENOMEM;
}
}
/* We read the extra byte before it so it's a word-aligned read */
ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf);
if (ret) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continune to obsolete it. Continue */
continue;
}
if (retlen != name_len+1) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
retlen, name_len+1, ref_offset(raw));
continue;
}
if (memcmp(namebuf+1, fd->name, name_len))
if (memcmp(rd->name, fd->name, name_len))
continue;
/* OK. The name really does match. There really is still an older node on
the flash which our deletion dirent obsoletes. So we have to write out
a new deletion dirent to replace it */
if (namebuf)
kfree(namebuf);
up(&c->erase_free_sem);
D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
kfree(rd);
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
up(&c->erase_free_sem);
if (namebuf)
kfree(namebuf);
kfree(rd);
}
/* No need for it any more. Just mark it obsolete and remove it from the list */
......@@ -1008,6 +988,9 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
je32_to_cpu(ri.ino));
});
/* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
mark_ref_normal(new_fn->raw);
for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
frag; frag = frag_next(frag)) {
if (frag->ofs > fn->size + fn->ofs)
......@@ -1042,10 +1025,9 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
int ret = 0;
unsigned char *comprbuf = NULL, *writebuf;
struct page *pg;
unsigned long pg;
unsigned char *pg_ptr;
/* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f);
memset(&ri, 0, sizeof(ri));
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
......@@ -1184,23 +1166,18 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
* page OK. We'll actually write it out again in commit_write, which is a little
* suboptimal, but at least we're correct.
*/
#ifdef __ECOS
pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
#else
pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
#endif
if (IS_ERR(pg)) {
printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
return PTR_ERR(pg);
pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
if (IS_ERR(pg_ptr)) {
printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
return PTR_ERR(pg_ptr);
}
pg_ptr = (char *)kmap(pg);
comprbuf = kmalloc(end - start, GFP_KERNEL);
offset = start;
while(offset < orig_end) {
uint32_t datalen;
uint32_t cdatalen;
char comprtype = JFFS2_COMPR_NONE;
uint16_t comprtype = JFFS2_COMPR_NONE;
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
......@@ -1214,14 +1191,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
if (comprbuf) {
comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
}
if (comprtype) {
writebuf = comprbuf;
} else {
datalen = cdatalen;
}
comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
......@@ -1239,11 +1210,14 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
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, writebuf, cdatalen));
ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, ALLOC_GC);
new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
jffs2_free_comprbuf(comprbuf, writebuf);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
......@@ -1258,12 +1232,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
f->metadata = NULL;
}
}
if (comprbuf) kfree(comprbuf);
kunmap(pg);
/* XXX: Does the page get freed automatically? */
/* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
page_cache_release(pg);
jffs2_gc_release_page(c, pg_ptr, &pg);
return ret;
}
/*
* 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 $
*
*/
......
......@@ -2,12 +2,14 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
* Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: wbuf.c,v 1.53 2003/10/11 11:46:09 dwmw2 Exp $
* $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
......@@ -27,7 +29,7 @@ static unsigned char *brokenbuf;
#endif
/* max. erase failures before we mark a block bad */
#define MAX_ERASE_FAILURES 5
#define MAX_ERASE_FAILURES 2
/* two seconds timeout for timed wbuf-flushing */
#define WBUF_FLUSH_TIMEOUT 2 * HZ
......@@ -179,10 +181,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
first_raw = &jeb->first_node;
while (*first_raw &&
(ref_obsolete(*first_raw) ||
(ref_offset(*first_raw) + (*first_raw)->totlen) < c->wbuf_ofs)) {
(ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
ref_offset(*first_raw), ref_flags(*first_raw),
(ref_offset(*first_raw) + (*first_raw)->totlen),
(ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
c->wbuf_ofs));
first_raw = &(*first_raw)->next_phys;
}
......@@ -195,13 +197,13 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
}
start = ref_offset(*first_raw);
end = ref_offset(*first_raw) + (*first_raw)->totlen;
end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
/* Find the last node to be recovered */
raw = first_raw;
while ((*raw)) {
if (!ref_obsolete(*raw))
end = ref_offset(*raw) + (*raw)->totlen;
end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
raw = &(*raw)->next_phys;
}
......@@ -295,7 +297,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
return;
raw2->flash_offset = ofs | REF_OBSOLETE;
raw2->totlen = (*first_raw)->totlen;
raw2->__totlen = ref_totlen(c, jeb, *first_raw);
raw2->next_phys = NULL;
raw2->next_in_ino = NULL;
......@@ -336,24 +338,26 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
raw = first_raw;
while (*raw) {
uint32_t rawlen = ref_totlen(c, jeb, *raw);
D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
(*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs));
rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
if (ref_obsolete(*raw)) {
/* Shouldn't really happen much */
new_jeb->dirty_size += (*raw)->totlen;
new_jeb->free_size -= (*raw)->totlen;
c->dirty_size += (*raw)->totlen;
new_jeb->dirty_size += rawlen;
new_jeb->free_size -= rawlen;
c->dirty_size += rawlen;
} else {
new_jeb->used_size += (*raw)->totlen;
new_jeb->free_size -= (*raw)->totlen;
jeb->dirty_size += (*raw)->totlen;
jeb->used_size -= (*raw)->totlen;
c->dirty_size += (*raw)->totlen;
new_jeb->used_size += rawlen;
new_jeb->free_size -= rawlen;
jeb->dirty_size += rawlen;
jeb->used_size -= rawlen;
c->dirty_size += rawlen;
}
c->free_size -= (*raw)->totlen;
c->free_size -= rawlen;
(*raw)->flash_offset = ofs | ref_flags(*raw);
ofs += (*raw)->totlen;
ofs += rawlen;
new_jeb->last_node = *raw;
raw = &(*raw)->next_phys;
......@@ -422,6 +426,9 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
} else {
/* Pad with JFFS2_DIRTY_BITMASK */
memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
}
}
/* else jffs2_flash_writev has actually filled in the rest of the
......@@ -454,31 +461,34 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
return ret;
}
/* Adjusting free size of next block only, if it's called from fsync ! */
if (pad == 2) {
D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
spin_lock(&c->erase_completion_lock);
if (!c->nextblock)
BUG();
spin_lock(&c->erase_completion_lock);
/* Adjust free size of the block if we padded. */
if (pad) {
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
(jeb==c->nextblock)?"next":"", jeb->offset));
/* wbuf_pagesize - wbuf_len is the amount of space that's to be
padded. If there is less free space in the block than that,
something screwed up */
if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
c->nextblock->offset, c->nextblock->free_size);
jeb->offset, jeb->free_size);
BUG();
}
c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
spin_unlock(&c->erase_completion_lock);
}
/* Stick any now-obsoleted blocks on the erase_pending_list */
spin_lock(&c->erase_completion_lock);
jffs2_refile_wbuf_blocks(c);
jffs2_clear_wbuf_ino_list(c);
spin_unlock(&c->erase_completion_lock);
......@@ -512,8 +522,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
old_wbuf_ofs = c->wbuf_ofs;
old_wbuf_len = c->wbuf_len;
while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
if (c->unchecked_size) {
/* GC won't make any progress for a while */
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
ret = __jffs2_flush_wbuf(c, 2);
} else while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
up(&c->alloc_sem);
......@@ -835,9 +849,8 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
size_t retlen;
int oob_size;
oob_size = c->mtd->oobsize;
/* allocate a buffer for all oob data in this sector */
oob_size = c->mtd->oobsize;
len = 4 * oob_size;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
......@@ -861,35 +874,23 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
goto out;
}
/* Special check for first two pages */
for (page = 0; page < 2 * oob_size; page += oob_size) {
/* Check for bad block marker */
if (buf[page+c->badblock_pos] != 0xff) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
/* Return 2 for bad and 3 for failed block
bad goes to list_bad and failed to list_erase */
ret = (!page) ? 2 : 3;
/* Special check for first page */
for(i = 0; i < oob_size ; i++) {
/* Yeah, we know about the cleanmarker. */
if (mode && i >= c->fsdata_pos &&
i < c->fsdata_pos + c->fsdata_len)
continue;
if (buf[i] != 0xFF) {
D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
buf[page+i], page+i, jeb->offset));
ret = 1;
goto out;
}
for(i = 0; i < oob_size ; i++) {
/* Yeah, we know about the cleanmarker. */
if (mode && i >= c->fsdata_pos &&
i < c->fsdata_pos+c->fsdata_len)
continue;
if (buf[page+i] != 0xFF) {
D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
buf[page+i], page+i, jeb->offset));
ret = 1;
goto out;
}
}
/* only the first page can contain a cleanmarker !*/
mode = 0;
}
}
/* we know, we are aligned :) */
for (; page < len; page += sizeof(long)) {
for (page = oob_size; page < len; page += sizeof(long)) {
unsigned long dat = *(unsigned long *)(&buf[page]);
if(dat != -1) {
ret = 1;
......@@ -912,7 +913,7 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_unknown_node n;
unsigned char buf[32];
unsigned char buf[2 * NAND_MAX_OOBSIZE];
unsigned char *p;
int ret, i, cnt, retval = 0;
size_t retlen, offset;
......@@ -923,6 +924,11 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
/* Loop through the physical blocks */
for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
/* Check first if the block is bad. */
if (c->mtd->block_isbad (c->mtd, offset)) {
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
return 2;
}
/*
* We read oob data from page 0 and 1 of the block.
* page 0 contains cleanmarker and badblock info
......@@ -939,19 +945,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
return -EIO;
}
/* Check for bad block marker */
if (buf[c->badblock_pos] != 0xff) {
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos));
return 2;
}
/* Check for failure counter in the second page */
if (buf[c->badblock_pos + oob_size] != 0xff) {
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
return 3;
}
/* Check cleanmarker only on the first physical block */
if (!cnt) {
n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
......@@ -1002,136 +995,100 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
}
/*
* We try to get the failure count of this block.
*/
int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
unsigned char buf[16];
int ret;
size_t retlen;
int oob_size;
oob_size = c->mtd->oobsize;
ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
if (ret) {
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
return ret;
}
if (retlen < oob_size) {
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
return -EIO;
}
jeb->bad_count = buf[c->badblock_pos];
return 0;
}
/*
* On NAND we try to mark this block bad. We try to write how often
* the block was erased and mark it finaly bad, if the count
* is > MAX_ERASE_FAILURES. We read this information on mount !
* jeb->bad_count contains the count before this erase.
* On NAND we try to mark this block bad. If the block was erased more
* than MAX_ERASE_FAILURES we mark it finaly bad.
* Don't care about failures. This block remains on the erase-pending
* or badblock list as long as nobody manipulates the flash with
* a bootloader or something like that.
*/
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
{
unsigned char buf = 0x0;
int ret;
size_t retlen;
/* if the count is < max, we try to write the counter to the 2nd page oob area */
if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
buf = (unsigned char)jeb->bad_count;
c->badblock_pos += c->mtd->oobblock;
}
ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
if( ++jeb->bad_count < MAX_ERASE_FAILURES)
return 0;
if (!c->mtd->block_markbad)
return 1; // What else can we do?
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
ret = c->mtd->block_markbad(c->mtd, bad_offset);
if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
return ret;
}
if (retlen != 1) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
return ret;
}
return 0;
return 1;
}
#define JFFS2_OOB_ECCPOS0 0
#define JFFS2_OOB_ECCPOS1 1
#define JFFS2_OOB_ECCPOS2 2
#define JFFS2_OOB_ECCPOS3 3
#define JFFS2_OOB_ECCPOS4 6
#define JFFS2_OOB_ECCPOS5 7
#define NAND_JFFS2_OOB8_FSDAPOS 6
#define NAND_JFFS2_OOB16_FSDAPOS 8
#define NAND_JFFS2_OOB8_FSDALEN 2
#define NAND_JFFS2_OOB16_FSDALEN 8
static struct nand_oobinfo jffs2_oobinfo_swecc = {
.useecc = 1,
.eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
};
static struct nand_oobinfo jffs2_oobinfo_docecc = {
.useecc = 1,
.useecc = MTD_NANDECC_PLACE,
.eccbytes = 6,
.eccpos = {0,1,2,3,4,5}
};
int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
{
struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
/* Do this only, if we have an oob buffer */
if (!c->mtd->oobsize)
return 0;
/* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_size = 0;
/* Initialise write buffer */
c->wbuf_pagesize = c->mtd->oobblock;
c->wbuf_ofs = 0xFFFFFFFF;
/* FIXME: If we had a generic way of describing the hardware's
use of OOB area, we could perhaps make this generic too. */
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
c->oobinfo = &jffs2_oobinfo_swecc;
if (c->mtd->oobsize == 8) {
c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
} else {
c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
/* Should we use autoplacement ? */
if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
/* Get the position of the free bytes */
if (!oinfo->oobfree[0][0]) {
printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
return -ENOSPC;
}
c->fsdata_pos = oinfo->oobfree[0][0];
c->fsdata_len = oinfo->oobfree[0][1];
if (c->fsdata_len > 8)
c->fsdata_len = 8;
} else {
/* This is just a legacy fallback and should go away soon */
switch(c->mtd->ecctype) {
case MTD_ECC_RS_DiskOnChip:
printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
c->oobinfo = &jffs2_oobinfo_docecc;
c->fsdata_pos = 6;
c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
c->badblock_pos = 15;
break;
default:
D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
return -EINVAL;
}
c->badblock_pos = NAND_BADBLOCK_POS;
break;
case MTD_ECC_RS_DiskOnChip:
D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
c->oobinfo = &jffs2_oobinfo_docecc;
c->fsdata_pos = 6;
c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
c->badblock_pos = 15;
break;
default:
printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
c->mtd->ecctype);
return -EINVAL;
}
return 0;
}
int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{
int res;
/* Initialise write buffer */
c->wbuf_pagesize = c->mtd->oobblock;
c->wbuf_ofs = 0xFFFFFFFF;
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
res = jffs2_nand_set_oobinfo(c);
#ifdef BREAKME
if (!brokenbuf)
brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
......@@ -1141,7 +1098,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
}
memset(brokenbuf, 0xdb, c->wbuf_pagesize);
#endif
return 0;
return res;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
......
......@@ -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