diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 709096f..fde9113 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -65,3 +65,9 @@ config BTRFS_MDCSREPAIR Enable kernel to user program NETLINK communication for automtically repairing of corrupted data using CRC chechsum and RAID data redundancy. + +config BTRFS_FIEMAP_PHYSICAL + bool "Btrfs physical address ouput of FIEMAP" + depends on BTRFS_FS + help + Enable BTRFS to report physical address for FIEMAP ioctl. diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 9240446..6176dc2 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -13,3 +13,4 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o btrfs-$(CONFIG_BTRFS_MDCSREPAIR) += cslink.o +btrfs-$(CONFIG_BTRFS_FIEMAP_PHYSICAL) += fiemap_physical.o diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 1f52e44..37e5a38 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3826,8 +3826,15 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode, return NULL; } -int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, - __u64 start, __u64 len, get_extent_t *get_extent) +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL +int extent_fiemap_log2phys(struct inode *inode, + struct fiemap_extent_info *fieinfo, + u64 foffset, u64 loffset, u64 len, u32 flags); +#endif + +static int __extent_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len, get_extent_t *get_extent, bool dophys) { int ret = 0; u64 off = start; @@ -3986,8 +3993,15 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, flags |= FIEMAP_EXTENT_LAST; end = 1; } - ret = fiemap_fill_next_extent(fieinfo, em_start, disko, - em_len, flags); +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL + if (dophys) + ret = extent_fiemap_log2phys(inode, fieinfo, + em_start, disko, + em_len, flags); + else +#endif + ret = fiemap_fill_next_extent(fieinfo, em_start, disko, + em_len, flags); if (ret) goto out_free; } @@ -3999,6 +4013,21 @@ out: return ret; } +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL +int extent_fiemap_physical(struct inode *inode, + struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len, get_extent_t *get_extent) +{ + return __extent_fiemap(inode, fieinfo, start, len, get_extent, true); +} +#endif + +int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len, get_extent_t *get_extent) +{ + return __extent_fiemap(inode, fieinfo, start, len, get_extent, false); +} + static void __free_extent_buffer(struct extent_buffer *eb) { #if LEAK_DEBUG diff --git a/fs/btrfs/fiemap_physical.c b/fs/btrfs/fiemap_physical.c new file mode 100644 index 0000000..bf297a4 --- /dev/null +++ b/fs/btrfs/fiemap_physical.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 NETGEAR, Inc. All rights reserved. + * Copyright (C) 2014 Hiro Sugawara All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ctree.h" +#include "disk-io.h" +#include "btrfs_inode.h" +#include "volumes.h" + +#define fe_pdev fe_reserved[2] +#define fe_llen fe_reserved64[1] +#define FIEMAP_EXTENT_PHYSICAL (1<<31) +#define FIEMAP_EXTENT_MORESTRIPE (1<<30) +#define FIEMAP_EXTENT_LOGLENGTH (1<<29) + +static int fiemap_fill_next_extent_physical(struct fiemap_extent_info *fieinfo, + u64 fpos, u64 llength, u64 phys, u64 len, u32 flags, + dev_t dev) +{ + int ret; + struct fiemap_extent __user *extent = + fieinfo->fi_extents_start + fieinfo->fi_extents_mapped; + typeof(extent->fe_pdev) pdev = new_encode_dev(dev); + typeof(extent->fe_llen) llen = llength; + + if ((ret = fiemap_fill_next_extent(fieinfo, fpos, phys, len, + flags | FIEMAP_EXTENT_PHYSICAL | FIEMAP_EXTENT_LOGLENGTH))) + return ret; + if (copy_to_user(&extent->fe_pdev, &pdev, sizeof(pdev))) + return -EFAULT; + if (copy_to_user(&extent->fe_llen, &llen, sizeof(llen))) + return -EFAULT; + return 0; +} + +int extent_fiemap_log2phys(struct inode *inode, + struct fiemap_extent_info *fieinfo, + u64 foffset, u64 logical, u64 len, u32 flags) +{ + struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; + struct btrfs_bio *bbio; + int ret, i; + u64 length = len; + + if ((ret = btrfs_map_block(fs_info, REQ_DISCARD, logical, &length, &bbio, 0))) + return ret; + for (i = 0; i < bbio->num_stripes; i++) { +printk("%s:%d %d:%d ino=%lu logical=%llu length=%llu stripe physical=%llu stripe length=%llu num_stripes=%d i=%d\n", + __FILE__,__LINE__, + MAJOR(bbio->stripes[i].dev->bdev->bd_dev), + MINOR(bbio->stripes[i].dev->bdev->bd_dev), + inode->i_ino, logical, length, + bbio->stripes[i].physical, + bbio->stripes[i].length, + bbio->num_stripes, i); + if ((ret = fiemap_fill_next_extent_physical(fieinfo, foffset, + length, + bbio->stripes[i].physical, + bbio->stripes[i].length, + (bbio->num_stripes > 1) && + (i < bbio->num_stripes - 1) + ? FIEMAP_EXTENT_MORESTRIPE + : 0, + bbio->stripes[i].dev->bdev->bd_dev))) + break; + } + kfree(bbio); + return ret; +} + + +#if 0 +struct extent_map *btrfs_get_extent_fiemap_physical(struct inode *inode, + struct page *page, size_t pg_offset, + u64 start, u64 len, int create) +{ + struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; + struct extent_map *em; + struct map_lookup *map; + u64 physical, logical, length = len; + struct btrfs_bio *bbio = NULL; + int ret; + + if ((ret = btrfs_map_block(fs_info, 0, start, &length, &bbio, 0))) + return ERR_PTR(err); + + em = alloc_extent_map(); + if (!em) + goto exit; + em->bdev = fs_info->fs_devices->latest_bdev + +exit: + free(bbio); + return em; +} +#endif diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1f328ce..eebc26c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7392,16 +7392,31 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, } #define BTRFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC) +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL +extern int extent_fiemap_physical(struct inode *inode, + struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len, + get_extent_t *get_extent); +#endif static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) { int ret; +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL + unsigned int physical = fieinfo->fi_flags & FIEMAP_FLAG_PHYSICAL; + fieinfo->fi_flags &= ~FIEMAP_FLAG_PHYSICAL; +#endif ret = fiemap_check_flags(fieinfo, BTRFS_FIEMAP_FLAGS); if (ret) return ret; +#ifdef CONFIG_BTRFS_FIEMAP_PHYSICAL + if (physical) + return extent_fiemap_physical(inode, fieinfo, start, len, + btrfs_get_extent_fiemap); +#endif return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent_fiemap); } diff --git a/include/linux/fiemap.h b/include/linux/fiemap.h index d830747..587771e 100644 --- a/include/linux/fiemap.h +++ b/include/linux/fiemap.h @@ -19,9 +19,12 @@ struct fiemap_extent { __u64 fe_physical; /* physical offset in bytes for the start * of the extent from the beginning of the disk */ __u64 fe_length; /* length in bytes for this extent */ - __u64 fe_reserved64[2]; + __u64 fe_reserved64[1]; + __u64 fe_loglength;/* logical extent length + * if FIEMAP_EXTENT_LOGLENGTH */ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ - __u32 fe_reserved[3]; + __u32 fe_reserved[2]; + __u32 fe_pdev; /* physical dev_t if FIEMAP_EXTENT_PHYSICAL */ }; struct fiemap { @@ -42,6 +45,7 @@ struct fiemap { #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) +#define FIEMAP_FLAG_PHYSICAL 0x80000000 /* report physical device extents */ #define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ #define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ @@ -64,5 +68,10 @@ struct fiemap { * merged for efficiency. */ #define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other * files. */ +#define FIEMAP_EXTENT_LOGLENGTH 0x20000000 /* fe_loglength is valid. */ +#define FIEMAP_EXTENT_MORESTRIPE 0x40000000 /* One ore more physical + * extent will follow. */ +#define FIEMAP_EXTENT_PHYSICAL 0x80000000 /* This extent represents + * physical device */ #endif /* _LINUX_FIEMAP_H */