Commit 09a0f719 authored by Vikas Chaudhary's avatar Vikas Chaudhary Committed by James Bottomley

[SCSI] qla4xxx: added support for abort task management command

* Handles SCSI command aborts.
* Serialization srb between error handler and command
  completion path.
Signed-off-by: default avatarVikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: default avatarRavi Anand <ravi.anand@qlogic.com>
Reviewed-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 5369887a
...@@ -172,7 +172,7 @@ struct srb { ...@@ -172,7 +172,7 @@ struct srb {
struct scsi_cmnd *cmd; /* (4) SCSI command block */ struct scsi_cmnd *cmd; /* (4) SCSI command block */
dma_addr_t dma_handle; /* (4) for unmap of single transfers */ dma_addr_t dma_handle; /* (4) for unmap of single transfers */
atomic_t ref_count; /* reference count for this srb */ struct kref srb_ref; /* reference count for this srb */
uint32_t fw_ddb_index; uint32_t fw_ddb_index;
uint8_t err_id; /* error id */ uint8_t err_id; /* error id */
#define SRB_ERR_PORT 1 /* Request failed because "port down" */ #define SRB_ERR_PORT 1 /* Request failed because "port down" */
......
...@@ -215,6 +215,7 @@ union external_hw_config_reg { ...@@ -215,6 +215,7 @@ union external_hw_config_reg {
/* Mailbox command definitions */ /* Mailbox command definitions */
#define MBOX_CMD_ABOUT_FW 0x0009 #define MBOX_CMD_ABOUT_FW 0x0009
#define MBOX_CMD_PING 0x000B #define MBOX_CMD_PING 0x000B
#define MBOX_CMD_ABORT_TASK 0x0015
#define MBOX_CMD_LUN_RESET 0x0016 #define MBOX_CMD_LUN_RESET 0x0016
#define MBOX_CMD_TARGET_WARM_RESET 0x0017 #define MBOX_CMD_TARGET_WARM_RESET 0x0017
#define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E
......
...@@ -25,6 +25,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); ...@@ -25,6 +25,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen);
int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha);
int qla4xxx_relogin_device(struct scsi_qla_host * ha, int qla4xxx_relogin_device(struct scsi_qla_host * ha,
struct ddb_entry * ddb_entry); struct ddb_entry * ddb_entry);
int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb);
int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry,
int lun); int lun);
int qla4xxx_reset_target(struct scsi_qla_host * ha, int qla4xxx_reset_target(struct scsi_qla_host * ha,
...@@ -65,7 +66,7 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, ...@@ -65,7 +66,7 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha,
int qla4xxx_init_rings(struct scsi_qla_host * ha); int qla4xxx_init_rings(struct scsi_qla_host * ha);
struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha,
uint32_t index); uint32_t index);
void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); void qla4xxx_srb_compl(struct kref *ref);
int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha);
int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
uint32_t state, uint32_t conn_error); uint32_t state, uint32_t conn_error);
......
...@@ -97,7 +97,7 @@ qla4xxx_status_cont_entry(struct scsi_qla_host *ha, ...@@ -97,7 +97,7 @@ qla4xxx_status_cont_entry(struct scsi_qla_host *ha,
/* Place command on done queue. */ /* Place command on done queue. */
if (srb->req_sense_len == 0) { if (srb->req_sense_len == 0) {
qla4xxx_srb_compl(ha, srb); kref_put(&srb->srb_ref, qla4xxx_srb_compl);
ha->status_srb = NULL; ha->status_srb = NULL;
} }
} }
...@@ -329,7 +329,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, ...@@ -329,7 +329,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
/* complete the request, if not waiting for status_continuation pkt */ /* complete the request, if not waiting for status_continuation pkt */
srb->cc_stat = sts_entry->completionStatus; srb->cc_stat = sts_entry->completionStatus;
if (ha->status_srb == NULL) if (ha->status_srb == NULL)
qla4xxx_srb_compl(ha, srb); kref_put(&srb->srb_ref, qla4xxx_srb_compl);
} }
/** /**
...@@ -393,7 +393,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) ...@@ -393,7 +393,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
/* ETRY normally by sending it back with /* ETRY normally by sending it back with
* DID_BUS_BUSY */ * DID_BUS_BUSY */
srb->cmd->result = DID_BUS_BUSY << 16; srb->cmd->result = DID_BUS_BUSY << 16;
qla4xxx_srb_compl(ha, srb); kref_put(&srb->srb_ref, qla4xxx_srb_compl);
break; break;
case ET_CONTINUE: case ET_CONTINUE:
......
...@@ -761,6 +761,59 @@ void qla4xxx_get_conn_event_log(struct scsi_qla_host * ha) ...@@ -761,6 +761,59 @@ void qla4xxx_get_conn_event_log(struct scsi_qla_host * ha)
event_log_dma); event_log_dma);
} }
/**
* qla4xxx_abort_task - issues Abort Task
* @ha: Pointer to host adapter structure.
* @srb: Pointer to srb entry
*
* This routine performs a LUN RESET on the specified target/lun.
* The caller must ensure that the ddb_entry and lun_entry pointers
* are valid before calling this routine.
**/
int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb)
{
uint32_t mbox_cmd[MBOX_REG_COUNT];
uint32_t mbox_sts[MBOX_REG_COUNT];
struct scsi_cmnd *cmd = srb->cmd;
int status = QLA_SUCCESS;
unsigned long flags = 0;
uint32_t index;
/*
* Send abort task command to ISP, so that the ISP will return
* request with ABORT status
*/
memset(&mbox_cmd, 0, sizeof(mbox_cmd));
memset(&mbox_sts, 0, sizeof(mbox_sts));
spin_lock_irqsave(&ha->hardware_lock, flags);
index = (unsigned long)(unsigned char *)cmd->host_scribble;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Firmware already posted completion on response queue */
if (index == MAX_SRBS)
return status;
mbox_cmd[0] = MBOX_CMD_ABORT_TASK;
mbox_cmd[1] = srb->fw_ddb_index;
mbox_cmd[2] = index;
/* Immediate Command Enable */
mbox_cmd[5] = 0x01;
qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0],
&mbox_sts[0]);
if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) {
status = QLA_ERROR;
DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%d: abort task FAILED: "
"mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n",
ha->host_no, cmd->device->id, cmd->device->lun, mbox_sts[0],
mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4]));
}
return status;
}
/** /**
* qla4xxx_reset_lun - issues LUN Reset * qla4xxx_reset_lun - issues LUN Reset
* @ha: Pointer to host adapter structure. * @ha: Pointer to host adapter structure.
......
...@@ -74,6 +74,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc); ...@@ -74,6 +74,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
*/ */
static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
void (*done) (struct scsi_cmnd *)); void (*done) (struct scsi_cmnd *));
static int qla4xxx_eh_abort(struct scsi_cmnd *cmd);
static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
...@@ -88,6 +89,7 @@ static struct scsi_host_template qla4xxx_driver_template = { ...@@ -88,6 +89,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
.proc_name = DRIVER_NAME, .proc_name = DRIVER_NAME,
.queuecommand = qla4xxx_queuecommand, .queuecommand = qla4xxx_queuecommand,
.eh_abort_handler = qla4xxx_eh_abort,
.eh_device_reset_handler = qla4xxx_eh_device_reset, .eh_device_reset_handler = qla4xxx_eh_device_reset,
.eh_target_reset_handler = qla4xxx_eh_target_reset, .eh_target_reset_handler = qla4xxx_eh_target_reset,
.eh_host_reset_handler = qla4xxx_eh_host_reset, .eh_host_reset_handler = qla4xxx_eh_host_reset,
...@@ -384,7 +386,7 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, ...@@ -384,7 +386,7 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
if (!srb) if (!srb)
return srb; return srb;
atomic_set(&srb->ref_count, 1); kref_init(&srb->srb_ref);
srb->ha = ha; srb->ha = ha;
srb->ddb = ddb_entry; srb->ddb = ddb_entry;
srb->cmd = cmd; srb->cmd = cmd;
...@@ -406,9 +408,11 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb) ...@@ -406,9 +408,11 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb)
CMD_SP(cmd) = NULL; CMD_SP(cmd) = NULL;
} }
void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb) void qla4xxx_srb_compl(struct kref *ref)
{ {
struct srb *srb = container_of(ref, struct srb, srb_ref);
struct scsi_cmnd *cmd = srb->cmd; struct scsi_cmnd *cmd = srb->cmd;
struct scsi_qla_host *ha = srb->ha;
qla4xxx_srb_free_dma(ha, srb); qla4xxx_srb_free_dma(ha, srb);
...@@ -887,7 +891,7 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) ...@@ -887,7 +891,7 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha)
srb = qla4xxx_del_from_active_array(ha, i); srb = qla4xxx_del_from_active_array(ha, i);
if (srb != NULL) { if (srb != NULL) {
srb->cmd->result = DID_RESET << 16; srb->cmd->result = DID_RESET << 16;
qla4xxx_srb_compl(ha, srb); kref_put(&srb->srb_ref, qla4xxx_srb_compl);
} }
} }
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
...@@ -1501,7 +1505,7 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in ...@@ -1501,7 +1505,7 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in
/** /**
* qla4xxx_eh_wait_on_command - waits for command to be returned by firmware * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware
* @ha: actual ha whose done queue will contain the comd returned by firmware. * @ha: Pointer to host adapter structure.
* @cmd: Scsi Command to wait on. * @cmd: Scsi Command to wait on.
* *
* This routine waits for the command to be returned by the Firmware * This routine waits for the command to be returned by the Firmware
...@@ -1584,6 +1588,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha, ...@@ -1584,6 +1588,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha,
return status; return status;
} }
/**
* qla4xxx_eh_abort - callback for abort task.
* @cmd: Pointer to Linux's SCSI command structure
*
* This routine is called by the Linux OS to abort the specified
* command.
**/
static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
{
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
unsigned int id = cmd->device->id;
unsigned int lun = cmd->device->lun;
unsigned long serial = cmd->serial_number;
struct srb *srb = NULL;
int ret = SUCCESS;
int wait = 0;
dev_info(&ha->pdev->dev,
"scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n",
ha->host_no, id, lun, cmd, serial);
srb = (struct srb *) CMD_SP(cmd);
if (!srb)
return SUCCESS;
kref_get(&srb->srb_ref);
if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) {
DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n",
ha->host_no, id, lun));
ret = FAILED;
} else {
DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n",
ha->host_no, id, lun));
wait = 1;
}
kref_put(&srb->srb_ref, qla4xxx_srb_compl);
/* Wait for command to complete */
if (wait) {
if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n",
ha->host_no, id, lun));
ret = FAILED;
}
}
dev_info(&ha->pdev->dev,
"scsi%ld:%d:%d: Abort command - %s\n",
ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed");
return ret;
}
/** /**
* qla4xxx_eh_device_reset - callback for target reset. * qla4xxx_eh_device_reset - callback for target reset.
* @cmd: Pointer to Linux's SCSI command structure * @cmd: Pointer to Linux's SCSI command structure
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment