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#include "../backref.h"
  25
  26static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
  27                                  u64 num_bytes, u64 parent, u64 root_objectid)
  28{
  29        struct btrfs_trans_handle trans;
  30        struct btrfs_extent_item *item;
  31        struct btrfs_extent_inline_ref *iref;
  32        struct btrfs_tree_block_info *block_info;
  33        struct btrfs_path *path;
  34        struct extent_buffer *leaf;
  35        struct btrfs_key ins;
  36        u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info);
  37        int ret;
  38
  39        btrfs_init_dummy_trans(&trans);
  40
  41        ins.objectid = bytenr;
  42        ins.type = BTRFS_EXTENT_ITEM_KEY;
  43        ins.offset = num_bytes;
  44
  45        path = btrfs_alloc_path();
  46        if (!path) {
  47                test_msg("Couldn't allocate path\n");
  48                return -ENOMEM;
  49        }
  50
  51        path->leave_spinning = 1;
  52        ret = btrfs_insert_empty_item(&trans, root, path, &ins, size);
  53        if (ret) {
  54                test_msg("Couldn't insert ref %d\n", ret);
  55                btrfs_free_path(path);
  56                return ret;
  57        }
  58
  59        leaf = path->nodes[0];
  60        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
  61        btrfs_set_extent_refs(leaf, item, 1);
  62        btrfs_set_extent_generation(leaf, item, 1);
  63        btrfs_set_extent_flags(leaf, item, BTRFS_EXTENT_FLAG_TREE_BLOCK);
  64        block_info = (struct btrfs_tree_block_info *)(item + 1);
  65        btrfs_set_tree_block_level(leaf, block_info, 1);
  66        iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
  67        if (parent > 0) {
  68                btrfs_set_extent_inline_ref_type(leaf, iref,
  69                                                 BTRFS_SHARED_BLOCK_REF_KEY);
  70                btrfs_set_extent_inline_ref_offset(leaf, iref, parent);
  71        } else {
  72                btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY);
  73                btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
  74        }
  75        btrfs_free_path(path);
  76        return 0;
  77}
  78
  79static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
  80                        u64 parent, u64 root_objectid)
  81{
  82        struct btrfs_trans_handle trans;
  83        struct btrfs_extent_item *item;
  84        struct btrfs_path *path;
  85        struct btrfs_key key;
  86        u64 refs;
  87        int ret;
  88
  89        btrfs_init_dummy_trans(&trans);
  90
  91        key.objectid = bytenr;
  92        key.type = BTRFS_EXTENT_ITEM_KEY;
  93        key.offset = num_bytes;
  94
  95        path = btrfs_alloc_path();
  96        if (!path) {
  97                test_msg("Couldn't allocate path\n");
  98                return -ENOMEM;
  99        }
 100
 101        path->leave_spinning = 1;
 102        ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
 103        if (ret) {
 104                test_msg("Couldn't find extent ref\n");
 105                btrfs_free_path(path);
 106                return ret;
 107        }
 108
 109        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
 110                              struct btrfs_extent_item);
 111        refs = btrfs_extent_refs(path->nodes[0], item);
 112        btrfs_set_extent_refs(path->nodes[0], item, refs + 1);
 113        btrfs_release_path(path);
 114
 115        key.objectid = bytenr;
 116        if (parent) {
 117                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
 118                key.offset = parent;
 119        } else {
 120                key.type = BTRFS_TREE_BLOCK_REF_KEY;
 121                key.offset = root_objectid;
 122        }
 123
 124        ret = btrfs_insert_empty_item(&trans, root, path, &key, 0);
 125        if (ret)
 126                test_msg("Failed to insert backref\n");
 127        btrfs_free_path(path);
 128        return ret;
 129}
 130
 131static int remove_extent_item(struct btrfs_root *root, u64 bytenr,
 132                              u64 num_bytes)
 133{
 134        struct btrfs_trans_handle trans;
 135        struct btrfs_key key;
 136        struct btrfs_path *path;
 137        int ret;
 138
 139        btrfs_init_dummy_trans(&trans);
 140
 141        key.objectid = bytenr;
 142        key.type = BTRFS_EXTENT_ITEM_KEY;
 143        key.offset = num_bytes;
 144
 145        path = btrfs_alloc_path();
 146        if (!path) {
 147                test_msg("Couldn't allocate path\n");
 148                return -ENOMEM;
 149        }
 150        path->leave_spinning = 1;
 151
 152        ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
 153        if (ret) {
 154                test_msg("Didn't find our key %d\n", ret);
 155                btrfs_free_path(path);
 156                return ret;
 157        }
 158        btrfs_del_item(&trans, root, path);
 159        btrfs_free_path(path);
 160        return 0;
 161}
 162
 163static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
 164                             u64 num_bytes, u64 parent, u64 root_objectid)
 165{
 166        struct btrfs_trans_handle trans;
 167        struct btrfs_extent_item *item;
 168        struct btrfs_path *path;
 169        struct btrfs_key key;
 170        u64 refs;
 171        int ret;
 172
 173        btrfs_init_dummy_trans(&trans);
 174
 175        key.objectid = bytenr;
 176        key.type = BTRFS_EXTENT_ITEM_KEY;
 177        key.offset = num_bytes;
 178
 179        path = btrfs_alloc_path();
 180        if (!path) {
 181                test_msg("Couldn't allocate path\n");
 182                return -ENOMEM;
 183        }
 184
 185        path->leave_spinning = 1;
 186        ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
 187        if (ret) {
 188                test_msg("Couldn't find extent ref\n");
 189                btrfs_free_path(path);
 190                return ret;
 191        }
 192
 193        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
 194                              struct btrfs_extent_item);
 195        refs = btrfs_extent_refs(path->nodes[0], item);
 196        btrfs_set_extent_refs(path->nodes[0], item, refs - 1);
 197        btrfs_release_path(path);
 198
 199        key.objectid = bytenr;
 200        if (parent) {
 201                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
 202                key.offset = parent;
 203        } else {
 204                key.type = BTRFS_TREE_BLOCK_REF_KEY;
 205                key.offset = root_objectid;
 206        }
 207
 208        ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
 209        if (ret) {
 210                test_msg("Couldn't find backref %d\n", ret);
 211                btrfs_free_path(path);
 212                return ret;
 213        }
 214        btrfs_del_item(&trans, root, path);
 215        btrfs_free_path(path);
 216        return ret;
 217}
 218
 219static int test_no_shared_qgroup(struct btrfs_root *root)
 220{
 221        struct btrfs_trans_handle trans;
 222        struct btrfs_fs_info *fs_info = root->fs_info;
 223        struct ulist *old_roots = NULL;
 224        struct ulist *new_roots = NULL;
 225        int ret;
 226
 227        btrfs_init_dummy_trans(&trans);
 228
 229        test_msg("Qgroup basic add\n");
 230        ret = btrfs_create_qgroup(NULL, fs_info, 5);
 231        if (ret) {
 232                test_msg("Couldn't create a qgroup %d\n", ret);
 233                return ret;
 234        }
 235
 236        /*
 237         * Since the test trans doesn't havee the complicated delayed refs,
 238         * we can only call btrfs_qgroup_account_extent() directly to test
 239         * quota.
 240         */
 241        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots);
 242        if (ret) {
 243                ulist_free(old_roots);
 244                test_msg("Couldn't find old roots: %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_find_all_roots(&trans, fs_info, 4096, 0, &new_roots);
 253        if (ret) {
 254                ulist_free(old_roots);
 255                ulist_free(new_roots);
 256                test_msg("Couldn't find old roots: %d\n", ret);
 257                return ret;
 258        }
 259
 260        ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096,
 261                                          old_roots, new_roots);
 262        if (ret) {
 263                test_msg("Couldn't account space for a qgroup %d\n", ret);
 264                return ret;
 265        }
 266
 267        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 268                test_msg("Qgroup counts didn't match expected values\n");
 269                return -EINVAL;
 270        }
 271        old_roots = NULL;
 272        new_roots = NULL;
 273
 274        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots);
 275        if (ret) {
 276                ulist_free(old_roots);
 277                test_msg("Couldn't find old roots: %d\n", ret);
 278                return ret;
 279        }
 280
 281        ret = remove_extent_item(root, 4096, 4096);
 282        if (ret)
 283                return -EINVAL;
 284
 285        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots);
 286        if (ret) {
 287                ulist_free(old_roots);
 288                ulist_free(new_roots);
 289                test_msg("Couldn't find old roots: %d\n", ret);
 290                return ret;
 291        }
 292
 293        ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096,
 294                                          old_roots, new_roots);
 295        if (ret) {
 296                test_msg("Couldn't account space for a qgroup %d\n", ret);
 297                return -EINVAL;
 298        }
 299
 300        if (btrfs_verify_qgroup_counts(fs_info, 5, 0, 0)) {
 301                test_msg("Qgroup counts didn't match expected values\n");
 302                return -EINVAL;
 303        }
 304
 305        return 0;
 306}
 307
 308/*
 309 * Add a ref for two different roots to make sure the shared value comes out
 310 * right, also remove one of the roots and make sure the exclusive count is
 311 * adjusted properly.
 312 */
 313static int test_multiple_refs(struct btrfs_root *root)
 314{
 315        struct btrfs_trans_handle trans;
 316        struct btrfs_fs_info *fs_info = root->fs_info;
 317        struct ulist *old_roots = NULL;
 318        struct ulist *new_roots = NULL;
 319        int ret;
 320
 321        btrfs_init_dummy_trans(&trans);
 322
 323        test_msg("Qgroup multiple refs test\n");
 324
 325        /* We have 5 created already from the previous test */
 326        ret = btrfs_create_qgroup(NULL, fs_info, 256);
 327        if (ret) {
 328                test_msg("Couldn't create a qgroup %d\n", ret);
 329                return ret;
 330        }
 331
 332        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots);
 333        if (ret) {
 334                ulist_free(old_roots);
 335                test_msg("Couldn't find old roots: %d\n", ret);
 336                return ret;
 337        }
 338
 339        ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5);
 340        if (ret)
 341                return ret;
 342
 343        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots);
 344        if (ret) {
 345                ulist_free(old_roots);
 346                ulist_free(new_roots);
 347                test_msg("Couldn't find old roots: %d\n", ret);
 348                return ret;
 349        }
 350
 351        ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096,
 352                                          old_roots, new_roots);
 353        if (ret) {
 354                test_msg("Couldn't account space for a qgroup %d\n", ret);
 355                return ret;
 356        }
 357
 358        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 359                test_msg("Qgroup counts didn't match expected values\n");
 360                return -EINVAL;
 361        }
 362
 363        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots);
 364        if (ret) {
 365                ulist_free(old_roots);
 366                test_msg("Couldn't find old roots: %d\n", ret);
 367                return ret;
 368        }
 369
 370        ret = add_tree_ref(root, 4096, 4096, 0, 256);
 371        if (ret)
 372                return ret;
 373
 374        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots);
 375        if (ret) {
 376                ulist_free(old_roots);
 377                ulist_free(new_roots);
 378                test_msg("Couldn't find old roots: %d\n", ret);
 379                return ret;
 380        }
 381
 382        ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096,
 383                                          old_roots, new_roots);
 384        if (ret) {
 385                test_msg("Couldn't account space for a qgroup %d\n", ret);
 386                return ret;
 387        }
 388
 389        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 0)) {
 390                test_msg("Qgroup counts didn't match expected values\n");
 391                return -EINVAL;
 392        }
 393
 394        if (btrfs_verify_qgroup_counts(fs_info, 256, 4096, 0)) {
 395                test_msg("Qgroup counts didn't match expected values\n");
 396                return -EINVAL;
 397        }
 398
 399        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots);
 400        if (ret) {
 401                ulist_free(old_roots);
 402                test_msg("Couldn't find old roots: %d\n", ret);
 403                return ret;
 404        }
 405
 406        ret = remove_extent_ref(root, 4096, 4096, 0, 256);
 407        if (ret)
 408                return ret;
 409
 410        ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots);
 411        if (ret) {
 412                ulist_free(old_roots);
 413                ulist_free(new_roots);
 414                test_msg("Couldn't find old roots: %d\n", ret);
 415                return ret;
 416        }
 417
 418        ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096,
 419                                          old_roots, new_roots);
 420        if (ret) {
 421                test_msg("Couldn't account space for a qgroup %d\n", ret);
 422                return ret;
 423        }
 424
 425        if (btrfs_verify_qgroup_counts(fs_info, 256, 0, 0)) {
 426                test_msg("Qgroup counts didn't match expected values\n");
 427                return -EINVAL;
 428        }
 429
 430        if (btrfs_verify_qgroup_counts(fs_info, 5, 4096, 4096)) {
 431                test_msg("Qgroup counts didn't match expected values\n");
 432                return -EINVAL;
 433        }
 434
 435        return 0;
 436}
 437
 438int btrfs_test_qgroups(void)
 439{
 440        struct btrfs_root *root;
 441        struct btrfs_root *tmp_root;
 442        int ret = 0;
 443
 444        root = btrfs_alloc_dummy_root();
 445        if (IS_ERR(root)) {
 446                test_msg("Couldn't allocate root\n");
 447                return PTR_ERR(root);
 448        }
 449
 450        root->fs_info = btrfs_alloc_dummy_fs_info();
 451        if (!root->fs_info) {
 452                test_msg("Couldn't allocate dummy fs info\n");
 453                ret = -ENOMEM;
 454                goto out;
 455        }
 456        /* We are using this root as our extent root */
 457        root->fs_info->extent_root = root;
 458
 459        /*
 460         * Some of the paths we test assume we have a filled out fs_info, so we
 461         * just need to add the root in there so we don't panic.
 462         */
 463        root->fs_info->tree_root = root;
 464        root->fs_info->quota_root = root;
 465        root->fs_info->quota_enabled = 1;
 466
 467        /*
 468         * Can't use bytenr 0, some things freak out
 469         * *cough*backref walking code*cough*
 470         */
 471        root->node = alloc_test_extent_buffer(root->fs_info, 4096);
 472        if (!root->node) {
 473                test_msg("Couldn't allocate dummy buffer\n");
 474                ret = -ENOMEM;
 475                goto out;
 476        }
 477        btrfs_set_header_level(root->node, 0);
 478        btrfs_set_header_nritems(root->node, 0);
 479        root->alloc_bytenr += 8192;
 480
 481        tmp_root = btrfs_alloc_dummy_root();
 482        if (IS_ERR(tmp_root)) {
 483                test_msg("Couldn't allocate a fs root\n");
 484                ret = PTR_ERR(tmp_root);
 485                goto out;
 486        }
 487
 488        tmp_root->root_key.objectid = 5;
 489        root->fs_info->fs_root = tmp_root;
 490        ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
 491        if (ret) {
 492                test_msg("Couldn't insert fs root %d\n", ret);
 493                goto out;
 494        }
 495
 496        tmp_root = btrfs_alloc_dummy_root();
 497        if (IS_ERR(tmp_root)) {
 498                test_msg("Couldn't allocate a fs root\n");
 499                ret = PTR_ERR(tmp_root);
 500                goto out;
 501        }
 502
 503        tmp_root->root_key.objectid = 256;
 504        ret = btrfs_insert_fs_root(root->fs_info, tmp_root);
 505        if (ret) {
 506                test_msg("Couldn't insert fs root %d\n", ret);
 507                goto out;
 508        }
 509
 510        test_msg("Running qgroup tests\n");
 511        ret = test_no_shared_qgroup(root);
 512        if (ret)
 513                goto out;
 514        ret = test_multiple_refs(root);
 515out:
 516        btrfs_free_dummy_root(root);
 517        return ret;
 518}
 519