fs/buffer.c    |   19 ++++++++-----------
 fs/direct-io.c |   12 ++++++++----
 mm/filemap.c   |   23 ++++++++++++-----------
 3 files changed, 28 insertions(+), 26 deletions(-)

diff -puN fs/direct-io.c~O_DIRECT-race-fixes-fixes fs/direct-io.c
--- 25/fs/direct-io.c~O_DIRECT-race-fixes-fixes	2003-09-16 18:39:36.000000000 -0700
+++ 25-akpm/fs/direct-io.c	2003-09-16 18:39:36.000000000 -0700
@@ -470,10 +470,14 @@ static int get_more_blocks(struct dio *d
 			fs_count++;
 
 		if (dio->is_reg_file) {
-			if (dio->block_in_file > dio->inode->i_size) {
+			if (dio->block_in_file >= (i_size_read(dio->inode) >>
+							dio->blkbits))
 				beyond_eof = 1;
-			}
 		}
+		/*
+		 * AKPM: huh?  We call get_blocks(create==0) for hole
+		 * overwrites?
+		 */
 		ret = (*dio->get_blocks)(dio->inode, fs_startblk, fs_count,
 				map_bh, (dio->rw == WRITE) && beyond_eof);
 	}
@@ -782,9 +786,9 @@ do_holes:
 			if (!buffer_mapped(map_bh)) {
 				char *kaddr;
 
-				if (dio->rw == WRITE) {
+				/* AKPM: eargh, -ENOTBLK is a hack */
+				if (dio->rw == WRITE)
 					return -ENOTBLK;
-				}
 
 				if (dio->block_in_file >=
 					i_size_read(dio->inode)>>blkbits) {
diff -puN mm/filemap.c~O_DIRECT-race-fixes-fixes mm/filemap.c
--- 25/mm/filemap.c~O_DIRECT-race-fixes-fixes	2003-09-16 18:39:36.000000000 -0700
+++ 25-akpm/mm/filemap.c	2003-09-16 18:39:36.000000000 -0700
@@ -70,6 +70,9 @@
  *  ->mmap_sem
  *    ->i_sem			(msync)
  *
+ *  ->i_alloc_sem
+ *    ->i_sem                   (various)
+ *
  *  ->inode_lock
  *    ->sb_lock			(fs/fs-writeback.c)
  *    ->mapping->page_lock	(__sync_single_inode)
@@ -1849,18 +1852,17 @@ __generic_file_aio_write_nolock(struct k
 			status = generic_osync_inode(inode, OSYNC_METADATA);
 		if (written >= 0 && !is_sync_kiocb(iocb))
 			written = -EIOCBQUEUED;
-		if (written == -ENOTBLK) {
-			if (S_ISREG(inode->i_mode)) {
-				down(&inode->i_sem);
-			}
-			goto do_buffer_io;
-		}
-		goto out_status;
+		if (written != -ENOTBLK)
+			goto out_status;
+		/*
+		 * direct-io write to a hole: fall through to buffered I/O
+		 */
+		written = 0;
+		if (S_ISREG(inode->i_mode))
+			down(&inode->i_sem);
 	}
 
-do_buffer_io:
 	buf = iov->iov_base;
-	written = 0; /* Reset in case this is a fallback from O_DIRECT */
 	do {
 		unsigned long index;
 		unsigned long offset;
@@ -1959,9 +1961,8 @@ do_buffer_io:
 	if (unlikely(file->f_flags & O_DIRECT)) {
 		if (written)
 			do_fdatasync(file);
-		if (S_ISREG(inode->i_mode)) {
+		if (S_ISREG(inode->i_mode))
 			up(&inode->i_sem);
-		}
 	}
 
 out_status:	
diff -puN fs/buffer.c~O_DIRECT-race-fixes-fixes fs/buffer.c
--- 25/fs/buffer.c~O_DIRECT-race-fixes-fixes	2003-09-16 18:39:36.000000000 -0700
+++ 25-akpm/fs/buffer.c	2003-09-16 18:39:36.000000000 -0700
@@ -402,20 +402,17 @@ asmlinkage long sys_fdatasync(unsigned i
 {
 	struct file * file;
 	struct inode * inode;
-	int ret, err;
+	int ret;
 
 	ret = -EBADF;
 	file = fget(fd);
-	if (!file)
-		goto out;
-
-	inode = file->f_dentry->d_inode;
-	down(&inode->i_sem);
-	ret = do_fdatasync(file);
-	up(&inode->i_sem);
-out_putf:
-	fput(file);
-out:
+	if (file) {
+		inode = file->f_dentry->d_inode;
+		down(&inode->i_sem);
+		ret = do_fdatasync(file);
+		up(&inode->i_sem);
+		fput(file);
+	}
 	return ret;
 }
 

_