Btrfs: Add per-root block accounting and sysfs entries
authorJosef Bacik <jbacik@redhat.com>
Wed, 29 Aug 2007 19:47:34 +0000 (15:47 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Wed, 29 Aug 2007 19:47:34 +0000 (15:47 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/Makefile
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/extent-tree.c
fs/btrfs/inode.c
fs/btrfs/super.c
fs/btrfs/sysfs.c
fs/btrfs/transaction.c

index ea7a2287035039d21e996b7f44842bf4f68db031..ee505c87ecf3a6dd3f58a83147a66947cc570ca8 100644 (file)
@@ -5,7 +5,7 @@ obj-m  := btrfs.o
 btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
           hash.o file-item.o inode-item.o inode-map.o disk-io.o \
           transaction.o bit-radix.o inode.o file.o tree-defrag.o \
-          extent_map.o
+          extent_map.o sysfs.o
 
 #btrfs-y := ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 #        root-tree.o dir-item.o hash.o file-item.o inode-item.o \
index 947f061ed1188d83c784888de019b3f48952199c..ffeee546a0d74e90cc8b4649e29696eafa2c68d4 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/fs.h>
 #include <linux/buffer_head.h>
 #include <linux/workqueue.h>
+#include <linux/completion.h>
 #include "bit-radix.h"
 
 struct btrfs_trans_handle;
@@ -313,6 +314,8 @@ struct btrfs_fs_info {
        struct list_head trans_list;
        struct list_head dead_roots;
        struct delayed_work trans_work;
+       struct kobject super_kobj;
+       struct completion kobj_unregister;
        int do_barriers;
        int closing;
 };
@@ -328,6 +331,8 @@ struct btrfs_root {
        struct btrfs_key root_key;
        struct btrfs_fs_info *fs_info;
        struct inode *inode;
+       struct kobject root_kobj;
+       struct completion kobj_unregister;
        u64 objectid;
        u64 last_trans;
        u32 blocksize;
@@ -338,6 +343,7 @@ struct btrfs_root {
        struct btrfs_key defrag_progress;
        int defrag_running;
        int defrag_level;
+       char *name;
 };
 
 /* the lower bits in the key flags defines the item type */
@@ -814,6 +820,28 @@ static inline void btrfs_set_root_flags(struct btrfs_root_item *item, u32 val)
        item->flags = cpu_to_le32(val);
 }
 
+static inline void btrfs_set_root_blocks_used(struct btrfs_root_item *item,
+                                                  u64 val)
+{
+       item->blocks_used = cpu_to_le64(val);
+}
+
+static inline u64 btrfs_root_blocks_used(struct btrfs_root_item *item)
+{
+       return le64_to_cpu(item->blocks_used);
+}
+
+static inline void btrfs_set_root_block_limit(struct btrfs_root_item *item,
+                                               u64 val)
+{
+       item->block_limit = cpu_to_le64(val);
+}
+
+static inline u64 btrfs_root_block_limit(struct btrfs_root_item *item)
+{
+       return le64_to_cpu(item->block_limit);
+}
+
 static inline u64 btrfs_super_blocknr(struct btrfs_super_block *s)
 {
        return le64_to_cpu(s->blocknr);
@@ -1014,6 +1042,23 @@ static inline void btrfs_memmove(struct btrfs_root *root,
        memmove(dst, src, nr);
 }
 
+static inline int btrfs_set_root_name(struct btrfs_root *root,
+                                     const char *name, int len)
+{
+       /* if we already have a name just free it */
+       if (root->name)
+               kfree(root->name);
+
+       root->name = kmalloc(len+1, GFP_KERNEL);
+       if (!root->name)
+               return -ENOMEM;
+
+       memcpy(root->name, name, len);
+       root->name[len] ='\0';
+
+       return 0;
+}
+
 /* helper function to cast into the data area of the leaf. */
 #define btrfs_item_ptr(leaf, slot, type) \
        ((type *)(btrfs_leaf_data(leaf) + \
@@ -1191,4 +1236,13 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
                        struct btrfs_root *root, int cache_only);
+
+/* sysfs.c */
+int btrfs_init_sysfs(void);
+void btrfs_exit_sysfs(void);
+int btrfs_sysfs_add_super(struct btrfs_fs_info *fs);
+int btrfs_sysfs_add_root(struct btrfs_root *root);
+void btrfs_sysfs_del_root(struct btrfs_root *root);
+void btrfs_sysfs_del_super(struct btrfs_fs_info *root);
+
 #endif
index b2f79878d51a05fb5b934910dd72e56d622797f6..c25ef0a68f18dce2c2cb18218348db76aeba6ad5 100644 (file)
@@ -294,9 +294,12 @@ static int __setup_root(int blocksize,
        root->last_trans = 0;
        root->highest_inode = 0;
        root->last_inode_alloc = 0;
+       root->name = NULL;
        memset(&root->root_key, 0, sizeof(root->root_key));
        memset(&root->root_item, 0, sizeof(root->root_item));
        memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
+       memset(&root->root_kobj, 0, sizeof(root->root_kobj));
+       init_completion(&root->kobj_unregister);
        root->defrag_running = 0;
        root->defrag_level = 0;
        root->root_key.objectid = objectid;
@@ -384,7 +387,8 @@ insert:
 }
 
 struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
-                                     struct btrfs_key *location)
+                                     struct btrfs_key *location,
+                                     const char *name, int namelen)
 {
        struct btrfs_root *root;
        int ret;
@@ -405,6 +409,22 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
                kfree(root);
                return ERR_PTR(ret);
        }
+
+       ret = btrfs_set_root_name(root, name, namelen);
+       if (ret) {
+               brelse(root->node);
+               kfree(root);
+               return ERR_PTR(ret);
+       }
+
+       ret = btrfs_sysfs_add_root(root);
+       if (ret) {
+               brelse(root->node);
+               kfree(root->name);
+               kfree(root);
+               return ERR_PTR(ret);
+       }
+
        return root;
 }
 
@@ -433,6 +453,8 @@ struct btrfs_root *open_ctree(struct super_block *sb)
        INIT_RADIX_TREE(&fs_info->block_group_data_radix, GFP_KERNEL);
        INIT_LIST_HEAD(&fs_info->trans_list);
        INIT_LIST_HEAD(&fs_info->dead_roots);
+       memset(&fs_info->super_kobj, 0, sizeof(fs_info->super_kobj));
+       init_completion(&fs_info->kobj_unregister);
        sb_set_blocksize(sb, 4096);
        fs_info->running_transaction = NULL;
        fs_info->last_trans_committed = 0;
@@ -500,8 +522,10 @@ struct btrfs_root *open_ctree(struct super_block *sb)
 
        fs_info->generation = btrfs_super_generation(disk_super) + 1;
        ret = btrfs_find_dead_roots(tree_root);
-       if (ret)
+       if (ret) {
+               mutex_unlock(&fs_info->fs_mutex);
                goto fail_tree_root;
+       }
        mutex_unlock(&fs_info->fs_mutex);
        return tree_root;
 
@@ -553,12 +577,15 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
 {
        radix_tree_delete(&fs_info->fs_roots_radix,
                          (unsigned long)root->root_key.objectid);
+       btrfs_sysfs_del_root(root);
        if (root->inode)
                iput(root->inode);
        if (root->node)
                brelse(root->node);
        if (root->commit_root)
                brelse(root->commit_root);
+       if (root->name)
+               kfree(root->name);
        kfree(root);
        return 0;
 }
index 5261733b8735388173b8f61090c1bd56f9c8799c..da6bb72750f251c29b071e17e71dd244bee331a3 100644 (file)
@@ -66,7 +66,8 @@ struct buffer_head *btrfs_find_tree_block(struct btrfs_root *root, u64 blocknr);
 int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len,
                    char *result);
 struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
-                                     struct btrfs_key *location);
+                                     struct btrfs_key *location,
+                                     const char *name, int namelen);
 struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
                                               struct btrfs_key *location);
 u64 bh_blocknr(struct buffer_head *bh);
index c31e84d426533209872ac54a1cbfb8e5dc4db48b..ff3f7c2be605dc3006200cc4974f33028e615df2 100644 (file)
@@ -858,16 +858,23 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_set_extent_refs(ei, refs);
        btrfs_mark_buffer_dirty(path->nodes[0]);
        if (refs == 0) {
-               u64 super_blocks_used;
+               u64 super_blocks_used, root_blocks_used;
 
                if (pin) {
                        ret = pin_down_block(root, blocknr, 0);
                        BUG_ON(ret);
                }
 
+               /* block accounting for super block */
                super_blocks_used = btrfs_super_blocks_used(&info->super_copy);
                btrfs_set_super_blocks_used(&info->super_copy,
                                            super_blocks_used - num_blocks);
+
+               /* block accounting for root item */
+               root_blocks_used = btrfs_root_blocks_used(&root->root_item);
+               btrfs_set_root_blocks_used(&root->root_item,
+                                          root_blocks_used - num_blocks);
+
                ret = btrfs_del_item(trans, extent_root, path);
                if (ret) {
                        return ret;
@@ -1175,7 +1182,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
 {
        int ret;
        int pending_ret;
-       u64 super_blocks_used;
+       u64 super_blocks_used, root_blocks_used;
        u64 search_start = 0;
        struct btrfs_fs_info *info = root->fs_info;
        struct btrfs_root *extent_root = info->extent_root;
@@ -1193,10 +1200,16 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
        if (ret)
                return ret;
 
+       /* block accounting for super block */
        super_blocks_used = btrfs_super_blocks_used(&info->super_copy);
        btrfs_set_super_blocks_used(&info->super_copy, super_blocks_used +
                                    num_blocks);
 
+       /* block accounting for root item */
+       root_blocks_used = btrfs_root_blocks_used(&root->root_item);
+       btrfs_set_root_blocks_used(&root->root_item, root_blocks_used +
+                                  num_blocks);
+
        if (root == extent_root) {
                BUG_ON(num_blocks != 1);
                set_radix_bit(&root->fs_info->extent_ins_radix, ins->objectid);
index dc181089aa74fc0f044537310fd21f683d9eca21..2e3918e6049eb133c548d241536e38b447d61553 100644 (file)
@@ -733,7 +733,8 @@ out:
  */
 static int fixup_tree_root_location(struct btrfs_root *root,
                             struct btrfs_key *location,
-                            struct btrfs_root **sub_root)
+                            struct btrfs_root **sub_root,
+                            struct dentry *dentry)
 {
        struct btrfs_path *path;
        struct btrfs_root_item *ri;
@@ -747,7 +748,9 @@ static int fixup_tree_root_location(struct btrfs_root *root,
        BUG_ON(!path);
        mutex_lock(&root->fs_info->fs_mutex);
 
-       *sub_root = btrfs_read_fs_root(root->fs_info, location);
+       *sub_root = btrfs_read_fs_root(root->fs_info, location,
+                                       dentry->d_name.name,
+                                       dentry->d_name.len);
        if (IS_ERR(*sub_root))
                return PTR_ERR(*sub_root);
 
@@ -812,7 +815,8 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                return ERR_PTR(ret);
        inode = NULL;
        if (location.objectid) {
-               ret = fixup_tree_root_location(root, &location, &sub_root);
+               ret = fixup_tree_root_location(root, &location, &sub_root,
+                                               dentry);
                if (ret < 0)
                        return ERR_PTR(ret);
                if (ret > 0)
@@ -1829,6 +1833,7 @@ static int create_subvol(struct btrfs_root *root, char *name, int namelen)
 
        btrfs_set_root_blocknr(&root_item, bh_blocknr(subvol));
        btrfs_set_root_refs(&root_item, 1);
+       btrfs_set_root_blocks_used(&root_item, 0);
        memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress));
        root_item.drop_level = 0;
        brelse(subvol);
@@ -1865,7 +1870,7 @@ static int create_subvol(struct btrfs_root *root, char *name, int namelen)
        if (ret)
                goto fail_commit;
 
-       new_root = btrfs_read_fs_root(root->fs_info, &key);
+       new_root = btrfs_read_fs_root(root->fs_info, &key, name, namelen);
        BUG_ON(!new_root);
 
        trans = btrfs_start_transaction(new_root, 1);
index 7da95538745f3c7f58a6f16f4e0c12653ba7cbad..a68101ae1a3b600f10b9286b5e867f30f00227e8 100644 (file)
@@ -45,12 +45,14 @@ static struct super_operations btrfs_super_ops;
 static void btrfs_put_super (struct super_block * sb)
 {
        struct btrfs_root *root = btrfs_sb(sb);
+       struct btrfs_fs_info *fs = root->fs_info;
        int ret;
 
        ret = close_ctree(root);
        if (ret) {
                printk("close ctree returns %d\n", ret);
        }
+       btrfs_sysfs_del_super(fs);
        sb->s_fs_info = NULL;
 }
 
@@ -101,6 +103,12 @@ static int btrfs_fill_super(struct super_block * sb, void * data, int silent)
                err = -ENOMEM;
                goto fail_close;
        }
+
+       /* this does the super kobj at the same time */
+       err = btrfs_sysfs_add_super(tree_root->fs_info);
+       if (err)
+               goto fail_close;
+
        sb->s_root = root_dentry;
        btrfs_transaction_queue_work(tree_root, HZ * 30);
        return 0;
@@ -182,6 +190,11 @@ static struct super_operations btrfs_super_ops = {
 static int __init init_btrfs_fs(void)
 {
        int err;
+
+       err = btrfs_init_sysfs();
+       if (err)
+               return err;
+
        btrfs_init_transaction_sys();
        err = btrfs_init_cachep();
        if (err)
@@ -196,6 +209,7 @@ static void __exit exit_btrfs_fs(void)
        btrfs_destroy_cachep();
        extent_map_exit();
        unregister_filesystem(&btrfs_fs_type);
+       btrfs_exit_sysfs();
 }
 
 module_init(init_btrfs_fs)
index db8917e5b2565200bc1a51b0c79a1800288beb1c..2058783373ebf05c9983c1b9e36f3248940c2432 100644 (file)
  * Boston, MA 021110-1307, USA.
  */
 
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
+
+static ssize_t root_blocks_used_show(struct btrfs_root *root, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)btrfs_root_blocks_used(&root->root_item));
+}
+
+static ssize_t root_block_limit_show(struct btrfs_root *root, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)btrfs_root_block_limit(&root->root_item));
+}
+
+static ssize_t super_blocks_used_show(struct btrfs_fs_info *fs, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)btrfs_super_blocks_used(fs->disk_super));
+}
+
+static ssize_t super_total_blocks_show(struct btrfs_fs_info *fs, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)btrfs_super_total_blocks(fs->disk_super));
+}
+
+static ssize_t super_blocksize_show(struct btrfs_fs_info *fs, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)btrfs_super_blocksize(fs->disk_super));
+}
+
+/* this is for root attrs (subvols/snapshots) */
+struct btrfs_root_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct btrfs_root *, char *);
+       ssize_t (*store)(struct btrfs_root *, const char *, size_t);
+};
+
+#define ROOT_ATTR(name, mode, show, store) \
+static struct btrfs_root_attr btrfs_root_attr_##name = __ATTR(name, mode, show, store)
+
+ROOT_ATTR(blocks_used, 0444,   root_blocks_used_show,  NULL);
+ROOT_ATTR(block_limit, 0644,   root_block_limit_show,  NULL);
+
+static struct attribute *btrfs_root_attrs[] = {
+       &btrfs_root_attr_blocks_used.attr,
+       &btrfs_root_attr_block_limit.attr,
+       NULL,
+};
+
+/* this is for super attrs (actual full fs) */
+struct btrfs_super_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct btrfs_fs_info *, char *);
+       ssize_t (*store)(struct btrfs_fs_info *, const char *, size_t);
+};
+
+#define SUPER_ATTR(name, mode, show, store) \
+static struct btrfs_super_attr btrfs_super_attr_##name = __ATTR(name, mode, show, store)
+
+SUPER_ATTR(blocks_used,                0444,   super_blocks_used_show,         NULL);
+SUPER_ATTR(total_blocks,       0444,   super_total_blocks_show,        NULL);
+SUPER_ATTR(blocksize,          0444,   super_blocksize_show,           NULL);
+
+static struct attribute *btrfs_super_attrs[] = {
+       &btrfs_super_attr_blocks_used.attr,
+       &btrfs_super_attr_total_blocks.attr,
+       &btrfs_super_attr_blocksize.attr,
+       NULL,
+};
+
+static ssize_t btrfs_super_attr_show(struct kobject *kobj,
+                                   struct attribute *attr, char *buf)
+{
+       struct btrfs_fs_info *fs = container_of(kobj, struct btrfs_fs_info,
+                                               super_kobj);
+       struct btrfs_super_attr *a = container_of(attr,
+                                                 struct btrfs_super_attr,
+                                                 attr);
+
+       return a->show ? a->show(fs, buf) : 0;
+}
+
+static ssize_t btrfs_super_attr_store(struct kobject *kobj,
+                                    struct attribute *attr,
+                                    const char *buf, size_t len)
+{
+       struct btrfs_fs_info *fs = container_of(kobj, struct btrfs_fs_info,
+                                               super_kobj);
+       struct btrfs_super_attr *a = container_of(attr,
+                                                 struct btrfs_super_attr,
+                                                 attr);
+
+       return a->store ? a->store(fs, buf, len) : 0;
+}
+
+static ssize_t btrfs_root_attr_show(struct kobject *kobj,
+                                   struct attribute *attr, char *buf)
+{
+       struct btrfs_root *root = container_of(kobj, struct btrfs_root,
+                                               root_kobj);
+       struct btrfs_root_attr *a = container_of(attr,
+                                                struct btrfs_root_attr,
+                                                attr);
+
+       return a->show ? a->show(root, buf) : 0;
+}
+
+static ssize_t btrfs_root_attr_store(struct kobject *kobj,
+                                    struct attribute *attr,
+                                    const char *buf, size_t len)
+{
+       struct btrfs_root *root = container_of(kobj, struct btrfs_root,
+                                               root_kobj);
+       struct btrfs_root_attr *a = container_of(attr,
+                                                struct btrfs_root_attr,
+                                                attr);
+       return a->store ? a->store(root, buf, len) : 0;
+}
+
+static void btrfs_super_release(struct kobject *kobj)
+{
+       struct btrfs_fs_info *fs = container_of(kobj, struct btrfs_fs_info,
+                                               super_kobj);
+       complete(&fs->kobj_unregister);
+}
+
+static void btrfs_root_release(struct kobject *kobj)
+{
+       struct btrfs_root *root = container_of(kobj, struct btrfs_root,
+                                               root_kobj);
+       complete(&root->kobj_unregister);
+}
+
+static struct sysfs_ops btrfs_super_attr_ops = {
+       .show   = btrfs_super_attr_show,
+       .store  = btrfs_super_attr_store,
+};
+
+static struct sysfs_ops btrfs_root_attr_ops = {
+       .show   = btrfs_root_attr_show,
+       .store  = btrfs_root_attr_store,
+};
+
+static struct kobj_type btrfs_root_ktype = {
+       .default_attrs  = btrfs_root_attrs,
+       .sysfs_ops      = &btrfs_root_attr_ops,
+       .release        = btrfs_root_release,
+};
+
+static struct kobj_type btrfs_super_ktype = {
+       .default_attrs  = btrfs_super_attrs,
+       .sysfs_ops      = &btrfs_super_attr_ops,
+       .release        = btrfs_super_release,
+};
+
+static struct kset btrfs_kset = {
+       .kobj   = {.name = "btrfs"},
+};
+
+int btrfs_sysfs_add_super(struct btrfs_fs_info *fs)
+{
+       int error;
+
+       fs->super_kobj.kset = &btrfs_kset;
+       fs->super_kobj.ktype = &btrfs_super_ktype;
+
+       error = kobject_set_name(&fs->super_kobj, "%s",
+                                fs->sb->s_id);
+       if (error)
+               goto fail;
+
+       error = kobject_register(&fs->super_kobj);
+       if (error)
+               goto fail;
+
+       return 0;
+
+fail:
+       printk(KERN_ERR "btrfs: sysfs creation for super failed\n");
+       return error;
+}
+
+int btrfs_sysfs_add_root(struct btrfs_root *root)
+{
+       int error;
+
+       root->root_kobj.ktype = &btrfs_root_ktype;
+       root->root_kobj.parent = &root->fs_info->super_kobj;
+
+       error = kobject_set_name(&root->root_kobj, "%s", root->name);
+       if (error) {
+               goto fail;
+       }
+
+       error = kobject_register(&root->root_kobj);
+       if (error)
+               goto fail;
+
+       return 0;
+
+fail:
+       printk(KERN_ERR "btrfs: sysfs creation for root failed\n");
+       return error;
+}
+
+void btrfs_sysfs_del_root(struct btrfs_root *root)
+{
+       kobject_unregister(&root->root_kobj);
+       wait_for_completion(&root->kobj_unregister);
+}
+
+void btrfs_sysfs_del_super(struct btrfs_fs_info *fs)
+{
+       kobject_unregister(&fs->super_kobj);
+       wait_for_completion(&fs->kobj_unregister);
+}
+
+int btrfs_init_sysfs()
+{
+       kobj_set_kset_s(&btrfs_kset, fs_subsys);
+       return kset_register(&btrfs_kset);
+}
+
+void btrfs_exit_sysfs()
+{
+       kset_unregister(&btrfs_kset);
+}
index 18abea8027943023c0756e8d36d1ee30b70a5c30..2b15daa3a9f21f973c59e29eda7b5079b4173fd4 100644 (file)
@@ -236,6 +236,7 @@ static int wait_for_commit(struct btrfs_root *root,
 struct dirty_root {
        struct list_head list;
        struct btrfs_root *root;
+       struct btrfs_root *latest_root;
 };
 
 int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list)
@@ -278,6 +279,15 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans,
                                        btrfs_root_blocknr(&root->root_item));
                                brelse(root->commit_root);
                                root->commit_root = NULL;
+
+                               /* make sure to update the root on disk
+                                * so we get any updates to the block used
+                                * counts
+                                */
+                               err = btrfs_update_root(trans,
+                                               root->fs_info->tree_root,
+                                               &root->root_key,
+                                               &root->root_item);
                                continue;
                        }
                        dirty = kmalloc(sizeof(*dirty), GFP_NOFS);
@@ -291,6 +301,7 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans,
 
                        memcpy(dirty->root, root, sizeof(*root));
                        dirty->root->node = root->commit_root;
+                       dirty->latest_root = root;
                        root->commit_root = NULL;
 
                        root->root_key.offset = root->fs_info->generation;
@@ -384,20 +395,29 @@ static int drop_dirty_roots(struct btrfs_root *tree_root,
 {
        struct dirty_root *dirty;
        struct btrfs_trans_handle *trans;
+       u64 num_blocks;
+       u64 blocks_used;
        int ret = 0;
        int err;
 
        while(!list_empty(list)) {
+               struct btrfs_root *root;
+
                mutex_lock(&tree_root->fs_info->fs_mutex);
                dirty = list_entry(list->next, struct dirty_root, list);
                list_del_init(&dirty->list);
 
+               num_blocks = btrfs_root_blocks_used(&dirty->root->root_item);
+               root = dirty->latest_root;
+
                while(1) {
                        trans = btrfs_start_transaction(tree_root, 1);
+
                        ret = btrfs_drop_snapshot(trans, dirty->root);
                        if (ret != -EAGAIN) {
                                break;
                        }
+
                        err = btrfs_update_root(trans,
                                        tree_root,
                                        &dirty->root->root_key,
@@ -414,9 +434,19 @@ static int drop_dirty_roots(struct btrfs_root *tree_root,
                        mutex_lock(&tree_root->fs_info->fs_mutex);
                }
                BUG_ON(ret);
+
+               num_blocks -= btrfs_root_blocks_used(&dirty->root->root_item);
+               blocks_used = btrfs_root_blocks_used(&root->root_item);
+               if (num_blocks) {
+                       record_root_in_trans(root);
+                       btrfs_set_root_blocks_used(&root->root_item,
+                                                  blocks_used - num_blocks);
+               }
                ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key);
-               if (ret)
+               if (ret) {
+                       BUG();
                        break;
+               }
                ret = btrfs_end_transaction(trans, tree_root);
                BUG_ON(ret);
 
@@ -534,10 +564,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        wake_up(&cur_trans->commit_wait);
        put_transaction(cur_trans);
        put_transaction(cur_trans);
+
        if (root->fs_info->closing)
                list_splice_init(&root->fs_info->dead_roots, &dirty_fs_roots);
        else
                list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
+
        mutex_unlock(&root->fs_info->trans_mutex);
        kmem_cache_free(btrfs_trans_handle_cachep, trans);