/* * Build the most complicated map of extents the earth has ever seen. We want * this so we can test all of the corner cases of btrfs_get_extent. Here is a * diagram of how the extents will look though this may not be possible we still * want to make sure everything acts normally (the last number is not inclusive) * * [0 - 5][5 - 6][ 6 - 4096 ][ 4096 - 4100][4100 - 8195][8195 - 12291] * [hole ][inline][hole but no extent][ hole ][ regular ][regular1 split] * * [12291 - 16387][16387 - 24579][24579 - 28675][ 28675 - 32771][32771 - 36867 ] * [ hole ][regular1 split][ prealloc ][ prealloc1 ][prealloc1 written] * * [36867 - 45059][45059 - 53251][53251 - 57347][57347 - 61443][61443- 69635] * [ prealloc1 ][ compressed ][ compressed1 ][ regular ][ compressed1] * * [69635-73731][ 73731 - 86019 ][86019-90115] * [ regular ][ hole but no extent][ regular ] */ static void setup_file_extents(struct btrfs_root *root, u32 sectorsize) { int slot = 0; u64 disk_bytenr = SZ_1M; u64 offset = 0; /* First we want a hole */ insert_extent(root, offset, 5, 5, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += 5; /* * Now we want an inline extent, I don't think this is possible but hey * why not? Also keep in mind if we have an inline extent it counts as * the whole first page. If we were to expand it we would have to cow * and we wouldn't have an inline extent anymore. */ insert_extent(root, offset, 1, 1, 0, 0, 0, BTRFS_FILE_EXTENT_INLINE, 0, slot); slot++; offset = sectorsize; /* Now another hole */ insert_extent(root, offset, 4, 4, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += 4; /* Now for a regular extent */ insert_extent(root, offset, sectorsize - 1, sectorsize - 1, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; disk_bytenr += sectorsize; offset += sectorsize - 1; /* * Now for 3 extents that were split from a hole punch so we test * offsets properly. */ insert_extent(root, offset, sectorsize, 4 * sectorsize, 0, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; insert_extent(root, offset, sectorsize, sectorsize, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += 2 * sectorsize; disk_bytenr += 4 * sectorsize; /* Now for a unwritten prealloc extent */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); slot++; offset += sectorsize; /* * We want to jack up disk_bytenr a little more so the em stuff doesn't * merge our records. */ disk_bytenr += 2 * sectorsize; /* * Now for a partially written prealloc extent, basically the same as * the hole punch example above. Ram_bytes never changes when you mark * extents written btw. */ insert_extent(root, offset, sectorsize, 4 * sectorsize, 0, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); slot++; offset += sectorsize; insert_extent(root, offset, sectorsize, 4 * sectorsize, sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, 4 * sectorsize, BTRFS_FILE_EXTENT_PREALLOC, 0, slot); slot++; offset += 2 * sectorsize; disk_bytenr += 4 * sectorsize; /* Now a normal compressed extent */ insert_extent(root, offset, 2 * sectorsize, 2 * sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); slot++; offset += 2 * sectorsize; /* No merges */ disk_bytenr += 2 * sectorsize; /* Now a split compressed extent */ insert_extent(root, offset, sectorsize, 4 * sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); slot++; offset += sectorsize; insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr + sectorsize, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += sectorsize; insert_extent(root, offset, 2 * sectorsize, 4 * sectorsize, 2 * sectorsize, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); slot++; offset += 2 * sectorsize; disk_bytenr += 2 * sectorsize; /* Now extents that have a hole but no hole extent */ insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); slot++; offset += 4 * sectorsize; disk_bytenr += sectorsize; insert_extent(root, offset, sectorsize, sectorsize, 0, disk_bytenr, sectorsize, BTRFS_FILE_EXTENT_REG, 0, slot); }
static int test_hole_first(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); BTRFS_I(inode)->root = root; ret = -EINVAL; /* * Need a blank inode item here just so we don't confuse * btrfs_get_extent. */ insert_inode_item_key(root); insert_extent(root, sectorsize, sectorsize, sectorsize, 0, sectorsize, sectorsize, BTRFS_FILE_EXTENT_REG, 0, 1); em = btrfs_get_extent(inode, NULL, 0, 0, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != sectorsize) { test_msg("Unexpected extent wanted start 0 len %u, " "got start %llu len %llu\n", sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only, em->flags); goto out; } free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, sectorsize, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != sectorsize) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != sectorsize || em->len != sectorsize) { test_msg("Unexpected extent wanted start %u len %u, " "got start %llu len %llu\n", sectorsize, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, wanted 0 got %lu\n", em->flags); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
/* Insert extent E into the tree rooted at extent ROOT. The row-positions in E are assumed to be relative to the row-position of ROOT. */ static void insert_extent(Lisp_Extent *e, Lisp_Extent *root) { Lisp_Extent *x; top: x = root->first_child; while(x != 0) { if(PPOS_LESS_EQUAL_P(&e->end, &x->start)) { /* Insert E before X */ e->parent = root; e->left_sibling = x->left_sibling; if(e->left_sibling != 0) e->left_sibling->right_sibling = e; x->left_sibling = e; e->right_sibling = x; if(root->first_child == x) root->first_child = e; assert_invariants (e); assert_invariants (x); return; } else if(PPOS_LESS_P(&e->start, &x->start)) { /* X overlaps E. The end of E clashes with the start of X. Break E into two fragments, insert the first before X, the second within X. */ Lisp_Extent *frag = alloc_extent(e); frag->end = x->start; e->start = x->start; frag->car |= EXTFF_OPEN_END; frag->frag_pred = e->frag_pred; if(e->frag_pred != 0) e->frag_pred->frag_next = frag; frag->frag_next = e; e->frag_pred = frag; assert_invariants (e); assert_invariants (x); assert_invariants (frag); insert_extent(frag, x->parent); assert_invariants (e); assert_invariants (x); assert_invariants (frag); continue; } else if(PPOS_LESS_P(&e->start, &x->end)) { /* E starts before X ends. */ if(PPOS_LESS_EQUAL_P(&e->end, &x->end)) { /* But E ends before X ends. So insert E in X. */ e->start.row -= x->start.row; e->end.row -= x->start.row; root = x; goto top; } else { /* E ends after X ends. So break E into two frags. */ Lisp_Extent *frag = alloc_extent(e); frag->start = x->end; e->end = x->end; frag->car |= EXTFF_OPEN_START; frag->frag_next = e->frag_next; if(e->frag_next != 0) e->frag_next->frag_pred = frag; frag->frag_pred = e; e->frag_next = frag; e->start.row -= x->start.row; e->end.row -= x->start.row; assert_invariants (e); assert_invariants (x); assert_invariants (frag); insert_extent(e, x); assert_invariants (e); assert_invariants (x); assert_invariants (frag); e = frag; continue; } } else { /* X ends before E starts, keep going.. */ } x = x->right_sibling; } /* Insert e at the end of the root. */ e->parent = root; e->left_sibling = root->last_child; if(root->last_child != 0) root->last_child->right_sibling = e; root->last_child = e; if(root->first_child == 0) root->first_child = e; assert_invariants (e); assert_invariants (root); }