From: Dave Chinner <dchinner@xxxxxxxxxx> To wait while IOs drain from the inode while inside an ioend context, we have to wait until the inode io count drops to 1 - the reference we hold - rather than zero. Add functionality to the ioend wait subsystem to do this. Signed-off-by: Dave Chinner <dchinner@xxxxxxxxxx> --- fs/xfs/linux-2.6/xfs_aops.c | 50 +++++++++++++++++++++++++++++++++++++++++- fs/xfs/xfs_inode.h | 13 ++++++----- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 5682490..ec499f2 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -64,6 +64,9 @@ xfs_ioend_init(void) init_waitqueue_head(&xfs_ioend_wq[i]); } +/* + * wait for all IO to drain from the inode + */ void xfs_ioend_wait( xfs_inode_t *ip) @@ -73,12 +76,55 @@ xfs_ioend_wait( wait_event(*wq, (atomic_read(&ip->i_iocount) == 0)); } +/* + * If we have an active ioend in the caller context (e.g. + * xfs_get_blocks_direct) we need to wait for the count to drop to one before + * we are woken. + * + * For this to work in the context of concurrent callers (concurrent direct IO), + * this function must be called with the iolock held exclusively to prevent + * other IOs blocking here and preventing the count from ever dropping to 1. + */ +STATIC void +xfs_ioend_wait_excl( + xfs_inode_t *ip) +{ + wait_queue_head_t *wq = to_ioend_wq(ip); + + ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); + xfs_iflags_set(ip, XFS_IIOEND_WAIT_EXCL); + wait_event(*wq, (atomic_read(&ip->i_iocount) == 1)); + xfs_iflags_clear(ip, XFS_IIOEND_WAIT_EXCL); +} + STATIC void xfs_ioend_wake( xfs_inode_t *ip) { - if (atomic_dec_and_test(&ip->i_iocount)) + if (atomic_dec_and_test(&ip->i_iocount)) { + wake_up(to_ioend_wq(ip)); + return; + } + + /* + * do an unlocked check for an exclusive wait before trying to get + * spinlocks to avoid hurting the normal path too much. We can do this + * check unlocked because if the flag is not set here and this is the + * last IO remaining (i.e. iocount == 1 after the above decrement), + * then any code that enters xfs_ioend_wait_excl() will now see that + * i_iocount == 1 and return immediately. Hence we don't need to issue + * a wakeup in this case, and it keeps the common case overhead as low + * as possible. + */ + smp_rmb(); + if (!__xfs_iflags_test(ip, XFS_IIOEND_WAIT_EXCL)) + return; + + spin_lock(&ip->i_flags_lock); + if (atomic_read(&ip->i_iocount) == 1 && + __xfs_iflags_test(ip, XFS_IIOEND_WAIT_EXCL)) wake_up(to_ioend_wq(ip)); + spin_unlock(&ip->i_flags_lock); } void @@ -1334,7 +1380,7 @@ remap: (flags & GET_BLOCKS_UNALIGNED) && !iolock_changed) { xfs_iunlock(XFS_I(inode), XFS_IOLOCK_SHARED); xfs_ilock(XFS_I(inode), XFS_IOLOCK_EXCL); - xfs_ioend_wait(XFS_I(inode)); + xfs_ioend_wait_excl(XFS_I(inode)); iolock_changed = 1; goto remap; } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 0898c54..40b5203 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -357,12 +357,13 @@ static inline void xfs_ifunlock(xfs_inode_t *ip) /* * In-core inode flags. */ -#define XFS_IRECLAIM 0x0001 /* we have started reclaiming this inode */ -#define XFS_ISTALE 0x0002 /* inode has been staled */ -#define XFS_IRECLAIMABLE 0x0004 /* inode can be reclaimed */ -#define XFS_INEW 0x0008 /* inode has just been allocated */ -#define XFS_IFILESTREAM 0x0010 /* inode is in a filestream directory */ -#define XFS_ITRUNCATED 0x0020 /* truncated down so flush-on-close */ +#define XFS_IRECLAIM 0x0001 /* have started reclaiming this inode */ +#define XFS_ISTALE 0x0002 /* inode has been staled */ +#define XFS_IRECLAIMABLE 0x0004 /* inode can be reclaimed */ +#define XFS_INEW 0x0008 /* inode has just been allocated */ +#define XFS_IFILESTREAM 0x0010 /* inode is in a filestream directory */ +#define XFS_ITRUNCATED 0x0020 /* truncated down so flush-on-close */ +#define XFS_IIOEND_WAIT_EXCL 0x0040 /* io completion waiter in io context */ /* * Flags for inode locking. -- 1.7.1 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs