[PATCH 3/9] xfs: defrag implement stop/suspend/resume/status

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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)





[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux