Btrfs: add better -ENOSPC handling
[deliverable/linux.git] / fs / btrfs / inode.c
index 5792816d4fca8f40c5be6dad4945b8f6dfc29b38..7d4f948bc22a5b5c811b93b385ff36d5b49e8977 100644 (file)
@@ -101,34 +101,6 @@ static int btrfs_init_inode_security(struct inode *inode,  struct inode *dir)
        return err;
 }
 
-/*
- * a very lame attempt at stopping writes when the FS is 85% full.  There
- * are countless ways this is incorrect, but it is better than nothing.
- */
-int btrfs_check_free_space(struct btrfs_root *root, u64 num_required,
-                          int for_del)
-{
-       u64 total;
-       u64 used;
-       u64 thresh;
-       int ret = 0;
-
-       spin_lock(&root->fs_info->delalloc_lock);
-       total = btrfs_super_total_bytes(&root->fs_info->super_copy);
-       used = btrfs_super_bytes_used(&root->fs_info->super_copy);
-       if (for_del)
-               thresh = total * 90;
-       else
-               thresh = total * 85;
-
-       do_div(thresh, 100);
-
-       if (used + root->fs_info->delalloc_bytes + num_required > thresh)
-               ret = -ENOSPC;
-       spin_unlock(&root->fs_info->delalloc_lock);
-       return ret;
-}
-
 /*
  * this does all the hard work for inserting an inline extent into
  * the btree.  The caller should have done a btrfs_drop_extents so that
@@ -360,6 +332,19 @@ again:
        nr_pages = (end >> PAGE_CACHE_SHIFT) - (start >> PAGE_CACHE_SHIFT) + 1;
        nr_pages = min(nr_pages, (128 * 1024UL) / PAGE_CACHE_SIZE);
 
+       /*
+        * we don't want to send crud past the end of i_size through
+        * compression, that's just a waste of CPU time.  So, if the
+        * end of the file is before the start of our current
+        * requested range of bytes, we bail out to the uncompressed
+        * cleanup code that can deal with all of this.
+        *
+        * It isn't really the fastest way to fix things, but this is a
+        * very uncommon corner.
+        */
+       if (actual_end <= start)
+               goto cleanup_and_bail_uncompressed;
+
        total_compressed = actual_end - start;
 
        /* we want to make sure that amount of ram required to uncompress
@@ -504,6 +489,7 @@ again:
                        goto again;
                }
        } else {
+cleanup_and_bail_uncompressed:
                /*
                 * No compression, but we still need to write the pages in
                 * the file we've been given so far.  redirty the locked
@@ -1176,6 +1162,7 @@ static int btrfs_set_bit_hook(struct inode *inode, u64 start, u64 end,
         */
        if (!(old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
                struct btrfs_root *root = BTRFS_I(inode)->root;
+               btrfs_delalloc_reserve_space(root, inode, end - start + 1);
                spin_lock(&root->fs_info->delalloc_lock);
                BTRFS_I(inode)->delalloc_bytes += end - start + 1;
                root->fs_info->delalloc_bytes += end - start + 1;
@@ -1209,9 +1196,12 @@ static int btrfs_clear_bit_hook(struct inode *inode, u64 start, u64 end,
                               (unsigned long long)end - start + 1,
                               (unsigned long long)
                               root->fs_info->delalloc_bytes);
+                       btrfs_delalloc_free_space(root, inode, (u64)-1);
                        root->fs_info->delalloc_bytes = 0;
                        BTRFS_I(inode)->delalloc_bytes = 0;
                } else {
+                       btrfs_delalloc_free_space(root, inode,
+                                                 end - start + 1);
                        root->fs_info->delalloc_bytes -= end - start + 1;
                        BTRFS_I(inode)->delalloc_bytes -= end - start + 1;
                }
@@ -2231,10 +2221,6 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
 
        root = BTRFS_I(dir)->root;
 
-       ret = btrfs_check_free_space(root, 1, 1);
-       if (ret)
-               goto fail;
-
        trans = btrfs_start_transaction(root, 1);
 
        btrfs_set_trans_block_group(trans, dir);
@@ -2247,7 +2233,6 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
        nr = trans->blocks_used;
 
        btrfs_end_transaction_throttle(trans, root);
-fail:
        btrfs_btree_balance_dirty(root, nr);
        return ret;
 }
@@ -2270,10 +2255,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
                return -ENOTEMPTY;
        }
 
-       ret = btrfs_check_free_space(root, 1, 1);
-       if (ret)
-               goto fail;
-
        trans = btrfs_start_transaction(root, 1);
        btrfs_set_trans_block_group(trans, dir);
 
@@ -2290,7 +2271,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
 fail_trans:
        nr = trans->blocks_used;
        ret = btrfs_end_transaction_throttle(trans, root);
-fail:
        btrfs_btree_balance_dirty(root, nr);
 
        if (ret && !err)
@@ -2490,7 +2470,7 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
        struct btrfs_path *path;
        struct btrfs_key key;
        struct btrfs_key found_key;
-       u32 found_type;
+       u32 found_type = (u8)-1;
        struct extent_buffer *leaf;
        struct btrfs_file_extent_item *fi;
        u64 extent_start = 0;
@@ -2517,8 +2497,6 @@ noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
        key.offset = (u64)-1;
        key.type = (u8)-1;
 
-       btrfs_init_path(path);
-
 search_again:
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret < 0)
@@ -2677,6 +2655,8 @@ next:
                        if (pending_del_nr)
                                goto del_pending;
                        btrfs_release_path(root, path);
+                       if (found_type == BTRFS_INODE_ITEM_KEY)
+                               break;
                        goto search_again;
                }
 
@@ -2693,6 +2673,8 @@ del_pending:
                        BUG_ON(ret);
                        pending_del_nr = 0;
                        btrfs_release_path(root, path);
+                       if (found_type == BTRFS_INODE_ITEM_KEY)
+                               break;
                        goto search_again;
                }
        }
@@ -2802,7 +2784,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
        if (size <= hole_start)
                return 0;
 
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                return err;
 
@@ -2998,6 +2980,7 @@ static noinline void init_btrfs_i(struct inode *inode)
        bi->last_trans = 0;
        bi->logged_trans = 0;
        bi->delalloc_bytes = 0;
+       bi->reserved_bytes = 0;
        bi->disk_i_size = 0;
        bi->flags = 0;
        bi->index_cnt = (u64)-1;
@@ -3019,6 +3002,7 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
        inode->i_ino = args->ino;
        init_btrfs_i(inode);
        BTRFS_I(inode)->root = args->root;
+       btrfs_set_inode_space_info(args->root, inode);
        return 0;
 }
 
@@ -3439,6 +3423,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        BTRFS_I(inode)->index_cnt = 2;
        BTRFS_I(inode)->root = root;
        BTRFS_I(inode)->generation = trans->transid;
+       btrfs_set_inode_space_info(root, inode);
 
        if (mode & S_IFDIR)
                owner = 0;
@@ -3473,7 +3458,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 
        inode->i_uid = current_fsuid();
 
-       if (dir->i_mode & S_ISGID) {
+       if (dir && (dir->i_mode & S_ISGID)) {
                inode->i_gid = dir->i_gid;
                if (S_ISDIR(mode))
                        mode |= S_ISGID;
@@ -3586,7 +3571,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                goto fail;
 
@@ -3649,7 +3634,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
        u64 objectid;
        u64 index = 0;
 
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                goto fail;
        trans = btrfs_start_transaction(root, 1);
@@ -3717,7 +3702,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
                return -ENOENT;
 
        btrfs_inc_nlink(inode);
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                goto fail;
        err = btrfs_set_inode_index(dir, &index);
@@ -3763,7 +3748,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        u64 index = 0;
        unsigned long nr = 1;
 
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                goto out_unlock;
 
@@ -4245,7 +4230,7 @@ static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 {
        if (PageWriteback(page) || PageDirty(page))
                return 0;
-       return __btrfs_releasepage(page, gfp_flags);
+       return __btrfs_releasepage(page, gfp_flags & GFP_NOFS);
 }
 
 static void btrfs_invalidatepage(struct page *page, unsigned long offset)
@@ -4320,7 +4305,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
        u64 page_start;
        u64 page_end;
 
-       ret = btrfs_check_free_space(root, PAGE_CACHE_SIZE, 0);
+       ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE);
        if (ret)
                goto out;
 
@@ -4333,6 +4318,7 @@ again:
 
        if ((page->mapping != inode->i_mapping) ||
            (page_start >= size)) {
+               btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE);
                /* page got truncated out from underneath us */
                goto out_unlock;
        }
@@ -4615,7 +4601,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
                return -EXDEV;
 
-       ret = btrfs_check_free_space(root, 1, 0);
+       ret = btrfs_check_metadata_free_space(root);
        if (ret)
                goto out_unlock;
 
@@ -4733,7 +4719,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
        if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
                return -ENAMETOOLONG;
 
-       err = btrfs_check_free_space(root, 1, 0);
+       err = btrfs_check_metadata_free_space(root);
        if (err)
                goto out_fail;
 
This page took 0.035846 seconds and 5 git commands to generate.