extern struct kmem_cache *btrfs_trans_handle_cachep;
extern struct kmem_cache *btrfs_transaction_cachep;
+static struct workqueue_struct *trans_wq;
+
#define BTRFS_ROOT_TRANS_TAG 0
-#define TRANS_MAGIC 0xE1E10E
static void put_transaction(struct btrfs_transaction *transaction)
{
WARN_ON(transaction->use_count == 0);
transaction->use_count--;
- WARN_ON(transaction->magic != TRANS_MAGIC);
if (transaction->use_count == 0) {
WARN_ON(total_trans == 0);
total_trans--;
+ list_del_init(&transaction->list);
memset(transaction, 0, sizeof(*transaction));
kmem_cache_free(btrfs_transaction_cachep, transaction);
}
cur_trans->transid = root->fs_info->generation;
init_waitqueue_head(&cur_trans->writer_wait);
init_waitqueue_head(&cur_trans->commit_wait);
- cur_trans->magic = TRANS_MAGIC;
cur_trans->in_commit = 0;
cur_trans->use_count = 1;
cur_trans->commit_done = 0;
+ cur_trans->start_time = get_seconds();
+ list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
+ init_bit_radix(&cur_trans->dirty_pages);
}
cur_trans->num_writers++;
return 0;
h->transaction = root->fs_info->running_transaction;
h->blocks_reserved = num_blocks;
h->blocks_used = 0;
+ h->block_group = NULL;
root->fs_info->running_transaction->use_count++;
mutex_unlock(&root->fs_info->trans_mutex);
- h->magic = h->magic2 = TRANS_MAGIC;
return h;
}
{
struct btrfs_transaction *cur_trans;
- WARN_ON(trans->magic != TRANS_MAGIC);
- WARN_ON(trans->magic2 != TRANS_MAGIC);
mutex_lock(&root->fs_info->trans_mutex);
cur_trans = root->fs_info->running_transaction;
WARN_ON(cur_trans->num_writers < 1);
int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
- filemap_write_and_wait(root->fs_info->btree_inode->i_mapping);
- return 0;
+ unsigned long gang[16];
+ int ret;
+ int i;
+ int err;
+ int werr = 0;
+ struct page *page;
+ struct radix_tree_root *dirty_pages;
+ struct inode *btree_inode = root->fs_info->btree_inode;
+
+ if (!trans || !trans->transaction) {
+ return filemap_write_and_wait(btree_inode->i_mapping);
+ }
+ dirty_pages = &trans->transaction->dirty_pages;
+ while(1) {
+ ret = find_first_radix_bit(dirty_pages, gang,
+ 0, ARRAY_SIZE(gang));
+ if (!ret)
+ break;
+ for (i = 0; i < ret; i++) {
+ /* FIXME EIO */
+ clear_radix_bit(dirty_pages, gang[i]);
+ page = find_lock_page(btree_inode->i_mapping,
+ gang[i]);
+ if (!page)
+ continue;
+ err = write_one_page(page, 0);
+ if (err)
+ werr = err;
+ page_cache_release(page);
+ }
+ }
+ err = filemap_fdatawait(btree_inode->i_mapping);
+ if (err)
+ werr = err;
+ return werr;
}
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
btrfs_set_super_device_root(fs_info->disk_super,
bh_blocknr(dev_root->node));
}
+ btrfs_write_dirty_block_groups(trans, extent_root);
while(1) {
old_extent_block = btrfs_root_blocknr(&extent_root->root_item);
if (old_extent_block == bh_blocknr(extent_root->node))
&extent_root->root_key,
&extent_root->root_item);
BUG_ON(ret);
+ btrfs_write_dirty_block_groups(trans, extent_root);
}
return 0;
}
struct btrfs_root *root;
};
-int add_dirty_roots(struct btrfs_trans_handle *trans,
- struct radix_tree_root *radix, struct list_head *list)
+static int add_dirty_roots(struct btrfs_trans_handle *trans,
+ struct radix_tree_root *radix,
+ struct list_head *list)
{
struct dirty_root *dirty;
struct btrfs_root *gang[8];
return 0;
}
-int drop_dirty_roots(struct btrfs_root *tree_root, struct list_head *list)
+static int drop_dirty_roots(struct btrfs_root *tree_root,
+ struct list_head *list)
{
struct dirty_root *dirty;
struct btrfs_trans_handle *trans;
int ret;
-
while(!list_empty(list)) {
+ mutex_lock(&tree_root->fs_info->fs_mutex);
dirty = list_entry(list->next, struct dirty_root, list);
list_del_init(&dirty->list);
trans = btrfs_start_transaction(tree_root, 1);
ret = btrfs_end_transaction(trans, tree_root);
BUG_ON(ret);
kfree(dirty);
+ mutex_unlock(&tree_root->fs_info->fs_mutex);
}
return 0;
}
{
int ret = 0;
struct btrfs_transaction *cur_trans;
+ struct btrfs_transaction *prev_trans = NULL;
struct list_head dirty_fs_roots;
DEFINE_WAIT(wait);
BUG_ON(ret);
cur_trans = root->fs_info->running_transaction;
root->fs_info->running_transaction = NULL;
- btrfs_set_super_generation(root->fs_info->disk_super,
- root->fs_info->generation + 1);
+ if (cur_trans->list.prev != &root->fs_info->trans_list) {
+ prev_trans = list_entry(cur_trans->list.prev,
+ struct btrfs_transaction, list);
+ if (prev_trans->commit_done)
+ prev_trans = NULL;
+ else
+ prev_trans->use_count++;
+ }
mutex_unlock(&root->fs_info->trans_mutex);
+ mutex_unlock(&root->fs_info->fs_mutex);
ret = btrfs_write_and_wait_transaction(trans, root);
+ if (prev_trans) {
+ mutex_lock(&root->fs_info->trans_mutex);
+ wait_for_commit(root, prev_trans);
+ put_transaction(prev_trans);
+ mutex_unlock(&root->fs_info->trans_mutex);
+ }
+ btrfs_set_super_generation(root->fs_info->disk_super,
+ cur_trans->transid);
BUG_ON(ret);
-
write_ctree_super(trans, root);
+
+ mutex_lock(&root->fs_info->fs_mutex);
btrfs_finish_extent_commit(trans, root);
mutex_lock(&root->fs_info->trans_mutex);
cur_trans->commit_done = 1;
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);
- drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
+ if (root->fs_info->closing) {
+ mutex_unlock(&root->fs_info->fs_mutex);
+ drop_dirty_roots(root->fs_info->tree_root, &dirty_fs_roots);
+ mutex_lock(&root->fs_info->fs_mutex);
+ }
return ret;
}
+void btrfs_transaction_cleaner(struct work_struct *work)
+{
+ struct btrfs_fs_info *fs_info = container_of(work,
+ struct btrfs_fs_info,
+ trans_work.work);
+
+ struct btrfs_root *root = fs_info->tree_root;
+ struct btrfs_transaction *cur;
+ struct btrfs_trans_handle *trans;
+ struct list_head dirty_roots;
+ unsigned long now;
+ unsigned long delay = HZ * 30;
+ int ret;
+
+ INIT_LIST_HEAD(&dirty_roots);
+ mutex_lock(&root->fs_info->trans_mutex);
+ list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
+ mutex_unlock(&root->fs_info->trans_mutex);
+
+ if (!list_empty(&dirty_roots)) {
+ drop_dirty_roots(root, &dirty_roots);
+ }
+ mutex_lock(&root->fs_info->fs_mutex);
+ mutex_lock(&root->fs_info->trans_mutex);
+ cur = root->fs_info->running_transaction;
+ if (!cur) {
+ mutex_unlock(&root->fs_info->trans_mutex);
+ goto out;
+ }
+ now = get_seconds();
+ if (now < cur->start_time || now - cur->start_time < 30) {
+ mutex_unlock(&root->fs_info->trans_mutex);
+ delay = HZ * 5;
+ goto out;
+ }
+ mutex_unlock(&root->fs_info->trans_mutex);
+ trans = btrfs_start_transaction(root, 1);
+ ret = btrfs_commit_transaction(trans, root);
+out:
+ mutex_unlock(&root->fs_info->fs_mutex);
+ btrfs_transaction_queue_work(root, delay);
+}
+
+void btrfs_transaction_queue_work(struct btrfs_root *root, int delay)
+{
+ queue_delayed_work(trans_wq, &root->fs_info->trans_work, delay);
+}
+
+void btrfs_transaction_flush_work(struct btrfs_root *root)
+{
+ cancel_rearming_delayed_workqueue(trans_wq, &root->fs_info->trans_work);
+ flush_workqueue(trans_wq);
+}
+
+void __init btrfs_init_transaction_sys(void)
+{
+ trans_wq = create_workqueue("btrfs");
+}
+
+void __exit btrfs_exit_transaction_sys(void)
+{
+ destroy_workqueue(trans_wq);
+}
+