| From hch@infradead.org Tue Nov 22 13:36:45 2011 |
| From: Christoph Hellwig <hch@infradead.org> |
| Date: Sat, 19 Nov 2011 13:13:41 -0500 |
| Subject: xfs: avoid direct I/O write vs buffered I/O race |
| To: stable@vger.kernel.org |
| Cc: xfs@oss.sgi.com, Alex Elder <aelder@sgi.com> |
| Message-ID: <20111119181544.434721480@bombadil.infradead.org> |
| |
| From: Christoph Hellwig <hch@infradead.org> |
| |
| commit c58cb165bd44de8aaee9755a144136ae743be116 upstream. |
| |
| Currently a buffered reader or writer can add pages to the pagecache |
| while we are waiting for the iolock in xfs_file_dio_aio_write. Prevent |
| this by re-checking mapping->nrpages after we got the iolock, and if |
| nessecary upgrade the lock to exclusive mode. To simplify this a bit |
| only take the ilock inside of xfs_file_aio_write_checks. |
| |
| Signed-off-by: Christoph Hellwig <hch@lst.de> |
| Reviewed-by: Dave Chinner <dchinner@redhat.com> |
| Signed-off-by: Alex Elder <aelder@sgi.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/xfs/linux-2.6/xfs_file.c | 17 ++++++++++++++--- |
| 1 file changed, 14 insertions(+), 3 deletions(-) |
| |
| --- a/fs/xfs/linux-2.6/xfs_file.c |
| +++ b/fs/xfs/linux-2.6/xfs_file.c |
| @@ -669,6 +669,7 @@ xfs_file_aio_write_checks( |
| xfs_fsize_t new_size; |
| int error = 0; |
| |
| + xfs_rw_ilock(ip, XFS_ILOCK_EXCL); |
| error = generic_write_checks(file, pos, count, S_ISBLK(inode->i_mode)); |
| if (error) { |
| xfs_rw_iunlock(ip, XFS_ILOCK_EXCL | *iolock); |
| @@ -760,14 +761,24 @@ xfs_file_dio_aio_write( |
| *iolock = XFS_IOLOCK_EXCL; |
| else |
| *iolock = XFS_IOLOCK_SHARED; |
| - xfs_rw_ilock(ip, XFS_ILOCK_EXCL | *iolock); |
| + xfs_rw_ilock(ip, *iolock); |
| |
| ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); |
| if (ret) |
| return ret; |
| |
| + /* |
| + * Recheck if there are cached pages that need invalidate after we got |
| + * the iolock to protect against other threads adding new pages while |
| + * we were waiting for the iolock. |
| + */ |
| + if (mapping->nrpages && *iolock == XFS_IOLOCK_SHARED) { |
| + xfs_rw_iunlock(ip, *iolock); |
| + *iolock = XFS_IOLOCK_EXCL; |
| + xfs_rw_ilock(ip, *iolock); |
| + } |
| + |
| if (mapping->nrpages) { |
| - WARN_ON(*iolock != XFS_IOLOCK_EXCL); |
| ret = -xfs_flushinval_pages(ip, (pos & PAGE_CACHE_MASK), -1, |
| FI_REMAPF_LOCKED); |
| if (ret) |
| @@ -812,7 +823,7 @@ xfs_file_buffered_aio_write( |
| size_t count = ocount; |
| |
| *iolock = XFS_IOLOCK_EXCL; |
| - xfs_rw_ilock(ip, XFS_ILOCK_EXCL | *iolock); |
| + xfs_rw_ilock(ip, *iolock); |
| |
| ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); |
| if (ret) |