diff -druN Linux-2.4.19-pre6-NFSALL/drivers/char/raw.c linux/drivers/char/raw.c
--- Linux-2.4.19-pre6-NFSALL/drivers/char/raw.c	Thu Apr 11 17:16:43 2002
+++ linux/drivers/char/raw.c	Thu Apr 11 17:32:08 2002
@@ -349,9 +349,9 @@
 			break;
 
 		for (i=0; i < blocks; i++) 
-			iobuf->blocks[i] = blocknr++;
+			iobuf->kio_blocks[i] = blocknr++;
 		
-		err = brw_kiovec(rw, 1, &iobuf, dev, iobuf->blocks, sector_size);
+		err = brw_kiovec(rw, 1, &iobuf, dev, iobuf->kio_blocks, sector_size);
 
 		if (rw == READ && err > 0)
 			mark_dirty_kiobuf(iobuf, err);
diff -druN Linux-2.4.19-pre6-NFSALL/fs/buffer.c linux/fs/buffer.c
--- Linux-2.4.19-pre6-NFSALL/fs/buffer.c	Thu Apr 11 17:17:35 2002
+++ linux/fs/buffer.c	Thu Apr 11 17:34:00 2002
@@ -2160,7 +2160,7 @@
 int generic_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize, get_block_t * get_block)
 {
 	int i, nr_blocks, retval;
-	unsigned long * blocks = iobuf->blocks;
+	unsigned long * blocks = iobuf->kio_blocks;
 	int length;
 
 	length = iobuf->length;
@@ -2202,7 +2202,7 @@
 
 	/* patch length to handle short I/O */
 	iobuf->length = i * blocksize;
-	retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->blocks, blocksize);
+	retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->kio_blocks, blocksize);
 	/* restore orig length */
 	iobuf->length = length;
  out:
@@ -2313,7 +2313,7 @@
 		length = iobuf->length;
 		iobuf->errno = 0;
 		if (!bhs)
-			bhs = iobuf->bh;
+			bhs = iobuf->kio_bh;
 		
 		for (pageind = 0; pageind < iobuf->nr_pages; pageind++) {
 			map  = iobuf->maplist[pageind];
diff -druN Linux-2.4.19-pre6-NFSALL/fs/dcache.c linux/fs/dcache.c
--- Linux-2.4.19-pre6-NFSALL/fs/dcache.c	Thu Apr 11 17:17:22 2002
+++ linux/fs/dcache.c	Thu Apr 11 17:34:48 2002
@@ -1252,6 +1252,7 @@
 
 extern void bdev_cache_init(void);
 extern void cdev_cache_init(void);
+extern void kio_cache_init(void);
 
 void __init vfs_caches_init(unsigned long mempages)
 {
@@ -1286,4 +1287,5 @@
 	mnt_init(mempages);
 	bdev_cache_init();
 	cdev_cache_init();
+	kio_cache_init();
 }
diff -druN Linux-2.4.19-pre6-NFSALL/fs/fcntl.c linux/fs/fcntl.c
--- Linux-2.4.19-pre6-NFSALL/fs/fcntl.c	Thu Apr 11 17:17:35 2002
+++ linux/fs/fcntl.c	Thu Apr 11 17:28:06 2002
@@ -223,6 +223,10 @@
 	}
 
 	if (arg & O_DIRECT) {
+		if (!inode->i_mapping || !inode->i_mapping->a_ops ||
+					!inode->i_mapping->a_ops->direct_IO)
+			return -EINVAL;
+
 		/*
 		 * alloc_kiovec() can sleep and we are only serialized by
 		 * the big kernel lock here, so abuse the i_sem to serialize
diff -druN Linux-2.4.19-pre6-NFSALL/fs/iobuf.c linux/fs/iobuf.c
--- Linux-2.4.19-pre6-NFSALL/fs/iobuf.c	Thu Apr 11 17:16:05 2002
+++ linux/fs/iobuf.c	Fri Apr 12 15:29:54 2002
@@ -10,6 +10,8 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 
+static kmem_cache_t *kiobuf_cachep;
+
 void end_kio_request(struct kiobuf *kiobuf, int uptodate)
 {
 	if ((!uptodate) && !kiobuf->errno)
@@ -22,36 +24,54 @@
 	}
 }
 
-static void kiobuf_init(struct kiobuf *iobuf)
+static int kiobuf_init(struct kiobuf *iobuf)
 {
 	memset(iobuf, 0, sizeof(*iobuf));
 	init_waitqueue_head(&iobuf->wait_queue);
-	iobuf->array_len = KIO_STATIC_PAGES;
-	iobuf->maplist   = iobuf->map_array;
+	iobuf->kio_bh = NULL;
+	iobuf->kio_blocks = NULL;
+	return expand_kiobuf(iobuf, KIO_STATIC_PAGES);
 }
 
 int alloc_kiobuf_bhs(struct kiobuf * kiobuf)
 {
 	int i;
 
+	kiobuf->kio_blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long),
+								GFP_KERNEL);
+	if (!kiobuf->kio_blocks)
+		goto nomem;
+
+	kiobuf->kio_bh = kmalloc(sizeof(struct buffer_head *) * KIO_MAX_SECTORS,
+								GFP_KERNEL);
+	if (!kiobuf->kio_bh)
+		goto nomem;
+
 	for (i = 0; i < KIO_MAX_SECTORS; i++)
-		if (!(kiobuf->bh[i] = kmem_cache_alloc(bh_cachep, SLAB_KERNEL))) {
-			while (i--) {
-				kmem_cache_free(bh_cachep, kiobuf->bh[i]);
-				kiobuf->bh[i] = NULL;
-			}
-			return -ENOMEM;
+		if (!(kiobuf->kio_bh[i] = kmem_cache_alloc(bh_cachep, SLAB_KERNEL))) {
+			goto nomem;
 		}
 	return 0;
+
+nomem:
+	free_kiobuf_bhs(kiobuf);
+	return -ENOMEM;
 }
 
 void free_kiobuf_bhs(struct kiobuf * kiobuf)
 {
 	int i;
 
-	for (i = 0; i < KIO_MAX_SECTORS; i++) {
-		kmem_cache_free(bh_cachep, kiobuf->bh[i]);
-		kiobuf->bh[i] = NULL;
+	if (kiobuf->kio_bh) {
+		for (i = 0; i < KIO_MAX_SECTORS; i++)
+			if (kiobuf->kio_bh[i])
+				kmem_cache_free(bh_cachep, kiobuf->kio_bh[i]);
+		kfree(kiobuf->kio_bh);
+		kiobuf->kio_bh = NULL;
+	}
+	if (kiobuf->kio_blocks) {
+		kfree(kiobuf->kio_blocks);
+		kiobuf->kio_blocks = NULL;
 	}
 }
 
@@ -61,14 +81,18 @@
 	struct kiobuf *iobuf;
 	
 	for (i = 0; i < nr; i++) {
-		iobuf = vmalloc(sizeof(struct kiobuf));
+		iobuf = kmem_cache_alloc(kiobuf_cachep, GFP_KERNEL);
 		if (!iobuf) {
 			free_kiovec(i, bufp);
 			return -ENOMEM;
 		}
-		kiobuf_init(iobuf);
+		if (kiobuf_init(iobuf)) {
+			kmem_cache_free(kiobuf_cachep, iobuf);
+			free_kiovec(i, bufp);
+			return -ENOMEM;
+		}
  		if (alloc_kiobuf_bhs(iobuf)) {
-			vfree(iobuf);
+			kmem_cache_free(kiobuf_cachep, iobuf);
  			free_kiovec(i, bufp);
  			return -ENOMEM;
  		}
@@ -87,10 +111,9 @@
 		iobuf = bufp[i];
 		if (iobuf->locked)
 			unlock_kiovec(1, &iobuf);
-		if (iobuf->array_len > KIO_STATIC_PAGES)
-			kfree (iobuf->maplist);
+		kfree (iobuf->maplist);
 		free_kiobuf_bhs(iobuf);
-		vfree(bufp[i]);
+		kmem_cache_free(kiobuf_cachep, bufp[i]);
 	}
 }
 
@@ -122,7 +145,6 @@
 	return 0;
 }
 
-
 void kiobuf_wait_for_io(struct kiobuf *kiobuf)
 {
 	struct task_struct *tsk = current;
@@ -144,5 +166,10 @@
 	remove_wait_queue(&kiobuf->wait_queue, &wait);
 }
 
-
-
+void __init kio_cache_init(void)
+{
+	kiobuf_cachep = kmem_cache_create("kiobuf", sizeof(struct kiobuf),
+					0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!kiobuf_cachep)
+		panic("Cannot create kiobuf SLAB cache");
+}
diff -druN Linux-2.4.19-pre6-NFSALL/fs/open.c linux/fs/open.c
--- Linux-2.4.19-pre6-NFSALL/fs/open.c	Thu Apr 11 17:17:35 2002
+++ linux/fs/open.c	Fri Apr 12 17:15:14 2002
@@ -715,6 +715,14 @@
 	}
 	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
+	/* NB: we're sure to have correct a_ops only after f_op->open */
+	if (f->f_flags & O_DIRECT) {
+		error = -EINVAL;
+		if (!inode->i_mapping || !inode->i_mapping->a_ops ||
+					!inode->i_mapping->a_ops->direct_IO)
+			goto cleanup_all;
+	}
+
 	return f;
 
 cleanup_all:
diff -druN Linux-2.4.19-pre6-NFSALL/include/linux/iobuf.h linux/include/linux/iobuf.h
--- Linux-2.4.19-pre6-NFSALL/include/linux/iobuf.h	Thu Apr 11 17:23:28 2002
+++ linux/include/linux/iobuf.h	Fri Apr 12 15:31:22 2002
@@ -46,10 +46,8 @@
 
 	unsigned int	locked : 1;	/* If set, pages has been locked */
 	
-	/* Always embed enough struct pages for atomic IO */
-	struct page *	map_array[KIO_STATIC_PAGES];
-	struct buffer_head * bh[KIO_MAX_SECTORS];
-	unsigned long blocks[KIO_MAX_SECTORS];
+	struct buffer_head ** kio_bh;
+	unsigned long * kio_blocks;
 
 	/* Dynamic state for IO completion: */
 	atomic_t	io_count;	/* IOs still in progress */
diff -druN Linux-2.4.19-pre6-NFSALL/mm/filemap.c linux/mm/filemap.c
--- Linux-2.4.19-pre6-NFSALL/mm/filemap.c	Thu Apr 11 17:19:17 2002
+++ linux/mm/filemap.c	Thu Apr 11 17:30:27 2002
@@ -1555,8 +1555,6 @@
 	retval = -EINVAL;
 	if ((offset & blocksize_mask) || (count & blocksize_mask))
 		goto out_free;
-	if (!mapping->a_ops->direct_IO)
-		goto out_free;
 
 	/*
 	 * Flush to disk exclusively the _data_, metadata must remain
