| From hch@infradead.org Wed Oct 19 15:57:58 2011 |
| From: Christoph Hellwig <hch@infradead.org> |
| Date: Tue, 18 Oct 2011 10:23:19 -0400 |
| Subject: xfs: revert to using a kthread for AIL pushing |
| To: stable@vger.kernel.org |
| Cc: greg@kroah.com |
| Message-ID: <20111018142352.317272450@bombadil.infradead.org> |
| |
| From: Christoph Hellwig <hch@infradead.org> |
| |
| commit 0030807c66f058230bcb20d2573bcaf28852e804 upstream |
| |
| Currently we have a few issues with the way the workqueue code is used to |
| implement AIL pushing: |
| |
| - it accidentally uses the same workqueue as the syncer action, and thus |
| can be prevented from running if there are enough sync actions active |
| in the system. |
| - it doesn't use the HIGHPRI flag to queue at the head of the queue of |
| work items |
| |
| At this point I'm not confident enough in getting all the workqueue flags and |
| tweaks right to provide a perfectly reliable execution context for AIL |
| pushing, which is the most important piece in XFS to make forward progress |
| when the log fills. |
| |
| Revert back to use a kthread per filesystem which fixes all the above issues |
| at the cost of having a task struct and stack around for each mounted |
| filesystem. In addition this also gives us much better ways to diagnose |
| any issues involving hung AIL pushing and removes a small amount of code. |
| |
| Signed-off-by: Christoph Hellwig <hch@lst.de> |
| Reported-by: Stefan Priebe <s.priebe@profihost.ag> |
| Tested-by: Stefan Priebe <s.priebe@profihost.ag> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/xfs/linux-2.6/xfs_linux.h | 2 + |
| fs/xfs/linux-2.6/xfs_super.c | 13 ------- |
| fs/xfs/xfs_trans_ail.c | 73 ++++++++++++++++++++++++------------------- |
| fs/xfs/xfs_trans_priv.h | 8 ---- |
| 4 files changed, 45 insertions(+), 51 deletions(-) |
| |
| --- a/fs/xfs/linux-2.6/xfs_linux.h |
| +++ b/fs/xfs/linux-2.6/xfs_linux.h |
| @@ -70,6 +70,8 @@ |
| #include <linux/ctype.h> |
| #include <linux/writeback.h> |
| #include <linux/capability.h> |
| +#include <linux/kthread.h> |
| +#include <linux/freezer.h> |
| #include <linux/list_sort.h> |
| |
| #include <asm/page.h> |
| --- a/fs/xfs/linux-2.6/xfs_super.c |
| +++ b/fs/xfs/linux-2.6/xfs_super.c |
| @@ -1660,24 +1660,13 @@ xfs_init_workqueues(void) |
| */ |
| xfs_syncd_wq = alloc_workqueue("xfssyncd", WQ_CPU_INTENSIVE, 8); |
| if (!xfs_syncd_wq) |
| - goto out; |
| - |
| - xfs_ail_wq = alloc_workqueue("xfsail", WQ_CPU_INTENSIVE, 8); |
| - if (!xfs_ail_wq) |
| - goto out_destroy_syncd; |
| - |
| + return -ENOMEM; |
| return 0; |
| - |
| -out_destroy_syncd: |
| - destroy_workqueue(xfs_syncd_wq); |
| -out: |
| - return -ENOMEM; |
| } |
| |
| STATIC void |
| xfs_destroy_workqueues(void) |
| { |
| - destroy_workqueue(xfs_ail_wq); |
| destroy_workqueue(xfs_syncd_wq); |
| } |
| |
| --- a/fs/xfs/xfs_trans_ail.c |
| +++ b/fs/xfs/xfs_trans_ail.c |
| @@ -28,8 +28,6 @@ |
| #include "xfs_trans_priv.h" |
| #include "xfs_error.h" |
| |
| -struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ |
| - |
| #ifdef DEBUG |
| /* |
| * Check that the list is sorted as it should be. |
| @@ -406,16 +404,10 @@ xfs_ail_delete( |
| xfs_trans_ail_cursor_clear(ailp, lip); |
| } |
| |
| -/* |
| - * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself |
| - * to run at a later time if there is more work to do to complete the push. |
| - */ |
| -STATIC void |
| -xfs_ail_worker( |
| - struct work_struct *work) |
| +static long |
| +xfsaild_push( |
| + struct xfs_ail *ailp) |
| { |
| - struct xfs_ail *ailp = container_of(to_delayed_work(work), |
| - struct xfs_ail, xa_work); |
| xfs_mount_t *mp = ailp->xa_mount; |
| struct xfs_ail_cursor *cur = &ailp->xa_cursors; |
| xfs_log_item_t *lip; |
| @@ -556,20 +548,6 @@ out_done: |
| /* We're past our target or empty, so idle */ |
| ailp->xa_last_pushed_lsn = 0; |
| |
| - /* |
| - * We clear the XFS_AIL_PUSHING_BIT first before checking |
| - * whether the target has changed. If the target has changed, |
| - * this pushes the requeue race directly onto the result of the |
| - * atomic test/set bit, so we are guaranteed that either the |
| - * the pusher that changed the target or ourselves will requeue |
| - * the work (but not both). |
| - */ |
| - clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags); |
| - smp_rmb(); |
| - if (XFS_LSN_CMP(ailp->xa_target, target) == 0 || |
| - test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) |
| - return; |
| - |
| tout = 50; |
| } else if (XFS_LSN_CMP(lsn, target) >= 0) { |
| /* |
| @@ -592,9 +570,30 @@ out_done: |
| tout = 20; |
| } |
| |
| - /* There is more to do, requeue us. */ |
| - queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, |
| - msecs_to_jiffies(tout)); |
| + return tout; |
| +} |
| + |
| +static int |
| +xfsaild( |
| + void *data) |
| +{ |
| + struct xfs_ail *ailp = data; |
| + long tout = 0; /* milliseconds */ |
| + |
| + while (!kthread_should_stop()) { |
| + if (tout && tout <= 20) |
| + __set_current_state(TASK_KILLABLE); |
| + else |
| + __set_current_state(TASK_INTERRUPTIBLE); |
| + schedule_timeout(tout ? |
| + msecs_to_jiffies(tout) : MAX_SCHEDULE_TIMEOUT); |
| + |
| + try_to_freeze(); |
| + |
| + tout = xfsaild_push(ailp); |
| + } |
| + |
| + return 0; |
| } |
| |
| /* |
| @@ -629,8 +628,9 @@ xfs_ail_push( |
| */ |
| smp_wmb(); |
| xfs_trans_ail_copy_lsn(ailp, &ailp->xa_target, &threshold_lsn); |
| - if (!test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) |
| - queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, 0); |
| + smp_wmb(); |
| + |
| + wake_up_process(ailp->xa_task); |
| } |
| |
| /* |
| @@ -865,9 +865,18 @@ xfs_trans_ail_init( |
| ailp->xa_mount = mp; |
| INIT_LIST_HEAD(&ailp->xa_ail); |
| spin_lock_init(&ailp->xa_lock); |
| - INIT_DELAYED_WORK(&ailp->xa_work, xfs_ail_worker); |
| + |
| + ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild/%s", |
| + ailp->xa_mount->m_fsname); |
| + if (IS_ERR(ailp->xa_task)) |
| + goto out_free_ailp; |
| + |
| mp->m_ail = ailp; |
| return 0; |
| + |
| +out_free_ailp: |
| + kmem_free(ailp); |
| + return ENOMEM; |
| } |
| |
| void |
| @@ -876,6 +885,6 @@ xfs_trans_ail_destroy( |
| { |
| struct xfs_ail *ailp = mp->m_ail; |
| |
| - cancel_delayed_work_sync(&ailp->xa_work); |
| + kthread_stop(ailp->xa_task); |
| kmem_free(ailp); |
| } |
| --- a/fs/xfs/xfs_trans_priv.h |
| +++ b/fs/xfs/xfs_trans_priv.h |
| @@ -64,23 +64,17 @@ struct xfs_ail_cursor { |
| */ |
| struct xfs_ail { |
| struct xfs_mount *xa_mount; |
| + struct task_struct *xa_task; |
| struct list_head xa_ail; |
| xfs_lsn_t xa_target; |
| struct xfs_ail_cursor xa_cursors; |
| spinlock_t xa_lock; |
| - struct delayed_work xa_work; |
| xfs_lsn_t xa_last_pushed_lsn; |
| - unsigned long xa_flags; |
| }; |
| |
| -#define XFS_AIL_PUSHING_BIT 0 |
| - |
| /* |
| * From xfs_trans_ail.c |
| */ |
| - |
| -extern struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ |
| - |
| void xfs_trans_ail_update_bulk(struct xfs_ail *ailp, |
| struct xfs_ail_cursor *cur, |
| struct xfs_log_item **log_items, int nr_items, |