* [PATCH 1/6] xfs: fiemap support for cow fork
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
2016-11-28 19:15 ` Darrick J. Wong
2016-11-28 19:09 ` [PATCH 2/6] xfs: refactor iomap delalloc existing extent search into helper Brian Foster
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
The XFS reflink implementation adds a copy-on-write inode fork to track
newly allocated extents used to replace shared blocks on write. While,
in principle, these extents are tracked by the cow fork temporarily,
fragmentation avoidance mechanisms like the cowextsize hint and COW fork
speculative preallocation allocate additional blocks outside of the
range of the write. This means that blocks in the COW fork can linger
for some time until written to and remapped to the data fork or reaped
by the background cow fork reclaimer.
To facilitate development and debugging, define and wire up a fiemap
flag to query the cow fork extent list of an inode. Note that fiemap
triggers writeback, which means all COW fork extents that are the target
of I/O are remapped to the data fork as part of the query. As a result,
the cow fork fiemap request returns only the blocks that have been
allocated and not yet written to or reclaimed.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_iomap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_iomap.h | 1 +
fs/xfs/xfs_iops.c | 4 ++++
include/uapi/linux/fiemap.h | 1 +
4 files changed, 51 insertions(+)
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 15a83813..4f46f49 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -1159,3 +1159,48 @@ xfs_xattr_iomap_begin(
struct iomap_ops xfs_xattr_iomap_ops = {
.iomap_begin = xfs_xattr_iomap_begin,
};
+
+static int
+xfs_cow_iomap_begin(
+ struct inode *inode,
+ loff_t offset,
+ loff_t length,
+ unsigned flags,
+ struct iomap *iomap)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + length);
+ struct xfs_bmbt_irec imap;
+ int error = 0;
+ int nimaps = 1;
+ unsigned lockmode;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return -EIO;
+
+ lockmode = xfs_ilock_data_map_shared(ip);
+
+ if (!xfs_is_reflink_inode(ip)) {
+ error = -ENOENT;
+ goto out_unlock;
+ }
+
+ error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
+ &nimaps, XFS_BMAPI_ENTIRE | XFS_BMAPI_COWFORK);
+
+out_unlock:
+ xfs_iunlock(ip, lockmode);
+
+ if (!error) {
+ ASSERT(nimaps);
+ xfs_bmbt_to_iomap(ip, iomap, &imap);
+ }
+
+ return error;
+}
+
+struct iomap_ops xfs_cow_iomap_ops = {
+ .iomap_begin = xfs_cow_iomap_begin,
+};
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 6d45cf0..69b62e4 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -35,5 +35,6 @@ xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
extern struct iomap_ops xfs_iomap_ops;
extern struct iomap_ops xfs_xattr_iomap_ops;
+extern struct iomap_ops xfs_cow_iomap_ops;
#endif /* __XFS_IOMAP_H__*/
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 405a65c..517eeed 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1043,6 +1043,10 @@ xfs_vn_fiemap(
fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR;
error = iomap_fiemap(inode, fieinfo, start, length,
&xfs_xattr_iomap_ops);
+ } else if (fieinfo->fi_flags & FIEMAP_FLAG_COW) {
+ fieinfo->fi_flags &= ~FIEMAP_FLAG_COW;
+ error = iomap_fiemap(inode, fieinfo, start, length,
+ &xfs_cow_iomap_ops);
} else {
error = iomap_fiemap(inode, fieinfo, start, length,
&xfs_iomap_ops);
diff --git a/include/uapi/linux/fiemap.h b/include/uapi/linux/fiemap.h
index 0c51d61..7014b4c 100644
--- a/include/uapi/linux/fiemap.h
+++ b/include/uapi/linux/fiemap.h
@@ -41,6 +41,7 @@ struct fiemap {
#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
#define FIEMAP_FLAG_CACHE 0x00000004 /* request caching of the extents */
+#define FIEMAP_FLAG_COW 0x00000010 /* map cow fork extents */
#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] xfs: fiemap support for cow fork
2016-11-28 19:09 ` [PATCH 1/6] xfs: fiemap support for cow fork Brian Foster
@ 2016-11-28 19:15 ` Darrick J. Wong
2016-11-28 19:31 ` Brian Foster
0 siblings, 1 reply; 12+ messages in thread
From: Darrick J. Wong @ 2016-11-28 19:15 UTC (permalink / raw)
To: Brian Foster; +Cc: linux-xfs
On Mon, Nov 28, 2016 at 02:09:36PM -0500, Brian Foster wrote:
> The XFS reflink implementation adds a copy-on-write inode fork to track
> newly allocated extents used to replace shared blocks on write. While,
> in principle, these extents are tracked by the cow fork temporarily,
> fragmentation avoidance mechanisms like the cowextsize hint and COW fork
> speculative preallocation allocate additional blocks outside of the
> range of the write. This means that blocks in the COW fork can linger
> for some time until written to and remapped to the data fork or reaped
> by the background cow fork reclaimer.
>
> To facilitate development and debugging, define and wire up a fiemap
> flag to query the cow fork extent list of an inode. Note that fiemap
> triggers writeback, which means all COW fork extents that are the target
> of I/O are remapped to the data fork as part of the query. As a result,
> the cow fork fiemap request returns only the blocks that have been
> allocated and not yet written to or reclaimed.
I don't think it's a good idea to expose implementation details through
a VFS level API. Since this is only for debugging purposes...
$ xfs_io -c 'bmap -c' /somefile
...should suffice and doesn't have the side effect of flushing dirty
file data.
FWIW, all the other fiemap implementations need to reject
FIEMAP_FLAG_COW if they don't know what that is.
--D
>
> Signed-off-by: Brian Foster <bfoster@redhat.com>
> ---
> fs/xfs/xfs_iomap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> fs/xfs/xfs_iomap.h | 1 +
> fs/xfs/xfs_iops.c | 4 ++++
> include/uapi/linux/fiemap.h | 1 +
> 4 files changed, 51 insertions(+)
>
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 15a83813..4f46f49 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -1159,3 +1159,48 @@ xfs_xattr_iomap_begin(
> struct iomap_ops xfs_xattr_iomap_ops = {
> .iomap_begin = xfs_xattr_iomap_begin,
> };
> +
> +static int
> +xfs_cow_iomap_begin(
> + struct inode *inode,
> + loff_t offset,
> + loff_t length,
> + unsigned flags,
> + struct iomap *iomap)
> +{
> + struct xfs_inode *ip = XFS_I(inode);
> + struct xfs_mount *mp = ip->i_mount;
> + xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
> + xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + length);
> + struct xfs_bmbt_irec imap;
> + int error = 0;
> + int nimaps = 1;
> + unsigned lockmode;
> +
> + if (XFS_FORCED_SHUTDOWN(mp))
> + return -EIO;
> +
> + lockmode = xfs_ilock_data_map_shared(ip);
> +
> + if (!xfs_is_reflink_inode(ip)) {
> + error = -ENOENT;
> + goto out_unlock;
> + }
> +
> + error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
> + &nimaps, XFS_BMAPI_ENTIRE | XFS_BMAPI_COWFORK);
> +
> +out_unlock:
> + xfs_iunlock(ip, lockmode);
> +
> + if (!error) {
> + ASSERT(nimaps);
> + xfs_bmbt_to_iomap(ip, iomap, &imap);
> + }
> +
> + return error;
> +}
> +
> +struct iomap_ops xfs_cow_iomap_ops = {
> + .iomap_begin = xfs_cow_iomap_begin,
> +};
> diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
> index 6d45cf0..69b62e4 100644
> --- a/fs/xfs/xfs_iomap.h
> +++ b/fs/xfs/xfs_iomap.h
> @@ -35,5 +35,6 @@ xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
>
> extern struct iomap_ops xfs_iomap_ops;
> extern struct iomap_ops xfs_xattr_iomap_ops;
> +extern struct iomap_ops xfs_cow_iomap_ops;
>
> #endif /* __XFS_IOMAP_H__*/
> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> index 405a65c..517eeed 100644
> --- a/fs/xfs/xfs_iops.c
> +++ b/fs/xfs/xfs_iops.c
> @@ -1043,6 +1043,10 @@ xfs_vn_fiemap(
> fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR;
> error = iomap_fiemap(inode, fieinfo, start, length,
> &xfs_xattr_iomap_ops);
> + } else if (fieinfo->fi_flags & FIEMAP_FLAG_COW) {
> + fieinfo->fi_flags &= ~FIEMAP_FLAG_COW;
> + error = iomap_fiemap(inode, fieinfo, start, length,
> + &xfs_cow_iomap_ops);
> } else {
> error = iomap_fiemap(inode, fieinfo, start, length,
> &xfs_iomap_ops);
> diff --git a/include/uapi/linux/fiemap.h b/include/uapi/linux/fiemap.h
> index 0c51d61..7014b4c 100644
> --- a/include/uapi/linux/fiemap.h
> +++ b/include/uapi/linux/fiemap.h
> @@ -41,6 +41,7 @@ struct fiemap {
> #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
> #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
> #define FIEMAP_FLAG_CACHE 0x00000004 /* request caching of the extents */
> +#define FIEMAP_FLAG_COW 0x00000010 /* map cow fork extents */
>
> #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
>
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] xfs: fiemap support for cow fork
2016-11-28 19:15 ` Darrick J. Wong
@ 2016-11-28 19:31 ` Brian Foster
2016-11-30 19:30 ` Christoph Hellwig
0 siblings, 1 reply; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:31 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
On Mon, Nov 28, 2016 at 11:15:51AM -0800, Darrick J. Wong wrote:
> On Mon, Nov 28, 2016 at 02:09:36PM -0500, Brian Foster wrote:
> > The XFS reflink implementation adds a copy-on-write inode fork to track
> > newly allocated extents used to replace shared blocks on write. While,
> > in principle, these extents are tracked by the cow fork temporarily,
> > fragmentation avoidance mechanisms like the cowextsize hint and COW fork
> > speculative preallocation allocate additional blocks outside of the
> > range of the write. This means that blocks in the COW fork can linger
> > for some time until written to and remapped to the data fork or reaped
> > by the background cow fork reclaimer.
> >
> > To facilitate development and debugging, define and wire up a fiemap
> > flag to query the cow fork extent list of an inode. Note that fiemap
> > triggers writeback, which means all COW fork extents that are the target
> > of I/O are remapped to the data fork as part of the query. As a result,
> > the cow fork fiemap request returns only the blocks that have been
> > allocated and not yet written to or reclaimed.
>
> I don't think it's a good idea to expose implementation details through
> a VFS level API. Since this is only for debugging purposes...
>
> $ xfs_io -c 'bmap -c' /somefile
>
> ...should suffice and doesn't have the side effect of flushing dirty
> file data.
>
I didn't realize we had a bmap variant available. That suits my needs
just fine, but I do see the following on a quick test against a
reflinked, partially COW'd file:
# xfs_io -c "bmap -c" /mnt/file
xfs_io: xfsctl(XFS_IOC_GETBMAPX) iflags=0x28 ["/mnt/file"]: Invalid
argument
Is this under construction or expected to work as is?
Brian
> FWIW, all the other fiemap implementations need to reject
> FIEMAP_FLAG_COW if they don't know what that is.
>
> --D
>
> >
> > Signed-off-by: Brian Foster <bfoster@redhat.com>
> > ---
> > fs/xfs/xfs_iomap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> > fs/xfs/xfs_iomap.h | 1 +
> > fs/xfs/xfs_iops.c | 4 ++++
> > include/uapi/linux/fiemap.h | 1 +
> > 4 files changed, 51 insertions(+)
> >
> > diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> > index 15a83813..4f46f49 100644
> > --- a/fs/xfs/xfs_iomap.c
> > +++ b/fs/xfs/xfs_iomap.c
> > @@ -1159,3 +1159,48 @@ xfs_xattr_iomap_begin(
> > struct iomap_ops xfs_xattr_iomap_ops = {
> > .iomap_begin = xfs_xattr_iomap_begin,
> > };
> > +
> > +static int
> > +xfs_cow_iomap_begin(
> > + struct inode *inode,
> > + loff_t offset,
> > + loff_t length,
> > + unsigned flags,
> > + struct iomap *iomap)
> > +{
> > + struct xfs_inode *ip = XFS_I(inode);
> > + struct xfs_mount *mp = ip->i_mount;
> > + xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
> > + xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + length);
> > + struct xfs_bmbt_irec imap;
> > + int error = 0;
> > + int nimaps = 1;
> > + unsigned lockmode;
> > +
> > + if (XFS_FORCED_SHUTDOWN(mp))
> > + return -EIO;
> > +
> > + lockmode = xfs_ilock_data_map_shared(ip);
> > +
> > + if (!xfs_is_reflink_inode(ip)) {
> > + error = -ENOENT;
> > + goto out_unlock;
> > + }
> > +
> > + error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
> > + &nimaps, XFS_BMAPI_ENTIRE | XFS_BMAPI_COWFORK);
> > +
> > +out_unlock:
> > + xfs_iunlock(ip, lockmode);
> > +
> > + if (!error) {
> > + ASSERT(nimaps);
> > + xfs_bmbt_to_iomap(ip, iomap, &imap);
> > + }
> > +
> > + return error;
> > +}
> > +
> > +struct iomap_ops xfs_cow_iomap_ops = {
> > + .iomap_begin = xfs_cow_iomap_begin,
> > +};
> > diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
> > index 6d45cf0..69b62e4 100644
> > --- a/fs/xfs/xfs_iomap.h
> > +++ b/fs/xfs/xfs_iomap.h
> > @@ -35,5 +35,6 @@ xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
> >
> > extern struct iomap_ops xfs_iomap_ops;
> > extern struct iomap_ops xfs_xattr_iomap_ops;
> > +extern struct iomap_ops xfs_cow_iomap_ops;
> >
> > #endif /* __XFS_IOMAP_H__*/
> > diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> > index 405a65c..517eeed 100644
> > --- a/fs/xfs/xfs_iops.c
> > +++ b/fs/xfs/xfs_iops.c
> > @@ -1043,6 +1043,10 @@ xfs_vn_fiemap(
> > fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR;
> > error = iomap_fiemap(inode, fieinfo, start, length,
> > &xfs_xattr_iomap_ops);
> > + } else if (fieinfo->fi_flags & FIEMAP_FLAG_COW) {
> > + fieinfo->fi_flags &= ~FIEMAP_FLAG_COW;
> > + error = iomap_fiemap(inode, fieinfo, start, length,
> > + &xfs_cow_iomap_ops);
> > } else {
> > error = iomap_fiemap(inode, fieinfo, start, length,
> > &xfs_iomap_ops);
> > diff --git a/include/uapi/linux/fiemap.h b/include/uapi/linux/fiemap.h
> > index 0c51d61..7014b4c 100644
> > --- a/include/uapi/linux/fiemap.h
> > +++ b/include/uapi/linux/fiemap.h
> > @@ -41,6 +41,7 @@ struct fiemap {
> > #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
> > #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
> > #define FIEMAP_FLAG_CACHE 0x00000004 /* request caching of the extents */
> > +#define FIEMAP_FLAG_COW 0x00000010 /* map cow fork extents */
> >
> > #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
> >
> > --
> > 2.7.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/6] xfs: refactor iomap delalloc existing extent search into helper
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
2016-11-28 19:09 ` [PATCH 1/6] xfs: fiemap support for cow fork Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
2016-11-30 1:15 ` Darrick J. Wong
2016-11-28 19:09 ` [PATCH 3/6] xfs: logically separate iomap range from allocation range Brian Foster
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
In preparation for reuse of the existing delayed allocation code for COW
reservation, factor out the part of xfs_file_iomap_begin_delay()
responsible for existing extent lookup into a new helper. This patch
does not change behavior.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_iomap.c | 72 ++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 57 insertions(+), 15 deletions(-)
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 4f46f49..0a4ef18 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -522,6 +522,48 @@ xfs_iomap_prealloc_size(
return alloc_blocks;
}
+/*
+ * Prepare to handle delayed allocation based on whether extents exist in the
+ * inode data and COW forks. If an existing data extent is shared, determine
+ * whether COW fork reservation is necessary.
+ *
+ * The 'found' parameter indicates whether a writable mapping was found. If the
+ * mapping is shared, a COW reservation is performed for the corresponding
+ * range.
+ */
+static int
+xfs_iomap_search_extents(
+ struct xfs_inode *ip,
+ xfs_fileoff_t offset_fsb,
+ xfs_fileoff_t end_fsb,
+ int *eof,
+ int *idx,
+ struct xfs_bmbt_irec *got,
+ bool *found) /* found usable extent */
+{
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ int error = 0;
+
+ *found = false;
+
+ *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, got);
+ if (*eof || got->br_startoff > offset_fsb)
+ return 0;
+
+ if (xfs_is_reflink_inode(ip)) {
+ bool shared;
+
+ xfs_trim_extent(got, offset_fsb, end_fsb - offset_fsb);
+ error = xfs_reflink_reserve_cow(ip, got, &shared);
+ if (error)
+ return error;
+ }
+
+ *found = true;
+
+ return error;
+}
+
static int
xfs_file_iomap_begin_delay(
struct inode *inode,
@@ -533,18 +575,22 @@ xfs_file_iomap_begin_delay(
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
- xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ xfs_fileoff_t offset_fsb;
+ xfs_fileoff_t end_fsb;
xfs_fileoff_t maxbytes_fsb =
XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
- xfs_fileoff_t end_fsb;
int error = 0, eof = 0;
struct xfs_bmbt_irec got;
xfs_extnum_t idx;
xfs_fsblock_t prealloc_blocks = 0;
+ bool found;
ASSERT(!XFS_IS_REALTIME_INODE(ip));
ASSERT(!xfs_get_extsz_hint(ip));
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
+
xfs_ilock(ip, XFS_ILOCK_EXCL);
if (unlikely(XFS_TEST_ERROR(
@@ -564,19 +610,15 @@ xfs_file_iomap_begin_delay(
goto out_unlock;
}
- eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
- if (!eof && got.br_startoff <= offset_fsb) {
- if (xfs_is_reflink_inode(ip)) {
- bool shared;
-
- end_fsb = min(XFS_B_TO_FSB(mp, offset + count),
- maxbytes_fsb);
- xfs_trim_extent(&got, offset_fsb, end_fsb - offset_fsb);
- error = xfs_reflink_reserve_cow(ip, &got, &shared);
- if (error)
- goto out_unlock;
- }
-
+ /*
+ * Search for preexisting extents. If an existing data extent is shared,
+ * this will perform COW fork reservation.
+ */
+ error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &eof, &idx,
+ &got, &found);
+ if (error)
+ goto out_unlock;
+ if (found) {
trace_xfs_iomap_found(ip, offset, count, 0, &got);
goto done;
}
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/6] xfs: refactor iomap delalloc existing extent search into helper
2016-11-28 19:09 ` [PATCH 2/6] xfs: refactor iomap delalloc existing extent search into helper Brian Foster
@ 2016-11-30 1:15 ` Darrick J. Wong
0 siblings, 0 replies; 12+ messages in thread
From: Darrick J. Wong @ 2016-11-30 1:15 UTC (permalink / raw)
To: Brian Foster; +Cc: linux-xfs
On Mon, Nov 28, 2016 at 02:09:37PM -0500, Brian Foster wrote:
> In preparation for reuse of the existing delayed allocation code for COW
> reservation, factor out the part of xfs_file_iomap_begin_delay()
> responsible for existing extent lookup into a new helper. This patch
> does not change behavior.
>
> Signed-off-by: Brian Foster <bfoster@redhat.com>
> ---
> fs/xfs/xfs_iomap.c | 72 ++++++++++++++++++++++++++++++++++++++++++------------
> 1 file changed, 57 insertions(+), 15 deletions(-)
>
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 4f46f49..0a4ef18 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -522,6 +522,48 @@ xfs_iomap_prealloc_size(
> return alloc_blocks;
> }
>
> +/*
> + * Prepare to handle delayed allocation based on whether extents exist in the
> + * inode data and COW forks. If an existing data extent is shared, determine
> + * whether COW fork reservation is necessary.
> + *
> + * The 'found' parameter indicates whether a writable mapping was found. If the
> + * mapping is shared, a COW reservation is performed for the corresponding
> + * range.
> + */
> +static int
> +xfs_iomap_search_extents(
> + struct xfs_inode *ip,
> + xfs_fileoff_t offset_fsb,
> + xfs_fileoff_t end_fsb,
> + int *eof,
> + int *idx,
> + struct xfs_bmbt_irec *got,
> + bool *found) /* found usable extent */
> +{
> + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
> + int error = 0;
> +
> + *found = false;
> +
> + *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, got);
> + if (*eof || got->br_startoff > offset_fsb)
> + return 0;
> +
> + if (xfs_is_reflink_inode(ip)) {
> + bool shared;
> +
> + xfs_trim_extent(got, offset_fsb, end_fsb - offset_fsb);
> + error = xfs_reflink_reserve_cow(ip, got, &shared);
> + if (error)
> + return error;
> + }
> +
> + *found = true;
> +
> + return error;
> +}
> +
> static int
> xfs_file_iomap_begin_delay(
> struct inode *inode,
> @@ -533,18 +575,22 @@ xfs_file_iomap_begin_delay(
> struct xfs_inode *ip = XFS_I(inode);
> struct xfs_mount *mp = ip->i_mount;
> struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
> - xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
> + xfs_fileoff_t offset_fsb;
> + xfs_fileoff_t end_fsb;
> xfs_fileoff_t maxbytes_fsb =
> XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
> - xfs_fileoff_t end_fsb;
> int error = 0, eof = 0;
> struct xfs_bmbt_irec got;
> xfs_extnum_t idx;
> xfs_fsblock_t prealloc_blocks = 0;
> + bool found;
>
> ASSERT(!XFS_IS_REALTIME_INODE(ip));
> ASSERT(!xfs_get_extsz_hint(ip));
>
> + offset_fsb = XFS_B_TO_FSBT(mp, offset);
> + end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
> +
> xfs_ilock(ip, XFS_ILOCK_EXCL);
>
> if (unlikely(XFS_TEST_ERROR(
> @@ -564,19 +610,15 @@ xfs_file_iomap_begin_delay(
> goto out_unlock;
> }
>
> - eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
> - if (!eof && got.br_startoff <= offset_fsb) {
> - if (xfs_is_reflink_inode(ip)) {
> - bool shared;
> -
> - end_fsb = min(XFS_B_TO_FSB(mp, offset + count),
> - maxbytes_fsb);
> - xfs_trim_extent(&got, offset_fsb, end_fsb - offset_fsb);
> - error = xfs_reflink_reserve_cow(ip, &got, &shared);
> - if (error)
> - goto out_unlock;
> - }
> -
> + /*
> + * Search for preexisting extents. If an existing data extent is shared,
> + * this will perform COW fork reservation.
> + */
> + error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &eof, &idx,
> + &got, &found);
> + if (error)
> + goto out_unlock;
> + if (found) {
Looks ok...
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
--D
> trace_xfs_iomap_found(ip, offset, count, 0, &got);
> goto done;
> }
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/6] xfs: logically separate iomap range from allocation range
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
2016-11-28 19:09 ` [PATCH 1/6] xfs: fiemap support for cow fork Brian Foster
2016-11-28 19:09 ` [PATCH 2/6] xfs: refactor iomap delalloc existing extent search into helper Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
2016-11-30 1:21 ` Darrick J. Wong
2016-11-28 19:09 ` [PATCH 4/6] xfs: reuse iomap delalloc code for COW fork reservation Brian Foster
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
xfs_file_iomap_begin_delay() uses the got variable for the extent
returned by the in-core extent list lookup, the allocated extent
returned by xfs_bmapi_reserve_delalloc() and the extent mapped to the
iomap data structure for the caller.
This is fine for current usage, but doesn't work for COW fork
reservation. In that case, the extent returned by
xfs_bmapi_reserve_delalloc() is the COW reservation and not the extent
to be mapped to the iomap. Instead, the shared data fork extent should
be trimmed appropriately and mapped to the iomap.
To prepare *_begin_delay() to support COW fork reservation, add a new
imap variable to separately track the data fork extent (for iomap) from
the extent returned from the lookup. The latter extent may refer to
either the data or COW fork in the future.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 0a4ef18..0dec431c 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -536,6 +536,7 @@ xfs_iomap_search_extents(
struct xfs_inode *ip,
xfs_fileoff_t offset_fsb,
xfs_fileoff_t end_fsb,
+ struct xfs_bmbt_irec *imap, /* for iomap mapping */
int *eof,
int *idx,
struct xfs_bmbt_irec *got,
@@ -546,15 +547,21 @@ xfs_iomap_search_extents(
*found = false;
- *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, got);
- if (*eof || got->br_startoff > offset_fsb)
+ /*
+ * Look up a preexisting extent directly into imap. Set got for the
+ * bmapi delalloc call if nothing is found.
+ */
+ *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, imap);
+ if (*eof || imap->br_startoff > offset_fsb) {
+ *got = *imap;
return 0;
+ }
if (xfs_is_reflink_inode(ip)) {
bool shared;
- xfs_trim_extent(got, offset_fsb, end_fsb - offset_fsb);
- error = xfs_reflink_reserve_cow(ip, got, &shared);
+ xfs_trim_extent(imap, offset_fsb, end_fsb - offset_fsb);
+ error = xfs_reflink_reserve_cow(ip, imap, &shared);
if (error)
return error;
}
@@ -580,6 +587,7 @@ xfs_file_iomap_begin_delay(
xfs_fileoff_t maxbytes_fsb =
XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
int error = 0, eof = 0;
+ struct xfs_bmbt_irec imap;
struct xfs_bmbt_irec got;
xfs_extnum_t idx;
xfs_fsblock_t prealloc_blocks = 0;
@@ -614,12 +622,12 @@ xfs_file_iomap_begin_delay(
* Search for preexisting extents. If an existing data extent is shared,
* this will perform COW fork reservation.
*/
- error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &eof, &idx,
- &got, &found);
+ error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &imap, &eof,
+ &idx, &got, &found);
if (error)
goto out_unlock;
if (found) {
- trace_xfs_iomap_found(ip, offset, count, 0, &got);
+ trace_xfs_iomap_found(ip, offset, count, 0, &imap);
goto done;
}
@@ -680,17 +688,18 @@ xfs_file_iomap_begin_delay(
}
trace_xfs_iomap_alloc(ip, offset, count, 0, &got);
+ imap = got;
done:
- if (isnullstartblock(got.br_startblock))
- got.br_startblock = DELAYSTARTBLOCK;
+ if (isnullstartblock(imap.br_startblock))
+ imap.br_startblock = DELAYSTARTBLOCK;
- if (!got.br_startblock) {
- error = xfs_alert_fsblock_zero(ip, &got);
+ if (!imap.br_startblock) {
+ error = xfs_alert_fsblock_zero(ip, &imap);
if (error)
goto out_unlock;
}
- xfs_bmbt_to_iomap(ip, iomap, &got);
+ xfs_bmbt_to_iomap(ip, iomap, &imap);
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 3/6] xfs: logically separate iomap range from allocation range
2016-11-28 19:09 ` [PATCH 3/6] xfs: logically separate iomap range from allocation range Brian Foster
@ 2016-11-30 1:21 ` Darrick J. Wong
0 siblings, 0 replies; 12+ messages in thread
From: Darrick J. Wong @ 2016-11-30 1:21 UTC (permalink / raw)
To: Brian Foster; +Cc: linux-xfs
On Mon, Nov 28, 2016 at 02:09:38PM -0500, Brian Foster wrote:
> xfs_file_iomap_begin_delay() uses the got variable for the extent
> returned by the in-core extent list lookup, the allocated extent
> returned by xfs_bmapi_reserve_delalloc() and the extent mapped to the
> iomap data structure for the caller.
>
> This is fine for current usage, but doesn't work for COW fork
> reservation. In that case, the extent returned by
> xfs_bmapi_reserve_delalloc() is the COW reservation and not the extent
> to be mapped to the iomap. Instead, the shared data fork extent should
> be trimmed appropriately and mapped to the iomap.
>
> To prepare *_begin_delay() to support COW fork reservation, add a new
> imap variable to separately track the data fork extent (for iomap) from
> the extent returned from the lookup. The latter extent may refer to
> either the data or COW fork in the future.
>
> Signed-off-by: Brian Foster <bfoster@redhat.com>
> ---
> fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++------------
> 1 file changed, 21 insertions(+), 12 deletions(-)
>
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 0a4ef18..0dec431c 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -536,6 +536,7 @@ xfs_iomap_search_extents(
> struct xfs_inode *ip,
> xfs_fileoff_t offset_fsb,
> xfs_fileoff_t end_fsb,
> + struct xfs_bmbt_irec *imap, /* for iomap mapping */
> int *eof,
> int *idx,
> struct xfs_bmbt_irec *got,
> @@ -546,15 +547,21 @@ xfs_iomap_search_extents(
>
> *found = false;
>
> - *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, got);
> - if (*eof || got->br_startoff > offset_fsb)
> + /*
> + * Look up a preexisting extent directly into imap. Set got for the
> + * bmapi delalloc call if nothing is found.
> + */
> + *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, imap);
> + if (*eof || imap->br_startoff > offset_fsb) {
> + *got = *imap;
> return 0;
> + }
>
> if (xfs_is_reflink_inode(ip)) {
> bool shared;
>
> - xfs_trim_extent(got, offset_fsb, end_fsb - offset_fsb);
> - error = xfs_reflink_reserve_cow(ip, got, &shared);
> + xfs_trim_extent(imap, offset_fsb, end_fsb - offset_fsb);
> + error = xfs_reflink_reserve_cow(ip, imap, &shared);
> if (error)
> return error;
> }
> @@ -580,6 +587,7 @@ xfs_file_iomap_begin_delay(
> xfs_fileoff_t maxbytes_fsb =
> XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
> int error = 0, eof = 0;
> + struct xfs_bmbt_irec imap;
> struct xfs_bmbt_irec got;
> xfs_extnum_t idx;
> xfs_fsblock_t prealloc_blocks = 0;
> @@ -614,12 +622,12 @@ xfs_file_iomap_begin_delay(
> * Search for preexisting extents. If an existing data extent is shared,
> * this will perform COW fork reservation.
> */
> - error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &eof, &idx,
> - &got, &found);
> + error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &imap, &eof,
> + &idx, &got, &found);
> if (error)
> goto out_unlock;
> if (found) {
> - trace_xfs_iomap_found(ip, offset, count, 0, &got);
> + trace_xfs_iomap_found(ip, offset, count, 0, &imap);
> goto done;
> }
>
> @@ -680,17 +688,18 @@ xfs_file_iomap_begin_delay(
> }
>
> trace_xfs_iomap_alloc(ip, offset, count, 0, &got);
> + imap = got;
> done:
> - if (isnullstartblock(got.br_startblock))
> - got.br_startblock = DELAYSTARTBLOCK;
> + if (isnullstartblock(imap.br_startblock))
> + imap.br_startblock = DELAYSTARTBLOCK;
>
> - if (!got.br_startblock) {
> - error = xfs_alert_fsblock_zero(ip, &got);
> + if (!imap.br_startblock) {
> + error = xfs_alert_fsblock_zero(ip, &imap);
> if (error)
> goto out_unlock;
> }
>
> - xfs_bmbt_to_iomap(ip, iomap, &got);
> + xfs_bmbt_to_iomap(ip, iomap, &imap);
>
> out_unlock:
> xfs_iunlock(ip, XFS_ILOCK_EXCL);
Seems fine to me, so
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/6] xfs: reuse iomap delalloc code for COW fork reservation
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
` (2 preceding siblings ...)
2016-11-28 19:09 ` [PATCH 3/6] xfs: logically separate iomap range from allocation range Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
2016-11-28 19:09 ` [PATCH 5/6] xfs: free cowblocks and retry on buffered write ENOSPC Brian Foster
2016-11-28 19:09 ` [PATCH 6/6] xfs: implement basic COW fork speculative preallocation Brian Foster
5 siblings, 0 replies; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
COW fork reservation (delayed allocation) is implemented in
xfs_reflink_reserve_cow() and is generally based on the traditional data
fork delalloc logic in xfs_file_iomap_begin_delay(). In preparation for
further changes to implement more aggressive COW fork preallocation,
refactor the COW reservation code to reuse xfs_file_iomap_begin_delay()
for data fork allocation as well as COW fork reservation. This patch
does not change behavior.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_iomap.c | 74 ++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 55 insertions(+), 19 deletions(-)
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 0dec431c..9072bdb 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -527,9 +527,8 @@ xfs_iomap_prealloc_size(
* inode data and COW forks. If an existing data extent is shared, determine
* whether COW fork reservation is necessary.
*
- * The 'found' parameter indicates whether a writable mapping was found. If the
- * mapping is shared, a COW reservation is performed for the corresponding
- * range.
+ * The 'found' parameter indicates whether a writable mapping was found. If a
+ * shared mapping exists, found is set only if COW reservation exists as well.
*/
static int
xfs_iomap_search_extents(
@@ -540,12 +539,14 @@ xfs_iomap_search_extents(
int *eof,
int *idx,
struct xfs_bmbt_irec *got,
+ bool *shared,
bool *found) /* found usable extent */
{
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
int error = 0;
+ bool trimmed;
- *found = false;
+ *shared = *found = false;
/*
* Look up a preexisting extent directly into imap. Set got for the
@@ -556,17 +557,37 @@ xfs_iomap_search_extents(
*got = *imap;
return 0;
}
+ if (!xfs_is_reflink_inode(ip)) {
+ *found = true;
+ return 0;
+ }
- if (xfs_is_reflink_inode(ip)) {
- bool shared;
-
- xfs_trim_extent(imap, offset_fsb, end_fsb - offset_fsb);
- error = xfs_reflink_reserve_cow(ip, imap, &shared);
- if (error)
- return error;
+ /*
+ * Found a data extent but we don't know if it is shared. If an extent
+ * exists in the cow fork, assume that it is. Use got for this lookup as
+ * imap must retain the data mapping.
+ */
+ xfs_trim_extent(imap, offset_fsb, end_fsb - offset_fsb);
+ ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+ *eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, idx, got);
+ if (!*eof && got->br_startoff <= imap->br_startoff) {
+ trace_xfs_reflink_cow_found(ip, got);
+ xfs_trim_extent(imap, got->br_startoff, got->br_blockcount);
+ *found = true;
+ return 0;
}
- *found = true;
+ /*
+ * There is no existing cow fork extent. We have to explicitly check if
+ * the data extent is shared to determine whether COW fork reservation
+ * is required to map the data extent. Trim the mapping to the next
+ * (un)shared boundary at the same time.
+ */
+ error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed);
+ if (error)
+ return error;
+ if (!*shared)
+ *found = true;
return error;
}
@@ -587,11 +608,13 @@ xfs_file_iomap_begin_delay(
xfs_fileoff_t maxbytes_fsb =
XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
int error = 0, eof = 0;
+ int fork = XFS_DATA_FORK;
struct xfs_bmbt_irec imap;
struct xfs_bmbt_irec got;
xfs_extnum_t idx;
xfs_fsblock_t prealloc_blocks = 0;
bool found;
+ bool shared;
ASSERT(!XFS_IS_REALTIME_INODE(ip));
ASSERT(!xfs_get_extsz_hint(ip));
@@ -620,16 +643,20 @@ xfs_file_iomap_begin_delay(
/*
* Search for preexisting extents. If an existing data extent is shared,
- * this will perform COW fork reservation.
+ * switch to the COW fork for COW reservation.
*/
error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &imap, &eof,
- &idx, &got, &found);
+ &idx, &got, &shared, &found);
if (error)
goto out_unlock;
if (found) {
trace_xfs_iomap_found(ip, offset, count, 0, &imap);
goto done;
}
+ if (shared) {
+ end_fsb = imap.br_startoff + imap.br_blockcount;
+ fork = XFS_COW_FORK;
+ }
error = xfs_qm_dqattach_locked(ip, 0);
if (error)
@@ -645,9 +672,10 @@ xfs_file_iomap_begin_delay(
* the lower level functions are updated.
*/
count = min_t(loff_t, count, 1024 * PAGE_SIZE);
- end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
+ end_fsb = min(end_fsb, XFS_B_TO_FSB(mp, offset + count));
+ xfs_trim_extent(&imap, offset_fsb, end_fsb - offset_fsb);
- if (eof) {
+ if (eof && fork == XFS_DATA_FORK) {
prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count, idx);
if (prealloc_blocks) {
xfs_extlen_t align;
@@ -669,7 +697,7 @@ xfs_file_iomap_begin_delay(
}
retry:
- error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
+ error = xfs_bmapi_reserve_delalloc(ip, fork, offset_fsb,
end_fsb - offset_fsb, prealloc_blocks, &got, &idx, eof);
switch (error) {
case 0:
@@ -687,8 +715,16 @@ xfs_file_iomap_begin_delay(
goto out_unlock;
}
- trace_xfs_iomap_alloc(ip, offset, count, 0, &got);
- imap = got;
+ /*
+ * For the data fork, the iomap mapping is simply the extent that was
+ * returned from the delalloc request. Otherwise, use imap as it refers
+ * to the data fork but is trimmed according to the shared state.
+ */
+ if (fork == XFS_DATA_FORK) {
+ trace_xfs_iomap_alloc(ip, offset, count, 0, &got);
+ imap = got;
+ } else
+ trace_xfs_reflink_cow_alloc(ip, &got);
done:
if (isnullstartblock(imap.br_startblock))
imap.br_startblock = DELAYSTARTBLOCK;
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 5/6] xfs: free cowblocks and retry on buffered write ENOSPC
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
` (3 preceding siblings ...)
2016-11-28 19:09 ` [PATCH 4/6] xfs: reuse iomap delalloc code for COW fork reservation Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
2016-11-28 19:09 ` [PATCH 6/6] xfs: implement basic COW fork speculative preallocation Brian Foster
5 siblings, 0 replies; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
XFS runs an eofblocks reclaim scan before returning an ENOSPC error to
userspace for buffered writes. This facilitates aggressive speculative
preallocation without causing user visible side effects such as
premature ENOSPC.
Run a cowblocks scan in the same situation to reclaim lingering COW fork
preallocation throughout the filesystem.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_file.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index d818c16..7fd97c3 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -716,6 +716,7 @@ xfs_file_buffered_aio_write(
eofb.eof_scan_owner = ip->i_ino; /* for locking */
eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+ xfs_icache_free_cowblocks(ip->i_mount, &eofb);
goto write_retry;
}
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] xfs: implement basic COW fork speculative preallocation
2016-11-28 19:09 [PATCH 0/6] xfs: basic cow fork speculative preallocation Brian Foster
` (4 preceding siblings ...)
2016-11-28 19:09 ` [PATCH 5/6] xfs: free cowblocks and retry on buffered write ENOSPC Brian Foster
@ 2016-11-28 19:09 ` Brian Foster
5 siblings, 0 replies; 12+ messages in thread
From: Brian Foster @ 2016-11-28 19:09 UTC (permalink / raw)
To: linux-xfs
COW fork preallocation is currently limited to the value specified by
the COW extent size hint. This is typically much less aggressive than
traditional data fork speculative preallocation performed when
sufficiently large files are extended.
A file extension based algorithm is not relevant for COW reservation
since, by design, COW reservation never involves extending the size of a
file. That said, we can be more aggressive with COW fork preallocation
given that we support cowblocks inode tagging and reclaim
infrastructure. This provides the ability to reclaim COW fork
preallocation in the background or on demand.
Add a simple COW fork speculative preallocation algorithm. Extend COW
fork reservations due to file writes out to the next data fork extent,
unshared boundary or the next preexisting extent in the COW fork,
whichever limit we hit first. This provides a prealloc algorithm that is
based on the size of preexisting extents, similar to the existing
post-eof speculative preallocation algorithm.
Signed-off-by: Brian Foster <bfoster@redhat.com>
---
fs/xfs/xfs_iomap.c | 139 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 83 insertions(+), 56 deletions(-)
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 9072bdb..b4aa603 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -395,57 +395,84 @@ xfs_iomap_prealloc_size(
struct xfs_inode *ip,
loff_t offset,
loff_t count,
+ int fork,
xfs_extnum_t idx)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
- struct xfs_bmbt_irec prev;
+ struct xfs_bmbt_irec base;
int shift = 0;
int64_t freesp;
xfs_fsblock_t qblocks;
int qshift = 0;
xfs_fsblock_t alloc_blocks = 0;
+ int error = 0;
- if (offset + count <= XFS_ISIZE(ip))
- return 0;
-
- if (!(mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) &&
- (XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_writeio_blocks)))
+ if (fork == XFS_DATA_FORK && offset + count <= XFS_ISIZE(ip))
return 0;
- /*
- * If an explicit allocsize is set, the file is small, or we
- * are writing behind a hole, then use the minimum prealloc:
- */
- if ((mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) ||
- XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
- !xfs_iext_get_extent(ifp, idx - 1, &prev) ||
- prev.br_startoff + prev.br_blockcount < offset_fsb)
+ if (mp->m_flags & XFS_MOUNT_DFLT_IOSIZE)
return mp->m_writeio_blocks;
/*
- * Determine the initial size of the preallocation. We are beyond the
- * current EOF here, but we need to take into account whether this is
- * a sparse write or an extending write when determining the
- * preallocation size. Hence we need to look up the extent that ends
- * at the current write offset and use the result to determine the
- * preallocation size.
- *
- * If the extent is a hole, then preallocation is essentially disabled.
- * Otherwise we take the size of the preceding data extent as the basis
- * for the preallocation size. If the size of the extent is greater than
- * half the maximum extent length, then use the current offset as the
- * basis. This ensures that for large files the preallocation size
- * always extends to MAXEXTLEN rather than falling short due to things
- * like stripe unit/width alignment of real extents.
+ * Determine the initial size of the preallocation depending on which
+ * fork we are in.
*/
- if (prev.br_blockcount <= (MAXEXTLEN >> 1))
- alloc_blocks = prev.br_blockcount << 1;
- else
- alloc_blocks = XFS_B_TO_FSB(mp, offset);
- if (!alloc_blocks)
- goto check_writeio;
+ if (fork == XFS_DATA_FORK) {
+ if (XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_writeio_blocks))
+ return 0;
+
+ /*
+ * Use the minimum prealloc if the file is small or we're
+ * writing behind a hole.
+ */
+ if (XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
+ !xfs_iext_get_extent(ifp, idx - 1, &base) ||
+ base.br_startoff + base.br_blockcount < offset_fsb)
+ return mp->m_writeio_blocks;
+
+ /*
+ * Use the size of the preceding data extent as the basis for
+ * the preallocation size. If the size of the extent is greater
+ * than half the maximum extent length, then use the current
+ * offset as the basis. This ensures that for large files the
+ * preallocation size always extends to MAXEXTLEN rather than
+ * falling short due to things like stripe unit/width alignment
+ * of real extents.
+ */
+ if (base.br_blockcount <= (MAXEXTLEN >> 1))
+ alloc_blocks = base.br_blockcount << 1;
+ else
+ alloc_blocks = XFS_B_TO_FSB(mp, offset);
+ if (!alloc_blocks)
+ goto check_writeio;
+ } else {
+ xfs_extlen_t len;
+ int didx;
+ bool shared, trimmed;
+
+ /* use the data fork extent as the basis for preallocation */
+ shared = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &didx,
+ &base);
+ ASSERT(shared && offset_fsb >= base.br_startoff);
+
+ /*
+ * Truncate the data fork extent to the next unshared boundary.
+ * This defines the maximum COW fork preallocation as we do not
+ * copy-on-write unshared blocks.
+ */
+ len = base.br_blockcount - (offset_fsb - base.br_startoff);
+ xfs_trim_extent(&base, offset_fsb, len);
+ error = xfs_reflink_trim_around_shared(ip, &base, &shared,
+ &trimmed);
+ ASSERT(!error && shared);
+ if (!error)
+ alloc_blocks = base.br_startoff + base.br_blockcount -
+ XFS_B_TO_FSB(mp, offset + count);
+ if (!alloc_blocks)
+ return 0;
+ }
qblocks = alloc_blocks;
/*
@@ -501,7 +528,7 @@ xfs_iomap_prealloc_size(
* rounddown_pow_of_two() returns an undefined result if we pass in
* alloc_blocks = 0.
*/
- if (alloc_blocks)
+ if (alloc_blocks && fork == XFS_DATA_FORK)
alloc_blocks = rounddown_pow_of_two(alloc_blocks);
if (alloc_blocks > MAXEXTLEN)
alloc_blocks = MAXEXTLEN;
@@ -540,13 +567,13 @@ xfs_iomap_search_extents(
int *idx,
struct xfs_bmbt_irec *got,
bool *shared,
+ bool *trimmed,
bool *found) /* found usable extent */
{
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
int error = 0;
- bool trimmed;
- *shared = *found = false;
+ *shared = *trimmed = *found = false;
/*
* Look up a preexisting extent directly into imap. Set got for the
@@ -583,7 +610,7 @@ xfs_iomap_search_extents(
* is required to map the data extent. Trim the mapping to the next
* (un)shared boundary at the same time.
*/
- error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed);
+ error = xfs_reflink_trim_around_shared(ip, imap, shared, trimmed);
if (error)
return error;
if (!*shared)
@@ -614,7 +641,7 @@ xfs_file_iomap_begin_delay(
xfs_extnum_t idx;
xfs_fsblock_t prealloc_blocks = 0;
bool found;
- bool shared;
+ bool shared, trimmed;
ASSERT(!XFS_IS_REALTIME_INODE(ip));
ASSERT(!xfs_get_extsz_hint(ip));
@@ -646,7 +673,7 @@ xfs_file_iomap_begin_delay(
* switch to the COW fork for COW reservation.
*/
error = xfs_iomap_search_extents(ip, offset_fsb, end_fsb, &imap, &eof,
- &idx, &got, &shared, &found);
+ &idx, &got, &shared, &trimmed, &found);
if (error)
goto out_unlock;
if (found) {
@@ -675,25 +702,25 @@ xfs_file_iomap_begin_delay(
end_fsb = min(end_fsb, XFS_B_TO_FSB(mp, offset + count));
xfs_trim_extent(&imap, offset_fsb, end_fsb - offset_fsb);
- if (eof && fork == XFS_DATA_FORK) {
- prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count, idx);
- if (prealloc_blocks) {
- xfs_extlen_t align;
- xfs_off_t end_offset;
- xfs_fileoff_t p_end_fsb;
+ if ((fork == XFS_DATA_FORK && eof) ||
+ (fork == XFS_COW_FORK && !trimmed))
+ prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count,
+ fork, idx);
+ if (prealloc_blocks) {
+ xfs_extlen_t align;
+ xfs_off_t end_offset;
+ xfs_fileoff_t p_end_fsb;
- end_offset = XFS_WRITEIO_ALIGN(mp, offset + count - 1);
- p_end_fsb = XFS_B_TO_FSBT(mp, end_offset) +
- prealloc_blocks;
+ end_offset = XFS_WRITEIO_ALIGN(mp, offset + count - 1);
+ p_end_fsb = XFS_B_TO_FSBT(mp, end_offset) + prealloc_blocks;
- align = xfs_eof_alignment(ip, 0);
- if (align)
- p_end_fsb = roundup_64(p_end_fsb, align);
+ align = xfs_eof_alignment(ip, 0);
+ if (align)
+ p_end_fsb = roundup_64(p_end_fsb, align);
- p_end_fsb = min(p_end_fsb, maxbytes_fsb);
- ASSERT(p_end_fsb > offset_fsb);
- prealloc_blocks = p_end_fsb - end_fsb;
- }
+ p_end_fsb = min(p_end_fsb, maxbytes_fsb);
+ ASSERT(p_end_fsb > offset_fsb);
+ prealloc_blocks = p_end_fsb - end_fsb;
}
retry:
--
2.7.4
^ permalink raw reply related [flat|nested] 12+ messages in thread