diff --git a/lustre/ChangeLog b/lustre/ChangeLog
index fa1b2aebb29293ff6e42786e2ea240b3adeb4af6..3c698ef2678dfc74669d8a2501ffd412be6f1c4a 100644
--- a/lustre/ChangeLog
+++ b/lustre/ChangeLog
@@ -1231,6 +1231,26 @@ Description: assertion failure in ldlm_handle2lock()
 Details    : fix a race between class_handle_unhash() and class_handle2object()
 	     introduced in lustre 1.6.5 by bug 13622.
 
+Severity   : minor
+Frequency  : rare
+Bugzilla   : 12755
+Description: Kernel BUG: sd_iostats_bump: unexpected disk index
+Details    : remove the limit of 256 scsi disks in the sd_iostat patch
+
+Severity   : minor
+Frequency  : rare
+Bugzilla   : 16494
+Description: oops in sd_iostats_seq_show()
+Details    : unloading/reloading the scsi low level driver triggers a kernel
+	     bug when trying to access the sd iostat file.
+
+Severity   : major
+Frequency  : rare
+Bugzilla   : 16404
+Description: Kernel panics during QLogic driver reload
+Details    : REQ_BLOCK_PC requests are not handled properly in the sd iostat
+	     patch, causing memory corruption.
+
 --------------------------------------------------------------------------------
 
 2007-08-10         Cluster File Systems, Inc. <info@clusterfs.com>
diff --git a/lustre/kernel_patches/patches/sd_iostats-2.6-rhel4.patch b/lustre/kernel_patches/patches/sd_iostats-2.6-rhel4.patch
index 33160d9915747b0cfe0d4c29d57dc908300c3338..954c445d72170af3ebdec80fd56add1aac579a3e 100644
--- a/lustre/kernel_patches/patches/sd_iostats-2.6-rhel4.patch
+++ b/lustre/kernel_patches/patches/sd_iostats-2.6-rhel4.patch
@@ -1,9 +1,7 @@
-Index: linux-2.6.9-5.0.3.EL/drivers/scsi/Kconfig
-===================================================================
-Index: linux-2.6.9/drivers/scsi/Kconfig
+Index: linux-2.6.9-67.0.20/drivers/scsi/Kconfig
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/Kconfig	2007-07-23 14:19:13.000000000 +0400
-+++ linux-2.6.9/drivers/scsi/Kconfig	2007-07-26 14:16:36.000000000 +0400
+--- linux-2.6.9-67.0.20.orig/drivers/scsi/Kconfig
++++ linux-2.6.9-67.0.20/drivers/scsi/Kconfig
 @@ -61,6 +61,14 @@ config SCSI_DUMP
  	help
  	   SCSI dump support
@@ -19,10 +17,10 @@ Index: linux-2.6.9/drivers/scsi/Kconfig
  config CHR_DEV_ST
  	tristate "SCSI tape support"
  	depends on SCSI
-Index: linux-2.6.9/drivers/scsi/scsi_proc.c
+Index: linux-2.6.9-67.0.20/drivers/scsi/scsi_proc.c
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/scsi_proc.c	2007-03-13 02:47:28.000000000 +0300
-+++ linux-2.6.9/drivers/scsi/scsi_proc.c	2007-07-26 14:16:36.000000000 +0400
+--- linux-2.6.9-67.0.20.orig/drivers/scsi/scsi_proc.c
++++ linux-2.6.9-67.0.20/drivers/scsi/scsi_proc.c
 @@ -38,7 +38,8 @@
  /* 4K page size, but our output routines, use some slack for overruns */
  #define PROC_BLOCK_SIZE (3*1024)
@@ -33,11 +31,11 @@ Index: linux-2.6.9/drivers/scsi/scsi_proc.c
  
  /* Protect sht->present and sht->proc_dir */
  static DECLARE_MUTEX(global_host_template_sem);
-Index: linux-2.6.9/drivers/scsi/sd.c
+Index: linux-2.6.9-67.0.20/drivers/scsi/sd.c
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/sd.c	2007-03-13 02:47:27.000000000 +0300
-+++ linux-2.6.9/drivers/scsi/sd.c	2007-07-28 14:55:56.000000000 +0400
-@@ -63,6 +63,67 @@
+--- linux-2.6.9-67.0.20.orig/drivers/scsi/sd.c
++++ linux-2.6.9-67.0.20/drivers/scsi/sd.c
+@@ -63,6 +63,63 @@
  
  #include "scsi_logging.h"
  
@@ -46,15 +44,15 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +# include <linux/seq_file.h>
 +
 +typedef struct {
-+        unsigned long long iostat_size;
-+        unsigned long long iostat_count;
++	unsigned long long iostat_size;
++	unsigned long long iostat_count;
 +} iostat_counter_t;
 +
 +#define IOSTAT_NCOUNTERS 16
 +typedef struct {
-+        iostat_counter_t        iostat_read_histogram[IOSTAT_NCOUNTERS];
-+        iostat_counter_t        iostat_write_histogram[IOSTAT_NCOUNTERS];
-+        struct timeval          iostat_timeval;
++	iostat_counter_t	iostat_read_histogram[IOSTAT_NCOUNTERS];
++	iostat_counter_t	iostat_write_histogram[IOSTAT_NCOUNTERS];
++	struct timeval		iostat_timeval;
 +
 +	/* queue depth: how well the pipe is filled up */
 +	unsigned long long	iostat_queue_ticks[IOSTAT_NCOUNTERS];
@@ -79,24 +77,20 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	unsigned long		iostat_rtime_in_queue[IOSTAT_NCOUNTERS];
 +	unsigned long		iostat_wtime_in_queue[IOSTAT_NCOUNTERS];
 +
-+	char			iostat_name[32];
-+
 +	/* must be the last field, as it's used to know size to be memset'ed */
-+	spinlock_t              iostat_lock;
-+}  ____cacheline_aligned_in_smp iostat_stats_t;
++	spinlock_t		iostat_lock;
++} ____cacheline_aligned_in_smp iostat_stats_t;
 +
-+iostat_stats_t       **sd_iostats;
-+struct proc_dir_entry *sd_iostats_procdir;
-+char                   sd_iostats_procdir_name[] = "sd_iostats";
++struct proc_dir_entry *sd_iostats_procdir = NULL;
++char sd_iostats_procdir_name[] = "sd_iostats";
++static struct file_operations sd_iostats_proc_fops;
 +
 +extern void sd_iostats_init(void);
-+extern void sd_iostats_init_disk(struct gendisk *);
 +extern void sd_iostats_fini(void);
 +void sd_iostats_start_req(struct scsi_cmnd *SCpnt);
 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt);
 +#else
 +static inline void sd_iostats_init(void) {}
-+static inline void sd_iostats_init_disk(struct gendisk *disk) {}
 +static inline void sd_iostats_fini(void) {}
 +static inline void sd_iostats_start_req(struct scsi_cmnd *SCpnt) {}
 +static inline void sd_iostats_finish_req(struct scsi_cmnd *SCpnt) {}
@@ -105,32 +99,26 @@ Index: linux-2.6.9/drivers/scsi/sd.c
  /*
   * More than enough for everybody ;)  The huge number of majors
   * is a leftover from 16bit dev_t days, we don't really need that
-@@ -76,6 +137,7 @@
-  */
- #define SD_MAX_DISKS	(((26 * 26) + 26 + 1) * 26)
+@@ -101,6 +158,9 @@ struct scsi_disk {
+ 	u8		write_prot;
+ 	unsigned	WCE : 1;	/* state of disk WCE bit */
+ 	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	iostat_stats_t	*stats;		/* scsi disk statistics */
++#endif
+ };
  
-+#define SD_STATS 256
- /*
-  * Time out in seconds for disks and Magneto-opticals (which are slower).
-  */
-@@ -278,6 +340,8 @@ static int sd_init_command(struct scsi_c
- 	SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n",
- 				   disk->disk_name, (unsigned long long)block));
+ static DEFINE_IDR(sd_index_idr);
+@@ -391,6 +451,8 @@ queue:
+ 	SCpnt->allowed = SD_MAX_RETRIES;
+ 	SCpnt->timeout_per_command = timeout;
  
 +	sd_iostats_start_req(SCpnt);
 +
  	/*
- 	 * If we have a 1K hardware sectorsize, prevent access to single
- 	 * 512 byte sectors.  In theory we could handle this - in fact
-@@ -474,6 +538,7 @@ static int sd_open(struct inode *inode, 
- 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
- 	}
- 
-+   sd_iostats_init_disk(disk);
- 	return 0;
- 
- error_out:
-@@ -849,6 +914,9 @@ static void sd_rw_intr(struct scsi_cmnd 
+ 	 * This is the completion routine we use.  This is matched in terms
+ 	 * of capability to this function.
+@@ -849,6 +911,9 @@ static void sd_rw_intr(struct scsi_cmnd 
  			break;
  		}
  	}
@@ -140,7 +128,60 @@ Index: linux-2.6.9/drivers/scsi/sd.c
  	/*
  	 * This calls the generic completion function, now that we know
  	 * how many actual sectors finished, and how many sectors we need
-@@ -1575,6 +1643,481 @@ static void sd_shutdown(struct device *d
+@@ -1487,6 +1552,36 @@ static int sd_probe(struct device *dev)
+ 		gd->flags |= GENHD_FL_REMOVABLE;
+ 	gd->queue = sdkp->device->request_queue;
+ 
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	sdkp->stats = kzalloc(sizeof(iostat_stats_t), GFP_KERNEL);
++	if (!sdkp->stats) {
++		printk(KERN_WARNING "cannot allocate iostat structure for"
++				    "%s\n", gd->disk_name);
++	} else {
++		do_gettimeofday(&sdkp->stats->iostat_timeval);
++		sdkp->stats->iostat_queue_stamp = jiffies;
++		spin_lock_init(&sdkp->stats->iostat_lock);
++		if (sd_iostats_procdir) {
++			struct proc_dir_entry *pde;
++			pde = create_proc_entry(gd->disk_name, S_IRUGO | S_IWUSR,
++						sd_iostats_procdir);
++			if (!pde) {
++				printk(KERN_WARNING "Can't create /proc/scsi/"
++						    "%s/%s\n",
++						    sd_iostats_procdir_name,
++						    gd->disk_name);
++				kfree(sdkp->stats);
++				sdkp->stats = NULL;
++			} else {
++				pde->proc_fops = &sd_iostats_proc_fops;
++				pde->data = gd;
++			}
++		} else {
++			kfree(sdkp->stats);
++			sdkp->stats = NULL;
++		}
++	}
++#endif
+ 	dev_set_drvdata(dev, sdkp);
+ 	add_disk(gd);
+ 
+@@ -1549,8 +1644,14 @@ static void scsi_disk_release(struct kre
+ 
+ 	disk->private_data = NULL;
+ 
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	if (sdkp->stats) {
++		remove_proc_entry(disk->disk_name, sd_iostats_procdir);
++		kfree(sdkp->stats);
++		sdkp->stats = NULL;
++	}
++#endif
+ 	put_disk(disk);
+-
+ 	kfree(sdkp);
+ }
+ 
+@@ -1575,6 +1676,366 @@ static void sd_shutdown(struct device *d
  	sd_sync_cache(sdp);
  }	
  
@@ -162,12 +203,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int                i;
 +	int                maxi;
 +
-+	if (sd_iostats == NULL) {
-+		printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
-+		BUG();
-+	}
-+
-+	stats = sd_iostats[scsi_disk(disk)->index];
++	stats = scsi_disk(disk)->stats;
 +	if (stats == NULL) {
 +		printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n");
 +		BUG();
@@ -314,7 +350,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +static int
 +sd_iostats_seq_open (struct inode *inode, struct file *file)
 +{
-+	int                    rc;
++	int rc;
 +
 +	rc = seq_open(file, &sd_iostats_seqops);
 +	if (rc != 0)
@@ -326,11 +362,11 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +
 +static ssize_t
 +sd_iostats_seq_write(struct file *file, const char *buffer,
-+                     size_t len, loff_t *off)
++		     size_t len, loff_t *off)
 +{
 +	struct seq_file   *seq = file->private_data;
 +	struct gendisk *disk = seq->private;
-+	iostat_stats_t    *stats = sd_iostats[scsi_disk(disk)->index];
++	iostat_stats_t    *stats = scsi_disk(disk)->stats;
 +	unsigned long      flags;
 +	unsigned long      qdepth;
 +
@@ -360,19 +396,6 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +void
 +sd_iostats_init(void)
 +{
-+	int    i;
-+
-+	sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL);
-+	if (sd_iostats == NULL) {
-+		printk(KERN_WARNING "Can't keep sd iostats: "
-+			"ENOMEM allocating stats array size %d\n",
-+			SD_STATS * sizeof(iostat_stats_t *));
-+		return;
-+	}
-+
-+	for (i = 0; i < SD_STATS; i++)
-+		sd_iostats[i] = NULL;
-+
 +	if (proc_scsi == NULL) {
 +		printk(KERN_WARNING "No access to sd iostats: "
 +			"proc_scsi is NULL\n");
@@ -386,91 +409,15 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +		printk(KERN_WARNING "No access to sd iostats: "
 +			"can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
 +		return;
-+        }
-+}
-+
-+void
-+sd_iostats_init_disk(struct gendisk *disk)
-+{
-+	struct proc_dir_entry *pde;
-+	unsigned long          flags;
-+	iostat_stats_t        *stats;
-+
-+	if (sd_iostats == NULL || sd_iostats_procdir == NULL)
-+		return;
-+
-+	if (scsi_disk(disk)->index > SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_init_disk: "
-+			"unexpected disk index %d(%d)\n",
-+			scsi_disk(disk)->index, SD_STATS);
-+		return;
-+	}
-+
-+	if (sd_iostats[scsi_disk(disk)->index] != NULL)
-+		return;
-+
-+	stats = kmalloc(sizeof(*stats), GFP_KERNEL);
-+	if (stats == NULL) {
-+		printk(KERN_WARNING "Can't keep %s iostats: "
-+			"ENOMEM allocating stats size %d\n", 
-+			disk->disk_name, sizeof(*stats));
-+		return;
-+	}
-+
-+	memset (stats, 0, sizeof(*stats));
-+	do_gettimeofday(&stats->iostat_timeval);
-+	stats->iostat_queue_stamp = jiffies;
-+	spin_lock_init(&stats->iostat_lock);
-+
-+
-+	spin_lock_irqsave(&stats->iostat_lock, flags);
-+
-+	if (sd_iostats[scsi_disk(disk)->index] != NULL) {
-+		spin_unlock_irqrestore(&stats->iostat_lock, flags);
-+		kfree (stats);
-+		return;
-+	}
-+
-+	sd_iostats[scsi_disk(disk)->index] = stats;
-+
-+	spin_unlock_irqrestore(&stats->iostat_lock, flags);
-+
-+	strncpy(stats->iostat_name, disk->disk_name,
-+		sizeof(stats->iostat_name)-1);
-+
-+	pde = create_proc_entry(stats->iostat_name, S_IRUGO | S_IWUSR,
-+				sd_iostats_procdir);
-+	if (pde == NULL) {
-+		printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
-+			sd_iostats_procdir_name, disk->disk_name);
-+	} else {
-+		pde->proc_fops = &sd_iostats_proc_fops;
-+		pde->data = disk;
 +	}
 +}
 +
 +void sd_iostats_fini(void)
 +{
-+	int  i;
-+
-+	if (sd_iostats == NULL)
-+		return;
-+
-+	for (i = 0; i < SD_STATS; i++) {
-+		if (sd_iostats[i] == NULL)
-+			continue;
-+		if (sd_iostats_procdir != NULL)
-+			remove_proc_entry(sd_iostats[i]->iostat_name,
-+						sd_iostats_procdir);
-+		kfree(sd_iostats[i]);
-+	}
-+
 +	if (proc_scsi != NULL && sd_iostats_procdir != NULL)
 +		remove_proc_entry(sd_iostats_procdir_name, proc_scsi);
 +
 +	sd_iostats_procdir = NULL;
-+	kfree(sd_iostats);
-+	sd_iostats = NULL;
 +}
 +
 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt)
@@ -481,20 +428,9 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int			tbucket;
 +	int			tmp;
 +	unsigned long		irqflags;
-+	int			disk, i;
-+
-+	disk = scsi_disk(rq->rq_disk)->index;
-+
-+	if (sd_iostats == NULL)
-+		return;
-+
-+	if (disk < 0 || disk >= SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_bump: unexpected disk index "
-+		        "%d([0-%d])\n", disk, SD_STATS);
-+		BUG();
-+	}
++	unsigned long		i;
 +
-+	stats = sd_iostats[disk];
++	stats = scsi_disk(rq->rq_disk)->stats;
 +	if (stats == NULL)
 +		return;
 +
@@ -519,6 +455,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +		i = IOSTAT_NCOUNTERS - 1;
 +	stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
 +	stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
++	BUG_ON(stats->iostat_queue_depth == 0);
 +	stats->iostat_queue_depth--;
 +
 +	/* update seek stats. XXX: not sure about nr_sectors */
@@ -547,21 +484,10 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int			tbucket;
 +	int			tmp;
 +	unsigned long		irqflags;
-+	int			disk, i;
++	unsigned long		i;
 +	int			nsect;
 +
-+	disk = scsi_disk(rq->rq_disk)->index;
-+
-+	if (sd_iostats == NULL)
-+		return;
-+
-+	if (disk < 0 || disk >= SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
-+			disk, SD_STATS);
-+		BUG();
-+	}
-+
-+	stats = sd_iostats[disk];
++	stats = scsi_disk(rq->rq_disk)->stats;
 +	if (stats == NULL)
 +		return;
 +
@@ -622,31 +548,32 @@ Index: linux-2.6.9/drivers/scsi/sd.c
  /**
   *	init_sd - entry point for this driver (both when built in or when
   *	a module).
-@@ -1584,6 +2127,7 @@ static void sd_shutdown(struct device *d
+@@ -1584,6 +2045,7 @@ static void sd_shutdown(struct device *d
  static int __init init_sd(void)
  {
  	int majors = 0, i;
-+   int rc = 0;
++	int rc = 0;
  
  	SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
  
-@@ -1594,7 +2138,10 @@ static int __init init_sd(void)
+@@ -1594,7 +2056,11 @@ static int __init init_sd(void)
  	if (!majors)
  		return -ENODEV;
  
 -	return scsi_register_driver(&sd_template.gendrv);
-+   rc = scsi_register_driver(&sd_template.gendrv);
-+   if (rc == 0)
-+      sd_iostats_init();
-+   return rc;
++	sd_iostats_init();
++	rc = scsi_register_driver(&sd_template.gendrv);
++	if (rc)
++		sd_iostats_fini();
++	return rc;
  }
  
  /**
-@@ -1608,6 +2155,7 @@ static void __exit exit_sd(void)
- 
- 	SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
- 
-+   sd_iostats_fini();
+@@ -1611,6 +2077,7 @@ static void __exit exit_sd(void)
  	scsi_unregister_driver(&sd_template.gendrv);
  	for (i = 0; i < SD_MAJORS; i++)
  		unregister_blkdev(sd_major(i), "sd");
++	sd_iostats_fini();
+ }
+ 
+ MODULE_LICENSE("GPL");
diff --git a/lustre/kernel_patches/patches/sd_iostats-2.6-rhel5.patch b/lustre/kernel_patches/patches/sd_iostats-2.6-rhel5.patch
index e38e22af27253235ca8e0c355c166cb92f4d609b..d0cc6f62eceae0f06a94cc173b1e23d90e3e4d3d 100644
--- a/lustre/kernel_patches/patches/sd_iostats-2.6-rhel5.patch
+++ b/lustre/kernel_patches/patches/sd_iostats-2.6-rhel5.patch
@@ -1,12 +1,10 @@
-Index: linux-2.6.9-5.0.3.EL/drivers/scsi/Kconfig
-===================================================================
-Index: linux-2.6.9/drivers/scsi/Kconfig
+Index: linux-2.6.18-53.1.21/drivers/scsi/Kconfig
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/Kconfig	2007-07-23 14:19:13.000000000 +0400
-+++ linux-2.6.9/drivers/scsi/Kconfig	2007-07-26 14:16:36.000000000 +0400
-@@ -61,6 +61,14 @@ config SCSI_DUMP
- 	help
- 	   SCSI dump support
+--- linux-2.6.18-53.1.21.orig/drivers/scsi/Kconfig
++++ linux-2.6.18-53.1.21/drivers/scsi/Kconfig
+@@ -66,6 +66,14 @@ config BLK_DEV_SD
+ 	  In this case, do not compile the driver for your SCSI host adapter
+ 	  (below) as a module either.
  
 +config SD_IOSTATS
 +   bool "Enable SCSI disk I/O stats"
@@ -19,11 +17,11 @@ Index: linux-2.6.9/drivers/scsi/Kconfig
  config CHR_DEV_ST
  	tristate "SCSI tape support"
  	depends on SCSI
-Index: linux-2.6.9/drivers/scsi/scsi_proc.c
+Index: linux-2.6.18-53.1.21/drivers/scsi/scsi_proc.c
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/scsi_proc.c	2007-03-13 02:47:28.000000000 +0300
-+++ linux-2.6.9/drivers/scsi/scsi_proc.c	2007-07-26 14:16:36.000000000 +0400
-@@ -38,7 +38,8 @@
+--- linux-2.6.18-53.1.21.orig/drivers/scsi/scsi_proc.c
++++ linux-2.6.18-53.1.21/drivers/scsi/scsi_proc.c
+@@ -40,7 +40,8 @@
  /* 4K page size, but our output routines, use some slack for overruns */
  #define PROC_BLOCK_SIZE (3*1024)
  
@@ -32,12 +30,12 @@ Index: linux-2.6.9/drivers/scsi/scsi_proc.c
 +EXPORT_SYMBOL(proc_scsi);
  
  /* Protect sht->present and sht->proc_dir */
- static DECLARE_MUTEX(global_host_template_sem);
-Index: linux-2.6.9/drivers/scsi/sd.c
+ static DEFINE_MUTEX(global_host_template_mutex);
+Index: linux-2.6.18-53.1.21/drivers/scsi/sd.c
 ===================================================================
---- linux-2.6.9.orig/drivers/scsi/sd.c	2007-03-13 02:47:27.000000000 +0300
-+++ linux-2.6.9/drivers/scsi/sd.c	2007-07-28 14:55:56.000000000 +0400
-@@ -63,6 +63,67 @@
+--- linux-2.6.18-53.1.21.orig/drivers/scsi/sd.c
++++ linux-2.6.18-53.1.21/drivers/scsi/sd.c
+@@ -62,6 +62,63 @@
  
  #include "scsi_logging.h"
  
@@ -46,15 +44,15 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +# include <linux/seq_file.h>
 +
 +typedef struct {
-+        unsigned long long iostat_size;
-+        unsigned long long iostat_count;
++	unsigned long long iostat_size;
++	unsigned long long iostat_count;
 +} iostat_counter_t;
 +
 +#define IOSTAT_NCOUNTERS 16
 +typedef struct {
-+        iostat_counter_t        iostat_read_histogram[IOSTAT_NCOUNTERS];
-+        iostat_counter_t        iostat_write_histogram[IOSTAT_NCOUNTERS];
-+        struct timeval          iostat_timeval;
++	iostat_counter_t	iostat_read_histogram[IOSTAT_NCOUNTERS];
++	iostat_counter_t	iostat_write_histogram[IOSTAT_NCOUNTERS];
++	struct timeval		iostat_timeval;
 +
 +	/* queue depth: how well the pipe is filled up */
 +	unsigned long long	iostat_queue_ticks[IOSTAT_NCOUNTERS];
@@ -79,24 +77,20 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	unsigned long		iostat_rtime_in_queue[IOSTAT_NCOUNTERS];
 +	unsigned long		iostat_wtime_in_queue[IOSTAT_NCOUNTERS];
 +
-+	char			iostat_name[32];
-+
 +	/* must be the last field, as it's used to know size to be memset'ed */
-+	spinlock_t              iostat_lock;
-+}  ____cacheline_aligned_in_smp iostat_stats_t;
++	spinlock_t		iostat_lock;
++} ____cacheline_aligned_in_smp iostat_stats_t;
 +
-+iostat_stats_t       **sd_iostats;
-+struct proc_dir_entry *sd_iostats_procdir;
-+char                   sd_iostats_procdir_name[] = "sd_iostats";
++struct proc_dir_entry *sd_iostats_procdir = NULL;
++char sd_iostats_procdir_name[] = "sd_iostats";
++static struct file_operations sd_iostats_proc_fops;
 +
 +extern void sd_iostats_init(void);
-+extern void sd_iostats_init_disk(struct gendisk *);
 +extern void sd_iostats_fini(void);
 +void sd_iostats_start_req(struct scsi_cmnd *SCpnt);
 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt);
 +#else
 +static inline void sd_iostats_init(void) {}
-+static inline void sd_iostats_init_disk(struct gendisk *disk) {}
 +static inline void sd_iostats_fini(void) {}
 +static inline void sd_iostats_start_req(struct scsi_cmnd *SCpnt) {}
 +static inline void sd_iostats_finish_req(struct scsi_cmnd *SCpnt) {}
@@ -105,42 +99,73 @@ Index: linux-2.6.9/drivers/scsi/sd.c
  /*
   * More than enough for everybody ;)  The huge number of majors
   * is a leftover from 16bit dev_t days, we don't really need that
-@@ -76,6 +137,7 @@
-  */
- #define SD_MAX_DISKS	(((26 * 26) + 26 + 1) * 26)
+@@ -126,6 +183,9 @@ struct scsi_disk {
+ 	unsigned	WCE : 1;	/* state of disk WCE bit */
+ 	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
+ 	unsigned	DPOFUA : 1;	/* state of disk DPOFUA bit */
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	iostat_stats_t	*stats;		/* scsi disk statistics */
++#endif
+ };
+ #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev)
  
-+#define SD_STATS 256
- /*
-  * Time out in seconds for disks and Magneto-opticals (which are slower).
-  */
-@@ -278,6 +340,8 @@ static int sd_init_command(struct scsi_c
- 	SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n",
- 				   disk->disk_name, (unsigned long long)block));
+@@ -557,6 +617,8 @@ static int sd_init_command(struct scsi_c
+ 	 */
+ 	SCpnt->done = sd_rw_intr;
  
 +	sd_iostats_start_req(SCpnt);
 +
  	/*
- 	 * If we have a 1K hardware sectorsize, prevent access to single
- 	 * 512 byte sectors.  In theory we could handle this - in fact
-@@ -474,6 +538,7 @@ static int sd_open(struct inode *inode, 
- 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
- 	}
- 
-+   sd_iostats_init_disk(disk);
- 	return 0;
- 
- error_out:
-@@ -849,6 +914,7 @@ static void sd_rw_intr(struct scsi_cmnd 
+ 	 * This indicates that the command is ready from our end to be
+ 	 * queued.
+@@ -1040,6 +1102,7 @@ static void sd_rw_intr(struct scsi_cmnd 
  		break;
  	}
   out:
 +	sd_iostats_finish_req(SCpnt);
  	scsi_io_completion(SCpnt, good_bytes);
  }
-
-@@ -1575,6 +1643,481 @@ static void sd_shutdown(struct device *d
- 	sd_sync_cache(sdp);
- }	
+ 
+@@ -1735,6 +1798,36 @@ static int sd_probe(struct device *dev)
+ 	if (sdp->removable)
+ 		gd->flags |= GENHD_FL_REMOVABLE;
+ 
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	sdkp->stats = kzalloc(sizeof(iostat_stats_t), GFP_KERNEL);
++	if (!sdkp->stats) {
++		printk(KERN_WARNING "cannot allocate iostat structure for"
++				    "%s\n", gd->disk_name);
++	} else {
++		do_gettimeofday(&sdkp->stats->iostat_timeval);
++		sdkp->stats->iostat_queue_stamp = jiffies;
++		spin_lock_init(&sdkp->stats->iostat_lock);
++		if (sd_iostats_procdir) {
++			struct proc_dir_entry *pde;
++			pde = create_proc_entry(gd->disk_name, S_IRUGO | S_IWUSR,
++					        sd_iostats_procdir);
++			if (!pde) {
++				printk(KERN_WARNING "Can't create /proc/scsi/"
++						    "%s/%s\n",
++						    sd_iostats_procdir_name,
++						    gd->disk_name);
++				kfree(sdkp->stats);
++				sdkp->stats = NULL;
++			} else {
++				pde->proc_fops = &sd_iostats_proc_fops;
++				pde->data = gd;
++			}
++		} else {
++			kfree(sdkp->stats);
++			sdkp->stats = NULL;
++		}
++	}
++#endif
+ 	dev_set_drvdata(dev, sdkp);
+ 	add_disk(gd);
+ 
+@@ -1778,6 +1871,366 @@ static int sd_remove(struct device *dev)
+ 	return 0;
+ }
  
 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
 +static int
@@ -160,12 +185,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int                i;
 +	int                maxi;
 +
-+	if (sd_iostats == NULL) {
-+		printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
-+		BUG();
-+	}
-+
-+	stats = sd_iostats[scsi_disk(disk)->index];
++	stats = scsi_disk(disk)->stats;
 +	if (stats == NULL) {
 +		printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n");
 +		BUG();
@@ -312,7 +332,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +static int
 +sd_iostats_seq_open (struct inode *inode, struct file *file)
 +{
-+	int                    rc;
++	int rc;
 +
 +	rc = seq_open(file, &sd_iostats_seqops);
 +	if (rc != 0)
@@ -324,11 +344,11 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +
 +static ssize_t
 +sd_iostats_seq_write(struct file *file, const char *buffer,
-+                     size_t len, loff_t *off)
++		     size_t len, loff_t *off)
 +{
 +	struct seq_file   *seq = file->private_data;
 +	struct gendisk *disk = seq->private;
-+	iostat_stats_t    *stats = sd_iostats[scsi_disk(disk)->index];
++	iostat_stats_t    *stats = scsi_disk(disk)->stats;
 +	unsigned long      flags;
 +	unsigned long      qdepth;
 +
@@ -358,19 +378,6 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +void
 +sd_iostats_init(void)
 +{
-+	int    i;
-+
-+	sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL);
-+	if (sd_iostats == NULL) {
-+		printk(KERN_WARNING "Can't keep sd iostats: "
-+			"ENOMEM allocating stats array size %d\n",
-+			SD_STATS * sizeof(iostat_stats_t *));
-+		return;
-+	}
-+
-+	for (i = 0; i < SD_STATS; i++)
-+		sd_iostats[i] = NULL;
-+
 +	if (proc_scsi == NULL) {
 +		printk(KERN_WARNING "No access to sd iostats: "
 +			"proc_scsi is NULL\n");
@@ -378,97 +385,21 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	}
 +
 +	sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
-+			S_IFDIR | S_IRUGO | S_IXUGO,
-+			proc_scsi);
++					       S_IFDIR | S_IRUGO | S_IXUGO,
++					        proc_scsi);
 +	if (sd_iostats_procdir == NULL) {
 +		printk(KERN_WARNING "No access to sd iostats: "
 +			"can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
 +		return;
-+        }
-+}
-+
-+void
-+sd_iostats_init_disk(struct gendisk *disk)
-+{
-+	struct proc_dir_entry *pde;
-+	unsigned long          flags;
-+	iostat_stats_t        *stats;
-+
-+	if (sd_iostats == NULL || sd_iostats_procdir == NULL)
-+		return;
-+
-+	if (scsi_disk(disk)->index > SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_init_disk: "
-+			"unexpected disk index %d(%d)\n",
-+			scsi_disk(disk)->index, SD_STATS);
-+		return;
-+	}
-+
-+	if (sd_iostats[scsi_disk(disk)->index] != NULL)
-+		return;
-+
-+	stats = kmalloc(sizeof(*stats), GFP_KERNEL);
-+	if (stats == NULL) {
-+		printk(KERN_WARNING "Can't keep %s iostats: "
-+			"ENOMEM allocating stats size %d\n", 
-+			disk->disk_name, sizeof(*stats));
-+		return;
-+	}
-+
-+	memset (stats, 0, sizeof(*stats));
-+	do_gettimeofday(&stats->iostat_timeval);
-+	stats->iostat_queue_stamp = jiffies;
-+	spin_lock_init(&stats->iostat_lock);
-+
-+
-+	spin_lock_irqsave(&stats->iostat_lock, flags);
-+
-+	if (sd_iostats[scsi_disk(disk)->index] != NULL) {
-+		spin_unlock_irqrestore(&stats->iostat_lock, flags);
-+		kfree (stats);
-+		return;
-+	}
-+
-+	sd_iostats[scsi_disk(disk)->index] = stats;
-+
-+	spin_unlock_irqrestore(&stats->iostat_lock, flags);
-+
-+	strncpy(stats->iostat_name, disk->disk_name,
-+		sizeof(stats->iostat_name)-1);
-+
-+	pde = create_proc_entry(stats->iostat_name, S_IRUGO | S_IWUSR,
-+				sd_iostats_procdir);
-+	if (pde == NULL) {
-+		printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
-+			sd_iostats_procdir_name, disk->disk_name);
-+	} else {
-+		pde->proc_fops = &sd_iostats_proc_fops;
-+		pde->data = disk;
 +	}
 +}
 +
 +void sd_iostats_fini(void)
 +{
-+	int  i;
-+
-+	if (sd_iostats == NULL)
-+		return;
-+
-+	for (i = 0; i < SD_STATS; i++) {
-+		if (sd_iostats[i] == NULL)
-+			continue;
-+		if (sd_iostats_procdir != NULL)
-+			remove_proc_entry(sd_iostats[i]->iostat_name,
-+						sd_iostats_procdir);
-+		kfree(sd_iostats[i]);
-+	}
-+
 +	if (proc_scsi != NULL && sd_iostats_procdir != NULL)
 +		remove_proc_entry(sd_iostats_procdir_name, proc_scsi);
 +
 +	sd_iostats_procdir = NULL;
-+	kfree(sd_iostats);
-+	sd_iostats = NULL;
 +}
 +
 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt)
@@ -479,31 +410,20 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int			tbucket;
 +	int			tmp;
 +	unsigned long		irqflags;
-+	int			disk, i;
-+
-+	disk = scsi_disk(rq->rq_disk)->index;
-+
-+	if (sd_iostats == NULL)
-+		return;
++	unsigned long		i;
 +
-+	if (disk < 0 || disk >= SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_bump: unexpected disk index "
-+		        "%d([0-%d])\n", disk, SD_STATS);
-+		BUG();
-+	}
-+
-+	stats = sd_iostats[disk];
++	stats = scsi_disk(rq->rq_disk)->stats;
 +	if (stats == NULL)
 +		return;
 +
-+	tmp = jiffies -  rq->start_time;
++	tmp = jiffies - rq->start_time;
 +	for (tbucket = 0; tmp > 1; tbucket++)
 +		tmp >>= 1;
 +	if (tbucket >= IOSTAT_NCOUNTERS)
 +		tbucket = IOSTAT_NCOUNTERS - 1;
 +	//printk("%u ticks in D to %u\n", jiffies - rq->start_time, tbucket);
 +
-+	tcounter = rq_data_dir(rq) == WRITE ? 
++	tcounter = rq_data_dir(rq) == WRITE ?
 +		&stats->iostat_wtime[tbucket] : &stats->iostat_rtime[tbucket];
 +
 +	spin_lock_irqsave(&stats->iostat_lock, irqflags);
@@ -517,13 +437,14 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +		i = IOSTAT_NCOUNTERS - 1;
 +	stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
 +	stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
++	BUG_ON(stats->iostat_queue_depth == 0);
 +	stats->iostat_queue_depth--;
 +
 +	/* update seek stats. XXX: not sure about nr_sectors */
 +	stats->iostat_sectors += rq->nr_sectors;
 +	stats->iostat_reqs++;
 +	if (rq->sector != stats->iostat_next_sector) {
-+		stats->iostat_seek_sectors += 
++		stats->iostat_seek_sectors +=
 +			rq->sector > stats->iostat_next_sector ?
 +			rq->sector - stats->iostat_next_sector :
 +			stats->iostat_next_sector - rq->sector;
@@ -545,21 +466,10 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +	int			tbucket;
 +	int			tmp;
 +	unsigned long		irqflags;
-+	int			disk, i;
++	unsigned long		i;
 +	int			nsect;
 +
-+	disk = scsi_disk(rq->rq_disk)->index;
-+
-+	if (sd_iostats == NULL)
-+		return;
-+
-+	if (disk < 0 || disk >= SD_STATS) {
-+		printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
-+			disk, SD_STATS);
-+		BUG();
-+	}
-+
-+	stats = sd_iostats[disk];
++	stats = scsi_disk(rq->rq_disk)->stats;
 +	if (stats == NULL)
 +		return;
 +
@@ -572,7 +482,7 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +		BUG();
 +	}
 +
-+	counter = rq_data_dir(rq) == WRITE ? 
++	counter = rq_data_dir(rq) == WRITE ?
 +		&stats->iostat_write_histogram[bucket] :
 +		&stats->iostat_read_histogram[bucket];
 +
@@ -618,33 +528,54 @@ Index: linux-2.6.9/drivers/scsi/sd.c
 +#endif
 +
  /**
-  *	init_sd - entry point for this driver (both when built in or when
-  *	a module).
-@@ -1584,6 +2127,7 @@ static void sd_shutdown(struct device *d
+  *	scsi_disk_release - Called to free the scsi_disk structure
+  *	@cdev: pointer to embedded class device
+@@ -1796,10 +2249,16 @@ static void scsi_disk_release(struct cla
+ 	idr_remove(&sd_index_idr, sdkp->index);
+ 	spin_unlock(&sd_index_lock);
+ 
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	if (sdkp->stats) {
++		remove_proc_entry(disk->disk_name, sd_iostats_procdir);
++		kfree(sdkp->stats);
++		sdkp->stats = NULL;
++	}
++#endif
+ 	disk->private_data = NULL;
+ 	put_disk(disk);
+ 	put_device(&sdkp->device->sdev_gendev);
+-
+ 	kfree(sdkp);
+ }
+ 
+@@ -1907,6 +2366,7 @@ done:
  static int __init init_sd(void)
  {
  	int majors = 0, i;
-+   int rc = 0;
++	int rc = 0;
  
  	SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
  
-@@ -1594,7 +2138,10 @@ static int __init init_sd(void)
+@@ -1917,9 +2377,13 @@ static int __init init_sd(void)
  	if (!majors)
  		return -ENODEV;
  
++	sd_iostats_init();
+ 	class_register(&sd_disk_class);
+ 
 -	return scsi_register_driver(&sd_template.gendrv);
-+   rc = scsi_register_driver(&sd_template.gendrv);
-+   if (rc == 0)
-+      sd_iostats_init();
-+   return rc;
++	rc = scsi_register_driver(&sd_template.gendrv);
++	if (rc)
++		sd_iostats_fini();
++	return rc;
  }
  
  /**
-@@ -1608,6 +2155,7 @@ static void __exit exit_sd(void)
+@@ -1938,6 +2402,7 @@ static void __exit exit_sd(void)
+ 		unregister_blkdev(sd_major(i), "sd");
  
- 	SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
+ 	class_unregister(&sd_disk_class);
++	sd_iostats_fini();
+ }
  
-+   sd_iostats_fini();
- 	scsi_unregister_driver(&sd_template.gendrv);
- 	for (i = 0; i < SD_MAJORS; i++)
- 		unregister_blkdev(sd_major(i), "sd");
+ module_init(init_sd);
diff --git a/lustre/kernel_patches/patches/sd_iostats-2.6.22-vanilla.patch b/lustre/kernel_patches/patches/sd_iostats-2.6.22-vanilla.patch
index a16c9578fa629b1736dd663f2fab3287457215d0..9e822d27ca9e0f2849dfa53c651ae99e15602972 100644
--- a/lustre/kernel_patches/patches/sd_iostats-2.6.22-vanilla.patch
+++ b/lustre/kernel_patches/patches/sd_iostats-2.6.22-vanilla.patch
@@ -1,7 +1,7 @@
-Index: linux-2.6.22.5/drivers/scsi/Kconfig
+Index: linux-2.6.22.19/drivers/scsi/Kconfig
 ===================================================================
---- linux-2.6.22.5.orig/drivers/scsi/Kconfig
-+++ linux-2.6.22.5/drivers/scsi/Kconfig
+--- linux-2.6.22.19.orig/drivers/scsi/Kconfig
++++ linux-2.6.22.19/drivers/scsi/Kconfig
 @@ -76,6 +76,14 @@ config BLK_DEV_SD
  	  In this case, do not compile the driver for your SCSI host adapter
  	  (below) as a module either.
@@ -17,99 +17,104 @@ Index: linux-2.6.22.5/drivers/scsi/Kconfig
  config CHR_DEV_ST
  	tristate "SCSI tape support"
  	depends on SCSI
-Index: linux+rhel4+chaos/drivers/scsi/sd.c
+Index: linux-2.6.22.19/drivers/scsi/scsi_proc.c
 ===================================================================
---- linux+rhel4+chaos.orig/drivers/scsi/sd.c
-+++ linux+rhel4+chaos/drivers/scsi/sd.c
-@@ -63,6 +63,38 @@
+--- linux-2.6.22.19.orig/drivers/scsi/scsi_proc.c
++++ linux-2.6.22.19/drivers/scsi/scsi_proc.c
+@@ -40,7 +40,8 @@
+ /* 4K page size, but our output routines, use some slack for overruns */
+ #define PROC_BLOCK_SIZE (3*1024)
+ 
+-static struct proc_dir_entry *proc_scsi;
++struct proc_dir_entry *proc_scsi;
++EXPORT_SYMBOL(proc_scsi);
  
- #include "scsi_logging.h"
+ /* Protect sht->present and sht->proc_dir */
+ static DEFINE_MUTEX(global_host_template_mutex);
+Index: linux-2.6.22.19/drivers/scsi/sd.c
+===================================================================
+--- linux-2.6.22.19.orig/drivers/scsi/sd.c
++++ linux-2.6.22.19/drivers/scsi/sd.c
+@@ -94,6 +94,24 @@ static DEFINE_SPINLOCK(sd_index_lock);
+  * object after last put) */
+ static DEFINE_MUTEX(sd_ref_mutex);
  
 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
 +# include <linux/proc_fs.h>
 +# include <linux/seq_file.h>
-+
-+typedef struct {
-+        unsigned long long iostat_size;
-+        unsigned long long iostat_count;
-+} iostat_counter_t;
-+
-+#define IOSTAT_NCOUNTERS 16
-+typedef struct {
-+        iostat_counter_t        iostat_read_histogram[IOSTAT_NCOUNTERS];
-+        iostat_counter_t        iostat_write_histogram[IOSTAT_NCOUNTERS];
-+        struct timeval          iostat_timeval;
-+} iostat_stats_t;
-+
-+iostat_stats_t       **sd_iostats;
-+spinlock_t             sd_iostats_lock;
-+struct proc_dir_entry *sd_iostats_procdir;
-+char                   sd_iostats_procdir_name[] = "sd_iostats";
++struct proc_dir_entry *sd_iostats_procdir = NULL;
++char sd_iostats_procdir_name[] = "sd_iostats";
++static struct file_operations sd_iostats_proc_fops;
 +
 +extern void sd_iostats_init(void);
-+extern void sd_iostats_init_disk(struct gendisk *);
 +extern void sd_iostats_fini(void);
-+extern void sd_iostats_bump(int disk, unsigned int nsect, int iswrite);
++void sd_iostats_start_req(struct scsi_cmnd *SCpnt);
++void sd_iostats_finish_req(struct scsi_cmnd *SCpnt);
 +#else
 +static inline void sd_iostats_init(void) {}
-+static inline void sd_iostats_init_disk(struct gendisk *disk) {}
 +static inline void sd_iostats_fini(void) {}
-+static inline void sd_iostats_bump(int disk, unsigned int nsect, int iswrite) {}
++static inline void sd_iostats_start_req(struct scsi_cmnd *SCpnt) {}
++static inline void sd_iostats_finish_req(struct scsi_cmnd *SCpnt) {}
 +#endif
 +
- MODULE_AUTHOR("Eric Youngdale");
- MODULE_DESCRIPTION("SCSI disk (sd) driver");
- MODULE_LICENSE("GPL");
-@@ -89,6 +121,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
- static DEFINE_IDR(sd_index_idr);
- static DEFINE_SPINLOCK(sd_index_lock);
+ static const char *sd_cache_types[] = {
+ 	"write through", "none", "write back",
+ 	"write back, no read (daft)"
+@@ -498,6 +516,8 @@ static int sd_init_command(struct scsi_c
+ 	 */
+ 	SCpnt->done = sd_rw_intr;
  
-+#define SD_STATS 256
- /* This semaphore is used to mediate the 0->1 reference get in the
-  * face of object destruction (i.e. we can't allow a get on an
-  * object after last put) */
-@@ -368,6 +401,9 @@ static int sd_init_command(struct scsi_c
- 	SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
- 					(unsigned long long)block));
- 
-+   sd_iostats_bump(scsi_disk(disk)->index, this_count,
-+                   rq_data_dir(SCpnt->request) == WRITE);
++	sd_iostats_start_req(SCpnt);
 +
  	/*
- 	 * If we have a 1K hardware sectorsize, prevent access to single
- 	 * 512 byte sectors.  In theory we could handle this - in fact
-@@ -575,6 +611,7 @@ static int sd_open(struct inode *inode, 
- 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
+ 	 * This indicates that the command is ready from our end to be
+ 	 * queued.
+@@ -980,6 +1000,7 @@ static void sd_rw_intr(struct scsi_cmnd 
+ 		break;
  	}
+  out:
++	sd_iostats_finish_req(SCpnt);
+ 	scsi_io_completion(SCpnt, good_bytes);
+ }
  
-+   sd_iostats_init_disk(disk);
- 	return 0;
- 
- error_out:
-@@ -601,8 +638,20 @@ static int sd_release(struct inode *inod
- 
- 	SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
+@@ -1666,6 +1687,36 @@ static int sd_probe(struct device *dev)
+ 	if (sdp->removable)
+ 		gd->flags |= GENHD_FL_REMOVABLE;
  
--	if (!--sdkp->openers && sdev->removable) {
--		if (scsi_block_when_processing_errors(sdev))
-+	if (!--sdkp->openers) {
-+		/*
-+		 * Remove sd_iostats information about this disk
-+		 */
-+		if (sd_iostats_procdir != NULL) {
-+			remove_proc_entry(disk->disk_name, sd_iostats_procdir);
-+		}
-+		if (sd_iostats != NULL) {
-+			if (sd_iostats[sdkp->index] != NULL) {
-+				kfree (sd_iostats[sdkp->index]);
-+				sd_iostats[sdkp->index] = NULL;
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	sdkp->stats = kzalloc(sizeof(iostat_stats_t), GFP_KERNEL);
++	if (!sdkp->stats) {
++		printk(KERN_WARNING "cannot allocate iostat structure for"
++				    "%s\n", gd->disk_name);
++	} else {
++		do_gettimeofday(&sdkp->stats->iostat_timeval);
++		sdkp->stats->iostat_queue_stamp = jiffies;
++		spin_lock_init(&sdkp->stats->iostat_lock);
++		if (sd_iostats_procdir) {
++			struct proc_dir_entry *pde;
++			pde = create_proc_entry(gd->disk_name, S_IRUGO | S_IWUSR,
++					        sd_iostats_procdir);
++			if (!pde) {
++				printk(KERN_WARNING "Can't create /proc/scsi/"
++						    "%s/%s\n",
++						    sd_iostats_procdir_name,
++						    gd->disk_name);
++				kfree(sdkp->stats);
++				sdkp->stats = NULL;
++			} else {
++				pde->proc_fops = &sd_iostats_proc_fops;
++				pde->data = gd;
 +			}
++		} else {
++			kfree(sdkp->stats);
++			sdkp->stats = NULL;
 +		}
-+		if (sdev->removable && scsi_block_when_processing_errors(sdev))
- 			scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
- 	}
++	}
++#endif
+ 	dev_set_drvdata(dev, sdkp);
+ 	add_disk(gd);
  
-@@ -1563,6 +1612,342 @@ static int sd_revalidate_disk(struct gen
+@@ -1709,6 +1760,366 @@ static int sd_remove(struct device *dev)
  	return 0;
  }
  
@@ -117,101 +122,150 @@ Index: linux+rhel4+chaos/drivers/scsi/sd.c
 +static int
 +sd_iostats_seq_show(struct seq_file *seq, void *v)
 +{
-+        struct timeval     now;
-+        struct gendisk *disk;
-+        iostat_stats_t    *stats;
-+        unsigned long long read_len;
-+        unsigned long long read_len_tot;
-+        unsigned long      read_num;
-+        unsigned long      read_num_tot;
-+        unsigned long long write_len;
-+        unsigned long long write_len_tot;
-+        unsigned long      write_num;
-+        unsigned long      write_num_tot;
-+        int                i;
-+        int                maxi;
-+
-+	if (seq == NULL || seq->private == NULL) {
-+		printk(KERN_ERR "sd_iostats_seq_show: NULL disk\n");
++	struct timeval     now;
++	struct gendisk *disk = seq->private;
++	iostat_stats_t    *stats;
++	unsigned long long read_len;
++	unsigned long long read_len_tot;
++	unsigned long      read_num;
++	unsigned long      read_num_tot;
++	unsigned long long write_len;
++	unsigned long long write_len_tot;
++	unsigned long      write_num;
++	unsigned long      write_num_tot;
++	int                i;
++	int                maxi;
++
++	stats = scsi_disk(disk)->stats;
++	if (stats == NULL) {
++		printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n");
 +		BUG();
 +	}
 +
-+	disk = seq->private;
++	do_gettimeofday(&now);
++	now.tv_sec -= stats->iostat_timeval.tv_sec;
++	now.tv_usec -= stats->iostat_timeval.tv_usec;
++	if (now.tv_usec < 0) {
++		now.tv_usec += 1000000;
++		now.tv_sec--;
++	}
++
++	/* this sampling races with updates */
++	seq_printf(seq, "index:        %lu   snapshot_time:         %lu.%06lu\n",
++			(unsigned long) scsi_disk(disk)->index,
++			now.tv_sec, now.tv_usec);
++
++	for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--)
++		if (stats->iostat_read_histogram[i].iostat_count != 0 ||
++				stats->iostat_write_histogram[i].iostat_count != 0)
++			break;
++	maxi = i;
++
++	seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", 
++			"reads", "total", "writes", "total");
++
++	read_len_tot = write_len_tot = 0;
++	read_num_tot = write_num_tot = 0;
++	for (i = 0; i <= maxi; i++) {
++		read_len = stats->iostat_read_histogram[i].iostat_size;
++		read_len_tot += read_len;
++		read_num = stats->iostat_read_histogram[i].iostat_count;
++		read_num_tot += read_num;
++
++		write_len = stats->iostat_write_histogram[i].iostat_size;
++		write_len_tot += write_len;
++		write_num = stats->iostat_write_histogram[i].iostat_count;
++		write_num_tot += write_num;
++
++		seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", 
++				512<<i, read_num, read_len, write_num, write_len);
++	}
++
++	seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n\n", "total",
++			read_num_tot, read_len_tot, 
++			write_num_tot, write_len_tot);
++
++	seq_printf(seq, "%8s %8s %8s\n", "qdepth", "ticks", "%");
++	for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
++		unsigned long long ticks, percent;
++		ticks = stats->iostat_queue_ticks[i];
++		if (ticks == 0)
++			continue;
++		percent = stats->iostat_queue_ticks[i] * 100;
++		do_div(percent, stats->iostat_queue_ticks_sum);
++		seq_printf(seq, "%8d %8llu %8llu\n", i, ticks, percent);
++	}
++
++	if (stats->iostat_reqs != 0) {
++		unsigned long long aveseek = 0, percent = 0;
++
++		if (stats->iostat_seeks) {
++			aveseek = stats->iostat_seek_sectors;
++			do_div(aveseek, stats->iostat_seeks);
++			percent = stats->iostat_seeks * 100;
++			do_div(percent, stats->iostat_reqs);
++		}
++
++		seq_printf(seq, "\n%llu sectors in %llu reqs: %llu seek(s) over "
++				"%llu sectors in ave, %llu%% of all reqs\n",
++				stats->iostat_sectors, stats->iostat_reqs,
++				stats->iostat_seeks, aveseek, percent);
++	}
 +
-+	if (scsi_disk(disk) == NULL || (disk->flags & GENHD_FL_UP) == 0) {
-+		seq_printf(seq, "sd_iostats_seq_show: Device %s "
-+				"does not exist\n", disk->disk_name);
-+		return 0;
++	seq_printf(seq, "\n%16s %8s %8s %8s %8s\n", "process time", "reads",
++			"%%", "writes", "%%");
++	for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
++		unsigned long read_percent = 0, write_percent = 0;
++		if (stats->iostat_wtime[i] == 0 &&
++				stats->iostat_rtime[i] == 0)
++			continue;
++		if (stats->iostat_read_reqs)
++			read_percent = stats->iostat_rtime[i] * 100 / 
++				stats->iostat_read_reqs;
++		if (stats->iostat_write_reqs)
++			write_percent = stats->iostat_wtime[i] * 100 / 
++				stats->iostat_write_reqs;
++		seq_printf(seq, "%16u %8lu %8lu %8lu %8lu\n",
++				jiffies_to_msecs(((1UL << i) >> 1) << 1),
++				stats->iostat_rtime[i], read_percent,
++				stats->iostat_wtime[i], write_percent);
 +	}
 +
-+        if (sd_iostats == NULL) {
-+                printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
-+                BUG();
-+        }
-+
-+        stats = sd_iostats[scsi_disk(disk)->index];
-+        if (stats == NULL) {
-+                seq_printf(seq, "sd_iostats_seq_show: sd_iostats "
-+				"entry %d does not exist\n",
-+				scsi_disk(disk)->index);
-+		return 0;
-+        }
-+
-+        do_gettimeofday(&now);
-+        now.tv_sec -= stats->iostat_timeval.tv_sec;
-+        now.tv_usec -= stats->iostat_timeval.tv_usec;
-+        if (now.tv_usec < 0) {
-+                now.tv_usec += 1000000;
-+                now.tv_sec--;
-+        }
-+
-+        /* this sampling races with updates */
-+        seq_printf(seq, "index:        %lu   snapshot_time:         %lu.%06lu\n",
-+                   scsi_disk(disk)->index, now.tv_sec, now.tv_usec);
-+
-+        for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--)
-+                if (stats->iostat_read_histogram[i].iostat_count != 0 ||
-+                    stats->iostat_write_histogram[i].iostat_count != 0)
-+                        break;
-+        maxi = i;
-+
-+        seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", 
-+                   "reads", "total", "writes", "total");
-+
-+        read_len_tot = write_len_tot = 0;
-+        read_num_tot = write_num_tot = 0;
-+        for (i = 0; i <= maxi; i++) {
-+                read_len = stats->iostat_read_histogram[i].iostat_size;
-+                read_len_tot += read_len;
-+                read_num = stats->iostat_read_histogram[i].iostat_count;
-+                read_num_tot += read_num;
-+
-+                write_len = stats->iostat_write_histogram[i].iostat_size;
-+                write_len_tot += write_len;
-+                write_num = stats->iostat_write_histogram[i].iostat_count;
-+                write_num_tot += write_num;
-+
-+                seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", 
-+                            512<<i, read_num, read_len, write_num, write_len);
-+        }
-+        
-+        seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n", "total",
-+                   read_num_tot, read_len_tot, 
-+                   write_num_tot, write_len_tot);
-+        return 0;
++	seq_printf(seq, "\n%16s %8s %8s %8s %8s\n", "time in queue", "reads",
++			"%%", "writes", "%%");
++	for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
++		unsigned long read_percent = 0, write_percent = 0;
++		if (stats->iostat_wtime_in_queue[i] == 0 &&
++				stats->iostat_rtime_in_queue[i] == 0)
++			continue;
++		if (stats->iostat_read_reqs)
++			read_percent = stats->iostat_rtime_in_queue[i] * 100 / 
++				stats->iostat_read_reqs;
++		if (stats->iostat_write_reqs)
++			write_percent = stats->iostat_wtime_in_queue[i] * 100 / 
++				stats->iostat_write_reqs;
++		seq_printf(seq, "%16u %8lu %8lu %8lu %8lu\n",
++				jiffies_to_msecs(((1UL << i) >> 1) << 1),
++				stats->iostat_rtime_in_queue[i],
++				read_percent,
++				stats->iostat_wtime_in_queue[i],
++				write_percent);
++	}
++
++	return 0;
 +}
 +
 +static void *
 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
 +{
-+        return (*pos == 0) ? (void *)1 : NULL;
++	return (*pos == 0) ? (void *)1 : NULL;
 +}
 +
 +static void *
 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
 +{
-+        ++*pos;
-+        return NULL;
++	++*pos;
++	return NULL;
 +}
 +
 +static void
@@ -220,50 +274,54 @@ Index: linux+rhel4+chaos/drivers/scsi/sd.c
 +}
 +
 +static struct seq_operations sd_iostats_seqops = {
-+        .start = sd_iostats_seq_start,
-+        .stop  = sd_iostats_seq_stop,
-+        .next  = sd_iostats_seq_next,
-+        .show  = sd_iostats_seq_show,
++	.start = sd_iostats_seq_start,
++	.stop  = sd_iostats_seq_stop,
++	.next  = sd_iostats_seq_next,
++	.show  = sd_iostats_seq_show,
 +};
 +
 +static int
 +sd_iostats_seq_open (struct inode *inode, struct file *file)
 +{
-+        int                    rc;
++	int rc;
 +
-+        rc = seq_open(file, &sd_iostats_seqops);
-+        if (rc != 0)
-+                return rc;
++	rc = seq_open(file, &sd_iostats_seqops);
++	if (rc != 0)
++		return rc;
 +
-+        ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
-+        return 0;
++	((struct seq_file *)file->private_data)->private = PDE(inode)->data;
++	return 0;
 +}
 +
 +static ssize_t
 +sd_iostats_seq_write(struct file *file, const char *buffer,
-+                     size_t len, loff_t *off)
++		     size_t len, loff_t *off)
 +{
-+        struct seq_file   *seq = file->private_data;
-+        struct gendisk *disk = seq->private;
-+        iostat_stats_t    *stats = sd_iostats[scsi_disk(disk)->index];
-+        unsigned long      flags;
-+        
-+        
-+        spin_lock_irqsave (&sd_iostats_lock, flags);
-+        memset (stats, 0, sizeof(*stats));
-+        do_gettimeofday(&stats->iostat_timeval);
-+        spin_unlock_irqrestore (&sd_iostats_lock, flags);
-+
-+        return len;
++	struct seq_file   *seq = file->private_data;
++	struct gendisk *disk = seq->private;
++	iostat_stats_t    *stats = scsi_disk(disk)->stats;
++	unsigned long      flags;
++	unsigned long      qdepth;
++
++
++	spin_lock_irqsave (&stats->iostat_lock, flags);
++	qdepth = stats->iostat_queue_depth;
++	memset (stats, 0, offsetof(iostat_stats_t, iostat_lock));
++	do_gettimeofday(&stats->iostat_timeval);
++	stats->iostat_queue_stamp = jiffies;
++	stats->iostat_queue_depth = qdepth;
++	spin_unlock_irqrestore (&stats->iostat_lock, flags);
++
++	return len;
 +}
 +
 +static struct file_operations sd_iostats_proc_fops = {
-+        .owner   = THIS_MODULE,
-+        .open    = sd_iostats_seq_open,
-+        .read    = seq_read,
-+        .write   = sd_iostats_seq_write,
-+        .llseek  = seq_lseek,
-+        .release = seq_release,
++	.owner   = THIS_MODULE,
++	.open    = sd_iostats_seq_open,
++	.read    = seq_read,
++	.write   = sd_iostats_seq_write,
++	.llseek  = seq_lseek,
++	.release = seq_release,
 +};
 +
 +extern struct proc_dir_entry *proc_scsi;
@@ -271,214 +329,251 @@ Index: linux+rhel4+chaos/drivers/scsi/sd.c
 +void
 +sd_iostats_init(void)
 +{
-+        int    i;
-+
-+        spin_lock_init(&sd_iostats_lock);
-+
-+        sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL);
-+        if (sd_iostats == NULL) {
-+                printk(KERN_WARNING "Can't keep sd iostats: "
-+                       "ENOMEM allocating stats array size %ld\n",
-+                       SD_STATS * sizeof(iostat_stats_t *));
-+                return;
-+        }
-+
-+        for (i = 0; i < SD_STATS; i++)
-+                sd_iostats[i] = NULL;
-+
-+        if (proc_scsi == NULL) {
-+                printk(KERN_WARNING "No access to sd iostats: "
-+                       "proc_scsi is NULL\n");
-+                return;
-+        }
-+
-+        sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
-+                                               S_IFDIR | S_IRUGO | S_IXUGO,
-+                                               proc_scsi);
-+        if (sd_iostats_procdir == NULL) {
-+                printk(KERN_WARNING "No access to sd iostats: "
-+                       "can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
-+                return;
-+        }
-+}
++	if (proc_scsi == NULL) {
++		printk(KERN_WARNING "No access to sd iostats: "
++			"proc_scsi is NULL\n");
++		return;
++	}
 +
-+void
-+sd_iostats_init_disk(struct gendisk *disk)
-+{
-+        struct proc_dir_entry *pde;
-+        unsigned long          flags;
-+        iostat_stats_t        *stats;
-+
-+        if (sd_iostats == NULL ||
-+            sd_iostats_procdir == NULL)
-+                return;
-+
-+        if (scsi_disk(disk)->index > SD_STATS) {
-+                printk(KERN_ERR "sd_iostats_init_disk: "
-+                       "unexpected disk index %d(%d)\n",
-+                       scsi_disk(disk)->index, SD_STATS);
-+				    return;
-+        }
-+
-+        if (sd_iostats[scsi_disk(disk)->index] != NULL)
-+                return;
-+
-+        stats = kmalloc(sizeof(*stats), GFP_KERNEL);
-+        if (stats == NULL) {
-+                printk(KERN_WARNING "Can't keep %s iostats: "
-+                       "ENOMEM allocating stats size %ld\n", 
-+                       disk->disk_name, sizeof(*stats));
-+                return;
-+        }
-+
-+        memset (stats, 0, sizeof(*stats));
-+        do_gettimeofday(&stats->iostat_timeval);
-+
-+        spin_lock_irqsave(&sd_iostats_lock, flags);
-+
-+        if (sd_iostats[scsi_disk(disk)->index] != NULL) {
-+                spin_unlock_irqrestore(&sd_iostats_lock, flags);
-+                kfree (stats);
-+                return;
-+        }
-+
-+        sd_iostats[scsi_disk(disk)->index] = stats;
-+        
-+        spin_unlock_irqrestore(&sd_iostats_lock, flags);
-+        
-+        pde = create_proc_entry(disk->disk_name, S_IRUGO | S_IWUSR, 
-+                                sd_iostats_procdir);
-+        if (pde == NULL) {
-+                printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
-+                       sd_iostats_procdir_name, disk->disk_name);
-+        } else {
-+                pde->proc_fops = &sd_iostats_proc_fops;
-+                pde->data = disk;
-+        }
++	sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
++					       S_IFDIR | S_IRUGO | S_IXUGO,
++					        proc_scsi);
++	if (sd_iostats_procdir == NULL) {
++		printk(KERN_WARNING "No access to sd iostats: "
++			"can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
++		return;
++	}
 +}
 +
-+static void sd_devname(unsigned int disknum, char *buffer)
++void sd_iostats_fini(void)
 +{
-+        if (disknum < 26)
-+                sprintf(buffer, "sd%c", 'a' + disknum);
-+        else {
-+                unsigned int min1;
-+                unsigned int min2;
-+                /*
-+                 * For larger numbers of disks, we need to go to a new
-+                 * naming scheme.
-+                 */
-+                min1 = disknum / 26;
-+                min2 = disknum % 26;
-+                sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
-+        }
++	if (proc_scsi != NULL && sd_iostats_procdir != NULL)
++		remove_proc_entry(sd_iostats_procdir_name, proc_scsi);
++
++	sd_iostats_procdir = NULL;
 +}
 +
-+void
-+sd_iostats_fini(void)
++void sd_iostats_finish_req(struct scsi_cmnd *SCpnt)
 +{
-+        char name[6];
-+        int  i;
-+        
-+        if (sd_iostats_procdir != NULL) {
-+                for (i = 0; i < SD_STATS; i++) {
-+                        sd_devname(i, name);
-+                        remove_proc_entry(name, sd_iostats_procdir);
-+                }
-+
-+                if (proc_scsi == NULL) {
-+                        printk(KERN_ERR "sd_iostats_fini: proc_scsi NULL\n");
-+                        BUG();
-+                }
-+                remove_proc_entry(sd_iostats_procdir_name,
-+                                  proc_scsi);
-+
-+                sd_iostats_procdir = NULL;
-+        }
-+        
-+        if (sd_iostats != NULL) {
-+                for (i = 0; i < SD_STATS; i++) {
-+                        if (sd_iostats[i] != NULL)
-+                                kfree (sd_iostats[i]);
-+                }
-+                
-+                kfree(sd_iostats);
-+                sd_iostats = NULL;
-+        }
++	struct request		*rq = SCpnt->request;
++	iostat_stats_t		*stats;
++	unsigned long		*tcounter;
++	int			tbucket;
++	int			tmp;
++	unsigned long		irqflags;
++	unsigned long		i;
++
++	stats = scsi_disk(rq->rq_disk)->stats;
++	if (stats == NULL)
++		return;
++
++	tmp = jiffies - rq->start_time;
++	for (tbucket = 0; tmp > 1; tbucket++)
++		tmp >>= 1;
++	if (tbucket >= IOSTAT_NCOUNTERS)
++		tbucket = IOSTAT_NCOUNTERS - 1;
++	//printk("%u ticks in D to %u\n", jiffies - rq->start_time, tbucket);
++
++	tcounter = rq_data_dir(rq) == WRITE ?
++		&stats->iostat_wtime[tbucket] : &stats->iostat_rtime[tbucket];
++
++	spin_lock_irqsave(&stats->iostat_lock, irqflags);
++
++	/* update delay stats */
++	(*tcounter)++;
++
++	/* update queue depth stats */
++	i = stats->iostat_queue_depth;
++	if (i >= IOSTAT_NCOUNTERS)
++		i = IOSTAT_NCOUNTERS - 1;
++	stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
++	stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
++	BUG_ON(stats->iostat_queue_depth == 0);
++	stats->iostat_queue_depth--;
++
++	/* update seek stats. XXX: not sure about nr_sectors */
++	stats->iostat_sectors += rq->nr_sectors;
++	stats->iostat_reqs++;
++	if (rq->sector != stats->iostat_next_sector) {
++		stats->iostat_seek_sectors +=
++			rq->sector > stats->iostat_next_sector ?
++			rq->sector - stats->iostat_next_sector :
++			stats->iostat_next_sector - rq->sector;
++		stats->iostat_seeks++;
++	}
++	stats->iostat_next_sector = rq->sector + rq->nr_sectors;
++
++	stats->iostat_queue_stamp = jiffies;
++
++	spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
 +}
 +
-+void
-+sd_iostats_bump(int disk, unsigned int nsect, int iswrite)
++void sd_iostats_start_req(struct scsi_cmnd *SCpnt)
 +{
-+        iostat_stats_t    *stats;
-+        iostat_counter_t  *counter;
-+        int                bucket;
-+        int                tmp;
-+        unsigned long      irqflags;
-+
-+        if (sd_iostats == NULL)
-+                return;
-+
-+        if (disk < 0 || disk >= SD_STATS) {
-+                printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
-+                       disk, SD_STATS);
-+                BUG();
-+        }
-+
-+        for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
-+                tmp /= 2;
-+
-+        if (bucket >= IOSTAT_NCOUNTERS) {
-+                printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
-+                BUG();
-+        }
-+
-+        spin_lock_irqsave(&sd_iostats_lock, irqflags);
-+        
-+        stats = sd_iostats[disk];
-+        if (stats != NULL) {
-+                counter = iswrite ? 
-+                          &stats->iostat_write_histogram[bucket] :
-+                          &stats->iostat_read_histogram[bucket];
-+
-+                counter->iostat_size += nsect;
-+                counter->iostat_count++;
-+        }
-+
-+        spin_unlock_irqrestore(&sd_iostats_lock, irqflags);
++	struct request		*rq = SCpnt->request;
++	iostat_stats_t		*stats;
++	iostat_counter_t	*counter;
++	int			bucket;
++	int			tbucket;
++	int			tmp;
++	unsigned long		irqflags;
++	unsigned long		i;
++	int			nsect;
++
++	stats = scsi_disk(rq->rq_disk)->stats;
++	if (stats == NULL)
++		return;
++
++	nsect = SCpnt->request_bufflen >> 9;
++	for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
++		tmp >>= 1;
++
++	if (bucket >= IOSTAT_NCOUNTERS) {
++		printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
++		BUG();
++	}
++
++	counter = rq_data_dir(rq) == WRITE ?
++		&stats->iostat_write_histogram[bucket] :
++		&stats->iostat_read_histogram[bucket];
++
++	tmp = jiffies - rq->start_time;
++	for (tbucket = 0; tmp > 1; tbucket++)
++		tmp >>= 1;
++	if (tbucket >= IOSTAT_NCOUNTERS)
++		tbucket = IOSTAT_NCOUNTERS - 1;
++	//printk("%u ticks in Q to %u\n", jiffies - rq->start_time, tbucket);
++
++	/* an ugly hack to know exact processing time. the right
++	 * solution is to add one more field to struct request
++	 * hopefully it will break nothing ... */
++	rq->start_time = jiffies;
++
++	spin_lock_irqsave(&stats->iostat_lock, irqflags);
++
++	/* update queue depth stats */
++	i = stats->iostat_queue_depth;
++	if (i >= IOSTAT_NCOUNTERS)
++		i = IOSTAT_NCOUNTERS - 1;
++	stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
++	stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
++	stats->iostat_queue_depth++;
++
++	/* update delay stats */
++	if (rq_data_dir(rq) == WRITE) {
++		stats->iostat_wtime_in_queue[tbucket]++;
++		stats->iostat_write_reqs++;
++	} else {
++		stats->iostat_rtime_in_queue[tbucket]++;
++		stats->iostat_read_reqs++;
++	}
++
++	/* update size stats */
++	counter->iostat_size += nsect;
++	counter->iostat_count++;
++
++	stats->iostat_queue_stamp = jiffies;
++
++	spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
 +}
 +#endif
 +
  /**
-  *	sd_probe - called during driver initialization and whenever a
-  *	new scsi device is attached to the system. It is called once
-@@ -1854,6 +2239,7 @@ static int __init init_sd(void)
- 	err = scsi_register_driver(&sd_template.gendrv);
- 	if (err)
- 		goto err_out_class;
-+	sd_iostats_init();
+  *	scsi_disk_release - Called to free the scsi_disk structure
+  *	@cdev: pointer to embedded class device
+@@ -1727,10 +2138,16 @@ static void scsi_disk_release(struct cla
+ 	idr_remove(&sd_index_idr, sdkp->index);
+ 	spin_unlock(&sd_index_lock);
  
- 	return 0;
- 
-@@ -1876,6 +2262,7 @@ static void __exit exit_sd(void)
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	if (sdkp->stats) {
++		remove_proc_entry(disk->disk_name, sd_iostats_procdir);
++		kfree(sdkp->stats);
++		sdkp->stats = NULL;
++	}
++#endif
+ 	disk->private_data = NULL;
+ 	put_disk(disk);
+ 	put_device(&sdkp->device->sdev_gendev);
+-
+ 	kfree(sdkp);
+ }
  
- 	SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
+@@ -1845,6 +2262,8 @@ static int __init init_sd(void)
+ 	if (!majors)
+ 		return -ENODEV;
  
++	sd_iostats_init();
++
+ 	err = class_register(&sd_disk_class);
+ 	if (err)
+ 		goto err_out;
+@@ -1860,6 +2279,7 @@ err_out_class:
+ err_out:
+ 	for (i = 0; i < SD_MAJORS; i++)
+ 		unregister_blkdev(sd_major(i), "sd");
 +	sd_iostats_fini();
- 	scsi_unregister_driver(&sd_template.gendrv);
- 	class_unregister(&sd_disk_class);
+ 	return err;
+ }
  
-Index: linux-2.6.22.5/drivers/scsi/scsi_proc.c
+Index: linux-2.6.22.19/include/scsi/sd.h
 ===================================================================
---- linux-2.6.22.5.orig/drivers/scsi/scsi_proc.c
-+++ linux-2.6.22.5/drivers/scsi/scsi_proc.c
-@@ -40,7 +40,8 @@
- /* 4K page size, but our output routines, use some slack for overruns */
- #define PROC_BLOCK_SIZE (3*1024)
+--- linux-2.6.22.19.orig/include/scsi/sd.h
++++ linux-2.6.22.19/include/scsi/sd.h
+@@ -31,6 +31,46 @@
+  */
+ #define SD_BUF_SIZE		512
  
--static struct proc_dir_entry *proc_scsi;
-+struct proc_dir_entry *proc_scsi;
-+EXPORT_SYMBOL(proc_scsi);
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++typedef struct {
++	unsigned long long iostat_size;
++	unsigned long long iostat_count;
++} iostat_counter_t;
++
++#define IOSTAT_NCOUNTERS 16
++typedef struct {
++	iostat_counter_t	iostat_read_histogram[IOSTAT_NCOUNTERS];
++	iostat_counter_t	iostat_write_histogram[IOSTAT_NCOUNTERS];
++	struct timeval		iostat_timeval;
++
++	/* queue depth: how well the pipe is filled up */
++	unsigned long long	iostat_queue_ticks[IOSTAT_NCOUNTERS];
++	unsigned long long	iostat_queue_ticks_sum;
++	unsigned long		iostat_queue_depth;
++	unsigned long		iostat_queue_stamp;
++
++	/* seeks: how linear the traffic is */
++	unsigned long long	iostat_next_sector;
++	unsigned long long	iostat_seek_sectors;
++	unsigned long long	iostat_seeks;
++	unsigned long long	iostat_sectors;
++	unsigned long long	iostat_reqs;
++	unsigned long		iostat_read_reqs;
++	unsigned long		iostat_write_reqs;
++
++	/* process time: how long it takes to process requests */
++	unsigned long		iostat_rtime[IOSTAT_NCOUNTERS];
++	unsigned long		iostat_wtime[IOSTAT_NCOUNTERS];
++
++	/* queue time: how long process spent in elevator's queue */
++	unsigned long		iostat_rtime_in_queue[IOSTAT_NCOUNTERS];
++	unsigned long		iostat_wtime_in_queue[IOSTAT_NCOUNTERS];
++
++	/* must be the last field, as it's used to know size to be memset'ed */
++	spinlock_t		iostat_lock;
++} ____cacheline_aligned_in_smp iostat_stats_t;
++#endif
++
+ struct scsi_disk {
+ 	struct scsi_driver *driver;	/* always &sd_template */
+ 	struct scsi_device *device;
+@@ -44,6 +84,9 @@ struct scsi_disk {
+ 	unsigned	WCE : 1;	/* state of disk WCE bit */
+ 	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
+ 	unsigned	DPOFUA : 1;	/* state of disk DPOFUA bit */
++#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
++	iostat_stats_t	*stats;		/* scsi disk statistics */
++#endif
+ };
+ #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev)
  
- /* Protect sht->present and sht->proc_dir */
- static DEFINE_MUTEX(global_host_template_mutex);
diff --git a/lustre/kernel_patches/series/2.6.22-vanilla.series b/lustre/kernel_patches/series/2.6.22-vanilla.series
index 5e8bebc7ee8623f7816938982bca59b57408130e..eba2991be2f9688ea486bd9fd6ddffd10620cf3f 100644
--- a/lustre/kernel_patches/series/2.6.22-vanilla.series
+++ b/lustre/kernel_patches/series/2.6.22-vanilla.series
@@ -10,4 +10,4 @@ dev_read_only-2.6.22-vanilla.patch
 export-2.6.18-vanilla.patch 
 8kstack-2.6.12.patch
 export-show_task-2.6.18-vanilla.patch 
-sd_iostats-2.6.22-vanilla.patch 
+sd_iostats-2.6.22-vanilla.patch