[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1345615545-26133-9-git-send-email-wenqing.lz@taobao.com>
Date: Wed, 22 Aug 2012 14:05:45 +0800
From: Zheng Liu <gnehzuil.liu@...il.com>
To: linux-ext4@...r.kernel.org, linux-fsdevel@...r.kernel.org
Cc: Hugh Dickins <hughd@...gle.com>, Jie Liu <jeff.liu@...cle.com>,
Zheng Liu <wenqing.lz@...bao.com>
Subject: [RFC][PATCH 8/8 v2] ext4: introduce lseek SEEK_DATA/SEEK_HOLE support
From: Zheng Liu <wenqing.lz@...bao.com>
This patch makes ext4 really support SEEK_DATA/SEEK_HOLE flags. Extent-based
and block-based files are implemented together because ext4_map_blocks hides
this difference.
CC: Hugh Dickins <hughd@...gle.com>
CC: Jie Liu <jeff.liu@...cle.com>
Signed-off-by: Zheng Liu <wenqing.lz@...bao.com>
---
[I notice that Jeff has an implementation of SEEK_DATA/SEEK_HOLE for ext4. So
Cc' to Jeff]
[Cc' to Hugh because tmpfs's folks are thinking about whether add this feature
or not]
fs/ext4/ext4.h | 1 +
fs/ext4/file.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/ext4/indirect.c | 1 -
3 files changed, 152 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f1d5087..149851b 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2398,6 +2398,7 @@ extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map, int flags);
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
+
/* move_extent.c */
extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
__u64 start_orig, __u64 start_donor,
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 3b0e3bd..cfdf774 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -285,6 +285,145 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
return dquot_file_open(inode, filp);
}
+static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
+{
+ struct inode *inode = file->f_mapping->host;
+ struct ext4_map_blocks map;
+ struct extent_status es;
+ ext4_lblk_t start_block, end_block, len, delblk;
+ loff_t dataoff, isize;
+ int blkbits;
+ int ret = 0;
+
+ mutex_lock(&inode->i_mutex);
+
+ blkbits = inode->i_sb->s_blocksize_bits;
+ isize = i_size_read(inode);
+ start_block = offset >> blkbits;
+ end_block = isize >> blkbits;
+ len = isize - offset + 1;
+
+ do {
+ map.m_lblk = start_block;
+ map.m_len = len >> blkbits;
+
+ ret = ext4_map_blocks(NULL, inode, &map, 0);
+
+ if (ret > 0) {
+ dataoff = start_block << blkbits;
+ break;
+ } else {
+ /* search in extent status */
+ es.start = start_block;
+ down_read(&EXT4_I(inode)->i_data_sem);
+ delblk = ext4_es_find_extent(inode, &es);
+ up_read(&EXT4_I(inode)->i_data_sem);
+
+ if (start_block >= es.start &&
+ start_block < es.start + es.len) {
+ dataoff = start_block << blkbits;
+ break;
+ }
+
+ start_block++;
+
+ /*
+ * currently hole punching doesn't change the size of
+ * file. So after hole punching, maybe there has a
+ * hole at the end of file.
+ */
+ if (start_block > end_block) {
+ dataoff = -ENXIO;
+ break;
+ }
+ }
+ } while (1);
+
+ mutex_unlock(&inode->i_mutex);
+ return dataoff;
+}
+
+static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
+{
+ struct inode *inode = file->f_mapping->host;
+ struct ext4_map_blocks map;
+ struct extent_status es;
+ ext4_lblk_t start_block, end_block, len, delblk;
+ loff_t holeoff, isize;
+ int blkbits;
+ int ret = 0;
+
+ mutex_lock(&inode->i_mutex);
+
+ blkbits = inode->i_sb->s_blocksize_bits;
+ isize = i_size_read(inode);
+ start_block = offset >> blkbits;
+ end_block = isize >> blkbits;
+ len = isize - offset + 1;
+
+ do {
+ map.m_lblk = start_block;
+ map.m_len = len >> blkbits;
+
+ ret = ext4_map_blocks(NULL, inode, &map, 0);
+
+ if (ret > 0) {
+ /* skip this extent */
+ start_block += ret;
+ if (start_block > end_block) {
+ holeoff = isize;
+ break;
+ }
+ } else {
+ /* search in extent status */
+ es.start = start_block;
+ down_read(&EXT4_I(inode)->i_data_sem);
+ delblk = ext4_es_find_extent(inode, &es);
+ up_read(&EXT4_I(inode)->i_data_sem);
+
+ if (start_block >= es.start &&
+ start_block < es.start + es.len) {
+ /* skip this delay extent */
+ start_block = es.start + es.len;
+ continue;
+ }
+
+ holeoff = start_block << blkbits;
+ break;
+ }
+ } while (1);
+
+ mutex_unlock(&inode->i_mutex);
+ return holeoff;
+}
+
+static loff_t ext4_seek_data_hole(struct file *file, loff_t offset,
+ int origin, loff_t maxsize)
+{
+ struct inode *inode = file->f_mapping->host;
+
+ BUG_ON((origin != SEEK_DATA) && (origin != SEEK_HOLE));
+
+ if (offset >= i_size_read(inode))
+ return -ENXIO;
+
+ if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
+ return -EINVAL;
+ if (offset > maxsize)
+ return -EINVAL;
+
+ switch (origin) {
+ case SEEK_DATA:
+ return ext4_seek_data(file, offset, maxsize);
+ case SEEK_HOLE:
+ return ext4_seek_hole(file, offset, maxsize);
+ default:
+ ext4_error(inode->i_sb, "Unknown origin");
+ }
+
+ return -EINVAL;
+}
+
/*
* ext4_llseek() handles both block-mapped and extent-mapped maxbytes values
* by calling generic_file_llseek_size() with the appropriate maxbytes
@@ -300,8 +439,18 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int origin)
else
maxbytes = inode->i_sb->s_maxbytes;
- return generic_file_llseek_size(file, offset, origin,
- maxbytes, i_size_read(inode));
+ switch (origin) {
+ case SEEK_END:
+ case SEEK_CUR:
+ case SEEK_SET:
+ return generic_file_llseek_size(file, offset, origin,
+ maxbytes, i_size_read(inode));
+ case SEEK_DATA:
+ case SEEK_HOLE:
+ return ext4_seek_data_hole(file, offset, origin, maxbytes);
+ }
+
+ return -EINVAL;
}
const struct file_operations ext4_file_operations = {
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 9db67b1..9917c42 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -1502,4 +1502,3 @@ out_stop:
ext4_journal_stop(handle);
trace_ext4_truncate_exit(inode);
}
-
--
1.7.4.1
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists