Here is a fun patch in an early state.  Essentially I want to trace
scsi commands, which has already been done long ago.  The problem I
have is that I care about all the scsi commands for one particular
device - without knowing in advance which device it will be.  Once I
know the device in question, I want to dump the last X commands.

The existing tracing is covering all commands to all devices.  So
either I have to oversize my trace buffer and dump way too much, or I
will miss the stuff I am interested in most of the time.  Hence my
per-device trace buffer (called fdr to avoid namespace collisions).

Steven, is there some magic 2-line patch that would also give me what
I want without having to reinvent all your infrastructure and doing
such a bad job?

Jörn

--
It is better to die of hunger having lived without grief and fear,
than to live with a troubled spirit amid abundance.
-- Epictetus


And dump the fdr whenever we run into a timeout.  If the timeout is
caused by a crashed target and the target crash is triggered by a weird
command sent by us, we might want to know which commands we send out.

Signed-off-by: Joern Engel <[email protected]>
---
 drivers/scsi/scsi.c        |   31 +++++++++++++++++++++++++++++++
 drivers/scsi/scsi_error.c  |   38 ++++++++++++++++++++++++++++++++++++++
 drivers/scsi/scsi_scan.c   |    1 +
 include/scsi/scsi_device.h |   13 +++++++++++++
 4 files changed, 83 insertions(+)

diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a884f13..54168bb 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -647,6 +647,36 @@ void scsi_cmd_get_serial(struct Scsi_Host *host, struct 
scsi_cmnd *cmd)
 }
 EXPORT_SYMBOL(scsi_cmd_get_serial);
 
+static void fdr_add(struct fdr_ring *ring, struct fdr_entry *entry)
+{
+       struct fdr_entry *old;
+       unsigned long flags;
+       int index;
+
+       spin_lock_irqsave(&ring->fdr_lock, flags);
+       index = ring->last_entry + 1;
+       if (index >= ARRAY_SIZE(ring->entry))
+               index = 0;
+       old = ring->entry[index];
+       ring->entry[index] = entry;
+       ring->last_entry = index;
+       spin_unlock_irqrestore(&ring->fdr_lock, flags);
+       kfree(old);
+}
+
+static void fdr_scsi_cmd(struct scsi_cmnd *cmd)
+{
+       struct fdr_entry *new;
+
+       new = kmalloc(sizeof(*new) + cmd->cmd_len, GFP_KERNEL);
+       if (!new)
+               return;
+       new->ts_nsec = local_clock();
+       new->size = cmd->cmd_len;
+       memcpy(new->data, cmd->cmnd, new->size);
+       fdr_add(&cmd->device->fdr, new);
+}
+
 /**
  * scsi_dispatch_command - Dispatch a command to the low-level driver.
  * @cmd: command block we are dispatching.
@@ -747,6 +777,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
                scsi_done(cmd);
        } else {
                trace_scsi_dispatch_cmd_start(cmd);
+               fdr_scsi_cmd(cmd);
                cmd->scsi_done = scsi_done;
                rtn = host->hostt->queuecommand(host, cmd);
        }
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 0dfae50..829cb74 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -132,6 +132,41 @@ static void queue_for_eh(struct Scsi_Host *shost, struct 
scsi_cmnd *scmd)
        schedule_work(&scmd->abort_work);
 }
 
+static void scsi_fdr_dump(struct fdr_entry *entry)
+{
+       char buf[64 + 8 + 2];
+       unsigned long ts = entry->ts_nsec;
+       unsigned long rem_nsec;
+
+       rem_nsec = do_div(ts, 1000000000);
+       hex_dump_to_buffer(entry->data, entry->size, 32, 8, buf, sizeof(buf), 
0);
+       pr_info("%6lu.%06lu: %s\n", ts, rem_nsec, buf);
+}
+
+static void fdr_dump(struct fdr_ring *ring, void (*dump) (struct fdr_entry * 
entry))
+{
+       struct fdr_entry *entry;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&ring->fdr_lock, flags);
+       for (i = ring->last_entry + 1; i < ARRAY_SIZE(ring->entry); i++) {
+               entry = ring->entry[i];
+               ring->entry[i] = NULL;
+               if (entry)
+                       dump(entry);
+               kfree(entry);
+       }
+       for (i = 0; i < ring->last_entry; i++) {
+               entry = ring->entry[i];
+               ring->entry[i] = NULL;
+               if (entry)
+                       dump(entry);
+               kfree(entry);
+       }
+       spin_unlock_irqrestore(&ring->fdr_lock, flags);
+}
+
 /**
  * scsi_times_out - Timeout function for normal scsi commands.
  * @req:       request that is timing out.
@@ -148,6 +183,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
        enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED;
        struct Scsi_Host *host = scmd->device->host;
 
+       sdev_printk(KERN_INFO, scmd->device, "dumping cdb recorder\n");
+       fdr_dump(&scmd->device->fdr, scsi_fdr_dump);
+
        trace_scsi_dispatch_cmd_timeout(scmd);
        scsi_log_completion(scmd, TIMEOUT_ERROR);
 
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index db89445..85dbae4 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -252,6 +252,7 @@ static struct scsi_device *scsi_alloc_sdev(struct 
scsi_target *starget,
        INIT_LIST_HEAD(&sdev->starved_entry);
        INIT_LIST_HEAD(&sdev->event_list);
        spin_lock_init(&sdev->list_lock);
+       spin_lock_init(&sdev->fdr.fdr_lock);
        INIT_WORK(&sdev->event_work, scsi_evt_thread);
        INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
        /* Limit scsi messages per device */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index bafc41c..00b7e9b 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -97,6 +97,18 @@ enum scsi_err_inj_duration {
 };
 #endif /* CONFIG_SCSI_ERROR_INJECTOR */
 
+struct fdr_entry {
+       unsigned long ts_nsec;
+       u32 size;
+       u8 data[0];
+};
+
+struct fdr_ring {
+       spinlock_t fdr_lock;
+       int last_entry;
+       struct fdr_entry *entry[1000];
+};
+
 struct scsi_device {
        struct Scsi_Host *host;
        struct request_queue *request_queue;
@@ -222,6 +234,7 @@ struct scsi_device {
        struct scsi_dh_data     *scsi_dh_data;
        enum scsi_device_state sdev_state;
        struct ratelimit_state  sdev_ratelimit;
+       struct fdr_ring         fdr;
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-- 
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to