[PATCH 14/15] xfs: Add shrink data ioctl(2)
Jeff Liu
jeff.liu at oracle.com
Fri Nov 16 00:46:59 CST 2012
So the real FS data section shrink function via ioctl(2) is here.
Signed-off-by: Jie Liu <jeff.liu at oracle.com>
---
fs/xfs/xfs_fs.h | 8 ++
fs/xfs/xfs_fsops.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_fsops.h | 1 +
fs/xfs/xfs_ioctl.c | 15 ++++
4 files changed, 234 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
index c459d52..3a85979 100644
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -272,6 +272,13 @@ typedef struct xfs_growfs_rt {
__u32 extsize; /* new realtime extent size, fsblocks */
} xfs_growfs_rt_t;
+/*
+ * Structure for XFS_IOC_SHRINKFS_DATA.
+ */
+typedef struct xfs_shrinkfs_data {
+ __u64 newblocks; /* new data subvol size, fsblocks */
+ __u32 imaxpct; /* new inode space percentage limit */
+} xfs_shrinkfs_data_t;
/*
* Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE
@@ -489,6 +496,7 @@ typedef struct xfs_handle {
#define XFS_IOC_SET_AGSTATE _IOW('X', 126, struct xfs_ioc_agstate)
#define XFS_IOC_GET_AGSTATE _IOR('X', 127, struct xfs_ioc_agstate)
#define XFS_IOC_SWAPINO _IOWR('X', 128, struct xfs_swapino)
+#define XFS_IOC_FSSHRINKFS_DATA _IOW('X', 129, struct xfs_shrinkfs_data)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 04a21d1..e5aa564 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -579,8 +579,6 @@ xfs_growfs_log_private(
* point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
* XFS_IOC_FSGROWFSRT
*/
-
-
int
xfs_growfs_data(
xfs_mount_t *mp,
@@ -948,6 +946,216 @@ error0:
return error;
}
+static int
+xfs_shrinkfs_process_ag(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_rfsblock_t tsize,
+ xfs_extlen_t *nfree)
+{
+ xfs_perag_t *pag;
+ bool empty;
+ int error;
+
+ /*
+ * The a.g. state must has already been set to offline
+ * by xfs_shrinkfs(8).
+ */
+ pag = xfs_perag_get(mp, agno);
+ if (!(pag->pag_state & XFS_AG_STATE_ALLOC_DENY)) {
+ xfs_perag_put(pag);
+ return XFS_ERROR(EINVAL);
+ }
+ xfs_perag_put(pag);
+
+ error = xfs_ag_is_empty(mp, agno, &empty);
+ if (error)
+ goto error0;
+
+ if (!empty)
+ error = xfs_truncate_ag(mp, agno, tsize, nfree);
+ else {
+ /*
+ * The Primary AG must not be empty except at the
+ * invitial phase(mkfs.xfs). But we should not
+ * remove it anyway.
+ */
+ if (agno == 0) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+ error = xfs_remove_empty_ag(mp, agno, nfree);
+ }
+
+error0:
+ if (error)
+ *nfree = 0;
+
+ return error;
+}
+
+static int
+xfs_shrinkfs_data_private(
+ xfs_mount_t *mp,
+ xfs_shrinkfs_data_t *in)
+{
+ xfs_agnumber_t oagcount; /* old AG count */
+ xfs_agnumber_t nagcount; /* new AG count */
+ xfs_agnumber_t agno;
+ xfs_rfsblock_t nb, nb_mod;
+ xfs_rfsblock_t new;
+ xfs_rfsblock_t tsize;
+ xfs_rfsblock_t nfree;
+ xfs_trans_t *tp;
+ int pct;
+ int dpct;
+ int error;
+
+ new = nb = in->newblocks;
+ pct = in->imaxpct;
+
+ if (new > mp->m_sb.sb_dblocks)
+ return XFS_ERROR(EINVAL);
+ if (new == mp->m_sb.sb_dblocks)
+ return 0;
+ if (pct < 0 || pct > 100)
+ return XFS_ERROR(EINVAL);
+
+ tsize = mp->m_sb.sb_dblocks - new;
+
+ /*
+ * If the filesystem has internal logs, cannot shrink beyond the
+ * end block of the log area for now.
+ */
+ if (mp->m_sb.sb_logstart > 0 &&
+ new < mp->m_sb.sb_logstart + mp->m_sb.sb_logblocks)
+ return XFS_ERROR(EINVAL);
+
+ nb_mod = do_div(new, mp->m_sb.sb_agblocks);
+ nagcount = new + (nb_mod != 0);
+ if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
+ nagcount--;
+ nb_mod = 0;
+ nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
+ }
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SHRINKFS);
+ tp->t_flags |= XFS_TRANS_RESERVE;
+ error = xfs_trans_reserve(tp, XFS_SHRINKFS_SPACE_RES(mp),
+ XFS_SHRINKDATA_LOG_RES(mp),
+ 0, 0, 0);
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ goto out;
+ }
+
+ oagcount = mp->m_sb.sb_agcount;
+ nfree = 0;
+ for (agno = nagcount - 1; agno < oagcount; agno++) {
+ xfs_extlen_t freed;
+
+ error = xfs_shrinkfs_process_ag(mp, tp, agno, tsize, &freed);
+ if (error)
+ goto out_trans_cancel;
+ nfree += freed;
+ tsize -= freed;
+ }
+
+ xfs_trans_agblocks_delta(tp, -nfree);
+
+ /*
+ * Update superblock to reflect current agcount if possbile.
+ */
+ if (nagcount < oagcount)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
+
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, -nfree);
+
+ /*
+ * Update superblock to reflect current data blocks.
+ */
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, -nfree);
+ dpct = pct - mp->m_sb.sb_imax_pct;
+ if (dpct)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
+ error = xfs_trans_commit(tp, 0);
+ if (error)
+ goto out;
+
+ /* FIXME: iterate perag to find out the maximum agi */
+ if (mp->m_maxagi > nagcount)
+ mp->m_maxagi = nagcount;
+ if (!mp->m_sb.sb_imax_pct)
+ mp->m_maxicount = 0;
+ else {
+ __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
+ do_div(icount, 100);
+ mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
+ }
+ xfs_set_low_space_thresholds(mp);
+
+ /*
+ * Update secondary superblocks.
+ */
+ for (agno = 1; agno < nagcount; agno++) {
+ xfs_buf_t *bp = NULL;
+
+ error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (error) {
+ xfs_warn(mp,
+ "error %d reading secondary superblock for ag %d",
+ error, agno);
+ break;
+ }
+
+ xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS);
+ /*
+ * If we get an error writing out the alternate superblocks,
+ * just issue a warning and continue. The real work is
+ * already done and committed.
+ */
+ error = xfs_bwrite(bp);
+ xfs_buf_relse(bp);
+ if (error) {
+ xfs_warn(mp,
+ "write error %d updating secondary superblock for ag %d",
+ error, agno);
+ break; /* no point in continuing */
+ }
+ }
+
+out:
+ return error;
+
+out_trans_cancel:
+ xfs_trans_cancel(tp, XFS_TRANS_ABORT);
+ return error;
+}
+
+/*
+ * Interface of shrinking file system data section via ioctl(2).
+ */
+int
+xfs_shrinkfs_data(
+ xfs_mount_t *mp,
+ xfs_shrinkfs_data_t *in)
+{
+ int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+
+ if (!mutex_trylock(&mp->m_shrinklock))
+ return XFS_ERROR(EWOULDBLOCK);
+ error = xfs_shrinkfs_data_private(mp, in);
+ mutex_unlock(&mp->m_shrinklock);
+
+ return error;
+}
+
/*
* exported through ioctl XFS_IOC_FSCOUNTS
*/
diff --git a/fs/xfs/xfs_fsops.h b/fs/xfs/xfs_fsops.h
index 1b6a98b..40be112 100644
--- a/fs/xfs/xfs_fsops.h
+++ b/fs/xfs/xfs_fsops.h
@@ -26,5 +26,6 @@ extern int xfs_reserve_blocks(xfs_mount_t *mp, __uint64_t *inval,
xfs_fsop_resblks_t *outval);
extern int xfs_fs_goingdown(xfs_mount_t *mp, __uint32_t inflags);
extern int xfs_fs_log_dummy(struct xfs_mount *mp);
+extern int xfs_shrinkfs_data(xfs_mount_t *mp, xfs_shrinkfs_data_t *in);
#endif /* __XFS_FSOPS_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 0e0c03f..41b2900 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1649,6 +1649,21 @@ xfs_file_ioctl(
return -error;
}
+ case XFS_IOC_FSSHRINKFS_DATA: {
+ xfs_shrinkfs_data_t in;
+
+ if (copy_from_user(&in, arg, sizeof(in)))
+ return -XFS_ERROR(EFAULT);
+
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
+
+ error = xfs_shrinkfs_data(mp, &in);
+ mnt_drop_write_file(filp);
+ return -error;
+ }
+
default:
return -ENOTTY;
}
--
1.7.4.1
More information about the xfs
mailing list