diff -urN vary-ref/drivers/block/ll_rw_blk.c vary/drivers/block/ll_rw_blk.c
--- vary-ref/drivers/block/ll_rw_blk.c	Mon Mar 11 05:53:35 2002
+++ vary/drivers/block/ll_rw_blk.c	Mon Mar 11 05:54:05 2002
@@ -119,6 +119,13 @@
 int * max_sectors[MAX_BLKDEV];
 
 /*
+ * blkdev_varyio indicates if variable size IO can be done on a device.
+ *
+ * Currently used for doing variable size IO on RAW devices.
+ */
+char * blkdev_varyio[MAX_BLKDEV];
+
+/*
  * The total number of requests in each queue.
  */
 static int queue_nr_requests;
@@ -1025,6 +1032,38 @@
 	 */
 	bh->b_rdev = bh->b_dev;
 	bh->b_rsector = bh->b_blocknr * count;
+
+	generic_make_request(rw, bh);
+
+	switch (rw) {
+		case WRITE:
+			kstat.pgpgout += count;
+			break;
+		default:
+			kstat.pgpgin += count;
+			break;
+	}
+}
+
+/*
+ * submit_bh_blknr() - same as submit_bh() except that b_rsector is
+ * set to b_blocknr. Used for RAW VARY.
+ */
+void submit_bh_blknr(int rw, struct buffer_head * bh)
+{
+	int count = bh->b_size >> 9;
+
+	if (!test_bit(BH_Lock, &bh->b_state))
+		BUG();
+
+	set_bit(BH_Req, &bh->b_state);
+
+	/*
+	 * First step, 'identity mapping' - RAID or LVM might
+	 * further remap this.
+	 */
+	bh->b_rdev = bh->b_dev;
+	bh->b_rsector = bh->b_blocknr;
 
 	generic_make_request(rw, bh);
 
diff -urN vary-ref/drivers/char/raw.c vary/drivers/char/raw.c
--- vary-ref/drivers/char/raw.c	Mon Mar 11 05:53:36 2002
+++ vary/drivers/char/raw.c	Mon Mar 11 05:54:05 2002
@@ -23,6 +23,7 @@
 	struct block_device *binding;
 	int inuse, sector_size, sector_bits;
 	struct semaphore mutex;
+	int can_do_vary;
 } raw_device_data_t;
 
 static raw_device_data_t raw_devices[256];
@@ -119,6 +120,8 @@
 	if (raw_devices[minor].inuse++)
 		goto out;
 
+	raw_devices[minor].can_do_vary = 
+			get_blkdev_varyio(MAJOR(rdev), MINOR(rdev));
 	/* 
 	 * Don't interfere with mounted devices: we cannot safely set
 	 * the blocksize on a device which is already mounted.  
@@ -128,6 +131,7 @@
 	if (is_mounted(rdev)) {
 		if (blksize_size[MAJOR(rdev)])
 			sector_size = blksize_size[MAJOR(rdev)][MINOR(rdev)];
+		 raw_devices[minor].can_do_vary = 0;
 	} else {
 		if (hardsect_size[MAJOR(rdev)])
 			sector_size = hardsect_size[MAJOR(rdev)][MINOR(rdev)];
@@ -135,6 +139,7 @@
 
 	set_blocksize(rdev, sector_size);
 	raw_devices[minor].sector_size = sector_size;
+	filp->f_iobuf->dovary = raw_devices[minor].can_do_vary;
 
 	for (sector_bits = 0; !(sector_size & 1); )
 		sector_size>>=1, sector_bits++;
@@ -322,6 +327,7 @@
 		if (err)
 			goto out;
 		new_iobuf = 1;
+		iobuf->dovary = raw_devices[minor].can_do_vary;
 	}
 
 	dev = to_kdev_t(raw_devices[minor].binding->bd_dev);
diff -urN vary-ref/drivers/scsi/aic7xxx/aic7xxx_linux_host.h vary/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
--- vary-ref/drivers/scsi/aic7xxx/aic7xxx_linux_host.h	Mon Mar 11 05:53:35 2002
+++ vary/drivers/scsi/aic7xxx/aic7xxx_linux_host.h	Mon Mar 11 05:54:05 2002
@@ -91,6 +91,7 @@
 	use_clustering: ENABLE_CLUSTERING,			\
 	use_new_eh_code: 1,					\
 	highmem_io: 1,						\
+	can_do_varyio: 1,					\
 }
 
 #endif /* _AIC7XXX_LINUX_HOST_H_ */
diff -urN vary-ref/drivers/scsi/hosts.h vary/drivers/scsi/hosts.h
--- vary-ref/drivers/scsi/hosts.h	Mon Mar 11 05:53:35 2002
+++ vary/drivers/scsi/hosts.h	Mon Mar 11 05:54:05 2002
@@ -294,6 +294,11 @@
     unsigned highmem_io:1;
 
     /*
+     * True for drivers which can handle variable length IO
+     */
+    unsigned can_do_varyio:1;
+
+    /*
      * Name of proc directory
      */
     char *proc_name;
diff -urN vary-ref/drivers/scsi/qlogicisp.h vary/drivers/scsi/qlogicisp.h
--- vary-ref/drivers/scsi/qlogicisp.h	Fri Nov 12 13:40:46 1999
+++ vary/drivers/scsi/qlogicisp.h	Mon Mar 11 05:54:05 2002
@@ -84,7 +84,8 @@
 	cmd_per_lun:		1,					   \
 	present:		0,					   \
 	unchecked_isa_dma:	0,					   \
-	use_clustering:		DISABLE_CLUSTERING			   \
+	use_clustering:		DISABLE_CLUSTERING,			   \
+	can_do_varyio:		1,					   \
 }
 
 #endif /* _QLOGICISP_H */
diff -urN vary-ref/drivers/scsi/sd.c vary/drivers/scsi/sd.c
--- vary-ref/drivers/scsi/sd.c	Mon Mar 11 05:53:36 2002
+++ vary/drivers/scsi/sd.c	Mon Mar 11 05:54:05 2002
@@ -91,6 +91,7 @@
 static int *sd_blocksizes;
 static int *sd_hardsizes;	/* Hardware sector size */
 static int *sd_max_sectors;
+static char *sd_varyio;
 
 static int check_scsidisk_media_change(kdev_t);
 static int fop_revalidate_scsidisk(kdev_t);
@@ -1111,6 +1112,12 @@
 	if (!sd_max_sectors)
 		goto cleanup_max_sectors;
 
+	sd_varyio = kmalloc((sd_template.dev_max << 4), GFP_ATOMIC);
+	if (!sd_varyio)
+		goto cleanup_varyio;
+
+	memset(sd_varyio, 0, (sd_template.dev_max << 4)); 
+
 	for (i = 0; i < sd_template.dev_max << 4; i++) {
 		sd_blocksizes[i] = 1024;
 		sd_hardsizes[i] = 512;
@@ -1181,6 +1188,8 @@
 cleanup_sd_gendisks:
 	kfree(sd);
 cleanup_sd:
+	kfree(sd_varyio);
+cleanup_varyio:
 	kfree(sd_max_sectors);
 cleanup_max_sectors:
 	kfree(sd_hardsizes);
@@ -1245,6 +1254,8 @@
 	return 1;
 }
 
+#define SD_DISK_MAJOR(i)	SD_MAJOR((i) >> 4)
+
 static int sd_attach(Scsi_Device * SDp)
 {
         unsigned int devnum;
@@ -1283,6 +1294,14 @@
 	printk("Attached scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n",
 	       SDp->removable ? "removable " : "",
 	       nbuff, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+	if (SDp->host->hostt->can_do_varyio) {
+		if (blkdev_varyio[SD_DISK_MAJOR(i)] == NULL) {
+			blkdev_varyio[SD_DISK_MAJOR(i)] = 
+				sd_varyio + ((i / SCSI_DISKS_PER_MAJOR) >> 8);
+		}
+		memset(blkdev_varyio[SD_DISK_MAJOR(i)] + (devnum << 4), 1, 16);
+	}
 	return 0;
 }
 
@@ -1411,6 +1430,7 @@
 		kfree(sd_sizes);
 		kfree(sd_blocksizes);
 		kfree(sd_hardsizes);
+		kfree(sd_varyio);
 		kfree((char *) sd);
 		for (i = 0; i < N_USED_SD_MAJORS; i++) {
 			kfree(sd_gendisks[i].de_arr);
diff -urN vary-ref/fs/buffer.c vary/fs/buffer.c
--- vary-ref/fs/buffer.c	Mon Mar 11 05:53:36 2002
+++ vary/fs/buffer.c	Mon Mar 11 05:54:05 2002
@@ -2250,11 +2250,11 @@
 	err = 0;
 
 	for (i = nr; --i >= 0; ) {
-		iosize += size;
 		tmp = bh[i];
 		if (buffer_locked(tmp)) {
 			wait_on_buffer(tmp);
 		}
+		iosize += tmp->b_size;
 		
 		if (!buffer_uptodate(tmp)) {
 			/* We are traversing bh'es in reverse order so
@@ -2296,6 +2296,7 @@
 	struct kiobuf *	iobuf = NULL;
 	struct page *	map;
 	struct buffer_head *tmp, **bhs = NULL;
+	int		iosize = size;
 
 	if (!nr)
 		return 0;
@@ -2332,7 +2333,7 @@
 			}
 			
 			while (length > 0) {
-				blocknr = b[bufind++];
+				blocknr = b[bufind];
 				if (blocknr == -1UL) {
 					if (rw == READ) {
 						/* there was an hole in the filesystem */
@@ -2345,9 +2346,15 @@
 					} else
 						BUG();
 				}
+				if (iobuf->dovary && ((offset & RAWIO_BLOCKMASK) == 0)) {
+					iosize = RAWIO_BLOCKSIZE; 
+					if (iosize > length)
+						iosize = length;
+				}
+				bufind += (iosize/size);
 				tmp = bhs[bhind++];
 
-				tmp->b_size = size;
+				tmp->b_size = iosize;
 				set_bh_page(tmp, map, offset);
 				tmp->b_this_page = tmp;
 
@@ -2363,7 +2370,10 @@
 					set_bit(BH_Uptodate, &tmp->b_state);
 
 				atomic_inc(&iobuf->io_count);
-				submit_bh(rw, tmp);
+				if (iobuf->dovary) 
+					submit_bh_blknr(rw, tmp);
+				else 
+					submit_bh(rw, tmp);
 				/* 
 				 * Wait for IO if we have got too much 
 				 */
@@ -2378,8 +2388,8 @@
 				}
 
 			skip_block:
-				length -= size;
-				offset += size;
+				length -= iosize;
+				offset += iosize;
 
 				if (offset >= PAGE_SIZE) {
 					offset = 0;
diff -urN vary-ref/include/linux/blkdev.h vary/include/linux/blkdev.h
--- vary-ref/include/linux/blkdev.h	Mon Mar 11 05:53:35 2002
+++ vary/include/linux/blkdev.h	Mon Mar 11 05:54:05 2002
@@ -213,6 +213,8 @@
 
 extern int * max_segments[MAX_BLKDEV];
 
+extern char * blkdev_varyio[MAX_BLKDEV];
+
 #define MAX_SEGMENTS 128
 #define MAX_SECTORS 255
 
@@ -266,4 +268,12 @@
 	return retval;
 }
 
+static inline int get_blkdev_varyio(int major, int minor)
+{
+	int retval = 0;
+	if (blkdev_varyio[major]) {
+		retval = blkdev_varyio[major][minor];	
+	}
+	return retval;
+}
 #endif
diff -urN vary-ref/include/linux/fs.h vary/include/linux/fs.h
--- vary-ref/include/linux/fs.h	Mon Mar 11 05:53:35 2002
+++ vary/include/linux/fs.h	Mon Mar 11 05:54:05 2002
@@ -1361,6 +1361,7 @@
 extern struct buffer_head * getblk(kdev_t, int, int);
 extern void ll_rw_block(int, int, struct buffer_head * bh[]);
 extern void submit_bh(int, struct buffer_head *);
+extern void submit_bh_blknr(int, struct buffer_head *);
 extern int is_read_only(kdev_t);
 extern void __brelse(struct buffer_head *);
 static inline void brelse(struct buffer_head *buf)
diff -urN vary-ref/include/linux/iobuf.h vary/include/linux/iobuf.h
--- vary-ref/include/linux/iobuf.h	Thu Mar  7 04:49:36 2002
+++ vary/include/linux/iobuf.h	Mon Mar 11 05:54:05 2002
@@ -28,6 +28,9 @@
 #define KIO_STATIC_PAGES	(KIO_MAX_ATOMIC_IO / (PAGE_SIZE >> 10) + 1)
 #define KIO_MAX_SECTORS		(KIO_MAX_ATOMIC_IO * 2)
 
+#define RAWIO_BLOCKSIZE		4096
+#define RAWIO_BLOCKMASK		(RAWIO_BLOCKSIZE-1)
+
 /* The main kiobuf struct used for all our IO! */
 
 struct kiobuf 
@@ -44,7 +47,8 @@
 
 	struct page **	maplist;
 
-	unsigned int	locked : 1;	/* If set, pages has been locked */
+	unsigned int	locked : 1,	/* If set, pages has been locked */
+			dovary : 1;	/* If set, do variable size IO */
 	
 	/* Always embed enough struct pages for atomic IO */
 	struct page *	map_array[KIO_STATIC_PAGES];
diff -urN vary-ref/kernel/ksyms.c vary/kernel/ksyms.c
--- vary-ref/kernel/ksyms.c	Mon Mar 11 05:53:35 2002
+++ vary/kernel/ksyms.c	Mon Mar 11 05:54:05 2002
@@ -315,6 +315,7 @@
 EXPORT_SYMBOL(refile_buffer);
 EXPORT_SYMBOL(max_sectors);
 EXPORT_SYMBOL(max_readahead);
+EXPORT_SYMBOL(blkdev_varyio);
 
 /* tty routines */
 EXPORT_SYMBOL(tty_hangup);