Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-04 Thread Amit K. Arora
On Wed, Jan 03, 2007 at 10:07:01AM -0800, Mingming Cao wrote:
 Alex Tomas wrote:
 I think that stuff that converts uninitialized blocks
 to initialized ones should be a separate codepath and
 shouldn't be done in the insert path. and an insert
 (basic tree manipulation) should BUG_ON() one tries
 to add extent with a block which is already covered
 by the tree.
 
 IMHO, get_blocks() should look like:
 
   path = find_path()
   if (found extent covers request block(s)) {
 if (extent is uninitialized) {
   convert();
 }
   }
 
 where
function convert()
   {
 /* adopt existing extent so that it
  * doesn't cover requested blocks */
 
 /* insert head or tail of existing
  * extent, if necessary */
 
 /* insert new extent of initialized blocks */
   }
 
 thanks, Alex
 
 I was thing about the same thing. The current ext4_ext_get_blocks()
 function becomes very bulky. The code to convert uninitialized blocks to
 initialized ones is pretty selfcontained, and worth the effort to put it
 into a seperate function.

Ok. I will move this code to a new function.

--
Regards,
Amit Arora
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-04 Thread Alex Tomas
 Mingming Cao (MC) writes:

 MC But the bug Amit pointed here is unrelated to the code convert
 MC uninitialized blocks to initialized ones. Rather, it's related to do
 MC multiple block allocation across on a window with parts already have
 MC blocks allocated. Without the check, the current code just simply
 MC allocate the requested extent and insert it into the tree which might
 MC overlap with existing extent.

correct. thanks for catching. in delayed allocation patch
get_blocks() isn't used and ext4_ext_walk_space() works
right in this case.

thanks, Alex
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-04 Thread Mingming Cao

Alex Tomas wrote:

Mingming Cao (MC) writes:



 MC But the bug Amit pointed here is unrelated to the code convert
 MC uninitialized blocks to initialized ones. Rather, it's related to do
 MC multiple block allocation across on a window with parts already have
 MC blocks allocated. Without the check, the current code just simply
 MC allocate the requested extent and insert it into the tree which might
 MC overlap with existing extent.

correct. thanks for catching. in delayed allocation patch
get_blocks() isn't used and ext4_ext_walk_space() works
right in this case.



Yep, I realized that yesterday. That explains we never see this overlap 
problem when we testing extent+delalloc+mballoc last year.


 ext4_ext_walk_space did almost all the overlap check.  I think we 
could reuse that code.


Mingming

thanks, Alex
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html



-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 1/1] Extent overlap bugfix in ext4

2007-01-02 Thread Amit K. Arora
The ext4_ext_get_blocks() and ext4_ext_insert_extent() routines do not
check for extent overlap, when a new extent needs to be inserted in an
inode. An overlap is possible when the new extent being inserted has
ee_block that is not part of any of the existing extents, but the
tail/center portion of this new extent _is_. This is possible only when
we are writing/preallocating blocks across a hole.

This problem was first sighted while stress testing (using modified
fsx-linux stress test) persistent preallocation patches that I posted
earlier.  Though I am not able to reproduce this bug (extent overlap)
without the persistent preallocation patches (because a write through a
hole results in get_blocks() of a single block at a time), but I think
that it is an independant problem and should be solved with a separate
patch. Hence this patch.

Comments please. Thanks!

Signed-off-by: Amit Arora ([EMAIL PROTECTED])
---
 fs/ext4/extents.c   |   71 +---
 include/linux/ext4_fs_extents.h |1 
 2 files changed, 68 insertions(+), 4 deletions(-)

Index: linux-2.6.19.prealloc/fs/ext4/extents.c
===
--- linux-2.6.19.prealloc.orig/fs/ext4/extents.c2007-01-02 
14:21:57.0 +0530
+++ linux-2.6.19.prealloc/fs/ext4/extents.c 2007-01-02 14:22:00.0 
+0530
@@ -1119,6 +1119,44 @@
 }
 
 /*
+ * ext4_ext_check_overlap:
+ * check if a portion of the newext extent overlaps with an
+ * existing extent.
+ */
+struct ext4_extent * ext4_ext_check_overlap(struct inode *inode,
+   struct ext4_extent *newext)
+{
+   struct ext4_ext_path *path;
+   struct ext4_extent *ex;
+   unsigned int depth, b1, b2, len1;
+
+   b1 = le32_to_cpu(newext-ee_block);
+   len1 = le16_to_cpu(newext-ee_len);
+   path = ext4_ext_find_extent(inode, b1, NULL);
+   if (IS_ERR(path))
+   return NULL;
+
+   depth = ext_depth(inode);
+   ex = path[depth].p_ext;
+   if (!ex)
+   return NULL;
+
+   b2 = ext4_ext_next_allocated_block(path);
+   if (b2 == EXT_MAX_BLOCK)
+   return NULL;
+   path = ext4_ext_find_extent(inode, b2, path);
+   if (IS_ERR(path))
+   return NULL;
+   BUG_ON(path[depth].p_hdr == NULL);
+   ex = path[depth].p_ext;
+
+   if (b1 + len1  b2)
+   return ex;
+
+   return NULL;
+}
+
+/*
  * ext4_ext_insert_extent:
  * tries to merge requsted extent into the existing extent or
  * inserts requested extent as new one into the tree,
@@ -1129,7 +1167,7 @@
struct ext4_extent *newext)
 {
struct ext4_extent_header * eh;
-   struct ext4_extent *ex, *fex;
+   struct ext4_extent *ex, *fex, *rex;
struct ext4_extent *nearex; /* nearest extent */
struct ext4_ext_path *npath = NULL;
int depth, len, err, next;
@@ -1139,6 +1177,18 @@
ex = path[depth].p_ext;
BUG_ON(path[depth].p_hdr == NULL);
 
+   /* check for overlap */
+   rex = ext4_ext_check_overlap(inode, newext);
+   if (rex) {
+   printk(KERN_ERR ERROR: ex=%u/%u overlaps newext=%u/%u\n,
+   le32_to_cpu(rex-ee_block),
+   le16_to_cpu(rex-ee_len),
+   le32_to_cpu(newext-ee_block),
+   le16_to_cpu(newext-ee_len));
+   ext4_ext_show_leaf(inode, path);
+   BUG();
+   }
+
/* try to insert block into found extent and return */
if (ex  ext4_can_extents_be_merged(inode, ex, newext)) {
ext_debug(append %d block to %d:%d (from %llu)\n,
@@ -1921,7 +1971,7 @@
int create, int extend_disksize)
 {
struct ext4_ext_path *path = NULL;
-   struct ext4_extent newex, *ex;
+   struct ext4_extent newex, *ex, *ex2;
ext4_fsblk_t goal, newblock;
int err = 0, depth;
unsigned long allocated = 0;
@@ -1984,6 +2034,10 @@
 */
if (ee_len  EXT_MAX_LEN)
goto out2;
+
+   if (iblock  ee_block  iblock + max_blocks = ee_block)
+   allocated = ee_block - iblock;
+
/* if found extent covers block, simply return it */
if (iblock = ee_block  iblock  ee_block + ee_len) {
newblock = iblock - ee_block + ee_start;
@@ -2016,7 +2070,17 @@
 
/* allocate new block */
goal = ext4_ext_find_goal(inode, path, iblock);
-   allocated = max_blocks;
+
+   /* Check if we can really insert (iblock)::(iblock+max_blocks) extent */
+   newex.ee_block = cpu_to_le32(iblock);
+   if (!allocated) {
+   newex.ee_len = cpu_to_le16(max_blocks);
+   ex2 = ext4_ext_check_overlap(inode, newex);
+   if (ex2)
+   

Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-02 Thread Alex Tomas
 Amit K Arora (AKA) writes:

 AKA The ext4_ext_get_blocks() and ext4_ext_insert_extent() routines do not
 AKA check for extent overlap, when a new extent needs to be inserted in an
 AKA inode. An overlap is possible when the new extent being inserted has
 AKA ee_block that is not part of any of the existing extents, but the
 AKA tail/center portion of this new extent _is_. This is possible only when
 AKA we are writing/preallocating blocks across a hole.

not sure I understand ... you shouldn't insert an extent that overlap
any existing extent. when you write block(s), you first check is
it already allocated and insert new extent only if it's not. for
preallocated block(s), you should adapt existing extent(s) so that
they don't overlap new extent you're inserting. am I missing something?
also, I think that modification of existing extent(s) (not merging)
isn't safe.

thanks, Alex
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-02 Thread Amit K. Arora
On Tue, Jan 02, 2007 at 12:25:21PM +0300, Alex Tomas (AT) wrote:
  Amit K Arora (AKA) writes:
 
  AKA The ext4_ext_get_blocks() and ext4_ext_insert_extent() routines do not
  AKA check for extent overlap, when a new extent needs to be inserted in an
  AKA inode. An overlap is possible when the new extent being inserted has
  AKA ee_block that is not part of any of the existing extents, but the
  AKA tail/center portion of this new extent _is_. This is possible only when
  AKA we are writing/preallocating blocks across a hole.
 
 AT not sure I understand ... you shouldn't insert an extent that overlap
 AT any existing extent. when you write block(s), you first check is
 AT it already allocated and insert new extent only if it's not.

You are right. That is what this patch does.
The current ext4 code is inserting an overlapped extent in a particular
scenario (explained above). The suggested patch fixes this by having a
check in get_blocks() for _not_ inserting an extent that may overlap
with an existing one.

 AT for preallocated block(s), you should adapt existing extent(s) so that
 AT they don't overlap new extent you're inserting. am I missing something?

The patch makes the new extent being inserted adjust its length based on any
existing extent that may overlap, so that the overlap does not happen at
all.

 AT also, I think that modification of existing extent(s) (not merging)
 AT isn't safe.

The existing extent(s) are not being modified in any way here. We check
if there is an overlap between the new extent being inserted by
get_blocks(), with an existing one. If there is, we update the new extent
(being inserted) accordingly. The existing extent is not touched (unless
the insert_extent() does a merge, if possible).

Please let me know if the intentions are still not clear here. Thanks!

Regards,
Amit Arora
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-02 Thread Mingming Cao
On Tue, 2007-01-02 at 14:39 +0530, Amit K. Arora wrote:

 This problem was first sighted while stress testing (using modified
 fsx-linux stress test) persistent preallocation patches that I posted
 earlier.  Though I am not able to reproduce this bug (extent overlap)
 without the persistent preallocation patches (because a write through a
 hole results in get_blocks() of a single block at a time), but I think
 that it is an independant problem and should be solved with a separate
 patch. Hence this patch.
 

Ah...without preallocation, I guess the problem still will be uncovered
when we have delayed allocation, that makes multiple block allocation
across hole possible.

  /*
 + * ext4_ext_check_overlap:
 + * check if a portion of the newext extent overlaps with an
 + * existing extent.
 + */
 +struct ext4_extent * ext4_ext_check_overlap(struct inode *inode,
 + struct ext4_extent *newext)
 +{
 + struct ext4_ext_path *path;
 + struct ext4_extent *ex;
 + unsigned int depth, b1, b2, len1;
 +
 + b1 = le32_to_cpu(newext-ee_block);
 + len1 = le16_to_cpu(newext-ee_len);
 + path = ext4_ext_find_extent(inode, b1, NULL);
 + if (IS_ERR(path))
 + return NULL;
 +
 + depth = ext_depth(inode);
 + ex = path[depth].p_ext;
 + if (!ex)
 + return NULL;
 +

I am confused, when we come here, isn't we confirmed that we need block
allocation, thus there is no extent start from b1?

 + b2 = ext4_ext_next_allocated_block(path);
 + if (b2 == EXT_MAX_BLOCK)
 +
   return NULL;
 + path = ext4_ext_find_extent(inode, b2, path);
 + if (IS_ERR(path))
 + return NULL;
 + BUG_ON(path[depth].p_hdr == NULL);
 + ex = path[depth].p_ext;
 +

How useful to have the next extent pointer?It seems only used to print
out warning messages. I am a little concerned about the expensive
ext4_ext_find_extent(). After all ext4_ext_next_allocated_block()
already returns the start block of next extent, isn't it?

 + if (b1 + len1  b2)
 + return ex;
 +
 + return NULL;
 +}
 +
 +/*
   * ext4_ext_insert_extent:
   * tries to merge requsted extent into the existing extent or
   * inserts requested extent as new one into the tree,
 @@ -1129,7 +1167,7 @@
   struct ext4_extent *newext)
  {
   struct ext4_extent_header * eh;
 - struct ext4_extent *ex, *fex;
 + struct ext4_extent *ex, *fex, *rex;
   struct ext4_extent *nearex; /* nearest extent */
   struct ext4_ext_path *npath = NULL;
   int depth, len, err, next;
 @@ -1139,6 +1177,18 @@
   ex = path[depth].p_ext;
   BUG_ON(path[depth].p_hdr == NULL);
 
 + /* check for overlap */
 + rex = ext4_ext_check_overlap(inode, newext);
 + if (rex) {
 + printk(KERN_ERR ERROR: ex=%u/%u overlaps newext=%u/%u\n,
 + le32_to_cpu(rex-ee_block),
 + le16_to_cpu(rex-ee_len),
 + le32_to_cpu(newext-ee_block),
 + le16_to_cpu(newext-ee_len));
 + ext4_ext_show_leaf(inode, path);
 + BUG();
 + }
 +
   /* try to insert block into found extent and return */
   if (ex  ext4_can_extents_be_merged(inode, ex, newext)) {
   ext_debug(append %d block to %d:%d (from %llu)\n,
 @@ -1921,7 +1971,7 @@
   int create, int extend_disksize)
  {
   struct ext4_ext_path *path = NULL;
 - struct ext4_extent newex, *ex;
 + struct ext4_extent newex, *ex, *ex2;
   ext4_fsblk_t goal, newblock;
   int err = 0, depth;
   unsigned long allocated = 0;
 @@ -1984,6 +2034,10 @@
*/
   if (ee_len  EXT_MAX_LEN)
   goto out2;
 +
 + if (iblock  ee_block  iblock + max_blocks = ee_block)
 + allocated = ee_block - iblock;
 +
   /* if found extent covers block, simply return it */
   if (iblock = ee_block  iblock  ee_block + ee_len) {
   newblock = iblock - ee_block + ee_start;
 @@ -2016,7 +2070,17 @@
 
   /* allocate new block */
   goal = ext4_ext_find_goal(inode, path, iblock);
 - allocated = max_blocks;
 +
 + /* Check if we can really insert (iblock)::(iblock+max_blocks) extent */
 + newex.ee_block = cpu_to_le32(iblock);
 + if (!allocated) {
 + newex.ee_len = cpu_to_le16(max_blocks);
 + ex2 = ext4_ext_check_overlap(inode, newex);
 + if (ex2)
 + allocated = le32_to_cpu(ex2-ee_block) - iblock;
 + else
 + allocated = max_blocks;
 + }
   newblock = ext4_new_blocks(handle, inode, goal, allocated, err);
   if (!newblock)
   goto out2;
 @@ -2024,7 +2088,6 @@
   goal, newblock, allocated);
 
   /* try to insert new extent into found leaf and return */
 - 

Re: [PATCH 1/1] Extent overlap bugfix in ext4

2007-01-02 Thread Amit K. Arora
On Tue, Jan 02, 2007 at 05:35:28PM -0800, Mingming Cao wrote:
  +struct ext4_extent * ext4_ext_check_overlap(struct inode *inode,
  +   struct ext4_extent *newext)
  +{
  +   struct ext4_ext_path *path;
  +   struct ext4_extent *ex;
  +   unsigned int depth, b1, b2, len1;
  +
  +   b1 = le32_to_cpu(newext-ee_block);
  +   len1 = le16_to_cpu(newext-ee_len);
  +   path = ext4_ext_find_extent(inode, b1, NULL);
  +   if (IS_ERR(path))
  +   return NULL;
  +
  +   depth = ext_depth(inode);
  +   ex = path[depth].p_ext;
  +   if (!ex)
  +   return NULL;
  +
 
 I am confused, when we come here, isn't we confirmed that we need block
 allocation, thus there is no extent start from b1?

Yes, we are sure here that there is no extent which covers b1 block.
Since I couldn't find a direct way to get the next extent (extent on the
right from the would be position of the new extent in the tree), we
make a call to ext4_ext_find_extent() to get the extent on the left, and
then use this to call ext4_ext_next_allocated_block() to get the logical
block number (LBN) of the next extent in the tree. This LBN is
compared with the LBN of the new extent plus its length, to detect an
overlap.

 
  +   b2 = ext4_ext_next_allocated_block(path);
  +   if (b2 == EXT_MAX_BLOCK)
  +
  return NULL;
  +   path = ext4_ext_find_extent(inode, b2, path);
  +   if (IS_ERR(path))
  +   return NULL;
  +   BUG_ON(path[depth].p_hdr == NULL);
  +   ex = path[depth].p_ext;
  +
 
 How useful to have the next extent pointer?It seems only used to print
 out warning messages. I am a little concerned about the expensive
 ext4_ext_find_extent(). After all ext4_ext_next_allocated_block()
 already returns the start block of next extent, isn't it?

Ok, agreed. Will get rid of this extra code.


--
Regards,
Amit Arora
-
To unsubscribe from this list: send the line unsubscribe linux-ext4 in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html