linux/fs/btrfs/tests/qgroup-tests.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Facebook.  All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public
   6 * License v2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11 * General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public
  14 * License along with this program; if not, write to the
  15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  16 * Boston, MA 021110-1307, USA.
  17 */
  18
  19#include "btrfs-tests.h"
  20#include "../ctree.h"
  21#include "../transaction.h"
  22#include "../disk-io.h"
  23#include "../qgroup.h"
  24
  25static void init_dummy_trans(struct btrfs_trans_handle *trans)
  26{
  27        memset(trans, 0, sizeof(*trans));
  28        trans->transid = 1;
  29        INIT_LIST_HEAD(&trans->qgroup_ref_list);
  30        trans->type = __TRANS_DUMMY;
  31}
  32
  33static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
  34                                  u64 num_bytes, u64 parent, u64 root_objectid)
  35{
  36        struct btrfs_trans_handle trans;
  37        struct btrfs_extent_item *item;
  38        struct btrfs_extent_inline_ref *iref;
  39        struct btrfs_tree_block_info *block_info;
  40        struct btrfs_path *path;
  41        struct extent_buffer *leaf;
  42        struct btrfs_key ins;
  43        u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info);
  44        int ret;
  45
  46        init_dummy_trans(&trans);
  47
  48        ins.objectid = bytenr;
  49        ins.type = BTRFS_EXTENT_ITEM_KEY;
  50        ins.offset = num_bytes;
  51
  52        path = btrfs_alloc_path();
  53        if (!path) {
  54                test_msg("Couldn't allocate path\n");
  55                return -ENOMEM;
  56        }
  57
  58        path->leave_spinning = 1;
  59        ret = btrfs_insert_empty_item(&trans, root, path, &ins, size);
  60        if (ret) {
  61                test_msg("Couldn't insert ref %d\n", ret);
  62                btrfs_free_path(path);
  63                return ret;
  64        }
  65
  66        leaf = path->nodes[0];
  67        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
  68        btrfs_set_extent_refs(leaf, item, 1);
  69        btrfs_set_extent_generation(leaf, item, 1);
  70        btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK);
  71        block_info = (struct btrfs_tree_block_info *)(item + 1);
  72        btrfs_set_tree_block_level(leaf, block_info, 1);
  73        iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
  74        if (parent > 0) {
  75                btrfs_set_extent_inline_ref_type(leaf, iref,
  76                                                 BTRFS_SHARED_BLOCK_REF_KEY);
  77                btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
  78        } else {
  79                btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY);
  80                btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
  81        }
  82        btrfs_free_path(path);
  83        return 0;
  84}
  85
  86static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
  87                        u64 parent, u64 root_objectid)
  88{
  89        struct btrfs_trans_handle trans;
  90        struct btrfs_extent_item *item;
  91        struct btrfs_path *path;
  92        struct btrfs_key key;
  93        u64 refs;
  94        int ret;
  95
  96        init_dummy_trans(&trans);
  97
  98        key.objectid = bytenr;
  99        key.type = BTRFS_EXTENT_ITEM_KEY;
 100        key.offset = num_bytes;
 101
 102        path = btrfs_alloc_path();
 103        if (!path) {
 104                test_msg("Couldn't allocate path\n");
 105                return -ENOMEM;
 106        }
 107
 108        path->leave_spinning = 1;
 109        ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
 110        if (ret) {
 111                test_msg("Couldn't find extent ref\n");
 112                btrfs_free_path(path);
 113                return ret;
 114        }
 115
 116        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
 117                              struct btrfs_extent_item);
 118        refs = btrfs_extent_refs(path->nodes[0], item);
 119        btrfs_set_extent_refs(path->nodes[0], item, refs + 1);
 120        btrfs_release_path(path);
 121
 122        key.objectid = bytenr;
 123        if (parent) {
 124                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
 125                key.offset = parent;
 126        } else {
 127                key.type = BTRFS_TREE_BLOCK_REF_KEY;
 128                key.offset = root_objectid;
 129        }
 130
 131        ret = btrfs_insert_empty_item(&trans, root, path, &key, 0);
 132        if (ret)
 133                test_msg("Failed to insert backref\n");
 134        btrfs_free_path(path);
 135        return ret;
 136}
 137
 138static int remove_extent_item(struct btrfs_root *root, u64 bytenr,
 139                              u64 num_bytes)
 140{
 141        struct btrfs_trans_handle trans;
 142        struct btrfs_key key;
 143        struct btrfs_path *path;
 144        int ret;
 145
 146        init_dummy_trans(&trans);
 147
 148        key.objectid = bytenr;
 149        key.type = BTRFS_EXTENT_ITEM_KEY;
 150        key.offset = num_bytes;
 151
 152        path = btrfs_alloc_path();
 153        if (!path) {
 154                test_msg("Couldn't allocate path\n");
 155                return -ENOMEM;
 156        }
 157        path->leave_spinning = 1;
 158
 159        ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
 160        if (ret) {
 161                test_msg("Didn't find our key %d\n", ret);
 162                btrfs_free_path(path);
 163                return ret;
 164        }
 165        btrfs_del_item(&trans, root, path);
 166        btrfs_free_path(path);
 167        return 0;
 168}
 169
 170static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
 171                             u64 num_bytes, u64 parent, u64 root_objectid)
 172{
 173        struct btrfs_trans_handle trans;
 174        struct btrfs_extent_item *item;
 175        struct btrfs_path *path;
 176        struct btrfs_key key;
 177        u64 refs;
 178        int ret;
 179
 180        init_dummy_trans(&trans);
 181
 182        key.objectid = bytenr;
 183        key.type = BTRFS_EXTENT_ITEM_KEY;
 184        key.offset = num_bytes;
 185
 186        path = btrfs_alloc_path();
 187        if (!path) {
 188                test_msg("Couldn't allocate path\n");
 189                return -ENOMEM;
 190        }
 191
 192        path->leave_spinning = 1;
 193        ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
 194        if (ret) {
 195                test_msg("Couldn't find extent ref\n");
 196                btrfs_free_path(path);
 197                return ret;
 198        }
 199
 200        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
 201                              struct btrfs_extent_item);
 202        refs = btrfs_extent_refs(path->nodes[0], item);
 203        btrfs_set_extent_refs(path->nodes[0], item, refs - 1);
 204        btrfs_release_path(path);
 205
 206        key.objectid = bytenr;
 207        if (parent) {
 208                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
 209                key.offset = parent;
 210        } else {
 211                key.type = BTRFS_TREE_BLOCK_REF_KEY;
 212                key.offset = root_objectid;
 213        }
 214
 215        ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
 216        if (ret) {
 217                test_msg("Couldn't find backref %d\n", ret);
 218                btrfs_free_path(path);
 219                return ret;
 220        }
 221        btrfs_del_item(&trans, root, path);
 222        btrfs_free_path(path);
 223        return ret;
 224}
 225
 226static int test_no_shared_qgroup(struct btrfs_root *root)
 227{
 228        struct btrfs_trans_handle trans;
 229        struct btrfs_fs_info *fs_info = root->fs_info;
 230        int ret;
 231
 232        init_dummy_trans(&trans);
 233
 234        test_msg("Qgroup basic add\n");
 235        ret = btrfs_create_qgroup(NULL, fs_info, 5, NULL);
 236        if (ret) {
 237                test_msg("Couldn't create a qgroup %d\n", ret);
 238                return ret;
 239        }
 240
 241        ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096,
 242                                      BTRFS_QGROUP_OPER_ADD_EXCL, 0);
 243        if (ret) {
 244                test_msg("Couldn't add space to a qgroup %d\n", ret);
 245                return ret;
 246        }
 247
 248        ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5);
 249        if (ret)
 250                return ret;
 251
 252        ret = btrfs_delayed_qgroup_accounting(&trans, fs_info);
 253        if (ret) {
 254                test_msg("Delayed qgroup accounting failed %d\n", ret);
 255                return ret;
 256        }
 257
 258        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 259                test_msg("Qgroup counts didn't match expected values\n");
 260                return -EINVAL;
 261        }
 262
 263        ret = remove_extent_item(root, 4096, 4096);
 264        if (ret)
 265                return -EINVAL;
 266
 267        ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096,
 268                                      BTRFS_QGROUP_OPER_SUB_EXCL, 0);
 269        if (ret) {
 270                test_msg("Couldn't remove space from the qgroup %d\n", ret);
 271                return -EINVAL;
 272        }
 273
 274        ret = btrfs_delayed_qgroup_accounting(&trans, fs_info);
 275        if (ret) {
 276                test_msg("Qgroup accounting failed %d\n", ret);
 277                return -EINVAL;
 278        }
 279
 280        if (btrfs_verify_qgroup_counts(fs_info, 5, 0, 0)) {
 281                test_msg("Qgroup counts didn't match expected values\n");
 282                return -EINVAL;
 283        }
 284
 285        return 0;
 286}
 287
 288/*
 289 * Add a ref for two different roots to make sure the shared value comes out
 290 * right, also remove one of the roots and make sure the exclusive count is
 291 * adjusted properly.
 292 */
 293static int test_multiple_refs(struct btrfs_root *root)
 294{
 295        struct btrfs_trans_handle trans;
 296        struct btrfs_fs_info *fs_info = root->fs_info;
 297        int ret;
 298
 299        init_dummy_trans(&trans);
 300
 301        test_msg("Qgroup multiple refs test\n");
 302
 303        /* We have 5 created already from the previous test */
 304        ret = btrfs_create_qgroup(NULL, fs_info, 256, NULL);
 305        if (ret) {
 306                test_msg("Couldn't create a qgroup %d\n", ret);
 307                return ret;
 308        }
 309
 310        ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5);
 311        if (ret)
 312                return ret;
 313
 314        ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096,
 315                                      BTRFS_QGROUP_OPER_ADD_EXCL, 0);
 316        if (ret) {
 317                test_msg("Couldn't add space to a qgroup %d\n", ret);
 318                return ret;
 319        }
 320
 321        ret = btrfs_delayed_qgroup_accounting(&trans, fs_info);
 322        if (ret) {
 323                test_msg("Delayed qgroup accounting failed %d\n", ret);
 324                return ret;
 325        }
 326
 327        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 328                test_msg("Qgroup counts didn't match expected values\n");
 329                return -EINVAL;
 330        }
 331
 332        ret = add_tree_ref(root, 4096, 4096, 0, 256);
 333        if (ret)
 334                return ret;
 335
 336        ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096,
 337                                      BTRFS_QGROUP_OPER_ADD_SHARED, 0);
 338        if (ret) {
 339                test_msg("Qgroup record ref failed %d\n", ret);
 340                return ret;
 341        }
 342
 343        ret = btrfs_delayed_qgroup_accounting(&trans, fs_info);
 344        if (ret) {
 345                test_msg("Qgroup accounting failed %d\n", ret);
 346                return ret;
 347        }
 348
 349        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 0)) {
 350                test_msg("Qgroup counts didn't match expected values\n");
 351                return -EINVAL;
 352        }
 353
 354        if (btrfs_verify_qgroup_counts(fs_info, 256, 4096, 0)) {
 355                test_msg("Qgroup counts didn't match expected values\n");
 356                return -EINVAL;
 357        }
 358
 359        ret = remove_extent_ref(root, 4096, 4096, 0, 256);
 360        if (ret)
 361                return ret;
 362
 363        ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096,
 364                                      BTRFS_QGROUP_OPER_SUB_SHARED, 0);
 365        if (ret) {
 366                test_msg("Qgroup record ref failed %d\n", ret);
 367                return ret;
 368        }
 369
 370        ret = btrfs_delayed_qgroup_accounting(&trans, fs_info);
 371        if (ret) {
 372                test_msg("Qgroup accounting failed %d\n", ret);
 373                return ret;
 374        }
 375
 376        if (btrfs_verify_qgroup_counts(fs_info, 256, 0, 0)) {
 377                test_msg("Qgroup counts didn't match expected values\n");
 378                return -EINVAL;
 379        }
 380
 381        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 382                test_msg("Qgroup counts didn't match expected values\n");
 383                return -EINVAL;
 384        }
 385
 386        return 0;
 387}
 388
 389int btrfs_test_qgroups(void)
 390{
 391        struct btrfs_root *root;
 392        struct btrfs_root *tmp_root;
 393        int ret = 0;
 394
 395        root = btrfs_alloc_dummy_root();
 396        if (IS_ERR(root)) {
 397                test_msg("Couldn't allocate root\n");
 398                return PTR_ERR(root);
 399        }
 400
 401        root->fs_info = btrfs_alloc_dummy_fs_info();
 402        if (!root->fs_info) {
 403                test_msg("Couldn't allocate dummy fs info\n");
 404                ret = -ENOMEM;
 405                goto out;
 406        }
 407        /* We are using this root as our extent root */
 408        root->fs_info->extent_root = root;
 409
 410        /*
 411         * Some of the paths we test assume we have a filled out fs_info, so we
 412         * just need to add the root in there so we don't panic.
 413         */
 414        root->fs_info->tree_root = root;
 415        root->fs_info->quota_root = root;
 416        root->fs_info->quota_enabled = 1;
 417
 418        /*
 419         * Can't use bytenr 0, some things freak out
 420         * *cough*backref walking code*cough*
 421         */
 422        root->node = alloc_test_extent_buffer(root->fs_info, 4096);
 423        if (!root->node) {
 424                test_msg("Couldn't allocate dummy buffer\n");
 425                ret = -ENOMEM;
 426                goto out;
 427        }
 428        btrfs_set_header_level(root->node, 0);
 429        btrfs_set_header_nritems(root->node, 0);
 430        root->alloc_bytenr += 8192;
 431
 432        tmp_root = btrfs_alloc_dummy_root();
 433        if (IS_ERR(tmp_root)) {
 434                test_msg("Couldn't allocate a fs root\n");
 435                ret = PTR_ERR(tmp_root);
 436                goto out;
 437        }
 438
 439        tmp_root->root_key.objectid = 5;
 440        root->fs_info->fs_root = tmp_root;
 441        ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
 442        if (ret) {
 443                test_msg("Couldn't insert fs root %d\n", ret);
 444                goto out;
 445        }
 446
 447        tmp_root = btrfs_alloc_dummy_root();
 448        if (IS_ERR(tmp_root)) {
 449                test_msg("Couldn't allocate a fs root\n");
 450                ret = PTR_ERR(tmp_root);
 451                goto out;
 452        }
 453
 454        tmp_root->root_key.objectid = 256;
 455        ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
 456        if (ret) {
 457                test_msg("Couldn't insert fs root %d\n", ret);
 458                goto out;
 459        }
 460
 461        test_msg("Running qgroup tests\n");
 462        ret = test_no_shared_qgroup(root);
 463        if (ret)
 464                goto out;
 465        ret = test_multiple_refs(root);
 466out:
 467        btrfs_free_dummy_root(root);
 468        return ret;
 469}
 470