scsi: pm8001: Use sas_ata_device_link_abort() to handle NCQ errors

In commit c6b9ef5779 ("[SCSI] pm80xx: NCQ error handling changes") the
driver had support added to handle NCQ errors but much of what is done in
this handling is duplicated from the libata EH.

In that named commit we handle in 2x main steps:

 a. Issue read log ext10 to examine and clear the errors

 b. Issue SATA_ABORT all command

Indeed, in libata EH, we do similar to above:

 a. ata_do_eh() -> ata_eh_autopsy() -> ata_eh_link_autopsy() ->
    ata_eh_analyze_ncq_error() -> ata_eh_read_log_10h()

 b. ata_do_eh() -> ata_eh_recover() which will issue a device soft reset
    or hard reset

Since there is so much duplication, use sas_ata_device_link_abort() which
will abort all pending IOs and kick of ATA EH which will do the steps,
above.

However we will not follow the advisory to send the SATA_ABORT all command
after the autopsy in read log ext10. Indeed, in libsas EH, we already send
a per-task SATA_ABORT command, and this is prior to the ATA EH kicking in
and issuing the read log ext10 in the recovery process. I judge that this
is ok as the SATA_ABORT command does not actually send any protocol on the
link to abort I/O on the other side, so would not change any state on the
disk (for the read log ext10 command).

Signed-off-by: John Garry <john.garry@huawei.com>
Link: https://lore.kernel.org/r/1665998435-199946-7-git-send-email-john.garry@huawei.com
Tested-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
Tested-by: Niklas Cassel <niklas.cassel@wdc.com> # pm80xx
Acked-by: Jack Wang <jinpu.wang@ionos.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
John Garry 2022-10-17 17:20:33 +08:00 committed by Martin K. Petersen
parent 0b639decf6
commit 811be570a9
4 changed files with 19 additions and 326 deletions

View File

@ -1724,7 +1724,14 @@ void pm8001_work_fn(struct work_struct *work)
pm8001_free_dev(pm8001_dev);
}
}
} break;
}
break;
case IO_XFER_ERROR_ABORTED_NCQ_MODE:
{
dev = pm8001_dev->sas_device;
sas_ata_device_link_abort(dev, false);
}
break;
}
kfree(pw);
}
@ -1748,110 +1755,6 @@ int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
return ret;
}
static void pm8001_send_abort_all(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
struct pm8001_ccb_info *ccb;
struct sas_task *task;
struct task_abort_req task_abort;
u32 opc = OPC_INB_SATA_ABORT;
int ret;
pm8001_ha_dev->id |= NCQ_ABORT_ALL_FLAG;
pm8001_ha_dev->id &= ~NCQ_READ_LOG_FLAG;
task = sas_alloc_slow_task(GFP_ATOMIC);
if (!task) {
pm8001_dbg(pm8001_ha, FAIL, "cannot allocate task\n");
return;
}
task->task_done = pm8001_task_done;
ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_ha_dev, task);
if (!ccb) {
sas_free_task(task);
return;
}
memset(&task_abort, 0, sizeof(task_abort));
task_abort.abort_all = cpu_to_le32(1);
task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
task_abort.tag = cpu_to_le32(ccb->ccb_tag);
ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &task_abort,
sizeof(task_abort), 0);
if (ret) {
sas_free_task(task);
pm8001_ccb_free(pm8001_ha, ccb);
}
}
static void pm8001_send_read_log(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
struct sata_start_req sata_cmd;
int res;
struct pm8001_ccb_info *ccb;
struct sas_task *task = NULL;
struct host_to_dev_fis fis;
struct domain_device *dev;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
task = sas_alloc_slow_task(GFP_ATOMIC);
if (!task) {
pm8001_dbg(pm8001_ha, FAIL, "cannot allocate task !!!\n");
return;
}
task->task_done = pm8001_task_done;
/*
* Allocate domain device by ourselves as libsas is not going to
* provide any.
*/
dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
if (!dev) {
sas_free_task(task);
pm8001_dbg(pm8001_ha, FAIL,
"Domain device cannot be allocated\n");
return;
}
task->dev = dev;
task->dev->lldd_dev = pm8001_ha_dev;
ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_ha_dev, task);
if (!ccb) {
sas_free_task(task);
kfree(dev);
return;
}
pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;
/* construct read log FIS */
memset(&fis, 0, sizeof(struct host_to_dev_fis));
fis.fis_type = 0x27;
fis.flags = 0x80;
fis.command = ATA_CMD_READ_LOG_EXT;
fis.lbal = 0x10;
fis.sector_count = 0x1;
memset(&sata_cmd, 0, sizeof(sata_cmd));
sata_cmd.tag = cpu_to_le32(ccb->ccb_tag);
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
sata_cmd.ncqtag_atap_dir_m = cpu_to_le32((0x1 << 7) | (0x5 << 9));
memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));
res = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &sata_cmd,
sizeof(sata_cmd), 0);
if (res) {
sas_free_task(task);
pm8001_ccb_free(pm8001_ha, ccb);
kfree(dev);
}
}
/**
* mpi_ssp_completion- process the event that FW response to the SSP request.
* @pm8001_ha: our hba card information
@ -2301,8 +2204,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
return;
}
if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
&& unlikely(!t || !t->lldd_task || !t->dev)) {
if (pm8001_dev && unlikely(!t || !t->lldd_task || !t->dev)) {
pm8001_dbg(pm8001_ha, FAIL, "task or dev null\n");
return;
}
@ -2360,15 +2262,6 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_SAM_STAT_GOOD;
/* check if response is for SEND READ LOG */
if (pm8001_dev &&
(pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
pm8001_send_abort_all(pm8001_ha, pm8001_dev);
/* Free the tag */
pm8001_tag_free(pm8001_ha, tag);
sas_free_task(t);
return;
}
} else {
u8 len;
ts->resp = SAS_TASK_COMPLETE;
@ -2666,9 +2559,10 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
/* find device using device id */
pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
/* send read log extension */
if (pm8001_dev)
pm8001_send_read_log(pm8001_ha, pm8001_dev);
pm8001_handle_event(pm8001_ha,
pm8001_dev,
IO_XFER_ERROR_ABORTED_NCQ_MODE);
return;
}
@ -3649,12 +3543,7 @@ int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
pm8001_ccb_task_free(pm8001_ha, ccb);
mb();
if (pm8001_dev->id & NCQ_ABORT_ALL_FLAG) {
sas_free_task(t);
pm8001_dev->id &= ~NCQ_ABORT_ALL_FLAG;
} else {
t->task_done(t);
}
t->task_done(t);
return 0;
}
@ -4206,7 +4095,6 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
u64 phys_addr;
u32 ATAP = 0x0;
u32 dir;
unsigned long flags;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
memset(&sata_cmd, 0, sizeof(sata_cmd));
@ -4261,39 +4149,6 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
sata_cmd.esgl = 0;
}
/* Check for read log for failed drive and return */
if (sata_cmd.sata_fis.command == 0x2f) {
if (((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
(pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
(pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
struct task_status_struct *ts;
pm8001_ha_dev->id &= 0xDFFFFFFF;
ts = &task->task_status;
spin_lock_irqsave(&task->task_state_lock, flags);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_SAM_STAT_GOOD;
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
task->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((task->task_state_flags &
SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_dbg(pm8001_ha, FAIL,
"task 0x%p resp 0x%x stat 0x%x but aborted by upper layer\n",
task, ts->resp,
ts->stat);
pm8001_ccb_task_free(pm8001_ha, ccb);
} else {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_ccb_task_free_done(pm8001_ha, ccb);
return 0;
}
}
}
return pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &sata_cmd,
sizeof(sata_cmd), 0);
}

View File

@ -687,12 +687,6 @@ int pm8001_dev_found(struct domain_device *dev)
return pm8001_dev_found_notify(dev);
}
void pm8001_task_done(struct sas_task *task)
{
del_timer(&task->slow_task->timer);
complete(&task->slow_task->completion);
}
#define PM8001_TASK_TIMEOUT 20
/**

View File

@ -579,10 +579,6 @@ struct pm8001_fw_image_header {
#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10
#define FLASH_UPDATE_DISABLED 0x11
#define NCQ_READ_LOG_FLAG 0x80000000
#define NCQ_ABORT_ALL_FLAG 0x40000000
#define NCQ_2ND_RLE_FLAG 0x20000000
/* Device states */
#define DS_OPERATIONAL 0x01
#define DS_PORT_IN_RESET 0x02
@ -709,7 +705,6 @@ int pm8001_mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha,
int pm8001_mpi_general_event(struct pm8001_hba_info *pm8001_ha, void *piomb);
int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
struct sas_task *pm8001_alloc_task(void);
void pm8001_task_done(struct sas_task *task);
void pm8001_free_task(struct sas_task *task);
void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag);
struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha,

View File

@ -1778,113 +1778,6 @@ pm80xx_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
pm80xx_chip_intx_interrupt_disable(pm8001_ha);
}
static void pm80xx_send_abort_all(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
struct pm8001_ccb_info *ccb;
struct sas_task *task;
struct task_abort_req task_abort;
u32 opc = OPC_INB_SATA_ABORT;
int ret;
pm8001_ha_dev->id |= NCQ_ABORT_ALL_FLAG;
pm8001_ha_dev->id &= ~NCQ_READ_LOG_FLAG;
task = sas_alloc_slow_task(GFP_ATOMIC);
if (!task) {
pm8001_dbg(pm8001_ha, FAIL, "cannot allocate task\n");
return;
}
task->task_done = pm8001_task_done;
ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_ha_dev, task);
if (!ccb) {
sas_free_task(task);
return;
}
memset(&task_abort, 0, sizeof(task_abort));
task_abort.abort_all = cpu_to_le32(1);
task_abort.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
task_abort.tag = cpu_to_le32(ccb->ccb_tag);
ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &task_abort,
sizeof(task_abort), 0);
pm8001_dbg(pm8001_ha, FAIL, "Executing abort task end\n");
if (ret) {
sas_free_task(task);
pm8001_ccb_free(pm8001_ha, ccb);
}
}
static void pm80xx_send_read_log(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_ha_dev)
{
struct sata_start_req sata_cmd;
int res;
struct pm8001_ccb_info *ccb;
struct sas_task *task = NULL;
struct host_to_dev_fis fis;
struct domain_device *dev;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
task = sas_alloc_slow_task(GFP_ATOMIC);
if (!task) {
pm8001_dbg(pm8001_ha, FAIL, "cannot allocate task !!!\n");
return;
}
task->task_done = pm8001_task_done;
/*
* Allocate domain device by ourselves as libsas is not going to
* provide any.
*/
dev = kzalloc(sizeof(struct domain_device), GFP_ATOMIC);
if (!dev) {
sas_free_task(task);
pm8001_dbg(pm8001_ha, FAIL,
"Domain device cannot be allocated\n");
return;
}
task->dev = dev;
task->dev->lldd_dev = pm8001_ha_dev;
ccb = pm8001_ccb_alloc(pm8001_ha, pm8001_ha_dev, task);
if (!ccb) {
sas_free_task(task);
kfree(dev);
return;
}
pm8001_ha_dev->id |= NCQ_READ_LOG_FLAG;
pm8001_ha_dev->id |= NCQ_2ND_RLE_FLAG;
memset(&sata_cmd, 0, sizeof(sata_cmd));
/* construct read log FIS */
memset(&fis, 0, sizeof(struct host_to_dev_fis));
fis.fis_type = 0x27;
fis.flags = 0x80;
fis.command = ATA_CMD_READ_LOG_EXT;
fis.lbal = 0x10;
fis.sector_count = 0x1;
sata_cmd.tag = cpu_to_le32(ccb->ccb_tag);
sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
sata_cmd.ncqtag_atap_dir_m_dad = cpu_to_le32(((0x1 << 7) | (0x5 << 9)));
memcpy(&sata_cmd.sata_fis, &fis, sizeof(struct host_to_dev_fis));
res = pm8001_mpi_build_cmd(pm8001_ha, 0, opc, &sata_cmd,
sizeof(sata_cmd), 0);
pm8001_dbg(pm8001_ha, FAIL, "Executing read log end\n");
if (res) {
sas_free_task(task);
pm8001_ccb_free(pm8001_ha, ccb);
kfree(dev);
}
}
/**
* mpi_ssp_completion - process the event that FW response to the SSP request.
* @pm8001_ha: our hba card information
@ -2402,11 +2295,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
return;
}
if ((pm8001_dev && !(pm8001_dev->id & NCQ_READ_LOG_FLAG))
&& unlikely(!t || !t->lldd_task || !t->dev)) {
pm8001_dbg(pm8001_ha, FAIL, "task or dev null\n");
if (pm8001_dev && unlikely(!t->lldd_task || !t->dev))
return;
}
ts = &t->task_status;
@ -2463,15 +2354,6 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha,
if (param == 0) {
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_SAM_STAT_GOOD;
/* check if response is for SEND READ LOG */
if (pm8001_dev &&
(pm8001_dev->id & NCQ_READ_LOG_FLAG)) {
pm80xx_send_abort_all(pm8001_ha, pm8001_dev);
/* Free the tag */
pm8001_tag_free(pm8001_ha, tag);
sas_free_task(t);
return;
}
} else {
u8 len;
ts->resp = SAS_TASK_COMPLETE;
@ -2806,9 +2688,11 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha,
if (event == IO_XFER_ERROR_ABORTED_NCQ_MODE) {
/* find device using device id */
pm8001_dev = pm8001_find_dev(pm8001_ha, dev_id);
/* send read log extension */
/* send read log extension by aborting the link - libata does what we want */
if (pm8001_dev)
pm80xx_send_read_log(pm8001_ha, pm8001_dev);
pm8001_handle_event(pm8001_ha,
pm8001_dev,
IO_XFER_ERROR_ABORTED_NCQ_MODE);
return;
}
@ -4556,7 +4440,6 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
u32 end_addr_high, end_addr_low;
u32 ATAP = 0x0;
u32 dir;
unsigned long flags;
u32 opc = OPC_INB_SATA_HOST_OPSTART;
memset(&sata_cmd, 0, sizeof(sata_cmd));
@ -4735,40 +4618,6 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
(task->ata_task.atapi_packet[15] << 24)));
}
/* Check for read log for failed drive and return */
if (sata_cmd.sata_fis.command == 0x2f) {
if (pm8001_ha_dev && ((pm8001_ha_dev->id & NCQ_READ_LOG_FLAG) ||
(pm8001_ha_dev->id & NCQ_ABORT_ALL_FLAG) ||
(pm8001_ha_dev->id & NCQ_2ND_RLE_FLAG))) {
struct task_status_struct *ts;
pm8001_ha_dev->id &= 0xDFFFFFFF;
ts = &task->task_status;
spin_lock_irqsave(&task->task_state_lock, flags);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_SAM_STAT_GOOD;
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
task->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((task->task_state_flags &
SAS_TASK_STATE_ABORTED))) {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_dbg(pm8001_ha, FAIL,
"task 0x%p resp 0x%x stat 0x%x but aborted by upper layer\n",
task, ts->resp,
ts->stat);
pm8001_ccb_task_free(pm8001_ha, ccb);
return 0;
} else {
spin_unlock_irqrestore(&task->task_state_lock,
flags);
pm8001_ccb_task_free_done(pm8001_ha, ccb);
atomic_dec(&pm8001_ha_dev->running_req);
return 0;
}
}
}
trace_pm80xx_request_issue(pm8001_ha->id,
ccb->device ? ccb->device->attached_phy : PM8001_MAX_PHYS,
ccb->ccb_tag, opc,