scsi: aacraid: Added support to abort cmd and reset lun
Added task management command support to abort any timed out commands in case of a eh_abort call and to reset lun's in case of eh_reset call. Signed-off-by: Raghava Aditya Renukunta <RaghavaAditya.Renukunta@microsemi.com> Signed-off-by: Dave Carroll <David.Carroll@microsemi.com> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Родитель
ab5d129f93
Коммит
954b2b5ac7
|
@ -206,6 +206,53 @@ struct aac_hba_cmd_req {
|
|||
*/
|
||||
};
|
||||
|
||||
/* Task Management Functions (TMF) */
|
||||
#define HBA_TMF_ABORT_TASK 0x01
|
||||
#define HBA_TMF_LUN_RESET 0x08
|
||||
|
||||
struct aac_hba_tm_req {
|
||||
u8 iu_type; /* HBA information unit type */
|
||||
u8 reply_qid; /* Host reply queue to post response to */
|
||||
u8 tmf; /* Task management function */
|
||||
u8 reserved1;
|
||||
|
||||
__le32 it_nexus; /* Device handle for the command */
|
||||
|
||||
u8 lun[8]; /* SCSI LUN */
|
||||
|
||||
/* Used to hold sender context. */
|
||||
__le32 request_id; /* Sender context */
|
||||
__le32 reserved2;
|
||||
|
||||
/* Request identifier of managed task */
|
||||
__le32 managed_request_id; /* Sender context being managed */
|
||||
__le32 reserved3;
|
||||
|
||||
/* Lower 32-bits of reserved error data target location on the host */
|
||||
__le32 error_ptr_lo;
|
||||
/* Upper 32-bits of reserved error data target location on the host */
|
||||
__le32 error_ptr_hi;
|
||||
/* Length of reserved error data area on the host in bytes */
|
||||
__le32 error_length;
|
||||
};
|
||||
|
||||
struct aac_hba_reset_req {
|
||||
u8 iu_type; /* HBA information unit type */
|
||||
/* 0 - reset specified device, 1 - reset all devices */
|
||||
u8 reset_type;
|
||||
u8 reply_qid; /* Host reply queue to post response to */
|
||||
u8 reserved1;
|
||||
|
||||
__le32 it_nexus; /* Device handle for the command */
|
||||
__le32 request_id; /* Sender context */
|
||||
/* Lower 32-bits of reserved error data target location on the host */
|
||||
__le32 error_ptr_lo;
|
||||
/* Upper 32-bits of reserved error data target location on the host */
|
||||
__le32 error_ptr_hi;
|
||||
/* Length of reserved error data area on the host in bytes */
|
||||
__le32 error_length;
|
||||
};
|
||||
|
||||
struct aac_hba_resp {
|
||||
u8 iu_type; /* HBA information unit type */
|
||||
u8 reserved1[3];
|
||||
|
@ -223,6 +270,7 @@ struct aac_hba_resp {
|
|||
struct aac_native_hba {
|
||||
union {
|
||||
struct aac_hba_cmd_req cmd;
|
||||
struct aac_hba_tm_req tmr;
|
||||
u8 cmd_bytes[AAC_MAX_NATIVE_SIZE-FW_ERROR_BUFFER_SIZE];
|
||||
} cmd;
|
||||
union {
|
||||
|
|
|
@ -574,46 +574,136 @@ static int aac_eh_abort(struct scsi_cmnd* cmd)
|
|||
struct scsi_device * dev = cmd->device;
|
||||
struct Scsi_Host * host = dev->host;
|
||||
struct aac_dev * aac = (struct aac_dev *)host->hostdata;
|
||||
int count;
|
||||
int count, found;
|
||||
u32 bus, cid;
|
||||
int ret = FAILED;
|
||||
|
||||
printk(KERN_ERR "%s: Host adapter abort request (%d,%d,%d,%llu)\n",
|
||||
AAC_DRIVERNAME,
|
||||
host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun);
|
||||
switch (cmd->cmnd[0]) {
|
||||
case SERVICE_ACTION_IN_16:
|
||||
if (!(aac->raw_io_interface) ||
|
||||
!(aac->raw_io_64) ||
|
||||
((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
|
||||
break;
|
||||
case INQUIRY:
|
||||
case READ_CAPACITY:
|
||||
/* Mark associated FIB to not complete, eh handler does this */
|
||||
bus = aac_logical_to_phys(scmd_channel(cmd));
|
||||
cid = scmd_id(cmd);
|
||||
if (aac->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
|
||||
struct fib *fib;
|
||||
struct aac_hba_tm_req *tmf;
|
||||
int status;
|
||||
u64 address;
|
||||
__le32 managed_request_id;
|
||||
|
||||
pr_err("%s: Host adapter abort request (%d,%d,%d,%d)\n",
|
||||
AAC_DRIVERNAME,
|
||||
host->host_no, sdev_channel(dev), sdev_id(dev), (int)dev->lun);
|
||||
|
||||
found = 0;
|
||||
for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
|
||||
struct fib * fib = &aac->fibs[count];
|
||||
if (fib->hw_fib_va->header.XferState &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
(fib->callback_data == cmd)) {
|
||||
fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
|
||||
ret = SUCCESS;
|
||||
fib = &aac->fibs[count];
|
||||
if (*(u8 *)fib->hw_fib_va != 0 &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) &&
|
||||
(fib->callback_data == cmd)) {
|
||||
found = 1;
|
||||
managed_request_id = ((struct aac_hba_cmd_req *)
|
||||
fib->hw_fib_va)->request_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TEST_UNIT_READY:
|
||||
/* Mark associated FIB to not complete, eh handler does this */
|
||||
for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
|
||||
struct scsi_cmnd * command;
|
||||
struct fib * fib = &aac->fibs[count];
|
||||
if ((fib->hw_fib_va->header.XferState & cpu_to_le32(Async | NoResponseExpected)) &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
((command = fib->callback_data)) &&
|
||||
(command->device == cmd->device)) {
|
||||
fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
command->SCp.phase = AAC_OWNER_ERROR_HANDLER;
|
||||
if (command == cmd)
|
||||
ret = SUCCESS;
|
||||
if (!found)
|
||||
return ret;
|
||||
|
||||
/* start a HBA_TMF_ABORT_TASK TMF request */
|
||||
fib = aac_fib_alloc(aac);
|
||||
if (!fib)
|
||||
return ret;
|
||||
|
||||
tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
|
||||
memset(tmf, 0, sizeof(*tmf));
|
||||
tmf->tmf = HBA_TMF_ABORT_TASK;
|
||||
tmf->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
|
||||
tmf->lun[1] = cmd->device->lun;
|
||||
|
||||
address = (u64)fib->hw_error_pa;
|
||||
tmf->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
|
||||
tmf->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
|
||||
tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
|
||||
|
||||
fib->hbacmd_size = sizeof(*tmf);
|
||||
cmd->SCp.sent_command = 0;
|
||||
|
||||
status = aac_hba_send(HBA_IU_TYPE_SCSI_TM_REQ, fib,
|
||||
(fib_callback) aac_hba_callback,
|
||||
(void *) cmd);
|
||||
|
||||
/* Wait up to 2 minutes for completion */
|
||||
for (count = 0; count < 120; ++count) {
|
||||
if (cmd->SCp.sent_command) {
|
||||
ret = SUCCESS;
|
||||
break;
|
||||
}
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
if (ret != SUCCESS)
|
||||
pr_err("%s: Host adapter abort request timed out\n",
|
||||
AAC_DRIVERNAME);
|
||||
} else {
|
||||
pr_err(
|
||||
"%s: Host adapter abort request.\n"
|
||||
"%s: Outstanding commands on (%d,%d,%d,%d):\n",
|
||||
AAC_DRIVERNAME, AAC_DRIVERNAME,
|
||||
host->host_no, sdev_channel(dev), sdev_id(dev),
|
||||
(int)dev->lun);
|
||||
switch (cmd->cmnd[0]) {
|
||||
case SERVICE_ACTION_IN_16:
|
||||
if (!(aac->raw_io_interface) ||
|
||||
!(aac->raw_io_64) ||
|
||||
((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
|
||||
break;
|
||||
case INQUIRY:
|
||||
case READ_CAPACITY:
|
||||
/*
|
||||
* Mark associated FIB to not complete,
|
||||
* eh handler does this
|
||||
*/
|
||||
for (count = 0;
|
||||
count < (host->can_queue + AAC_NUM_MGT_FIB);
|
||||
++count) {
|
||||
struct fib *fib = &aac->fibs[count];
|
||||
|
||||
if (fib->hw_fib_va->header.XferState &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
(fib->callback_data == cmd)) {
|
||||
fib->flags |=
|
||||
FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
cmd->SCp.phase =
|
||||
AAC_OWNER_ERROR_HANDLER;
|
||||
ret = SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TEST_UNIT_READY:
|
||||
/*
|
||||
* Mark associated FIB to not complete,
|
||||
* eh handler does this
|
||||
*/
|
||||
for (count = 0;
|
||||
count < (host->can_queue + AAC_NUM_MGT_FIB);
|
||||
++count) {
|
||||
struct scsi_cmnd *command;
|
||||
struct fib *fib = &aac->fibs[count];
|
||||
|
||||
command = fib->callback_data;
|
||||
|
||||
if ((fib->hw_fib_va->header.XferState &
|
||||
cpu_to_le32
|
||||
(Async | NoResponseExpected)) &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
((command)) &&
|
||||
(command->device == cmd->device)) {
|
||||
fib->flags |=
|
||||
FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
command->SCp.phase =
|
||||
AAC_OWNER_ERROR_HANDLER;
|
||||
if (command == cmd)
|
||||
ret = SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -628,70 +718,165 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
|
|||
{
|
||||
struct scsi_device * dev = cmd->device;
|
||||
struct Scsi_Host * host = dev->host;
|
||||
struct scsi_cmnd * command;
|
||||
int count;
|
||||
struct aac_dev * aac = (struct aac_dev *)host->hostdata;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
u32 bus, cid;
|
||||
int ret = FAILED;
|
||||
|
||||
/* Mark the associated FIB to not complete, eh handler does this */
|
||||
for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
|
||||
struct fib * fib = &aac->fibs[count];
|
||||
if (fib->hw_fib_va->header.XferState &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
(fib->callback_data == cmd)) {
|
||||
fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
|
||||
bus = aac_logical_to_phys(scmd_channel(cmd));
|
||||
cid = scmd_id(cmd);
|
||||
if (bus < AAC_MAX_BUSES && cid < AAC_MAX_TARGETS &&
|
||||
aac->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
|
||||
struct fib *fib;
|
||||
int status;
|
||||
u64 address;
|
||||
u8 command;
|
||||
|
||||
pr_err("%s: Host adapter reset request. SCSI hang ?\n",
|
||||
AAC_DRIVERNAME);
|
||||
|
||||
fib = aac_fib_alloc(aac);
|
||||
if (!fib)
|
||||
return ret;
|
||||
|
||||
|
||||
if (aac->hba_map[bus][cid].reset_state == 0) {
|
||||
struct aac_hba_tm_req *tmf;
|
||||
|
||||
/* start a HBA_TMF_LUN_RESET TMF request */
|
||||
tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
|
||||
memset(tmf, 0, sizeof(*tmf));
|
||||
tmf->tmf = HBA_TMF_LUN_RESET;
|
||||
tmf->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
|
||||
tmf->lun[1] = cmd->device->lun;
|
||||
|
||||
address = (u64)fib->hw_error_pa;
|
||||
tmf->error_ptr_hi = cpu_to_le32
|
||||
((u32)(address >> 32));
|
||||
tmf->error_ptr_lo = cpu_to_le32
|
||||
((u32)(address & 0xffffffff));
|
||||
tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
|
||||
fib->hbacmd_size = sizeof(*tmf);
|
||||
|
||||
command = HBA_IU_TYPE_SCSI_TM_REQ;
|
||||
aac->hba_map[bus][cid].reset_state++;
|
||||
} else if (aac->hba_map[bus][cid].reset_state >= 1) {
|
||||
struct aac_hba_reset_req *rst;
|
||||
|
||||
/* already tried, start a hard reset now */
|
||||
rst = (struct aac_hba_reset_req *)fib->hw_fib_va;
|
||||
memset(rst, 0, sizeof(*rst));
|
||||
/* reset_type is already zero... */
|
||||
rst->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
|
||||
|
||||
address = (u64)fib->hw_error_pa;
|
||||
rst->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
|
||||
rst->error_ptr_lo = cpu_to_le32
|
||||
((u32)(address & 0xffffffff));
|
||||
rst->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
|
||||
fib->hbacmd_size = sizeof(*rst);
|
||||
|
||||
command = HBA_IU_TYPE_SATA_REQ;
|
||||
aac->hba_map[bus][cid].reset_state = 0;
|
||||
}
|
||||
}
|
||||
printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n",
|
||||
cmd->SCp.sent_command = 0;
|
||||
|
||||
status = aac_hba_send(command, fib,
|
||||
(fib_callback) aac_hba_callback,
|
||||
(void *) cmd);
|
||||
|
||||
/* Wait up to 2 minutes for completion */
|
||||
for (count = 0; count < 120; ++count) {
|
||||
if (cmd->SCp.sent_command) {
|
||||
ret = SUCCESS;
|
||||
break;
|
||||
}
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
if (ret != SUCCESS)
|
||||
pr_err("%s: Host adapter reset request timed out\n",
|
||||
AAC_DRIVERNAME);
|
||||
} else {
|
||||
struct scsi_cmnd *command;
|
||||
unsigned long flags;
|
||||
|
||||
/* Mark the assoc. FIB to not complete, eh handler does this */
|
||||
for (count = 0;
|
||||
count < (host->can_queue + AAC_NUM_MGT_FIB);
|
||||
++count) {
|
||||
struct fib *fib = &aac->fibs[count];
|
||||
|
||||
if (fib->hw_fib_va->header.XferState &&
|
||||
(fib->flags & FIB_CONTEXT_FLAG) &&
|
||||
(fib->callback_data == cmd)) {
|
||||
fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
|
||||
cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("%s: Host adapter reset request. SCSI hang ?\n",
|
||||
AAC_DRIVERNAME);
|
||||
|
||||
if ((count = aac_check_health(aac)))
|
||||
return count;
|
||||
/*
|
||||
* Wait for all commands to complete to this specific
|
||||
* target (block maximum 60 seconds).
|
||||
*/
|
||||
for (count = 60; count; --count) {
|
||||
int active = aac->in_reset;
|
||||
|
||||
if (active == 0)
|
||||
__shost_for_each_device(dev, host) {
|
||||
spin_lock_irqsave(&dev->list_lock, flags);
|
||||
list_for_each_entry(command, &dev->cmd_list, list) {
|
||||
if ((command != cmd) &&
|
||||
(command->SCp.phase == AAC_OWNER_FIRMWARE)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->list_lock, flags);
|
||||
if (active)
|
||||
break;
|
||||
|
||||
}
|
||||
count = aac_check_health(aac);
|
||||
if (count)
|
||||
return count;
|
||||
/*
|
||||
* We can exit If all the commands are complete
|
||||
* Wait for all commands to complete to this specific
|
||||
* target (block maximum 60 seconds).
|
||||
*/
|
||||
if (active == 0)
|
||||
return SUCCESS;
|
||||
ssleep(1);
|
||||
for (count = 60; count; --count) {
|
||||
int active = aac->in_reset;
|
||||
|
||||
if (active == 0)
|
||||
__shost_for_each_device(dev, host) {
|
||||
spin_lock_irqsave(&dev->list_lock, flags);
|
||||
list_for_each_entry(command, &dev->cmd_list,
|
||||
list) {
|
||||
if ((command != cmd) &&
|
||||
(command->SCp.phase ==
|
||||
AAC_OWNER_FIRMWARE)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->list_lock, flags);
|
||||
if (active)
|
||||
break;
|
||||
|
||||
}
|
||||
/*
|
||||
* We can exit If all the commands are complete
|
||||
*/
|
||||
if (active == 0)
|
||||
return SUCCESS;
|
||||
ssleep(1);
|
||||
}
|
||||
pr_err("%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
|
||||
|
||||
/*
|
||||
* This adapter needs a blind reset, only do so for
|
||||
* Adapters that support a register, instead of a commanded,
|
||||
* reset.
|
||||
*/
|
||||
if (((aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_MU_RESET) ||
|
||||
(aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_DOORBELL_RESET)) &&
|
||||
aac_check_reset &&
|
||||
((aac_check_reset != 1) ||
|
||||
!(aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_IGNORE_RESET))) {
|
||||
/* Bypass wait for command quiesce */
|
||||
aac_reset_adapter(aac, 2);
|
||||
}
|
||||
ret = SUCCESS;
|
||||
}
|
||||
printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
|
||||
/*
|
||||
* This adapter needs a blind reset, only do so for Adapters that
|
||||
* support a register, instead of a commanded, reset.
|
||||
* Cause an immediate retry of the command with a ten second delay
|
||||
* after successful tur
|
||||
*/
|
||||
if (((aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_MU_RESET) ||
|
||||
(aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_DOORBELL_RESET)) &&
|
||||
aac_check_reset &&
|
||||
((aac_check_reset != 1) ||
|
||||
!(aac->supplement_adapter_info.SupportedOptions2 &
|
||||
AAC_OPTION_IGNORE_RESET)))
|
||||
aac_reset_adapter(aac, 2); /* Bypass wait for command quiesce */
|
||||
return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -497,10 +497,35 @@ static int aac_src_deliver_message(struct fib *fib)
|
|||
vector_no = fib->vector_no;
|
||||
|
||||
if (native_hba) {
|
||||
((struct aac_hba_cmd_req *)fib->hw_fib_va)->reply_qid
|
||||
= vector_no;
|
||||
((struct aac_hba_cmd_req *)fib->hw_fib_va)->request_id
|
||||
+= (vector_no << 16);
|
||||
if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF) {
|
||||
struct aac_hba_tm_req *tm_req;
|
||||
|
||||
tm_req = (struct aac_hba_tm_req *)
|
||||
fib->hw_fib_va;
|
||||
if (tm_req->iu_type ==
|
||||
HBA_IU_TYPE_SCSI_TM_REQ) {
|
||||
((struct aac_hba_tm_req *)
|
||||
fib->hw_fib_va)->reply_qid
|
||||
= vector_no;
|
||||
((struct aac_hba_tm_req *)
|
||||
fib->hw_fib_va)->request_id
|
||||
+= (vector_no << 16);
|
||||
} else {
|
||||
((struct aac_hba_reset_req *)
|
||||
fib->hw_fib_va)->reply_qid
|
||||
= vector_no;
|
||||
((struct aac_hba_reset_req *)
|
||||
fib->hw_fib_va)->request_id
|
||||
+= (vector_no << 16);
|
||||
}
|
||||
} else {
|
||||
((struct aac_hba_cmd_req *)
|
||||
fib->hw_fib_va)->reply_qid
|
||||
= vector_no;
|
||||
((struct aac_hba_cmd_req *)
|
||||
fib->hw_fib_va)->request_id
|
||||
+= (vector_no << 16);
|
||||
}
|
||||
} else {
|
||||
fib->hw_fib_va->header.Handle += (vector_no << 16);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче