If the reflink mount option is specified we'll add the reflink btree root block to each AG late in the mount process and can then use the reflink tree. Signed-off-by: Christoph Hellwig <hch@xxxxxx> --- fs/xfs/libxfs/xfs_refcount_btree.c | 83 ++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_refcount_btree.h | 2 + fs/xfs/libxfs/xfs_trans_resv.c | 23 +++++++++++ fs/xfs/libxfs/xfs_trans_resv.h | 1 + fs/xfs/xfs_mount.c | 24 +++++++++-- fs/xfs/xfs_mount.h | 5 ++- fs/xfs/xfs_super.c | 7 +++- 7 files changed, 139 insertions(+), 6 deletions(-) diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index 7ae3ad7..aaea8da 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -484,3 +484,86 @@ xfs_refcountbt_calc_reserves( return error; } + +static int +xfs_reflink_ag_add( + struct xfs_trans *tp, + xfs_agnumber_t agno) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_perag *pag; + struct xfs_buf *bp, *agbp; + struct xfs_agf *agf; + __be32 bno; + int stat, error; + + error = __xfs_refcountbt_alloc_block(tp, agno, XFS_AG_RESV_NONE, + &bno, &stat); + if (error) + return error; + + bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, + XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(bno)), + mp->m_bsize, 0); + if (!bp) + return -ENOMEM; + + bp->b_ops = &xfs_refcountbt_buf_ops; + xfs_btree_init_block(mp, bp, XFS_REFC_CRC_MAGIC, 0, 0, agno, + XFS_BTREE_CRC_BLOCKS); + xfs_trans_buf_set_type(tp, bp, XFS_BLFT_BTREE_BUF); + xfs_trans_log_buf(tp, bp, 0, XFS_BTREE_SBLOCK_CRC_LEN - 1); + + error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp); + if (error) + return error; + + agf = XFS_BUF_TO_AGF(agbp); + agf->agf_refcount_root = bno; + agf->agf_refcount_level = cpu_to_be32(1); + + pag = xfs_perag_get(mp, agno); + pag->pagf_refcount_level = 1; + xfs_perag_put(pag); + + xfs_alloc_log_agf(tp, agbp, + XFS_AGF_REFCOUNT_ROOT | XFS_AGF_REFCOUNT_LEVEL); + return 0; +} + +int +xfs_reflink_add( + struct xfs_mount *mp) +{ + struct xfs_trans *tp = NULL; + int error, i; + + tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE); + tp->t_flags |= XFS_TRANS_RESERVE; + error = xfs_trans_reserve(tp, &M_RES(mp)->tr_add_reflink, + mp->m_sb.sb_agcount, 0); + if (error) + goto out_trans_cancel; + + for (i = 0; i < mp->m_sb.sb_agcount; i++) { + xfs_log_sb(tp); + + error = xfs_reflink_ag_add(tp, i); + if (error) + goto out_trans_cancel; + + error = xfs_trans_roll(&tp, NULL); + if (error) + goto out_trans_cancel; + } + + mp->m_sb.sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK; + xfs_log_sb(tp); + + return xfs_trans_commit(tp); + +out_trans_cancel: + if (tp) + xfs_trans_cancel(tp); + return error; +} diff --git a/fs/xfs/libxfs/xfs_refcount_btree.h b/fs/xfs/libxfs/xfs_refcount_btree.h index 6f4bf70..481e0d0 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.h +++ b/fs/xfs/libxfs/xfs_refcount_btree.h @@ -70,4 +70,6 @@ extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp); extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used); +extern int xfs_reflink_add(struct xfs_mount *mp); + #endif /* __XFS_REFCOUNT_BTREE_H__ */ diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index d53fe89..346c0c1 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -802,6 +802,25 @@ xfs_calc_sb_reservation( return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize); } +/* + * For adding the reflinking we need to allocate the new root block + * and modify the AGF and SB, giving: + * the AGF: sectorsize + * the superblock for the reflink flag: sector size + * the reflink root block itself: sector size + * the allocation btrees: 2 trees * (max depth - 1) * block size + */ +STATIC uint +xfs_calc_add_reflink_resv_alloc( + struct xfs_mount *mp) +{ + return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) + + mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 1), + XFS_FSB_TO_B(mp, 1)); +} + void xfs_trans_resv_calc( struct xfs_mount *mp, @@ -885,6 +904,10 @@ xfs_trans_resv_calc( resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT; resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES; + resp->tr_add_reflink.tr_logres = xfs_calc_add_reflink_resv_alloc(mp); + resp->tr_add_reflink.tr_logcount = XFS_DEFAULT_LOG_COUNT; + resp->tr_add_reflink.tr_logflags = XFS_TRANS_PERM_LOG_RES; + /* * The following transactions are logged in logical format with * a default log count. diff --git a/fs/xfs/libxfs/xfs_trans_resv.h b/fs/xfs/libxfs/xfs_trans_resv.h index cf734cf..1c007da 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.h +++ b/fs/xfs/libxfs/xfs_trans_resv.h @@ -60,6 +60,7 @@ struct xfs_trans_resv { struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */ struct xfs_trans_res tr_qm_quotaoff; /* turn quota off */ struct xfs_trans_res tr_qm_equotaoff;/* end of turn quota off */ + struct xfs_trans_res tr_add_reflink; /* add reflink */ struct xfs_trans_res tr_sb; /* modify superblock */ struct xfs_trans_res tr_fsyncts; /* update timestamps on fsync */ }; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 792f547..6e4cebf 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -962,11 +962,27 @@ xfs_mountfs( xfs_fs_reserve_ag_blocks(mp); - /* Recover any CoW blocks that never got remapped. */ - error = xfs_reflink_recover_cow(mp); - if (error && !XFS_FORCED_SHUTDOWN(mp)) - xfs_err(mp, + if (!xfs_sb_version_hasreflink(&mp->m_sb) && + (mp->m_flags & XFS_MOUNT_REFLINK)) { + if (XFS_SB_VERSION_NUM(&mp->m_sb) < XFS_SB_VERSION_5) { + xfs_warn(mp, + "Can't enable reflinks on version %d superblock.", + XFS_SB_VERSION_NUM(&mp->m_sb)); + return -EINVAL; + } + + error = xfs_reflink_add(mp); + if (error) { + xfs_err(mp, + "Failed to enable reflinks: %d\n", error); + } + } else { + /* Recover any CoW blocks that never got remapped. */ + error = xfs_reflink_recover_cow(mp); + if (error && !XFS_FORCED_SHUTDOWN(mp)) + xfs_err(mp, "Error %d recovering leftover CoW allocations.", error); + } } return 0; diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index c2e1294..e3002cb 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -178,6 +178,8 @@ typedef struct xfs_mount { #define XFS_MOUNT_WSYNC (1ULL << 0) /* for nfs - all metadata ops must be synchronous except for space allocations */ +#define XFS_MOUNT_REFLINK (1ULL << 1) /* allow use of reflinks (will + be permanent once used) */ #define XFS_MOUNT_WAS_CLEAN (1ULL << 3) #define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem operations, typically for @@ -229,7 +231,8 @@ typedef struct xfs_mount { static inline bool xfs_mp_hasreflink(struct xfs_mount *mp) { - return xfs_sb_version_hasreflink(&mp->m_sb); + return xfs_sb_version_hasreflink(&mp->m_sb) || + (mp->m_flags & XFS_MOUNT_REFLINK); } /* diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 84348af..dfa5077 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -82,7 +82,7 @@ enum { Opt_quota, Opt_noquota, Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_uquota, Opt_gquota, Opt_pquota, Opt_uqnoenforce, Opt_gqnoenforce, Opt_pqnoenforce, Opt_qnoenforce, - Opt_discard, Opt_nodiscard, Opt_dax, Opt_err, + Opt_discard, Opt_nodiscard, Opt_reflink, Opt_dax, Opt_err, }; static const match_table_t tokens = { @@ -132,6 +132,7 @@ static const match_table_t tokens = { {Opt_qnoenforce, "qnoenforce"}, /* same as uqnoenforce */ {Opt_discard, "discard"}, /* Discard unused blocks */ {Opt_nodiscard, "nodiscard"}, /* Do not discard unused blocks */ + {Opt_reflink, "reflink"}, /* Do not discard unused blocks */ {Opt_dax, "dax"}, /* Enable direct access to bdev pages */ {Opt_err, NULL}, @@ -219,6 +220,7 @@ xfs_parseargs( */ mp->m_flags |= XFS_MOUNT_BARRIER; mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE; + mp->m_flags |= XFS_MOUNT_REFLINK; /* * These can be overridden by the mount option parsing. @@ -368,6 +370,9 @@ xfs_parseargs( case Opt_nodiscard: mp->m_flags &= ~XFS_MOUNT_DISCARD; break; + case Opt_reflink: + mp->m_flags |= XFS_MOUNT_REFLINK; + break; #ifdef CONFIG_FS_DAX case Opt_dax: mp->m_flags |= XFS_MOUNT_DAX; -- 2.1.4 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs