[PATCH V3 10/12] xfs: Extend per-inode extent counter widths

Chandan Babu R
2021-09-16T10:07:56Z

This commit adds a new 64-bit per-inode data extent counter. However the
maximum number of extents that a data fork can hold is limited to 2^48
extents. This feature is available only when XFS_SB_FEAT_INCOMPAT_NREXT64
feature bit is enabled on the filesystem. Also, enabling this feature bit
causes attr fork extent counter to use the 32-bit extent counter that was
previously used to hold the data fork extent counter. This implies that the
attr fork can now occupy a maximum of 2^32 extents.

Signed-off-by: Chandan Babu R <chandan.babu@oracle.com>
---
fs/xfs/libxfs/xfs_bmap.c | 8 ++-
fs/xfs/libxfs/xfs_format.h | 87 ++++++++++++++++++++++-----------
fs/xfs/libxfs/xfs_fs.h | 1 +
fs/xfs/libxfs/xfs_ialloc.c | 2 +
fs/xfs/libxfs/xfs_inode_buf.c | 25 +++++++++-
fs/xfs/libxfs/xfs_inode_fork.h | 18 +++++--
fs/xfs/libxfs/xfs_log_format.h | 3 +-
fs/xfs/libxfs/xfs_sb.c | 4 ++
fs/xfs/libxfs/xfs_trans_inode.c | 6 +++
fs/xfs/scrub/inode_repair.c | 11 ++++-
fs/xfs/xfs_inode.c | 2 +-
fs/xfs/xfs_inode.h | 5 ++
fs/xfs/xfs_inode_item.c | 21 +++++++-
fs/xfs/xfs_inode_item_recover.c | 26 +++++++---
fs/xfs/xfs_mount.h | 2 +
15 files changed, 171 insertions(+), 50 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 1a716067901f..a77cf8619ec0 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -55,18 +55,16 @@ xfs_bmap_compute_maxlevels(
int whichfork) /* data or attr fork */
{
xfs_extnum_t maxleafents; /* max leaf entries possible */
+ xfs_rfsblock_t maxblocks; /* max blocks at this level */
int level; /* btree level */
- uint maxblocks; /* max blocks at this level */
int maxrootrecs; /* max records in root block */
int minleafrecs; /* min records in leaf block */
int minnoderecs; /* min records in node block */
int sz; /* root block size */

/*
- * The maximum number of extents in a file, hence the maximum number of
- * leaf entries, is controlled by the size of the on-disk extent count,
- * either a signed 32-bit number for the data fork, or a signed 16-bit
- * number for the attr fork.
+ * The maximum number of extents in a fork, hence the maximum number of
+ * leaf entries, is controlled by the size of the on-disk extent count.
*
* Note that we can no longer assume that if we are in ATTR1 that the
* fork offset of all the inodes will be
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 87c927d912f6..7373ac8b890d 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -388,6 +388,7 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
#define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 5) /* metadata dir tree */
+#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 6) /* 64-bit data fork extent counter */
#define XFS_SB_FEAT_INCOMPAT_ALL \
(XFS_SB_FEAT_INCOMPAT_FTYPE| \
XFS_SB_FEAT_INCOMPAT_SPINODES| \
@@ -802,6 +803,16 @@ typedef struct xfs_dinode {
__be64 di_size; /* number of bytes in file */
__be64 di_nblocks; /* # of direct & btree blocks used */
__be32 di_extsize; /* basic/minimum extent size for file */
+
+ /*
+ * On a extcnt64bit filesystem, di_nextents64 holds the data fork
+ * extent count, di_nextents32 holds the attr fork extent count,
+ * and di_nextents16 must be zero.
+ *
+ * Otherwise, di_nextents32 holds the data fork extent count,
+ * di_nextents16 holds the attr fork extent count, and di_nextents64
+ * must be zero.
+ */
__be32 di_nextents32; /* number of extents in data fork */
__be16 di_nextents16; /* number of extents in attribute fork*/
__u8 di_forkoff; /* attr fork offs, <<3 for 64b align */
@@ -820,7 +831,8 @@ typedef struct xfs_dinode {
__be64 di_lsn; /* flush sequence */
__be64 di_flags2; /* more random flags */
__be32 di_cowextsize; /* basic cow extent size for file */
- __u8 di_pad2[12]; /* more padding for future expansion */
+ __u8 di_pad2[4]; /* more padding for future expansion */
+ __be64 di_nextents64;

/* fields only written to during inode creation */
xfs_timestamp_t di_crtime; /* time created */
@@ -876,6 +888,8 @@ enum xfs_dinode_fmt {
* Max values for extlen and disk inode's extent counters.
*/
#define MAXEXTLEN ((xfs_extlen_t)0x1fffff) /* 21 bits */
+#define XFS_IFORK_EXTCNT_MAXU48 ((xfs_extnum_t)0xffffffffffff) /* Unsigned 48-bits */
+#define XFS_IFORK_EXTCNT_MAXU32 ((xfs_aextnum_t)0xffffffff) /* Unsigned 32-bits */
#define XFS_IFORK_EXTCNT_MAXS32 ((xfs_extnum_t)0x7fffffff) /* Signed 32-bits */
#define XFS_IFORK_EXTCNT_MAXS16 ((xfs_aextnum_t)0x7fff) /* Signed 16-bits */

@@ -931,32 +945,6 @@ enum xfs_dinode_fmt {
(dip)->di_format : \
(dip)->di_aformat)

-static inline int
-xfs_dfork_nextents(
- struct xfs_dinode *dip,
- int whichfork,
- xfs_extnum_t *nextents)
-{
- int error = 0;
-
- switch (whichfork) {
- case XFS_DATA_FORK:
- *nextents = be32_to_cpu(dip->di_nextents32);
- break;
-
- case XFS_ATTR_FORK:
- *nextents = be16_to_cpu(dip->di_nextents16);
- break;
-
- default:
- ASSERT(0);
- error = -EFSCORRUPTED;
- break;
- }
-
- return error;
-}
-
/*
* For block and character special files the 32bit dev_t is stored at the
* beginning of the data fork.
@@ -1023,6 +1011,7 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
#define XFS_DIFLAG2_COWEXTSIZE_BIT 2 /* copy on write extent size hint */
#define XFS_DIFLAG2_BIGTIME_BIT 3 /* big timestamps */
#define XFS_DIFLAG2_METADATA_BIT 4 /* filesystem metadata */
+#define XFS_DIFLAG2_NREXT64_BIT 5 /* 64-bit extent counter enabled */

#define XFS_DIFLAG2_DAX (1 << XFS_DIFLAG2_DAX_BIT)
#define XFS_DIFLAG2_REFLINK (1 << XFS_DIFLAG2_REFLINK_BIT)
@@ -1053,10 +1042,12 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
* - Metadata directory entries must have correct ftype.
*/
#define XFS_DIFLAG2_METADATA (1 << XFS_DIFLAG2_METADATA_BIT)
+#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT)
+

#define XFS_DIFLAG2_ANY \
(XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \
- XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_METADATA)
+ XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_METADATA | XFS_DIFLAG2_NREXT64)

static inline bool xfs_dinode_has_bigtime(const struct xfs_dinode *dip)
{
@@ -1064,6 +1055,46 @@ static inline bool xfs_dinode_has_bigtime(const struct xfs_dinode *dip)
(dip->di_flags2 & cpu_to_be64(XFS_DIFLAG2_BIGTIME));
}

+static inline bool xfs_dinode_has_nrext64(const struct xfs_dinode *dip)
+{
+ return dip->di_version >= 3 &&
+ (dip->di_flags2 & cpu_to_be64(XFS_DIFLAG2_NREXT64));
+}
+
+static inline int
+xfs_dfork_nextents(
+ struct xfs_dinode *dip,
+ int whichfork,
+ xfs_extnum_t *nextents)
+{
+ int error = 0;
+ bool inode_has_nrext64;
+
+ inode_has_nrext64 = xfs_dinode_has_nrext64(dip);
+
+ if (inode_has_nrext64 && dip->di_nextents16 != 0)
+ return -EFSCORRUPTED;
+
+ switch (whichfork) {
+ case XFS_DATA_FORK:
+ *nextents = inode_has_nrext64 ? be64_to_cpu(dip->di_nextents64) :
+ be32_to_cpu(dip->di_nextents32);
+ break;
+
+ case XFS_ATTR_FORK:
+ *nextents = inode_has_nrext64 ? be32_to_cpu(dip->di_nextents32) :
+ be16_to_cpu(dip->di_nextents16);
+ break;
+
+ default:
+ ASSERT(0);
+ error = -EFSCORRUPTED;
+ break;
+ }
+
+ return error;
+}
+
/*
* Inode number format:
* low inopblog bits - offset in block
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index b76906914d89..3d0b679d96d7 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -254,6 +254,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_ATOMIC_SWAP (1 << 23) /* atomic swapext */
#define XFS_FSOP_GEOM_FLAGS_METADIR (1 << 24) /* metadata directories */
+#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 25) /* 64-bit extent counter */

/*
* Minimum and maximum sizes need for growth checks.
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 77119ea7d1ce..585743208392 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2836,6 +2836,8 @@ xfs_ialloc_setup_geometry(
igeo->new_diflags2 = 0;
if (xfs_has_bigtime(mp))
igeo->new_diflags2 |= XFS_DIFLAG2_BIGTIME;
+ if (xfs_has_nrext64(mp))
+ igeo->new_diflags2 |= XFS_DIFLAG2_NREXT64;

/* Compute inode btree geometry. */
igeo->agino_log = sbp->sb_inopblog + sbp->sb_agblklog;
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 882ed4873afe..0ab332c913c4 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -285,6 +285,27 @@ xfs_inode_to_disk_ts(
return ts;
}

+static inline void
+xfs_inode_to_disk_iext_counters(
+ struct xfs_inode *ip,
+ struct xfs_dinode *to)
+{
+ if (xfs_inode_has_nrext64(ip)) {
+ to->di_nextents64 = cpu_to_be64(xfs_ifork_nextents(&ip->i_df));
+ to->di_nextents32 = cpu_to_be32(xfs_ifork_nextents(ip->i_afp));
+ /*
+ * We might be upgrading the inode to use wider extent counters
+ * than was previously used. Hence zero the unused field.
+ */
+ to->di_nextents16 = cpu_to_be16(0);
+ } else {
+ if (xfs_has_v3inodes(ip->i_mount))
+ to->di_nextents64 = 0;
+ to->di_nextents32 = cpu_to_be32(xfs_ifork_nextents(&ip->i_df));
+ to->di_nextents16 = cpu_to_be16(xfs_ifork_nextents(ip->i_afp));
+ }
+}
+
void
xfs_inode_to_disk(
struct xfs_inode *ip,
@@ -313,8 +334,6 @@ xfs_inode_to_disk(
to->di_size = cpu_to_be64(ip->i_disk_size);
to->di_nblocks = cpu_to_be64(ip->i_nblocks);
to->di_extsize = cpu_to_be32(ip->i_extsize);
- to->di_nextents32 = cpu_to_be32(xfs_ifork_nextents(&ip->i_df));
- to->di_nextents16 = cpu_to_be16(xfs_ifork_nextents(ip->i_afp));
to->di_forkoff = ip->i_forkoff;
to->di_aformat = xfs_ifork_format(ip->i_afp);
to->di_flags = cpu_to_be16(ip->i_diflags);
@@ -334,6 +353,8 @@ xfs_inode_to_disk(
to->di_version = 2;
to->di_flushiter = cpu_to_be16(ip->i_flushiter);
}
+
+ xfs_inode_to_disk_iext_counters(ip, to);
}

static xfs_failaddr_t
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index 4b9df10e8eea..f8a85ba6e9e9 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -136,10 +136,22 @@ static inline int8_t xfs_ifork_format(struct xfs_ifork *ifp)
static inline xfs_extnum_t xfs_iext_max_nextents(struct xfs_mount *mp,
int whichfork)
{
- if (whichfork == XFS_DATA_FORK || whichfork == XFS_COW_FORK)
- return XFS_IFORK_EXTCNT_MAXS32;
+ bool has_64bit_extcnt = xfs_has_nrext64(mp);

- return XFS_IFORK_EXTCNT_MAXS16;
+ switch (whichfork) {
+ case XFS_DATA_FORK:
+ case XFS_COW_FORK:
+ return has_64bit_extcnt ? XFS_IFORK_EXTCNT_MAXU48
+ : XFS_IFORK_EXTCNT_MAXS32;
+
+ case XFS_ATTR_FORK:
+ return has_64bit_extcnt ? XFS_IFORK_EXTCNT_MAXU32
+ : XFS_IFORK_EXTCNT_MAXS16;
+
+ default:
+ ASSERT(0);
+ return 0;
+ }
}

struct xfs_ifork *xfs_ifork_alloc(enum xfs_dinode_fmt format,
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 9f352ff4352b..de4bcb94c732 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -429,7 +429,8 @@ struct xfs_log_dinode {

uint64_t di_flags2; /* more random flags */
uint32_t di_cowextsize; /* basic cow extent size for file */
- uint8_t di_pad2[12]; /* more padding for future expansion */
+ uint8_t di_pad2[4]; /* more padding for future expansion */
+ uint64_t di_nextents64; /* higher part of data fork extent count */

/* fields only written to during inode creation */
xfs_log_timestamp_t di_crtime; /* time created */
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index ffff91081036..a6b84893ebda 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -126,6 +126,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NEEDSREPAIR;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)
features |= XFS_FEAT_METADIR;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_NREXT64)
+ features |= XFS_FEAT_NREXT64;

if (sbp->sb_features_log_incompat & XFS_SB_FEAT_INCOMPAT_LOG_ATOMIC_SWAP)
features |= XFS_FEAT_ATOMIC_SWAP;
@@ -1175,6 +1177,8 @@ xfs_fs_geometry(
geo->flags |= XFS_FSOP_GEOM_FLAGS_ATOMIC_SWAP;
if (xfs_has_metadir(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_METADIR;
+ if (xfs_has_nrext64(mp))
+ geo->flags |= XFS_FSOP_GEOM_FLAGS_NREXT64;
geo->rtsectsize = sbp->sb_blocksize;
geo->dirblocksize = xfs_dir2_dirblock_bytes(sbp);

diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c
index 6a3a869635bf..ac622097243a 100644
--- a/fs/xfs/libxfs/xfs_trans_inode.c
+++ b/fs/xfs/libxfs/xfs_trans_inode.c
@@ -144,6 +144,12 @@ xfs_trans_log_inode(
flags |= XFS_ILOG_CORE;
}

+ if ((flags & XFS_ILOG_CORE) &&
+ xfs_has_nrext64(ip->i_mount) &&
+ !xfs_inode_has_nrext64(ip)) {
+ ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
+ }
+
/*
* Inode verifiers do not check that the extent size hint is an integer
* multiple of the rt extent size on a directory with both rtinherit
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 133109d84b98..995bad2cedd6 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -740,7 +740,10 @@ xrep_dinode_zap_dfork(
{
trace_xrep_dinode_zap_dfork(sc, dip);

- dip->di_nextents32 = 0;
+ if (xfs_dinode_has_nrext64(dip))
+ dip->di_nextents64 = 0;
+ else
+ dip->di_nextents32 = 0;

/* Special files always get reset to DEV */
switch (mode & S_IFMT) {
@@ -827,7 +830,11 @@ xrep_dinode_zap_afork(
trace_xrep_dinode_zap_afork(sc, dip);

dip->di_aformat = XFS_DINODE_FMT_EXTENTS;
- dip->di_nextents16 = 0;
+
+ if (xfs_dinode_has_nrext64(dip))
+ dip->di_nextents32 = 0;
+ else
+ dip->di_nextents16 = 0;

dip->di_forkoff = 0;
dip->di_mode = cpu_to_be16(mode & ~0777);
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 6338a93b975c..3c969803e671 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2539,7 +2539,7 @@ xfs_iflush(
ip->i_nblocks, mp, XFS_ERRTAG_IFLUSH_5)) {
xfs_alert_tag(mp, XFS_PTAG_IFLUSH,
"%s: detected corrupt incore inode %llu, "
- "total extents = %llu nblocks = %lld, ptr "PTR_FMT,
+ "total extents = %llu, nblocks = %lld, ptr "PTR_FMT,
__func__, ip->i_ino,
ip->i_df.if_nextents + xfs_ifork_nextents(ip->i_afp),
ip->i_nblocks, ip);
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index b0114c8cef76..348b1dbe42c0 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -230,6 +230,11 @@ static inline bool xfs_inode_has_bigrtextents(struct xfs_inode *ip)
return XFS_IS_REALTIME_INODE(ip) && ip->i_mount->m_sb.sb_rextsize > 1;
}

+static inline bool xfs_inode_has_nrext64(struct xfs_inode *ip)
+{
+ return ip->i_diflags2 & XFS_DIFLAG2_NREXT64;
+}
+
/*
* Return the buftarg used for data allocations on a given inode.
*/
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index e4800a965670..5c318aaecff4 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -358,6 +358,23 @@ xfs_copy_dm_fields_to_log_dinode(
}
}

+static inline void
+xfs_inode_to_log_dinode_iext_counters(
+ struct xfs_inode *ip,
+ struct xfs_log_dinode *to)
+{
+ if (xfs_inode_has_nrext64(ip)) {
+ to->di_nextents64 = xfs_ifork_nextents(&ip->i_df);
+ to->di_nextents32 = xfs_ifork_nextents(ip->i_afp);
+ to->di_nextents16 = 0;
+ } else {
+ if (xfs_has_v3inodes(ip->i_mount))
+ to->di_nextents64 = 0;
+ to->di_nextents32 = xfs_ifork_nextents(&ip->i_df);
+ to->di_nextents16 = xfs_ifork_nextents(ip->i_afp);
+ }
+}
+
static void
xfs_inode_to_log_dinode(
struct xfs_inode *ip,
@@ -385,8 +402,6 @@ xfs_inode_to_log_dinode(
to->di_size = ip->i_disk_size;
to->di_nblocks = ip->i_nblocks;
to->di_extsize = ip->i_extsize;
- to->di_nextents32 = xfs_ifork_nextents(&ip->i_df);
- to->di_nextents16 = xfs_ifork_nextents(ip->i_afp);
to->di_forkoff = ip->i_forkoff;
to->di_aformat = xfs_ifork_format(ip->i_afp);
to->di_flags = ip->i_diflags;
@@ -411,6 +426,8 @@ xfs_inode_to_log_dinode(
to->di_version = 2;
to->di_flushiter = ip->i_flushiter;
}
+
+ xfs_inode_to_log_dinode_iext_counters(ip, to);
}

/*
diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
index c21fb3d2ddca..980d6615f6f2 100644
--- a/fs/xfs/xfs_inode_item_recover.c
+++ b/fs/xfs/xfs_inode_item_recover.c
@@ -167,8 +167,6 @@ xfs_log_dinode_to_disk(
to->di_size = cpu_to_be64(from->di_size);
to->di_nblocks = cpu_to_be64(from->di_nblocks);
to->di_extsize = cpu_to_be32(from->di_extsize);
- to->di_nextents32 = cpu_to_be32(from->di_nextents32);
- to->di_nextents16 = cpu_to_be16(from->di_nextents16);
to->di_forkoff = from->di_forkoff;
to->di_aformat = from->di_aformat;
to->di_dmevmask = cpu_to_be32(from->di_dmevmask);
@@ -182,12 +180,17 @@ xfs_log_dinode_to_disk(
from->di_crtime);
to->di_flags2 = cpu_to_be64(from->di_flags2);
to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
+ to->di_nextents64 = cpu_to_be64(from->di_nextents64);
+ to->di_nextents32 = cpu_to_be32(from->di_nextents32);
+ to->di_nextents16 = cpu_to_be16(from->di_nextents16);
to->di_ino = cpu_to_be64(from->di_ino);
to->di_lsn = cpu_to_be64(lsn);
memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
uuid_copy(&to->di_uuid, &from->di_uuid);
to->di_flushiter = 0;
} else {
+ to->di_nextents32 = cpu_to_be32(from->di_nextents32);
+ to->di_nextents16 = cpu_to_be16(from->di_nextents16);
to->di_flushiter = cpu_to_be16(from->di_flushiter);
}
}
@@ -203,6 +206,8 @@ xlog_recover_inode_commit_pass2(
struct xfs_mount *mp = log->l_mp;
struct xfs_buf *bp;
struct xfs_dinode *dip;
+ xfs_extnum_t nextents;
+ xfs_aextnum_t anextents;
int len;
char *src;
char *dest;
@@ -342,16 +347,25 @@ xlog_recover_inode_commit_pass2(
goto out_release;
}
}
- if (unlikely(ldip->di_nextents32 + ldip->di_nextents16 > ldip->di_nblocks)) {
+
+ if (xfs_has_v3inodes(mp) &&
+ ldip->di_flags2 & XFS_DIFLAG2_NREXT64) {
+ nextents = ldip->di_nextents64;
+ anextents = ldip->di_nextents32;
+ } else {
+ nextents = ldip->di_nextents32;
+ anextents = ldip->di_nextents16;
+ }
+
+ if (unlikely(nextents + anextents > ldip->di_nblocks)) {
XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
XFS_ERRLEVEL_LOW, mp, ldip,
sizeof(*ldip));
xfs_alert(mp,
"%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
- "dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
+ "dino bp "PTR_FMT", ino %Ld, total extents = %llu, nblocks = %Ld",
__func__, item, dip, bp, in_f->ilf_ino,
- ldip->di_nextents32 + ldip->di_nextents16,
- ldip->di_nblocks);
+ nextents + anextents, ldip->di_nblocks);
error = -EFSCORRUPTED;
goto out_release;
}
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index a4c149670476..f558d5c4a5f1 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -288,6 +288,7 @@ typedef struct xfs_mount {
#define XFS_FEAT_NEEDSREPAIR (1ULL << 25) /* needs xfs_repair */
#define XFS_FEAT_ATOMIC_SWAP (1ULL << 26) /* extent swap log items */
#define XFS_FEAT_METADIR (1ULL << 27) /* metadata directory tree */
+#define XFS_FEAT_NREXT64 (1ULL << 28) /* 64-bit inode extent counters */

/* Mount features */
#define XFS_FEAT_NOATTR2 (1ULL << 48) /* disable attr2 creation */
@@ -370,6 +371,7 @@ __XFS_HAS_FEAT(bigtime, BIGTIME)
__XFS_HAS_FEAT(needsrepair, NEEDSREPAIR)
__XFS_LOG_FEAT(atomicswap, ATOMIC_SWAP)
__XFS_HAS_FEAT(metadir, METADIR)
+__XFS_HAS_FEAT(nrext64, NREXT64)

/*
* Decide if this filesystem can use log-assisted ("atomic") extent swapping.
--
2.30.2