ext4: return hole from ext4_map_blocks()
authorJan Kara <jack@suse.cz>
Thu, 10 Mar 2016 03:54:00 +0000 (22:54 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 10 Mar 2016 03:54:00 +0000 (22:54 -0500)
Currently, ext4_map_blocks() just returns 0 when it finds a hole and
allocation is not requested. However we have all the information
available to tell how large the hole actually is and there are callers
of ext4_map_blocks() which would save some block-by-block hole iteration
if they knew this information. So fill in struct ext4_map_blocks even
for holes with the information we have. We keep returning 0 for holes to
maintain backward compatibility of the function.

Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/extents.c
fs/ext4/indirect.c
fs/ext4/inode.c

index 7858fc1d2501a99c322ef497424e61dea73f1ded..2730039d474285f3e36806d8d4d6d9cd1afb1d51 100644 (file)
@@ -4374,13 +4374,20 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
        if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
                ext4_lblk_t hole_start, hole_len;
 
+               hole_start = map->m_lblk;
+               hole_len = ext4_ext_determine_hole(inode, path, &hole_start);
                /*
                 * put just found gap into cache to speed up
                 * subsequent requests
                 */
-               hole_start = map->m_lblk;
-               hole_len = ext4_ext_determine_hole(inode, path, &hole_start);
                ext4_ext_put_gap_in_cache(inode, hole_start, hole_len);
+
+               /* Update hole_len to reflect hole size after map->m_lblk */
+               if (hole_start != map->m_lblk)
+                       hole_len -= map->m_lblk - hole_start;
+               map->m_pblk = 0;
+               map->m_len = min_t(unsigned int, map->m_len, hole_len);
+
                goto out2;
        }
 
index 1655ff195e0eb0709b5ce77e1121b9a781c0bec9..3027fa681de537c586289a26959f8d4f37ae025b 100644 (file)
@@ -555,8 +555,23 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
                goto got_it;
        }
 
-       /* Next simple case - plain lookup or failed read of indirect block */
-       if ((flags & EXT4_GET_BLOCKS_CREATE) == 0 || err == -EIO)
+       /* Next simple case - plain lookup failed */
+       if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
+               unsigned epb = inode->i_sb->s_blocksize / sizeof(u32);
+               int i;
+
+               /* Count number blocks in a subtree under 'partial' */
+               count = 1;
+               for (i = 0; partial + i != chain + depth - 1; i++)
+                       count *= epb;
+               /* Fill in size of a hole we found */
+               map->m_pblk = 0;
+               map->m_len = min_t(unsigned int, map->m_len, count);
+               goto cleanup;
+       }
+
+       /* Failed read of indirect block */
+       if (err == -EIO)
                goto cleanup;
 
        /*
index 971892d7c2133164afe1347456377996ec0f98b9..1a156ec043a026863687c946a501cddd6bc872bf 100644 (file)
@@ -455,13 +455,13 @@ static void ext4_map_blocks_es_recheck(handle_t *handle,
  * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping
  * based files
  *
- * On success, it returns the number of blocks being mapped or allocated.
- * if create==0 and the blocks are pre-allocated and unwritten block,
- * the result buffer head is unmapped. If the create ==1, it will make sure
- * the buffer head is mapped.
+ * On success, it returns the number of blocks being mapped or allocated.  if
+ * create==0 and the blocks are pre-allocated and unwritten, the resulting @map
+ * is marked as unwritten. If the create == 1, it will mark @map as mapped.
  *
  * It returns 0 if plain look up failed (blocks have not been allocated), in
- * that case, buffer head is unmapped
+ * that case, @map is returned as unmapped but we still do fill map->m_len to
+ * indicate the length of a hole starting at map->m_lblk.
  *
  * It returns the error in case of allocation failure.
  */
@@ -504,6 +504,11 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
                                retval = map->m_len;
                        map->m_len = retval;
                } else if (ext4_es_is_delayed(&es) || ext4_es_is_hole(&es)) {
+                       map->m_pblk = 0;
+                       retval = es.es_len - (map->m_lblk - es.es_lblk);
+                       if (retval > map->m_len)
+                               retval = map->m_len;
+                       map->m_len = retval;
                        retval = 0;
                } else {
                        BUG_ON(1);