In the main process xfs_defrag_process(), deal with following cases: 1. sleep until next defragmentation time come 2. sleep if no defragmetation job exist 3. defragmentation job is stopped by user 4. defragmentation job failed (stay a while for user to pick up error) 5. defragmentation job is suspended 6. defragmentation job is done successfully Signed-off-by: Wengang Wang <wen.gang.wang@xxxxxxxxxx> --- fs/xfs/xfs_defrag.c | 146 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_defrag.c b/fs/xfs/xfs_defrag.c index fec617ac5945..aee4cfd3f86e 100644 --- a/fs/xfs/xfs_defrag.c +++ b/fs/xfs/xfs_defrag.c @@ -190,6 +190,14 @@ static void __xfs_drop_defrag(struct xfs_defrag_info *di, struct xfs_mount *mp) kfree(di); } +/* cleanup when a defragmentation is done, failed, or cancelled. */ +static void xfs_drop_defrag(struct xfs_defrag_info *di, struct xfs_mount *mp) +{ + down(&mp->m_defrag_lock); + __xfs_drop_defrag(di, mp); + up(&mp->m_defrag_lock); +} + /* clean up all defragmentation jobs in this XFS */ void clean_up_defrags(struct xfs_mount *mp) { @@ -203,15 +211,149 @@ void clean_up_defrags(struct xfs_mount *mp) up(&mp->m_defrag_lock); } +/* + * if mp->m_defrag_list is not empty, return the first one in the list. + * returns NULL otherwise. + */ +static struct xfs_defrag_info *get_first_defrag(struct xfs_mount *mp) +{ + struct xfs_defrag_info *first; + + down(&mp->m_defrag_lock); + if (list_empty(&mp->m_defrag_list)) + first = NULL; + else + first = container_of(mp->m_defrag_list.next, + struct xfs_defrag_info, di_list); + up(&mp->m_defrag_lock); + return first; +} + +/* + * if mp->m_defrag_list is not empty, return the last one in the list. + * returns NULL otherwise. + */ +static struct xfs_defrag_info *get_last_defrag(struct xfs_mount *mp) +{ + struct xfs_defrag_info *last; + + down(&mp->m_defrag_lock); + if (list_empty(&mp->m_defrag_list)) + last = NULL; + else + last = container_of(mp->m_defrag_list.prev, + struct xfs_defrag_info, di_list); + up(&mp->m_defrag_lock); + return last; +} + +static inline bool xfs_defrag_failed(struct xfs_defrag_info *di) +{ + return di->di_defrag.df_status != 0; +} + +/* so far do nothing */ +static bool xfs_defrag_file(struct xfs_defrag_info *di) +{ + return true; +} + +static inline bool xfs_defrag_suspended(struct xfs_defrag_info *di) +{ + return di->di_defrag.df_suspended; +} + /* run as a separated process. * defragment files in mp->m_defrag_list */ int xfs_defrag_process(void *data) { + unsigned long smallest_wait = ULONG_MAX; struct xfs_mount *mp = data; + struct xfs_defrag_info *di, *last; + + while (!kthread_should_stop()) { + bool defrag_any = false; - while (!kthread_should_stop()) - xfs_defrag_idle(1000); + if (smallest_wait != ULONG_MAX) { + smallest_wait = max_t(unsigned long, smallest_wait, 10); + xfs_defrag_idle(smallest_wait); + smallest_wait = ULONG_MAX; + } + + last = get_last_defrag(mp); + if (!last) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + continue; /* while loop */ + } + + do { + unsigned long next_defrag_time; + unsigned long save_jiffies; + + if (kthread_should_stop()) + break; /* do */ + + di = get_first_defrag(mp); + /* done this round */ + if (!di) + break; /* do */ + + /* stopped by user, clean up right now */ + if (di->di_user_stopped) { + xfs_drop_defrag(di, mp); + continue; /* do */ + } + + /* + * Defrag failed on this file, give some grace time, say 30s + * for user space to capture the error + */ + if (xfs_defrag_failed(di)) { + unsigned long drop_time = di->di_last_process + + msecs_to_jiffies(XFS_DERFAG_GRACE_PERIOD); + save_jiffies = jiffies; + /* not the time to drop this failed file yet */ + if (time_before(save_jiffies, drop_time)) { + /* wait a while before dropping this file */ + if (smallest_wait > drop_time - save_jiffies) + smallest_wait = drop_time - save_jiffies; + } else { + xfs_drop_defrag(di, mp); + } + continue; /* do */ + } + + if (xfs_defrag_suspended(di)) + continue; /* do */ + + next_defrag_time = di->di_last_process + + msecs_to_jiffies(di->di_defrag.df_idle_time); + + save_jiffies = jiffies; + if (time_before(save_jiffies, next_defrag_time)) { + if (smallest_wait > next_defrag_time - save_jiffies) + smallest_wait = next_defrag_time - save_jiffies; + continue; /* do */ + } + + defrag_any = true; + /* whole file defrag done successfully */ + if (xfs_defrag_file(di)) + xfs_drop_defrag(di, mp); + + /* avoid tight CPU usage */ + xfs_defrag_idle(2); + } while (di != last); + + /* all the left defragmentations are suspended */ + if (defrag_any == false && smallest_wait == ULONG_MAX) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + + } clean_up_defrags(mp); return 0; -- 2.39.3 (Apple Git-145)