xfs
[Top] [All Lists]

[PATCH 14/15] xfs: Add shrink data ioctl(2)

To: xfs@xxxxxxxxxxx
Subject: [PATCH 14/15] xfs: Add shrink data ioctl(2)
From: Jeff Liu <jeff.liu@xxxxxxxxxx>
Date: Fri, 16 Nov 2012 14:46:59 +0800
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121028 Thunderbird/16.0.2
So the real FS data section shrink function via ioctl(2) is here.


Signed-off-by: Jie Liu <jeff.liu@xxxxxxxxxx>
---
 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

<Prev in Thread] Current Thread [Next in Thread>
  • [PATCH 14/15] xfs: Add shrink data ioctl(2), Jeff Liu <=