nilfs2: fix kernel oops in error case of nilfs_ioctl_move_blocks
This fixes a kernel oops reported by Markus Trippelsdorf in the email titled "[NILFS users] kernel Oops while running nilfs_cleanerd". The oops was caused by a bug of error path in nilfs_ioctl_move_blocks() function, which was inlined in nilfs_ioctl_clean_segments(). nilfs_ioctl_move_blocks checks duplication of blocks which will be moved in garbage collection. But, the check should have be done within nilfs_ioctl_move_inode_block() to prevent list corruption among buffers storing the target blocks. To fix the kernel oops, this moves forward the duplication check before the list insertion. I also tested this for stable trees [2.6.30, 2.6.31]. Reported-by: Markus Trippelsdorf <markus@trippelsdorf.de> Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Cc: stable <stable@kernel.org>
This commit is contained in:
parent
b419148e56
commit
5399dd1fc8
|
@ -297,7 +297,18 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode,
|
||||||
(unsigned long long)vdesc->vd_vblocknr);
|
(unsigned long long)vdesc->vd_vblocknr);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
bh->b_private = vdesc;
|
if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
|
||||||
|
printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
|
||||||
|
"cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
|
||||||
|
__func__, vdesc->vd_flags ? "node" : "data",
|
||||||
|
(unsigned long long)vdesc->vd_ino,
|
||||||
|
(unsigned long long)vdesc->vd_cno,
|
||||||
|
(unsigned long long)vdesc->vd_offset,
|
||||||
|
(unsigned long long)vdesc->vd_blocknr,
|
||||||
|
(unsigned long long)vdesc->vd_vblocknr);
|
||||||
|
brelse(bh);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
list_add_tail(&bh->b_assoc_buffers, buffers);
|
list_add_tail(&bh->b_assoc_buffers, buffers);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -335,24 +346,10 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
|
||||||
list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
|
list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
|
||||||
ret = nilfs_gccache_wait_and_mark_dirty(bh);
|
ret = nilfs_gccache_wait_and_mark_dirty(bh);
|
||||||
if (unlikely(ret < 0)) {
|
if (unlikely(ret < 0)) {
|
||||||
if (ret == -EEXIST) {
|
WARN_ON(ret == -EEXIST);
|
||||||
vdesc = bh->b_private;
|
|
||||||
printk(KERN_CRIT
|
|
||||||
"%s: conflicting %s buffer: "
|
|
||||||
"ino=%llu, cno=%llu, offset=%llu, "
|
|
||||||
"blocknr=%llu, vblocknr=%llu\n",
|
|
||||||
__func__,
|
|
||||||
vdesc->vd_flags ? "node" : "data",
|
|
||||||
(unsigned long long)vdesc->vd_ino,
|
|
||||||
(unsigned long long)vdesc->vd_cno,
|
|
||||||
(unsigned long long)vdesc->vd_offset,
|
|
||||||
(unsigned long long)vdesc->vd_blocknr,
|
|
||||||
(unsigned long long)vdesc->vd_vblocknr);
|
|
||||||
}
|
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
list_del_init(&bh->b_assoc_buffers);
|
list_del_init(&bh->b_assoc_buffers);
|
||||||
bh->b_private = NULL;
|
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
}
|
}
|
||||||
return nmembs;
|
return nmembs;
|
||||||
|
@ -360,7 +357,6 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
|
||||||
failed:
|
failed:
|
||||||
list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
|
list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
|
||||||
list_del_init(&bh->b_assoc_buffers);
|
list_del_init(&bh->b_assoc_buffers);
|
||||||
bh->b_private = NULL;
|
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue