• Wang Xiaoguang's avatar
    btrfs: fix false enospc error when truncating heavily reflinked file · 47b5d646
    Wang Xiaoguang authored
    Below test script can reveal this bug:
        dd if=/dev/zero of=fs.img bs=$((1024*1024)) count=100
        dev=$(losetup --show -f fs.img)
        mkdir -p /mnt/mntpoint
        mkfs.btrfs  -f $dev
        mount $dev /mnt/mntpoint
        cd /mnt/mntpoint
    
        echo "workdir is: /mnt/mntpoint"
        blocksize=$((128 * 1024))
        dd if=/dev/zero of=testfile bs=$blocksize count=1
        sync
        count=$((17*1024*1024*1024/blocksize))
        echo "file size is:" $((count*blocksize))
        for ((i = 1; i <= $count; i++)); do
            dst_offset=$((blocksize * i))
            xfs_io -f -c "reflink testfile 0 $dst_offset $blocksize"\
                    testfile > /dev/null
        done
        sync
        truncate --size 0 testfile
    
    The last truncate operation will fail for ENOSPC reason, but indeed
    it should not fail.
    
    In btrfs_truncate(), we use a temporary block_rsv to do truncate
    operation. With every btrfs_truncate_inode_items() call, we migrate space
    to this block_rsv, but forget to cleanup previous reservation, which
    will make this block_rsv's reserved bytes keep growing, and this reserved
    space will only be released in the end of btrfs_truncate(), this metadata
    leak will impact other's metadata reservation. In this case, it's
    "btrfs_start_transaction(root, 2);" fails for enospc error, which make
    this truncate operation fail.
    
    Call btrfs_block_rsv_release() to fix this bug.
    Signed-off-by: default avatarWang Xiaoguang <wangxg.fnst@cn.fujitsu.com>
    Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    47b5d646
inode.c 283 KB