Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5399dd1f authored by Ryusuke Konishi's avatar Ryusuke Konishi
Browse files

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: default avatarMarkus Trippelsdorf <markus@trippelsdorf.de>
Signed-off-by: default avatarRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: stable <stable@kernel.org>
parent b419148e
Loading
Loading
Loading
Loading
+13 −17
Original line number Diff line number Diff line
@@ -297,7 +297,18 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode,
			       (unsigned long long)vdesc->vd_vblocknr);
		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);
	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) {
		ret = nilfs_gccache_wait_and_mark_dirty(bh);
		if (unlikely(ret < 0)) {
			if (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);
			}
			WARN_ON(ret == -EEXIST);
			goto failed;
		}
		list_del_init(&bh->b_assoc_buffers);
		bh->b_private = NULL;
		brelse(bh);
	}
	return nmembs;
@@ -360,7 +357,6 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
 failed:
	list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
		list_del_init(&bh->b_assoc_buffers);
		bh->b_private = NULL;
		brelse(bh);
	}
	return ret;