Btrfs: Bring back mount -o ssd optimizations
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 96a4932..acf22ad 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1405,6 +1405,9 @@
struct btrfs_root *root, struct btrfs_path *path,
u64 isize);
/* inode.c */
+int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
+ size_t size, struct bio *bio);
+
static inline void dec_i_blocks(struct inode *inode, u64 dec)
{
dec = dec >> 9;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 26185d4..4890151 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1103,4 +1103,6 @@
static struct extent_io_ops btree_extent_io_ops = {
.writepage_io_hook = btree_writepage_io_hook,
.submit_bio_hook = btree_submit_bio_hook,
+ /* note we're sharing with inode.c for the merge bio hook */
+ .merge_bio_hook = btrfs_merge_bio_hook,
};
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 14eb8fc..e9ef644 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1473,13 +1473,31 @@
struct btrfs_root * root = orig_root->fs_info->extent_root;
struct btrfs_fs_info *info = root->fs_info;
u64 total_needed = num_bytes;
+ u64 *last_ptr = NULL;
struct btrfs_block_group_cache *block_group;
int full_scan = 0;
int wrapped = 0;
+ int empty_cluster = 2 * 1024 * 1024;
WARN_ON(num_bytes < root->sectorsize);
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
+ if (data & BTRFS_BLOCK_GROUP_METADATA) {
+ last_ptr = &root->fs_info->last_alloc;
+ }
+
+ if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD)) {
+ last_ptr = &root->fs_info->last_data_alloc;
+ }
+
+ if (last_ptr) {
+ if (*last_ptr)
+ hint_byte = *last_ptr;
+ else {
+ empty_size += empty_cluster;
+ }
+ }
+
if (search_end == (u64)-1)
search_end = btrfs_super_total_bytes(&info->super_copy);
@@ -1489,11 +1507,14 @@
hint_byte = search_start;
block_group = btrfs_find_block_group(root, block_group,
hint_byte, data, 1);
+ if (last_ptr && *last_ptr == 0 && block_group)
+ hint_byte = block_group->key.objectid;
} else {
block_group = btrfs_find_block_group(root,
trans->block_group,
search_start, data, 1);
}
+ search_start = max(search_start, hint_byte);
total_needed += empty_size;
@@ -1506,9 +1527,36 @@
}
ret = find_search_start(root, &block_group, &search_start,
total_needed, data);
+ if (ret == -ENOSPC && last_ptr && *last_ptr) {
+ *last_ptr = 0;
+ block_group = btrfs_lookup_block_group(info,
+ orig_search_start);
+ search_start = orig_search_start;
+ ret = find_search_start(root, &block_group, &search_start,
+ total_needed, data);
+ }
+ if (ret == -ENOSPC)
+ goto enospc;
if (ret)
goto error;
+ if (last_ptr && *last_ptr && search_start != *last_ptr) {
+ *last_ptr = 0;
+ if (!empty_size) {
+ empty_size += empty_cluster;
+ total_needed += empty_size;
+ }
+ block_group = btrfs_lookup_block_group(info,
+ orig_search_start);
+ search_start = orig_search_start;
+ ret = find_search_start(root, &block_group,
+ &search_start, total_needed, data);
+ if (ret == -ENOSPC)
+ goto enospc;
+ if (ret)
+ goto error;
+ }
+
search_start = stripe_align(root, search_start);
ins->objectid = search_start;
ins->offset = num_bytes;
@@ -1547,6 +1595,13 @@
trans->block_group = block_group;
}
ins->offset = num_bytes;
+ if (last_ptr) {
+ *last_ptr = ins->objectid + ins->offset;
+ if (*last_ptr ==
+ btrfs_super_total_bytes(&root->fs_info->super_copy)) {
+ *last_ptr = 0;
+ }
+ }
return 0;
new_group:
@@ -1612,12 +1667,12 @@
if (root->ref_cows) {
if (data != BTRFS_BLOCK_GROUP_METADATA) {
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
- num_bytes,
+ 2 * 1024 * 1024,
BTRFS_BLOCK_GROUP_METADATA);
BUG_ON(ret);
}
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
- num_bytes, data);
+ num_bytes + 2 * 1024 * 1024, data);
BUG_ON(ret);
}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7e3a1eb..6dab664 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1730,6 +1730,8 @@
if (bio_ret && *bio_ret) {
bio = *bio_ret;
if (bio->bi_sector + (bio->bi_size >> 9) != sector ||
+ (tree->ops && tree->ops->merge_bio_hook &&
+ tree->ops->merge_bio_hook(page, offset, size, bio)) ||
bio_add_page(bio, page, size, offset) < size) {
ret = submit_one_bio(rw, bio);
bio = NULL;
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 0dca893..8b5319db25 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -29,6 +29,8 @@
int (*fill_delalloc)(struct inode *inode, u64 start, u64 end);
int (*writepage_io_hook)(struct page *page, u64 start, u64 end);
int (*submit_bio_hook)(struct inode *inode, int rw, struct bio *bio);
+ int (*merge_bio_hook)(struct page *page, unsigned long offset,
+ size_t size, struct bio *bio);
int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end,
struct extent_state *state);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 109576b..5140d68 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -296,6 +296,34 @@
return 0;
}
+int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
+ size_t size, struct bio *bio)
+{
+ struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
+ struct btrfs_mapping_tree *map_tree;
+ struct btrfs_device *dev;
+ u64 logical = bio->bi_sector << 9;
+ u64 physical;
+ u64 length = 0;
+ u64 map_length;
+ struct bio_vec *bvec;
+ int i;
+ int ret;
+
+ bio_for_each_segment(bvec, bio, i) {
+ length += bvec->bv_len;
+ }
+ map_tree = &root->fs_info->mapping_tree;
+ map_length = length;
+ ret = btrfs_map_block(map_tree, logical, &physical, &map_length, &dev);
+ if (map_length < length + size) {
+ printk("merge bio hook logical %Lu bio len %Lu physical %Lu "
+ "len %Lu\n", logical, length, physical, map_length);
+ return 1;
+ }
+ return 0;
+}
+
int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -3033,6 +3061,7 @@
static struct extent_io_ops btrfs_extent_io_ops = {
.fill_delalloc = run_delalloc_range,
.submit_bio_hook = btrfs_submit_bio_hook,
+ .merge_bio_hook = btrfs_merge_bio_hook,
.readpage_io_hook = btrfs_readpage_io_hook,
.readpage_end_io_hook = btrfs_readpage_end_io_hook,
.set_bit_hook = btrfs_set_bit_hook,
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index ae22d01..16fb6bb 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -578,6 +578,11 @@
map_tree = &root->fs_info->mapping_tree;
map_length = length;
ret = btrfs_map_block(map_tree, logical, &physical, &map_length, &dev);
+ if (map_length < length) {
+ printk("mapping failed logical %Lu bio len %Lu physical %Lu "
+ "len %Lu\n", logical, length, physical, map_length);
+ BUG();
+ }
BUG_ON(map_length < length);
bio->bi_sector = physical >> 9;
bio->bi_bdev = dev->bdev;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 77fa6ef..2025912 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -64,4 +64,7 @@
void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree);
int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio);
int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer *buf);
+int btrfs_map_block(struct btrfs_mapping_tree *map_tree,
+ u64 logical, u64 *phys, u64 *length,
+ struct btrfs_device **dev);
#endif