1. we support at most 128 running file defragmentation at a time. 2. the max piece size is 4096 3. the max piece size must no less than twice of target extent size 4. defrag jobs are stored in mp->m_defrag_list. 5. for 'status' command, set the inode number to -1UL for return 6. a separated process m_defrag_task processes all defragmentation jobs Signed-off-by: Wengang Wang <wen.gang.wang@xxxxxxxxxx> --- fs/xfs/xfs_defrag.c | 200 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_defrag.c b/fs/xfs/xfs_defrag.c index 8bdc6290a69d..4a10528912ca 100644 --- a/fs/xfs/xfs_defrag.c +++ b/fs/xfs/xfs_defrag.c @@ -54,6 +54,47 @@ */ #define XFS_DEFRAG_MAX_PIECE_BLOCKS 4096U +/* + * A piece is a contigurous (by file block number) range in a file. It contains one + * or more extents. When it contains two or more extents, it's subject to be + * defragmented. During the defragmenting, the original extents are + * deallocated and replaced by a single new-allocated extent covering this + * whole piece. + */ +struct xfs_defrag_piece { + /* the start file block in this piece */ + xfs_fileoff_t dp_start_off; + /* number of blocks contained in this piece */ + xfs_filblks_t dp_len; + /* + * the extents in this piece. they are contigourous by file block + * number after the piece is picked. they are sorted by filesystem + * lock number (low -> high) before unmapping. + */ + struct xfs_bmbt_irec dp_extents[XFS_DEFRAG_PIECE_MAX_EXT]; + /* number of xfs_bmbt_irecs in dp_extents */ + int dp_nr_ext; +}; + +struct xfs_defrag_info { + /* links to xfs_mount.m_defrag_list */ + struct list_head di_list; /* links to xfs_mount.m_defrag_list */ + /* defrag configuration and status */ + struct xfs_defrag di_defrag; + /* the xfs_inode to defragment on */ + struct xfs_inode *di_ip; + /* next file block to start with */ + xfs_fileoff_t di_next_blk; + /* number of pieces which are defragmented */ + unsigned long di_round_nr; + /* current piece to defragment */ + struct xfs_defrag_piece di_dp; + /* timestamp of last defragmenting in jiffies */ + unsigned long di_last_process; + /* flag indicating if defragmentation is stopped by user */ + bool di_user_stopped; +}; + /* initialization called for new mount */ void xfs_initialize_defrag(struct xfs_mount *mp) { @@ -77,7 +118,164 @@ void xfs_stop_wait_defrags(struct xfs_mount *mp) mp->m_defrag_task = NULL; } -int xfs_file_defrag(struct file *filp, struct xfs_defrag *defrag) + +static bool xfs_is_defrag_param_valid(struct xfs_defrag *defrag) +{ + if (defrag->df_piece_size > XFS_DEFRAG_MAX_PIECE_BLOCKS) + return false; + if (defrag->df_piece_size < 2 * defrag->df_tgt_extsize) + return false; + return true; +} + +static inline bool __xfs_new_defrag_allowed(struct xfs_mount *mp) + { + if (mp->m_nr_defrag >= XFS_DEFRAG_MAX_PARALLEL) + return false; + + return true; +} + +/* + * lookup this mount for the xfs_defrag_info structure specified by @ino + * m_defrag_lock is held by caller. + * returns: + * The pointer to that structure on found or NULL if not found. + */ +struct xfs_defrag_info *__xfs_find_defrag(unsigned long ino, + struct xfs_mount *mp) +{ + struct xfs_defrag_info *di; + + list_for_each_entry(di, &mp->m_defrag_list, di_list) { + if (di->di_defrag.df_ino == ino) + return di; + } + return NULL; +} + +/* start a new defragmetation or change the parameters on the existing one */ +static int xfs_file_defrag_start(struct inode *inode, struct xfs_defrag *defrag) { + int ret = 0; + + if ((inode->i_mode & S_IFMT) != S_IFREG) { + ret = -EOPNOTSUPP; + goto out; + } + + if (IS_DAX(inode)) { + ret = -EOPNOTSUPP; + goto out; + } + + if (!xfs_is_defrag_param_valid(defrag)) { + ret = EINVAL; + goto out; + } + +out: return -EOPNOTSUPP; + } + +static void xfs_file_defrag_status(struct inode *inode, struct xfs_defrag *defrag) +{ + struct xfs_mount *mp = XFS_I(inode)->i_mount; + struct xfs_defrag_info *di; + + down(&mp->m_defrag_lock); + di = __xfs_find_defrag(inode->i_ino, mp); + if (di == NULL) { + up(&mp->m_defrag_lock); + defrag->df_ino = -1UL; + return; + } + di->di_defrag.df_cmd = defrag->df_cmd; + *defrag = di->di_defrag; + up(&mp->m_defrag_lock); +} + +static int xfs_file_defrag_stop(struct inode *inode, struct xfs_defrag *defrag) +{ + struct xfs_mount *mp = XFS_I(inode)->i_mount; + struct xfs_defrag_info *di; + + down(&mp->m_defrag_lock); + di = __xfs_find_defrag(inode->i_ino, mp); + if (di == NULL) { + up(&mp->m_defrag_lock); + defrag->df_ino = -1UL; + return -EINVAL; + } + + di->di_user_stopped = true; + di->di_defrag.df_cmd = defrag->df_cmd; + *defrag = di->di_defrag; + up(&mp->m_defrag_lock); + /* wait up the process to process the dropping */ + wake_up_process(mp->m_defrag_task); + return 0; +} + +static int xfs_file_defrag_suspend(struct inode *inode, struct xfs_defrag *defrag) +{ + struct xfs_mount *mp = XFS_I(inode)->i_mount; + struct xfs_defrag_info *di; + + down(&mp->m_defrag_lock); + di = __xfs_find_defrag(inode->i_ino, mp); + if (di == NULL) { + up(&mp->m_defrag_lock); + defrag->df_ino = -1UL; + return -EINVAL; + } + di->di_defrag.df_suspended = true; + di->di_defrag.df_cmd = defrag->df_cmd; + *defrag = di->di_defrag; + up(&mp->m_defrag_lock); + return 0; +} + +static int xfs_file_defrag_resume(struct inode *inode, struct xfs_defrag *defrag) +{ + struct xfs_mount *mp = XFS_I(inode)->i_mount; + struct xfs_defrag_info *di; + + down(&mp->m_defrag_lock); + di = __xfs_find_defrag(inode->i_ino, mp); + if (di == NULL) { + up(&mp->m_defrag_lock); + defrag->df_ino = -1UL; + return -EINVAL; + } + di->di_defrag.df_suspended = false; + + di->di_defrag.df_cmd = defrag->df_cmd; + *defrag = di->di_defrag; + up(&mp->m_defrag_lock); + wake_up_process(mp->m_defrag_task); + return 0; +} + +int xfs_file_defrag(struct file *filp, struct xfs_defrag *defrag) +{ + struct inode *inode = filp->f_inode; + + defrag->df_ino = inode->i_ino; + + switch (defrag->df_cmd) { + case XFS_DEFRAG_CMD_START: + return xfs_file_defrag_start(inode, defrag); + case XFS_DEFRAG_CMD_STOP: + return xfs_file_defrag_stop(inode, defrag); + case XFS_DEFRAG_CMD_STATUS: + xfs_file_defrag_status(inode, defrag); + return 0; + case XFS_DEFRAG_CMD_SUSPEND: + return xfs_file_defrag_suspend(inode, defrag); + case XFS_DEFRAG_CMD_RESUME: + return xfs_file_defrag_resume(inode, defrag); + default: + return -EOPNOTSUPP; + } } -- 2.39.3 (Apple Git-145)