2014-05-07 17:06:09 -04:00
/*
* Copyright ( C ) 2013 Facebook . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
# include "btrfs-tests.h"
# include "../ctree.h"
# include "../transaction.h"
# include "../disk-io.h"
# include "../qgroup.h"
static void init_dummy_trans ( struct btrfs_trans_handle * trans )
{
memset ( trans , 0 , sizeof ( * trans ) ) ;
trans - > transid = 1 ;
INIT_LIST_HEAD ( & trans - > qgroup_ref_list ) ;
trans - > type = __TRANS_DUMMY ;
}
static int insert_normal_tree_ref ( struct btrfs_root * root , u64 bytenr ,
u64 num_bytes , u64 parent , u64 root_objectid )
{
struct btrfs_trans_handle trans ;
struct btrfs_extent_item * item ;
struct btrfs_extent_inline_ref * iref ;
struct btrfs_tree_block_info * block_info ;
struct btrfs_path * path ;
struct extent_buffer * leaf ;
struct btrfs_key ins ;
u32 size = sizeof ( * item ) + sizeof ( * iref ) + sizeof ( * block_info ) ;
int ret ;
init_dummy_trans ( & trans ) ;
ins . objectid = bytenr ;
ins . type = BTRFS_EXTENT_ITEM_KEY ;
ins . offset = num_bytes ;
path = btrfs_alloc_path ( ) ;
if ( ! path ) {
test_msg ( " Couldn't allocate path \n " ) ;
return - ENOMEM ;
}
path - > leave_spinning = 1 ;
ret = btrfs_insert_empty_item ( & trans , root , path , & ins , size ) ;
if ( ret ) {
test_msg ( " Couldn't insert ref %d \n " , ret ) ;
btrfs_free_path ( path ) ;
return ret ;
}
leaf = path - > nodes [ 0 ] ;
item = btrfs_item_ptr ( leaf , path - > slots [ 0 ] , struct btrfs_extent_item ) ;
btrfs_set_extent_refs ( leaf , item , 1 ) ;
btrfs_set_extent_generation ( leaf , item , 1 ) ;
btrfs_set_extent_flags ( leaf , item , BTRFS_EXTENT_FLAG_TREE_BLOCK ) ;
block_info = ( struct btrfs_tree_block_info * ) ( item + 1 ) ;
btrfs_set_tree_block_level ( leaf , block_info , 1 ) ;
iref = ( struct btrfs_extent_inline_ref * ) ( block_info + 1 ) ;
if ( parent > 0 ) {
btrfs_set_extent_inline_ref_type ( leaf , iref ,
BTRFS_SHARED_BLOCK_REF_KEY ) ;
btrfs_set_extent_inline_ref_offset ( leaf , iref , parent ) ;
} else {
btrfs_set_extent_inline_ref_type ( leaf , iref , BTRFS_TREE_BLOCK_REF_KEY ) ;
btrfs_set_extent_inline_ref_offset ( leaf , iref , root_objectid ) ;
}
btrfs_free_path ( path ) ;
return 0 ;
}
static int add_tree_ref ( struct btrfs_root * root , u64 bytenr , u64 num_bytes ,
u64 parent , u64 root_objectid )
{
struct btrfs_trans_handle trans ;
struct btrfs_extent_item * item ;
struct btrfs_path * path ;
struct btrfs_key key ;
u64 refs ;
int ret ;
init_dummy_trans ( & trans ) ;
key . objectid = bytenr ;
key . type = BTRFS_EXTENT_ITEM_KEY ;
key . offset = num_bytes ;
path = btrfs_alloc_path ( ) ;
if ( ! path ) {
test_msg ( " Couldn't allocate path \n " ) ;
return - ENOMEM ;
}
path - > leave_spinning = 1 ;
ret = btrfs_search_slot ( & trans , root , & key , path , 0 , 1 ) ;
if ( ret ) {
test_msg ( " Couldn't find extent ref \n " ) ;
btrfs_free_path ( path ) ;
return ret ;
}
item = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_item ) ;
refs = btrfs_extent_refs ( path - > nodes [ 0 ] , item ) ;
btrfs_set_extent_refs ( path - > nodes [ 0 ] , item , refs + 1 ) ;
btrfs_release_path ( path ) ;
key . objectid = bytenr ;
if ( parent ) {
key . type = BTRFS_SHARED_BLOCK_REF_KEY ;
key . offset = parent ;
} else {
key . type = BTRFS_TREE_BLOCK_REF_KEY ;
key . offset = root_objectid ;
}
ret = btrfs_insert_empty_item ( & trans , root , path , & key , 0 ) ;
if ( ret )
test_msg ( " Failed to insert backref \n " ) ;
btrfs_free_path ( path ) ;
return ret ;
}
static int remove_extent_item ( struct btrfs_root * root , u64 bytenr ,
u64 num_bytes )
{
struct btrfs_trans_handle trans ;
struct btrfs_key key ;
struct btrfs_path * path ;
int ret ;
init_dummy_trans ( & trans ) ;
key . objectid = bytenr ;
key . type = BTRFS_EXTENT_ITEM_KEY ;
key . offset = num_bytes ;
path = btrfs_alloc_path ( ) ;
if ( ! path ) {
test_msg ( " Couldn't allocate path \n " ) ;
return - ENOMEM ;
}
path - > leave_spinning = 1 ;
ret = btrfs_search_slot ( & trans , root , & key , path , - 1 , 1 ) ;
if ( ret ) {
test_msg ( " Didn't find our key %d \n " , ret ) ;
btrfs_free_path ( path ) ;
return ret ;
}
btrfs_del_item ( & trans , root , path ) ;
btrfs_free_path ( path ) ;
return 0 ;
}
static int remove_extent_ref ( struct btrfs_root * root , u64 bytenr ,
u64 num_bytes , u64 parent , u64 root_objectid )
{
struct btrfs_trans_handle trans ;
struct btrfs_extent_item * item ;
struct btrfs_path * path ;
struct btrfs_key key ;
u64 refs ;
int ret ;
init_dummy_trans ( & trans ) ;
key . objectid = bytenr ;
key . type = BTRFS_EXTENT_ITEM_KEY ;
key . offset = num_bytes ;
path = btrfs_alloc_path ( ) ;
if ( ! path ) {
test_msg ( " Couldn't allocate path \n " ) ;
return - ENOMEM ;
}
path - > leave_spinning = 1 ;
ret = btrfs_search_slot ( & trans , root , & key , path , 0 , 1 ) ;
if ( ret ) {
test_msg ( " Couldn't find extent ref \n " ) ;
btrfs_free_path ( path ) ;
return ret ;
}
item = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_extent_item ) ;
refs = btrfs_extent_refs ( path - > nodes [ 0 ] , item ) ;
btrfs_set_extent_refs ( path - > nodes [ 0 ] , item , refs - 1 ) ;
btrfs_release_path ( path ) ;
key . objectid = bytenr ;
if ( parent ) {
key . type = BTRFS_SHARED_BLOCK_REF_KEY ;
key . offset = parent ;
} else {
key . type = BTRFS_TREE_BLOCK_REF_KEY ;
key . offset = root_objectid ;
}
ret = btrfs_search_slot ( & trans , root , & key , path , - 1 , 1 ) ;
if ( ret ) {
test_msg ( " Couldn't find backref %d \n " , ret ) ;
btrfs_free_path ( path ) ;
return ret ;
}
btrfs_del_item ( & trans , root , path ) ;
btrfs_free_path ( path ) ;
return ret ;
}
static int test_no_shared_qgroup ( struct btrfs_root * root )
{
struct btrfs_trans_handle trans ;
struct btrfs_fs_info * fs_info = root - > fs_info ;
int ret ;
init_dummy_trans ( & trans ) ;
test_msg ( " Qgroup basic add \n " ) ;
ret = btrfs_create_qgroup ( NULL , fs_info , 5 , NULL ) ;
if ( ret ) {
test_msg ( " Couldn't create a qgroup %d \n " , ret ) ;
return ret ;
}
ret = btrfs_qgroup_record_ref ( & trans , fs_info , 5 , 4096 , 4096 ,
BTRFS_QGROUP_OPER_ADD_EXCL , 0 ) ;
if ( ret ) {
test_msg ( " Couldn't add space to a qgroup %d \n " , ret ) ;
return ret ;
}
ret = insert_normal_tree_ref ( root , 4096 , 4096 , 0 , 5 ) ;
if ( ret )
return ret ;
ret = btrfs_delayed_qgroup_accounting ( & trans , fs_info ) ;
if ( ret ) {
test_msg ( " Delayed qgroup accounting failed %d \n " , ret ) ;
return ret ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 5 , 4096 , 4096 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
ret = remove_extent_item ( root , 4096 , 4096 ) ;
if ( ret )
return - EINVAL ;
ret = btrfs_qgroup_record_ref ( & trans , fs_info , 5 , 4096 , 4096 ,
BTRFS_QGROUP_OPER_SUB_EXCL , 0 ) ;
if ( ret ) {
test_msg ( " Couldn't remove space from the qgroup %d \n " , ret ) ;
return - EINVAL ;
}
ret = btrfs_delayed_qgroup_accounting ( & trans , fs_info ) ;
if ( ret ) {
test_msg ( " Qgroup accounting failed %d \n " , ret ) ;
return - EINVAL ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 5 , 0 , 0 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
return 0 ;
}
/*
* Add a ref for two different roots to make sure the shared value comes out
* right , also remove one of the roots and make sure the exclusive count is
* adjusted properly .
*/
static int test_multiple_refs ( struct btrfs_root * root )
{
struct btrfs_trans_handle trans ;
struct btrfs_fs_info * fs_info = root - > fs_info ;
int ret ;
init_dummy_trans ( & trans ) ;
test_msg ( " Qgroup multiple refs test \n " ) ;
/* We have 5 created already from the previous test */
ret = btrfs_create_qgroup ( NULL , fs_info , 256 , NULL ) ;
if ( ret ) {
test_msg ( " Couldn't create a qgroup %d \n " , ret ) ;
return ret ;
}
ret = insert_normal_tree_ref ( root , 4096 , 4096 , 0 , 5 ) ;
if ( ret )
return ret ;
ret = btrfs_qgroup_record_ref ( & trans , fs_info , 5 , 4096 , 4096 ,
BTRFS_QGROUP_OPER_ADD_EXCL , 0 ) ;
if ( ret ) {
test_msg ( " Couldn't add space to a qgroup %d \n " , ret ) ;
return ret ;
}
ret = btrfs_delayed_qgroup_accounting ( & trans , fs_info ) ;
if ( ret ) {
test_msg ( " Delayed qgroup accounting failed %d \n " , ret ) ;
return ret ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 5 , 4096 , 4096 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
ret = add_tree_ref ( root , 4096 , 4096 , 0 , 256 ) ;
if ( ret )
return ret ;
ret = btrfs_qgroup_record_ref ( & trans , fs_info , 256 , 4096 , 4096 ,
BTRFS_QGROUP_OPER_ADD_SHARED , 0 ) ;
if ( ret ) {
test_msg ( " Qgroup record ref failed %d \n " , ret ) ;
return ret ;
}
ret = btrfs_delayed_qgroup_accounting ( & trans , fs_info ) ;
if ( ret ) {
test_msg ( " Qgroup accounting failed %d \n " , ret ) ;
return ret ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 5 , 4096 , 0 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 256 , 4096 , 0 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
ret = remove_extent_ref ( root , 4096 , 4096 , 0 , 256 ) ;
if ( ret )
return ret ;
ret = btrfs_qgroup_record_ref ( & trans , fs_info , 256 , 4096 , 4096 ,
BTRFS_QGROUP_OPER_SUB_SHARED , 0 ) ;
if ( ret ) {
test_msg ( " Qgroup record ref failed %d \n " , ret ) ;
return ret ;
}
ret = btrfs_delayed_qgroup_accounting ( & trans , fs_info ) ;
if ( ret ) {
test_msg ( " Qgroup accounting failed %d \n " , ret ) ;
return ret ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 256 , 0 , 0 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
if ( btrfs_verify_qgroup_counts ( fs_info , 5 , 4096 , 4096 ) ) {
test_msg ( " Qgroup counts didn't match expected values \n " ) ;
return - EINVAL ;
}
return 0 ;
}
int btrfs_test_qgroups ( void )
{
struct btrfs_root * root ;
struct btrfs_root * tmp_root ;
int ret = 0 ;
root = btrfs_alloc_dummy_root ( ) ;
if ( IS_ERR ( root ) ) {
test_msg ( " Couldn't allocate root \n " ) ;
return PTR_ERR ( root ) ;
}
root - > fs_info = btrfs_alloc_dummy_fs_info ( ) ;
if ( ! root - > fs_info ) {
test_msg ( " Couldn't allocate dummy fs info \n " ) ;
ret = - ENOMEM ;
goto out ;
}
/*
* Can ' t use bytenr 0 , some things freak out
* * cough * backref walking code * cough *
*/
root - > node = alloc_test_extent_buffer ( root - > fs_info , 4096 , 4096 ) ;
if ( ! root - > node ) {
test_msg ( " Couldn't allocate dummy buffer \n " ) ;
ret = - ENOMEM ;
goto out ;
}
2014-06-12 02:47:37 +01:00
btrfs_set_header_level ( root - > node , 0 ) ;
btrfs_set_header_nritems ( root - > node , 0 ) ;
2014-05-07 17:06:09 -04:00
root - > alloc_bytenr + = 8192 ;
tmp_root = btrfs_alloc_dummy_root ( ) ;
if ( IS_ERR ( tmp_root ) ) {
test_msg ( " Couldn't allocate a fs root \n " ) ;
ret = PTR_ERR ( tmp_root ) ;
goto out ;
}
tmp_root - > root_key . objectid = 5 ;
root - > fs_info - > fs_root = tmp_root ;
ret = btrfs_insert_fs_root ( root - > fs_info , tmp_root ) ;
if ( ret ) {
test_msg ( " Couldn't insert fs root %d \n " , ret ) ;
goto out ;
}
tmp_root = btrfs_alloc_dummy_root ( ) ;
if ( IS_ERR ( tmp_root ) ) {
test_msg ( " Couldn't allocate a fs root \n " ) ;
ret = PTR_ERR ( tmp_root ) ;
goto out ;
}
tmp_root - > root_key . objectid = 256 ;
ret = btrfs_insert_fs_root ( root - > fs_info , tmp_root ) ;
if ( ret ) {
test_msg ( " Couldn't insert fs root %d \n " , ret ) ;
goto out ;
}
/* We are using this root as our extent root */
root - > fs_info - > extent_root = root ;
/*
* Some of the paths we test assume we have a filled out fs_info , so we
* just need to addt he root in there so we don ' t panic .
*/
root - > fs_info - > tree_root = root ;
root - > fs_info - > quota_root = root ;
root - > fs_info - > quota_enabled = 1 ;
test_msg ( " Running qgroup tests \n " ) ;
ret = test_no_shared_qgroup ( root ) ;
if ( ret )
goto out ;
ret = test_multiple_refs ( root ) ;
out :
btrfs_free_dummy_root ( root ) ;
return ret ;
}