Skip to content

Commit

Permalink
xfs: prevent unwritten extent conversion from blocking I/O completion
Browse files Browse the repository at this point in the history
Unwritten extent conversion can recurse back into the filesystem due
to memory allocation. Memory reclaim requires I/O completions to be
processed to allow the callers to make progress. If the I/O
completion workqueue thread is doing the recursion, then we have a
deadlock situation.

Move unwritten extent completion into it's own workqueue so it
doesn't block I/O completions for normal delayed allocation or
overwrite data.

Signed-off-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
  • Loading branch information
Dave Chinner authored and Christoph Hellwig committed Apr 6, 2009
1 parent 705db3f commit c626d17
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 17 deletions.
38 changes: 21 additions & 17 deletions fs/xfs/linux-2.6/xfs_aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,23 +152,6 @@ xfs_find_bdev_for_inode(
return mp->m_ddev_targp->bt_bdev;
}

/*
* Schedule IO completion handling on a xfsdatad if this was
* the final hold on this ioend. If we are asked to wait,
* flush the workqueue.
*/
STATIC void
xfs_finish_ioend(
xfs_ioend_t *ioend,
int wait)
{
if (atomic_dec_and_test(&ioend->io_remaining)) {
queue_work(xfsdatad_workqueue, &ioend->io_work);
if (wait)
flush_workqueue(xfsdatad_workqueue);
}
}

/*
* We're now finished for good with this ioend structure.
* Update the page state via the associated buffer_heads,
Expand Down Expand Up @@ -309,6 +292,27 @@ xfs_end_bio_read(
xfs_destroy_ioend(ioend);
}

/*
* Schedule IO completion handling on a xfsdatad if this was
* the final hold on this ioend. If we are asked to wait,
* flush the workqueue.
*/
STATIC void
xfs_finish_ioend(
xfs_ioend_t *ioend,
int wait)
{
if (atomic_dec_and_test(&ioend->io_remaining)) {
struct workqueue_struct *wq = xfsdatad_workqueue;
if (ioend->io_work.func == xfs_end_bio_unwritten)
wq = xfsconvertd_workqueue;

queue_work(wq, &ioend->io_work);
if (wait)
flush_workqueue(wq);
}
}

/*
* Allocate and initialise an IO completion structure.
* We need to track unwritten extent write completion here initially.
Expand Down
1 change: 1 addition & 0 deletions fs/xfs/linux-2.6/xfs_aops.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define __XFS_AOPS_H__

extern struct workqueue_struct *xfsdatad_workqueue;
extern struct workqueue_struct *xfsconvertd_workqueue;
extern mempool_t *xfs_ioend_pool;

/*
Expand Down
9 changes: 9 additions & 0 deletions fs/xfs/linux-2.6/xfs_buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ static struct shrinker xfs_buf_shake = {

static struct workqueue_struct *xfslogd_workqueue;
struct workqueue_struct *xfsdatad_workqueue;
struct workqueue_struct *xfsconvertd_workqueue;

#ifdef XFS_BUF_TRACE
void
Expand Down Expand Up @@ -1775,6 +1776,7 @@ xfs_flush_buftarg(
xfs_buf_t *bp, *n;
int pincount = 0;

xfs_buf_runall_queues(xfsconvertd_workqueue);
xfs_buf_runall_queues(xfsdatad_workqueue);
xfs_buf_runall_queues(xfslogd_workqueue);

Expand Down Expand Up @@ -1831,9 +1833,15 @@ xfs_buf_init(void)
if (!xfsdatad_workqueue)
goto out_destroy_xfslogd_workqueue;

xfsconvertd_workqueue = create_workqueue("xfsconvertd");
if (!xfsconvertd_workqueue)
goto out_destroy_xfsdatad_workqueue;

register_shrinker(&xfs_buf_shake);
return 0;

out_destroy_xfsdatad_workqueue:
destroy_workqueue(xfsdatad_workqueue);
out_destroy_xfslogd_workqueue:
destroy_workqueue(xfslogd_workqueue);
out_free_buf_zone:
Expand All @@ -1849,6 +1857,7 @@ void
xfs_buf_terminate(void)
{
unregister_shrinker(&xfs_buf_shake);
destroy_workqueue(xfsconvertd_workqueue);
destroy_workqueue(xfsdatad_workqueue);
destroy_workqueue(xfslogd_workqueue);
kmem_zone_destroy(xfs_buf_zone);
Expand Down

0 comments on commit c626d17

Please sign in to comment.