#include <linux/workqueue.h>
+#include <linux/statistic.h>
#include <asm/atomic.h>
struct request_queue;
@@ -45,6 +46,17 @@ enum scsi_device_state {
* to the scsi lld. */
};
+enum scsi_unit_stats {
+ SCSI_STAT_U_SW, /* size, write */
+ SCSI_STAT_U_SR, /* size, read */
+ SCSI_STAT_U_LW, /* latency, write */
+ SCSI_STAT_U_LR, /* latency, read */
+ SCSI_STAT_U_LN, /* latency, no data */
+ SCSI_STAT_U_R, /* result */
+ SCSI_STAT_U_QUD, /* queue used depth */
+ _SCSI_STAT_U_NUMBER,
+};
+
struct scsi_device {
struct Scsi_Host *host;
struct request_queue *request_queue;
@@ -133,6 +145,9 @@ struct scsi_device {
atomic_t iodone_cnt;
atomic_t ioerr_cnt;
+ struct statistic_interface stat_if;
+ struct statistic stat[_SCSI_STAT_U_NUMBER];
+
int timeout;
struct device sdev_gendev;
diff -urp a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
--- a/include/scsi/scsi_cmnd.h 2006-09-18 09:23:10.000000000 +0200
+++ b/include/scsi/scsi_cmnd.h 2006-09-18 09:54:06.000000000 +0200
@@ -54,6 +54,9 @@ struct scsi_cmnd {
*/
unsigned long jiffies_at_alloc;
+ struct timeval issued;
+ struct timeval received;
+
int retries;
int allowed;
int timeout_per_command;
diff -urp a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c 2006-09-18 09:23:08.000000000 +0200
+++ b/drivers/scsi/scsi_scan.c 2006-09-18 11:21:38.000000000 +0200
@@ -133,6 +133,54 @@ static void scsi_unlock_floptical(struct
SCSI_TIMEOUT, 3);
}
+static struct statistic_info scsi_statinfo_u[] = {
+ [SCSI_STAT_U_SW] = {
+ .name = "size_write",
+ .x_unit = "bytes",
+ .y_unit = "request",
+ .defaults = "type=sparse entries=256"
+ },
+ [SCSI_STAT_U_SR] = {
+ .name = "size_read",
+ .x_unit = "bytes",
+ .y_unit = "request",
+ .defaults = "type=sparse entries=256"
+ },
+ [SCSI_STAT_U_LW] = {
+ .name = "latency_write",
+ .x_unit = "microsec",
+ .y_unit = "request",
+ .defaults = "type=histogram_log2 entries=13 "
+ "base_interval=1000 range_min=0"
+ },
+ [SCSI_STAT_U_LR] = {
+ .name = "latency_read",
+ .x_unit = "microsec",
+ .y_unit = "request",
+ .defaults = "type=histogram_log2 entries=13 "
+ "base_interval=1000 range_min=0"
+ },
+ [SCSI_STAT_U_LN] = {
+ .name = "latency_nodata",
+ .x_unit = "microsec",
+ .y_unit = "request",
+ .defaults = "type=histogram_log2 entries=13 "
+ "base_interval=1000 range_min=0"
+ },
+ [SCSI_STAT_U_R] = {
+ .name = "result",
+ .x_unit = "flags",
+ .y_unit = "request",
+ .defaults = "type=sparse entries=256"
+ },
+ [SCSI_STAT_U_QUD] = {
+ .name = "queue_used_depth",
+ .x_unit = "requests",
+ .y_unit = "",
+ .defaults = "type=utilisation"
+ }
+};
+
/**
* scsi_alloc_sdev - allocate and setup a scsi_Device
*
@@ -150,6 +198,7 @@ static struct scsi_device *scsi_alloc_sd
struct scsi_device *sdev;
int display_failure_msg = 1, ret;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ char name[64];
sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
GFP_ATOMIC);
@@ -206,6 +255,14 @@ static struct scsi_device *scsi_alloc_sd
scsi_sysfs_device_initialize(sdev);
+ sprintf(name, "scsi-%d:%d:%d:%d", sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun);
+ sdev->stat_if.stat = sdev->stat;
+ sdev->stat_if.info = scsi_statinfo_u;
+ sdev->stat_if.number = _SCSI_STAT_U_NUMBER;
+ if (statistic_create(&sdev->stat_if, name))
+ goto out_device_destroy;
+
if (shost->hostt->slave_alloc) {
ret = shost->hostt->slave_alloc(sdev);
if (ret) {
@@ -215,12 +272,14 @@ static struct scsi_device *scsi_alloc_sd
*/
if (ret == -ENXIO)
display_failure_msg = 0;
- goto out_device_destroy;
+ goto out_stat_destroy;
}
}
return sdev;
+out_stat_destroy:
+ statistic_remove(&sdev->stat_if);
out_device_destroy:
transport_destroy_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
diff -urp a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
--- a/drivers/scsi/scsi_sysfs.c 2006-09-18 09:23:08.000000000 +0200
+++ b/drivers/scsi/scsi_sysfs.c 2006-09-18 09:47:13.000000000 +0200
@@ -247,6 +247,8 @@ static void scsi_device_dev_release_user
scsi_target_reap(scsi_target(sdev));
+ statistic_remove(&sdev->stat_if);
+
kfree(sdev->inquiry);
kfree(sdev);
diff -urp a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c 2006-09-18 09:23:08.000000000 +0200
+++ b/drivers/scsi/scsi.c 2006-09-18 11:04:44.000000000 +0200
@@ -582,6 +582,9 @@ int scsi_dispatch_cmd(struct scsi_cmnd *
cmd->result = (DID_NO_CONNECT << 16);
scsi_done(cmd);
} else {
+#ifdef CONFIG_STATISTICS
+ do_gettimeofday(&cmd->issued);
+#endif
rtn = host->hostt->queuecommand(cmd, scsi_done);
}
spin_unlock_irqrestore(host->host_lock, flags);
@@ -653,6 +656,10 @@ void __scsi_done(struct scsi_cmnd *cmd)
{
struct request *rq = cmd->request;
+#ifdef CONFIG_STATISTICS
+ do_gettimeofday(&cmd->received);
+#endif
+
/*
* Set the serial numbers back to zero
*/
diff -urp a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c 2006-09-18 09:23:08.000000000 +0200
+++ b/drivers/scsi/scsi_lib.c 2006-09-18 11:22:29.000000000 +0200
@@ -1358,6 +1358,30 @@ static void scsi_kill_request(struct req
__scsi_done(cmd);
}
+static void scsi_stat_completion(struct scsi_cmnd *cmd)
+{
+#ifdef CONFIG_STATISTICS
+ struct statistic *stat = cmd->device->stat;
+ unsigned size = cmd->request_bufflen;
+ s64 issued = timeval_to_us(&cmd->issued);
+ s64 received = timeval_to_us(&cmd->received);
+ s64 latency = received - issued;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ _statistic_inc(stat, SCSI_STAT_U_R, cmd->result);
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ _statistic_inc(stat, SCSI_STAT_U_SW, size);
+ _statistic_inc(stat, SCSI_STAT_U_LW, latency);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ _statistic_inc(stat, SCSI_STAT_U_SR, size);
+ _statistic_inc(stat, SCSI_STAT_U_LR, latency);
+ } else if (cmd->sc_data_direction == DMA_NONE)
+ _statistic_inc(stat, SCSI_STAT_U_LN, latency);
+ local_irq_restore(flags);
+#endif
+}
+
static void scsi_softirq_done(struct request *rq)
{
struct scsi_cmnd *cmd = rq->completion_data;
@@ -1376,6 +1400,7 @@ static void scsi_softirq_done(struct req
}
scsi_log_completion(cmd, disposition);
+ scsi_stat_completion(cmd);
switch (disposition) {
case SUCCESS:
@@ -1452,6 +1477,7 @@ static void scsi_request_fn(struct reque
if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req)))
blkdev_dequeue_request(req);
sdev->device_busy++;
+ statistic_inc(sdev->stat, SCSI_STAT_U_QUD, sdev->device_busy);
spin_unlock(q->queue_lock);
cmd = req->special;
diff -urp a/include/linux/time.h b/include/linux/time.h
--- a/include/linux/time.h 2006-09-18 09:23:10.000000000 +0200
+++ b/include/linux/time.h 2006-09-18 09:49:30.000000000 +0200
@@ -132,8 +132,20 @@ static inline s64 timespec_to_ns(const s
}
/**
+ * timeval_to_us - Convert timeval to microseconds
+ * @tv: pointer to the timeval variable to be converted
+ *
+ * Returns the scalar nanosecond representation of the timeval
+ * parameter.
+ */
+static inline s64 timeval_to_us(const struct timeval *tv)
+{
+ return ((s64) tv->tv_sec * USEC_PER_SEC) + tv->tv_usec;
+}
+
+/**
* timeval_to_ns - Convert timeval to nanoseconds
- * @ts: pointer to the timeval variable to be converted
+ * @tv: pointer to the timeval variable to be converted
*
* Returns the scalar nanosecond representation of the timeval
* parameter.