]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - fs/btrfs/extent-tree.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[net-next-2.6.git] / fs / btrfs / extent-tree.c
index b1822e752b4aa335ae1c44b0c7a0e703275ce510..b9080d71991a35cea63d2620d1534234cde6e0a9 100644 (file)
@@ -1696,7 +1696,7 @@ static void btrfs_issue_discard(struct block_device *bdev,
                                u64 start, u64 len)
 {
        blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL,
-                            DISCARD_FL_BARRIER);
+                       BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER);
 }
 
 static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
@@ -2895,10 +2895,9 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
 again:
        /* make sure we have enough space to handle the data first */
        spin_lock(&data_sinfo->lock);
-       used = data_sinfo->bytes_used + data_sinfo->bytes_delalloc +
-               data_sinfo->bytes_reserved + data_sinfo->bytes_pinned +
-               data_sinfo->bytes_readonly + data_sinfo->bytes_may_use +
-               data_sinfo->bytes_super;
+       used = data_sinfo->bytes_used + data_sinfo->bytes_reserved +
+               data_sinfo->bytes_pinned + data_sinfo->bytes_readonly +
+               data_sinfo->bytes_may_use;
 
        if (used + bytes > data_sinfo->total_bytes) {
                struct btrfs_trans_handle *trans;
@@ -2922,7 +2921,7 @@ alloc:
                                             bytes + 2 * 1024 * 1024,
                                             alloc_target, 0);
                        btrfs_end_transaction(trans, root);
-                       if (ret)
+                       if (ret < 0)
                                return ret;
 
                        if (!data_sinfo) {
@@ -2945,17 +2944,18 @@ alloc:
                        goto again;
                }
 
-               printk(KERN_ERR "no space left, need %llu, %llu delalloc bytes"
-                      ", %llu bytes_used, %llu bytes_reserved, "
-                      "%llu bytes_pinned, %llu bytes_readonly, %llu may use "
-                      "%llu total\n", (unsigned long long)bytes,
-                      (unsigned long long)data_sinfo->bytes_delalloc,
+#if 0 /* I hope we never need this code again, just in case */
+               printk(KERN_ERR "no space left, need %llu, %llu bytes_used, "
+                      "%llu bytes_reserved, " "%llu bytes_pinned, "
+                      "%llu bytes_readonly, %llu may use %llu total\n",
+                      (unsigned long long)bytes,
                       (unsigned long long)data_sinfo->bytes_used,
                       (unsigned long long)data_sinfo->bytes_reserved,
                       (unsigned long long)data_sinfo->bytes_pinned,
                       (unsigned long long)data_sinfo->bytes_readonly,
                       (unsigned long long)data_sinfo->bytes_may_use,
                       (unsigned long long)data_sinfo->total_bytes);
+#endif
                return -ENOSPC;
        }
        data_sinfo->bytes_may_use += bytes;
@@ -3464,6 +3464,91 @@ void btrfs_block_rsv_release(struct btrfs_root *root,
        block_rsv_release_bytes(block_rsv, global_rsv, num_bytes);
 }
 
+/*
+ * helper to calculate size of global block reservation.
+ * the desired value is sum of space used by extent tree,
+ * checksum tree and root tree
+ */
+static u64 calc_global_metadata_size(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_space_info *sinfo;
+       u64 num_bytes;
+       u64 meta_used;
+       u64 data_used;
+       int csum_size = btrfs_super_csum_size(&fs_info->super_copy);
+#if 0
+       /*
+        * per tree used space accounting can be inaccuracy, so we
+        * can't rely on it.
+        */
+       spin_lock(&fs_info->extent_root->accounting_lock);
+       num_bytes = btrfs_root_used(&fs_info->extent_root->root_item);
+       spin_unlock(&fs_info->extent_root->accounting_lock);
+
+       spin_lock(&fs_info->csum_root->accounting_lock);
+       num_bytes += btrfs_root_used(&fs_info->csum_root->root_item);
+       spin_unlock(&fs_info->csum_root->accounting_lock);
+
+       spin_lock(&fs_info->tree_root->accounting_lock);
+       num_bytes += btrfs_root_used(&fs_info->tree_root->root_item);
+       spin_unlock(&fs_info->tree_root->accounting_lock);
+#endif
+       sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_DATA);
+       spin_lock(&sinfo->lock);
+       data_used = sinfo->bytes_used;
+       spin_unlock(&sinfo->lock);
+
+       sinfo = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
+       spin_lock(&sinfo->lock);
+       meta_used = sinfo->bytes_used;
+       spin_unlock(&sinfo->lock);
+
+       num_bytes = (data_used >> fs_info->sb->s_blocksize_bits) *
+                   csum_size * 2;
+       num_bytes += div64_u64(data_used + meta_used, 50);
+
+       if (num_bytes * 3 > meta_used)
+               num_bytes = div64_u64(meta_used, 3);
+
+       return ALIGN(num_bytes, fs_info->extent_root->leafsize << 10);
+}
+
+static void update_global_block_rsv(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
+       struct btrfs_space_info *sinfo = block_rsv->space_info;
+       u64 num_bytes;
+
+       num_bytes = calc_global_metadata_size(fs_info);
+
+       spin_lock(&block_rsv->lock);
+       spin_lock(&sinfo->lock);
+
+       block_rsv->size = num_bytes;
+
+       num_bytes = sinfo->bytes_used + sinfo->bytes_pinned +
+                   sinfo->bytes_reserved + sinfo->bytes_readonly;
+
+       if (sinfo->total_bytes > num_bytes) {
+               num_bytes = sinfo->total_bytes - num_bytes;
+               block_rsv->reserved += num_bytes;
+               sinfo->bytes_reserved += num_bytes;
+       }
+
+       if (block_rsv->reserved >= block_rsv->size) {
+               num_bytes = block_rsv->reserved - block_rsv->size;
+               sinfo->bytes_reserved -= num_bytes;
+               block_rsv->reserved = block_rsv->size;
+               block_rsv->full = 1;
+       }
+#if 0
+       printk(KERN_INFO"global block rsv size %llu reserved %llu\n",
+               block_rsv->size, block_rsv->reserved);
+#endif
+       spin_unlock(&sinfo->lock);
+       spin_unlock(&block_rsv->lock);
+}
+
 static void init_global_block_rsv(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_space_info *space_info;
@@ -3473,11 +3558,36 @@ static void init_global_block_rsv(struct btrfs_fs_info *fs_info)
        fs_info->chunk_block_rsv.priority = 10;
 
        space_info = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA);
+       fs_info->global_block_rsv.space_info = space_info;
+       fs_info->global_block_rsv.priority = 10;
+       fs_info->global_block_rsv.refill_used = 1;
+       fs_info->delalloc_block_rsv.space_info = space_info;
        fs_info->trans_block_rsv.space_info = space_info;
        fs_info->empty_block_rsv.space_info = space_info;
        fs_info->empty_block_rsv.priority = 10;
 
+       fs_info->extent_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->csum_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->dev_root->block_rsv = &fs_info->global_block_rsv;
+       fs_info->tree_root->block_rsv = &fs_info->global_block_rsv;
        fs_info->chunk_root->block_rsv = &fs_info->chunk_block_rsv;
+
+       btrfs_add_durable_block_rsv(fs_info, &fs_info->global_block_rsv);
+
+       btrfs_add_durable_block_rsv(fs_info, &fs_info->delalloc_block_rsv);
+
+       update_global_block_rsv(fs_info);
+}
+
+static void release_global_block_rsv(struct btrfs_fs_info *fs_info)
+{
+       block_rsv_release_bytes(&fs_info->global_block_rsv, NULL, (u64)-1);
+       WARN_ON(fs_info->delalloc_block_rsv.size > 0);
+       WARN_ON(fs_info->delalloc_block_rsv.reserved > 0);
+       WARN_ON(fs_info->trans_block_rsv.size > 0);
+       WARN_ON(fs_info->trans_block_rsv.reserved > 0);
+       WARN_ON(fs_info->chunk_block_rsv.size > 0);
+       WARN_ON(fs_info->chunk_block_rsv.reserved > 0);
 }
 
 static u64 calc_trans_metadata_size(struct btrfs_root *root, int num_items)
@@ -3518,6 +3628,34 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
        trans->bytes_reserved = 0;
 }
 
+int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
+                                 struct inode *inode)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_block_rsv *src_rsv = get_block_rsv(trans, root);
+       struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv;
+
+       /*
+        * one for deleting orphan item, one for updating inode and
+        * two for calling btrfs_truncate_inode_items.
+        *
+        * btrfs_truncate_inode_items is a delete operation, it frees
+        * more space than it uses in most cases. So two units of
+        * metadata space should be enough for calling it many times.
+        * If all of the metadata space is used, we can commit
+        * transaction and use space it freed.
+        */
+       u64 num_bytes = calc_trans_metadata_size(root, 4);
+       return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes);
+}
+
+void btrfs_orphan_release_metadata(struct inode *inode)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       u64 num_bytes = calc_trans_metadata_size(root, 4);
+       btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes);
+}
+
 int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,
                                struct btrfs_pending_snapshot *pending)
 {
@@ -3826,6 +3964,8 @@ int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
                fs_info->pinned_extents = &fs_info->freed_extents[0];
 
        up_write(&fs_info->extent_commit_sem);
+
+       update_global_block_rsv(fs_info);
        return 0;
 }
 
@@ -4818,19 +4958,16 @@ static void dump_space_info(struct btrfs_space_info *info, u64 bytes,
        printk(KERN_INFO "space_info has %llu free, is %sfull\n",
               (unsigned long long)(info->total_bytes - info->bytes_used -
                                    info->bytes_pinned - info->bytes_reserved -
-                                   info->bytes_super),
+                                   info->bytes_readonly),
               (info->full) ? "" : "not ");
-       printk(KERN_INFO "space_info total=%llu, pinned=%llu, delalloc=%llu,"
-              " may_use=%llu, used=%llu, root=%llu, super=%llu, reserved=%llu"
-              "\n",
+       printk(KERN_INFO "space_info total=%llu, used=%llu, pinned=%llu, "
+              "reserved=%llu, may_use=%llu, readonly=%llu\n",
               (unsigned long long)info->total_bytes,
+              (unsigned long long)info->bytes_used,
               (unsigned long long)info->bytes_pinned,
-              (unsigned long long)info->bytes_delalloc,
+              (unsigned long long)info->bytes_reserved,
               (unsigned long long)info->bytes_may_use,
-              (unsigned long long)info->bytes_used,
-              (unsigned long long)info->bytes_root,
-              (unsigned long long)info->bytes_super,
-              (unsigned long long)info->bytes_reserved);
+              (unsigned long long)info->bytes_readonly);
        spin_unlock(&info->lock);
 
        if (!dump_block_groups)
@@ -5740,7 +5877,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
  * also make sure backrefs for the shared block and all lower level
  * blocks are properly updated.
  */
-int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
+int btrfs_drop_snapshot(struct btrfs_root *root,
+                       struct btrfs_block_rsv *block_rsv, int update_ref)
 {
        struct btrfs_path *path;
        struct btrfs_trans_handle *trans;
@@ -5759,6 +5897,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
        BUG_ON(!wc);
 
        trans = btrfs_start_transaction(tree_root, 0);
+       if (block_rsv)
+               trans->block_rsv = block_rsv;
 
        if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
                level = btrfs_header_level(root->node);
@@ -5846,24 +5986,16 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
                }
 
                BUG_ON(wc->level == 0);
-               if (trans->transaction->in_commit ||
-                   trans->transaction->delayed_refs.flushing) {
+               if (btrfs_should_end_transaction(trans, tree_root)) {
                        ret = btrfs_update_root(trans, tree_root,
                                                &root->root_key,
                                                root_item);
                        BUG_ON(ret);
 
-                       btrfs_end_transaction(trans, tree_root);
+                       btrfs_end_transaction_throttle(trans, tree_root);
                        trans = btrfs_start_transaction(tree_root, 0);
-                       if (IS_ERR(trans))
-                               return PTR_ERR(trans);
-               } else {
-                       unsigned long update;
-                       update = trans->delayed_ref_updates;
-                       trans->delayed_ref_updates = 0;
-                       if (update)
-                               btrfs_run_delayed_refs(trans, tree_root,
-                                                      update);
+                       if (block_rsv)
+                               trans->block_rsv = block_rsv;
                }
        }
        btrfs_release_path(root, path);
@@ -5891,7 +6023,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
                kfree(root);
        }
 out:
-       btrfs_end_transaction(trans, tree_root);
+       btrfs_end_transaction_throttle(trans, tree_root);
        kfree(wc);
        btrfs_free_path(path);
        return err;
@@ -7727,6 +7859,8 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
         */
        synchronize_rcu();
 
+       release_global_block_rsv(info);
+
        while(!list_empty(&info->space_info)) {
                space_info = list_entry(info->space_info.next,
                                        struct btrfs_space_info,