Commit 9bfeb691 authored by David Woodhouse's avatar David Woodhouse

[JFFS2] Switch to using an array of jffs2_raw_node_refs instead of a list.

This allows us to drop another pointer from the struct jffs2_raw_node_ref,
shrinking it to 8 bytes on 32-bit machines (if the TEST_TOTLEN) paranoia
check is turned off, which will be committed soon).
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent f75e5097
......@@ -285,20 +285,25 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *ref;
struct jffs2_raw_node_ref *block, *ref;
D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
while(jeb->first_node) {
ref = jeb->first_node;
jeb->first_node = ref->next_phys;
/* Remove from the inode-list */
if (ref->next_in_ino)
block = ref = jeb->first_node;
while (ref) {
if (ref->flash_offset == REF_LINK_NODE) {
ref = ref->next_in_ino;
jffs2_free_refblock(block);
block = ref;
continue;
}
if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino)
jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
/* else it was a non-inode node or already removed, so don't bother */
__jffs2_free_raw_node_ref(ref);
ref++;
}
jeb->last_node = NULL;
jeb->first_node = jeb->last_node = NULL;
}
static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
......
......@@ -26,9 +26,6 @@ struct jffs2_inodirty;
struct jffs2_sb_info {
struct mtd_info *mtd;
struct jffs2_raw_node_ref *refs;
int reserved_refs;
uint32_t highest_ino;
uint32_t checked_ino;
......
......@@ -57,8 +57,8 @@ int __init jffs2_create_slab_caches(void)
if (!tmp_dnode_info_slab)
goto err;
raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
sizeof(struct jffs2_raw_node_ref),
raw_node_ref_slab = kmem_cache_create("jffs2_refblock",
sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1),
0, 0, NULL, NULL);
if (!raw_node_ref_slab)
goto err;
......@@ -190,38 +190,65 @@ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
kmem_cache_free(tmp_dnode_info_slab, x);
}
struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
{
struct jffs2_raw_node_ref *ret;
ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
if (ret) {
int i = 0;
for (i=0; i < REFS_PER_BLOCK; i++) {
ret[i].flash_offset = REF_EMPTY_NODE;
ret[i].next_in_ino = NULL;
}
ret[i].flash_offset = REF_LINK_NODE;
ret[i].next_in_ino = NULL;
}
return ret;
}
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb, int nr)
{
struct jffs2_raw_node_ref *p = c->refs;
struct jffs2_raw_node_ref **p, *ref;
int i = nr;
dbg_memalloc("%d\n", nr);
while (nr && p) {
p = p->next_in_ino;
nr--;
}
while (nr) {
p = __jffs2_alloc_raw_node_ref();
if (!p)
return -ENOMEM;
p->next_in_ino = c->refs;
c->refs = p;
nr--;
p = &jeb->last_node;
ref = *p;
dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset);
/* If jeb->last_node is really a valid node then skip over it */
if (ref && ref->flash_offset != REF_EMPTY_NODE)
ref++;
while (i) {
if (!ref) {
dbg_memalloc("Allocating new refblock linked from %p\n", p);
ref = *p = jffs2_alloc_refblock();
if (!ref)
return -ENOMEM;
}
if (ref->flash_offset == REF_LINK_NODE) {
p = &ref->next_in_ino;
ref = *p;
continue;
}
i--;
ref++;
}
c->reserved_refs = nr;
return 0;
}
jeb->allocated_refs = nr;
struct jffs2_raw_node_ref *__jffs2_alloc_raw_node_ref(void)
{
struct jffs2_raw_node_ref *ret;
ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
dbg_memalloc("%p\n", ret);
return ret;
dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n",
nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset,
jeb->last_node->next_in_ino);
return 0;
}
void __jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
void jffs2_free_refblock(struct jffs2_raw_node_ref *x)
{
dbg_memalloc("%p\n", x);
kmem_cache_free(raw_node_ref_slab, x);
......
......@@ -954,18 +954,16 @@ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
for (i=0; i<c->nr_blocks; i++) {
this = c->blocks[i].first_node;
while (this) {
next = this->next_phys;
__jffs2_free_raw_node_ref(this);
if (this[REFS_PER_BLOCK].flash_offset == REF_LINK_NODE)
next = this[REFS_PER_BLOCK].next_in_ino;
else
next = NULL;
jffs2_free_refblock(this);
this = next;
}
c->blocks[i].first_node = c->blocks[i].last_node = NULL;
}
this = c->refs;
while (this) {
next = this->next_in_ino;
__jffs2_free_raw_node_ref(this);
this = next;
}
}
struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
......@@ -1060,32 +1058,37 @@ struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
{
struct jffs2_raw_node_ref *ref;
/* These will be preallocated _very_ shortly. */
ref = c->refs;
if (!c->refs) {
JFFS2_WARNING("Using non-preallocated refs!\n");
ref = __jffs2_alloc_raw_node_ref();
BUG_ON(!ref);
WARN_ON(1);
} else {
c->refs = ref->next_in_ino;
BUG_ON(!jeb->allocated_refs);
jeb->allocated_refs--;
ref = jeb->last_node;
dbg_noderef("Last node at %p is (%08x,%p)\n", ref, ref->flash_offset,
ref->next_in_ino);
while (ref->flash_offset != REF_EMPTY_NODE) {
if (ref->flash_offset == REF_LINK_NODE)
ref = ref->next_in_ino;
else
ref++;
}
ref->next_phys = NULL;
dbg_noderef("New ref is %p (%08x becomes %08x,%p) len 0x%x\n", ref,
ref->flash_offset, ofs, ref->next_in_ino, len);
ref->flash_offset = ofs;
if (!jeb->first_node)
if (!jeb->first_node) {
jeb->first_node = ref;
if (jeb->last_node) {
jeb->last_node->next_phys = ref;
#ifdef TEST_TOTLEN
if (ref_offset(jeb->last_node) + jeb->last_node->__totlen != ref_offset(ref)) {
printk(KERN_CRIT "Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
ref_offset(jeb->last_node), ref_offset(jeb->last_node)+jeb->last_node->__totlen);
WARN_ON(1);
}
#endif
BUG_ON(ref_offset(ref) != jeb->offset);
} else if (unlikely(ref_offset(ref) != jeb->offset + c->sector_size - jeb->free_size)) {
uint32_t last_len = ref_totlen(c, jeb, jeb->last_node);
JFFS2_ERROR("Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
ref, ref_offset(ref), ref_offset(ref)+len,
ref_offset(jeb->last_node),
ref_offset(jeb->last_node)+last_len);
BUG();
}
jeb->last_node = ref;
......@@ -1130,12 +1133,13 @@ int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
{
if (!size)
return 0;
if (size > c->sector_size - jeb->used_size) {
printk(KERN_CRIT "Dirty space 0x%x larger then used_size 0x%x (wasted 0x%x)\n",
size, jeb->used_size, jeb->wasted_size);
if (unlikely(size > jeb->free_size)) {
printk(KERN_CRIT "Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
size, jeb->free_size, jeb->wasted_size);
BUG();
}
if (jeb->last_node && ref_obsolete(jeb->last_node)) {
/* REF_EMPTY_NODE is !obsolete, so that works OK */
if (ref_obsolete(jeb->last_node)) {
#ifdef TEST_TOTLEN
jeb->last_node->__totlen += size;
#endif
......@@ -1168,7 +1172,7 @@ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
jeb = &c->blocks[ref->flash_offset / c->sector_size];
/* Last node in block. Use free_space */
if (ref != jeb->last_node) {
if (unlikely(ref != jeb->last_node)) {
printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0);
BUG();
......@@ -1183,17 +1187,13 @@ uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
{
uint32_t ret;
#if CONFIG_JFFS2_FS_DEBUG > 0
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();
}
#endif
ret = __ref_totlen(c, jeb, ref);
#ifdef TEST_TOTLEN
if (ret != ref->__totlen) {
if (unlikely(ret != ref->__totlen)) {
if (!jeb)
jeb = &c->blocks[ref->flash_offset / c->sector_size];
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);
......@@ -1204,13 +1204,14 @@ uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node);
printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size);
ret = ref->__totlen;
if (!jeb)
jeb = &c->blocks[ref->flash_offset / c->sector_size];
#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
__jffs2_dbg_dump_node_refs_nolock(c, jeb);
#endif
WARN_ON(1);
ret = ref->__totlen;
}
#endif /* TEST_TOTLEN */
return ret;
......
......@@ -80,7 +80,6 @@ struct jffs2_raw_node_ref
for this object. If this _is_ the last, it points to the inode_cache,
xattr_ref or xattr_datum instead. The common part of those structures
has NULL in the first word. See jffs2_raw_ref_to_ic() below */
struct jffs2_raw_node_ref *next_phys;
uint32_t flash_offset;
#define TEST_TOTLEN
#ifdef TEST_TOTLEN
......@@ -88,7 +87,29 @@ struct jffs2_raw_node_ref
#endif
};
#define ref_next(r) ((r)->next_phys)
#define REF_LINK_NODE ((int32_t)-1)
#define REF_EMPTY_NODE ((int32_t)-2)
/* Use blocks of about 256 bytes */
#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
{
ref++;
/* Link to another block of refs */
if (ref->flash_offset == REF_LINK_NODE) {
ref = ref->next_in_ino;
if (!ref)
return ref;
}
/* End of chain */
if (ref->flash_offset == REF_EMPTY_NODE)
return NULL;
return ref;
}
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
{
......@@ -234,6 +255,7 @@ struct jffs2_eraseblock
uint32_t wasted_size;
uint32_t free_size; /* Note that sector_size - free_size
is the address of the first free space */
uint32_t allocated_refs;
struct jffs2_raw_node_ref *first_node;
struct jffs2_raw_node_ref *last_node;
......@@ -378,10 +400,9 @@ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
void jffs2_free_raw_inode(struct jffs2_raw_inode *);
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb, int nr);
struct jffs2_raw_node_ref *__jffs2_alloc_raw_node_ref(void);
void __jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
void jffs2_free_refblock(struct jffs2_raw_node_ref *);
struct jffs2_node_frag *jffs2_alloc_node_frag(void);
void jffs2_free_node_frag(struct jffs2_node_frag *);
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
......
......@@ -458,14 +458,13 @@ static inline int on_list(struct list_head *obj, struct list_head *head)
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
{
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *next_ref;
int blocknr;
struct jffs2_unknown_node n;
int ret, addedsize;
size_t retlen;
uint32_t freed_len;
if(!ref) {
if(unlikely(!ref)) {
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
return;
}
......@@ -683,54 +682,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_unlock(&c->erase_completion_lock);
}
/* Merge with the next node in the physical list, if there is one
and if it's also obsolete and if it doesn't belong to any inode */
next_ref = ref_next(ref);
if (next_ref && ref_obsolete(next_ref) && !next_ref->next_in_ino) {
spin_lock(&c->erase_completion_lock);
#ifdef TEST_TOTLEN
ref->__totlen += next_ref->__totlen;
#endif
ref->next_phys = ref_next(next_ref);
if (jeb->last_node == next_ref) jeb->last_node = ref;
if (jeb->gc_node == next_ref) {
/* gc will be happy continuing gc on this node */
jeb->gc_node=ref;
}
spin_unlock(&c->erase_completion_lock);
__jffs2_free_raw_node_ref(next_ref);
}
/* Also merge with the previous node in the list, if there is one
and that one is obsolete */
if (ref != jeb->first_node ) {
struct jffs2_raw_node_ref *p = jeb->first_node;
spin_lock(&c->erase_completion_lock);
while ((next_ref = ref_next(ref)) != ref)
p = next_ref;
if (ref_obsolete(p) && !ref->next_in_ino) {
#ifdef TEST_TOTLEN
p->__totlen += ref->__totlen;
#endif
if (jeb->last_node == ref) {
jeb->last_node = p;
}
if (jeb->gc_node == ref) {
/* gc will be happy continuing gc on this node */
jeb->gc_node=p;
}
p->next_phys = ref_next(ref);
__jffs2_free_raw_node_ref(ref);
}
spin_unlock(&c->erase_completion_lock);
}
out_erase_sem:
up(&c->erase_free_sem);
}
......
......@@ -95,6 +95,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_dataflash(c) (0)
#define jffs2_dataflash_setup(c) (0)
#define jffs2_dataflash_cleanup(c) do {} while (0)
#define jffs2_nor_wbuf_flash(c) (0)
#define jffs2_nor_wbuf_flash_setup(c) (0)
#define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
......
......@@ -511,7 +511,8 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
spr = (struct jffs2_sum_xref_flash *)sp;
dbg_summary("xref at %#08x-%#08x\n",
jeb->offset + je32_to_cpu(spr->offset),
jeb->offset + je32_to_cpu(spr->offset) + PAD(sizeof(struct jffs2_raw_xref)));
jeb->offset + je32_to_cpu(spr->offset) +
(uint32_t)PAD(sizeof(struct jffs2_raw_xref)));
ref = jffs2_alloc_xattr_ref();
if (!ref) {
......@@ -787,10 +788,12 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n",
infosize, sum_ofs, ret, retlen);
/* Waste remaining space */
spin_lock(&c->erase_completion_lock);
jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
spin_unlock(&c->erase_completion_lock);
if (retlen) {
/* Waste remaining space */
spin_lock(&c->erase_completion_lock);
jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
spin_unlock(&c->erase_completion_lock);
}
c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
......@@ -836,6 +839,7 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
jffs2_sum_disable_collecting(c->summary);
JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize);
spin_lock(&c->erase_completion_lock);
return 0;
}
......
......@@ -156,72 +156,126 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock
jffs2_erase_pending_trigger(c);
}
/* Adjust its size counts accordingly */
c->wasted_size += jeb->free_size;
c->free_size -= jeb->free_size;
jeb->wasted_size += jeb->free_size;
jeb->free_size = 0;
if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) {
uint32_t oldfree = jeb->free_size;
jffs2_link_node_ref(c, jeb,
(jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE,
oldfree, NULL);
/* convert to wasted */
c->wasted_size += oldfree;
jeb->wasted_size += oldfree;
c->dirty_size -= oldfree;
jeb->dirty_size -= oldfree;
}
jffs2_dbg_dump_block_lists_nolock(c);
jffs2_dbg_acct_sanity_check_nolock(c,jeb);
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
}
static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c,
struct jffs2_inode_info *f,
struct jffs2_raw_node_ref *raw,
union jffs2_node_union *node)
{
struct jffs2_node_frag *frag;
struct jffs2_full_dirent *fd;
dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n",
node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype));
BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 &&
je16_to_cpu(node->u.magic) != 0);
switch (je16_to_cpu(node->u.nodetype)) {
case JFFS2_NODETYPE_INODE:
frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset));
BUG_ON(!frag);
/* Find a frag which refers to the full_dnode we want to modify */
while (!frag->node || frag->node->raw != raw) {
frag = frag_next(frag);
BUG_ON(!frag);
}
dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node);
return &frag->node->raw;
break;
case JFFS2_NODETYPE_DIRENT:
for (fd = f->dents; fd; fd = fd->next) {
if (fd->raw == raw) {
dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd);
return &fd->raw;
}
}
BUG();
default:
dbg_noderef("Don't care about replacing raw for nodetype %x\n",
je16_to_cpu(node->u.nodetype));
break;
}
return NULL;
}
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *jeb, *new_jeb;
struct jffs2_raw_node_ref **first_raw, **raw;
struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL;
size_t retlen;
int ret;
int nr_refile = 0;
unsigned char *buf;
uint32_t start, end, ofs, len;
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
if (jffs2_prealloc_raw_node_refs(c, jeb, c->reserved_refs + 1))
return;
spin_lock(&c->erase_completion_lock);
jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
spin_unlock(&c->erase_completion_lock);
BUG_ON(!ref_obsolete(jeb->last_node));
/* Find the first node to be recovered, by skipping over every
node which ends before the wbuf starts, or which is obsolete. */
first_raw = &jeb->first_node;
while (*first_raw &&
(ref_obsolete(*first_raw) ||
(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) + ref_totlen(c, jeb, *first_raw)),
c->wbuf_ofs));
first_raw = &(*first_raw)->next_phys;
for (next = raw = jeb->first_node; next; raw = next) {
next = ref_next(raw);
if (ref_obsolete(raw) ||
(next && ref_offset(next) <= c->wbuf_ofs)) {
dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
ref_offset(raw), ref_flags(raw),
(ref_offset(raw) + ref_totlen(c, jeb, raw)),
c->wbuf_ofs);
continue;
}
dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n",
ref_offset(raw), ref_flags(raw),
(ref_offset(raw) + ref_totlen(c, jeb, raw)));
first_raw = raw;
break;
}
if (!*first_raw) {
if (!first_raw) {
/* All nodes were obsolete. Nothing to recover. */
D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
spin_unlock(&c->erase_completion_lock);
c->wbuf_len = 0;
return;
}
start = ref_offset(*first_raw);
end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
start = ref_offset(first_raw);
end = ref_offset(jeb->last_node);
nr_refile = 1;
/* Find the last node to be recovered */
raw = first_raw;
while ((*raw)) {
if (!ref_obsolete(*raw))
end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
/* Count the number of refs which need to be copied */
while ((raw = ref_next(raw)) != jeb->last_node)
nr_refile++;
raw = &(*raw)->next_phys;
}
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n",
start, end, end - start, nr_refile);
buf = NULL;
if (start < c->wbuf_ofs) {
......@@ -248,13 +302,24 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
kfree(buf);
buf = NULL;
read_failed:
first_raw = &(*first_raw)->next_phys;
first_raw = ref_next(first_raw);
nr_refile--;
while (first_raw && ref_obsolete(first_raw)) {
first_raw = ref_next(first_raw);
nr_refile--;
}
/* If this was the only node to be recovered, give up */
if (!(*first_raw))
if (!first_raw) {
c->wbuf_len = 0;
return;
}
/* It wasn't. Go on and try to recover nodes complete in the wbuf */
start = ref_offset(*first_raw);
start = ref_offset(first_raw);
dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n",
start, end, end - start, nr_refile);
} else {
/* Read succeeded. Copy the remaining data from the wbuf */
memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
......@@ -263,7 +328,6 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
Either 'buf' contains the data, or we find it in the wbuf */
/* ... and get an allocation of space from a shiny new block instead */
ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE);
if (ret) {
......@@ -271,6 +335,14 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
kfree(buf);
return;
}
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
if (ret) {
printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
kfree(buf);
return;
}
ofs = write_ofs(c);
if (end-start >= c->wbuf_pagesize) {
......@@ -304,7 +376,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
kfree(buf);
if (retlen)
jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, *first_raw), NULL);
jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL);
return;
}
......@@ -314,12 +386,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
c->wbuf_ofs = ofs + towrite;
memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
kfree(buf);
} else {
/* OK, now we're left with the dregs in whichever buffer we're using */
if (buf) {
memcpy(c->wbuf, buf, end-start);
kfree(buf);
} else {
memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
}
......@@ -331,62 +401,111 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
new_jeb = &c->blocks[ofs / c->sector_size];
spin_lock(&c->erase_completion_lock);
if (new_jeb->first_node) {
/* Odd, but possible with ST flash later maybe */
new_jeb->last_node->next_phys = *first_raw;
} else {
new_jeb->first_node = *first_raw;
}
raw = first_raw;
while (*raw) {
uint32_t rawlen = ref_totlen(c, jeb, *raw);
for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) {
uint32_t rawlen = ref_totlen(c, jeb, raw);
struct jffs2_inode_cache *ic;
struct jffs2_raw_node_ref *new_ref;
struct jffs2_raw_node_ref **adjust_ref = NULL;
struct jffs2_inode_info *f = NULL;
D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
rawlen, ref_offset(raw), ref_flags(raw), ofs));
ic = jffs2_raw_ref_to_ic(raw);
/* Ick. This XATTR mess should be fixed shortly... */
if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) {
struct jffs2_xattr_datum *xd = (void *)ic;
BUG_ON(xd->node != raw);
adjust_ref = &xd->node;
raw->next_in_ino = NULL;
ic = NULL;
} else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) {
struct jffs2_xattr_datum *xr = (void *)ic;
BUG_ON(xr->node != raw);
adjust_ref = &xr->node;
raw->next_in_ino = NULL;
ic = NULL;
} else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) {
struct jffs2_raw_node_ref **p = &ic->nodes;
/* Remove the old node from the per-inode list */
while (*p && *p != (void *)ic) {
if (*p == raw) {
(*p) = (raw->next_in_ino);
raw->next_in_ino = NULL;
break;
}
p = &((*p)->next_in_ino);
}
if (ref_obsolete(*raw)) {
/* Shouldn't really happen much */
new_jeb->dirty_size += rawlen;
new_jeb->free_size -= rawlen;
c->dirty_size += rawlen;
} else {
new_jeb->used_size += rawlen;
new_jeb->free_size -= rawlen;
if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) {
/* If it's an in-core inode, then we have to adjust any
full_dirent or full_dnode structure to point to the
new version instead of the old */
f = jffs2_gc_fetch_inode(c, ic->ino, ic->nlink);
if (IS_ERR(f)) {
/* Should never happen; it _must_ be present */
JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",
ic->ino, PTR_ERR(f));
BUG();
}
/* We don't lock f->sem. There's a number of ways we could
end up in here with it already being locked, and nobody's
going to modify it on us anyway because we hold the
alloc_sem. We're only changing one ->raw pointer too,
which we can get away with without upsetting readers. */
adjust_ref = jffs2_incore_replace_raw(c, f, raw,
(void *)(buf?:c->wbuf) + (ref_offset(raw) - start));
} else if (unlikely(ic->state != INO_STATE_PRESENT &&
ic->state != INO_STATE_CHECKEDABSENT &&
ic->state != INO_STATE_GC)) {
JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state);
BUG();
}
}
new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic);
if (adjust_ref) {
BUG_ON(*adjust_ref != raw);
*adjust_ref = new_ref;
}
if (f)
jffs2_gc_release_inode(c, f);
if (!ref_obsolete(raw)) {
jeb->dirty_size += rawlen;
jeb->used_size -= rawlen;
c->dirty_size += rawlen;
c->used_size -= rawlen;
raw->flash_offset = ref_offset(raw) | REF_OBSOLETE;
BUG_ON(raw->next_in_ino);
}
c->free_size -= rawlen;
(*raw)->flash_offset = ofs | ref_flags(*raw);
ofs += rawlen;
new_jeb->last_node = *raw;
raw = &(*raw)->next_phys;
}
kfree(buf);
/* Fix up the original jeb now it's on the bad_list */
*first_raw = NULL;
if (first_raw == &jeb->first_node) {
jeb->last_node = NULL;
if (first_raw == jeb->first_node) {
D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
}
else
jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
jffs2_dbg_acct_sanity_check_nolock(c, jeb);
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
jffs2_dbg_acct_sanity_check_nolock(c, new_jeb);
jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb);
jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb);
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
D1(printk(KERN_DEBUG "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", c->wbuf_ofs, c->wbuf_len));
}
/* Meaning of pad argument:
......@@ -400,6 +519,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
{
struct jffs2_eraseblock *wbuf_jeb;
int ret;
size_t retlen;
......@@ -417,7 +537,8 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
if (!c->wbuf_len) /* already checked c->wbuf above */
return 0;
if (jffs2_prealloc_raw_node_refs(c, c->nextblock, c->reserved_refs + 1))
wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1))
return -ENOMEM;
/* claim remaining space on the page
......@@ -473,32 +594,29 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
/* Adjust free size of the block if we padded. */
if (pad) {
struct jffs2_eraseblock *jeb;
uint32_t waste = c->wbuf_pagesize - c->wbuf_len;
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_jeb==c->nextblock)?"next":"", wbuf_jeb->offset));
/* wbuf_pagesize - wbuf_len is the amount of space that's to be
padded. If there is less free space in the block than that,
something screwed up */
if (jeb->free_size < waste) {
if (wbuf_jeb->free_size < waste) {
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, waste);
printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
jeb->offset, jeb->free_size);
wbuf_jeb->offset, wbuf_jeb->free_size);
BUG();
}
spin_lock(&c->erase_completion_lock);
jffs2_link_node_ref(c, jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL);
jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL);
/* FIXME: that made it count as dirty. Convert to wasted */
jeb->dirty_size -= waste;
wbuf_jeb->dirty_size -= waste;
c->dirty_size -= waste;
jeb->wasted_size += waste;
wbuf_jeb->wasted_size += waste;
c->wasted_size += waste;
} else
spin_lock(&c->erase_completion_lock);
......@@ -758,7 +876,8 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
* This is the entry for flash write.
* Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
*/
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
size_t *retlen, const u_char *buf)
{
struct kvec vecs[1];
......@@ -953,7 +1072,7 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
}
D1(if (retval == 1) {
printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
printk(KERN_WARNING "OOB at %08x was ", offset);
printk(KERN_WARNING "OOB at %08zx was ", offset);
for (i=0; i < oob_size; i++) {
printk("%02x ", buf[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