Fix various deadlocks due to not releasing various locks on various error paths. NFS direct-IO is totally broken. fs/direct-io.c | 20 +++++++++++++------- mm/filemap.c | 15 ++++++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff -puN fs/direct-io.c~O_DIRECT-race-fixes-fixes-2 fs/direct-io.c --- 25/fs/direct-io.c~O_DIRECT-race-fixes-fixes-2 2003-09-18 22:33:26.000000000 -0700 +++ 25-akpm/fs/direct-io.c 2003-09-18 22:47:13.000000000 -0700 @@ -858,18 +858,15 @@ out: static int direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, const struct iovec *iov, loff_t offset, unsigned long nr_segs, - unsigned blkbits, get_blocks_t get_blocks, dio_iodone_t end_io) + unsigned blkbits, get_blocks_t get_blocks, dio_iodone_t end_io, + struct dio *dio) { unsigned long user_addr; int seg; int ret = 0; int ret2; - struct dio *dio; size_t bytes; - dio = kmalloc(sizeof(*dio), GFP_KERNEL); - if (!dio) - return -ENOMEM; dio->is_async = !is_sync_kiocb(iocb); dio->bio = NULL; @@ -1016,6 +1013,7 @@ blockdev_direct_IO(int rw, struct kiocb unsigned bdev_blkbits = 0; unsigned blocksize_mask = (1 << blkbits) - 1; ssize_t retval = -EINVAL; + struct dio *dio; if (bdev) bdev_blkbits = blksize_bits(bdev_hardsect_size(bdev)); @@ -1041,8 +1039,16 @@ blockdev_direct_IO(int rw, struct kiocb } } - retval = direct_io_worker(rw, iocb, inode, iov, offset, - nr_segs, blkbits, get_blocks, end_io); + dio = kmalloc(sizeof(*dio), GFP_KERNEL); + retval = -ENOMEM; + if (!dio) + goto out; + + return direct_io_worker(rw, iocb, inode, iov, offset, + nr_segs, blkbits, get_blocks, end_io, dio); out: + up(&inode->i_sem); + if (S_ISREG(inode->i_mode)) + up_read(&inode->i_alloc_sem); return retval; } diff -puN mm/filemap.c~O_DIRECT-race-fixes-fixes-2 mm/filemap.c --- 25/mm/filemap.c~O_DIRECT-race-fixes-fixes-2 2003-09-18 22:42:49.000000000 -0700 +++ 25-akpm/mm/filemap.c 2003-09-18 22:51:30.000000000 -0700 @@ -886,12 +886,12 @@ __generic_file_aio_read(struct kiocb *io retval = 0; if (!count) goto out; /* skip atime */ - if (S_ISREG(inode->i_mode)) { - down_read(&inode->i_alloc_sem); - down(&inode->i_sem); - } size = i_size_read(inode); if (pos < size) { + if (S_ISREG(inode->i_mode)) { + down_read(&inode->i_alloc_sem); + down(&inode->i_sem); + } retval = generic_file_direct_IO(READ, iocb, iov, pos, nr_segs); if (retval >= 0 && !is_sync_kiocb(iocb)) @@ -2176,7 +2176,8 @@ generic_file_direct_IO(int rw, struct ki loff_t offset, unsigned long nr_segs) { struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_dentry->d_inode->i_mapping; + struct inode *inode = file->f_dentry->d_inode; + struct address_space *mapping = inode->i_mapping; ssize_t retval; if (mapping->nrpages) { @@ -2190,6 +2191,10 @@ generic_file_direct_IO(int rw, struct ki retval = mapping->a_ops->direct_IO(rw, iocb, iov, offset, nr_segs); if (rw == WRITE && mapping->nrpages) invalidate_inode_pages2(mapping); + return retval; out: + up(&inode->i_sem); + if (S_ISREG(inode->i_mode)) + up_read(&inode->i_alloc_sem); return retval; } _