Skip to contentSkip to conversation

fiemap patches (RFC/testing)

PATCH In review Eric Sandeen 3 participants 7 messages Mar 26, 2008 – Apr 11, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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 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;
}
#1 lore raw

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
Index: 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 */
include/linux/fs.h +3 -0
Index: 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;
fs/ioctl.c +30 -0
Index: 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:
#2 lore raw

PATCH 2/3 reinstate ext4_ext_walk_space()

In reply to 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.

-Eric

fs/ext4/extents.c +106 -0
Index: 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)
include/linux/ext4_fs_extents.h +14 -0
Index: 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 *,
#3 lore raw

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.

#4 lore raw

PATCH 3/3 ext4 fiemap implementation

In reply to Eric Sandeen

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.

... again also probably signed-off-by Andreas & Kalpak as well.

-Eric

fs/ext4/extents.c +182 -1
Index: 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;
+}
+
fs/ext4/file.c +4 -0
Index: 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,
 };
 
include/linux/ext4_fs.h +2 -0
Index: 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
include/linux/ext4_fs_extents.h +1 -1
Index: 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
#5 lore raw
In reply to Eric Sandeen
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 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

#6 lore raw
In reply to Eric Sandeen
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.