]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/btrfs/scrub.c
Merge remote-tracking branch 'sound/for-next'
[karo-tx-linux.git] / fs / btrfs / scrub.c
index 64a157becbe573f778c02e9475483cff5e167ea9..a18e0e23f6a6742cd21277702ed6599df84c32c3 100644 (file)
@@ -158,12 +158,20 @@ struct scrub_fixup_nodatasum {
        int                     mirror_num;
 };
 
+struct scrub_nocow_inode {
+       u64                     inum;
+       u64                     offset;
+       u64                     root;
+       struct list_head        list;
+};
+
 struct scrub_copy_nocow_ctx {
        struct scrub_ctx        *sctx;
        u64                     logical;
        u64                     len;
        int                     mirror_num;
        u64                     physical_for_dev_replace;
+       struct list_head        inodes;
        struct btrfs_work       work;
 };
 
@@ -245,7 +253,7 @@ static void scrub_wr_bio_end_io_worker(struct btrfs_work *work);
 static int write_page_nocow(struct scrub_ctx *sctx,
                            u64 physical_for_dev_replace, struct page *page);
 static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root,
-                                     void *ctx);
+                                     struct scrub_copy_nocow_ctx *ctx);
 static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
                            int mirror_num, u64 physical_for_dev_replace);
 static void copy_nocow_pages_worker(struct btrfs_work *work);
@@ -754,8 +762,7 @@ out:
                        num_uncorrectable_read_errors);
                printk_ratelimited_in_rcu(KERN_ERR
                        "btrfs: unable to fixup (nodatasum) error at logical %llu on dev %s\n",
-                       (unsigned long long)fixup->logical,
-                       rcu_str_deref(fixup->dev->name));
+                       fixup->logical, rcu_str_deref(fixup->dev->name));
        }
 
        btrfs_free_path(path);
@@ -1154,8 +1161,7 @@ corrected_error:
                        spin_unlock(&sctx->stat_lock);
                        printk_ratelimited_in_rcu(KERN_ERR
                                "btrfs: fixed up error at logical %llu on dev %s\n",
-                               (unsigned long long)logical,
-                               rcu_str_deref(dev->name));
+                               logical, rcu_str_deref(dev->name));
                }
        } else {
 did_not_correct_error:
@@ -1164,8 +1170,7 @@ did_not_correct_error:
                spin_unlock(&sctx->stat_lock);
                printk_ratelimited_in_rcu(KERN_ERR
                        "btrfs: unable to fixup (regular) error at logical %llu on dev %s\n",
-                       (unsigned long long)logical,
-                       rcu_str_deref(dev->name));
+                       logical, rcu_str_deref(dev->name));
        }
 
 out:
@@ -1345,12 +1350,12 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
                mapped_buffer = kmap_atomic(sblock->pagev[0]->page);
                h = (struct btrfs_header *)mapped_buffer;
 
-               if (sblock->pagev[0]->logical != le64_to_cpu(h->bytenr) ||
+               if (sblock->pagev[0]->logical != btrfs_stack_header_bytenr(h) ||
                    memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) ||
                    memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid,
                           BTRFS_UUID_SIZE)) {
                        sblock->header_error = 1;
-               } else if (generation != le64_to_cpu(h->generation)) {
+               } else if (generation != btrfs_stack_header_generation(h)) {
                        sblock->header_error = 1;
                        sblock->generation_error = 1;
                }
@@ -1720,10 +1725,10 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock)
         * b) the page is already kmapped
         */
 
-       if (sblock->pagev[0]->logical != le64_to_cpu(h->bytenr))
+       if (sblock->pagev[0]->logical != btrfs_stack_header_bytenr(h))
                ++fail;
 
-       if (sblock->pagev[0]->generation != le64_to_cpu(h->generation))
+       if (sblock->pagev[0]->generation != btrfs_stack_header_generation(h))
                ++fail;
 
        if (memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
@@ -1786,10 +1791,10 @@ static int scrub_checksum_super(struct scrub_block *sblock)
        s = (struct btrfs_super_block *)mapped_buffer;
        memcpy(on_disk_csum, s->csum, sctx->csum_size);
 
-       if (sblock->pagev[0]->logical != le64_to_cpu(s->bytenr))
+       if (sblock->pagev[0]->logical != btrfs_super_bytenr(s))
                ++fail_cor;
 
-       if (sblock->pagev[0]->generation != le64_to_cpu(s->generation))
+       if (sblock->pagev[0]->generation != btrfs_super_generation(s))
                ++fail_gen;
 
        if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
@@ -2455,8 +2460,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
                                printk(KERN_ERR
                                       "btrfs scrub: tree block %llu spanning "
                                       "stripes, ignored. logical=%llu\n",
-                                      (unsigned long long)key.objectid,
-                                      (unsigned long long)logical);
+                                      key.objectid, logical);
                                goto next;
                        }
 
@@ -2863,9 +2867,8 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
        if (fs_info->chunk_root->sectorsize != PAGE_SIZE) {
                /* not supported for data w/o checksums */
                printk(KERN_ERR
-                      "btrfs_scrub: size assumption sectorsize != PAGE_SIZE (%d != %lld) fails\n",
-                      fs_info->chunk_root->sectorsize,
-                      (unsigned long long)PAGE_SIZE);
+                      "btrfs_scrub: size assumption sectorsize != PAGE_SIZE (%d != %lu) fails\n",
+                      fs_info->chunk_root->sectorsize, PAGE_SIZE);
                return -EINVAL;
        }
 
@@ -3131,12 +3134,30 @@ static int copy_nocow_pages(struct scrub_ctx *sctx, u64 logical, u64 len,
        nocow_ctx->mirror_num = mirror_num;
        nocow_ctx->physical_for_dev_replace = physical_for_dev_replace;
        nocow_ctx->work.func = copy_nocow_pages_worker;
+       INIT_LIST_HEAD(&nocow_ctx->inodes);
        btrfs_queue_worker(&fs_info->scrub_nocow_workers,
                           &nocow_ctx->work);
 
        return 0;
 }
 
+static int record_inode_for_nocow(u64 inum, u64 offset, u64 root, void *ctx)
+{
+       struct scrub_copy_nocow_ctx *nocow_ctx = ctx;
+       struct scrub_nocow_inode *nocow_inode;
+
+       nocow_inode = kzalloc(sizeof(*nocow_inode), GFP_NOFS);
+       if (!nocow_inode)
+               return -ENOMEM;
+       nocow_inode->inum = inum;
+       nocow_inode->offset = offset;
+       nocow_inode->root = root;
+       list_add_tail(&nocow_inode->list, &nocow_ctx->inodes);
+       return 0;
+}
+
+#define COPY_COMPLETE 1
+
 static void copy_nocow_pages_worker(struct btrfs_work *work)
 {
        struct scrub_copy_nocow_ctx *nocow_ctx =
@@ -3172,19 +3193,42 @@ static void copy_nocow_pages_worker(struct btrfs_work *work)
        }
 
        ret = iterate_inodes_from_logical(logical, fs_info, path,
-                                         copy_nocow_pages_for_inode,
-                                         nocow_ctx);
+                                         record_inode_for_nocow, nocow_ctx);
        if (ret != 0 && ret != -ENOENT) {
-               pr_warn("iterate_inodes_from_logical() failed: log %llu, phys %llu, len %llu, mir %llu, ret %d\n",
-                       (unsigned long long)logical,
-                       (unsigned long long)physical_for_dev_replace,
-                       (unsigned long long)len,
-                       (unsigned long long)mirror_num, ret);
+               pr_warn("iterate_inodes_from_logical() failed: log %llu, phys %llu, len %llu, mir %u, ret %d\n",
+                       logical, physical_for_dev_replace, len, mirror_num,
+                       ret);
                not_written = 1;
                goto out;
        }
 
+       btrfs_end_transaction(trans, root);
+       trans = NULL;
+       while (!list_empty(&nocow_ctx->inodes)) {
+               struct scrub_nocow_inode *entry;
+               entry = list_first_entry(&nocow_ctx->inodes,
+                                        struct scrub_nocow_inode,
+                                        list);
+               list_del_init(&entry->list);
+               ret = copy_nocow_pages_for_inode(entry->inum, entry->offset,
+                                                entry->root, nocow_ctx);
+               kfree(entry);
+               if (ret == COPY_COMPLETE) {
+                       ret = 0;
+                       break;
+               } else if (ret) {
+                       break;
+               }
+       }
 out:
+       while (!list_empty(&nocow_ctx->inodes)) {
+               struct scrub_nocow_inode *entry;
+               entry = list_first_entry(&nocow_ctx->inodes,
+                                        struct scrub_nocow_inode,
+                                        list);
+               list_del_init(&entry->list);
+               kfree(entry);
+       }
        if (trans && !IS_ERR(trans))
                btrfs_end_transaction(trans, root);
        if (not_written)
@@ -3197,20 +3241,25 @@ out:
        scrub_pending_trans_workers_dec(sctx);
 }
 
-static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
+static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root,
+                                     struct scrub_copy_nocow_ctx *nocow_ctx)
 {
-       struct scrub_copy_nocow_ctx *nocow_ctx = ctx;
        struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info;
        struct btrfs_key key;
        struct inode *inode;
        struct page *page;
        struct btrfs_root *local_root;
+       struct btrfs_ordered_extent *ordered;
+       struct extent_map *em;
+       struct extent_state *cached_state = NULL;
+       struct extent_io_tree *io_tree;
        u64 physical_for_dev_replace;
-       u64 len;
+       u64 len = nocow_ctx->len;
+       u64 lockstart = offset, lockend = offset + len - 1;
        unsigned long index;
        int srcu_index;
-       int ret;
-       int err;
+       int ret = 0;
+       int err = 0;
 
        key.objectid = root;
        key.type = BTRFS_ROOT_ITEM_KEY;
@@ -3224,11 +3273,6 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
                return PTR_ERR(local_root);
        }
 
-       if (btrfs_root_refs(&local_root->root_item) == 0) {
-               srcu_read_unlock(&fs_info->subvol_srcu, srcu_index);
-               return -ENOENT;
-       }
-
        key.type = BTRFS_INODE_ITEM_KEY;
        key.objectid = inum;
        key.offset = 0;
@@ -3241,9 +3285,33 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
        mutex_lock(&inode->i_mutex);
        inode_dio_wait(inode);
 
-       ret = 0;
        physical_for_dev_replace = nocow_ctx->physical_for_dev_replace;
-       len = nocow_ctx->len;
+       io_tree = &BTRFS_I(inode)->io_tree;
+
+       lock_extent_bits(io_tree, lockstart, lockend, 0, &cached_state);
+       ordered = btrfs_lookup_ordered_range(inode, lockstart, len);
+       if (ordered) {
+               btrfs_put_ordered_extent(ordered);
+               goto out_unlock;
+       }
+
+       em = btrfs_get_extent(inode, NULL, 0, lockstart, len, 0);
+       if (IS_ERR(em)) {
+               ret = PTR_ERR(em);
+               goto out_unlock;
+       }
+
+       /*
+        * This extent does not actually cover the logical extent anymore,
+        * move on to the next inode.
+        */
+       if (em->block_start > nocow_ctx->logical ||
+           em->block_start + em->block_len < nocow_ctx->logical + len) {
+               free_extent_map(em);
+               goto out_unlock;
+       }
+       free_extent_map(em);
+
        while (len >= PAGE_CACHE_SIZE) {
                index = offset >> PAGE_CACHE_SHIFT;
 again:
@@ -3259,10 +3327,9 @@ again:
                                goto next_page;
                } else {
                        ClearPageError(page);
-                       err = extent_read_full_page(&BTRFS_I(inode)->
-                                                        io_tree,
-                                                       page, btrfs_get_extent,
-                                                       nocow_ctx->mirror_num);
+                       err = extent_read_full_page_nolock(io_tree, page,
+                                                          btrfs_get_extent,
+                                                          nocow_ctx->mirror_num);
                        if (err) {
                                ret = err;
                                goto next_page;
@@ -3276,6 +3343,7 @@ again:
                         * page in the page cache.
                         */
                        if (page->mapping != inode->i_mapping) {
+                               unlock_page(page);
                                page_cache_release(page);
                                goto again;
                        }
@@ -3299,6 +3367,10 @@ next_page:
                physical_for_dev_replace += PAGE_CACHE_SIZE;
                len -= PAGE_CACHE_SIZE;
        }
+       ret = COPY_COMPLETE;
+out_unlock:
+       unlock_extent_cached(io_tree, lockstart, lockend, &cached_state,
+                            GFP_NOFS);
 out:
        mutex_unlock(&inode->i_mutex);
        iput(inode);