@@ -105,7 +105,7 @@ struct xfs_ifork;
typedef struct xfs_sb {
uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
uint32_t sb_blocksize; /* logical block size, bytes */
- xfs_rfsblock_t sb_dblocks; /* number of data blocks */
+ xfs_rfsblock_t sb_dblocks; /* number of data blocks in device LBA */
xfs_rfsblock_t sb_rblocks; /* number of realtime blocks */
xfs_rtblock_t sb_rextents; /* number of realtime extents */
uuid_t sb_uuid; /* user-visible file system unique id */
@@ -184,6 +184,8 @@ typedef struct xfs_sb {
xfs_lsn_t sb_lsn; /* last write sequence */
uuid_t sb_meta_uuid; /* metadata file system unique id */
+ uint64_t sb_usable_dblocks; /* usable space limit */
+
/* must be padded to 64 bit alignment */
} xfs_sb_t;
@@ -196,7 +198,7 @@ typedef struct xfs_sb {
typedef struct xfs_dsb {
__be32 sb_magicnum; /* magic number == XFS_SB_MAGIC */
__be32 sb_blocksize; /* logical block size, bytes */
- __be64 sb_dblocks; /* number of data blocks */
+ __be64 sb_dblocks; /* number of data blocks in device LBA */
__be64 sb_rblocks; /* number of realtime blocks */
__be64 sb_rextents; /* number of realtime extents */
uuid_t sb_uuid; /* user-visible file system unique id */
@@ -271,6 +273,8 @@ typedef struct xfs_dsb {
__be64 sb_lsn; /* last write sequence */
uuid_t sb_meta_uuid; /* metadata file system unique id */
+ uint64_t sb_usable_dblocks; /* usable space limit */
+
/* must be padded to 64 bit alignment */
} xfs_dsb_t;
@@ -478,10 +482,12 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
+#define XFS_SB_FEAT_INCOMPAT_THINSPACE (1 << 3) /* usable space limited */
#define XFS_SB_FEAT_INCOMPAT_ALL \
- (XFS_SB_FEAT_INCOMPAT_FTYPE| \
- XFS_SB_FEAT_INCOMPAT_SPINODES| \
- XFS_SB_FEAT_INCOMPAT_META_UUID)
+ (XFS_SB_FEAT_INCOMPAT_FTYPE | \
+ XFS_SB_FEAT_INCOMPAT_SPINODES | \
+ XFS_SB_FEAT_INCOMPAT_META_UUID | \
+ XFS_SB_FEAT_INCOMPAT_THINSPACE)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
@@ -559,6 +565,12 @@ static inline bool xfs_sb_version_hasreflink(struct xfs_sb *sbp)
(sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_REFLINK);
}
+static inline bool xfs_sb_version_hasthinspace(struct xfs_sb *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+ (sbp->sb_features_ro_compat & XFS_SB_FEAT_INCOMPAT_THINSPACE);
+}
+
/*
* end of superblock version macros
*/
@@ -211,7 +211,8 @@ struct xfs_fsop_geom {
__u32 rtsectsize; /* realtime sector size, bytes */
__u32 dirblocksize; /* directory block size, bytes */
__u32 logsunit; /* log stripe unit, bytes */
- __u64 pad[16]; /* expansion space */
+ __u64 usable_dblocks; /* usable space limit, fsblocks */
+ __u64 pad[15]; /* expansion space */
};
/* Output for XFS_FS_COUNTS */
@@ -252,6 +253,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_SPINODES (1 << 18) /* sparse inode chunks */
#define XFS_FSOP_GEOM_FLAGS_RMAPBT (1 << 19) /* reverse mapping btree */
#define XFS_FSOP_GEOM_FLAGS_REFLINK (1 << 20) /* files can share blocks */
+#define XFS_FSOP_GEOM_FLAGS_THINSPACE (1 << 21) /* space limited fs */
/*
* Minimum and maximum sizes need for growth checks.
@@ -268,6 +268,14 @@ xfs_mount_validate_sb(
return -EFSCORRUPTED;
}
+ if (xfs_sb_version_hasthinspace(sbp) &&
+ (sbp->sb_usable_dblocks > sbp->sb_dblocks ||
+ sbp->sb_usable_dblocks < sbp->sb_fdblocks ||
+ sbp->sb_usable_dblocks < XFS_MIN_DBLOCKS(sbp))) {
+ xfs_notice(mp, "Thinspace SB sanity check failed");
+ return -EFSCORRUPTED;
+ }
+
/*
* Until this is fixed only page-sized or smaller data blocks work.
*/
@@ -417,11 +425,13 @@ __xfs_sb_from_disk(
to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat);
to->sb_features_log_incompat =
be32_to_cpu(from->sb_features_log_incompat);
+
/* crc is only used on disk, not in memory; just init to 0 here. */
to->sb_crc = 0;
to->sb_spino_align = be32_to_cpu(from->sb_spino_align);
to->sb_pquotino = be64_to_cpu(from->sb_pquotino);
to->sb_lsn = be64_to_cpu(from->sb_lsn);
+
/*
* sb_meta_uuid is only on disk if it differs from sb_uuid and the
* feature flag is set; if not set we keep it only in memory.
@@ -430,9 +440,20 @@ __xfs_sb_from_disk(
uuid_copy(&to->sb_meta_uuid, &from->sb_meta_uuid);
else
uuid_copy(&to->sb_meta_uuid, &from->sb_uuid);
+
/* Convert on-disk flags to in-memory flags? */
if (convert_xquota)
xfs_sb_quota_from_disk(to);
+
+ /*
+ * Everything in memory relies on sb_usable_dblocks having a correct
+ * value. For non-thin filesystems, this is not set on disk but is
+ * simply the size of the device, so use it instead.
+ */
+ if (xfs_sb_version_hasthinspace(to))
+ to->sb_usable_dblocks = be64_to_cpu(from->sb_usable_dblocks);
+ else
+ to->sb_usable_dblocks = to->sb_dblocks;
}
void
@@ -561,19 +582,24 @@ xfs_sb_to_disk(
to->sb_features2 = cpu_to_be32(from->sb_features2);
to->sb_bad_features2 = cpu_to_be32(from->sb_bad_features2);
- if (xfs_sb_version_hascrc(from)) {
- to->sb_features_compat = cpu_to_be32(from->sb_features_compat);
- to->sb_features_ro_compat =
- cpu_to_be32(from->sb_features_ro_compat);
- to->sb_features_incompat =
- cpu_to_be32(from->sb_features_incompat);
- to->sb_features_log_incompat =
+ if (!xfs_sb_version_hascrc(from))
+ return;
+
+ /*
+ * V5+ fields only after this point.
+ */
+ to->sb_features_compat = cpu_to_be32(from->sb_features_compat);
+ to->sb_features_ro_compat = cpu_to_be32(from->sb_features_ro_compat);
+ to->sb_features_incompat = cpu_to_be32(from->sb_features_incompat);
+ to->sb_features_log_incompat =
cpu_to_be32(from->sb_features_log_incompat);
- to->sb_spino_align = cpu_to_be32(from->sb_spino_align);
- to->sb_lsn = cpu_to_be64(from->sb_lsn);
- if (xfs_sb_version_hasmetauuid(from))
- uuid_copy(&to->sb_meta_uuid, &from->sb_meta_uuid);
- }
+ to->sb_spino_align = cpu_to_be32(from->sb_spino_align);
+ to->sb_lsn = cpu_to_be64(from->sb_lsn);
+
+ if (xfs_sb_version_hasmetauuid(from))
+ uuid_copy(&to->sb_meta_uuid, &from->sb_meta_uuid);
+ if (xfs_sb_version_hasthinspace(from))
+ to->sb_usable_dblocks = cpu_to_be64(from->sb_usable_dblocks);
}
static int
@@ -732,7 +758,7 @@ xfs_sb_mount_common(
* Set up the filesystem size and addressing limits
*/
mp->m_LBA_size = sbp->sb_dblocks;
- mp->m_usable_blocks = sbp->sb_dblocks;
+ mp->m_usable_blocks = sbp->sb_usable_dblocks;
mp->m_alloc_mxr[0] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 1);
mp->m_alloc_mxr[1] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 0);
@@ -824,6 +850,16 @@ xfs_initialize_perag_data(
sbp->sb_ifree = ifree;
sbp->sb_icount = ialloc;
sbp->sb_fdblocks = bfree + bfreelst + btree;
+
+ /*
+ * The aggregate free space from the AGs does not take into account the
+ * difference between the address space size and the maximum usable
+ * space we have configured for thinspace filesystems. Take that into
+ * account now.
+ */
+ if (xfs_sb_version_hasthinspace(sbp))
+ sbp->sb_fdblocks -= sbp->sb_dblocks - sbp->sb_usable_dblocks;
+
spin_unlock(&mp->m_sb_lock);
xfs_reinit_percpu_counters(mp);
@@ -124,8 +124,13 @@ xfs_fs_geometry(
geo->logsunit = mp->m_sb.sb_logsunit;
}
- if (new_version >= 5)
+ if (new_version >= 5) {
geo->version = XFS_FSOP_GEOM_VERSION_V5;
+ geo->flags |=
+ (xfs_sb_version_hasthinspace(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_THINSPACE : 0);
+ geo->usable_dblocks = mp->m_sb.sb_usable_dblocks;
+ }
return 0;
}
@@ -45,7 +45,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_dinode, 176);
XFS_CHECK_STRUCT_SIZE(struct xfs_disk_dquot, 104);
XFS_CHECK_STRUCT_SIZE(struct xfs_dqblk, 136);
- XFS_CHECK_STRUCT_SIZE(struct xfs_dsb, 264);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_dsb, 272);
XFS_CHECK_STRUCT_SIZE(struct xfs_dsymlink_hdr, 56);
XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_key, 4);
XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_rec, 16);