btrfs: unlock on error in btrfs_file_llseek()
[linux-2.6.git] / fs / btrfs / file.c
index 982b5ea..f7d9df7 100644 (file)
@@ -74,7 +74,7 @@ struct inode_defrag {
  * If an existing record is found the defrag item you
  * pass in is freed
  */
-static int __btrfs_add_inode_defrag(struct inode *inode,
+static void __btrfs_add_inode_defrag(struct inode *inode,
                                    struct inode_defrag *defrag)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -106,11 +106,11 @@ static int __btrfs_add_inode_defrag(struct inode *inode,
        BTRFS_I(inode)->in_defrag = 1;
        rb_link_node(&defrag->rb_node, parent, p);
        rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
-       return 0;
+       return;
 
 exists:
        kfree(defrag);
-       return 0;
+       return;
 
 }
 
@@ -123,13 +123,12 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct inode_defrag *defrag;
-       int ret = 0;
        u64 transid;
 
        if (!btrfs_test_opt(root, AUTO_DEFRAG))
                return 0;
 
-       if (root->fs_info->closing)
+       if (btrfs_fs_closing(root->fs_info))
                return 0;
 
        if (BTRFS_I(inode)->in_defrag)
@@ -150,9 +149,9 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
 
        spin_lock(&root->fs_info->defrag_inodes_lock);
        if (!BTRFS_I(inode)->in_defrag)
-               ret = __btrfs_add_inode_defrag(inode, defrag);
+               __btrfs_add_inode_defrag(inode, defrag);
        spin_unlock(&root->fs_info->defrag_inodes_lock);
-       return ret;
+       return 0;
 }
 
 /*
@@ -229,7 +228,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
                first_ino = defrag->ino + 1;
                rb_erase(&defrag->rb_node, &fs_info->defrag_inodes);
 
-               if (fs_info->closing)
+               if (btrfs_fs_closing(fs_info))
                        goto next_free;
 
                spin_unlock(&fs_info->defrag_inodes_lock);
@@ -855,7 +854,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
        btrfs_drop_extent_cache(inode, start, end - 1, 0);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 again:
        recow = 0;
        split = start;
@@ -1059,7 +1059,7 @@ static int prepare_uptodate_page(struct page *page, u64 pos)
 static noinline int prepare_pages(struct btrfs_root *root, struct file *file,
                         struct page **pages, size_t num_pages,
                         loff_t pos, unsigned long first_index,
-                        unsigned long last_index, size_t write_bytes)
+                        size_t write_bytes)
 {
        struct extent_state *cached_state = NULL;
        int i;
@@ -1081,7 +1081,8 @@ static noinline int prepare_pages(struct btrfs_root *root, struct file *file,
 
 again:
        for (i = 0; i < num_pages; i++) {
-               pages[i] = grab_cache_page(inode->i_mapping, index + i);
+               pages[i] = find_or_create_page(inode->i_mapping, index + i,
+                                              GFP_NOFS);
                if (!pages[i]) {
                        faili = i - 1;
                        err = -ENOMEM;
@@ -1158,7 +1159,6 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct page **pages = NULL;
        unsigned long first_index;
-       unsigned long last_index;
        size_t num_written = 0;
        int nrptrs;
        int ret = 0;
@@ -1171,7 +1171,6 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                return -ENOMEM;
 
        first_index = pos >> PAGE_CACHE_SHIFT;
-       last_index = (pos + iov_iter_count(i)) >> PAGE_CACHE_SHIFT;
 
        while (iov_iter_count(i) > 0) {
                size_t offset = pos & (PAGE_CACHE_SIZE - 1);
@@ -1205,8 +1204,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                 * contents of pages from loop to loop
                 */
                ret = prepare_pages(root, file, pages, num_pages,
-                                   pos, first_index, last_index,
-                                   write_bytes);
+                                   pos, first_index, write_bytes);
                if (ret) {
                        btrfs_delalloc_release_space(inode,
                                        num_pages << PAGE_CACHE_SHIFT);
@@ -1238,9 +1236,11 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                 * managed to copy.
                 */
                if (num_pages > dirty_pages) {
-                       if (copied > 0)
-                               atomic_inc(
-                                       &BTRFS_I(inode)->outstanding_extents);
+                       if (copied > 0) {
+                               spin_lock(&BTRFS_I(inode)->lock);
+                               BTRFS_I(inode)->outstanding_extents++;
+                               spin_unlock(&BTRFS_I(inode)->lock);
+                       }
                        btrfs_delalloc_release_space(inode,
                                        (num_pages - dirty_pages) <<
                                        PAGE_CACHE_SHIFT);
@@ -1452,7 +1452,7 @@ int btrfs_release_file(struct inode *inode, struct file *filp)
  * important optimization for directories because holding the mutex prevents
  * new operations on the dir while we write to disk.
  */
-int btrfs_sync_file(struct file *file, int datasync)
+int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
@@ -1462,9 +1462,13 @@ int btrfs_sync_file(struct file *file, int datasync)
 
        trace_btrfs_sync_file(file, datasync);
 
+       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       if (ret)
+               return ret;
+       mutex_lock(&inode->i_mutex);
+
        /* we wait first, since the writeback may change the inode */
        root->log_batch++;
-       /* the VFS called filemap_fdatawrite for us */
        btrfs_wait_ordered_range(inode, 0, (u64)-1);
        root->log_batch++;
 
@@ -1472,8 +1476,10 @@ int btrfs_sync_file(struct file *file, int datasync)
         * check the transaction that last modified this inode
         * and see if its already been committed
         */
-       if (!BTRFS_I(inode)->last_trans)
+       if (!BTRFS_I(inode)->last_trans) {
+               mutex_unlock(&inode->i_mutex);
                goto out;
+       }
 
        /*
         * if the last transaction that changed this file was before
@@ -1484,6 +1490,7 @@ int btrfs_sync_file(struct file *file, int datasync)
        if (BTRFS_I(inode)->last_trans <=
            root->fs_info->last_trans_committed) {
                BTRFS_I(inode)->last_trans = 0;
+               mutex_unlock(&inode->i_mutex);
                goto out;
        }
 
@@ -1496,12 +1503,15 @@ int btrfs_sync_file(struct file *file, int datasync)
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
+               mutex_unlock(&inode->i_mutex);
                goto out;
        }
 
        ret = btrfs_log_dentry_safe(trans, root, dentry);
-       if (ret < 0)
+       if (ret < 0) {
+               mutex_unlock(&inode->i_mutex);
                goto out;
+       }
 
        /* we've logged all the items and now have a consistent
         * version of the file in the log.  It is possible that
@@ -1513,7 +1523,7 @@ int btrfs_sync_file(struct file *file, int datasync)
         * file again, but that will end up using the synchronization
         * inside btrfs_sync_log to keep things safe.
         */
-       mutex_unlock(&dentry->d_inode->i_mutex);
+       mutex_unlock(&inode->i_mutex);
 
        if (ret != BTRFS_NO_LOG_SYNC) {
                if (ret > 0) {
@@ -1528,7 +1538,6 @@ int btrfs_sync_file(struct file *file, int datasync)
        } else {
                ret = btrfs_end_transaction(trans, root);
        }
-       mutex_lock(&dentry->d_inode->i_mutex);
 out:
        return ret > 0 ? -EIO : ret;
 }
@@ -1664,8 +1673,158 @@ out:
        return ret;
 }
 
+static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct extent_map *em;
+       struct extent_state *cached_state = NULL;
+       u64 lockstart = *offset;
+       u64 lockend = i_size_read(inode);
+       u64 start = *offset;
+       u64 orig_start = *offset;
+       u64 len = i_size_read(inode);
+       u64 last_end = 0;
+       int ret = 0;
+
+       lockend = max_t(u64, root->sectorsize, lockend);
+       if (lockend <= lockstart)
+               lockend = lockstart + root->sectorsize;
+
+       len = lockend - lockstart + 1;
+
+       len = max_t(u64, len, root->sectorsize);
+       if (inode->i_size == 0)
+               return -ENXIO;
+
+       lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
+                        &cached_state, GFP_NOFS);
+
+       /*
+        * Delalloc is such a pain.  If we have a hole and we have pending
+        * delalloc for a portion of the hole we will get back a hole that
+        * exists for the entire range since it hasn't been actually written
+        * yet.  So to take care of this case we need to look for an extent just
+        * before the position we want in case there is outstanding delalloc
+        * going on here.
+        */
+       if (origin == SEEK_HOLE && start != 0) {
+               if (start <= root->sectorsize)
+                       em = btrfs_get_extent_fiemap(inode, NULL, 0, 0,
+                                                    root->sectorsize, 0);
+               else
+                       em = btrfs_get_extent_fiemap(inode, NULL, 0,
+                                                    start - root->sectorsize,
+                                                    root->sectorsize, 0);
+               if (IS_ERR(em)) {
+                       ret = -ENXIO;
+                       goto out;
+               }
+               last_end = em->start + em->len;
+               if (em->block_start == EXTENT_MAP_DELALLOC)
+                       last_end = min_t(u64, last_end, inode->i_size);
+               free_extent_map(em);
+       }
+
+       while (1) {
+               em = btrfs_get_extent_fiemap(inode, NULL, 0, start, len, 0);
+               if (IS_ERR(em)) {
+                       ret = -ENXIO;
+                       break;
+               }
+
+               if (em->block_start == EXTENT_MAP_HOLE) {
+                       if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
+                               if (last_end <= orig_start) {
+                                       free_extent_map(em);
+                                       ret = -ENXIO;
+                                       break;
+                               }
+                       }
+
+                       if (origin == SEEK_HOLE) {
+                               *offset = start;
+                               free_extent_map(em);
+                               break;
+                       }
+               } else {
+                       if (origin == SEEK_DATA) {
+                               if (em->block_start == EXTENT_MAP_DELALLOC) {
+                                       if (start >= inode->i_size) {
+                                               free_extent_map(em);
+                                               ret = -ENXIO;
+                                               break;
+                                       }
+                               }
+
+                               *offset = start;
+                               free_extent_map(em);
+                               break;
+                       }
+               }
+
+               start = em->start + em->len;
+               last_end = em->start + em->len;
+
+               if (em->block_start == EXTENT_MAP_DELALLOC)
+                       last_end = min_t(u64, last_end, inode->i_size);
+
+               if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
+                       free_extent_map(em);
+                       ret = -ENXIO;
+                       break;
+               }
+               free_extent_map(em);
+               cond_resched();
+       }
+       if (!ret)
+               *offset = min(*offset, inode->i_size);
+out:
+       unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+                            &cached_state, GFP_NOFS);
+       return ret;
+}
+
+static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int origin)
+{
+       struct inode *inode = file->f_mapping->host;
+       int ret;
+
+       mutex_lock(&inode->i_mutex);
+       switch (origin) {
+       case SEEK_END:
+       case SEEK_CUR:
+               offset = generic_file_llseek_unlocked(file, offset, origin);
+               goto out;
+       case SEEK_DATA:
+       case SEEK_HOLE:
+               ret = find_desired_extent(inode, &offset, origin);
+               if (ret) {
+                       mutex_unlock(&inode->i_mutex);
+                       return ret;
+               }
+       }
+
+       if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       if (offset > inode->i_sb->s_maxbytes) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Special lock needed here? */
+       if (offset != file->f_pos) {
+               file->f_pos = offset;
+               file->f_version = 0;
+       }
+out:
+       mutex_unlock(&inode->i_mutex);
+       return offset;
+}
+
 const struct file_operations btrfs_file_operations = {
-       .llseek         = generic_file_llseek,
+       .llseek         = btrfs_file_llseek,
        .read           = do_sync_read,
        .write          = do_sync_write,
        .aio_read       = generic_file_aio_read,