Btrfs: add extent buffer bitmap sanity tests
authorOmar Sandoval <osandov@fb.com>
Wed, 30 Sep 2015 03:50:31 +0000 (20:50 -0700)
committerChris Mason <clm@fb.com>
Thu, 17 Dec 2015 20:16:46 +0000 (12:16 -0800)
Sanity test the extent buffer bitmap operations (test, set, and clear)
against the equivalent standard kernel operations.

Signed-off-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/tests/extent-io-tests.c

index 324d382..a6eec2d 100644 (file)
@@ -4730,24 +4730,14 @@ struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
        return new;
 }
 
-struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
-                                               u64 start)
+struct extent_buffer *__alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
+                                                 u64 start, unsigned long len)
 {
        struct extent_buffer *eb;
-       unsigned long len;
        unsigned long num_pages;
        unsigned long i;
 
-       if (!fs_info) {
-               /*
-                * Called only from tests that don't always have a fs_info
-                * available, but we know that nodesize is 4096
-                */
-               len = 4096;
-       } else {
-               len = fs_info->tree_root->nodesize;
-       }
-       num_pages = num_extent_pages(0, len);
+       num_pages = num_extent_pages(start, len);
 
        eb = __alloc_extent_buffer(fs_info, start, len);
        if (!eb)
@@ -4770,6 +4760,24 @@ err:
        return NULL;
 }
 
+struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
+                                               u64 start)
+{
+       unsigned long len;
+
+       if (!fs_info) {
+               /*
+                * Called only from tests that don't always have a fs_info
+                * available, but we know that nodesize is 4096
+                */
+               len = 4096;
+       } else {
+               len = fs_info->tree_root->nodesize;
+       }
+
+       return __alloc_dummy_extent_buffer(fs_info, start, len);
+}
+
 static void check_buffer_tree_ref(struct extent_buffer *eb)
 {
        int refs;
index 9185a20..9f8d7d1 100644 (file)
@@ -263,8 +263,10 @@ void set_page_extent_mapped(struct page *page);
 
 struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
                                          u64 start);
+struct extent_buffer *__alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
+                                                 u64 start, unsigned long len);
 struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
-               u64 start);
+                                               u64 start);
 struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
 struct extent_buffer *find_extent_buffer(struct btrfs_fs_info *fs_info,
                                         u64 start);
index 9e9f236..71ab575 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/pagemap.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 #include "btrfs-tests.h"
 #include "../extent_io.h"
 
@@ -76,6 +77,8 @@ static int test_find_delalloc(void)
        u64 found;
        int ret = -EINVAL;
 
+       test_msg("Running find delalloc tests\n");
+
        inode = btrfs_new_test_inode();
        if (!inode) {
                test_msg("Failed to allocate test inode\n");
@@ -268,8 +271,139 @@ out:
        return ret;
 }
 
+static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
+                            unsigned long len)
+{
+       unsigned long i, x;
+
+       memset(bitmap, 0, len);
+       memset_extent_buffer(eb, 0, 0, len);
+       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               test_msg("Bitmap was not zeroed\n");
+               return -EINVAL;
+       }
+
+       bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
+       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               test_msg("Setting all bits failed\n");
+               return -EINVAL;
+       }
+
+       bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
+       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               test_msg("Clearing all bits failed\n");
+               return -EINVAL;
+       }
+
+       bitmap_set(bitmap, (PAGE_CACHE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE,
+                  sizeof(long) * BITS_PER_BYTE);
+       extent_buffer_bitmap_set(eb, PAGE_CACHE_SIZE - sizeof(long) / 2, 0,
+                                sizeof(long) * BITS_PER_BYTE);
+       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               test_msg("Setting straddling pages failed\n");
+               return -EINVAL;
+       }
+
+       bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
+       bitmap_clear(bitmap,
+                    (PAGE_CACHE_SIZE - sizeof(long) / 2) * BITS_PER_BYTE,
+                    sizeof(long) * BITS_PER_BYTE);
+       extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_clear(eb, PAGE_CACHE_SIZE - sizeof(long) / 2, 0,
+                                  sizeof(long) * BITS_PER_BYTE);
+       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               test_msg("Clearing straddling pages failed\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Generate a wonky pseudo-random bit pattern for the sake of not using
+        * something repetitive that could miss some hypothetical off-by-n bug.
+        */
+       x = 0;
+       for (i = 0; i < len / sizeof(long); i++) {
+               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL;
+               bitmap[i] = x;
+       }
+       write_extent_buffer(eb, bitmap, 0, len);
+
+       for (i = 0; i < len * BITS_PER_BYTE; i++) {
+               int bit, bit1;
+
+               bit = !!test_bit(i, bitmap);
+               bit1 = !!extent_buffer_test_bit(eb, 0, i);
+               if (bit1 != bit) {
+                       test_msg("Testing bit pattern failed\n");
+                       return -EINVAL;
+               }
+
+               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
+                                               i % BITS_PER_BYTE);
+               if (bit1 != bit) {
+                       test_msg("Testing bit pattern with offset failed\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int test_eb_bitmaps(void)
+{
+       unsigned long len = PAGE_CACHE_SIZE * 4;
+       unsigned long *bitmap;
+       struct extent_buffer *eb;
+       int ret;
+
+       test_msg("Running extent buffer bitmap tests\n");
+
+       bitmap = kmalloc(len, GFP_NOFS);
+       if (!bitmap) {
+               test_msg("Couldn't allocate test bitmap\n");
+               return -ENOMEM;
+       }
+
+       eb = __alloc_dummy_extent_buffer(NULL, 0, len);
+       if (!eb) {
+               test_msg("Couldn't allocate test extent buffer\n");
+               kfree(bitmap);
+               return -ENOMEM;
+       }
+
+       ret = __test_eb_bitmaps(bitmap, eb, len);
+       if (ret)
+               goto out;
+
+       /* Do it over again with an extent buffer which isn't page-aligned. */
+       free_extent_buffer(eb);
+       eb = __alloc_dummy_extent_buffer(NULL, PAGE_CACHE_SIZE / 2, len);
+       if (!eb) {
+               test_msg("Couldn't allocate test extent buffer\n");
+               kfree(bitmap);
+               return -ENOMEM;
+       }
+
+       ret = __test_eb_bitmaps(bitmap, eb, len);
+out:
+       free_extent_buffer(eb);
+       kfree(bitmap);
+       return ret;
+}
+
 int btrfs_test_extent_io(void)
 {
-       test_msg("Running find delalloc tests\n");
-       return test_find_delalloc();
+       int ret;
+
+       test_msg("Running extent I/O tests\n");
+
+       ret = test_find_delalloc();
+       if (ret)
+               goto out;
+
+       ret = test_eb_bitmaps();
+out:
+       test_msg("Extent I/O tests finished\n");
+       return ret;
 }