| From 696a562072e3c14bcd13ae5acc19cdf27679e865 Mon Sep 17 00:00:00 2001 |
| From: Brian Foster <bfoster@redhat.com> |
| Date: Tue, 28 Mar 2017 14:51:44 -0700 |
| Subject: xfs: use dedicated log worker wq to avoid deadlock with cil wq |
| |
| From: Brian Foster <bfoster@redhat.com> |
| |
| commit 696a562072e3c14bcd13ae5acc19cdf27679e865 upstream. |
| |
| The log covering background task used to be part of the xfssyncd |
| workqueue. That workqueue was removed as of commit 5889608df ("xfs: |
| syncd workqueue is no more") and the associated work item scheduled |
| to the xfs-log wq. The latter is used for log buffer I/O completion. |
| |
| Since xfs_log_worker() can invoke a log flush, a deadlock is |
| possible between the xfs-log and xfs-cil workqueues. Consider the |
| following codepath from xfs_log_worker(): |
| |
| xfs_log_worker() |
| xfs_log_force() |
| _xfs_log_force() |
| xlog_cil_force() |
| xlog_cil_force_lsn() |
| xlog_cil_push_now() |
| flush_work() |
| |
| The above is in xfs-log wq context and blocked waiting on the |
| completion of an xfs-cil work item. Concurrently, the cil push in |
| progress can end up blocked here: |
| |
| xlog_cil_push_work() |
| xlog_cil_push() |
| xlog_write() |
| xlog_state_get_iclog_space() |
| xlog_wait(&log->l_flush_wait, ...) |
| |
| The above is in xfs-cil context waiting on log buffer I/O |
| completion, which executes in xfs-log wq context. In this scenario |
| both workqueues are deadlocked waiting on eachother. |
| |
| Add a new workqueue specifically for the high level log covering and |
| ail pushing worker, as was the case prior to commit 5889608df. |
| |
| Diagnosed-by: David Jeffery <djeffery@redhat.com> |
| Signed-off-by: Brian Foster <bfoster@redhat.com> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/xfs/xfs_log.c | 2 +- |
| fs/xfs/xfs_mount.h | 1 + |
| fs/xfs/xfs_super.c | 8 ++++++++ |
| 3 files changed, 10 insertions(+), 1 deletion(-) |
| |
| --- a/fs/xfs/xfs_log.c |
| +++ b/fs/xfs/xfs_log.c |
| @@ -1293,7 +1293,7 @@ void |
| xfs_log_work_queue( |
| struct xfs_mount *mp) |
| { |
| - queue_delayed_work(mp->m_log_workqueue, &mp->m_log->l_work, |
| + queue_delayed_work(mp->m_sync_workqueue, &mp->m_log->l_work, |
| msecs_to_jiffies(xfs_syncd_centisecs * 10)); |
| } |
| |
| --- a/fs/xfs/xfs_mount.h |
| +++ b/fs/xfs/xfs_mount.h |
| @@ -183,6 +183,7 @@ typedef struct xfs_mount { |
| struct workqueue_struct *m_reclaim_workqueue; |
| struct workqueue_struct *m_log_workqueue; |
| struct workqueue_struct *m_eofblocks_workqueue; |
| + struct workqueue_struct *m_sync_workqueue; |
| |
| /* |
| * Generation of the filesysyem layout. This is incremented by each |
| --- a/fs/xfs/xfs_super.c |
| +++ b/fs/xfs/xfs_super.c |
| @@ -872,8 +872,15 @@ xfs_init_mount_workqueues( |
| if (!mp->m_eofblocks_workqueue) |
| goto out_destroy_log; |
| |
| + mp->m_sync_workqueue = alloc_workqueue("xfs-sync/%s", WQ_FREEZABLE, 0, |
| + mp->m_fsname); |
| + if (!mp->m_sync_workqueue) |
| + goto out_destroy_eofb; |
| + |
| return 0; |
| |
| +out_destroy_eofb: |
| + destroy_workqueue(mp->m_eofblocks_workqueue); |
| out_destroy_log: |
| destroy_workqueue(mp->m_log_workqueue); |
| out_destroy_reclaim: |
| @@ -894,6 +901,7 @@ STATIC void |
| xfs_destroy_mount_workqueues( |
| struct xfs_mount *mp) |
| { |
| + destroy_workqueue(mp->m_sync_workqueue); |
| destroy_workqueue(mp->m_eofblocks_workqueue); |
| destroy_workqueue(mp->m_log_workqueue); |
| destroy_workqueue(mp->m_reclaim_workqueue); |