]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/commitdiff
Btrfs: Metadata ENOSPC handling for tree log
authorYan, Zheng <zheng.yan@oracle.com>
Sun, 16 May 2010 14:49:59 +0000 (10:49 -0400)
committerChris Mason <chris.mason@oracle.com>
Tue, 25 May 2010 14:34:53 +0000 (10:34 -0400)
Previous patches make the allocater return -ENOSPC if there is no
unreserved free metadata space. This patch updates tree log code
and various other places to propagate/handle the ENOSPC error.

Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/file-item.c
fs/btrfs/tree-log.c
fs/btrfs/tree-log.h

index 82955b73a962937ef4f14b50bdcad4d379c64567..a8772b5a9cb55737287622e29bce31c9d59b92ef 100644 (file)
@@ -972,42 +972,6 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
        return 0;
 }
 
-int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
-                            struct btrfs_fs_info *fs_info)
-{
-       struct extent_buffer *eb;
-       struct btrfs_root *log_root_tree = fs_info->log_root_tree;
-       u64 start = 0;
-       u64 end = 0;
-       int ret;
-
-       if (!log_root_tree)
-               return 0;
-
-       while (1) {
-               ret = find_first_extent_bit(&log_root_tree->dirty_log_pages,
-                               0, &start, &end, EXTENT_DIRTY | EXTENT_NEW);
-               if (ret)
-                       break;
-
-               clear_extent_bits(&log_root_tree->dirty_log_pages, start, end,
-                                 EXTENT_DIRTY | EXTENT_NEW, GFP_NOFS);
-       }
-       eb = fs_info->log_root_tree->node;
-
-       WARN_ON(btrfs_header_level(eb) != 0);
-       WARN_ON(btrfs_header_nritems(eb) != 0);
-
-       ret = btrfs_free_reserved_extent(fs_info->tree_root,
-                               eb->start, eb->len);
-       BUG_ON(ret);
-
-       free_extent_buffer(eb);
-       kfree(fs_info->log_root_tree);
-       fs_info->log_root_tree = NULL;
-       return 0;
-}
-
 static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
                                         struct btrfs_fs_info *fs_info)
 {
index c958ecbc19168ef0e7ef8e7bdd123f5f527b55f7..2c064eba6f09264473fc0c3bc51c23fca45b9e08 100644 (file)
@@ -95,8 +95,6 @@ int btrfs_congested_async(struct btrfs_fs_info *info, int iodone);
 unsigned long btrfs_async_submit_limit(struct btrfs_fs_info *info);
 int btrfs_write_tree_block(struct extent_buffer *buf);
 int btrfs_wait_tree_block_writeback(struct extent_buffer *buf);
-int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
-                            struct btrfs_fs_info *fs_info);
 int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
                             struct btrfs_fs_info *fs_info);
 int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
index 54a255065aa3235a7672d64f5fcf506615ae8ef3..21aead39a76c481b00783fb11495f4a60ca9f60c 100644 (file)
@@ -657,6 +657,9 @@ again:
                goto found;
        }
        ret = PTR_ERR(item);
+       if (ret != -EFBIG && ret != -ENOENT)
+               goto fail_unlock;
+
        if (ret == -EFBIG) {
                u32 item_size;
                /* we found one, but it isn't big enough yet */
index af57dd2b43d429777ff2530dfbc978ed33f76e2a..fb102a9aee9cc1ee3213d04bc1bae6fec6644f2d 100644 (file)
@@ -135,6 +135,7 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root)
 {
        int ret;
+       int err = 0;
 
        mutex_lock(&root->log_mutex);
        if (root->log_root) {
@@ -155,17 +156,19 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
        mutex_lock(&root->fs_info->tree_log_mutex);
        if (!root->fs_info->log_root_tree) {
                ret = btrfs_init_log_root_tree(trans, root->fs_info);
-               BUG_ON(ret);
+               if (ret)
+                       err = ret;
        }
-       if (!root->log_root) {
+       if (err == 0 && !root->log_root) {
                ret = btrfs_add_log_tree(trans, root);
-               BUG_ON(ret);
+               if (ret)
+                       err = ret;
        }
        mutex_unlock(&root->fs_info->tree_log_mutex);
        root->log_batch++;
        atomic_inc(&root->log_writers);
        mutex_unlock(&root->log_mutex);
-       return 0;
+       return err;
 }
 
 /*
@@ -376,7 +379,7 @@ insert:
                        BUG_ON(ret);
                }
        } else if (ret) {
-               BUG();
+               return ret;
        }
        dst_ptr = btrfs_item_ptr_offset(path->nodes[0],
                                        path->slots[0]);
@@ -1699,9 +1702,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                next = btrfs_find_create_tree_block(root, bytenr, blocksize);
 
-               wc->process_func(root, next, wc, ptr_gen);
-
                if (*level == 1) {
+                       wc->process_func(root, next, wc, ptr_gen);
+
                        path->slots[*level]++;
                        if (wc->free) {
                                btrfs_read_buffer(next, ptr_gen);
@@ -1734,35 +1737,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
        WARN_ON(*level < 0);
        WARN_ON(*level >= BTRFS_MAX_LEVEL);
 
-       if (path->nodes[*level] == root->node)
-               parent = path->nodes[*level];
-       else
-               parent = path->nodes[*level + 1];
-
-       bytenr = path->nodes[*level]->start;
-
-       blocksize = btrfs_level_size(root, *level);
-       root_owner = btrfs_header_owner(parent);
-       root_gen = btrfs_header_generation(parent);
-
-       wc->process_func(root, path->nodes[*level], wc,
-                        btrfs_header_generation(path->nodes[*level]));
-
-       if (wc->free) {
-               next = path->nodes[*level];
-               btrfs_tree_lock(next);
-               clean_tree_block(trans, root, next);
-               btrfs_set_lock_blocking(next);
-               btrfs_wait_tree_block_writeback(next);
-               btrfs_tree_unlock(next);
-
-               WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
-               ret = btrfs_free_reserved_extent(root, bytenr, blocksize);
-               BUG_ON(ret);
-       }
-       free_extent_buffer(path->nodes[*level]);
-       path->nodes[*level] = NULL;
-       *level += 1;
+       path->slots[*level] = btrfs_header_nritems(path->nodes[*level]);
 
        cond_resched();
        return 0;
@@ -1781,7 +1756,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
 
        for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
                slot = path->slots[i];
-               if (slot < btrfs_header_nritems(path->nodes[i]) - 1) {
+               if (slot + 1 < btrfs_header_nritems(path->nodes[i])) {
                        struct extent_buffer *node;
                        node = path->nodes[i];
                        path->slots[i]++;
@@ -2047,7 +2022,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        mutex_unlock(&log_root_tree->log_mutex);
 
        ret = update_log_root(trans, log);
-       BUG_ON(ret);
 
        mutex_lock(&log_root_tree->log_mutex);
        if (atomic_dec_and_test(&log_root_tree->log_writers)) {
@@ -2056,6 +2030,15 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
                        wake_up(&log_root_tree->log_writer_wait);
        }
 
+       if (ret) {
+               BUG_ON(ret != -ENOSPC);
+               root->fs_info->last_trans_log_full_commit = trans->transid;
+               btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
+               mutex_unlock(&log_root_tree->log_mutex);
+               ret = -EAGAIN;
+               goto out;
+       }
+
        index2 = log_root_tree->log_transid % 2;
        if (atomic_read(&log_root_tree->log_commit[index2])) {
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
@@ -2129,15 +2112,10 @@ out:
        return 0;
 }
 
-/*
- * free all the extents used by the tree log.  This should be called
- * at commit time of the full transaction
- */
-int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
+static void free_log_tree(struct btrfs_trans_handle *trans,
+                         struct btrfs_root *log)
 {
        int ret;
-       struct btrfs_root *log;
-       struct key;
        u64 start;
        u64 end;
        struct walk_control wc = {
@@ -2145,10 +2123,6 @@ int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
                .process_func = process_one_buffer
        };
 
-       if (!root->log_root || root->fs_info->log_root_recovering)
-               return 0;
-
-       log = root->log_root;
        ret = walk_log_tree(trans, log, &wc);
        BUG_ON(ret);
 
@@ -2162,14 +2136,30 @@ int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
                                  EXTENT_DIRTY | EXTENT_NEW, GFP_NOFS);
        }
 
-       if (log->log_transid > 0) {
-               ret = btrfs_del_root(trans, root->fs_info->log_root_tree,
-                                    &log->root_key);
-               BUG_ON(ret);
-       }
-       root->log_root = NULL;
        free_extent_buffer(log->node);
        kfree(log);
+}
+
+/*
+ * free all the extents used by the tree log.  This should be called
+ * at commit time of the full transaction
+ */
+int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
+{
+       if (root->log_root) {
+               free_log_tree(trans, root->log_root);
+               root->log_root = NULL;
+       }
+       return 0;
+}
+
+int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
+                            struct btrfs_fs_info *fs_info)
+{
+       if (fs_info->log_root_tree) {
+               free_log_tree(trans, fs_info->log_root_tree);
+               fs_info->log_root_tree = NULL;
+       }
        return 0;
 }
 
@@ -2203,6 +2193,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
        struct btrfs_dir_item *di;
        struct btrfs_path *path;
        int ret;
+       int err = 0;
        int bytes_del = 0;
 
        if (BTRFS_I(dir)->logged_trans < trans->transid)
@@ -2218,7 +2209,11 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
        path = btrfs_alloc_path();
        di = btrfs_lookup_dir_item(trans, log, path, dir->i_ino,
                                   name, name_len, -1);
-       if (di && !IS_ERR(di)) {
+       if (IS_ERR(di)) {
+               err = PTR_ERR(di);
+               goto fail;
+       }
+       if (di) {
                ret = btrfs_delete_one_dir_name(trans, log, path, di);
                bytes_del += name_len;
                BUG_ON(ret);
@@ -2226,7 +2221,11 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
        btrfs_release_path(log, path);
        di = btrfs_lookup_dir_index_item(trans, log, path, dir->i_ino,
                                         index, name, name_len, -1);
-       if (di && !IS_ERR(di)) {
+       if (IS_ERR(di)) {
+               err = PTR_ERR(di);
+               goto fail;
+       }
+       if (di) {
                ret = btrfs_delete_one_dir_name(trans, log, path, di);
                bytes_del += name_len;
                BUG_ON(ret);
@@ -2244,6 +2243,10 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
                btrfs_release_path(log, path);
 
                ret = btrfs_search_slot(trans, log, &key, path, 0, 1);
+               if (ret < 0) {
+                       err = ret;
+                       goto fail;
+               }
                if (ret == 0) {
                        struct btrfs_inode_item *item;
                        u64 i_size;
@@ -2261,9 +2264,13 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
                        ret = 0;
                btrfs_release_path(log, path);
        }
-
+fail:
        btrfs_free_path(path);
        mutex_unlock(&BTRFS_I(dir)->log_mutex);
+       if (ret == -ENOSPC) {
+               root->fs_info->last_trans_log_full_commit = trans->transid;
+               ret = 0;
+       }
        btrfs_end_log_trans(root);
 
        return 0;
@@ -2291,6 +2298,10 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        ret = btrfs_del_inode_ref(trans, log, name, name_len, inode->i_ino,
                                  dirid, &index);
        mutex_unlock(&BTRFS_I(inode)->log_mutex);
+       if (ret == -ENOSPC) {
+               root->fs_info->last_trans_log_full_commit = trans->transid;
+               ret = 0;
+       }
        btrfs_end_log_trans(root);
 
        return ret;
@@ -2318,7 +2329,8 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
        else
                key.type = BTRFS_DIR_LOG_INDEX_KEY;
        ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*item));
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
                              struct btrfs_dir_log_item);
@@ -2343,6 +2355,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
        struct btrfs_key max_key;
        struct btrfs_root *log = root->log_root;
        struct extent_buffer *src;
+       int err = 0;
        int ret;
        int i;
        int nritems;
@@ -2405,6 +2418,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                        ret = overwrite_item(trans, log, dst_path,
                                             path->nodes[0], path->slots[0],
                                             &tmp);
+                       if (ret) {
+                               err = ret;
+                               goto done;
+                       }
                }
        }
        btrfs_release_path(root, path);
@@ -2432,7 +2449,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                                goto done;
                        ret = overwrite_item(trans, log, dst_path, src, i,
                                             &min_key);
-                       BUG_ON(ret);
+                       if (ret) {
+                               err = ret;
+                               goto done;
+                       }
                }
                path->slots[0] = nritems;
 
@@ -2454,22 +2474,30 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
                        ret = overwrite_item(trans, log, dst_path,
                                             path->nodes[0], path->slots[0],
                                             &tmp);
-
-                       BUG_ON(ret);
-                       last_offset = tmp.offset;
+                       if (ret)
+                               err = ret;
+                       else
+                               last_offset = tmp.offset;
                        goto done;
                }
        }
 done:
-       *last_offset_ret = last_offset;
        btrfs_release_path(root, path);
        btrfs_release_path(log, dst_path);
 
-       /* insert the log range keys to indicate where the log is valid */
-       ret = insert_dir_log_key(trans, log, path, key_type, inode->i_ino,
-                                first_offset, last_offset);
-       BUG_ON(ret);
-       return 0;
+       if (err == 0) {
+               *last_offset_ret = last_offset;
+               /*
+                * insert the log range keys to indicate where the log
+                * is valid
+                */
+               ret = insert_dir_log_key(trans, log, path, key_type,
+                                        inode->i_ino, first_offset,
+                                        last_offset);
+               if (ret)
+                       err = ret;
+       }
+       return err;
 }
 
 /*
@@ -2501,7 +2529,8 @@ again:
                ret = log_dir_items(trans, root, inode, path,
                                    dst_path, key_type, min_key,
                                    &max_key);
-               BUG_ON(ret);
+               if (ret)
+                       return ret;
                if (max_key == (u64)-1)
                        break;
                min_key = max_key + 1;
@@ -2535,8 +2564,8 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
 
        while (1) {
                ret = btrfs_search_slot(trans, log, &key, path, -1, 1);
-
-               if (ret != 1)
+               BUG_ON(ret == 0);
+               if (ret < 0)
                        break;
 
                if (path->slots[0] == 0)
@@ -2554,7 +2583,7 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
                btrfs_release_path(log, path);
        }
        btrfs_release_path(log, path);
-       return 0;
+       return ret;
 }
 
 static noinline int copy_items(struct btrfs_trans_handle *trans,
@@ -2587,7 +2616,10 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
        }
        ret = btrfs_insert_empty_items(trans, log, dst_path,
                                       ins_keys, ins_sizes, nr);
-       BUG_ON(ret);
+       if (ret) {
+               kfree(ins_data);
+               return ret;
+       }
 
        for (i = 0; i < nr; i++, dst_path->slots[0]++) {
                dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
@@ -2660,16 +2692,17 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
         * we have to do this after the loop above to avoid changing the
         * log tree while trying to change the log tree.
         */
+       ret = 0;
        while (!list_empty(&ordered_sums)) {
                struct btrfs_ordered_sum *sums = list_entry(ordered_sums.next,
                                                   struct btrfs_ordered_sum,
                                                   list);
-               ret = btrfs_csum_file_blocks(trans, log, sums);
-               BUG_ON(ret);
+               if (!ret)
+                       ret = btrfs_csum_file_blocks(trans, log, sums);
                list_del(&sums->list);
                kfree(sums);
        }
-       return 0;
+       return ret;
 }
 
 /* log a single inode in the tree log.
@@ -2697,6 +2730,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
        struct btrfs_root *log = root->log_root;
        struct extent_buffer *src = NULL;
        u32 size;
+       int err = 0;
        int ret;
        int nritems;
        int ins_start_slot = 0;
@@ -2739,7 +2773,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
        } else {
                ret = btrfs_truncate_inode_items(trans, log, inode, 0, 0);
        }
-       BUG_ON(ret);
+       if (ret) {
+               err = ret;
+               goto out_unlock;
+       }
        path->keep_locks = 1;
 
        while (1) {
@@ -2768,7 +2805,10 @@ again:
 
                ret = copy_items(trans, log, dst_path, src, ins_start_slot,
                                 ins_nr, inode_only);
-               BUG_ON(ret);
+               if (ret) {
+                       err = ret;
+                       goto out_unlock;
+               }
                ins_nr = 1;
                ins_start_slot = path->slots[0];
 next_slot:
@@ -2784,7 +2824,10 @@ next_slot:
                        ret = copy_items(trans, log, dst_path, src,
                                         ins_start_slot,
                                         ins_nr, inode_only);
-                       BUG_ON(ret);
+                       if (ret) {
+                               err = ret;
+                               goto out_unlock;
+                       }
                        ins_nr = 0;
                }
                btrfs_release_path(root, path);
@@ -2802,7 +2845,10 @@ next_slot:
                ret = copy_items(trans, log, dst_path, src,
                                 ins_start_slot,
                                 ins_nr, inode_only);
-               BUG_ON(ret);
+               if (ret) {
+                       err = ret;
+                       goto out_unlock;
+               }
                ins_nr = 0;
        }
        WARN_ON(ins_nr);
@@ -2810,14 +2856,18 @@ next_slot:
                btrfs_release_path(root, path);
                btrfs_release_path(log, dst_path);
                ret = log_directory_changes(trans, root, inode, path, dst_path);
-               BUG_ON(ret);
+               if (ret) {
+                       err = ret;
+                       goto out_unlock;
+               }
        }
        BTRFS_I(inode)->logged_trans = trans->transid;
+out_unlock:
        mutex_unlock(&BTRFS_I(inode)->log_mutex);
 
        btrfs_free_path(path);
        btrfs_free_path(dst_path);
-       return 0;
+       return err;
 }
 
 /*
@@ -2942,10 +2992,13 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
                goto end_no_trans;
        }
 
-       start_log_trans(trans, root);
+       ret = start_log_trans(trans, root);
+       if (ret)
+               goto end_trans;
 
        ret = btrfs_log_inode(trans, root, inode, inode_only);
-       BUG_ON(ret);
+       if (ret)
+               goto end_trans;
 
        /*
         * for regular files, if its inode is already on disk, we don't
@@ -2955,8 +3008,10 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
         */
        if (S_ISREG(inode->i_mode) &&
            BTRFS_I(inode)->generation <= last_committed &&
-           BTRFS_I(inode)->last_unlink_trans <= last_committed)
-                       goto no_parent;
+           BTRFS_I(inode)->last_unlink_trans <= last_committed) {
+               ret = 0;
+               goto end_trans;
+       }
 
        inode_only = LOG_INODE_EXISTS;
        while (1) {
@@ -2970,15 +3025,21 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
                if (BTRFS_I(inode)->generation >
                    root->fs_info->last_trans_committed) {
                        ret = btrfs_log_inode(trans, root, inode, inode_only);
-                       BUG_ON(ret);
+                       if (ret)
+                               goto end_trans;
                }
                if (IS_ROOT(parent))
                        break;
 
                parent = parent->d_parent;
        }
-no_parent:
        ret = 0;
+end_trans:
+       if (ret < 0) {
+               BUG_ON(ret != -ENOSPC);
+               root->fs_info->last_trans_log_full_commit = trans->transid;
+               ret = 1;
+       }
        btrfs_end_log_trans(root);
 end_no_trans:
        return ret;
@@ -3020,7 +3081,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
        path = btrfs_alloc_path();
        BUG_ON(!path);
 
-       trans = btrfs_start_transaction(fs_info->tree_root, 1);
+       trans = btrfs_start_transaction(fs_info->tree_root, 0);
 
        wc.trans = trans;
        wc.pin = 1;
index 0776eacb50830f14c405bd44d8d8817dc9554295..3dfae84c8cc8bc1fada072bb45a3676dcedb323d 100644 (file)
@@ -25,6 +25,8 @@
 int btrfs_sync_log(struct btrfs_trans_handle *trans,
                   struct btrfs_root *root);
 int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root);
+int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
+                            struct btrfs_fs_info *fs_info);
 int btrfs_recover_log_trees(struct btrfs_root *tree_root);
 int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
                          struct btrfs_root *root, struct dentry *dentry);