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