scsi: hisi_sas: add v2 hw internal abort timeout workaround
This patch is a workaround for a SoC bug where an internal abort command may timeout. In v2 hw, the channel should become idle in order to finish abort process. If the target side has been sending HOLD, host side channel failed to complete the frame to send, and can not enter the idle state. Then internal abort command will timeout. As this issue is only in v2 hw, we deal with it in the hw layer. Our workaround solution is: If abort is not finished within a certain period of time, we will check HOLD status. If HOLD has been sending, we will send break command. Signed-off-by: John Garry <john.garry@huawei.com> Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
819cbf1895
commit
0844a3ff00
@ -138,6 +138,7 @@ struct hisi_sas_slot {
|
|||||||
struct hisi_sas_sge_page *sge_page;
|
struct hisi_sas_sge_page *sge_page;
|
||||||
dma_addr_t sge_page_dma;
|
dma_addr_t sge_page_dma;
|
||||||
struct work_struct abort_slot;
|
struct work_struct abort_slot;
|
||||||
|
struct timer_list internal_abort_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hisi_sas_tmf_task {
|
struct hisi_sas_tmf_task {
|
||||||
|
@ -1224,7 +1224,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
|
|||||||
task->task_done = hisi_sas_task_done;
|
task->task_done = hisi_sas_task_done;
|
||||||
task->slow_task->timer.data = (unsigned long)task;
|
task->slow_task->timer.data = (unsigned long)task;
|
||||||
task->slow_task->timer.function = hisi_sas_tmf_timedout;
|
task->slow_task->timer.function = hisi_sas_tmf_timedout;
|
||||||
task->slow_task->timer.expires = jiffies + 20*HZ;
|
task->slow_task->timer.expires = jiffies + msecs_to_jiffies(110);
|
||||||
add_timer(&task->slow_task->timer);
|
add_timer(&task->slow_task->timer);
|
||||||
|
|
||||||
/* Lock as we are alloc'ing a slot, which cannot be interrupted */
|
/* Lock as we are alloc'ing a slot, which cannot be interrupted */
|
||||||
|
@ -2260,15 +2260,18 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
|
|||||||
case STAT_IO_COMPLETE:
|
case STAT_IO_COMPLETE:
|
||||||
/* internal abort command complete */
|
/* internal abort command complete */
|
||||||
ts->stat = TMF_RESP_FUNC_SUCC;
|
ts->stat = TMF_RESP_FUNC_SUCC;
|
||||||
|
del_timer(&slot->internal_abort_timer);
|
||||||
goto out;
|
goto out;
|
||||||
case STAT_IO_NO_DEVICE:
|
case STAT_IO_NO_DEVICE:
|
||||||
ts->stat = TMF_RESP_FUNC_COMPLETE;
|
ts->stat = TMF_RESP_FUNC_COMPLETE;
|
||||||
|
del_timer(&slot->internal_abort_timer);
|
||||||
goto out;
|
goto out;
|
||||||
case STAT_IO_NOT_VALID:
|
case STAT_IO_NOT_VALID:
|
||||||
/* abort single io, controller don't find
|
/* abort single io, controller don't find
|
||||||
* the io need to abort
|
* the io need to abort
|
||||||
*/
|
*/
|
||||||
ts->stat = TMF_RESP_FUNC_FAILED;
|
ts->stat = TMF_RESP_FUNC_FAILED;
|
||||||
|
del_timer(&slot->internal_abort_timer);
|
||||||
goto out;
|
goto out;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -2502,6 +2505,40 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hisi_sas_internal_abort_quirk_timeout(unsigned long data)
|
||||||
|
{
|
||||||
|
struct hisi_sas_slot *slot = (struct hisi_sas_slot *)data;
|
||||||
|
struct hisi_sas_port *port = slot->port;
|
||||||
|
struct asd_sas_port *asd_sas_port;
|
||||||
|
struct asd_sas_phy *sas_phy;
|
||||||
|
|
||||||
|
if (!port)
|
||||||
|
return;
|
||||||
|
|
||||||
|
asd_sas_port = &port->sas_port;
|
||||||
|
|
||||||
|
/* Kick the hardware - send break command */
|
||||||
|
list_for_each_entry(sas_phy, &asd_sas_port->phy_list, port_phy_el) {
|
||||||
|
struct hisi_sas_phy *phy = sas_phy->lldd_phy;
|
||||||
|
struct hisi_hba *hisi_hba = phy->hisi_hba;
|
||||||
|
int phy_no = sas_phy->id;
|
||||||
|
u32 link_dfx2;
|
||||||
|
|
||||||
|
link_dfx2 = hisi_sas_phy_read32(hisi_hba, phy_no, LINK_DFX2);
|
||||||
|
if ((link_dfx2 == LINK_DFX2_RCVR_HOLD_STS_MSK) ||
|
||||||
|
(link_dfx2 & LINK_DFX2_SEND_HOLD_STS_MSK)) {
|
||||||
|
u32 txid_auto;
|
||||||
|
|
||||||
|
txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no,
|
||||||
|
TXID_AUTO);
|
||||||
|
txid_auto |= TXID_AUTO_CTB_MSK;
|
||||||
|
hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO,
|
||||||
|
txid_auto);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
|
static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
|
||||||
struct hisi_sas_slot *slot,
|
struct hisi_sas_slot *slot,
|
||||||
int device_id, int abort_flag, int tag_to_abort)
|
int device_id, int abort_flag, int tag_to_abort)
|
||||||
@ -2510,6 +2547,13 @@ static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
|
|||||||
struct domain_device *dev = task->dev;
|
struct domain_device *dev = task->dev;
|
||||||
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
|
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
|
||||||
struct hisi_sas_port *port = slot->port;
|
struct hisi_sas_port *port = slot->port;
|
||||||
|
struct timer_list *timer = &slot->internal_abort_timer;
|
||||||
|
|
||||||
|
/* setup the quirk timer */
|
||||||
|
setup_timer(timer, hisi_sas_internal_abort_quirk_timeout,
|
||||||
|
(unsigned long)slot);
|
||||||
|
/* Set the timeout to 10ms less than internal abort timeout */
|
||||||
|
mod_timer(timer, jiffies + msecs_to_jiffies(100));
|
||||||
|
|
||||||
/* dw0 */
|
/* dw0 */
|
||||||
hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
|
hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
|
||||||
|
Loading…
Reference in New Issue
Block a user