Commit ad431025 authored by Eric Whitney's avatar Eric Whitney Committed by Theodore Ts'o

ext4: generalize extents status tree search functions

Ext4 contains a few functions that are used to search for delayed
extents or blocks in the extents status tree.  Rather than duplicate
code to add new functions to search for extents with different status
values, such as written or a combination of delayed and unwritten,
generalize the existing code to search for caller-specified extents
status values.  Also, move this code into extents_status.c where it
is better associated with the data structures it operates upon, and
where it can be more readily used to implement new extents status tree
functions that might want a broader scope for i_es_lock.

Three missing static specifiers in RFC version of patch reported and
fixed by Fengguang Wu <fengguang.wu@intel.com>.
Signed-off-by: default avatarEric Whitney <enwlinux@gmail.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 17b57b18
...@@ -3142,10 +3142,6 @@ extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t, ...@@ -3142,10 +3142,6 @@ extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t,
int flags); int flags);
extern void ext4_ext_drop_refs(struct ext4_ext_path *); extern void ext4_ext_drop_refs(struct ext4_ext_path *);
extern int ext4_ext_check_inode(struct inode *inode); extern int ext4_ext_check_inode(struct inode *inode);
extern int ext4_find_delalloc_range(struct inode *inode,
ext4_lblk_t lblk_start,
ext4_lblk_t lblk_end);
extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk);
extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path); extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path);
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len); __u64 start, __u64 len);
......
...@@ -2351,8 +2351,8 @@ ext4_ext_put_gap_in_cache(struct inode *inode, ext4_lblk_t hole_start, ...@@ -2351,8 +2351,8 @@ ext4_ext_put_gap_in_cache(struct inode *inode, ext4_lblk_t hole_start,
{ {
struct extent_status es; struct extent_status es;
ext4_es_find_delayed_extent_range(inode, hole_start, ext4_es_find_extent_range(inode, &ext4_es_is_delayed, hole_start,
hole_start + hole_len - 1, &es); hole_start + hole_len - 1, &es);
if (es.es_len) { if (es.es_len) {
/* There's delayed extent containing lblock? */ /* There's delayed extent containing lblock? */
if (es.es_lblk <= hole_start) if (es.es_lblk <= hole_start)
...@@ -3819,39 +3819,6 @@ static int check_eofblocks_fl(handle_t *handle, struct inode *inode, ...@@ -3819,39 +3819,6 @@ static int check_eofblocks_fl(handle_t *handle, struct inode *inode,
return ext4_mark_inode_dirty(handle, inode); return ext4_mark_inode_dirty(handle, inode);
} }
/**
* ext4_find_delalloc_range: find delayed allocated block in the given range.
*
* Return 1 if there is a delalloc block in the range, otherwise 0.
*/
int ext4_find_delalloc_range(struct inode *inode,
ext4_lblk_t lblk_start,
ext4_lblk_t lblk_end)
{
struct extent_status es;
ext4_es_find_delayed_extent_range(inode, lblk_start, lblk_end, &es);
if (es.es_len == 0)
return 0; /* there is no delay extent in this tree */
else if (es.es_lblk <= lblk_start &&
lblk_start < es.es_lblk + es.es_len)
return 1;
else if (lblk_start <= es.es_lblk && es.es_lblk <= lblk_end)
return 1;
else
return 0;
}
int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk)
{
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
ext4_lblk_t lblk_start, lblk_end;
lblk_start = EXT4_LBLK_CMASK(sbi, lblk);
lblk_end = lblk_start + sbi->s_cluster_ratio - 1;
return ext4_find_delalloc_range(inode, lblk_start, lblk_end);
}
/** /**
* Determines how many complete clusters (out of those specified by the 'map') * Determines how many complete clusters (out of those specified by the 'map')
* are under delalloc and were reserved quota for. * are under delalloc and were reserved quota for.
...@@ -3910,7 +3877,8 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start, ...@@ -3910,7 +3877,8 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
lblk_from = EXT4_LBLK_CMASK(sbi, lblk_start); lblk_from = EXT4_LBLK_CMASK(sbi, lblk_start);
lblk_to = lblk_from + c_offset - 1; lblk_to = lblk_from + c_offset - 1;
if (ext4_find_delalloc_range(inode, lblk_from, lblk_to)) if (ext4_es_scan_range(inode, &ext4_es_is_delayed, lblk_from,
lblk_to))
allocated_clusters--; allocated_clusters--;
} }
...@@ -3920,7 +3888,8 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start, ...@@ -3920,7 +3888,8 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
lblk_from = lblk_start + num_blks; lblk_from = lblk_start + num_blks;
lblk_to = lblk_from + (sbi->s_cluster_ratio - c_offset) - 1; lblk_to = lblk_from + (sbi->s_cluster_ratio - c_offset) - 1;
if (ext4_find_delalloc_range(inode, lblk_from, lblk_to)) if (ext4_es_scan_range(inode, &ext4_es_is_delayed, lblk_from,
lblk_to))
allocated_clusters--; allocated_clusters--;
} }
...@@ -5075,8 +5044,10 @@ static int ext4_find_delayed_extent(struct inode *inode, ...@@ -5075,8 +5044,10 @@ static int ext4_find_delayed_extent(struct inode *inode,
ext4_lblk_t block, next_del; ext4_lblk_t block, next_del;
if (newes->es_pblk == 0) { if (newes->es_pblk == 0) {
ext4_es_find_delayed_extent_range(inode, newes->es_lblk, ext4_es_find_extent_range(inode, &ext4_es_is_delayed,
newes->es_lblk + newes->es_len - 1, &es); newes->es_lblk,
newes->es_lblk + newes->es_len - 1,
&es);
/* /*
* No extent in extent-tree contains block @newes->es_pblk, * No extent in extent-tree contains block @newes->es_pblk,
...@@ -5097,7 +5068,8 @@ static int ext4_find_delayed_extent(struct inode *inode, ...@@ -5097,7 +5068,8 @@ static int ext4_find_delayed_extent(struct inode *inode,
} }
block = newes->es_lblk + newes->es_len; block = newes->es_lblk + newes->es_len;
ext4_es_find_delayed_extent_range(inode, block, EXT_MAX_BLOCKS, &es); ext4_es_find_extent_range(inode, &ext4_es_is_delayed, block,
EXT_MAX_BLOCKS, &es);
if (es.es_len == 0) if (es.es_len == 0)
next_del = EXT_MAX_BLOCKS; next_del = EXT_MAX_BLOCKS;
else else
......
...@@ -233,30 +233,38 @@ static struct extent_status *__es_tree_search(struct rb_root *root, ...@@ -233,30 +233,38 @@ static struct extent_status *__es_tree_search(struct rb_root *root,
} }
/* /*
* ext4_es_find_delayed_extent_range: find the 1st delayed extent covering * ext4_es_find_extent_range - find extent with specified status within block
* @es->lblk if it exists, otherwise, the next extent after @es->lblk. * range or next extent following block range in
* extents status tree
* *
* @inode: the inode which owns delayed extents * @inode - file containing the range
* @lblk: the offset where we start to search * @matching_fn - pointer to function that matches extents with desired status
* @end: the offset where we stop to search * @lblk - logical block defining start of range
* @es: delayed extent that we found * @end - logical block defining end of range
* @es - extent found, if any
*
* Find the first extent within the block range specified by @lblk and @end
* in the extents status tree that satisfies @matching_fn. If a match
* is found, it's returned in @es. If not, and a matching extent is found
* beyond the block range, it's returned in @es. If no match is found, an
* extent is returned in @es whose es_lblk, es_len, and es_pblk components
* are 0.
*/ */
void ext4_es_find_delayed_extent_range(struct inode *inode, static void __es_find_extent_range(struct inode *inode,
ext4_lblk_t lblk, ext4_lblk_t end, int (*matching_fn)(struct extent_status *es),
struct extent_status *es) ext4_lblk_t lblk, ext4_lblk_t end,
struct extent_status *es)
{ {
struct ext4_es_tree *tree = NULL; struct ext4_es_tree *tree = NULL;
struct extent_status *es1 = NULL; struct extent_status *es1 = NULL;
struct rb_node *node; struct rb_node *node;
BUG_ON(es == NULL); WARN_ON(es == NULL);
BUG_ON(end < lblk); WARN_ON(end < lblk);
trace_ext4_es_find_delayed_extent_range_enter(inode, lblk);
read_lock(&EXT4_I(inode)->i_es_lock);
tree = &EXT4_I(inode)->i_es_tree; tree = &EXT4_I(inode)->i_es_tree;
/* find extent in cache firstly */ /* see if the extent has been cached */
es->es_lblk = es->es_len = es->es_pblk = 0; es->es_lblk = es->es_len = es->es_pblk = 0;
if (tree->cache_es) { if (tree->cache_es) {
es1 = tree->cache_es; es1 = tree->cache_es;
...@@ -271,28 +279,133 @@ void ext4_es_find_delayed_extent_range(struct inode *inode, ...@@ -271,28 +279,133 @@ void ext4_es_find_delayed_extent_range(struct inode *inode,
es1 = __es_tree_search(&tree->root, lblk); es1 = __es_tree_search(&tree->root, lblk);
out: out:
if (es1 && !ext4_es_is_delayed(es1)) { if (es1 && !matching_fn(es1)) {
while ((node = rb_next(&es1->rb_node)) != NULL) { while ((node = rb_next(&es1->rb_node)) != NULL) {
es1 = rb_entry(node, struct extent_status, rb_node); es1 = rb_entry(node, struct extent_status, rb_node);
if (es1->es_lblk > end) { if (es1->es_lblk > end) {
es1 = NULL; es1 = NULL;
break; break;
} }
if (ext4_es_is_delayed(es1)) if (matching_fn(es1))
break; break;
} }
} }
if (es1 && ext4_es_is_delayed(es1)) { if (es1 && matching_fn(es1)) {
tree->cache_es = es1; tree->cache_es = es1;
es->es_lblk = es1->es_lblk; es->es_lblk = es1->es_lblk;
es->es_len = es1->es_len; es->es_len = es1->es_len;
es->es_pblk = es1->es_pblk; es->es_pblk = es1->es_pblk;
} }
}
/*
* Locking for __es_find_extent_range() for external use
*/
void ext4_es_find_extent_range(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk, ext4_lblk_t end,
struct extent_status *es)
{
trace_ext4_es_find_extent_range_enter(inode, lblk);
read_lock(&EXT4_I(inode)->i_es_lock);
__es_find_extent_range(inode, matching_fn, lblk, end, es);
read_unlock(&EXT4_I(inode)->i_es_lock);
trace_ext4_es_find_extent_range_exit(inode, es);
}
/*
* __es_scan_range - search block range for block with specified status
* in extents status tree
*
* @inode - file containing the range
* @matching_fn - pointer to function that matches extents with desired status
* @lblk - logical block defining start of range
* @end - logical block defining end of range
*
* Returns true if at least one block in the specified block range satisfies
* the criterion specified by @matching_fn, and false if not. If at least
* one extent has the specified status, then there is at least one block
* in the cluster with that status. Should only be called by code that has
* taken i_es_lock.
*/
static bool __es_scan_range(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t start, ext4_lblk_t end)
{
struct extent_status es;
__es_find_extent_range(inode, matching_fn, start, end, &es);
if (es.es_len == 0)
return false; /* no matching extent in the tree */
else if (es.es_lblk <= start &&
start < es.es_lblk + es.es_len)
return true;
else if (start <= es.es_lblk && es.es_lblk <= end)
return true;
else
return false;
}
/*
* Locking for __es_scan_range() for external use
*/
bool ext4_es_scan_range(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk, ext4_lblk_t end)
{
bool ret;
read_lock(&EXT4_I(inode)->i_es_lock);
ret = __es_scan_range(inode, matching_fn, lblk, end);
read_unlock(&EXT4_I(inode)->i_es_lock);
return ret;
}
/*
* __es_scan_clu - search cluster for block with specified status in
* extents status tree
*
* @inode - file containing the cluster
* @matching_fn - pointer to function that matches extents with desired status
* @lblk - logical block in cluster to be searched
*
* Returns true if at least one extent in the cluster containing @lblk
* satisfies the criterion specified by @matching_fn, and false if not. If at
* least one extent has the specified status, then there is at least one block
* in the cluster with that status. Should only be called by code that has
* taken i_es_lock.
*/
static bool __es_scan_clu(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk)
{
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
ext4_lblk_t lblk_start, lblk_end;
lblk_start = EXT4_LBLK_CMASK(sbi, lblk);
lblk_end = lblk_start + sbi->s_cluster_ratio - 1;
return __es_scan_range(inode, matching_fn, lblk_start, lblk_end);
}
/*
* Locking for __es_scan_clu() for external use
*/
bool ext4_es_scan_clu(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk)
{
bool ret;
read_lock(&EXT4_I(inode)->i_es_lock);
ret = __es_scan_clu(inode, matching_fn, lblk);
read_unlock(&EXT4_I(inode)->i_es_lock); read_unlock(&EXT4_I(inode)->i_es_lock);
trace_ext4_es_find_delayed_extent_range_exit(inode, es); return ret;
} }
static void ext4_es_list_add(struct inode *inode) static void ext4_es_list_add(struct inode *inode)
......
...@@ -90,11 +90,18 @@ extern void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, ...@@ -90,11 +90,18 @@ extern void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
unsigned int status); unsigned int status);
extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
ext4_lblk_t len); ext4_lblk_t len);
extern void ext4_es_find_delayed_extent_range(struct inode *inode, extern void ext4_es_find_extent_range(struct inode *inode,
ext4_lblk_t lblk, ext4_lblk_t end, int (*match_fn)(struct extent_status *es),
struct extent_status *es); ext4_lblk_t lblk, ext4_lblk_t end,
struct extent_status *es);
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
struct extent_status *es); struct extent_status *es);
extern bool ext4_es_scan_range(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk, ext4_lblk_t end);
extern bool ext4_es_scan_clu(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk);
static inline unsigned int ext4_es_status(struct extent_status *es) static inline unsigned int ext4_es_status(struct extent_status *es)
{ {
......
...@@ -577,8 +577,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -577,8 +577,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
!(status & EXTENT_STATUS_WRITTEN) && !(status & EXTENT_STATUS_WRITTEN) &&
ext4_find_delalloc_range(inode, map->m_lblk, ext4_es_scan_range(inode, &ext4_es_is_delayed, map->m_lblk,
map->m_lblk + map->m_len - 1)) map->m_lblk + map->m_len - 1))
status |= EXTENT_STATUS_DELAYED; status |= EXTENT_STATUS_DELAYED;
ret = ext4_es_insert_extent(inode, map->m_lblk, ret = ext4_es_insert_extent(inode, map->m_lblk,
map->m_len, map->m_pblk, status); map->m_len, map->m_pblk, status);
...@@ -701,8 +701,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -701,8 +701,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
!(status & EXTENT_STATUS_WRITTEN) && !(status & EXTENT_STATUS_WRITTEN) &&
ext4_find_delalloc_range(inode, map->m_lblk, ext4_es_scan_range(inode, &ext4_es_is_delayed, map->m_lblk,
map->m_lblk + map->m_len - 1)) map->m_lblk + map->m_len - 1))
status |= EXTENT_STATUS_DELAYED; status |= EXTENT_STATUS_DELAYED;
ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
map->m_pblk, status); map->m_pblk, status);
...@@ -1681,7 +1681,7 @@ static void ext4_da_page_release_reservation(struct page *page, ...@@ -1681,7 +1681,7 @@ static void ext4_da_page_release_reservation(struct page *page,
lblk = (page->index << (PAGE_SHIFT - inode->i_blkbits)) + lblk = (page->index << (PAGE_SHIFT - inode->i_blkbits)) +
((num_clusters - 1) << sbi->s_cluster_bits); ((num_clusters - 1) << sbi->s_cluster_bits);
if (sbi->s_cluster_ratio == 1 || if (sbi->s_cluster_ratio == 1 ||
!ext4_find_delalloc_cluster(inode, lblk)) !ext4_es_scan_clu(inode, &ext4_es_is_delayed, lblk))
ext4_da_release_space(inode, 1); ext4_da_release_space(inode, 1);
num_clusters--; num_clusters--;
...@@ -1859,6 +1859,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, ...@@ -1859,6 +1859,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
add_delayed: add_delayed:
if (retval == 0) { if (retval == 0) {
int ret; int ret;
/* /*
* XXX: __block_prepare_write() unmaps passed block, * XXX: __block_prepare_write() unmaps passed block,
* is it OK? * is it OK?
...@@ -1869,7 +1870,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, ...@@ -1869,7 +1870,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
* to reserve metadata for every block we're going to write. * to reserve metadata for every block we're going to write.
*/ */
if (EXT4_SB(inode->i_sb)->s_cluster_ratio == 1 || if (EXT4_SB(inode->i_sb)->s_cluster_ratio == 1 ||
!ext4_find_delalloc_cluster(inode, map->m_lblk)) { !ext4_es_scan_clu(inode,
&ext4_es_is_delayed, map->m_lblk)) {
ret = ext4_da_reserve_space(inode); ret = ext4_da_reserve_space(inode);
if (ret) { if (ret) {
/* not enough space to reserve */ /* not enough space to reserve */
...@@ -3450,7 +3452,8 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length, ...@@ -3450,7 +3452,8 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
ext4_lblk_t end = map.m_lblk + map.m_len - 1; ext4_lblk_t end = map.m_lblk + map.m_len - 1;
struct extent_status es; struct extent_status es;
ext4_es_find_delayed_extent_range(inode, map.m_lblk, end, &es); ext4_es_find_extent_range(inode, &ext4_es_is_delayed,
map.m_lblk, end, &es);
if (!es.es_len || es.es_lblk > end) { if (!es.es_len || es.es_lblk > end) {
/* entire range is a hole */ /* entire range is a hole */
......
...@@ -2270,7 +2270,7 @@ TRACE_EVENT(ext4_es_remove_extent, ...@@ -2270,7 +2270,7 @@ TRACE_EVENT(ext4_es_remove_extent,
__entry->lblk, __entry->len) __entry->lblk, __entry->len)
); );
TRACE_EVENT(ext4_es_find_delayed_extent_range_enter, TRACE_EVENT(ext4_es_find_extent_range_enter,
TP_PROTO(struct inode *inode, ext4_lblk_t lblk), TP_PROTO(struct inode *inode, ext4_lblk_t lblk),
TP_ARGS(inode, lblk), TP_ARGS(inode, lblk),
...@@ -2292,7 +2292,7 @@ TRACE_EVENT(ext4_es_find_delayed_extent_range_enter, ...@@ -2292,7 +2292,7 @@ TRACE_EVENT(ext4_es_find_delayed_extent_range_enter,
(unsigned long) __entry->ino, __entry->lblk) (unsigned long) __entry->ino, __entry->lblk)
); );
TRACE_EVENT(ext4_es_find_delayed_extent_range_exit, TRACE_EVENT(ext4_es_find_extent_range_exit,
TP_PROTO(struct inode *inode, struct extent_status *es), TP_PROTO(struct inode *inode, struct extent_status *es),
TP_ARGS(inode, es), TP_ARGS(inode, es),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment