Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c626d174 authored by Dave Chinner's avatar Dave Chinner Committed by Christoph Hellwig
Browse files

xfs: prevent unwritten extent conversion from blocking I/O completion



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: default avatarDave Chinner <david@fromorbit.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 705db3fd
Loading
Loading
Loading
Loading
+21 −17
Original line number Diff line number Diff line
@@ -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,
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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;

/*
+9 −0
Original line number Diff line number Diff line
@@ -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
@@ -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);

@@ -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:
@@ -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);