* [PATCH 4/4] ext4: implement ext4_fiemap
@ 2008-06-25 22:19 Mark Fasheh
2008-07-03 17:01 ` Kalpak Shah
0 siblings, 1 reply; 3+ messages in thread
From: Mark Fasheh @ 2008-06-25 22:19 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Eric Sandeen
From: Eric Sandeen <sandeen@redhat.com>
Hook ext4 to the vfs fiemap interface.
ext4_ext_walk_space() was reinstated to be used for iterating over file
extents with a callback; it is used by the ext4 fiemap implementation.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
fs/ext4/ext4.h | 2 +
fs/ext4/ext4_extents.h | 15 +++
fs/ext4/extents.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/file.c | 4 +
fs/ext4/inode.c | 2 +-
5 files changed, 270 insertions(+), 1 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8158083..99e1c44 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1025,6 +1025,8 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *,
ext4_lblk_t, int, int *);
struct buffer_head *ext4_bread(handle_t *, struct inode *,
ext4_lblk_t, int, int *);
+int ext4_get_block(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create);
int ext4_get_blocks_handle(handle_t *handle, struct inode *inode,
ext4_lblk_t iblock, unsigned long maxblocks,
struct buffer_head *bh_result,
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h
index 75333b5..c9fabb0 100644
--- a/fs/ext4/ext4_extents.h
+++ b/fs/ext4/ext4_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 *,
+ struct ext4_extent *, void *);
+
+#define EXT_CONTINUE 0
+#define EXT_BREAK 1
+#define EXT_REPEAT 2
#define EXT_MAX_BLOCK 0xffffffff
@@ -221,6 +234,8 @@ extern int ext4_ext_try_to_merge(struct inode *inode,
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 *,
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 47929c4..c6309c3 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/falloc.h>
#include <asm/uaccess.h>
+#include <linux/fiemap.h>
#include "ext4_jbd2.h"
#include "ext4_extents.h"
@@ -1587,6 +1588,113 @@ 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, ex, 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)
@@ -2945,3 +3053,143 @@ retry:
mutex_unlock(&inode->i_mutex);
return ret > 0 ? ret2 : ret;
}
+
+/*
+ * 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_extent_info *fieinfo = data;
+ unsigned long blksize_bits = inode->i_sb->s_blocksize_bits;
+ __u64 logical;
+ __u64 physical;
+ __u64 length;
+ __u32 flags = 0;
+ int error;
+
+ logical = (__u64)newex->ec_block << blksize_bits;
+
+ if (newex->ec_type == EXT4_EXT_CACHE_GAP) {
+ pgoff_t offset;
+ struct page *page;
+ struct buffer_head *bh = NULL;
+
+ offset = logical >> PAGE_SHIFT;
+ page = find_get_page(inode->i_mapping, offset);
+ if (!page || !page_has_buffers(page))
+ return EXT_CONTINUE;
+
+ bh = page_buffers(page);
+
+ if (!bh)
+ return EXT_CONTINUE;
+
+ if (buffer_delay(bh)) {
+ flags |= FIEMAP_EXTENT_DELALLOC;
+ page_cache_release(page);
+ } else {
+ page_cache_release(page);
+ return EXT_CONTINUE;
+ }
+ }
+
+ physical = (__u64)newex->ec_start << blksize_bits;
+ length = (__u64)newex->ec_len << blksize_bits;
+
+ if (ex && ext4_ext_is_uninitialized(ex))
+ flags |= FIEMAP_EXTENT_UNWRITTEN;
+
+ /*
+ * If this extent reaches EXT_MAX_BLOCK, it must be last.
+ *
+ * Or if ext4_ext_next_allocated_block is EXT_MAX_BLOCK,
+ * this also indicates no more allocated blocks.
+ *
+ * XXX this might miss a single-block extent at EXT_MAX_BLOCK
+ */
+ if (logical + length - 1 == EXT_MAX_BLOCK ||
+ ext4_ext_next_allocated_block(path) == EXT_MAX_BLOCK)
+ flags |= FIEMAP_EXTENT_LAST;
+
+ error = fiemap_fill_next_extent(fieinfo, logical, physical,
+ length, flags, inode->i_sb->s_dev);
+ if (error < 0)
+ return error;
+ if (error == 1)
+ return EXT_BREAK;
+
+ return EXT_CONTINUE;
+}
+
+/* fiemap flags we can handle specified here */
+#define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+
+int ext4_xattr_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo)
+{
+ __u64 physical = 0;
+ __u64 length;
+ __u32 flags = FIEMAP_EXTENT_LAST;
+ int blockbits = inode->i_sb->s_blocksize_bits;
+ int error = 0;
+
+ /* in-inode? */
+ if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) {
+ struct ext4_iloc iloc;
+ int offset; /* offset of xattr in inode */
+
+ error = ext4_get_inode_loc(inode, &iloc);
+ if (error)
+ return error;
+ physical = iloc.bh->b_blocknr << blockbits;
+ offset = EXT4_GOOD_OLD_INODE_SIZE +
+ EXT4_I(inode)->i_extra_isize;
+ physical += offset;
+ length = EXT4_SB(inode->i_sb)->s_inode_size - offset;
+ flags |= FIEMAP_EXTENT_DATA_INLINE;
+ } else { /* external block */
+ physical = EXT4_I(inode)->i_file_acl << blockbits;
+ length = inode->i_sb->s_blocksize;
+ }
+
+ if (physical)
+ error = fiemap_fill_next_extent(fieinfo, 0, physical,
+ length, flags, inode->i_sb->s_dev);
+ return (error < 0 ? error : 0);
+}
+
+int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ __u64 start, __u64 len)
+{
+ ext4_lblk_t start_blk;
+ ext4_lblk_t len_blks;
+ int error = 0;
+
+ /* fallback to generic here if not in extents fmt */
+ if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
+ return generic_block_fiemap(inode, fieinfo, start, len,
+ ext4_get_block);
+
+ if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS))
+ return -EBADR;
+
+ if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
+ error = ext4_xattr_fiemap(inode, fieinfo);
+ } else {
+ start_blk = start >> inode->i_sb->s_blocksize_bits;
+ len_blks = len >> inode->i_sb->s_blocksize_bits;
+
+ /*
+ * Walk the extent tree gathering extent information.
+ * ext4_ext_fiemap_cb will push extents back to user.
+ */
+ down_write(&EXT4_I(inode)->i_data_sem);
+ error = ext4_ext_walk_space(inode, start_blk, len_blks,
+ ext4_ext_fiemap_cb, fieinfo);
+ up_write(&EXT4_I(inode)->i_data_sem);
+ }
+
+ return error;
+}
+
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 4159be6..99bde24 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -123,6 +123,9 @@ force_commit:
return ret;
}
+extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ __u64 start, __u64 len);
+
const struct file_operations ext4_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
@@ -152,5 +155,6 @@ const struct inode_operations ext4_file_inode_operations = {
#endif
.permission = ext4_permission,
.fallocate = ext4_fallocate,
+ .fiemap = ext4_fiemap,
};
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 8d97077..59ecbbc 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1000,7 +1000,7 @@ int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode, sector_t block,
return retval;
}
-static int ext4_get_block(struct inode *inode, sector_t iblock,
+int ext4_get_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
handle_t *handle = ext4_journal_current_handle();
--
1.5.4.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 4/4] ext4: implement ext4_fiemap
2008-06-25 22:19 [PATCH 4/4] ext4: implement ext4_fiemap Mark Fasheh
@ 2008-07-03 17:01 ` Kalpak Shah
2008-07-03 18:49 ` Eric Sandeen
0 siblings, 1 reply; 3+ messages in thread
From: Kalpak Shah @ 2008-07-03 17:01 UTC (permalink / raw)
To: Mark Fasheh; +Cc: linux-fsdevel, Eric Sandeen
On Wed, 2008-06-25 at 15:19 -0700, Mark Fasheh wrote:
> From: Eric Sandeen <sandeen@redhat.com>
> +/* fiemap flags we can handle specified here */
> +#define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
> +
> +int ext4_xattr_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo)
> +{
> + __u64 physical = 0;
> + __u64 length;
> + __u32 flags = FIEMAP_EXTENT_LAST;
> + int blockbits = inode->i_sb->s_blocksize_bits;
> + int error = 0;
> +
> + /* in-inode? */
> + if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) {
> + struct ext4_iloc iloc;
> + int offset; /* offset of xattr in inode */
> +
> + error = ext4_get_inode_loc(inode, &iloc);
> + if (error)
> + return error;
> + physical = iloc.bh->b_blocknr << blockbits;
> + offset = EXT4_GOOD_OLD_INODE_SIZE +
> + EXT4_I(inode)->i_extra_isize;
> + physical += offset;
> + length = EXT4_SB(inode->i_sb)->s_inode_size - offset;
> + flags |= FIEMAP_EXTENT_DATA_INLINE;
> + } else { /* external block */
> + physical = EXT4_I(inode)->i_file_acl << blockbits;
> + length = inode->i_sb->s_blocksize;
> + }
> +
> + if (physical)
> + error = fiemap_fill_next_extent(fieinfo, 0, physical,
> + length, flags, inode->i_sb->s_dev);
> + return (error < 0 ? error : 0);
> +}
> +
Extended attributes can be present in the inode as well as the external block at the same time and we should be returning extents for both them in that case. Below patch fixes this problem:
Signed-off-by: Kalpak Shah <kalpak.shah@sun.com>
Index: linux-2.6/fs/ext4/extents.c
===================================================================
--- linux-2.6.orig/fs/ext4/extents.c
+++ linux-2.6/fs/ext4/extents.c
@@ -3130,7 +3130,7 @@ int ext4_xattr_fiemap(struct inode *inod
{
__u64 physical = 0;
__u64 length;
- __u32 flags = FIEMAP_EXTENT_LAST;
+ __u32 flags = 0;
int blockbits = inode->i_sb->s_blocksize_bits;
int error = 0;
@@ -3148,14 +3148,24 @@ int ext4_xattr_fiemap(struct inode *inod
physical += offset;
length = EXT4_SB(inode->i_sb)->s_inode_size - offset;
flags |= FIEMAP_EXTENT_DATA_INLINE;
- } else { /* external block */
- physical = EXT4_I(inode)->i_file_acl << blockbits;
- length = inode->i_sb->s_blocksize;
+ if (EXT4_I(inode)->i_file_acl == 0)
+ flags |= FIEMAP_EXTENT_LAST;
+
+ error = fiemap_fill_next_extent(fieinfo, 0, physical, length,
+ flags, inode->i_sb->s_dev);
+ if (error)
+ goto out;
}
+ /* external block */
+ flags = FIEMAP_EXTENT_LAST;
+ physical = EXT4_I(inode)->i_file_acl << blockbits;
+ length = inode->i_sb->s_blocksize;
if (physical)
- error = fiemap_fill_next_extent(fieinfo, 0, physical,
- length, flags, inode->i_sb->s_dev);
+ error = fiemap_fill_next_extent(fieinfo, 0, physical, length,
+ flags, inode->i_sb->s_dev);
+
+out:
return (error < 0 ? error : 0);
}
Thanks,
Kalpak
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 4/4] ext4: implement ext4_fiemap
2008-07-03 17:01 ` Kalpak Shah
@ 2008-07-03 18:49 ` Eric Sandeen
0 siblings, 0 replies; 3+ messages in thread
From: Eric Sandeen @ 2008-07-03 18:49 UTC (permalink / raw)
To: Kalpak Shah; +Cc: Mark Fasheh, linux-fsdevel
Kalpak Shah wrote:
> Extended attributes can be present in the inode as well as the external block at the same time and we should be returning extents for both them in that case. Below patch fixes this problem:
Thanks Kalpak, was on my todo list but not done. :)
-Eric
> Signed-off-by: Kalpak Shah <kalpak.shah@sun.com>
>
> Index: linux-2.6/fs/ext4/extents.c
> ===================================================================
> --- linux-2.6.orig/fs/ext4/extents.c
> +++ linux-2.6/fs/ext4/extents.c
> @@ -3130,7 +3130,7 @@ int ext4_xattr_fiemap(struct inode *inod
> {
> __u64 physical = 0;
> __u64 length;
> - __u32 flags = FIEMAP_EXTENT_LAST;
> + __u32 flags = 0;
> int blockbits = inode->i_sb->s_blocksize_bits;
> int error = 0;
>
> @@ -3148,14 +3148,24 @@ int ext4_xattr_fiemap(struct inode *inod
> physical += offset;
> length = EXT4_SB(inode->i_sb)->s_inode_size - offset;
> flags |= FIEMAP_EXTENT_DATA_INLINE;
> - } else { /* external block */
> - physical = EXT4_I(inode)->i_file_acl << blockbits;
> - length = inode->i_sb->s_blocksize;
> + if (EXT4_I(inode)->i_file_acl == 0)
> + flags |= FIEMAP_EXTENT_LAST;
> +
> + error = fiemap_fill_next_extent(fieinfo, 0, physical, length,
> + flags, inode->i_sb->s_dev);
> + if (error)
> + goto out;
> }
>
> + /* external block */
> + flags = FIEMAP_EXTENT_LAST;
> + physical = EXT4_I(inode)->i_file_acl << blockbits;
> + length = inode->i_sb->s_blocksize;
> if (physical)
> - error = fiemap_fill_next_extent(fieinfo, 0, physical,
> - length, flags, inode->i_sb->s_dev);
> + error = fiemap_fill_next_extent(fieinfo, 0, physical, length,
> + flags, inode->i_sb->s_dev);
> +
> +out:
> return (error < 0 ? error : 0);
> }
>
>
> Thanks,
> Kalpak
>
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-07-03 18:49 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-25 22:19 [PATCH 4/4] ext4: implement ext4_fiemap Mark Fasheh
2008-07-03 17:01 ` Kalpak Shah
2008-07-03 18:49 ` Eric Sandeen
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).