aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-11-07 04:45:16 -0500
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-11-08 05:01:35 -0500
commit5399dd1fc8f5e812db931225ef5f67d89f3b1a56 (patch)
treecb75ad2f91fb2ec6ad70f3c1a055090418fc643f /fs/nilfs2
parentb419148e567728f6af0c3b01965c1cc141e3e13a (diff)
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>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r--fs/nilfs2/ioctl.c30
1 files changed, 13 insertions, 17 deletions
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 6572ea4bc4df..89dd73ead9ac 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -297,7 +297,18 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode,
297 (unsigned long long)vdesc->vd_vblocknr); 297 (unsigned long long)vdesc->vd_vblocknr);
298 return ret; 298 return ret;
299 } 299 }
300 bh->b_private = vdesc; 300 if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
301 printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
302 "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
303 __func__, vdesc->vd_flags ? "node" : "data",
304 (unsigned long long)vdesc->vd_ino,
305 (unsigned long long)vdesc->vd_cno,
306 (unsigned long long)vdesc->vd_offset,
307 (unsigned long long)vdesc->vd_blocknr,
308 (unsigned long long)vdesc->vd_vblocknr);
309 brelse(bh);
310 return -EEXIST;
311 }
301 list_add_tail(&bh->b_assoc_buffers, buffers); 312 list_add_tail(&bh->b_assoc_buffers, buffers);
302 return 0; 313 return 0;
303} 314}
@@ -335,24 +346,10 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
335 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 346 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
336 ret = nilfs_gccache_wait_and_mark_dirty(bh); 347 ret = nilfs_gccache_wait_and_mark_dirty(bh);
337 if (unlikely(ret < 0)) { 348 if (unlikely(ret < 0)) {
338 if (ret == -EEXIST) { 349 WARN_ON(ret == -EEXIST);
339 vdesc = bh->b_private;
340 printk(KERN_CRIT
341 "%s: conflicting %s buffer: "
342 "ino=%llu, cno=%llu, offset=%llu, "
343 "blocknr=%llu, vblocknr=%llu\n",
344 __func__,
345 vdesc->vd_flags ? "node" : "data",
346 (unsigned long long)vdesc->vd_ino,
347 (unsigned long long)vdesc->vd_cno,
348 (unsigned long long)vdesc->vd_offset,
349 (unsigned long long)vdesc->vd_blocknr,
350 (unsigned long long)vdesc->vd_vblocknr);
351 }
352 goto failed; 350 goto failed;
353 } 351 }
354 list_del_init(&bh->b_assoc_buffers); 352 list_del_init(&bh->b_assoc_buffers);
355 bh->b_private = NULL;
356 brelse(bh); 353 brelse(bh);
357 } 354 }
358 return nmembs; 355 return nmembs;
@@ -360,7 +357,6 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
360 failed: 357 failed:
361 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 358 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
362 list_del_init(&bh->b_assoc_buffers); 359 list_del_init(&bh->b_assoc_buffers);
363 bh->b_private = NULL;
364 brelse(bh); 360 brelse(bh);
365 } 361 }
366 return ret; 362 return ret;