Here is a patch for 2.5.3pre6 to make RAW and O_DIRECT use "bio"
more efficiently. Idea is to simply eliminate intermediate "kiobuf".
I used some of the infrastructure from Ben's AIO patch (2.4.18).
Since Ben's patch is for 2.4, I had to rewrite some of it to suit
2.5 BIO design.
Description:
============
Added kvec (memory container), kvec_cb (control block for kvec IO).
Added support routines for kvec: map_user_kvec(), unmap_kvec(), 
alloc_kvec(), free_kvec() etc..
Use veclets from kvec directly as bio_vecs for "bio" to do the IO.
Issues:
=======
generic_direct_IO() uses stack to build a extent list <block#, size>
of discontiguous disk blocks and passes it to brw_kvec(). Need to
find a better way to do it. 
I am restructuring the code so that, I can push down one extent at a time 
and wait for completion at higher level. One problem with this approach
is kvec_cb (control block) need to have info on the last veclet where
we did IO. 
Any ideas ?
Things to follow:
=================
1) generic_direct_IO() cleanup.
2) I have not removed f_iobuf from filp. Need to do some kiobuf cleanup.
3) Add support for readv/writev for RAW.
Any comments on approach/code are welcome. I can make 2.5.4-preX patch
if the approach is acceptable.
Thanks,
Badari
diff -Naur -X dontdiff linux-2.5.3pre6.org/drivers/char/raw.c linux-2.5.3pre6/drivers/char/raw.c
--- linux-2.5.3pre6.org/drivers/char/raw.c	Tue Jan  1 11:40:34 2002
+++ linux-2.5.3pre6/drivers/char/raw.c	Mon Feb  4 20:36:39 2002
@@ -85,12 +85,6 @@
 		return 0;
 	}
 	
-	if (!filp->f_iobuf) {
-		err = alloc_kiovec(1, &filp->f_iobuf);
-		if (err)
-			return err;
-	}
-
 	down(&raw_devices[minor].mutex);
 	/*
 	 * No, it is a normal raw device.  All we need to do on open is
@@ -255,8 +249,7 @@
 ssize_t	rw_raw_dev(int rw, struct file *filp, char *buf, 
 		   size_t size, loff_t *offp)
 {
-	struct kiobuf * iobuf;
-	int		new_iobuf;
+	struct kvec 	*vec;
 	int		err = 0;
 	unsigned long	blocks;
 	size_t		transferred;
@@ -273,17 +266,11 @@
 
 	minor = minor(filp->f_dentry->d_inode->i_rdev);
 
-	new_iobuf = 0;
-	iobuf = filp->f_iobuf;
-	if (test_and_set_bit(0, &filp->f_iobuf_lock)) {
-		/*
-		 * A parallel read/write is using the preallocated iobuf
-		 * so just run slow and allocate a new one.
-		 */
-		err = alloc_kiovec(1, &iobuf);
-		if (err)
-			goto out;
-		new_iobuf = 1;
+
+	vec = alloc_kvec((size/PAGE_SIZE) + 1);
+	if (!vec) {
+		err = -ENOMEM;
+		goto out;
 	}
 
 	dev = to_kdev_t(raw_devices[minor].binding->bd_dev);
@@ -318,14 +305,14 @@
 
 		iosize = blocks << sector_bits;
 
-		err = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize);
+		err = map_user_kvec(rw, vec, (unsigned long) buf, iosize);
 		if (err)
 			break;
 
-		err = brw_kiovec(rw, 1, &iobuf, dev, &blocknr, sector_size);
+		err = brw_kvec(rw, 1, vec, dev, &blocknr, &iosize, sector_size);
 
 		if (rw == READ && err > 0)
-			mark_dirty_kiobuf(iobuf, err);
+			mark_dirty_kvec(vec, err);
 		
 		if (err >= 0) {
 			transferred += err;
@@ -335,7 +322,7 @@
 
 		blocknr += blocks;
 
-		unmap_kiobuf(iobuf);
+		unmap_kvec(vec);
 
 		if (err != iosize)
 			break;
@@ -346,11 +333,8 @@
 		err = transferred;
 	}
 
- out_free:
-	if (!new_iobuf)
-		clear_bit(0, &filp->f_iobuf_lock);
-	else
-		free_kiovec(1, &iobuf);
- out:	
+out_free:	
+	free_kvec(vec);
+out:
 	return err;
 }
diff -Naur -X dontdiff linux-2.5.3pre6.org/fs/bio.c linux-2.5.3pre6/fs/bio.c
--- linux-2.5.3pre6.org/fs/bio.c	Thu Dec 27 08:15:15 2001
+++ linux-2.5.3pre6/fs/bio.c	Wed Feb  6 21:17:14 2002
@@ -495,6 +495,98 @@
 	return 0;
 }
 
+static int bio_end_io_kvec(struct bio *bio, int nr_sectors)
+{
+	struct kvec_cb *kvec_cb = (struct kvec_cb *) bio->bi_private;
+
+	end_kvec_request(kvec_cb, test_bit(BIO_UPTODATE, &bio->bi_flags));
+	bio_put(bio);
+	return 0;
+}
+
+int ll_rw_kvec(int rw, struct kvec_cb *vec_cb, kdev_t dev, int nr, sector_t b[], int len[])
+{
+	int i, err, tlen, blen, total;
+	struct kvec *vec = vec_cb->vec;
+	struct bio *bio;
+	kveclet_t *veclet;
+	int	sector;
+
+	err = 0;
+	total = 0;
+
+	if ((rw & WRITE) && is_read_only(dev)) {
+		printk("ll_rw_kvec: WRITE to ro device %s\n", kdevname(dev));
+		err = -EPERM;
+		goto out;
+	}
+
+	if (!vec->nr) {
+		err = -EINVAL;
+		goto out;
+	}
+
+
+	atomic_set(&vec_cb->io_count, 1);
+	veclet = vec->veclet;
+
+	for (i = 0; i < nr; i++) {
+		tlen = len[i];
+		sector = b[i];
+
+		while (tlen) {
+
+			blen = 0;
+
+			/*
+			 * allocate bio and do initial setup
+			 */
+			if ((bio = bio_alloc(GFP_NOIO, 0)) == NULL) {
+				err = -ENOMEM;
+				goto out;
+			}
+			atomic_inc(&vec_cb->io_count);
+
+			bio->bi_sector = sector;
+			bio->bi_dev = dev;
+			bio->bi_idx = 0;
+			bio->bi_end_io = bio_end_io_kvec;
+			bio->bi_private = vec_cb;
+			bio->bi_io_vec = veclet;
+
+			while (blen < tlen) {
+				if ((blen + veclet->bv_len) > BIO_MAX_SIZE) 
+					break;
+
+				blen += veclet->bv_len;
+				bio->bi_vcnt++;
+				veclet++;
+			}
+
+			bio->bi_size = blen;
+			sector += (blen >> 9);
+			tlen -= blen;
+
+			submit_bio(rw, bio);
+			total += blen;
+		}
+	}
+
+out:
+	if (err)
+		vec_cb->errno = err;
+
+	/*
+	 * final atomic_dec of io_count to match our initial setting of 1.
+	 * I/O may or may not have completed at this point, final completion
+	 * handler is only run on last decrement.
+	 */
+	end_kvec_request(vec_cb, !err);
+	return total;
+}
+
 module_init(init_bio);
 
 EXPORT_SYMBOL(bio_alloc);
diff -Naur -X dontdiff linux-2.5.3pre6.org/fs/block_dev.c linux-2.5.3pre6/fs/block_dev.c
--- linux-2.5.3pre6.org/fs/block_dev.c	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/fs/block_dev.c	Mon Feb  4 21:06:44 2002
@@ -134,9 +134,9 @@
 	return 0;
 }
 
-static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+static int blkdev_direct_IO(int rw, struct inode * inode, struct kvec * iobuf, unsigned long blocknr, int iosize, int blocksize)
 {
-	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block);
+	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, iosize, blkdev_get_block);
 }
 
 static int blkdev_writepage(struct page * page)
diff -Naur -X dontdiff linux-2.5.3pre6.org/fs/buffer.c linux-2.5.3pre6/fs/buffer.c
--- linux-2.5.3pre6.org/fs/buffer.c	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/fs/buffer.c	Wed Feb  6 22:41:59 2002
@@ -1919,14 +1919,20 @@
 	return tmp.b_blocknr;
 }
 
-int generic_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize, get_block_t * get_block)
+int generic_direct_IO(int rw, struct inode * inode, struct kvec * vec, unsigned long blocknr, int blocksize, int iosize, get_block_t * get_block)
 {
 	int i, nr_blocks, retval;
-	sector_t *blocks = iobuf->blocks;
+	int 	blkno, len;
+	sector_t blocks[256];
+	int	blen[256];
+
+	nr_blocks = iosize/blocksize;
+	i = 0;
+	len = 0;
+	blkno = -2;
 
-	nr_blocks = iobuf->length / blocksize;
 	/* build the blocklist */
-	for (i = 0; i < nr_blocks; i++, blocknr++) {
+	while (nr_blocks) {
 		struct buffer_head bh;
 
 		bh.b_state = 0;
@@ -1937,12 +1943,17 @@
 		if (retval)
 			goto out;
 
+		blocknr++;
+		nr_blocks--;
+
 		if (rw == READ) {
 			if (buffer_new(&bh))
 				BUG();
 			if (!buffer_mapped(&bh)) {
 				/* there was an hole in the filesystem */
 				blocks[i] = -1UL;
+				blen[i] = blocksize;
+				i++;
 				continue;
 			}
 		} else {
@@ -1951,11 +1962,28 @@
 			if (!buffer_mapped(&bh))
 				BUG();
 		}
-		blocks[i] = bh.b_blocknr;
+		if (bh.b_blocknr == blkno + 1) {
+			len += blocksize;
+		} else {
+			if (len != 0) {
+				blocks[i] = blkno * (blocksize >> 9);
+				blen[i] = len;
+				i++;
+			}
+			blkno = bh.b_blocknr;
+			len = blocksize;
+		}
+		
+	}
+
+	if (blkno != -2) {
+		blocks[i] = blkno * (blocksize >> 9);
+		blen[i] = len;
+		i++;
 	}
 
 	/* This does not understand multi-device filesystems currently */
-	retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, blocks, blocksize);
+	retval = brw_kvec(rw, i, vec, inode->i_dev, blocks, blen, blocksize);
 
  out:
 	return retval;
@@ -2017,6 +2045,40 @@
 		if (!err)
 			transferred += iobuf->length;
 	}
+
+	return err ? err : transferred;
+}
+
+int brw_kvec(int rw, int nr, struct kvec *vec, kdev_t dev, sector_t b[],
+	       int len[], int size)
+{
+	int		transferred;
+	int		i, err;
+	struct	kvec_cb	vec_cb;
+	kveclet_t	*vecl;
+
+	if (!nr)
+		return 0;
+	
+	if (!vec->nr)
+		panic("brw_kvec: kvec not initialised");
+
+	vecl = vec->veclet;
+	for (i = 0; i < vec->nr; i++, vecl++) {
+		if ((vecl->bv_offset & (size-1)) || (vecl->bv_len & (size-1)))
+			return -EINVAL;
+	}
+
+	vec_cb.errno = 0;
+	init_waitqueue_head(&vec_cb.wait_queue);
+	atomic_set(&vec_cb.io_count, 0);
+	vec_cb.vec = vec;
+
+	transferred = ll_rw_kvec(rw, &vec_cb, dev, nr, b, len);
+
+	kvec_wait_for_io(&vec_cb);
+
+	err = vec_cb.errno;
 
 	return err ? err : transferred;
 }
diff -Naur -X dontdiff linux-2.5.3pre6.org/fs/ext2/inode.c linux-2.5.3pre6/fs/ext2/inode.c
--- linux-2.5.3pre6.org/fs/ext2/inode.c	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/fs/ext2/inode.c	Mon Feb  4 21:13:06 2002
@@ -592,9 +592,9 @@
 {
 	return generic_block_bmap(mapping,block,ext2_get_block);
 }
-static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+static int ext2_direct_IO(int rw, struct inode * inode, struct kvec * iobuf, unsigned long blocknr, int blocksize, int iosize)
 {
-	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block);
+	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, iosize, ext2_get_block);
 }
 struct address_space_operations ext2_aops = {
 	readpage: ext2_readpage,
diff -Naur -X dontdiff linux-2.5.3pre6.org/fs/iobuf.c linux-2.5.3pre6/fs/iobuf.c
--- linux-2.5.3pre6.org/fs/iobuf.c	Tue Nov 27 09:23:27 2001
+++ linux-2.5.3pre6/fs/iobuf.c	Tue Feb  5 16:38:09 2002
@@ -120,3 +120,56 @@
 
 
 
+struct kvec *alloc_kvec(int nr)
+{
+	struct kvec *vec;
+	
+	vec = kmalloc(sizeof(struct kvec) + nr * sizeof(kveclet_t), GFP_KERNEL);
+	if (!vec) {
+		return 0;
+	}
+	vec->nr = 0;
+	vec->max_nr = nr;
+	return vec; 
+}
+
+void free_kvec(struct kvec *vec)
+{
+	kfree(vec);
+}
+
+int end_kvec_request(struct kvec_cb *vec_cb, int uptodate)
+{
+	int ret = 1;
+
+	if ((!uptodate) && !vec_cb->errno)
+		vec_cb->errno = -EIO;
+
+	if (atomic_dec_and_test(&vec_cb->io_count)) {
+		ret = 0;
+		wake_up(&vec_cb->wait_queue);
+	}
+
+	return ret;
+}
+
+void kvec_wait_for_io(struct kvec_cb *vec_cb)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+
+	if (atomic_read(&vec_cb->io_count) == 0)
+		return;
+
+	add_wait_queue(&vec_cb->wait_queue, &wait);
+repeat:
+	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+	if (atomic_read(&vec_cb->io_count) != 0) {
+		run_task_queue(&tq_disk);
+		schedule();
+		if (atomic_read(&vec_cb->io_count) != 0)
+			goto repeat;
+	}
+	tsk->state = TASK_RUNNING;
+	remove_wait_queue(&vec_cb->wait_queue, &wait);
+}
diff -Naur -X dontdiff linux-2.5.3pre6.org/include/linux/fs.h linux-2.5.3pre6/include/linux/fs.h
--- linux-2.5.3pre6.org/include/linux/fs.h	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/include/linux/fs.h	Mon Feb  4 21:13:39 2002
@@ -350,6 +350,7 @@
 struct page;
 struct address_space;
 struct kiobuf;
+struct kvec;
 
 struct address_space_operations {
 	int (*writepage)(struct page *);
@@ -366,7 +367,7 @@
 	int (*flushpage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, int);
 #define KERNEL_HAS_O_DIRECT /* this is for modules out of the kernel */
-	int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int);
+	int (*direct_IO)(int, struct inode *, struct kvec *, unsigned long, int, int);
 };
 
 struct address_space {
@@ -1434,7 +1435,7 @@
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
-extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *);
+extern int generic_direct_IO(int, struct inode *, struct kvec *, unsigned long, int, int, get_block_t *);
 
 extern int waitfor_one_page(struct page*);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
diff -Naur -X dontdiff linux-2.5.3pre6.org/include/linux/iobuf.h linux-2.5.3pre6/include/linux/iobuf.h
--- linux-2.5.3pre6.org/include/linux/iobuf.h	Mon Jan 14 13:27:14 2002
+++ linux-2.5.3pre6/include/linux/iobuf.h	Tue Feb  5 16:28:54 2002
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <asm/atomic.h>
+#include <linux/bio.h>
 
 /*
  * The kiobuf structure describes a physical set of pages reserved
@@ -84,5 +85,42 @@
 
 /* fs/bio.c */
 void	ll_rw_kio(int rw, struct kiobuf *kio, kdev_t dev, sector_t block);
+
+
+typedef struct bio_vec kveclet_t;
+
+struct kvec {
+	unsigned	max_nr;
+	unsigned	nr;
+	kveclet_t	veclet[0];
+};
+
+struct kvec_cb {
+	struct kvec	*vec;
+	atomic_t	io_count;
+	int		errno;	
+	wait_queue_head_t wait_queue;
+};
+
+/* mm/memory.c */
+
+int	map_user_kvec(int rw, struct kvec *vec, unsigned long va, size_t len);
+void	unmap_kvec(struct kvec *vec);
+void	mark_dirty_kvec(struct kvec *vec, int bytes);
+
+/* fs/iobuf.c */
+
+int	end_kvec_request(struct kvec_cb *, int);
+struct kvec *alloc_kvec(int nr);
+void	free_kvec(struct kvec *);
+void	kvec_wait_for_io(struct kvec_cb *);
+
+/* fs/buffer.c */
+int	brw_kvec(int rw, int nr, struct kvec *vec, 
+		   kdev_t dev, sector_t [], int len[], int size);
+
+/* fs/bio.c */
+int	ll_rw_kvec(int rw, struct kvec_cb *vec_cb, kdev_t dev, int nr, sector_t b[], int len[]);
+
 
 #endif /* __LINUX_IOBUF_H */
diff -Naur -X dontdiff linux-2.5.3pre6.org/mm/filemap.c linux-2.5.3pre6/mm/filemap.c
--- linux-2.5.3pre6.org/mm/filemap.c	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/mm/filemap.c	Mon Feb  4 21:08:09 2002
@@ -1483,22 +1483,14 @@
 static ssize_t generic_file_direct_IO(int rw, struct file * filp, char * buf, size_t count, loff_t offset)
 {
 	ssize_t retval;
-	int new_iobuf, chunk_size, blocksize_mask, blocksize, blocksize_bits, iosize, progress;
-	struct kiobuf * iobuf;
+	int chunk_size, blocksize_mask, blocksize, blocksize_bits, iosize, progress;
+	struct kvec *vec;
 	struct address_space * mapping = filp->f_dentry->d_inode->i_mapping;
 	struct inode * inode = mapping->host;
 
-	new_iobuf = 0;
-	iobuf = filp->f_iobuf;
-	if (test_and_set_bit(0, &filp->f_iobuf_lock)) {
-		/*
-		 * A parallel read/write is using the preallocated iobuf
-		 * so just run slow and allocate a new one.
-		 */
-		retval = alloc_kiovec(1, &iobuf);
-		if (retval)
-			goto out;
-		new_iobuf = 1;
+	vec = alloc_kvec((count/PAGE_SIZE) + 1);
+	if (!vec) {
+		return -ENOMEM;
 	}
 
 	blocksize = 1 << inode->i_blkbits;
@@ -1528,14 +1520,14 @@
 		if (iosize > chunk_size)
 			iosize = chunk_size;
 
-		retval = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize);
+		retval = map_user_kvec(rw, vec, (unsigned long) buf, iosize);
 		if (retval)
 			break;
 
-		retval = mapping->a_ops->direct_IO(rw, inode, iobuf, (offset+progress) >> blocksize_bits, blocksize);
+		retval = mapping->a_ops->direct_IO(rw, inode, vec, (offset+progress) >> blocksize_bits, blocksize, iosize);
 
 		if (rw == READ && retval > 0)
-			mark_dirty_kiobuf(iobuf, retval);
+			mark_dirty_kvec(vec, retval);
 		
 		if (retval >= 0) {
 			count -= retval;
@@ -1543,7 +1535,7 @@
 			progress += retval;
 		}
 
-		unmap_kiobuf(iobuf);
+		unmap_kvec(vec);
 
 		if (retval != iosize)
 			break;
@@ -1553,10 +1545,7 @@
 		retval = progress;
 
  out_free:
-	if (!new_iobuf)
-		clear_bit(0, &filp->f_iobuf_lock);
-	else
-		free_kiovec(1, &iobuf);
+	free_kvec(vec);
  out:	
 	return retval;
 }
diff -Naur -X dontdiff linux-2.5.3pre6.org/mm/memory.c linux-2.5.3pre6/mm/memory.c
--- linux-2.5.3pre6.org/mm/memory.c	Mon Feb  4 21:20:28 2002
+++ linux-2.5.3pre6/mm/memory.c	Wed Feb  6 17:13:41 2002
@@ -1443,3 +1443,138 @@
 			len, write, 0, NULL, NULL);
 	return ret == len ? 0 : -1;
 }
+
+int get_user_pages_veclet(struct task_struct *tsk, struct mm_struct *mm, 
+		unsigned long start, int len, int write, int force, kveclet_t *vecl)
+{
+	int i = 0;
+	unsigned long offset;
+
+	offset = start & ~PAGE_MASK;
+
+	do {
+		struct vm_area_struct *	vma;
+
+		vma = find_extend_vma(mm, start);
+
+		if ( !vma ||
+		    (!force &&
+		     	((write && (!(vma->vm_flags & VM_WRITE))) ||
+		    	 (!write && (!(vma->vm_flags & VM_READ))) ) )) {
+			if (i) return i;
+			return -EFAULT;
+		}
+
+		spin_lock(&mm->page_table_lock);
+		do {
+			struct page *map;
+			while (!(map = follow_page(mm, start, write))) {
+				spin_unlock(&mm->page_table_lock);
+				switch (handle_mm_fault(mm, vma, start, write)) {
+				case 1:
+					tsk->min_flt++;
+					break;
+				case 2:
+					tsk->maj_flt++;
+					break;
+				case 0:
+					if (i) return i;
+					return -EFAULT;
+				default:
+					if (i) return i;
+					return -ENOMEM;
+				}
+				spin_lock(&mm->page_table_lock);
+			}
+			map = get_page_map(map);
+			if (map) {
+				vecl->bv_page = map;
+				page_cache_get(map);
+				flush_dcache_page(map);
+			} else 
+				printk (KERN_INFO "Mapped page missing\n");
+
+			vecl->bv_offset = offset;
+			vecl->bv_len = PAGE_SIZE - offset;
+			if (len < vecl->bv_len)
+				vecl->bv_len = len;
+			start += PAGE_SIZE;
+			offset = 0;
+			len -= vecl->bv_len;
+			vecl++;
+			i++;
+		} while(len && start < vma->vm_end);
+		spin_unlock(&mm->page_table_lock);
+	} while(len);
+	return i;
+}
+
+int map_user_kvec(int rw, struct kvec *vec, unsigned long va, size_t len)
+{
+	int pgcount, err;
+	struct mm_struct *mm;
+	
+	mm = current->mm;
+	
+	pgcount = (va + len + PAGE_SIZE - 1)/PAGE_SIZE - va/PAGE_SIZE;
+
+	if (!pgcount) BUG();
+
+	if (vec->nr + pgcount > vec->max_nr) 
+		return -ENOMEM;
+
+	/* Try to fault in all of the necessary pages */
+	down_read(&mm->mmap_sem);
+
+	/* rw==READ means read from disk, write into memory area */
+	err = get_user_pages_veclet(current, mm, va, len,
+			(rw==READ), 0, &vec->veclet[vec->nr]);
+
+	up_read(&mm->mmap_sem);
+
+	if (err < 0) {
+		unmap_kvec(vec);
+		return err;
+	}
+	vec->nr += err;
+
+	return 0;
+}
+
+void unmap_kvec (struct kvec *vec) 
+{
+	int i;
+	struct page *map;
+	kveclet_t *vecl;
+
+	vecl = vec->veclet;
+	
+	for (i = 0; i < vec->nr; i++, vecl++) {
+		map = vecl->bv_page;
+		if (map) {
+			page_cache_release(map);
+		}
+	}
+	
+	vec->nr = 0;
+}
+
+void mark_dirty_kvec(struct kvec *vec, int bytes)
+{
+	int index;
+	struct page *page;
+	kveclet_t *vecl;
+	
+	index = 0;
+	vecl = vec->veclet;
+	while (bytes > 0 && index < vec->max_nr) {
+		page = vecl->bv_page;
+		
+		if (!PageReserved(page))
+			SetPageDirty(page);
+
+		bytes -= vecl->bv_len;
+		index++;
+		vecl++;
+	}
+}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/