linux-ext4.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] fiemap patches (RFC/testing)
@ 2008-03-26 15:37 Eric Sandeen
  2008-03-26 15:40 ` [PATCH 1/3] vfs-level fiemap interface Eric Sandeen
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Eric Sandeen @ 2008-03-26 15:37 UTC (permalink / raw)
  To: ext4 development

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;
}


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/3] vfs-level fiemap interface
  2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
@ 2008-03-26 15:40 ` Eric Sandeen
  2008-03-26 15:42 ` [PATCH 2/3] reinstate ext4_ext_walk_space() Eric Sandeen
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Eric Sandeen @ 2008-03-26 15:40 UTC (permalink / raw)
  To: ext4 development

Basic vfs-level fiemap infrastructure, which sets up a new
->fiemap inode operation.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---

(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 <kalpak.shah@sun.com>
+ *	   Andreas Dilger <adilger@sun.com>
+ */
+
+#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:


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/3] reinstate ext4_ext_walk_space()
  2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
  2008-03-26 15:40 ` [PATCH 1/3] vfs-level fiemap interface Eric Sandeen
@ 2008-03-26 15:42 ` Eric Sandeen
  2008-04-11 23:31   ` Andreas Dilger
  2008-03-26 15:44 ` [PATCH 3/3] ext4 fiemap implementation Eric Sandeen
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Eric Sandeen @ 2008-03-26 15:42 UTC (permalink / raw)
  To: ext4 development

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 *,


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 3/3] ext4 fiemap implementation
  2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
  2008-03-26 15:40 ` [PATCH 1/3] vfs-level fiemap interface Eric Sandeen
  2008-03-26 15:42 ` [PATCH 2/3] reinstate ext4_ext_walk_space() Eric Sandeen
@ 2008-03-26 15:44 ` Eric Sandeen
  2008-03-26 23:07 ` [PATCH 0/3] fiemap patches (RFC/testing) Mingming Cao
  2008-04-11 23:29 ` Andreas Dilger
  4 siblings, 0 replies; 7+ messages in thread
From: Eric Sandeen @ 2008-03-26 15:44 UTC (permalink / raw)
  To: ext4 development

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 <sandeen@redhat.com>
... 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


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/3] fiemap patches (RFC/testing)
  2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
                   ` (2 preceding siblings ...)
  2008-03-26 15:44 ` [PATCH 3/3] ext4 fiemap implementation Eric Sandeen
@ 2008-03-26 23:07 ` Mingming Cao
  2008-04-11 23:29 ` Andreas Dilger
  4 siblings, 0 replies; 7+ messages in thread
From: Mingming Cao @ 2008-03-26 23:07 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: ext4 development

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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/3] fiemap patches (RFC/testing)
  2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
                   ` (3 preceding siblings ...)
  2008-03-26 23:07 ` [PATCH 0/3] fiemap patches (RFC/testing) Mingming Cao
@ 2008-04-11 23:29 ` Andreas Dilger
  4 siblings, 0 replies; 7+ messages in thread
From: Andreas Dilger @ 2008-04-11 23:29 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: ext4 development, Kalpak Shah

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.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/3] reinstate ext4_ext_walk_space()
  2008-03-26 15:42 ` [PATCH 2/3] reinstate ext4_ext_walk_space() Eric Sandeen
@ 2008-04-11 23:31   ` Andreas Dilger
  0 siblings, 0 replies; 7+ messages in thread
From: Andreas Dilger @ 2008-04-11 23:31 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: ext4 development

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 majordomo@vger.kernel.org
> 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.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-04-11 23:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-26 15:37 [PATCH 0/3] fiemap patches (RFC/testing) Eric Sandeen
2008-03-26 15:40 ` [PATCH 1/3] vfs-level fiemap interface Eric Sandeen
2008-03-26 15:42 ` [PATCH 2/3] reinstate ext4_ext_walk_space() Eric Sandeen
2008-04-11 23:31   ` Andreas Dilger
2008-03-26 15:44 ` [PATCH 3/3] ext4 fiemap implementation Eric Sandeen
2008-03-26 23:07 ` [PATCH 0/3] fiemap patches (RFC/testing) Mingming Cao
2008-04-11 23:29 ` Andreas Dilger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).