xfs: use dedicated log worker wq to avoid deadlock with cil wq
authorBrian Foster <bfoster@redhat.com>
Tue, 28 Mar 2017 21:51:44 +0000 (14:51 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Mon, 3 Apr 2017 22:18:15 +0000 (15:18 -0700)
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>
fs/xfs/xfs_log.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_super.c

index b1469f0a91a6c04329cb2fff2dc6e9953485a071..bb58cd1873c9b2eba1ac9180d85548c22abbdff5 100644 (file)
@@ -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));
 }
 
index 6db6fd6b82b0ae7eaee47fd88735325734aa51e0..22b2185e93a0c4aa4edcd25411bed006d4fbe715 100644 (file)
@@ -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
index 685c042a120f16a8a9a8dad69d8ee7ce6f9274a0..47d239dcf3f4d46cf73153fdfc9521692b1847bb 100644 (file)
@@ -877,8 +877,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:
@@ -899,6 +906,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);