mtd/fs/jffs2 erase.c,1.78,1.79
gleixner at infradead.org
gleixner at infradead.org
Thu Jul 14 12:34:39 EDT 2005
Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv27288
Modified Files:
erase.c
Log Message:
[JFFS2] Fix node allocation leak
When the cleanmarker write failed, the allocated raw node was not
freed.
Index: erase.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/erase.c,v
retrieving revision 1.78
retrieving revision 1.79
diff -u -r1.78 -r1.79
--- erase.c 14 Jul 2005 14:31:34 -0000 1.78
+++ erase.c 14 Jul 2005 16:34:35 -0000 1.79
@@ -300,20 +300,21 @@
jeb->last_node = NULL;
}
-static int jffs2_block_is_erased(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
+static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
{
void *ebuf;
uint32_t ofs;
size_t retlen;
- int ret = 0;
+ int ret = -1;
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ebuf) {
- printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
- return 1; /* XXX: Not sure who came up with this policy. If we don't get memory, we just assume the block actually is erased */
+ printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
+ return -EAGAIN;
}
D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+
for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) {
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
int i;
@@ -341,7 +342,7 @@
ofs += readlen;
cond_resched();
}
- ret = 1;
+ ret = 0;
fail:
kfree(ebuf);
return ret;
@@ -354,45 +355,31 @@
int ret;
uint32_t bad_offset;
- if (! jffs2_block_is_erased(c, jeb, &bad_offset))
- goto bad2;
-
- if (!jffs2_cleanmarker_oob(c)) {
- marker_ref = jffs2_alloc_raw_node_ref();
- if (!marker_ref) {
- printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
- /* Stick it back on the list from whence it came and come back later */
- jffs2_erase_pending_trigger(c);
- spin_lock(&c->erase_completion_lock);
- list_add(&jeb->list, &c->erase_complete_list);
- spin_unlock(&c->erase_completion_lock);
- return;
- }
+ switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+ case -EAGAIN: goto refile;
+ case -1: goto filebad;
}
- 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)) {
+ bad_offset = jeb->offset;
- if (jffs2_write_nand_cleanmarker(c, jeb))
- goto bad2;
-
- jeb->first_node = jeb->last_node = NULL;
+ /* Cleanmarker in oob area or no cleanmarker at all ? */
+ if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
- jeb->free_size = c->sector_size;
- jeb->used_size = 0;
- jeb->dirty_size = 0;
- jeb->wasted_size = 0;
- } else if (c->cleanmarker_size == 0) {
- jeb->first_node = jeb->last_node = NULL;
+ if (jffs2_cleanmarker_oob(c)) {
+ if (jffs2_write_nand_cleanmarker(c, jeb))
+ goto filebad;
+ }
+ jeb->first_node = jeb->last_node = NULL;
jeb->free_size = c->sector_size;
jeb->used_size = 0;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
+
} else {
+
struct kvec vecs[1];
struct jffs2_unknown_node marker = {
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
@@ -400,21 +387,28 @@
.totlen = cpu_to_je32(c->cleanmarker_size)
};
+ marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+ printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+ goto refile;
+ }
+
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
vecs[0].iov_base = (unsigned char *) ▮
vecs[0].iov_len = sizeof(marker);
ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
- if (ret) {
- printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
- jeb->offset, ret);
- goto bad2;
- }
- if (retlen != sizeof(marker)) {
- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
- jeb->offset, sizeof(marker), retlen);
- goto bad2;
+ if (ret || retlen != sizeof(marker)) {
+ if (ret)
+ printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+ jeb->offset, ret);
+ else
+ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+ jeb->offset, sizeof(marker), retlen);
+
+ jffs2_free_raw_node_ref(marker_ref);
+ goto filebad;
}
marker_ref->next_in_ino = NULL;
@@ -445,18 +439,20 @@
wake_up(&c->erase_wait);
return;
-bad_marker:
- if (!jffs2_cleanmarker_oob(c))
- jffs2_free_raw_node_ref(marker_ref);
-bad2:
+filebad:
spin_lock(&c->erase_completion_lock);
- /* Stick it on a list (any list) so
- erase_failed can take it right off
- again. Silly, but shouldn't happen
- often. */
+ /* 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);
jffs2_erase_failed(c, jeb, bad_offset);
return;
-}
+refile:
+ /* Stick it back on the list from whence it came and come back later */
+ jffs2_erase_pending_trigger(c);
+ spin_lock(&c->erase_completion_lock);
+ list_add(&jeb->list, &c->erase_complete_list);
+ spin_unlock(&c->erase_completion_lock);
+ return;
+}
More information about the linux-mtd-cvs
mailing list