ext4: Add new functions for searching extent tree
authorAlex Tomas <alex@clusterfs.com>
Tue, 29 Jan 2008 04:58:27 +0000 (23:58 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 29 Jan 2008 04:58:27 +0000 (23:58 -0500)
Add the functions ext4_ext_search_left() and ext4_ext_search_right(),
which are used by mballoc during ext4_ext_get_blocks to decided whether
to merge extent information.

Signed-off-by: Alex Tomas <alex@clusterfs.com>
Signed-off-by: Andreas Dilger <adilger@clusterfs.com>
Signed-off-by: Johann Lombardi <johann@clusterfs.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/extents.c
include/linux/ext4_fs_extents.h

index 01eda5c5281e38c0f5a69af66037d52566ce4f04..f5cf2a94b6fc3d678c71ab3feec80fed8f9a3dad 100644 (file)
@@ -1016,6 +1016,148 @@ out:
        return err;
 }
 
+/*
+ * search the closest allocated block to the left for *logical
+ * and returns it at @logical + it's physical address at @phys
+ * if *logical is the smallest allocated block, the function
+ * returns 0 at @phys
+ * return value contains 0 (success) or error code
+ */
+int
+ext4_ext_search_left(struct inode *inode, struct ext4_ext_path *path,
+                       ext4_lblk_t *logical, ext4_fsblk_t *phys)
+{
+       struct ext4_extent_idx *ix;
+       struct ext4_extent *ex;
+       int depth;
+
+       BUG_ON(path == NULL);
+       depth = path->p_depth;
+       *phys = 0;
+
+       if (depth == 0 && path->p_ext == NULL)
+               return 0;
+
+       /* usually extent in the path covers blocks smaller
+        * then *logical, but it can be that extent is the
+        * first one in the file */
+
+       ex = path[depth].p_ext;
+       if (*logical < le32_to_cpu(ex->ee_block)) {
+               BUG_ON(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex);
+               while (--depth >= 0) {
+                       ix = path[depth].p_idx;
+                       BUG_ON(ix != EXT_FIRST_INDEX(path[depth].p_hdr));
+               }
+               return 0;
+       }
+
+       BUG_ON(*logical < le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len));
+
+       *logical = le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - 1;
+       *phys = ext_pblock(ex) + le16_to_cpu(ex->ee_len) - 1;
+       return 0;
+}
+
+/*
+ * search the closest allocated block to the right for *logical
+ * and returns it at @logical + it's physical address at @phys
+ * if *logical is the smallest allocated block, the function
+ * returns 0 at @phys
+ * return value contains 0 (success) or error code
+ */
+int
+ext4_ext_search_right(struct inode *inode, struct ext4_ext_path *path,
+                       ext4_lblk_t *logical, ext4_fsblk_t *phys)
+{
+       struct buffer_head *bh = NULL;
+       struct ext4_extent_header *eh;
+       struct ext4_extent_idx *ix;
+       struct ext4_extent *ex;
+       ext4_fsblk_t block;
+       int depth;
+
+       BUG_ON(path == NULL);
+       depth = path->p_depth;
+       *phys = 0;
+
+       if (depth == 0 && path->p_ext == NULL)
+               return 0;
+
+       /* usually extent in the path covers blocks smaller
+        * then *logical, but it can be that extent is the
+        * first one in the file */
+
+       ex = path[depth].p_ext;
+       if (*logical < le32_to_cpu(ex->ee_block)) {
+               BUG_ON(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex);
+               while (--depth >= 0) {
+                       ix = path[depth].p_idx;
+                       BUG_ON(ix != EXT_FIRST_INDEX(path[depth].p_hdr));
+               }
+               *logical = le32_to_cpu(ex->ee_block);
+               *phys = ext_pblock(ex);
+               return 0;
+       }
+
+       BUG_ON(*logical < le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len));
+
+       if (ex != EXT_LAST_EXTENT(path[depth].p_hdr)) {
+               /* next allocated block in this leaf */
+               ex++;
+               *logical = le32_to_cpu(ex->ee_block);
+               *phys = ext_pblock(ex);
+               return 0;
+       }
+
+       /* go up and search for index to the right */
+       while (--depth >= 0) {
+               ix = path[depth].p_idx;
+               if (ix != EXT_LAST_INDEX(path[depth].p_hdr))
+                       break;
+       }
+
+       if (depth < 0) {
+               /* we've gone up to the root and
+                * found no index to the right */
+               return 0;
+       }
+
+       /* we've found index to the right, let's
+        * follow it and find the closest allocated
+        * block to the right */
+       ix++;
+       block = idx_pblock(ix);
+       while (++depth < path->p_depth) {
+               bh = sb_bread(inode->i_sb, block);
+               if (bh == NULL)
+                       return -EIO;
+               eh = ext_block_hdr(bh);
+               if (ext4_ext_check_header(inode, eh, depth)) {
+                       put_bh(bh);
+                       return -EIO;
+               }
+               ix = EXT_FIRST_INDEX(eh);
+               block = idx_pblock(ix);
+               put_bh(bh);
+       }
+
+       bh = sb_bread(inode->i_sb, block);
+       if (bh == NULL)
+               return -EIO;
+       eh = ext_block_hdr(bh);
+       if (ext4_ext_check_header(inode, eh, path->p_depth - depth)) {
+               put_bh(bh);
+               return -EIO;
+       }
+       ex = EXT_FIRST_EXTENT(eh);
+       *logical = le32_to_cpu(ex->ee_block);
+       *phys = ext_pblock(ex);
+       put_bh(bh);
+       return 0;
+
+}
+
 /*
  * ext4_ext_next_allocated_block:
  * returns allocated block in subsequent extent or EXT_MAX_BLOCK.
index 1cfb4854c964ef2673c229ff5a79202409e5aafa..697da4bce6c513b003e9118d95e8bda34eeeb81a 100644 (file)
@@ -223,5 +223,9 @@ extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *,
 extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *);
 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 *,
+                                               ext4_lblk_t *, ext4_fsblk_t *);
+extern int ext4_ext_search_right(struct inode *, struct ext4_ext_path *,
+                                               ext4_lblk_t *, ext4_fsblk_t *);
 #endif /* _LINUX_EXT4_EXTENTS */