* [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data()
@ 2016-03-03 10:23 Jan Kara
2016-03-03 10:23 ` [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent() Jan Kara
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
Hello,
I've seen several reports of softlockups somewhere under ext4_seek_data()
usually during fuzzing. The culprit is that ext4_seek_data() iterates holes
block by block and thus suitably crafted file can make ext4_seek_data() do
a *lot* of lookups using ext4_map_blocks() which is slow. This is really
unnecessary since in principle we have all the information for efficient
skipping of holes, just a layer below. Even for indirect-block based files
we can easily reduce the asymptotic complexity of hole iteration from linear
to logarithmic albeit with a relatively large constant.
Patches 1 and 4 in this series are independent cleanups I've spotted while
changing the code, they can be merged separately. Patches 2 and 3 are
preparatory changes which make ext4_map_blocks() return hole size in
map->m_len field. We still return 0 from ext4_map_blocks() calls finding hole
to maintain backward compatibility of this function and it still makes sense
as such - it still returns the number of allocated blocks starting at given
logical offset. Patch 5 then uses the newly available information in
ext4_seek_data() and it also rewrites ext4_seek_hole() so that both functions
are symmetric.
Honza
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent()
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
@ 2016-03-03 10:23 ` Jan Kara
2016-03-10 3:45 ` Theodore Ts'o
2016-03-03 10:23 ` [PATCH 2/5] ext4: Factor out determining of hole size Jan Kara
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
We were setting referenced bit on the extent structure we return from
ext4_es_lookup_extent() which is just a private structure on stack. Thus
setting had no effect. Set the bit in the structure in the status tree
instead.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext4/extents_status.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index ac748b3af1c1..e38b987ac7f5 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -823,8 +823,8 @@ out:
es->es_lblk = es1->es_lblk;
es->es_len = es1->es_len;
es->es_pblk = es1->es_pblk;
- if (!ext4_es_is_referenced(es))
- ext4_es_set_referenced(es);
+ if (!ext4_es_is_referenced(es1))
+ ext4_es_set_referenced(es1);
stats->es_stats_cache_hits++;
} else {
stats->es_stats_cache_misses++;
--
2.6.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/5] ext4: Factor out determining of hole size
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
2016-03-03 10:23 ` [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent() Jan Kara
@ 2016-03-03 10:23 ` Jan Kara
2016-03-10 3:54 ` Theodore Ts'o
2016-03-03 10:23 ` [PATCH 3/5] ext4: Return hole from ext4_map_blocks() Jan Kara
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
ext4_ext_put_gap_in_cache() determines hole size in the extent tree,
then trims this with possible delayed allocated blocks, and inserts the
result into the extent status tree. Factor out determination of the size
of the hole in the extent tree as we will need this information in
ext4_ext_map_blocks() as well.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext4/extents.c | 80 ++++++++++++++++++++++++++++++++-----------------------
1 file changed, 47 insertions(+), 33 deletions(-)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 3753ceb0b0dd..2dc6261e247f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2293,59 +2293,69 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
}
/*
- * ext4_ext_put_gap_in_cache:
- * calculate boundaries of the gap that the requested block fits into
- * and cache this gap
+ * ext4_ext_determine_hole - determine hole around given block
+ * @inode: inode we lookup in
+ * @path: path in extent tree to @lblk
+ * @lblk: pointer to logical block around which we want to determine hole
+ *
+ * Determine hole length (and start if easily possible) around given logical
+ * block. We don't try too hard to find the beginning of the hole but @path
+ * actually points to extent before @lblk, we provide it.
+ *
+ * The function returns the length of a hole starting at @lblk. We update @lblk
+ * to the beginning of the hole if we managed to find it.
*/
-static void
-ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
- ext4_lblk_t block)
+static ext4_lblk_t ext4_ext_determine_hole(struct inode *inode,
+ struct ext4_ext_path *path,
+ ext4_lblk_t *lblk)
{
int depth = ext_depth(inode);
- ext4_lblk_t len;
- ext4_lblk_t lblock;
struct ext4_extent *ex;
- struct extent_status es;
+ ext4_lblk_t len;
ex = path[depth].p_ext;
if (ex == NULL) {
/* there is no extent yet, so gap is [0;-] */
- lblock = 0;
+ *lblk = 0;
len = EXT_MAX_BLOCKS;
- ext_debug("cache gap(whole file):");
- } else if (block < le32_to_cpu(ex->ee_block)) {
- lblock = block;
- len = le32_to_cpu(ex->ee_block) - block;
- ext_debug("cache gap(before): %u [%u:%u]",
- block,
- le32_to_cpu(ex->ee_block),
- ext4_ext_get_actual_len(ex));
- } else if (block >= le32_to_cpu(ex->ee_block)
+ } else if (*lblk < le32_to_cpu(ex->ee_block)) {
+ len = le32_to_cpu(ex->ee_block) - *lblk;
+ } else if (*lblk >= le32_to_cpu(ex->ee_block)
+ ext4_ext_get_actual_len(ex)) {
ext4_lblk_t next;
- lblock = le32_to_cpu(ex->ee_block)
- + ext4_ext_get_actual_len(ex);
+ *lblk = le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex);
next = ext4_ext_next_allocated_block(path);
- ext_debug("cache gap(after): [%u:%u] %u",
- le32_to_cpu(ex->ee_block),
- ext4_ext_get_actual_len(ex),
- block);
- BUG_ON(next == lblock);
- len = next - lblock;
+ BUG_ON(next == *lblk);
+ len = next - *lblk;
} else {
BUG();
}
+ return len;
+}
- ext4_es_find_delayed_extent_range(inode, lblock, lblock + len - 1, &es);
+/*
+ * ext4_ext_put_gap_in_cache:
+ * calculate boundaries of the gap that the requested block fits into
+ * and cache this gap
+ */
+static void
+ext4_ext_put_gap_in_cache(struct inode *inode, ext4_lblk_t hole_start,
+ ext4_lblk_t hole_len)
+{
+ struct extent_status es;
+
+ ext4_es_find_delayed_extent_range(inode, hole_start,
+ hole_start + hole_len - 1, &es);
if (es.es_len) {
/* There's delayed extent containing lblock? */
- if (es.es_lblk <= lblock)
+ if (es.es_lblk <= hole_start)
return;
- len = min(es.es_lblk - lblock, len);
+ hole_len = min(es.es_lblk - hole_start, hole_len);
}
- ext_debug(" -> %u:%u\n", lblock, len);
- ext4_es_insert_extent(inode, lblock, len, ~0, EXTENT_STATUS_HOLE);
+ ext_debug(" -> %u:%u\n", hole_start, hole_len);
+ ext4_es_insert_extent(inode, hole_start, hole_len, ~0,
+ EXTENT_STATUS_HOLE);
}
/*
@@ -4368,11 +4378,15 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
* we couldn't try to create block if create flag is zero
*/
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
+ ext4_lblk_t hole_start, hole_len;
+
/*
* put just found gap into cache to speed up
* subsequent requests
*/
- ext4_ext_put_gap_in_cache(inode, path, map->m_lblk);
+ 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);
goto out2;
}
--
2.6.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/5] ext4: Return hole from ext4_map_blocks()
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
2016-03-03 10:23 ` [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent() Jan Kara
2016-03-03 10:23 ` [PATCH 2/5] ext4: Factor out determining of hole size Jan Kara
@ 2016-03-03 10:23 ` Jan Kara
2016-03-03 10:23 ` [PATCH 4/5] ext4: Cleanup handling of bh->b_state in DAX mmap Jan Kara
2016-03-03 10:23 ` [PATCH 5/5] ext4: More efficient SEEK_DATA implementation Jan Kara
4 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
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>
---
fs/ext4/extents.c | 11 +++++++++--
fs/ext4/indirect.c | 19 +++++++++++++++++--
fs/ext4/inode.c | 15 ++++++++++-----
3 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 2dc6261e247f..0e924ca153f9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4380,13 +4380,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;
}
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 355ef9c36c87..7544d1047dc6 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -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;
/*
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9cc57c3b4661..ef590607b4a7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -458,13 +458,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.
*/
@@ -507,6 +507,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);
--
2.6.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/5] ext4: Cleanup handling of bh->b_state in DAX mmap
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
` (2 preceding siblings ...)
2016-03-03 10:23 ` [PATCH 3/5] ext4: Return hole from ext4_map_blocks() Jan Kara
@ 2016-03-03 10:23 ` Jan Kara
2016-03-03 10:23 ` [PATCH 5/5] ext4: More efficient SEEK_DATA implementation Jan Kara
4 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
ext4_dax_mmap_get_block() updates bh->b_state directly instead of using
ext4_update_bh_state(). This is mostly a cosmetic issue since DAX code
always passes on-stack buffer_head but clean this up to make code more
uniform.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext4/inode.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ef590607b4a7..e38c88b37e2e 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3180,13 +3180,12 @@ out:
WARN_ON_ONCE(ret == 0 && create);
if (ret > 0) {
map_bh(bh_result, inode->i_sb, map.m_pblk);
- bh_result->b_state = (bh_result->b_state & ~EXT4_MAP_FLAGS) |
- map.m_flags;
/*
* At least for now we have to clear BH_New so that DAX code
* doesn't attempt to zero blocks again in a racy way.
*/
- bh_result->b_state &= ~(1 << BH_New);
+ map.m_flags &= ~EXT4_MAP_NEW;
+ ext4_update_bh_state(bh_result, map.m_flags);
bh_result->b_size = map.m_len << inode->i_blkbits;
ret = 0;
}
--
2.6.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/5] ext4: More efficient SEEK_DATA implementation
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
` (3 preceding siblings ...)
2016-03-03 10:23 ` [PATCH 4/5] ext4: Cleanup handling of bh->b_state in DAX mmap Jan Kara
@ 2016-03-03 10:23 ` Jan Kara
4 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2016-03-03 10:23 UTC (permalink / raw)
To: Ted Tso; +Cc: linux-ext4, Jan Kara
Using SEEK_DATA in a huge sparse file can easily lead to sotflockups as
ext4_seek_data() iterates hole block-by-block. Fix the problem by using
returned hole size from ext4_map_blocks() and thus skip the hole in one
go.
Update also SEEK_HOLE implementation to follow the same pattern as
SEEK_DATA to make future maintenance easier.
Furthermore we add cond_resched() to both ext4_seek_data() and
ext4_seek_hole() to avoid softlockups in case evil user creates huge
fragmented file and we have to go through lots of extents.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext4/ext4.h | 3 ++
fs/ext4/file.c | 97 +++++++++++++++++++++------------------------------------
fs/ext4/inode.c | 67 +++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+), 61 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 157b458a69d4..f9b1adf9f902 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2559,6 +2559,9 @@ extern void ext4_da_update_reserve_space(struct inode *inode,
int used, int quota_claim);
extern int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk,
ext4_fsblk_t pblk, ext4_lblk_t len);
+extern int ext4_get_next_extent(struct inode *inode, ext4_lblk_t lblk,
+ unsigned int map_len,
+ struct extent_status *result);
/* indirect.c */
extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 474f1a4d2ca8..7191095a8ca2 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -432,7 +432,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
*/
static int ext4_find_unwritten_pgoff(struct inode *inode,
int whence,
- struct ext4_map_blocks *map,
+ ext4_lblk_t end_blk,
loff_t *offset)
{
struct pagevec pvec;
@@ -447,7 +447,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
blkbits = inode->i_sb->s_blocksize_bits;
startoff = *offset;
lastoff = startoff;
- endoff = (loff_t)(map->m_lblk + map->m_len) << blkbits;
+ endoff = (loff_t)end_blk << blkbits;
index = startoff >> PAGE_CACHE_SHIFT;
end = endoff >> PAGE_CACHE_SHIFT;
@@ -565,12 +565,11 @@ out:
static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
{
struct inode *inode = file->f_mapping->host;
- struct ext4_map_blocks map;
struct extent_status es;
ext4_lblk_t start, last, end;
loff_t dataoff, isize;
int blkbits;
- int ret = 0;
+ int ret;
inode_lock(inode);
@@ -587,41 +586,32 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
dataoff = offset;
do {
- map.m_lblk = last;
- map.m_len = end - last + 1;
- ret = ext4_map_blocks(NULL, inode, &map, 0);
- if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
- if (last != start)
- dataoff = (loff_t)last << blkbits;
- break;
+ ret = ext4_get_next_extent(inode, last, end - last + 1, &es);
+ if (ret <= 0) {
+ /* No extent found -> no data */
+ if (ret == 0)
+ ret = -ENXIO;
+ inode_unlock(inode);
+ return ret;
}
- /*
- * If there is a delay extent at this offset,
- * it will be as a data.
- */
- ext4_es_find_delayed_extent_range(inode, last, last, &es);
- if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) {
- if (last != start)
- dataoff = (loff_t)last << blkbits;
+ last = es.es_lblk;
+ if (last != start)
+ dataoff = (loff_t)last << blkbits;
+ if (!ext4_es_is_unwritten(&es))
break;
- }
/*
* If there is a unwritten extent at this offset,
* it will be as a data or a hole according to page
* cache that has data or not.
*/
- if (map.m_flags & EXT4_MAP_UNWRITTEN) {
- int unwritten;
- unwritten = ext4_find_unwritten_pgoff(inode, SEEK_DATA,
- &map, &dataoff);
- if (unwritten)
- break;
- }
-
- last++;
+ if (ext4_find_unwritten_pgoff(inode, SEEK_DATA,
+ es.es_lblk + es.es_len, &dataoff))
+ break;
+ last += es.es_len;
dataoff = (loff_t)last << blkbits;
+ cond_resched();
} while (last <= end);
inode_unlock(inode);
@@ -638,12 +628,11 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
{
struct inode *inode = file->f_mapping->host;
- struct ext4_map_blocks map;
struct extent_status es;
ext4_lblk_t start, last, end;
loff_t holeoff, isize;
int blkbits;
- int ret = 0;
+ int ret;
inode_lock(inode);
@@ -660,44 +649,30 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
holeoff = offset;
do {
- map.m_lblk = last;
- map.m_len = end - last + 1;
- ret = ext4_map_blocks(NULL, inode, &map, 0);
- if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
- last += ret;
- holeoff = (loff_t)last << blkbits;
- continue;
+ ret = ext4_get_next_extent(inode, last, end - last + 1, &es);
+ if (ret < 0) {
+ inode_unlock(inode);
+ return ret;
}
-
- /*
- * If there is a delay extent at this offset,
- * we will skip this extent.
- */
- ext4_es_find_delayed_extent_range(inode, last, last, &es);
- if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) {
- last = es.es_lblk + es.es_len;
- holeoff = (loff_t)last << blkbits;
- continue;
+ /* Found a hole? */
+ if (ret == 0 || es.es_lblk > last) {
+ if (last != start)
+ holeoff = (loff_t)last << blkbits;
+ break;
}
-
/*
* If there is a unwritten extent at this offset,
* it will be as a data or a hole according to page
* cache that has data or not.
*/
- if (map.m_flags & EXT4_MAP_UNWRITTEN) {
- int unwritten;
- unwritten = ext4_find_unwritten_pgoff(inode, SEEK_HOLE,
- &map, &holeoff);
- if (!unwritten) {
- last += ret;
- holeoff = (loff_t)last << blkbits;
- continue;
- }
- }
+ if (ext4_es_is_unwritten(&es) &&
+ ext4_find_unwritten_pgoff(inode, SEEK_HOLE,
+ last + es.es_len, &holeoff))
+ break;
- /* find a hole */
- break;
+ last += es.es_len;
+ holeoff = (loff_t)last << blkbits;
+ cond_resched();
} while (last <= end);
inode_unlock(inode);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e38c88b37e2e..af7b1e9f7762 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -5545,3 +5545,70 @@ int ext4_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return err;
}
+
+/*
+ * Find the first extent at or after @lblk in an inode that is not a hole.
+ * Search for @map_len blocks at most. The extent is returned in @result.
+ *
+ * The function returns 1 if we found an extent. The function returns 0 in
+ * case there is no extent at or after @lblk and in that case also sets
+ * @result->es_len to 0. In case of error, the error code is returned.
+ */
+int ext4_get_next_extent(struct inode *inode, ext4_lblk_t lblk,
+ unsigned int map_len, struct extent_status *result)
+{
+ struct ext4_map_blocks map;
+ struct extent_status es = {};
+ int ret;
+
+ map.m_lblk = lblk;
+ map.m_len = map_len;
+
+ /*
+ * For non-extent based files this loop may iterate several times since
+ * we do not determine full hole size.
+ */
+ while (map.m_len > 0) {
+ ret = ext4_map_blocks(NULL, inode, &map, 0);
+ if (ret < 0)
+ return ret;
+ /* There's extent covering m_lblk? Just return it. */
+ if (ret > 0) {
+ int status;
+
+ ext4_es_store_pblock(result, map.m_pblk);
+ result->es_lblk = map.m_lblk;
+ result->es_len = map.m_len;
+ if (map.m_flags & EXT4_MAP_UNWRITTEN)
+ status = EXTENT_STATUS_UNWRITTEN;
+ else
+ status = EXTENT_STATUS_WRITTEN;
+ ext4_es_store_status(result, status);
+ return 1;
+ }
+ ext4_es_find_delayed_extent_range(inode, map.m_lblk,
+ map.m_lblk + map.m_len - 1,
+ &es);
+ /* Is delalloc data before next block in extent tree? */
+ if (es.es_len && es.es_lblk < map.m_lblk + map.m_len) {
+ ext4_lblk_t offset = 0;
+
+ if (es.es_lblk < lblk)
+ offset = lblk - es.es_lblk;
+ result->es_lblk = es.es_lblk + offset;
+ ext4_es_store_pblock(result,
+ ext4_es_pblock(&es) + offset);
+ result->es_len = es.es_len - offset;
+ ext4_es_store_status(result, ext4_es_status(&es));
+
+ return 1;
+ }
+ /* There's a hole at m_lblk, advance us after it */
+ map.m_lblk += map.m_len;
+ map_len -= map.m_len;
+ map.m_len = map_len;
+ cond_resched();
+ }
+ result->es_len = 0;
+ return 0;
+}
--
2.6.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent()
2016-03-03 10:23 ` [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent() Jan Kara
@ 2016-03-10 3:45 ` Theodore Ts'o
0 siblings, 0 replies; 8+ messages in thread
From: Theodore Ts'o @ 2016-03-10 3:45 UTC (permalink / raw)
To: Jan Kara; +Cc: linux-ext4
On Thu, Mar 03, 2016 at 11:23:47AM +0100, Jan Kara wrote:
> We were setting referenced bit on the extent structure we return from
> ext4_es_lookup_extent() which is just a private structure on stack. Thus
> setting had no effect. Set the bit in the structure in the status tree
> instead.
>
> Signed-off-by: Jan Kara <jack@suse.cz>
Thanks, applied.
- Ted
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/5] ext4: Factor out determining of hole size
2016-03-03 10:23 ` [PATCH 2/5] ext4: Factor out determining of hole size Jan Kara
@ 2016-03-10 3:54 ` Theodore Ts'o
0 siblings, 0 replies; 8+ messages in thread
From: Theodore Ts'o @ 2016-03-10 3:54 UTC (permalink / raw)
To: Jan Kara; +Cc: linux-ext4
On Thu, Mar 03, 2016 at 11:23:48AM +0100, Jan Kara wrote:
> ext4_ext_put_gap_in_cache() determines hole size in the extent tree,
> then trims this with possible delayed allocated blocks, and inserts the
> result into the extent status tree. Factor out determination of the size
> of the hole in the extent tree as we will need this information in
> ext4_ext_map_blocks() as well.
>
> Signed-off-by: Jan Kara <jack@suse.cz>
Thanks, applied.
- Ted
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2016-03-10 3:54 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-03-03 10:23 [PATCH 0/5] ext4: Fix softlockups in ext4_seek_data() Jan Kara
2016-03-03 10:23 ` [PATCH 1/5] ext4: Fix setting of referenced bit in ext4_es_lookup_extent() Jan Kara
2016-03-10 3:45 ` Theodore Ts'o
2016-03-03 10:23 ` [PATCH 2/5] ext4: Factor out determining of hole size Jan Kara
2016-03-10 3:54 ` Theodore Ts'o
2016-03-03 10:23 ` [PATCH 3/5] ext4: Return hole from ext4_map_blocks() Jan Kara
2016-03-03 10:23 ` [PATCH 4/5] ext4: Cleanup handling of bh->b_state in DAX mmap Jan Kara
2016-03-03 10:23 ` [PATCH 5/5] ext4: More efficient SEEK_DATA implementation Jan Kara
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).