From: "Adam Kropelin" <[EMAIL PROTECTED]>
Date: Mon, 16 Apr 2007 23:35:56 -0400
> David Miller wrote:
> > From: "Adam Kropelin" <[EMAIL PROTECTED]>
> > Date: Mon, 16 Apr 2007 18:58:39 -0400
> >
> >> Starting smartd: esp: esp0: Aborting command [fffff80030b05280:2a]
> >> esp: esp0: Active command [fffff80030b05280:2a]
> >> esp: esp0: Aborting command [fffff80030b05280:00]
> >> esp: esp0: Queued command [fffff80030b05280:00]
> >> esp: esp0: Aborting command [fffff80030b05280:00]
> >> esp: esp0: Queued command [fffff80030b05280:00]
> >
> > Let's see what the heck smartd is sending to the device. Please
> > reboot with smartd enabled and post the logs that get output
> > from this patch below applied, thanks.
>
> Here it is:
>
> Starting smartd:
...
Ok I think I nailed this.
I converted the ESP driver over to using auto-requestsense
when CHECK_CONDITION occurs for a scsi command.
The new ESP driver now passes my dbench+smartd-loop stress
test.
Let me know if you have some trouble with this patch.
Thanks in advance for testing!
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
index 2d96fba..fd5548f 100644
--- a/drivers/scsi/esp.c
+++ b/drivers/scsi/esp.c
@@ -47,7 +47,7 @@ static u32 esp_debug;
#define ESP_DEBUG_DATASTART 0x00000080
#define ESP_DEBUG_DATADONE 0x00000100
#define ESP_DEBUG_RECONNECT 0x00000200
-#define ESP_DEBUG_SENSE 0x00000400
+#define ESP_DEBUG_AUTOSENSE 0x00000400
#define esp_log_intr(f, a...) \
do { if (esp_debug & ESP_DEBUG_INTR) \
@@ -94,8 +94,8 @@ do { if (esp_debug & ESP_DEBUG_RECONNECT) \
printk(f, ## a); \
} while (0)
-#define esp_log_sense(f, a...) \
-do { if (esp_debug & ESP_DEBUG_SENSE) \
+#define esp_log_autosense(f, a...) \
+do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \
printk(f, ## a); \
} while (0)
@@ -537,10 +537,15 @@ static void esp_map_dma(struct esp *esp, struct scsi_cmnd
*cmd)
}
}
-static dma_addr_t esp_cur_dma_addr(struct scsi_cmnd *cmd)
+static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd)
{
struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ return ent->sense_dma +
+ (ent->sense_ptr - cmd->sense_buffer);
+ }
if (p->mapping_type == MAPPING_TYPE_SINGLE) {
return p->u.dma_addr +
(cmd->request_bufflen -
@@ -553,18 +558,28 @@ static dma_addr_t esp_cur_dma_addr(struct scsi_cmnd *cmd)
}
}
-static unsigned int esp_cur_dma_len(struct scsi_cmnd *cmd)
+static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd)
{
struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ return SCSI_SENSE_BUFFERSIZE -
+ (ent->sense_ptr - cmd->sense_buffer);
+ }
return p->cur_residue;
}
-static void esp_advance_dma(struct esp *esp, struct scsi_cmnd *cmd,
- unsigned int len)
+static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent,
+ struct scsi_cmnd *cmd, unsigned int len)
{
struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->sense_ptr += len;
+ return;
+ }
+
p->cur_residue -= len;
p->tot_residue -= len;
if (p->cur_residue < 0 || p->tot_residue < 0) {
@@ -612,6 +627,10 @@ static void esp_save_pointers(struct esp *esp, struct
esp_cmd_entry *ent)
struct scsi_cmnd *cmd = ent->cmd;
struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->saved_sense_ptr = ent->sense_ptr;
+ return;
+ }
ent->saved_cur_residue = spriv->cur_residue;
ent->saved_cur_sg = spriv->cur_sg;
ent->saved_tot_residue = spriv->tot_residue;
@@ -622,6 +641,10 @@ static void esp_restore_pointers(struct esp *esp, struct
esp_cmd_entry *ent)
struct scsi_cmnd *cmd = ent->cmd;
struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd);
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->sense_ptr = ent->saved_sense_ptr;
+ return;
+ }
spriv->cur_residue = ent->saved_cur_residue;
spriv->cur_sg = ent->saved_cur_sg;
spriv->tot_residue = ent->saved_tot_residue;
@@ -834,8 +857,7 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent,
* the queue and run this untagged command.
*/
lp->hold = 0;
- } else if (lp->num_tagged &&
- ent->cmd->cmnd[0] != REQUEST_SENSE) {
+ } else if (lp->num_tagged) {
/* Plug the queue until num_tagged decreases
* to zero in esp_free_lun_tag.
*/
@@ -877,28 +899,67 @@ static void esp_free_lun_tag(struct esp_cmd_entry *ent,
}
}
-static int force_nontagged(struct scsi_cmnd *cmd)
+/* When a contingent allegiance conditon is created, we force feed a
+ * REQUEST_SENSE command to the device to fetch the sense data. I
+ * tried many other schemes, relying on the scsi error handling layer
+ * to send out the REQUEST_SENSE automatically, but this was difficult
+ * to get right especially in the presence of applications like smartd
+ * which use SG_IO to send out their own REQUEST_SENSE commands.
+ */
+static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent)
{
- /* Why force INQUIRY non-tagged? The reason is that I've seen
- * some seagate drives do some weird things when they were
- * allowed to disconnect an INQUIRY command. This particular
- * disk was configured for 16-bit wide transfers, it would
- * go to DATA phase, transfer 12 bytes of the INQUIRY response,
- * spit out an IGNORE_WIDE_RESIDUE message, disconnect, then
- * reconnect and send the whole INQUIRY response. These INQUIRY
- * requests were generated by udev's "scsi_id" command.
- *
- * This probably accounts for the BLIST_NOTQ seagate entries in
- * scsi_devinfo.c, one of which states "Chokes on tagged INQUIRY".
- *
- * All of this is just silly and asking for trouble, so just do
- * INQUIRY commands non-tagged.
- */
- if (cmd->cmnd[0] == REQUEST_SENSE ||
- cmd->cmnd[0] == INQUIRY)
- return 1;
+ struct scsi_cmnd *cmd = ent->cmd;
+ struct scsi_device *dev = cmd->device;
+ int tgt, lun;
+ u8 *p, val;
- return 0;
+ tgt = dev->id;
+ lun = dev->lun;
+
+
+ if (!ent->sense_ptr) {
+ esp_log_autosense("esp%d: Doing auto-sense for "
+ "tgt[%d] lun[%d]\n",
+ esp->host->unique_id, tgt, lun);
+
+ ent->sense_ptr = cmd->sense_buffer;
+ ent->sense_dma = sbus_map_single(esp->sbus_dev,
+ ent->sense_ptr,
+ SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
+ }
+ ent->saved_sense_ptr = ent->sense_ptr;
+
+ esp->active_cmd = ent;
+
+ p = esp->command_block;
+ esp->msg_out_len = 0;
+
+ *p++ = IDENTIFY(0, lun);
+ *p++ = REQUEST_SENSE;
+ *p++ = ((dev->scsi_level <= SCSI_2) ?
+ (lun << 5) : 0);
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = SCSI_SENSE_BUFFERSIZE;
+ *p++ = 0;
+
+ esp->select_state = ESP_SELECT_BASIC;
+
+ val = tgt;
+ if (esp->rev == FASHME)
+ val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT;
+ esp_write8(val, ESP_BUSID);
+
+ esp_write_tgt_sync(esp, tgt);
+ esp_write_tgt_config3(esp, tgt);
+
+ val = (p - esp->command_block);
+
+ if (esp->rev == FASHME)
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ esp_send_dma_command(esp, esp->command_block_dma,
+ val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA);
}
static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
@@ -913,8 +974,13 @@ static struct esp_cmd_entry
*find_and_prep_issuable_command(struct esp *esp)
int tgt = dev->id;
int lun = dev->lun;
- if (force_nontagged(cmd) ||
- !scsi_populate_tag_msg(cmd, &ent->tag[0])) {
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ ent->tag[0] = 0;
+ ent->tag[1] = 0;
+ return ent;
+ }
+
+ if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) {
ent->tag[0] = 0;
ent->tag[1] = 0;
}
@@ -949,6 +1015,11 @@ static void esp_maybe_execute_command(struct esp *esp)
if (!ent)
return;
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ esp_autosense(esp, ent);
+ return;
+ }
+
cmd = ent->cmd;
dev = cmd->device;
tgt = dev->id;
@@ -1112,23 +1183,32 @@ static void esp_cmd_is_done(struct esp *esp, struct
esp_cmd_entry *ent,
ent->eh_done = NULL;
}
- if (cmd->cmnd[0] == REQUEST_SENSE) {
- unsigned char *buf;
- int i;
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ sbus_unmap_single(esp->sbus_dev, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
- if (cmd->use_sg == 0)
- buf = cmd->request_buffer;
- else {
- struct scatterlist *sg = cmd->request_buffer;
+ /* Restore the message/status bytes to what we actually
+ * saw originally. Also, report that we are providing
+ * the sense data.
+ */
+ cmd->result = ((DRIVER_SENSE << 24) |
+ (DID_OK << 16) |
+ (COMMAND_COMPLETE << 8) |
+ (SAM_STAT_CHECK_CONDITION << 0));
- buf = page_address(sg[0].page) + sg[0].offset;
- }
+ ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE;
+ if (esp_debug & ESP_DEBUG_AUTOSENSE) {
+ int i;
- esp_log_sense("ESP: SENSE [ ");
- for (i = 0; i < 18; i++)
- esp_log_sense("%02x ", buf[i]);
- esp_log_sense("]\n");
+ printk("esp%d: tgt[%d] lun[%d] AUTO SENSE[ ",
+ esp->host->unique_id, tgt, lun);
+ for (i = 0; i < 18; i++)
+ printk("%02x ", cmd->sense_buffer[i]);
+ printk("]\n");
+ }
}
+
cmd->scsi_done(cmd);
list_del(&ent->list);
@@ -1490,12 +1570,19 @@ static int esp_finish_select(struct esp *esp)
* resources (such as DMA mapping & TAG) and reset state (such
* as message out and command delivery variables).
*/
- esp_unmap_dma(esp, cmd);
- esp_free_lun_tag(ent, tp->lun[cmd->device->lun]);
- tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
- esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
- esp->cmd_bytes_ptr = NULL;
- esp->cmd_bytes_left = 0;
+ if (!(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+ esp_unmap_dma(esp, cmd);
+ esp_free_lun_tag(ent, tp->lun[cmd->device->lun]);
+ tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE);
+ esp->flags &= ~ESP_FLAG_DOING_SLOWCMD;
+ esp->cmd_bytes_ptr = NULL;
+ esp->cmd_bytes_left = 0;
+ } else {
+ sbus_unmap_single(esp->sbus_dev, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+ }
/* Now that the state is unwound properly, put back onto
* the issue queue. This command is no longer active.
@@ -1947,8 +2034,8 @@ again:
case ESP_EVENT_DATA_OUT: {
struct esp_cmd_entry *ent = esp->active_cmd;
struct scsi_cmnd *cmd = ent->cmd;
- dma_addr_t dma_addr = esp_cur_dma_addr(cmd);
- unsigned int dma_len = esp_cur_dma_len(cmd);
+ dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd);
+ unsigned int dma_len = esp_cur_dma_len(ent, cmd);
if (esp->rev == ESP100)
esp_cmd(esp, ESP_CMD_NULL);
@@ -1966,7 +2053,8 @@ again:
esp->host->unique_id);
printk(KERN_ERR PFX "esp%d: cur adr[%08x] len[%08x]\n",
esp->host->unique_id,
- esp_cur_dma_addr(cmd), esp_cur_dma_len(cmd));
+ esp_cur_dma_addr(ent, cmd),
+ esp_cur_dma_len(ent, cmd));
esp_schedule_reset(esp);
return 0;
}
@@ -2020,7 +2108,7 @@ again:
return 0;
}
- esp_advance_dma(esp, cmd, bytes_sent);
+ esp_advance_dma(esp, ent, cmd, bytes_sent);
esp_event(esp, ESP_EVENT_CHECK_PHASE);
goto again;
break;
@@ -2065,10 +2153,17 @@ again:
ent->status, ent->message);
if (ent->status == SAM_STAT_TASK_SET_FULL)
esp_event_queue_full(esp, ent);
- esp_cmd_is_done(esp, ent, cmd,
- compose_result(ent->status,
- ent->message,
- DID_OK));
+
+ if (ent->status == SAM_STAT_CHECK_CONDITION &&
+ !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
+ ent->flags |= ESP_CMD_FLAG_AUTOSENSE;
+ esp_autosense(esp, ent);
+ } else {
+ esp_cmd_is_done(esp, ent, cmd,
+ compose_result(ent->status,
+ ent->message,
+ DID_OK));
+ }
} else if (ent->message == DISCONNECT) {
esp_log_disconnect("ESP: Disconnecting tgt[%d] "
"tag[%x:%x]\n",
@@ -2236,6 +2331,13 @@ static void esp_reset_cleanup_one(struct esp *esp,
struct esp_cmd_entry *ent)
esp_unmap_dma(esp, cmd);
esp_free_lun_tag(ent, esp->target[tgt].lun[lun]);
cmd->result = DID_RESET << 16;
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) {
+ sbus_unmap_single(esp->sbus_dev, ent->sense_dma,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ ent->sense_ptr = NULL;
+ }
+
cmd->scsi_done(cmd);
list_del(&ent->list);
esp_put_ent(esp, ent);
@@ -3273,7 +3375,7 @@ MODULE_PARM_DESC(esp_debug,
" 0x00000080 Log data start\n"
" 0x00000100 Log data done\n"
" 0x00000200 Log reconnects\n"
-" 0x00000400 Log sense data\n"
+" 0x00000400 Log auto-sense data\n"
);
module_init(esp_init);
diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h
index bd3237d..3d18dfa 100644
--- a/drivers/scsi/esp.h
+++ b/drivers/scsi/esp.h
@@ -275,12 +275,17 @@ struct esp_cmd_entry {
u8 flags;
#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */
#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */
+#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */
u8 tag[2];
u8 status;
u8 message;
+ unsigned char *sense_ptr;
+ unsigned char *saved_sense_ptr;
+ dma_addr_t sense_dma;
+
struct completion *eh_done;
};
-
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html