|
@@ -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
|
|
|
- * @es->lblk if it exists, otherwise, the next extent after @es->lblk.
|
|
|
+ * ext4_es_find_extent_range - find extent with specified status within block
|
|
|
+ * range or next extent following block range in
|
|
|
+ * extents status tree
|
|
|
*
|
|
|
- * @inode: the inode which owns delayed extents
|
|
|
- * @lblk: the offset where we start to search
|
|
|
- * @end: the offset where we stop to search
|
|
|
- * @es: delayed extent that we found
|
|
|
+ * @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
|
|
|
+ * @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,
|
|
|
- ext4_lblk_t lblk, ext4_lblk_t end,
|
|
|
- struct extent_status *es)
|
|
|
+static void __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)
|
|
|
{
|
|
|
struct ext4_es_tree *tree = NULL;
|
|
|
struct extent_status *es1 = NULL;
|
|
|
struct rb_node *node;
|
|
|
|
|
|
- BUG_ON(es == NULL);
|
|
|
- BUG_ON(end < lblk);
|
|
|
- trace_ext4_es_find_delayed_extent_range_enter(inode, lblk);
|
|
|
+ WARN_ON(es == NULL);
|
|
|
+ WARN_ON(end < lblk);
|
|
|
|
|
|
- read_lock(&EXT4_I(inode)->i_es_lock);
|
|
|
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;
|
|
|
if (tree->cache_es) {
|
|
|
es1 = tree->cache_es;
|
|
@@ -271,28 +279,133 @@ void ext4_es_find_delayed_extent_range(struct inode *inode,
|
|
|
es1 = __es_tree_search(&tree->root, lblk);
|
|
|
|
|
|
out:
|
|
|
- if (es1 && !ext4_es_is_delayed(es1)) {
|
|
|
+ if (es1 && !matching_fn(es1)) {
|
|
|
while ((node = rb_next(&es1->rb_node)) != NULL) {
|
|
|
es1 = rb_entry(node, struct extent_status, rb_node);
|
|
|
if (es1->es_lblk > end) {
|
|
|
es1 = NULL;
|
|
|
break;
|
|
|
}
|
|
|
- if (ext4_es_is_delayed(es1))
|
|
|
+ if (matching_fn(es1))
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (es1 && ext4_es_is_delayed(es1)) {
|
|
|
+ if (es1 && matching_fn(es1)) {
|
|
|
tree->cache_es = es1;
|
|
|
es->es_lblk = es1->es_lblk;
|
|
|
es->es_len = es1->es_len;
|
|
|
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);
|
|
|
|
|
|
- trace_ext4_es_find_delayed_extent_range_exit(inode, es);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void ext4_es_list_add(struct inode *inode)
|