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 #include +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