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: Dave Chinner <david@fromorbit.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>

authored by

Dave Chinner and committed by
Christoph Hellwig
c626d174 705db3fd

+31 -17
+21 -17
fs/xfs/linux-2.6/xfs_aops.c
··· 153 153 } 154 154 155 155 /* 156 - * Schedule IO completion handling on a xfsdatad if this was 157 - * the final hold on this ioend. If we are asked to wait, 158 - * flush the workqueue. 159 - */ 160 - STATIC void 161 - xfs_finish_ioend( 162 - xfs_ioend_t *ioend, 163 - int wait) 164 - { 165 - if (atomic_dec_and_test(&ioend->io_remaining)) { 166 - queue_work(xfsdatad_workqueue, &ioend->io_work); 167 - if (wait) 168 - flush_workqueue(xfsdatad_workqueue); 169 - } 170 - } 171 - 172 - /* 173 156 * We're now finished for good with this ioend structure. 174 157 * Update the page state via the associated buffer_heads, 175 158 * release holds on the inode and bio, and finally free ··· 290 307 container_of(work, xfs_ioend_t, io_work); 291 308 292 309 xfs_destroy_ioend(ioend); 310 + } 311 + 312 + /* 313 + * Schedule IO completion handling on a xfsdatad if this was 314 + * the final hold on this ioend. If we are asked to wait, 315 + * flush the workqueue. 316 + */ 317 + STATIC void 318 + xfs_finish_ioend( 319 + xfs_ioend_t *ioend, 320 + int wait) 321 + { 322 + if (atomic_dec_and_test(&ioend->io_remaining)) { 323 + struct workqueue_struct *wq = xfsdatad_workqueue; 324 + if (ioend->io_work.func == xfs_end_bio_unwritten) 325 + wq = xfsconvertd_workqueue; 326 + 327 + queue_work(wq, &ioend->io_work); 328 + if (wait) 329 + flush_workqueue(wq); 330 + } 293 331 } 294 332 295 333 /*
+1
fs/xfs/linux-2.6/xfs_aops.h
··· 19 19 #define __XFS_AOPS_H__ 20 20 21 21 extern struct workqueue_struct *xfsdatad_workqueue; 22 + extern struct workqueue_struct *xfsconvertd_workqueue; 22 23 extern mempool_t *xfs_ioend_pool; 23 24 24 25 /*
+9
fs/xfs/linux-2.6/xfs_buf.c
··· 51 51 52 52 static struct workqueue_struct *xfslogd_workqueue; 53 53 struct workqueue_struct *xfsdatad_workqueue; 54 + struct workqueue_struct *xfsconvertd_workqueue; 54 55 55 56 #ifdef XFS_BUF_TRACE 56 57 void ··· 1776 1775 xfs_buf_t *bp, *n; 1777 1776 int pincount = 0; 1778 1777 1778 + xfs_buf_runall_queues(xfsconvertd_workqueue); 1779 1779 xfs_buf_runall_queues(xfsdatad_workqueue); 1780 1780 xfs_buf_runall_queues(xfslogd_workqueue); 1781 1781 ··· 1833 1831 if (!xfsdatad_workqueue) 1834 1832 goto out_destroy_xfslogd_workqueue; 1835 1833 1834 + xfsconvertd_workqueue = create_workqueue("xfsconvertd"); 1835 + if (!xfsconvertd_workqueue) 1836 + goto out_destroy_xfsdatad_workqueue; 1837 + 1836 1838 register_shrinker(&xfs_buf_shake); 1837 1839 return 0; 1838 1840 1841 + out_destroy_xfsdatad_workqueue: 1842 + destroy_workqueue(xfsdatad_workqueue); 1839 1843 out_destroy_xfslogd_workqueue: 1840 1844 destroy_workqueue(xfslogd_workqueue); 1841 1845 out_free_buf_zone: ··· 1857 1849 xfs_buf_terminate(void) 1858 1850 { 1859 1851 unregister_shrinker(&xfs_buf_shake); 1852 + destroy_workqueue(xfsconvertd_workqueue); 1860 1853 destroy_workqueue(xfsdatad_workqueue); 1861 1854 destroy_workqueue(xfslogd_workqueue); 1862 1855 kmem_zone_destroy(xfs_buf_zone);