Hello,
Sorry for the delay! I have worked on another high priority task these days.
So the below patch is the first try to add SEEK_DATA/SEEK_HOLE to XFS, I have
tested it through seek_test.c from Sunil, looks all works to me.
http://oss.oracle.com/~smushran/seek_data/seek_test.c
Any feedback are welcome, thank you!
Signed-off-by: Jie Liu <jeff.liu@xxxxxxxxxx>
---
fs/xfs/xfs_bmap.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_bmap.h | 1 +
fs/xfs/xfs_file.c | 51 ++++++++++++++++++++++++++++++++-
fs/xfs/xfs_iops.c | 64 +++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_iops.h | 3 ++
5 files changed, 199 insertions(+), 1 deletions(-)
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index c68baeb..04c3930 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -6133,3 +6133,84 @@ next_block:
return error;
}
+
+
+int
+xfs_seek_data_hole(
+ struct xfs_inode *ip,
+ loff_t *start,
+ u32 type)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_fileoff_t seekoff = *start;
+ xfs_fileoff_t filelen;
+ xfs_extnum_t lastx;
+ xfs_ifork_t *ifp;
+ struct xfs_bmbt_irec got;
+ struct xfs_bmbt_irec prev;
+ u64 extoff;
+ u64 extlen;
+ int eof;
+
+ if (xfs_get_extsz_hint(ip) ||
+ ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
+ filelen = XFS_MAXIOFFSET(mp);
+ } else {
+ filelen = ip->i_size;
+ }
+
+ ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ xfs_bmap_search_extents(ip, XFS_BB_TO_FSBT(mp, BTOBB(*start)),
+ XFS_DATA_FORK, &eof, &lastx, &got, &prev);
+
+ if (type == SEEK_DATA) {
+ extoff = BBTOB(XFS_FSB_TO_BB(mp, got.br_startoff));
+ extlen = BBTOB(XFS_FSB_TO_BB(mp, got.br_blockcount));
+
+ if (eof) {
+ if (seekoff < extoff + extlen)
+ *start = seekoff;
+ else {
+ /*
+ * There is no more data region past the
+ * supplied offset.
+ */
+ return XFS_ERROR(ENXIO);
+ }
+ }
+
+ *start = seekoff < extoff ? extoff : seekoff;
+ } else {
+ for (;;) {
+ extoff = BBTOB(XFS_FSB_TO_BB(mp, got.br_startoff));
+ extlen = BBTOB(XFS_FSB_TO_BB(mp, got.br_blockcount));
+ if (eof) {
+ /*
+ * There might be a hole at the end of the
+ * file, adjust to the file size.
+ */
+ if (seekoff >= extoff) {
+ *start = min_t(xfs_fileoff_t, filelen,
+ (extoff + extlen));
+ }
+ break;
+ }
+
+ /* A hole found */
+ if (seekoff < extoff) {
+ *start = seekoff;
+ break;
+ }
+
+ /* Fetch the next extent */
+ seekoff = extoff + extlen;
+ if (++lastx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx),
+ &got);
+ else
+ eof = 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index 89ee672..964e29b 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -196,6 +196,7 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
xfs_extnum_t num);
uint xfs_default_attroffset(struct xfs_inode *ip);
+int xfs_seek_data_hole(struct xfs_inode *ip, loff_t *start, u32 type);
#ifdef __KERNEL__
/* bmap to userspace formatter - copy to user & advance pointer */
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 753ed9b..bf5471b 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -1141,8 +1141,57 @@ xfs_vm_page_mkwrite(
return block_page_mkwrite(vma, vmf, xfs_get_blocks);
}
+STATIC loff_t
+xfs_file_llseek(
+ struct file *file,
+ loff_t offset,
+ int origin)
+{
+ struct inode *inode = file->f_mapping->host;
+ int ret;
+
+ if (origin != SEEK_DATA && origin != SEEK_HOLE)
+ return generic_file_llseek(file, offset, origin);
+
+ mutex_lock(&inode->i_mutex);
+ switch (origin) {
+ case SEEK_DATA:
+ case SEEK_HOLE:
+ if (offset >= i_size_read(inode)) {
+ ret = -ENXIO;
+ goto error;
+ }
+
+ ret = xfs_find_desired_extent(inode, &offset, origin);
+ if (ret)
+ goto error;
+ }
+
+ if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (offset > inode->i_sb->s_maxbytes) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_version = 0;
+ }
+
+ mutex_unlock(&inode->i_mutex);
+ return offset;
+
+error:
+ mutex_unlock(&inode->i_mutex);
+ return ret;
+}
+
const struct file_operations xfs_file_operations = {
- .llseek = generic_file_llseek,
+ .llseek = xfs_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = xfs_file_aio_read,
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 23ce927..482c1ff 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1030,6 +1030,70 @@ xfs_vn_fiemap(
return 0;
}
+int
+xfs_find_desired_extent(
+ struct inode *inode,
+ loff_t *start,
+ u32 type)
+{
+ xfs_inode_t *ip = XFS_I(inode);
+ xfs_mount_t *mp = ip->i_mount;
+ struct xfs_ifork *ifp;
+ int lock;
+ int error;
+
+ if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
+ ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
+ return XFS_ERROR(EINVAL);
+
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+ /*
+ * Flush the delay alloc blocks. Even after flushing the inode,
+ * there can still be delalloc blocks on the inode beyond EOF
+ * due to speculative preallocation. These are not removed until
+ * the release function is called or the inode is inactivated.
+ * Hence we cannot assert here that ip->i_delayed_blks == 0.
+ */
+ if (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size) {
+ error = xfs_flush_pages(ip, 0, -1, 0, FI_REMAPF);
+ if (error)
+ goto out_unlock_iolock;
+ }
+
+ lock = xfs_ilock_map_shared(ip);
+
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ error = EIO;
+ goto out_unlock_ilock;
+ }
+
+ XFS_STATS_INC(xs_blk_mapr);
+ ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, XFS_DATA_FORK) / (uint)sizeof(xfs_bmbt_rec_t));
+
+ if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+ error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+ if (error)
+ goto out_unlock_ilock;
+ }
+
+ error = xfs_seek_data_hole(ip, start, type);
+
+out_unlock_ilock:
+ xfs_iunlock_map_shared(ip, lock);
+out_unlock_iolock:
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+
+ if (error)
+ return -error;
+
+ return 0;
+}
+
static const struct inode_operations xfs_inode_operations = {
.get_acl = xfs_get_acl,
.getattr = xfs_vn_getattr,
diff --git a/fs/xfs/xfs_iops.h b/fs/xfs/xfs_iops.h
index ef41c92..ea47abd 100644
--- a/fs/xfs/xfs_iops.h
+++ b/fs/xfs/xfs_iops.h
@@ -27,4 +27,7 @@ extern ssize_t xfs_vn_listxattr(struct dentry *, char *data,
size_t size);
extern void xfs_setup_inode(struct xfs_inode *);
+extern int xfs_find_desired_extent(struct inode *inode, loff_t *start,
+ u32 type);
+
#endif /* __XFS_IOPS_H__ */
--
1.7.4.1
|