[PATCH v3 3/7] ext4: add support FALLOC_FL_COLLAPSE_RANGE for fallocate

2013-09-08 Thread Namjae Jeon
From: Namjae Jeon 

Add support FALLOC_FL_COLLAPSE_RANGE for fallocate.

Signed-off-by: Namjae Jeon 
Signed-off-by: Ashish Sangwan 
---
 fs/ext4/ext4.h  |3 +
 fs/ext4/extents.c   |  286 ++-
 fs/ext4/move_extent.c   |2 +-
 include/trace/events/ext4.h |   25 
 4 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f3c43fc..a13e0f4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2737,6 +2737,7 @@ extern int ext4_find_delalloc_cluster(struct inode 
*inode, ext4_lblk_t lblk);
 extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
 extern int ext4_ext_precache(struct inode *inode);
+extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
 
 /* move_extent.c */
 extern void ext4_double_down_write_data_sem(struct inode *first,
@@ -2748,6 +2749,8 @@ void ext4_inode_double_unlock(struct inode *inode1, 
struct inode *inode2);
 extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
 __u64 start_orig, __u64 start_donor,
 __u64 len, __u64 *moved_len);
+extern int mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
+   struct ext4_extent **extent);
 
 /* page-io.c */
 extern int __init ext4_init_pageio(void);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 54d52af..baf1faf 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4580,12 +4580,16 @@ long ext4_fallocate(struct file *file, int mode, loff_t 
offset, loff_t len)
unsigned int credits, blkbits = inode->i_blkbits;
 
/* Return error if mode is not supported */
-   if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+   if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+FALLOC_FL_COLLAPSE_RANGE))
return -EOPNOTSUPP;
 
if (mode & FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);
 
+   if (mode & FALLOC_FL_COLLAPSE_RANGE)
+   return ext4_collapse_range(inode, offset, len);
+
ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
@@ -4884,3 +4888,283 @@ int ext4_fiemap(struct inode *inode, struct 
fiemap_extent_info *fieinfo,
ext4_es_lru_add(inode);
return error;
 }
+
+/*
+ * ext4_access_path:
+ * Function to access the path buffer for marking it dirty.
+ * It also checks if there are sufficient credits left in the journal handle
+ * to update path.
+ */
+static int
+ext4_access_path(handle_t *handle, struct inode *inode,
+   struct ext4_ext_path *path)
+{
+   int credits, err;
+
+   /*
+* Check if need to extend journal credits
+* 3 for leaf, sb, and inode plus 2 (bmap and group
+* descriptor) for each block group; assume two block
+* groups
+*/
+   if (handle->h_buffer_credits < 7) {
+   credits = ext4_writepage_trans_blocks(inode);
+   err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+   /* EAGAIN is success */
+   if (err && err != -EAGAIN)
+   return err;
+   }
+
+   err = ext4_ext_get_access(handle, inode, path);
+   return err;
+}
+
+/*
+ * ext4_ext_shift_path_extents:
+ * Shift the extents of a path structure lying between path[depth].p_ext
+ * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
+ * from starting block for each extent.
+ */
+static int
+ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
+   struct inode *inode, handle_t *handle,
+   ext4_lblk_t *start)
+{
+   int depth, err = 0;
+   struct ext4_extent *ex_start, *ex_last;
+   bool update = 0;
+   depth = path->p_depth;
+
+   while (depth >= 0) {
+   if (depth == path->p_depth) {
+   ex_start = path[depth].p_ext;
+   if (!ex_start)
+   return -EIO;
+
+   ex_last = EXT_LAST_EXTENT(path[depth].p_hdr);
+   if (!ex_last)
+   return -EIO;
+
+   err = ext4_access_path(handle, inode, path + depth);
+   if (err)
+   goto out;
+
+   if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
+   update = 1;
+
+   *start = ex_last->ee_block +
+   ext4_ext_get_actual_len(ex_last);
+
+   while (ex_start <= ex_last) {
+   ex_start->ee_block -= shift;
+   ext4_ext_try_to_merge(handle, inode,
+   path, 

[PATCH v3 3/7] ext4: add support FALLOC_FL_COLLAPSE_RANGE for fallocate

2013-09-08 Thread Namjae Jeon
From: Namjae Jeon namjae.j...@samsung.com

Add support FALLOC_FL_COLLAPSE_RANGE for fallocate.

Signed-off-by: Namjae Jeon namjae.j...@samsung.com
Signed-off-by: Ashish Sangwan a.sang...@samsung.com
---
 fs/ext4/ext4.h  |3 +
 fs/ext4/extents.c   |  286 ++-
 fs/ext4/move_extent.c   |2 +-
 include/trace/events/ext4.h |   25 
 4 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f3c43fc..a13e0f4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2737,6 +2737,7 @@ extern int ext4_find_delalloc_cluster(struct inode 
*inode, ext4_lblk_t lblk);
 extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
 extern int ext4_ext_precache(struct inode *inode);
+extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
 
 /* move_extent.c */
 extern void ext4_double_down_write_data_sem(struct inode *first,
@@ -2748,6 +2749,8 @@ void ext4_inode_double_unlock(struct inode *inode1, 
struct inode *inode2);
 extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
 __u64 start_orig, __u64 start_donor,
 __u64 len, __u64 *moved_len);
+extern int mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
+   struct ext4_extent **extent);
 
 /* page-io.c */
 extern int __init ext4_init_pageio(void);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 54d52af..baf1faf 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4580,12 +4580,16 @@ long ext4_fallocate(struct file *file, int mode, loff_t 
offset, loff_t len)
unsigned int credits, blkbits = inode-i_blkbits;
 
/* Return error if mode is not supported */
-   if (mode  ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+   if (mode  ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+FALLOC_FL_COLLAPSE_RANGE))
return -EOPNOTSUPP;
 
if (mode  FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);
 
+   if (mode  FALLOC_FL_COLLAPSE_RANGE)
+   return ext4_collapse_range(inode, offset, len);
+
ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
@@ -4884,3 +4888,283 @@ int ext4_fiemap(struct inode *inode, struct 
fiemap_extent_info *fieinfo,
ext4_es_lru_add(inode);
return error;
 }
+
+/*
+ * ext4_access_path:
+ * Function to access the path buffer for marking it dirty.
+ * It also checks if there are sufficient credits left in the journal handle
+ * to update path.
+ */
+static int
+ext4_access_path(handle_t *handle, struct inode *inode,
+   struct ext4_ext_path *path)
+{
+   int credits, err;
+
+   /*
+* Check if need to extend journal credits
+* 3 for leaf, sb, and inode plus 2 (bmap and group
+* descriptor) for each block group; assume two block
+* groups
+*/
+   if (handle-h_buffer_credits  7) {
+   credits = ext4_writepage_trans_blocks(inode);
+   err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+   /* EAGAIN is success */
+   if (err  err != -EAGAIN)
+   return err;
+   }
+
+   err = ext4_ext_get_access(handle, inode, path);
+   return err;
+}
+
+/*
+ * ext4_ext_shift_path_extents:
+ * Shift the extents of a path structure lying between path[depth].p_ext
+ * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
+ * from starting block for each extent.
+ */
+static int
+ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
+   struct inode *inode, handle_t *handle,
+   ext4_lblk_t *start)
+{
+   int depth, err = 0;
+   struct ext4_extent *ex_start, *ex_last;
+   bool update = 0;
+   depth = path-p_depth;
+
+   while (depth = 0) {
+   if (depth == path-p_depth) {
+   ex_start = path[depth].p_ext;
+   if (!ex_start)
+   return -EIO;
+
+   ex_last = EXT_LAST_EXTENT(path[depth].p_hdr);
+   if (!ex_last)
+   return -EIO;
+
+   err = ext4_access_path(handle, inode, path + depth);
+   if (err)
+   goto out;
+
+   if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
+   update = 1;
+
+   *start = ex_last-ee_block +
+   ext4_ext_get_actual_len(ex_last);
+
+   while (ex_start = ex_last) {
+   ex_start-ee_block -= shift;
+   ext4_ext_try_to_merge(handle, inode,
+   

[PATCH v3 3/7] ext4: add support FALLOC_FL_COLLAPSE_RANGE for fallocate

2013-09-02 Thread Namjae Jeon
From: Namjae Jeon 

Add support FALLOC_FL_COLLAPSE_RANGE for fallocate.

Signed-off-by: Namjae Jeon 
Signed-off-by: Ashish Sangwan 
---
 fs/ext4/ext4.h  |3 +
 fs/ext4/extents.c   |  286 ++-
 fs/ext4/move_extent.c   |2 +-
 include/trace/events/ext4.h |   25 
 4 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f3c43fc..a13e0f4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2737,6 +2737,7 @@ extern int ext4_find_delalloc_cluster(struct inode 
*inode, ext4_lblk_t lblk);
 extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
 extern int ext4_ext_precache(struct inode *inode);
+extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
 
 /* move_extent.c */
 extern void ext4_double_down_write_data_sem(struct inode *first,
@@ -2748,6 +2749,8 @@ void ext4_inode_double_unlock(struct inode *inode1, 
struct inode *inode2);
 extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
 __u64 start_orig, __u64 start_donor,
 __u64 len, __u64 *moved_len);
+extern int mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
+   struct ext4_extent **extent);
 
 /* page-io.c */
 extern int __init ext4_init_pageio(void);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 54d52af..baf1faf 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4580,12 +4580,16 @@ long ext4_fallocate(struct file *file, int mode, loff_t 
offset, loff_t len)
unsigned int credits, blkbits = inode->i_blkbits;
 
/* Return error if mode is not supported */
-   if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+   if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+FALLOC_FL_COLLAPSE_RANGE))
return -EOPNOTSUPP;
 
if (mode & FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);
 
+   if (mode & FALLOC_FL_COLLAPSE_RANGE)
+   return ext4_collapse_range(inode, offset, len);
+
ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
@@ -4884,3 +4888,283 @@ int ext4_fiemap(struct inode *inode, struct 
fiemap_extent_info *fieinfo,
ext4_es_lru_add(inode);
return error;
 }
+
+/*
+ * ext4_access_path:
+ * Function to access the path buffer for marking it dirty.
+ * It also checks if there are sufficient credits left in the journal handle
+ * to update path.
+ */
+static int
+ext4_access_path(handle_t *handle, struct inode *inode,
+   struct ext4_ext_path *path)
+{
+   int credits, err;
+
+   /*
+* Check if need to extend journal credits
+* 3 for leaf, sb, and inode plus 2 (bmap and group
+* descriptor) for each block group; assume two block
+* groups
+*/
+   if (handle->h_buffer_credits < 7) {
+   credits = ext4_writepage_trans_blocks(inode);
+   err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+   /* EAGAIN is success */
+   if (err && err != -EAGAIN)
+   return err;
+   }
+
+   err = ext4_ext_get_access(handle, inode, path);
+   return err;
+}
+
+/*
+ * ext4_ext_shift_path_extents:
+ * Shift the extents of a path structure lying between path[depth].p_ext
+ * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
+ * from starting block for each extent.
+ */
+static int
+ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
+   struct inode *inode, handle_t *handle,
+   ext4_lblk_t *start)
+{
+   int depth, err = 0;
+   struct ext4_extent *ex_start, *ex_last;
+   bool update = 0;
+   depth = path->p_depth;
+
+   while (depth >= 0) {
+   if (depth == path->p_depth) {
+   ex_start = path[depth].p_ext;
+   if (!ex_start)
+   return -EIO;
+
+   ex_last = EXT_LAST_EXTENT(path[depth].p_hdr);
+   if (!ex_last)
+   return -EIO;
+
+   err = ext4_access_path(handle, inode, path + depth);
+   if (err)
+   goto out;
+
+   if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
+   update = 1;
+
+   *start = ex_last->ee_block +
+   ext4_ext_get_actual_len(ex_last);
+
+   while (ex_start <= ex_last) {
+   ex_start->ee_block -= shift;
+   ext4_ext_try_to_merge(handle, inode,
+   path, 

[PATCH v3 3/7] ext4: add support FALLOC_FL_COLLAPSE_RANGE for fallocate

2013-09-02 Thread Namjae Jeon
From: Namjae Jeon namjae.j...@samsung.com

Add support FALLOC_FL_COLLAPSE_RANGE for fallocate.

Signed-off-by: Namjae Jeon namjae.j...@samsung.com
Signed-off-by: Ashish Sangwan a.sang...@samsung.com
---
 fs/ext4/ext4.h  |3 +
 fs/ext4/extents.c   |  286 ++-
 fs/ext4/move_extent.c   |2 +-
 include/trace/events/ext4.h |   25 
 4 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f3c43fc..a13e0f4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2737,6 +2737,7 @@ extern int ext4_find_delalloc_cluster(struct inode 
*inode, ext4_lblk_t lblk);
 extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
 extern int ext4_ext_precache(struct inode *inode);
+extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
 
 /* move_extent.c */
 extern void ext4_double_down_write_data_sem(struct inode *first,
@@ -2748,6 +2749,8 @@ void ext4_inode_double_unlock(struct inode *inode1, 
struct inode *inode2);
 extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
 __u64 start_orig, __u64 start_donor,
 __u64 len, __u64 *moved_len);
+extern int mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
+   struct ext4_extent **extent);
 
 /* page-io.c */
 extern int __init ext4_init_pageio(void);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 54d52af..baf1faf 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4580,12 +4580,16 @@ long ext4_fallocate(struct file *file, int mode, loff_t 
offset, loff_t len)
unsigned int credits, blkbits = inode-i_blkbits;
 
/* Return error if mode is not supported */
-   if (mode  ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+   if (mode  ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
+FALLOC_FL_COLLAPSE_RANGE))
return -EOPNOTSUPP;
 
if (mode  FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);
 
+   if (mode  FALLOC_FL_COLLAPSE_RANGE)
+   return ext4_collapse_range(inode, offset, len);
+
ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
@@ -4884,3 +4888,283 @@ int ext4_fiemap(struct inode *inode, struct 
fiemap_extent_info *fieinfo,
ext4_es_lru_add(inode);
return error;
 }
+
+/*
+ * ext4_access_path:
+ * Function to access the path buffer for marking it dirty.
+ * It also checks if there are sufficient credits left in the journal handle
+ * to update path.
+ */
+static int
+ext4_access_path(handle_t *handle, struct inode *inode,
+   struct ext4_ext_path *path)
+{
+   int credits, err;
+
+   /*
+* Check if need to extend journal credits
+* 3 for leaf, sb, and inode plus 2 (bmap and group
+* descriptor) for each block group; assume two block
+* groups
+*/
+   if (handle-h_buffer_credits  7) {
+   credits = ext4_writepage_trans_blocks(inode);
+   err = ext4_ext_truncate_extend_restart(handle, inode, credits);
+   /* EAGAIN is success */
+   if (err  err != -EAGAIN)
+   return err;
+   }
+
+   err = ext4_ext_get_access(handle, inode, path);
+   return err;
+}
+
+/*
+ * ext4_ext_shift_path_extents:
+ * Shift the extents of a path structure lying between path[depth].p_ext
+ * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
+ * from starting block for each extent.
+ */
+static int
+ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
+   struct inode *inode, handle_t *handle,
+   ext4_lblk_t *start)
+{
+   int depth, err = 0;
+   struct ext4_extent *ex_start, *ex_last;
+   bool update = 0;
+   depth = path-p_depth;
+
+   while (depth = 0) {
+   if (depth == path-p_depth) {
+   ex_start = path[depth].p_ext;
+   if (!ex_start)
+   return -EIO;
+
+   ex_last = EXT_LAST_EXTENT(path[depth].p_hdr);
+   if (!ex_last)
+   return -EIO;
+
+   err = ext4_access_path(handle, inode, path + depth);
+   if (err)
+   goto out;
+
+   if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
+   update = 1;
+
+   *start = ex_last-ee_block +
+   ext4_ext_get_actual_len(ex_last);
+
+   while (ex_start = ex_last) {
+   ex_start-ee_block -= shift;
+   ext4_ext_try_to_merge(handle, inode,
+