提交 5399dd1f 编写于 作者: R Ryusuke Konishi

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: NMarkus Trippelsdorf <markus@trippelsdorf.de>
Signed-off-by: NRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: stable <stable@kernel.org>
上级 b419148e
...@@ -297,7 +297,18 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode, ...@@ -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, ...@@ -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, ...@@ -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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册