Skip to content

Commit

Permalink
[JFFS2] Debug code simplification, update TODO
Browse files Browse the repository at this point in the history
Simplify the debugging code further.
Update the TODO list

Signed-off-by: Artem B. Bityutskiy <[email protected]>
Signed-off-by: Thomas Gleixner <[email protected]>
  • Loading branch information
dedekind authored and Thomas Gleixner committed Nov 6, 2005
1 parent b523b3b commit 733802d
Show file tree
Hide file tree
Showing 14 changed files with 235 additions and 266 deletions.
38 changes: 19 additions & 19 deletions fs/jffs2/TODO
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
$Id: TODO,v 1.10 2002/09/09 16:31:21 dwmw2 Exp $
$Id: TODO,v 1.18 2005/09/22 11:24:56 dedekind Exp $

- support asynchronous operation -- add a per-fs 'reserved_space' count,
let each outstanding write reserve the _maximum_ amount of physical
space it could take. Let GC flush the outstanding writes because the
reservations will necessarily be pessimistic. With this we could even
do shared writable mmap, if we can have a fs hook for do_wp_page() to
make the reservation.
- disable compression in commit_write()?
- fine-tune the allocation / GC thresholds
- chattr support - turning on/off and tuning compression per-inode
Expand All @@ -11,30 +17,24 @@ $Id: TODO,v 1.10 2002/09/09 16:31:21 dwmw2 Exp $
- test, test, test

- NAND flash support:
- flush_wbuf using GC to fill it, don't just pad.
- Deal with write errors. Data don't get lost - we just have to write
the affected node(s) out again somewhere else.
- make fsync flush only if actually required
- make sys_sync() work.
- reboot notifier
- timed flush of old wbuf
- fix magical second arg of jffs2_flush_wbuf(). Split into two or more functions instead.

- almost done :)
- use bad block check instead of the hardwired byte check

- Optimisations:
- Stop GC from decompressing and immediately recompressing nodes which could
just be copied intact. (We now keep track of REF_PRISTINE flag. Easy now.)
- Furthermore, in the case where it could be copied intact we don't even need
to call iget() for it -- if we use (raw_node_raw->flash_offset & 2) as a flag
to show a node can be copied intact and it's _not_ in icache, we could just do
it, fix up the next_in_ino list and move on. We would need a way to find out
_whether_ it's in icache though -- if it's in icache we also need to do the
fragment lists, etc. P'raps a flag or pointer in the jffs2_inode_cache could
help. (We have half of this now.)
- Split writes so they go to two separate blocks rather than just c->nextblock.
By writing _new_ nodes to one block, and garbage-collected REF_PRISTINE
nodes to a different one, we can separate clean nodes from those which
are likely to become dirty, and end up with blocks which are each far
closer to 100% or 0% clean, hence speeding up later GC progress dramatically.
- Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in
the full dirent, we only need to go to the flash in lookup() when we think we've
got a match, and in readdir().
- Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately?
- Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into
jffs2_mark_node_obsolete(). Can all callers work it out?
- Remove size from jffs2_raw_node_frag.

dedekind:
1. __jffs2_flush_wbuf() has a strange 'pad' parameter. Eliminate.
2. get_sb()->build_fs()->scan() path... Why get_sb() removes scan()'s crap in
case of failure? scan() does not clean everything. Fix.
112 changes: 53 additions & 59 deletions fs/jffs2/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: build.c,v 1.79 2005/09/07 11:21:57 havasi Exp $
* $Id: build.c,v 1.83 2005/09/21 15:52:33 dedekind Exp $
*
*/

Expand All @@ -18,7 +18,8 @@
#include <linux/mtd/mtd.h>
#include "nodelist.h"

static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
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)
Expand Down Expand Up @@ -46,38 +47,36 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
ic = next_inode(&i, ic, (c)))


static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
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));
dbg_fsbuild("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 */
/* we 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",
dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
fd->name, fd->ino, ic->ino);
jffs2_mark_node_obsolete(c, fd->raw);
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? */
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
fd->name, fd->ino, ic->ino);
/* TODO: 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 */
dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
/* Can't free scan_dents so far. We might need them in pass 2 */
}
}

Expand All @@ -94,6 +93,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
struct jffs2_full_dirent *fd;
struct jffs2_full_dirent *dead_fds = NULL;

dbg_fsbuild("build FS data structures\n");

/* First, scan the medium and build all the inode caches with
lists of physical nodes */

Expand All @@ -103,60 +104,54 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
if (ret)
goto exit;

D1(printk(KERN_DEBUG "Scanned flash completely\n"));
dbg_fsbuild("scanned flash completely\n");
jffs2_dbg_dump_block_lists_nolock(c);

dbg_fsbuild("pass 1 starting\n");
c->flags |= JFFS2_SB_FLAG_BUILDING;
/* 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));

D1(BUG_ON(ic->ino > c->highest_ino));

if (ic->scan_dents) {
jffs2_build_inode_pass1(c, ic);
cond_resched();
}
}

D1(printk(KERN_DEBUG "Pass 1 complete\n"));
dbg_fsbuild("pass 1 complete\n");

/* 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. */
D1(printk(KERN_DEBUG "Pass 2 starting\n"));
dbg_fsbuild("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;

jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
cond_resched();
}

D1(printk(KERN_DEBUG "Pass 2a starting\n"));
dbg_fsbuild("pass 2a starting\n");

while (dead_fds) {
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"));
dbg_fsbuild("pass 2a complete\n");
dbg_fsbuild("freeing temporary data structures\n");

/* Finally, we can scan again and free the dirent structs */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));

while(ic->scan_dents) {
fd = ic->scan_dents;
ic->scan_dents = fd->next;
Expand All @@ -167,8 +162,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
}
c->flags &= ~JFFS2_SB_FLAG_BUILDING;

D1(printk(KERN_DEBUG "Pass 3 complete\n"));
jffs2_dbg_dump_block_lists_nolock(c);
dbg_fsbuild("FS build complete\n");

/* Rotate the lists by some number to ensure wear levelling */
jffs2_rotate_lists(c);
Expand All @@ -189,24 +183,26 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
return ret;
}

static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
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;

D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino);

raw = ic->nodes;
while (raw != (void *)ic) {
struct jffs2_raw_node_ref *next = raw->next_in_ino;
D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw));
jffs2_mark_node_obsolete(c, raw);
raw = next;
}

if (ic->scan_dents) {
int whinged = 0;
D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
dbg_fsbuild("inode #%u was a directory which may have children...\n", ic->ino);

while(ic->scan_dents) {
struct jffs2_inode_cache *child_ic;
Expand All @@ -216,21 +212,19 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jf

if (!fd->ino) {
/* It's a deletion dirent. Ignore it */
D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name);
jffs2_free_full_dirent(fd);
continue;
}
if (!whinged) {
if (!whinged)
whinged = 1;
printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
}

D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
fd->name, fd->ino));
dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino);

child_ic = jffs2_get_ino_cache(c, fd->ino);
if (!child_ic) {
printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n",
fd->name, fd->ino);
jffs2_free_full_dirent(fd);
continue;
}
Expand All @@ -241,13 +235,13 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jf
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));
dbg_fsbuild("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));
dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
fd->ino, fd->name, child_ic->nlink);
jffs2_free_full_dirent(fd);
}
}
Expand Down Expand Up @@ -295,20 +289,20 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
trying to GC to make more space. It'll be a fruitless task */
c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);

D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n",
c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n",
c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n",
c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n",
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
c->nospc_dirty_size));
dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks);
dbg_fsbuild("Blocks required to allow deletion: %d (%d KiB)\n",
c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024);
dbg_fsbuild("Blocks required to allow writes: %d (%d KiB)\n",
c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024);
dbg_fsbuild("Blocks required to quiesce GC thread: %d (%d KiB)\n",
c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024);
dbg_fsbuild("Blocks required to allow GC merges: %d (%d KiB)\n",
c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024);
dbg_fsbuild("Blocks required to GC bad blocks: %d (%d KiB)\n",
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
c->nospc_dirty_size);
}

int jffs2_do_mount_fs(struct jffs2_sb_info *c)
Expand Down Expand Up @@ -358,7 +352,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
return ret;

if (jffs2_build_filesystem(c)) {
D1(printk(KERN_DEBUG "build_fs failed\n"));
dbg_fsbuild("build_fs failed\n");
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
#ifndef __ECOS
Expand Down
3 changes: 2 additions & 1 deletion fs/jffs2/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: debug.c,v 1.10 2005/09/14 16:57:32 dedekind Exp $
* $Id: debug.c,v 1.11 2005/09/21 13:28:35 dedekind Exp $
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
#include "debug.h"

Expand Down
Loading

0 comments on commit 733802d

Please sign in to comment.