From 60bf65f24c85e1ad096e4475e7ccd914d89ca265 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Thu, 23 Sep 2004 19:44:23 -0700 Subject: [PATCH] Fix generic direct IO code for XFS The following patch to the generic direct IO code fixes a problem we've come across that affects the way XFS interacts with it. Between 2.6.5 and 2.6.6 several changes to direct IO were made, in particular the fallback-to-buffered path was introduced in that timeframe. Those changes split the locking done within direct-io.c into two cases - generic filesystems and blkdev/XFS. There is no locking done for the second case, and we also never set the create parameter to the get_blocks call (via direct_io_worker ->do_direct_IO->get_more_blocks) for that case. This meant that XFS was accidentally no longer being told when a direct IO write was being performed, which in turn meant that XFS would (often - depending on the inode's size and bmap) tell get_more_blocks that it was mapping to a hole. It also means that currently all direct writes into XFS are falling back to buffered writes. Further, skipping the i_alloc_sem locking entirely is not correct for us, we are relying on that aspect of the generic locking. The fallback behaviour from direct to buffered is "silent", so we didn't actually pick up on these problems until just recently, unfortunately. The approach I've taken here is to split the blkdev/XFS case into two, and corrected the new third case behaviour for the functionality XFS provides. The generic behavior used by other filesystems remains unchanged, as does direct IO to the block device, and XFS now becomes fully functional. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 5bb9738362d5..4f6fe6b575a8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1474,18 +1474,21 @@ static inline void do_generic_file_read(struct file * filp, loff_t *ppos, ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io, - int needs_special_locking); + int lock_type); + +enum { + DIO_LOCKING = 1, /* need locking between buffered and direct access */ + DIO_NO_LOCKING, /* bdev; no locking at all between buffered/direct */ + DIO_OWN_LOCKING, /* filesystem locks buffered and direct internally */ +}; -/* - * For filesystems which need locking between buffered and direct access - */ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io) { return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_blocks, end_io, 1); + nr_segs, get_blocks, end_io, DIO_LOCKING); } static inline ssize_t blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb, @@ -1494,7 +1497,16 @@ static inline ssize_t blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb, dio_iodone_t end_io) { return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_blocks, end_io, 0); + nr_segs, get_blocks, end_io, DIO_NO_LOCKING); +} + +static inline ssize_t blockdev_direct_IO_own_locking(int rw, struct kiocb *iocb, + struct inode *inode, struct block_device *bdev, const struct iovec *iov, + loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + dio_iodone_t end_io) +{ + return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, + nr_segs, get_blocks, end_io, DIO_OWN_LOCKING); } extern struct file_operations generic_ro_fops; -- cgit v1.2.3