|
@@ -179,7 +179,9 @@ static void ext4_es_print_tree(struct inode *inode)
|
|
|
while (node) {
|
|
|
struct extent_status *es;
|
|
|
es = rb_entry(node, struct extent_status, rb_node);
|
|
|
- printk(KERN_DEBUG " [%u/%u)", es->es_lblk, es->es_len);
|
|
|
+ printk(KERN_DEBUG " [%u/%u) %llu %llx",
|
|
|
+ es->es_lblk, es->es_len,
|
|
|
+ ext4_es_pblock(es), ext4_es_status(es));
|
|
|
node = rb_next(node);
|
|
|
}
|
|
|
printk(KERN_DEBUG "\n");
|
|
@@ -234,7 +236,7 @@ static struct extent_status *__es_tree_search(struct rb_root *root,
|
|
|
* @es: delayed extent that we found
|
|
|
*
|
|
|
* Returns the first block of the next extent after es, otherwise
|
|
|
- * EXT_MAX_BLOCKS if no delay extent is found.
|
|
|
+ * EXT_MAX_BLOCKS if no extent is found.
|
|
|
* Delayed extent is returned via @es.
|
|
|
*/
|
|
|
ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es)
|
|
@@ -249,17 +251,18 @@ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es)
|
|
|
read_lock(&EXT4_I(inode)->i_es_lock);
|
|
|
tree = &EXT4_I(inode)->i_es_tree;
|
|
|
|
|
|
- /* find delay extent in cache firstly */
|
|
|
+ /* find extent in cache firstly */
|
|
|
+ es->es_len = es->es_pblk = 0;
|
|
|
if (tree->cache_es) {
|
|
|
es1 = tree->cache_es;
|
|
|
if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) {
|
|
|
- es_debug("%u cached by [%u/%u)\n",
|
|
|
- es->es_lblk, es1->es_lblk, es1->es_len);
|
|
|
+ es_debug("%u cached by [%u/%u) %llu %llx\n",
|
|
|
+ es->es_lblk, es1->es_lblk, es1->es_len,
|
|
|
+ ext4_es_pblock(es1), ext4_es_status(es1));
|
|
|
goto out;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- es->es_len = 0;
|
|
|
es1 = __es_tree_search(&tree->root, es->es_lblk);
|
|
|
|
|
|
out:
|
|
@@ -267,6 +270,7 @@ out:
|
|
|
tree->cache_es = es1;
|
|
|
es->es_lblk = es1->es_lblk;
|
|
|
es->es_len = es1->es_len;
|
|
|
+ es->es_pblk = es1->es_pblk;
|
|
|
node = rb_next(&es1->rb_node);
|
|
|
if (node) {
|
|
|
es1 = rb_entry(node, struct extent_status, rb_node);
|
|
@@ -281,7 +285,7 @@ out:
|
|
|
}
|
|
|
|
|
|
static struct extent_status *
|
|
|
-ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len)
|
|
|
+ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk)
|
|
|
{
|
|
|
struct extent_status *es;
|
|
|
es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC);
|
|
@@ -289,6 +293,7 @@ ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len)
|
|
|
return NULL;
|
|
|
es->es_lblk = lblk;
|
|
|
es->es_len = len;
|
|
|
+ es->es_pblk = pblk;
|
|
|
return es;
|
|
|
}
|
|
|
|
|
@@ -301,6 +306,8 @@ static void ext4_es_free_extent(struct extent_status *es)
|
|
|
* Check whether or not two extents can be merged
|
|
|
* Condition:
|
|
|
* - logical block number is contiguous
|
|
|
+ * - physical block number is contiguous
|
|
|
+ * - status is equal
|
|
|
*/
|
|
|
static int ext4_es_can_be_merged(struct extent_status *es1,
|
|
|
struct extent_status *es2)
|
|
@@ -308,6 +315,13 @@ static int ext4_es_can_be_merged(struct extent_status *es1,
|
|
|
if (es1->es_lblk + es1->es_len != es2->es_lblk)
|
|
|
return 0;
|
|
|
|
|
|
+ if (ext4_es_status(es1) != ext4_es_status(es2))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if ((ext4_es_is_written(es1) || ext4_es_is_unwritten(es1)) &&
|
|
|
+ (ext4_es_pblock(es1) + es1->es_len != ext4_es_pblock(es2)))
|
|
|
+ return 0;
|
|
|
+
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -371,6 +385,10 @@ static int __es_insert_extent(struct ext4_es_tree *tree,
|
|
|
*/
|
|
|
es->es_lblk = newes->es_lblk;
|
|
|
es->es_len += newes->es_len;
|
|
|
+ if (ext4_es_is_written(es) ||
|
|
|
+ ext4_es_is_unwritten(es))
|
|
|
+ ext4_es_store_pblock(es,
|
|
|
+ newes->es_pblk);
|
|
|
es = ext4_es_try_to_merge_left(tree, es);
|
|
|
goto out;
|
|
|
}
|
|
@@ -388,7 +406,8 @@ static int __es_insert_extent(struct ext4_es_tree *tree,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len);
|
|
|
+ es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len,
|
|
|
+ newes->es_pblk);
|
|
|
if (!es)
|
|
|
return -ENOMEM;
|
|
|
rb_link_node(&es->rb_node, parent, p);
|
|
@@ -408,21 +427,24 @@ out:
|
|
|
* Return 0 on success, error code on failure.
|
|
|
*/
|
|
|
int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
|
|
- ext4_lblk_t len)
|
|
|
+ ext4_lblk_t len, ext4_fsblk_t pblk,
|
|
|
+ unsigned long long status)
|
|
|
{
|
|
|
struct ext4_es_tree *tree;
|
|
|
struct extent_status newes;
|
|
|
ext4_lblk_t end = lblk + len - 1;
|
|
|
int err = 0;
|
|
|
|
|
|
- trace_ext4_es_insert_extent(inode, lblk, len);
|
|
|
- es_debug("add [%u/%u) to extent status tree of inode %lu\n",
|
|
|
- lblk, len, inode->i_ino);
|
|
|
+ es_debug("add [%u/%u) %llu %llx to extent status tree of inode %lu\n",
|
|
|
+ lblk, len, pblk, status, inode->i_ino);
|
|
|
|
|
|
BUG_ON(end < lblk);
|
|
|
|
|
|
newes.es_lblk = lblk;
|
|
|
newes.es_len = len;
|
|
|
+ ext4_es_store_pblock(&newes, pblk);
|
|
|
+ ext4_es_store_status(&newes, status);
|
|
|
+ trace_ext4_es_insert_extent(inode, &newes);
|
|
|
|
|
|
write_lock(&EXT4_I(inode)->i_es_lock);
|
|
|
tree = &EXT4_I(inode)->i_es_tree;
|
|
@@ -446,6 +468,7 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
|
|
|
struct extent_status *es;
|
|
|
struct extent_status orig_es;
|
|
|
ext4_lblk_t len1, len2;
|
|
|
+ ext4_fsblk_t block;
|
|
|
int err = 0;
|
|
|
|
|
|
es = __es_tree_search(&tree->root, lblk);
|
|
@@ -459,6 +482,8 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
|
|
|
|
|
|
orig_es.es_lblk = es->es_lblk;
|
|
|
orig_es.es_len = es->es_len;
|
|
|
+ orig_es.es_pblk = es->es_pblk;
|
|
|
+
|
|
|
len1 = lblk > es->es_lblk ? lblk - es->es_lblk : 0;
|
|
|
len2 = ext4_es_end(es) > end ? ext4_es_end(es) - end : 0;
|
|
|
if (len1 > 0)
|
|
@@ -469,6 +494,13 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
|
|
|
|
|
|
newes.es_lblk = end + 1;
|
|
|
newes.es_len = len2;
|
|
|
+ if (ext4_es_is_written(&orig_es) ||
|
|
|
+ ext4_es_is_unwritten(&orig_es)) {
|
|
|
+ block = ext4_es_pblock(&orig_es) +
|
|
|
+ orig_es.es_len - len2;
|
|
|
+ ext4_es_store_pblock(&newes, block);
|
|
|
+ }
|
|
|
+ ext4_es_store_status(&newes, ext4_es_status(&orig_es));
|
|
|
err = __es_insert_extent(tree, &newes);
|
|
|
if (err) {
|
|
|
es->es_lblk = orig_es.es_lblk;
|
|
@@ -478,6 +510,11 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
|
|
|
} else {
|
|
|
es->es_lblk = end + 1;
|
|
|
es->es_len = len2;
|
|
|
+ if (ext4_es_is_written(es) ||
|
|
|
+ ext4_es_is_unwritten(es)) {
|
|
|
+ block = orig_es.es_pblk + orig_es.es_len - len2;
|
|
|
+ ext4_es_store_pblock(es, block);
|
|
|
+ }
|
|
|
}
|
|
|
goto out;
|
|
|
}
|
|
@@ -502,9 +539,15 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk,
|
|
|
}
|
|
|
|
|
|
if (es && es->es_lblk < end + 1) {
|
|
|
+ ext4_lblk_t orig_len = es->es_len;
|
|
|
+
|
|
|
len1 = ext4_es_end(es) - end;
|
|
|
es->es_lblk = end + 1;
|
|
|
es->es_len = len1;
|
|
|
+ if (ext4_es_is_written(es) || ext4_es_is_unwritten(es)) {
|
|
|
+ block = es->es_pblk + orig_len - len1;
|
|
|
+ ext4_es_store_pblock(es, block);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
out:
|