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 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs