172b28077SLiu Bo /* 272b28077SLiu Bo * Copyright (C) 2017 Oracle. All rights reserved. 372b28077SLiu Bo * 472b28077SLiu Bo * This program is free software; you can redistribute it and/or 572b28077SLiu Bo * modify it under the terms of the GNU General Public 672b28077SLiu Bo * License v2 as published by the Free Software Foundation. 772b28077SLiu Bo * 872b28077SLiu Bo * This program is distributed in the hope that it will be useful, 972b28077SLiu Bo * but WITHOUT ANY WARRANTY; without even the implied warranty of 1072b28077SLiu Bo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1172b28077SLiu Bo * General Public License for more details. 1272b28077SLiu Bo * 1372b28077SLiu Bo * You should have received a copy of the GNU General Public 1472b28077SLiu Bo * License along with this program; if not, write to the 1572b28077SLiu Bo * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 1672b28077SLiu Bo * Boston, MA 021110-1307, USA. 1772b28077SLiu Bo */ 1872b28077SLiu Bo 1972b28077SLiu Bo #include <linux/types.h> 2072b28077SLiu Bo #include "btrfs-tests.h" 2172b28077SLiu Bo #include "../ctree.h" 2272b28077SLiu Bo 2372b28077SLiu Bo static void free_extent_map_tree(struct extent_map_tree *em_tree) 2472b28077SLiu Bo { 2572b28077SLiu Bo struct extent_map *em; 2672b28077SLiu Bo struct rb_node *node; 2772b28077SLiu Bo 2872b28077SLiu Bo while (!RB_EMPTY_ROOT(&em_tree->map)) { 2972b28077SLiu Bo node = rb_first(&em_tree->map); 3072b28077SLiu Bo em = rb_entry(node, struct extent_map, rb_node); 3172b28077SLiu Bo remove_extent_mapping(em_tree, em); 3272b28077SLiu Bo 3372b28077SLiu Bo #ifdef CONFIG_BTRFS_DEBUG 3472b28077SLiu Bo if (refcount_read(&em->refs) != 1) { 3572b28077SLiu Bo test_msg( 3672b28077SLiu Bo "em leak: em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx) refs %d\n", 3772b28077SLiu Bo em->start, em->len, em->block_start, 3872b28077SLiu Bo em->block_len, refcount_read(&em->refs)); 3972b28077SLiu Bo 4072b28077SLiu Bo refcount_set(&em->refs, 1); 4172b28077SLiu Bo } 4272b28077SLiu Bo #endif 4372b28077SLiu Bo free_extent_map(em); 4472b28077SLiu Bo } 4572b28077SLiu Bo } 4672b28077SLiu Bo 4772b28077SLiu Bo /* 4872b28077SLiu Bo * Test scenario: 4972b28077SLiu Bo * 5072b28077SLiu Bo * Suppose that no extent map has been loaded into memory yet, there is a file 5172b28077SLiu Bo * extent [0, 16K), followed by another file extent [16K, 20K), two dio reads 5272b28077SLiu Bo * are entering btrfs_get_extent() concurrently, t1 is reading [8K, 16K), t2 is 5372b28077SLiu Bo * reading [0, 8K) 5472b28077SLiu Bo * 5572b28077SLiu Bo * t1 t2 5672b28077SLiu Bo * btrfs_get_extent() btrfs_get_extent() 5772b28077SLiu Bo * -> lookup_extent_mapping() ->lookup_extent_mapping() 5872b28077SLiu Bo * -> add_extent_mapping(0, 16K) 5972b28077SLiu Bo * -> return em 6072b28077SLiu Bo * ->add_extent_mapping(0, 16K) 6172b28077SLiu Bo * -> #handle -EEXIST 6272b28077SLiu Bo */ 6372b28077SLiu Bo static void test_case_1(struct extent_map_tree *em_tree) 6472b28077SLiu Bo { 6572b28077SLiu Bo struct extent_map *em; 6672b28077SLiu Bo u64 start = 0; 6772b28077SLiu Bo u64 len = SZ_8K; 6872b28077SLiu Bo int ret; 6972b28077SLiu Bo 7072b28077SLiu Bo em = alloc_extent_map(); 7172b28077SLiu Bo if (!em) 7272b28077SLiu Bo /* Skip the test on error. */ 7372b28077SLiu Bo return; 7472b28077SLiu Bo 7572b28077SLiu Bo /* Add [0, 16K) */ 7672b28077SLiu Bo em->start = 0; 7772b28077SLiu Bo em->len = SZ_16K; 7872b28077SLiu Bo em->block_start = 0; 7972b28077SLiu Bo em->block_len = SZ_16K; 8072b28077SLiu Bo ret = add_extent_mapping(em_tree, em, 0); 8172b28077SLiu Bo ASSERT(ret == 0); 8272b28077SLiu Bo free_extent_map(em); 8372b28077SLiu Bo 8472b28077SLiu Bo /* Add [16K, 20K) following [0, 16K) */ 8572b28077SLiu Bo em = alloc_extent_map(); 8672b28077SLiu Bo if (!em) 8772b28077SLiu Bo goto out; 8872b28077SLiu Bo 8972b28077SLiu Bo em->start = SZ_16K; 9072b28077SLiu Bo em->len = SZ_4K; 9172b28077SLiu Bo em->block_start = SZ_32K; /* avoid merging */ 9272b28077SLiu Bo em->block_len = SZ_4K; 9372b28077SLiu Bo ret = add_extent_mapping(em_tree, em, 0); 9472b28077SLiu Bo ASSERT(ret == 0); 9572b28077SLiu Bo free_extent_map(em); 9672b28077SLiu Bo 9772b28077SLiu Bo em = alloc_extent_map(); 9872b28077SLiu Bo if (!em) 9972b28077SLiu Bo goto out; 10072b28077SLiu Bo 10172b28077SLiu Bo /* Add [0, 8K), should return [0, 16K) instead. */ 10272b28077SLiu Bo em->start = start; 10372b28077SLiu Bo em->len = len; 10472b28077SLiu Bo em->block_start = start; 10572b28077SLiu Bo em->block_len = len; 10672b28077SLiu Bo ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len); 10772b28077SLiu Bo if (ret) 10872b28077SLiu Bo test_msg("case1 [%llu %llu]: ret %d\n", start, start + len, ret); 10972b28077SLiu Bo if (em && 11072b28077SLiu Bo (em->start != 0 || extent_map_end(em) != SZ_16K || 11172b28077SLiu Bo em->block_start != 0 || em->block_len != SZ_16K)) 11272b28077SLiu Bo test_msg( 11372b28077SLiu Bo "case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n", 11472b28077SLiu Bo start, start + len, ret, em->start, em->len, 11572b28077SLiu Bo em->block_start, em->block_len); 11672b28077SLiu Bo free_extent_map(em); 11772b28077SLiu Bo out: 11872b28077SLiu Bo /* free memory */ 11972b28077SLiu Bo free_extent_map_tree(em_tree); 12072b28077SLiu Bo } 12172b28077SLiu Bo 12272b28077SLiu Bo /* 12372b28077SLiu Bo * Test scenario: 12472b28077SLiu Bo * 12572b28077SLiu Bo * Reading the inline ending up with EEXIST, ie. read an inline 12672b28077SLiu Bo * extent and discard page cache and read it again. 12772b28077SLiu Bo */ 12872b28077SLiu Bo static void test_case_2(struct extent_map_tree *em_tree) 12972b28077SLiu Bo { 13072b28077SLiu Bo struct extent_map *em; 13172b28077SLiu Bo int ret; 13272b28077SLiu Bo 13372b28077SLiu Bo em = alloc_extent_map(); 13472b28077SLiu Bo if (!em) 13572b28077SLiu Bo /* Skip the test on error. */ 13672b28077SLiu Bo return; 13772b28077SLiu Bo 13872b28077SLiu Bo /* Add [0, 1K) */ 13972b28077SLiu Bo em->start = 0; 14072b28077SLiu Bo em->len = SZ_1K; 14172b28077SLiu Bo em->block_start = EXTENT_MAP_INLINE; 14272b28077SLiu Bo em->block_len = (u64)-1; 14372b28077SLiu Bo ret = add_extent_mapping(em_tree, em, 0); 14472b28077SLiu Bo ASSERT(ret == 0); 14572b28077SLiu Bo free_extent_map(em); 14672b28077SLiu Bo 14772b28077SLiu Bo /* Add [4K, 4K) following [0, 1K) */ 14872b28077SLiu Bo em = alloc_extent_map(); 14972b28077SLiu Bo if (!em) 15072b28077SLiu Bo goto out; 15172b28077SLiu Bo 15272b28077SLiu Bo em->start = SZ_4K; 15372b28077SLiu Bo em->len = SZ_4K; 15472b28077SLiu Bo em->block_start = SZ_4K; 15572b28077SLiu Bo em->block_len = SZ_4K; 15672b28077SLiu Bo ret = add_extent_mapping(em_tree, em, 0); 15772b28077SLiu Bo ASSERT(ret == 0); 15872b28077SLiu Bo free_extent_map(em); 15972b28077SLiu Bo 16072b28077SLiu Bo em = alloc_extent_map(); 16172b28077SLiu Bo if (!em) 16272b28077SLiu Bo goto out; 16372b28077SLiu Bo 16472b28077SLiu Bo /* Add [0, 1K) */ 16572b28077SLiu Bo em->start = 0; 16672b28077SLiu Bo em->len = SZ_1K; 16772b28077SLiu Bo em->block_start = EXTENT_MAP_INLINE; 16872b28077SLiu Bo em->block_len = (u64)-1; 16972b28077SLiu Bo ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len); 17072b28077SLiu Bo if (ret) 17172b28077SLiu Bo test_msg("case2 [0 1K]: ret %d\n", ret); 17272b28077SLiu Bo if (em && 17372b28077SLiu Bo (em->start != 0 || extent_map_end(em) != SZ_1K || 17472b28077SLiu Bo em->block_start != EXTENT_MAP_INLINE || em->block_len != (u64)-1)) 17572b28077SLiu Bo test_msg( 17672b28077SLiu Bo "case2 [0 1K]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n", 17772b28077SLiu Bo ret, em->start, em->len, em->block_start, 17872b28077SLiu Bo em->block_len); 17972b28077SLiu Bo free_extent_map(em); 18072b28077SLiu Bo out: 18172b28077SLiu Bo /* free memory */ 18272b28077SLiu Bo free_extent_map_tree(em_tree); 18372b28077SLiu Bo } 18472b28077SLiu Bo 185*fd87526fSLiu Bo static void __test_case_3(struct extent_map_tree *em_tree, u64 start) 186*fd87526fSLiu Bo { 187*fd87526fSLiu Bo struct extent_map *em; 188*fd87526fSLiu Bo u64 len = SZ_4K; 189*fd87526fSLiu Bo int ret; 190*fd87526fSLiu Bo 191*fd87526fSLiu Bo em = alloc_extent_map(); 192*fd87526fSLiu Bo if (!em) 193*fd87526fSLiu Bo /* Skip this test on error. */ 194*fd87526fSLiu Bo return; 195*fd87526fSLiu Bo 196*fd87526fSLiu Bo /* Add [4K, 8K) */ 197*fd87526fSLiu Bo em->start = SZ_4K; 198*fd87526fSLiu Bo em->len = SZ_4K; 199*fd87526fSLiu Bo em->block_start = SZ_4K; 200*fd87526fSLiu Bo em->block_len = SZ_4K; 201*fd87526fSLiu Bo ret = add_extent_mapping(em_tree, em, 0); 202*fd87526fSLiu Bo ASSERT(ret == 0); 203*fd87526fSLiu Bo free_extent_map(em); 204*fd87526fSLiu Bo 205*fd87526fSLiu Bo em = alloc_extent_map(); 206*fd87526fSLiu Bo if (!em) 207*fd87526fSLiu Bo goto out; 208*fd87526fSLiu Bo 209*fd87526fSLiu Bo /* Add [0, 16K) */ 210*fd87526fSLiu Bo em->start = 0; 211*fd87526fSLiu Bo em->len = SZ_16K; 212*fd87526fSLiu Bo em->block_start = 0; 213*fd87526fSLiu Bo em->block_len = SZ_16K; 214*fd87526fSLiu Bo ret = btrfs_add_extent_mapping(em_tree, &em, start, len); 215*fd87526fSLiu Bo if (ret) 216*fd87526fSLiu Bo test_msg("case3 [0x%llx 0x%llx): ret %d\n", 217*fd87526fSLiu Bo start, start + len, ret); 218*fd87526fSLiu Bo /* 219*fd87526fSLiu Bo * Since bytes within em are contiguous, em->block_start is identical to 220*fd87526fSLiu Bo * em->start. 221*fd87526fSLiu Bo */ 222*fd87526fSLiu Bo if (em && 223*fd87526fSLiu Bo (start < em->start || start + len > extent_map_end(em) || 224*fd87526fSLiu Bo em->start != em->block_start || em->len != em->block_len)) 225*fd87526fSLiu Bo test_msg( 226*fd87526fSLiu Bo "case3 [0x%llx 0x%llx): ret %d em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)\n", 227*fd87526fSLiu Bo start, start + len, ret, em->start, em->len, 228*fd87526fSLiu Bo em->block_start, em->block_len); 229*fd87526fSLiu Bo free_extent_map(em); 230*fd87526fSLiu Bo out: 231*fd87526fSLiu Bo /* free memory */ 232*fd87526fSLiu Bo free_extent_map_tree(em_tree); 233*fd87526fSLiu Bo } 234*fd87526fSLiu Bo 235*fd87526fSLiu Bo /* 236*fd87526fSLiu Bo * Test scenario: 237*fd87526fSLiu Bo * 238*fd87526fSLiu Bo * Suppose that no extent map has been loaded into memory yet. 239*fd87526fSLiu Bo * There is a file extent [0, 16K), two jobs are running concurrently 240*fd87526fSLiu Bo * against it, t1 is buffered writing to [4K, 8K) and t2 is doing dio 241*fd87526fSLiu Bo * read from [0, 4K) or [8K, 12K) or [12K, 16K). 242*fd87526fSLiu Bo * 243*fd87526fSLiu Bo * t1 goes ahead of t2 and adds em [4K, 8K) into tree. 244*fd87526fSLiu Bo * 245*fd87526fSLiu Bo * t1 t2 246*fd87526fSLiu Bo * cow_file_range() btrfs_get_extent() 247*fd87526fSLiu Bo * -> lookup_extent_mapping() 248*fd87526fSLiu Bo * -> add_extent_mapping() 249*fd87526fSLiu Bo * -> add_extent_mapping() 250*fd87526fSLiu Bo */ 251*fd87526fSLiu Bo static void test_case_3(struct extent_map_tree *em_tree) 252*fd87526fSLiu Bo { 253*fd87526fSLiu Bo __test_case_3(em_tree, 0); 254*fd87526fSLiu Bo __test_case_3(em_tree, SZ_8K); 255*fd87526fSLiu Bo __test_case_3(em_tree, (12 * 1024ULL)); 256*fd87526fSLiu Bo } 257*fd87526fSLiu Bo 25872b28077SLiu Bo int btrfs_test_extent_map() 25972b28077SLiu Bo { 26072b28077SLiu Bo struct extent_map_tree *em_tree; 26172b28077SLiu Bo 26272b28077SLiu Bo test_msg("Running extent_map tests\n"); 26372b28077SLiu Bo 26472b28077SLiu Bo em_tree = kzalloc(sizeof(*em_tree), GFP_KERNEL); 26572b28077SLiu Bo if (!em_tree) 26672b28077SLiu Bo /* Skip the test on error. */ 26772b28077SLiu Bo return 0; 26872b28077SLiu Bo 26972b28077SLiu Bo extent_map_tree_init(em_tree); 27072b28077SLiu Bo 27172b28077SLiu Bo test_case_1(em_tree); 27272b28077SLiu Bo test_case_2(em_tree); 273*fd87526fSLiu Bo test_case_3(em_tree); 27472b28077SLiu Bo 27572b28077SLiu Bo kfree(em_tree); 27672b28077SLiu Bo return 0; 27772b28077SLiu Bo } 278