fiemap patches (RFC/testing)
Subsystems (3)
cover fiemap patches (RFC/testing)
Since Akira would like to use the fiemap ioctl for defrag, I thought I should put what I have so far out on the list, at least. This could go in the unstable part of the tree if you like, though I need to do more testing etc before it's really ready to go.
Also, below is a quick test application I was using with the ioctl.
-Eric
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <ext2fs/ext2_types.h>
/*************************************************/
/* All this should come from fiemap.h eventually */
struct fiemap_extent {
__u64 fe_offset; /* offset in bytes for the start of the extent */ __u64 fe_length; /* length in bytes for the extent */ __u32 fe_flags; /* returned FIEMAP_EXTENT_* flags for the extent */
__u32 fe_lun; /* logical device number for extent (starting at 0) */
};
struct fiemap {
__u64 fm_start; /* logical starting byte offset (in/out) */
__u64 fm_length; /* logical length of map (in/out) */
__u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
__u32 fm_extent_count; /* number of extents in fm_extents (in/out) */
__u64 fm_end_offset; /* logical offset of end of mapping in last ioctl */
struct fiemap_extent fm_extents[0];
};#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
#define FIEMAP_FLAG_HSM_READ 0x00000002 /* get data from HSM before map */
#define FIEMAP_FLAG_NUM_EXTENTS 0x00000004 /* return only number of extents */
#define FIEMAP_FLAG_INCOMPAT 0xff000000 /* error for unknown flags in here */
#define FIEMAP_FLAG_LUN_OFFSET 0x01000000 /* use lun offsets, instead of
* logical file offsets */
#define FIEMAP_EXTENT_HOLE 0x00000001 /* has no data or space allocation */
#define FIEMAP_EXTENT_UNWRITTEN 0x00000002 /* space allocated, but no data */ #define FIEMAP_EXTENT_UNMAPPED 0x00000004 /* has data but no space allocation*/
#define FIEMAP_EXTENT_ERROR 0x00000008 /* mapping error, errno in fe_start*/
#define FIEMAP_EXTENT_NO_DIRECT 0x00000010 /* cannot access data directly */
#define FIEMAP_EXTENT_LAST 0x00000020 /* last extent in the file */
#define FIEMAP_EXTENT_DELALLOC 0x00000040 /* has data but not yet written,
must have EXTENT_UNKNOWN set */
#define FIEMAP_EXTENT_SECONDARY 0x00000080 /* data (also) in secondary storage,
not in primary if EXTENT_UNKNOWN*/
#define FIEMAP_EXTENT_EOF 0x00000100 /* if fm_start+fm_len is beyond EOF*/
#define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */
#define EXT4_IOC_FIEMAP _IOWR('f', 10, struct fiemap) /* get file extent info*/
/* End of what should be coming from fiemap.h */
/**********************************************/
void usage(void)
{
printf("Usage: fiemap [-vrSCL] [-s start] [-l length] [-c buf count] [-m max] filename\n");
printf(" -v : verbose mode\n");
printf(" -r : raw output: print raw ioctl structure values\n");
printf(" -S : set FIEMAP_FLAG_SYNC to sync before mapping\n");
printf(" -C : set FIEMAP_FLAG_NUM_EXTENTS to only get extent count, not mapping\n");
printf(" -L : set FIEMAP_FLAG_LUN_OFFSET to report extents in lun order\n");
printf(" -s start : start of mapping in bytes (default 0)\n");
printf(" -l length : length of mapping in bytes (default to end of file)\n");
printf(" -c count : count of extents in ioctl input structure (default 32)\n");
printf(" -m max : max nr of ioctls to call before exit (default 512)\n");
exit(EXIT_FAILURE);
}#define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) #define GIGABYTES(x) ((long long)(x) << 30) #define MEGABYTES(x) ((long long)(x) << 20) #define KILOBYTES(x) ((long long)(x) << 10)
long longcvtnum(char *s)
{
long long i;
char *sp;
int c;
i = strtoll(s, &sp, 0);
if (i == 0 && sp == s)
return -1LL;
if (*sp == '\0')
return i;
if (sp[1] != '\0')
return -1LL;
c = tolower(*sp);
switch (c) {
case 'k':
return KILOBYTES(i);
case 'm':
return MEGABYTES(i);
case 'g':
return GIGABYTES(i);
case 't':
return TERABYTES(i);
case 'p':
return PETABYTES(i);
case 'e':
return EXABYTES(i);
}
return -1LL;
}void show_extents_table(struct fiemap *fiemap, int blocksize, int start_extent, int *is_last)
{
unsigned int i;
__u64 lstart;
lstart = fiemap->fm_start;
for (i = 0; i < fiemap->fm_extent_count; i++) {
__u64 length = fiemap->fm_extents[i].fe_length;
__u64 phys = fiemap->fm_extents[i].fe_offset;
int flags = fiemap->fm_extents[i].fe_flags;
printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu flags: 0x%03X tot: %llu\n",
i + start_extent,
lstart, lstart + length - 1,
(phys / blocksize),
(flags & FIEMAP_EXTENT_HOLE) ? 0 : (phys + length - 1) / blocksize,
flags,
(length / blocksize));
lstart += length;
if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
*is_last = 1;
return; /* XXX should we? or should look for exents filled in past last? */
}
}
}void show_extents_raw(struct fiemap *fiemap, int start_extent, int *is_last)
{
unsigned int i;
for (i = 0; i < fiemap->fm_extent_count; i++) {
printf("\tExtent %3u: start: %10lld length: %10lld flags 0x%03X lun %3u\n",
i + start_extent,
fiemap->fm_extents[i].fe_offset,
fiemap->fm_extents[i].fe_length,
fiemap->fm_extents[i].fe_flags,
fiemap->fm_extents[i].fe_lun);
if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
*is_last = 1;
return; /* XXX should we? or should look for exents filled in past last? */
}
}
}int main(int argc, char**argv)
{
int blocksize = 0; /* filesystem blocksize */
uint count = 32; /* extent count */
int countonly = 0; /* return only extent count? */
int fd; /* file descriptor */
int last = 0; /* last extent found */
int lunwise = 0; /* return extents lun-wise */
int maxioctls = 512; /* max ioctls to try */
int opt;
int rc;
int raw = 0; /* raw output format */
int sync = 0; /* sync file before mapping */
int verbose = 0; /* verbose output */
char *fname; /* filename to map */
char *fiebuf; /* fiemap buffer / ioctl argument */
__u64 lstart = 0; /* logical input mapping start */
__u64 llength = ~0ULL;/* logical input mapping length */
uint start_ext = 0; /* starting extent nr. for this batch */
struct fiemap *fiemap;
while ((opt = getopt(argc, argv, "s:l:c:m:rSCLv")) != -1) {
switch(opt) {
/* logical mapping offset */
case 's':
lstart = cvtnum(optarg);
break;
/* logical mapping length */
case 'l':
llength = cvtnum(optarg);
break;
/* count of extent buffers to send */
case 'c':
count = atoi(optarg);
break;
/* max nr. of ioctls to try (safety net) */
case 'm':
maxioctls = atoi(optarg);
break;
/* raw format output */
case 'r':
raw++;
break;
/* sync file before mapping */
case 'S':
sync++;
break;
/* count extents only, no details */
case 'C':
countonly++;
break;
/* return extents in lun order */
case 'L':
lunwise++;
break;
/* be verbose */
case 'v':
verbose++;
break;
default:
usage();
}
}
fname = argv[optind++];
if (!fname)
usage();
/* The whole buffer, extent maps and all */
fiebuf = malloc(sizeof (struct fiemap) + (count * sizeof(struct fiemap_extent)));
if (!fiebuf) {
perror("Could not allocate fiemap buffers");
exit(1);
}
/* Just the header */
fiemap = (struct fiemap *)fiebuf;
fiemap->fm_start = lstart;
fiemap->fm_length = llength;
fiemap->fm_flags = 0;
if (sync)
fiemap->fm_flags |= FIEMAP_FLAG_SYNC;
if (countonly)
fiemap->fm_flags |= FIEMAP_FLAG_NUM_EXTENTS;
if (lunwise)
fiemap->fm_flags |= FIEMAP_FLAG_LUN_OFFSET;
fiemap->fm_extent_count = count;
fiemap->fm_end_offset = 0; /* output only */
fd = open(fname, O_RDONLY);
if (fd < 0) {
perror("Can't open file");
exit(1);
}
if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
perror("Can't get block size");
close(fd);
return;
}
do {
if (verbose)
printf("Input: start %llu length %llu flags 0x%X count %u end_offset %lld\n",
fiemap->fm_start, fiemap->fm_length,
fiemap->fm_flags, fiemap->fm_extent_count,
fiemap->fm_end_offset);
rc = ioctl(fd, EXT4_IOC_FIEMAP, (unsigned long)fiemap);
if (rc < 0) {
perror("FIEMAP ioctl failed");
close(fd);
exit(1);
}
if (verbose)
printf("Output: start %llu length %llu flags 0x%X count %u end_offset %lld\n",
fiemap->fm_start, fiemap->fm_length,
fiemap->fm_flags, fiemap->fm_extent_count,
fiemap->fm_end_offset);
if (raw)
show_extents_raw(fiemap, start_ext, &last);
else
show_extents_table(fiemap, blocksize, start_ext, &last);
start_ext += fiemap->fm_extent_count;
/* Did we finish up the last of the reqest? */
if (fiemap->fm_length >= llength)
break;
/* Set up the next call arguments */
fiemap->fm_start += fiemap->fm_length;
llength -= fiemap->fm_length;
fiemap->fm_length = llength;
fiemap->fm_extent_count = count;
maxioctls--;
} while (!last && maxioctls > 0);
close(fd);
return 0;
}PATCH 1/3 vfs-level fiemap interface
Basic vfs-level fiemap infrastructure, which sets up a new ->fiemap inode operation.
---
(and I think also signed-off-by Andreas and/or Kalpak as well should also go here, since it's their work originally.)
include/linux/fiemap.h new file +49 -0
@@ -0,0 +1,49 @@ +/* +* FIEMAP ioctl infrastructure. +* +* Copyright (C) 2007 Cluster File Systems, Inc +* +* Author: Kalpak Shah <[email protected]> +* Andreas Dilger <[email protected]> +*/ + +#ifndef _LINUX_FIEMAP_H +#define _LINUX_FIEMAP_H + +struct fiemap_extent { +__u64 fe_offset;/* offset in bytes for the start of the extent */ +__u64 fe_length;/* length in bytes for the extent */ +__u32 fe_flags;/* returned FIEMAP_EXTENT_* flags for the extent */ +__u32 fe_lun;/* logical device number for extent (starting at 0)*/ +}; + +struct fiemap { +__u64 fm_start;/* logical starting byte offset (in/out) */ +__u64 fm_length;/* logical length of map (in/out) */ +__u32 fm_flags;/* FIEMAP_FLAG_* flags for request (in/out) */ +__u32 fm_extent_count;/* number of extents in fm_extents (in/out) */ +__u64 fm_end_offset;/* logical offset of end of mapping in last ioctl (out) */ +struct fiemap_extent fm_extents[0 ]; +}; + +#define FIEMAP_FLAG_SYNC 0x00000001/* sync file data before map */ +#define FIEMAP_FLAG_HSM_READ 0x00000002/* get data from HSM before map */ +#define FIEMAP_FLAG_NUM_EXTENTS 0x00000004/* return only number of extents */ +#define FIEMAP_FLAG_INCOMPAT 0xff000000/* error for unknown flags in here */ + +#define FIEMAP_FLAG_LUN_OFFSET 0x01000000/* use lun offsets, instead of +* logical file offsets */ + +#define FIEMAP_EXTENT_HOLE 0x00000001/* has no data or space allocation */ +#define FIEMAP_EXTENT_UNWRITTEN 0x00000002/* space allocated, but no data */ +#define FIEMAP_EXTENT_UNKNOWN 0x00000004/* in use, location unknown */ +#define FIEMAP_EXTENT_ERROR 0x00000008/* mapping error, errno in fe_start*/ +#define FIEMAP_EXTENT_NO_DIRECT 0x00000010/* cannot access data directly */ +#define FIEMAP_EXTENT_LAST 0x00000020/* last extent in the file */ +#define FIEMAP_EXTENT_DELALLOC 0x00000040/* has data but not yet written, +* must have EXTENT_UNKNOWN set */ +#define FIEMAP_EXTENT_SECONDARY 0x00000080/* data (also) in secondary storage, +* not in primary if EXTENT_UNKNOWN*/ +#define FIEMAP_EXTENT_EOF 0x00000100/* if fm_start+fm_len is beyond EOF*/ + +#endif /* _LINUX_FIEMAP_H */
include/linux/fs.h +3 -0
@@ -228,6 +228,7 @@ extern int dir_notify_enable;#define FS_IOC_SETFLAGS _IOW('f', 2, long)#define FS_IOC_GETVERSION _IOR('v', 1, long)#define FS_IOC_SETVERSION _IOW('v', 2, long) +#define FS_IOC_FIEMAP _IOWR('f', 10, struct fiemap)#define FS_IOC32_GETFLAGS _IOR('f', 1, int)#define FS_IOC32_SETFLAGS _IOW('f', 2, int)#define FS_IOC32_GETVERSION _IOR('v', 1, int)
@@ -287,6 +288,7 @@ extern int dir_notify_enable;#include <linux/pid.h> #include <linux/mutex.h> #include <linux/capability.h> +#include <linux/fiemap.h> #include <asm/atomic.h> #include <asm/semaphore.h>
@@ -1223,6 +1225,7 @@ struct inode_operations {void (*truncate_range)(struct inode *,loff_t ,loff_t );long (*fallocate)(struct inode *inode,int mode,loff_t offset,loff_t len); +int (*fiemap) (struct inode *,unsigned long arg); };struct seq_file ;
fs/ioctl.c +30 -0
@@ -71,6 +71,34 @@ static int ioctl_fibmap(struct file *filreturn put_user (res, p); } +static int ioctl_fiemap (struct file *filp,unsigned long arg) +{ +struct fiemap fiemap_s; +struct inode *inode = filp->f_path.dentry->d_inode; +int error =0 ; + +if (!inode->i_op->fiemap) +return -EOPNOTSUPP ; + +if (copy_from_user (&fiemap_s, (struct fiemap __user *)arg, +sizeof (struct fiemap ))) +return -EFAULT ; + +/* Need arg sanity checking: +* start >= 0? Must be; unsigned. +* length > 0? (or is -1 valid?) +* extent count non-zero if not FLAG_NUM_EXTENTS +*/ + +/* Should fs do this under a lock? */ +if (fiemap_s.fm_flags &FIEMAP_FLAG_SYNC ) +filemap_write_and_wait (inode->i_mapping); + + error = inode->i_op->fiemap (inode, arg); + +return error; +} +static int file_ioctl (struct file *filp,unsigned int cmd,unsigned long arg) {
@@ -80,6 +108,8 @@ static int file_ioctl(struct file *filp,switch (cmd) {case FIBMAP :return ioctl_fibmap (filp, p); +case FS_IOC_FIEMAP : +return ioctl_fiemap (filp, arg);case FIGETBSZ :return put_user (inode->i_sb->s_blocksize, p);case FIONREAD :
PATCH 2/3 reinstate ext4_ext_walk_space()
In reply to Eric Sandeenthe ext4 fiemap call needs this helper function.
I need to dig for the original author to see what signed-off-by lines should be here.
-Eric
fs/ext4/extents.c +106 -0
@@ -1588,6 +1588,112 @@ cleanup:return err; } +int ext4_ext_walk_space (struct inode *inode,ext4_lblk_t block, +ext4_lblk_t num,ext_prepare_callback func, +void *cbdata) +{ +struct ext4_ext_path *path =NULL ; +struct ext4_ext_cache cbex; +struct ext4_extent *ex; +ext4_lblk_t next, start =0 , end =0 ; +ext4_lblk_t last = block + num; +int depth, exists, err =0 ; + +BUG_ON (func ==NULL ); +BUG_ON (inode ==NULL ); + +while (block < last && block !=EXT_MAX_BLOCK ) { + num = last - block; +/* find extent for this block */ + path =ext4_ext_find_extent (inode, block, path); +if (IS_ERR (path)) { + err =PTR_ERR (path); + path =NULL ; +break ; + } + + depth =ext_depth (inode); +BUG_ON (path[depth].p_hdr ==NULL ); + ex = path[depth].p_ext; + next =ext4_ext_next_allocated_block (path); + + exists =0 ; +if (!ex) { +/* there is no extent yet, so try to allocate +* all requested space */ + start = block; + end = block + num; + }else if (le32_to_cpu (ex->ee_block) > block) { +/* need to allocate space before found extent */ + start = block; + end =le32_to_cpu (ex->ee_block); +if (block + num < end) + end = block + num; + }else if (block >=le32_to_cpu (ex->ee_block) + +ext4_ext_get_actual_len (ex)) { +/* need to allocate space after found extent */ + start = block; + end = block + num; +if (end >= next) + end = next; + }else if (block >=le32_to_cpu (ex->ee_block)) { +/* +* some part of requested space is covered +* by found extent +*/ + start = block; + end =le32_to_cpu (ex->ee_block) + +ext4_ext_get_actual_len (ex); +if (block + num < end) + end = block + num; + exists =1 ; + }else { +BUG (); + } +BUG_ON (end <= start); + +if (!exists) { + cbex.ec_block = start; + cbex.ec_len = end - start; + cbex.ec_start =0 ; + cbex.ec_type =EXT4_EXT_CACHE_GAP ; + }else { + cbex.ec_block =le32_to_cpu (ex->ee_block); + cbex.ec_len =ext4_ext_get_actual_len (ex); + cbex.ec_start =ext_pblock (ex); + cbex.ec_type =EXT4_EXT_CACHE_EXTENT ; + } + +BUG_ON (cbex.ec_len ==0 ); + err =func (inode, path, &cbex, cbdata); +ext4_ext_drop_refs (path); + +if (err <0 ) +break ; +if (err ==EXT_REPEAT ) +continue ; +else if (err ==EXT_BREAK ) { + err =0 ; +break ; + } + +if (ext_depth (inode) != depth) { +/* depth was changed. we have to realloc path */ +kfree (path); + path =NULL ; + } + + block = cbex.ec_block + cbex.ec_len; + } + +if (path) { +ext4_ext_drop_refs (path); +kfree (path); + } + +return err; +} +static void ext4_ext_put_in_cache (struct inode *inode,ext4_lblk_t block,__u32 len,ext4_fsblk_t start,int type)
include/linux/ext4_fs_extents.h +14 -0
@@ -124,6 +124,19 @@ struct ext4_ext_path {#define EXT4_EXT_CACHE_GAP 1#define EXT4_EXT_CACHE_EXTENT 2 +/* +* to be called by ext4_ext_walk_space() +* negative retcode - error +* positive retcode - signal for ext4_ext_walk_space(), see below +* callback must return valid extent (passed or newly created) +*/ +typedef int (*ext_prepare_callback )(struct inode *,struct ext4_ext_path *, +struct ext4_ext_cache *, +void *); + +#define EXT_CONTINUE 0 +#define EXT_BREAK 1 +#define EXT_REPEAT 2#define EXT_MAX_BLOCK 0xffffffff
@@ -221,6 +234,7 @@ extern int ext4_ext_try_to_merge(struct struct ext4_extent *);extern unsigned int ext4_ext_check_overlap (struct inode *,struct ext4_extent *,struct ext4_ext_path *);extern int ext4_ext_insert_extent (handle_t *,struct inode *,struct ext4_ext_path *,struct ext4_extent *); +extern int ext4_ext_walk_space (struct inode *,ext4_lblk_t ,ext4_lblk_t ,ext_prepare_callback ,void *);extern struct ext4_ext_path *ext4_ext_find_extent (struct inode *,ext4_lblk_t ,struct ext4_ext_path *); extern int ext4_ext_search_left(struct inode *, struct ext4_ext_path *,
Re: [PATCH 2/3] reinstate ext4_ext_walk_space()
Quoted 4 lines · Eric Sandeen
the ext4 fiemap call needs this helper function.
I need to dig for the original author to see what signed-off-by lines should be here.
This was originally written by Alex and was in the extents patches but later removed.
Quoted 151 lines
Index:
linux-2.6.25-rc1/fs/ext4/extents.c
===================================================================fs/ext4/extents.c +106
-0@@ -1588,6 +1588,112 @@ cleanup:return err; } +int ext4_ext_walk_space (struct inode *inode,ext4_lblk_t block, +ext4_lblk_t num,ext_prepare_callback func, +void *cbdata) +{ +struct ext4_ext_path *path =NULL ; +struct ext4_ext_cache cbex; +struct ext4_extent *ex; +ext4_lblk_t next, start =0 , end =0 ; +ext4_lblk_t last = block + num; +int depth, exists, err =0 ; + +BUG_ON (func ==NULL ); +BUG_ON (inode ==NULL ); + +while (block < last && block !=EXT_MAX_BLOCK ) { + num = last - block; +/* find extent for this block */ + path =ext4_ext_find_extent (inode, block, path); +if (IS_ERR (path)) { + err =PTR_ERR (path); + path =NULL ; +break ; + } + + depth =ext_depth (inode); +BUG_ON (path[depth].p_hdr ==NULL ); + ex = path[depth].p_ext; + next =ext4_ext_next_allocated_block (path); + + exists =0 ; +if (!ex) { +/* there is no extent yet, so try to allocate +* all requested space */ + start = block; + end = block + num; + }else if (le32_to_cpu (ex->ee_block) > block) { +/* need to allocate space before found extent */ + start = block; + end =le32_to_cpu (ex->ee_block); +if (block + num < end) + end = block + num; + }else if (block >=le32_to_cpu (ex->ee_block) + +ext4_ext_get_actual_len (ex)) { +/* need to allocate space after found extent */ + start = block; + end = block + num; +if (end >= next) + end = next; + }else if (block >=le32_to_cpu (ex->ee_block)) { +/* +* some part of requested space is covered +* by found extent +*/ + start = block; + end =le32_to_cpu (ex->ee_block) + +ext4_ext_get_actual_len (ex); +if (block + num < end) + end = block + num; + exists =1 ; + }else { +BUG (); + } +BUG_ON (end <= start); + +if (!exists) { + cbex.ec_block = start; + cbex.ec_len = end - start; + cbex.ec_start =0 ; + cbex.ec_type =EXT4_EXT_CACHE_GAP ; + }else { + cbex.ec_block =le32_to_cpu (ex->ee_block); + cbex.ec_len =ext4_ext_get_actual_len (ex); + cbex.ec_start =ext_pblock (ex); + cbex.ec_type =EXT4_EXT_CACHE_EXTENT ; + } + +BUG_ON (cbex.ec_len ==0 ); + err =func (inode, path, &cbex, cbdata); +ext4_ext_drop_refs (path); + +if (err <0 ) +break ; +if (err ==EXT_REPEAT ) +continue ; +else if (err ==EXT_BREAK ) { + err =0 ; +break ; + } + +if (ext_depth (inode) != depth) { +/* depth was changed. we have to realloc path */ +kfree (path); + path =NULL ; + } + + block = cbex.ec_block + cbex.ec_len; + } + +if (path) { +ext4_ext_drop_refs (path); +kfree (path); + } + +return err; +} +static void ext4_ext_put_in_cache (struct inode *inode,ext4_lblk_t block,__u32 len,ext4_fsblk_t start,int type)Index:
linux-2.6.25-rc1/include/linux/ext4_fs_extents.h
===================================================================include/linux/ext4_fs_extents.h +14
-1@@ -124,6 +124,19 @@ struct ext4_ext_path {#define EXT4_EXT_CACHE_GAP 1#define EXT4_EXT_CACHE_EXTENT 2 +/* +* to be called by ext4_ext_walk_space() +* negative retcode - error +* positive retcode - signal for ext4_ext_walk_space(), see below +* callback must return valid extent (passed or newly created) +*/ +typedef int (*ext_prepare_callback )(struct inode *,struct ext4_ext_path *, +struct ext4_ext_cache *, +void *); + +#define EXT_CONTINUE 0 +#define EXT_BREAK 1 +#define EXT_REPEAT 2#define EXT_MAX_BLOCK 0xffffffff@@ -221,6 +234,7 @@ extern int ext4_ext_try_to_merge(struct struct ext4_extent *);extern unsigned int ext4_ext_check_overlap (struct inode *,struct ext4_extent *,struct ext4_ext_path *);extern int ext4_ext_insert_extent (handle_t *,struct inode *,struct ext4_ext_path *,struct ext4_extent *); +extern int ext4_ext_walk_space (struct inode *,ext4_lblk_t ,ext4_lblk_t ,ext_prepare_callback ,void *);extern struct ext4_ext_path *ext4_ext_find_extent (struct inode *,ext4_lblk_t ,struct ext4_ext_path *); extern int ext4_ext_search_left(struct inode *, struct ext4_ext_path *, --
Quoted 3 lines
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.
PATCH 3/3 ext4 fiemap implementation
In reply to Eric SandeenHere is ext4_fiemap() itself. This still needs a bit of testing & work, but is correct for most file layouts.... I still hit occasional problems with interesting mappings such as sparse/preallocated/etc.
... again also probably signed-off-by Andreas & Kalpak as well.
-Eric
fs/ext4/extents.c +182 -1
@@ -42,6 +42,7 @@#include <linux/slab.h> #include <linux/falloc.h> #include <linux/ext4_fs_extents.h> +#include <linux/fiemap.h> #include <asm/uaccess.h>
@@ -1665,7 +1666,7 @@ int ext4_ext_walk_space(struct inode *in }BUG_ON (cbex.ec_len ==0 ); - err =func (inode, path, &cbex, cbdata); + err =func (inode, path, &cbex, ex, cbdata);ext4_ext_drop_refs (path);if (err <0 )
@@ -3000,3 +3001,183 @@ retry:mutex_unlock (&inode->i_mutex);return ret >0 ? ret2 : ret; } + +struct fiemap_internal { +struct fiemap *fiemap_s; +struct fiemap_extent fm_extent; +size_t tot_mapping_len; +char *cur_ext_ptr; +int current_extent; +int err; +}; + +/* +* Callback function called for each extent to gather FIEMAP information. +*/ +int ext4_ext_fiemap_cb (struct inode *inode,struct ext4_ext_path *path, +struct ext4_ext_cache *newex,struct ext4_extent *ex, +void *data) +{ +struct fiemap_internal *fiemap_i = data; +struct fiemap *fiemap_s = fiemap_i->fiemap_s; +struct fiemap_extent *fm_extent = &fiemap_i->fm_extent; +int current_extent = fiemap_i->current_extent; +unsigned long blksize_bits = inode->i_sb->s_blocksize_bits; + +/* +* ext4_ext_walk_space returns a hole for extents that have not been +* allocated yet. +*/ +if (((u64)(newex->ec_block + newex->ec_len) << blksize_bits >= + inode->i_size) && newex->ec_type ==EXT4_EXT_CACHE_GAP ) { +if (((u64 )newex->ec_block << blksize_bits) < inode->i_size) + newex->ec_len = (inode->i_size - ((u64 )newex->ec_block<< + blksize_bits)) >> blksize_bits; +else +return EXT_BREAK ; + } + +/* +* We only need to return number of extents and total length of mapping +*/ +if (fiemap_s->fm_flags &FIEMAP_FLAG_NUM_EXTENTS ) { + fiemap_i->tot_mapping_len += ((__u64 )newex->ec_len << + blksize_bits); + goto count_extents; + } + +if (current_extent >= fiemap_s->fm_extent_count) +return EXT_BREAK ; + +/* caller's start should be set to the start of the first extent (or hole...?) */ +if (newex->ec_block << blksize_bits < fiemap_s->fm_start) + fiemap_s->fm_start = newex->ec_block << blksize_bits; + +memset (fm_extent,0 ,sizeof (*fm_extent)); + fm_extent->fe_offset = (__u64 )newex->ec_start << blksize_bits; + fm_extent->fe_length = (__u64 )newex->ec_len << blksize_bits; + fiemap_i->tot_mapping_len += fm_extent->fe_length;/* move this above the goto? */ + +if (newex->ec_type ==EXT4_EXT_CACHE_GAP ) + fm_extent->fe_flags |=FIEMAP_EXTENT_HOLE ; +else if (ex &&ext4_ext_is_uninitialized (ex)) + fm_extent->fe_flags |=FIEMAP_EXTENT_UNWRITTEN ; + +/* +* Mark this fiemap_extent as FIEMAP_EXTENT_EOF if it's past the end +* of file. +*/ +/* block + len to bytes... >= size? check off by one */ +if ((u64)(newex->ec_block + newex->ec_len) << blksize_bits >= + inode->i_size) + fm_extent->fe_flags |=FIEMAP_EXTENT_EOF ; +// XXX ERS HACK AROUND _LAST problem +//fm_extent->fe_flags |= (FIEMAP_EXTENT_EOF|FIEMAP_EXTENT_LAST); + +if (!copy_to_user (fiemap_i->cur_ext_ptr, fm_extent, +sizeof (struct fiemap_extent ))) { +/* c_t_u succeeded, advance current exent ptr to next */ + fiemap_i->cur_ext_ptr +=sizeof (struct fiemap_extent ); + }else { + fiemap_i->err = -EFAULT ; +return EXT_BREAK ; + } + +count_extents: +/* +* Don't count holes when only returning number of extents +* XXX ERS hm, ok if that's how it's defined... +*/ +if (!((fiemap_s->fm_flags &FIEMAP_FLAG_NUM_EXTENTS ) && + (newex->ec_type ==EXT4_EXT_CACHE_GAP ))) + fiemap_i->current_extent++;/* hmm why? oh, advance count */ + +/* +* Stop if we are beyond requested mapping size but return complete last +* extent. +*/ + +/* is this extent's last byte >= length of mapping? +* (XXX really? not start+length of mapping? */ +if ((u64)(newex->ec_block + newex->ec_len) << blksize_bits >= + fiemap_s->fm_length) +return EXT_BREAK ; + +return EXT_CONTINUE ; +} + +int ext4_fiemap (struct inode *inode,unsigned long arg) +{ +struct fiemap *fiemap_s; +struct fiemap_internal fiemap_i; +struct fiemap_extent *last_extent; +ext4_lblk_t start_blk; +int fm_extent_size =sizeof (struct fiemap_extent ); +int err =0 ; + +/* could use getblock here for non-extent files? */ +if (!(EXT4_I (inode)->i_flags &EXT4_EXTENTS_FL )) +return -EOPNOTSUPP ; + + fiemap_s =kmalloc (sizeof (*fiemap_s),GFP_KERNEL ); +if (fiemap_s ==NULL ) +return -ENOMEM ; + +if (copy_from_user (fiemap_s, (struct fiemap __user *)arg, +sizeof (*fiemap_s))) { + err = -EFAULT ; + goto out_free; + } + +/* bail on unsupported flags for this fs */ +if (fiemap_s->fm_flags &EXT4_FIEMAP_FLAG_INCOMPAT_UNSUPP ) { + err = -EOPNOTSUPP ; + goto out_free; + } + + start_blk = fiemap_s->fm_start >> inode->i_sb->s_blocksize_bits; + fiemap_i.fiemap_s = fiemap_s; + fiemap_i.tot_mapping_len =0 ; + fiemap_i.cur_ext_ptr = (char *)(arg +sizeof (*fiemap_s)); + fiemap_i.current_extent =0 ; + fiemap_i.err =0 ; + + start_blk = fiemap_s->fm_start >> inode->i_sb->s_blocksize_bits; + +/* +* Walk the extent tree gathering extent information +*/ +down_write (&EXT4_I (inode)->i_data_sem); + err =ext4_ext_walk_space (inode, start_blk,EXT_MAX_BLOCK - start_blk, + ext4_ext_fiemap_cb, &fiemap_i); +up_write (&EXT4_I (inode)->i_data_sem); +if (err) + goto out_free; + + fiemap_s->fm_extent_count = fiemap_i.current_extent; + fiemap_s->fm_length = fiemap_i.tot_mapping_len; +/* +* Mark last extent as EXTENT_LAST and copy the extent to userspace.` +* XXX ERS fixme, this isn't always working. +*/ +if (fiemap_i.current_extent !=0 && + fiemap_i.current_extent < fiemap_s->fm_extent_count && + !(fiemap_s->fm_flags &FIEMAP_FLAG_NUM_EXTENTS )) { +char *dest; + + last_extent = &fiemap_i.fm_extent; + last_extent->fe_flags |=FIEMAP_EXTENT_LAST ; + dest = (char *)arg +sizeof (*fiemap_s) + fm_extent_size * + (fiemap_s->fm_extent_count -1 ); + err =copy_to_user (dest, last_extent, fm_extent_size); +if (err) + goto out_free; + } + + err =copy_to_user ((void *)arg, fiemap_s,sizeof (*fiemap_s)); + +out_free: +kfree (fiemap_s); +return err; +} +
fs/ext4/file.c +4 -0
@@ -140,6 +140,9 @@ static int ext4_file_mmap(struct file *freturn 0 ; } +/* XXX ERS should this go into this file? */ +extern int ext4_fiemap (struct inode *inode,unsigned long arg); +const struct file_operations ext4_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read,
@@ -169,5 +172,6 @@ const struct inode_operations ext4_file_#endif .permission = ext4_permission, .fallocate = ext4_fallocate, + .fiemap = ext4_fiemap, };
include/linux/ext4_fs.h +2 -0
@@ -317,6 +317,8 @@ struct ext4_new_group_data {#define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION +#define EXT4_FIEMAP_FLAG_INCOMPAT_UNSUPP (FIEMAP_FLAG_INCOMPAT & \ + ~(FIEMAP_FLAG_LUN_OFFSET))/* * Mount options
include/linux/ext4_fs_extents.h +1 -1
@@ -132,7 +132,7 @@ struct ext4_ext_path {*/ typedef int (*ext_prepare_callback )(struct inode *,struct ext4_ext_path *,struct ext4_ext_cache *, -void *); +struct ext4_extent *,void *);#define EXT_CONTINUE 0#define EXT_BREAK 1
Quoted 5 lines · Eric Sandeen
Since Akira would like to use the fiemap ioctl for defrag, I thought I should put what I have so far out on the list, at least. This could go in the unstable part of the tree if you like, though I need to do more testing etc before it's really ready to go.
I added the patches to the unstable queue, just fyi.
Quoted 314 lines
Also, below is a quick test application I was using with the ioctl.
-Eric
#include <
stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <ext2fs/ext2_types.h>/*************************************************/
/* All this should come fromfiemap.heventually */
structfiemap_extent{
__u64fe_offset; /* offset in bytes for the start of the extent */__u64fe_length; /* length in bytes for the extent */__u32fe_flags; /* returned FIEMAP_EXTENT_* flags for the extent */
__u32fe_lun; /* logical device number for extent (starting at 0) */
};struct fiemap { __u64 fm_start; /* logical starting byte offset (in/out) */ __u64 fm_length; /* logical length of map (in/out) */ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ __u32 fm_extent_count; /* number of extents in fm_extents (in/out) */ __u64 fm_end_offset; /* logical offset of end of mapping in last ioctl */ struct fiemap_extent fm_extents[0]; };#define
FIEMAP_FLAG_SYNC0x00000001/* sync file data before map */
#defineFIEMAP_FLAG_HSM_READ0x00000002/* get data from HSM before map */
#defineFIEMAP_FLAG_NUM_EXTENTS0x00000004/* return only number of extents */
#defineFIEMAP_FLAG_INCOMPAT0xff000000/* error for unknown flags in here */
#defineFIEMAP_FLAG_LUN_OFFSET0x01000000/* use lun offsets, instead of
* logical file offsets */#define
FIEMAP_EXTENT_HOLE0x00000001/* has no data or space allocation */
#defineFIEMAP_EXTENT_UNWRITTEN0x00000002/* space allocated, but no data */ #defineFIEMAP_EXTENT_UNMAPPED0x00000004/* has data but no space allocation*/
#defineFIEMAP_EXTENT_ERROR0x00000008/* mapping error, errno in fe_start*/
#defineFIEMAP_EXTENT_NO_DIRECT0x00000010/* cannot access data directly */
#defineFIEMAP_EXTENT_LAST0x00000020/* last extent in the file */
#defineFIEMAP_EXTENT_DELALLOC0x00000040/* has data but not yet written,
must haveEXTENT_UNKNOWNset */
#defineFIEMAP_EXTENT_SECONDARY0x00000080/* data (also) in secondary storage,
not in primary if EXTENT_UNKNOWN*/
#defineFIEMAP_EXTENT_EOF0x00000100/* if fm_start+fm_len is beyond EOF*/#define FIGETBSZ
_IO(0x00, 2)/* get the block size used for bmap */
#defineEXT4_IOC_FIEMAP_IOWR('f', 10, struct fiemap)/* get file extent info*//* End of what should be coming from
fiemap.h*/
/**********************************************/void usage(void)
{ printf("Usage: fiemap [-vrSCL] [-s start] [-l length] [-c buf count] [-m max] filename\n"); printf(" -v : verbose mode\n"); printf(" -r : raw output: print raw ioctl structure values\n"); printf(" -S : set FIEMAP_FLAG_SYNC to sync before mapping\n"); printf(" -C : set FIEMAP_FLAG_NUM_EXTENTS to only get extent count, not mapping\n"); printf(" -L : set FIEMAP_FLAG_LUN_OFFSET to report extents in lun order\n"); printf(" -s start : start of mapping in bytes (default 0)\n"); printf(" -l length : length of mapping in bytes (default to end of file)\n"); printf(" -c count : count of extents in ioctl input structure (default 32)\n"); printf(" -m max : max nr of ioctls to call before exit (default 512)\n"); exit(EXIT_FAILURE); }#define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) #define GIGABYTES(x) ((long long)(x) << 30) #define MEGABYTES(x) ((long long)(x) << 20) #define KILOBYTES(x) ((long long)(x) << 10)long long
cvtnum(char *s){ long long i; char *sp; int c; i = strtoll(s, &sp, 0); if (i == 0 && sp == s) return -1LL; if (*sp == '\0') return i; if (sp[1] != '\0') return -1LL; c = tolower(*sp); switch (c) { case 'k': return KILOBYTES(i); case 'm': return MEGABYTES(i); case 'g': return GIGABYTES(i); case 't': return TERABYTES(i); case 'p': return PETABYTES(i); case 'e': return EXABYTES(i); } return -1LL; }void
show_extents_table(struct fiemap *fiemap, int blocksize, int start_extent, int *is_last){ unsigned int i; __u64 lstart; lstart = fiemap->fm_start; for (i = 0; i < fiemap->fm_extent_count; i++) { __u64 length = fiemap->fm_extents[i].fe_length; __u64 phys = fiemap->fm_extents[i].fe_offset; int flags = fiemap->fm_extents[i].fe_flags; printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu flags: 0x%03X tot: %llu\n", i + start_extent, lstart, lstart + length - 1, (phys / blocksize), (flags & FIEMAP_EXTENT_HOLE) ? 0 : (phys + length - 1) / blocksize, flags, (length / blocksize)); lstart += length; if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { *is_last = 1; return; /* XXX should we? or should look for exents filled in past last? */ } } }void
show_extents_raw(struct fiemap *fiemap, int start_extent, int *is_last){ unsigned int i; for (i = 0; i < fiemap->fm_extent_count; i++) { printf("\tExtent %3u: start: %10lld length: %10lld flags 0x%03X lun %3u\n", i + start_extent, fiemap->fm_extents[i].fe_offset, fiemap->fm_extents[i].fe_length, fiemap->fm_extents[i].fe_flags, fiemap->fm_extents[i].fe_lun); if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { *is_last = 1; return; /* XXX should we? or should look for exents filled in past last? */ } } }int
main(int argc, char**argv){ int blocksize = 0; /* filesystem blocksize */ uint count = 32; /* extent count */ int countonly = 0; /* return only extent count? */ int fd; /* file descriptor */ int last = 0; /* last extent found */ int lunwise = 0; /* return extents lun-wise */ int maxioctls = 512; /* max ioctls to try */ int opt; int rc; int raw = 0; /* raw output format */ int sync = 0; /* sync file before mapping */ int verbose = 0; /* verbose output */ char *fname; /* filename to map */ char *fiebuf; /* fiemap buffer / ioctl argument */ __u64 lstart = 0; /* logical input mapping start */ __u64 llength = ~0ULL;/* logical input mapping length */ uint start_ext = 0; /* starting extent nr. for this batch */ struct fiemap *fiemap; while ((opt = getopt(argc, argv, "s:l:c:m:rSCLv")) != -1) { switch(opt) { /* logical mapping offset */ case 's': lstart = cvtnum(optarg); break; /* logical mapping length */ case 'l': llength = cvtnum(optarg); break; /* count of extent buffers to send */ case 'c': count = atoi(optarg); break; /* max nr. of ioctls to try (safety net) */ case 'm': maxioctls = atoi(optarg); break; /* raw format output */ case 'r': raw++; break; /* sync file before mapping */ case 'S': sync++; break; /* count extents only, no details */ case 'C': countonly++; break; /* return extents in lun order */ case 'L': lunwise++; break; /* be verbose */ case 'v': verbose++; break; default: usage(); } } fname = argv[optind++]; if (!fname) usage(); /* The whole buffer, extent maps and all */ fiebuf = malloc(sizeof (struct fiemap) + (count * sizeof(struct fiemap_extent))); if (!fiebuf) { perror("Could not allocate fiemap buffers"); exit(1); } /* Just the header */ fiemap = (struct fiemap *)fiebuf; fiemap->fm_start = lstart; fiemap->fm_length = llength; fiemap->fm_flags = 0; if (sync) fiemap->fm_flags |= FIEMAP_FLAG_SYNC; if (countonly) fiemap->fm_flags |= FIEMAP_FLAG_NUM_EXTENTS; if (lunwise) fiemap->fm_flags |= FIEMAP_FLAG_LUN_OFFSET; fiemap->fm_extent_count = count; fiemap->fm_end_offset = 0; /* output only */ fd = open(fname, O_RDONLY); if (fd < 0) { perror("Can't open file"); exit(1); } if (ioctl(fd, FIGETBSZ, &blocksize) < 0) { perror("Can't get block size"); close(fd); return; } do { if (verbose) printf("Input: start %llu length %llu flags 0x%X count %u end_offset %lld\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count, fiemap->fm_end_offset); rc = ioctl(fd, EXT4_IOC_FIEMAP, (unsigned long)fiemap); if (rc < 0) { perror("FIEMAP ioctl failed"); close(fd); exit(1); } if (verbose) printf("Output: start %llu length %llu flags 0x%X count %u end_offset %lld\n", fiemap->fm_start, fiemap->fm_length, fiemap->fm_flags, fiemap->fm_extent_count, fiemap->fm_end_offset); if (raw) show_extents_raw(fiemap, start_ext, &last); else show_extents_table(fiemap, blocksize, start_ext, &last); start_ext += fiemap->fm_extent_count; /* Did we finish up the last of the reqest? */ if (fiemap->fm_length >= llength) break; /* Set up the next call arguments */ fiemap->fm_start += fiemap->fm_length; llength -= fiemap->fm_length; fiemap->fm_length = llength; fiemap->fm_extent_count = count; maxioctls--; } while (!last && maxioctls > 0); close(fd); return 0; }--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 6 lines · Eric Sandeen
Since Akira would like to use the fiemap ioctl for defrag, I thought I should put what I have so far out on the list, at least. This could go in the unstable part of the tree if you like, though I need to do more testing etc before it's really ready to go.
Also, below is a quick test application I was using with the ioctl.
Note that we have a patch for the e2fsprogs filefrag tool to use FIEMAP ioctl first, and fall back to FIBMAP if that returns -EOPNOTSUPP or -ENOTTY.
Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.