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 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;
}
Basic vfs-level fiemap infrastructure, which sets up a new
->fiemap inode operation.
Signed-off-by: Eric Sandeen <[email protected]>
---
(and I think also signed-off-by Andreas and/or Kalpak as well should
also go here, since it's their work originally.)
Index: linux-2.6.25-rc1/include/linux/fiemap.h
===================================================================
--- /dev/null
+++ linux-2.6.25-rc1/include/linux/fiemap.h
@@ -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 */
Index: linux-2.6.25-rc1/include/linux/fs.h
===================================================================
--- linux-2.6.25-rc1.orig/include/linux/fs.h
+++ linux-2.6.25-rc1/include/linux/fs.h
@@ -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;
Index: linux-2.6.25-rc1/fs/ioctl.c
===================================================================
--- linux-2.6.25-rc1.orig/fs/ioctl.c
+++ linux-2.6.25-rc1/fs/ioctl.c
@@ -71,6 +71,34 @@ static int ioctl_fibmap(struct file *fil
return 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:
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.
-Eric
Index: linux-2.6.25-rc1/fs/ext4/extents.c
===================================================================
--- linux-2.6.25-rc1.orig/fs/ext4/extents.c
+++ linux-2.6.25-rc1/fs/ext4/extents.c
@@ -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
===================================================================
--- linux-2.6.25-rc1.orig/include/linux/ext4_fs_extents.h
+++ linux-2.6.25-rc1/include/linux/ext4_fs_extents.h
@@ -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 *,
Here 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.
Signed-off-by: Eric Sandeen <[email protected]>
... again also probably signed-off-by Andreas & Kalpak as well.
-Eric
Index: linux-2.6.25-rc1/fs/ext4/extents.c
===================================================================
--- linux-2.6.25-rc1.orig/fs/ext4/extents.c
+++ linux-2.6.25-rc1/fs/ext4/extents.c
@@ -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;
+}
+
Index: linux-2.6.25-rc1/fs/ext4/file.c
===================================================================
--- linux-2.6.25-rc1.orig/fs/ext4/file.c
+++ linux-2.6.25-rc1/fs/ext4/file.c
@@ -140,6 +140,9 @@ static int ext4_file_mmap(struct file *f
return 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,
};
Index: linux-2.6.25-rc1/include/linux/ext4_fs.h
===================================================================
--- linux-2.6.25-rc1.orig/include/linux/ext4_fs.h
+++ linux-2.6.25-rc1/include/linux/ext4_fs.h
@@ -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
Index: linux-2.6.25-rc1/include/linux/ext4_fs_extents.h
===================================================================
--- linux-2.6.25-rc1.orig/include/linux/ext4_fs_extents.h
+++ linux-2.6.25-rc1/include/linux/ext4_fs_extents.h
@@ -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
On Wed, 2008-03-26 at 10:37 -0500, Eric Sandeen wrote:
> 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.
> 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 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
On Mar 26, 2008 10:37 -0500, Eric Sandeen wrote:
> 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.
On Mar 26, 2008 10:42 -0500, Eric Sandeen wrote:
> 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.
> Index: linux-2.6.25-rc1/fs/ext4/extents.c
> ===================================================================
> --- linux-2.6.25-rc1.orig/fs/ext4/extents.c
> +++ linux-2.6.25-rc1/fs/ext4/extents.c
> @@ -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
> ===================================================================
> --- linux-2.6.25-rc1.orig/include/linux/ext4_fs_extents.h
> +++ linux-2.6.25-rc1/include/linux/ext4_fs_extents.h
> @@ -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 *,
>
> --
> 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.