diff options
| author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-11-07 04:45:16 -0500 |
|---|---|---|
| committer | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-11-08 05:01:35 -0500 |
| commit | 5399dd1fc8f5e812db931225ef5f67d89f3b1a56 (patch) | |
| tree | cb75ad2f91fb2ec6ad70f3c1a055090418fc643f /fs | |
| parent | b419148e567728f6af0c3b01965c1cc141e3e13a (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')
| -rw-r--r-- | fs/nilfs2/ioctl.c | 30 |
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; |
