• Filipe Manana's avatar
    btrfs: skip unnecessary delalloc search during fiemap and lseek · a2853ffc
    Filipe Manana authored
    During fiemap and lseek (hole and data seeking), there's no point in
    iterating the inode's io tree to count delalloc bits if the inode's
    delalloc bytes counter has a value of zero, as that counter is updated
    whenever we set a range for delalloc or clear a range from delalloc.
    
    So skip the counting and io tree iteration if the inode's delalloc bytes
    counter has a value of zero. This helps save time when processing a file
    range corresponding to a hole or prealloc (unwritten) extent.
    
    This patch is part of a series comprised of the following patches:
    
      btrfs: get the next extent map during fiemap/lseek more efficiently
      btrfs: skip unnecessary extent map searches during fiemap and lseek
      btrfs: skip unnecessary delalloc search during fiemap and lseek
    
    The following test was performed on a release kernel (Debian's default
    kernel config) before and after applying those 3 patches.
    
       # Wrapper to call fiemap in extent count only mode.
       # (struct fiemap::fm_extent_count set to 0)
       $ cat fiemap.c
       #include <stdio.h>
       #include <unistd.h>
       #include <stdlib.h>
       #include <fcntl.h>
       #include <errno.h>
       #include <string.h>
       #include <sys/ioctl.h>
       #include <linux/fs.h>
       #include <linux/fiemap.h>
    
       int main(int argc, char **argv)
       {
                struct fiemap fiemap = { 0 };
                int fd;
    
                if (argc != 2) {
                        printf("usage: %s <path>\n", argv[0]);
                        return 1;
                }
                fd = open(argv[1], O_RDONLY);
                if (fd < 0) {
                        fprintf(stderr, "error opening file: %s\n",
                                strerror(errno));
                        return 1;
                }
    
                /* fiemap.fm_extent_count set to 0, to count extents only. */
                fiemap.fm_length = FIEMAP_MAX_OFFSET;
                if (ioctl(fd, FS_IOC_FIEMAP, &fiemap) < 0) {
                        fprintf(stderr, "fiemap error: %s\n",
                                strerror(errno));
                        return 1;
                }
                close(fd);
                printf("fm_mapped_extents = %d\n", fiemap.fm_mapped_extents);
    
                return 0;
       }
    
       $ gcc -o fiemap fiemap.c
    
    And the wrapper shell script that creates a file with many holes and runs
    fiemap against it:
    
       $ cat test.sh
       #!/bin/bash
    
       DEV=/dev/sdi
       MNT=/mnt/sdi
    
       mkfs.btrfs -f $DEV
       mount $DEV $MNT
    
       FILE_SIZE=$((1 * 1024 * 1024 * 1024))
       echo -n > $MNT/foobar
       for ((off = 0; off < $FILE_SIZE; off += 8192)); do
               xfs_io -c "pwrite -S 0xab $off 4K" $MNT/foobar > /dev/null
       done
    
       # flush all delalloc
       sync
    
       start=$(date +%s%N)
       ./fiemap $MNT/foobar
       end=$(date +%s%N)
       dur=$(( (end - start) / 1000000 ))
       echo "fiemap took $dur milliseconds"
    
       umount $MNT
    
    Result before applying patchset:
    
       fm_mapped_extents = 131072
       fiemap took 63 milliseconds
    
    Result after applying patchset:
    
       fm_mapped_extents = 131072
       fiemap took 39 milliseconds   (-38.1%)
    
    Running the same test for a 512M file instead of a 1G file, gave the
    following results.
    
    Result before applying patchset:
    
       fm_mapped_extents = 65536
       fiemap took 29 milliseconds
    
    Result after applying patchset:
    
       fm_mapped_extents = 65536
       fiemap took 20 milliseconds    (-31.0%)
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    a2853ffc
file.c 115 KB