[PATCH 078/119] xfs: support bmapping delalloc extents in the CoW fork

Darrick J. Wong
2016-06-17T01:26:21Z

Allow the creation of delayed allocation extents in the CoW fork.
In a subsequent patch we'll wire up write_begin and page_mkwrite to
actually do this.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_bmap.c | 29 +++++++++++++++++-----------
fs/xfs/libxfs/xfs_bmap.h | 2 +-
fs/xfs/xfs_iomap.c | 48 +++++++++++++++++++++++++++++++++++++---------
fs/xfs/xfs_iomap.h | 2 ++
4 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index ccfaf60..18b94e6 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2760,6 +2760,7 @@ done:
STATIC void
xfs_bmap_add_extent_hole_delay(
xfs_inode_t *ip, /* incore inode pointer */
+ int whichfork,
xfs_extnum_t *idx, /* extent number to update/insert */
xfs_bmbt_irec_t *new) /* new data to add to file extents */
{
@@ -2771,8 +2772,10 @@ xfs_bmap_add_extent_hole_delay(
int state; /* state bits, accessed thru macros */
xfs_filblks_t temp=0; /* temp for indirect calculations */

- ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
state = 0;
+ if (whichfork == XFS_COW_FORK)
+ state |= BMAP_COWFORK;
ASSERT(isnullstartblock(new->br_startblock));

/*
@@ -2790,7 +2793,7 @@ xfs_bmap_add_extent_hole_delay(
* Check and set flags if the current (right) segment exists.
* If it doesn't exist, we're converting the hole at end-of-file.
*/
- if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+ if (*idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);

@@ -4140,6 +4143,7 @@ xfs_bmapi_read(
STATIC int
xfs_bmapi_reserve_delalloc(
struct xfs_inode *ip,
+ int whichfork,
xfs_fileoff_t aoff,
xfs_filblks_t len,
struct xfs_bmbt_irec *got,
@@ -4148,7 +4152,7 @@ xfs_bmapi_reserve_delalloc(
int eof)
{
struct xfs_mount *mp = ip->i_mount;
- struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
xfs_extlen_t alen;
xfs_extlen_t indlen;
char rt = XFS_IS_REALTIME_INODE(ip);
@@ -4207,7 +4211,7 @@ xfs_bmapi_reserve_delalloc(
got->br_startblock = nullstartblock(indlen);
got->br_blockcount = alen;
got->br_state = XFS_EXT_NORM;
- xfs_bmap_add_extent_hole_delay(ip, lastx, got);
+ xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);

/*
* Update our extent pointer, given that xfs_bmap_add_extent_hole_delay
@@ -4239,6 +4243,7 @@ out_unreserve_quota:
int
xfs_bmapi_delay(
struct xfs_inode *ip, /* incore inode */
+ int whichfork, /* data or cow fork? */
xfs_fileoff_t bno, /* starting file offs. mapped */
xfs_filblks_t len, /* length to map in file */
struct xfs_bmbt_irec *mval, /* output: map values */
@@ -4246,7 +4251,7 @@ xfs_bmapi_delay(
int flags) /* XFS_BMAPI_... */
{
struct xfs_mount *mp = ip->i_mount;
- struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
struct xfs_bmbt_irec got; /* current file extent record */
struct xfs_bmbt_irec prev; /* previous file extent record */
xfs_fileoff_t obno; /* old block number (offset) */
@@ -4256,14 +4261,15 @@ xfs_bmapi_delay(
int n = 0; /* current extent index */
int error = 0;

+ ASSERT(whichfork == XFS_DATA_FORK || whichfork == XFS_COW_FORK);
ASSERT(*nmap >= 1);
ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
ASSERT(!(flags & ~XFS_BMAPI_ENTIRE));
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

if (unlikely(XFS_TEST_ERROR(
- (XFS_IFORK_FORMAT(ip, XFS_DATA_FORK) != XFS_DINODE_FMT_EXTENTS &&
- XFS_IFORK_FORMAT(ip, XFS_DATA_FORK) != XFS_DINODE_FMT_BTREE),
+ (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
XFS_ERROR_REPORT("xfs_bmapi_delay", XFS_ERRLEVEL_LOW, mp);
return -EFSCORRUPTED;
@@ -4274,19 +4280,20 @@ xfs_bmapi_delay(

XFS_STATS_INC(mp, xs_blk_mapw);

- if (!(ifp->if_flags & XFS_IFEXTENTS)) {
- error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+ if (whichfork == XFS_DATA_FORK && !(ifp->if_flags & XFS_IFEXTENTS)) {
+ error = xfs_iread_extents(NULL, ip, whichfork);
if (error)
return error;
}

- xfs_bmap_search_extents(ip, bno, XFS_DATA_FORK, &eof, &lastx, &got, &prev);
+ xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev);
end = bno + len;
obno = bno;

while (bno < end && n < *nmap) {
if (eof || got.br_startoff > bno) {
- error = xfs_bmapi_reserve_delalloc(ip, bno, len, &got,
+ error = xfs_bmapi_reserve_delalloc(ip, whichfork,
+ bno, len, &got,
&prev, &lastx, eof);
if (error) {
if (n == 0) {
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index a8ef1c6..d90f88e 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -205,7 +205,7 @@ int xfs_bmap_read_extents(struct xfs_trans *tp, struct xfs_inode *ip,
int xfs_bmapi_read(struct xfs_inode *ip, xfs_fileoff_t bno,
xfs_filblks_t len, struct xfs_bmbt_irec *mval,
int *nmap, int flags);
-int xfs_bmapi_delay(struct xfs_inode *ip, xfs_fileoff_t bno,
+int xfs_bmapi_delay(struct xfs_inode *ip, int whichfork, xfs_fileoff_t bno,
xfs_filblks_t len, struct xfs_bmbt_irec *mval,
int *nmap, int flags);
int xfs_bmapi_write(struct xfs_trans *tp, struct xfs_inode *ip,
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 61b61f51..82c4697 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -560,12 +560,13 @@ check_writeio:
return alloc_blocks;
}

-int
-xfs_iomap_write_delay(
+STATIC int
+__xfs_iomap_write_delay(
xfs_inode_t *ip,
xfs_off_t offset,
size_t count,
- xfs_bmbt_irec_t *ret_imap)
+ xfs_bmbt_irec_t *ret_imap,
+ int whichfork)
{
xfs_mount_t *mp = ip->i_mount;
xfs_fileoff_t offset_fsb;
@@ -591,10 +592,14 @@ xfs_iomap_write_delay(
extsz = xfs_get_extsz_hint(ip);
offset_fsb = XFS_B_TO_FSBT(mp, offset);

- error = xfs_iomap_eof_want_preallocate(mp, ip, offset, count,
- imap, XFS_WRITE_IMAPS, &prealloc);
- if (error)
- return error;
+ if (whichfork == XFS_DATA_FORK) {
+ error = xfs_iomap_eof_want_preallocate(mp, ip, offset, count,
+ imap, XFS_WRITE_IMAPS, &prealloc);
+ if (error)
+ return error;
+ } else {
+ prealloc = 0;
+ }

retry:
if (prealloc) {
@@ -626,8 +631,8 @@ retry:
ASSERT(last_fsb > offset_fsb);

nimaps = XFS_WRITE_IMAPS;
- error = xfs_bmapi_delay(ip, offset_fsb, last_fsb - offset_fsb,
- imap, &nimaps, XFS_BMAPI_ENTIRE);
+ error = xfs_bmapi_delay(ip, whichfork, offset_fsb,
+ last_fsb - offset_fsb, imap, &nimaps, XFS_BMAPI_ENTIRE);
switch (error) {
case 0:
case -ENOSPC:
@@ -665,6 +670,31 @@ retry:
return 0;
}

+int
+xfs_iomap_write_delay(
+ xfs_inode_t *ip,
+ xfs_off_t offset,
+ size_t count,
+ xfs_bmbt_irec_t *ret_imap)
+{
+ return __xfs_iomap_write_delay(ip, offset, count, ret_imap,
+ XFS_DATA_FORK);
+}
+
+int
+xfs_iomap_cow_delay(
+ xfs_inode_t *ip,
+ xfs_off_t offset,
+ size_t count,
+ xfs_bmbt_irec_t *ret_imap)
+{
+ ASSERT(XFS_IFORK_PTR(ip, XFS_COW_FORK) != NULL);
+ trace_xfs_iomap_cow_delay(ip, offset, count);
+
+ return __xfs_iomap_write_delay(ip, offset, count, ret_imap,
+ XFS_COW_FORK);
+}
+
/*
* Pass in a delayed allocate extent, convert it to real extents;
* return to the caller the extent we create which maps on top of
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 8688e66..f6a9adf 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -28,5 +28,7 @@ int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t,
int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
struct xfs_bmbt_irec *);
int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
+int xfs_iomap_cow_delay(struct xfs_inode *, xfs_off_t, size_t,
+ struct xfs_bmbt_irec *);

#endif /* __XFS_IOMAP_H__*/