reiserfs分区空闲8TB写文件提示磁盘空间不足
产品的老大直接找到了我:他们对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; // In case we are writing past the end of the last file block, break. 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
链接为:https://oenhan.com/reiserfs_check_can_fit_pages_for_8tb