首页 > KernelBug > reiserfs分区空闲8TB写文件提示磁盘空间不足

reiserfs分区空闲8TB写文件提示磁盘空间不足

KernelBug 2013-02-04

产品的老大直接找到了我:他们对reiserfs的一个分区写文件数据时返回失败,返回值是28,既是No space left on device,直接到产品环境上一看,12TB的空间用了不到10GB,自己顿时头大了,我一直对“设计精妙,代码残废”的reiserfs心怀敬畏(首席设计人员中途溜号,后面全靠开发人员脑补的复杂工程真是灾难),恰好还有其他要紧任务,便把问题转给了李义处理,几天后,李义发了一个补丁给我:内核:kernel-2.6.16.60-0.83.2 for sles10sp3

diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c
index 50bdc7f..5046628 100644
--- a/fs/reiserfs/bitmap.c +++ b/fs/reiserfs/bitmap.c
@@ -1253,16 +1253,21 @@ void reiserfs_release_claimed_blocks(struct super_block *sb, /* super block of
int reiserfs_can_fit_pages(struct super_block *sb /* superblock of filesystem
to estimate space */ )
{
- int space;
+ unsigned long space, free_blocks, reserve_blocks;

spin_lock(&REISERFS_SB(sb)->bitmap_lock);
- space =
- (SB_FREE_BLOCKS(sb) -
- REISERFS_SB(sb)->reserved_blocks) >> (PAGE_CACHE_SHIFT -
- sb->s_blocksize_bits);
+
+ free_blocks = SB_FREE_BLOCKS(sb);
+ reserve_blocks = REISERFS_SB(sb)->reserved_blocks;
+ if (unlikely(free_blocks <= reserve_blocks))
+ space = 0;
+ else
+ space = (free_blocks - reserve_blocks) >> (PAGE_CACHE_SHIFT-
+ sb->s_blocksize_bits);
+
spin_unlock(&REISERFS_SB(sb)->bitmap_lock);

- return space > 0 ? space : 0;
+ return space;
}

void reiserfs_cache_bitmap_metadata(struct super_block *sb,

 

竟然是一个低级错误,多少年了木有人发现:int型溢出了 VFS调用reiserfs_file_operations中的reiserfs_file_write写数据会预判分区剩余空间

num_pages =
	min_t(size_t, REISERFS_WRITE_PAGES_AT_A_TIME,
		reiserfs_can_fit_pages(inode->i_sb));

if (!num_pages) {	/* If we do not have enough space even for a single page... */
			if (pos >
			    inode->i_size + inode->i_sb->s_blocksize -
			    (pos & (inode->i_sb->s_blocksize - 1))) {
				res = -ENOSPC;
				break;	// In case we are writing past the end of the last file block, break.
			}

当num_pages值为0时2×pos>filesize+blocksize(一般为4K)就会返回失败,提示空间不足。 而在reiserfs_can_fit_pages中:

int reiserfs_can_fit_pages(struct super_block *sb /* superblock of filesystem
to estimate space */ )
{
int space;

spin_lock(&REISERFS_SB(sb)->bitmap_lock);
space =
(SB_FREE_BLOCKS(sb) -
REISERFS_SB(sb)->reserved_blocks) >> (PAGE_CACHE_SHIFT -
sb->s_blocksize_bits);
spin_unlock(&REISERFS_SB(sb)->bitmap_lock);

return space > 0 ? space : 0;
}

目前文件系统的块号是32位(无符号),函数中int型,就最大支持0x7FFFFFFF个page,由于block和page大小皆为4KB,则当剩余空间达到0x7FFFFFFF×4KB即约8TB时,space值就会溢出,返回值为0,触发了问题。

总结:一开始以为是产品的代码问题,没有重视,内核补丁人员就要怀疑一切,(遥拜组内一位解决了intel处理器bug的大牛),像师兄说过“是人写的代码就有bug”,哪怕是如此低级的错误..

附带SUSE发布的补丁地址: http://kernel.opensuse.org/cgit/kernel/commit/?h=SLES10_SP4_BRANCH&id=5fc8d3a240af4a929e2bcb2d5baaca89536a924b


reiserfs分区空闲8TB写文件提示磁盘空间不足来自于OenHan,链接为:http://oenhan.com/reiserfs_check_can_fit_pages_for_8tb
更多阅读