scsi: libsas: Add sas_execute_ata_cmd()
Add a function to execute an ATA command using the TMF code, and use in the hisi_sas driver. That driver needs to be able to issue the command on a specific phy, so add an interface for that. With that, hisi_sas_exec_internal_tmf_task() may be deleted. Link: https://lore.kernel.org/r/1645534259-27068-19-git-send-email-john.garry@huawei.com Tested-by: Yihang Li <liyihang6@hisilicon.com> Tested-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: John Garry <john.garry@huawei.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
4fea759edf
commit
3f2e252ef7
|
@ -1239,124 +1239,7 @@ static void hisi_sas_tmf_timedout(struct timer_list *t)
|
|||
complete(&task->slow_task->completion);
|
||||
}
|
||||
|
||||
#define TASK_TIMEOUT (20 * HZ)
|
||||
#define TASK_RETRY 3
|
||||
#define INTERNAL_ABORT_TIMEOUT (6 * HZ)
|
||||
static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
|
||||
void *parameter, u32 para_len,
|
||||
struct sas_tmf_task *tmf)
|
||||
{
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
|
||||
struct device *dev = hisi_hba->dev;
|
||||
struct sas_task *task;
|
||||
int res, retry;
|
||||
|
||||
for (retry = 0; retry < TASK_RETRY; retry++) {
|
||||
task = sas_alloc_slow_task(GFP_KERNEL);
|
||||
if (!task)
|
||||
return -ENOMEM;
|
||||
|
||||
task->dev = device;
|
||||
task->task_proto = device->tproto;
|
||||
|
||||
if (dev_is_sata(device)) {
|
||||
task->ata_task.device_control_reg_update = 1;
|
||||
memcpy(&task->ata_task.fis, parameter, para_len);
|
||||
} else {
|
||||
memcpy(&task->ssp_task, parameter, para_len);
|
||||
}
|
||||
task->task_done = hisi_sas_task_done;
|
||||
|
||||
task->slow_task->timer.function = hisi_sas_tmf_timedout;
|
||||
task->slow_task->timer.expires = jiffies + TASK_TIMEOUT;
|
||||
add_timer(&task->slow_task->timer);
|
||||
|
||||
task->tmf = tmf;
|
||||
|
||||
res = hisi_sas_queue_command(task, GFP_KERNEL);
|
||||
if (res) {
|
||||
del_timer_sync(&task->slow_task->timer);
|
||||
dev_err(dev, "abort tmf: executing internal task failed: %d\n",
|
||||
res);
|
||||
goto ex_err;
|
||||
}
|
||||
|
||||
wait_for_completion(&task->slow_task->completion);
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
/* Even TMF timed out, return direct. */
|
||||
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
||||
struct hisi_sas_slot *slot = task->lldd_task;
|
||||
|
||||
dev_err(dev, "abort tmf: TMF task timeout and not done\n");
|
||||
if (slot) {
|
||||
struct hisi_sas_cq *cq =
|
||||
&hisi_hba->cq[slot->dlvry_queue];
|
||||
/*
|
||||
* sync irq to avoid free'ing task
|
||||
* before using task in IO completion
|
||||
*/
|
||||
synchronize_irq(cq->irq_no);
|
||||
slot->task = NULL;
|
||||
}
|
||||
|
||||
goto ex_err;
|
||||
} else
|
||||
dev_err(dev, "abort tmf: TMF task timeout\n");
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == TMF_RESP_FUNC_SUCC) {
|
||||
res = TMF_RESP_FUNC_SUCC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == SAS_DATA_UNDERRUN) {
|
||||
/* no error, but return the number of bytes of
|
||||
* underrun
|
||||
*/
|
||||
dev_warn(dev, "abort tmf: task to dev %016llx resp: 0x%x sts 0x%x underrun\n",
|
||||
SAS_ADDR(device->sas_addr),
|
||||
task->task_status.resp,
|
||||
task->task_status.stat);
|
||||
res = task->task_status.residual;
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == SAS_DATA_OVERRUN) {
|
||||
dev_warn(dev, "abort tmf: blocked task error\n");
|
||||
res = -EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == SAS_OPEN_REJECT) {
|
||||
dev_warn(dev, "abort tmf: open reject failed\n");
|
||||
res = -EIO;
|
||||
} else {
|
||||
dev_warn(dev, "abort tmf: task to dev %016llx resp: 0x%x status 0x%x\n",
|
||||
SAS_ADDR(device->sas_addr),
|
||||
task->task_status.resp,
|
||||
task->task_status.stat);
|
||||
}
|
||||
sas_free_task(task);
|
||||
task = NULL;
|
||||
}
|
||||
ex_err:
|
||||
if (retry == TASK_RETRY)
|
||||
dev_warn(dev, "abort tmf: executing internal task failed!\n");
|
||||
sas_free_task(task);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void hisi_sas_fill_ata_reset_cmd(struct ata_device *dev,
|
||||
bool reset, int pmp, u8 *fis)
|
||||
|
@ -1380,14 +1263,12 @@ static int hisi_sas_softreset_ata_disk(struct domain_device *device)
|
|||
int rc = TMF_RESP_FUNC_FAILED;
|
||||
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
|
||||
struct device *dev = hisi_hba->dev;
|
||||
int s = sizeof(struct host_to_dev_fis);
|
||||
struct sas_tmf_task tmf = {};
|
||||
|
||||
ata_for_each_link(link, ap, EDGE) {
|
||||
int pmp = sata_srst_pmp(link);
|
||||
|
||||
hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis);
|
||||
rc = hisi_sas_exec_internal_tmf_task(device, fis, s, &tmf);
|
||||
rc = sas_execute_ata_cmd(device, fis, -1);
|
||||
if (rc != TMF_RESP_FUNC_COMPLETE)
|
||||
break;
|
||||
}
|
||||
|
@ -1397,8 +1278,7 @@ static int hisi_sas_softreset_ata_disk(struct domain_device *device)
|
|||
int pmp = sata_srst_pmp(link);
|
||||
|
||||
hisi_sas_fill_ata_reset_cmd(link->device, 0, pmp, fis);
|
||||
rc = hisi_sas_exec_internal_tmf_task(device, fis,
|
||||
s, &tmf);
|
||||
rc = sas_execute_ata_cmd(device, fis, -1);
|
||||
if (rc != TMF_RESP_FUNC_COMPLETE)
|
||||
dev_err(dev, "ata disk %016llx de-reset failed\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
|
@ -1508,10 +1388,8 @@ static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba,
|
|||
struct asd_sas_port *sas_port,
|
||||
struct domain_device *device)
|
||||
{
|
||||
struct sas_tmf_task tmf_task = { .force_phy = 1 };
|
||||
struct ata_port *ap = device->sata_dev.ap;
|
||||
struct device *dev = hisi_hba->dev;
|
||||
int s = sizeof(struct host_to_dev_fis);
|
||||
int rc = TMF_RESP_FUNC_FAILED;
|
||||
struct ata_link *link;
|
||||
u8 fis[20] = {0};
|
||||
|
@ -1524,10 +1402,8 @@ static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba,
|
|||
ata_for_each_link(link, ap, EDGE) {
|
||||
int pmp = sata_srst_pmp(link);
|
||||
|
||||
tmf_task.phy_id = i;
|
||||
hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis);
|
||||
rc = hisi_sas_exec_internal_tmf_task(device, fis, s,
|
||||
&tmf_task);
|
||||
rc = sas_execute_ata_cmd(device, fis, i);
|
||||
if (rc != TMF_RESP_FUNC_COMPLETE) {
|
||||
dev_err(dev, "phy%d ata reset failed rc=%d\n",
|
||||
i, rc);
|
||||
|
|
|
@ -2491,6 +2491,7 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
|
|||
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
|
||||
struct asd_sas_port *sas_port = device->port;
|
||||
struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
|
||||
struct sas_ata_task *ata_task = &task->ata_task;
|
||||
struct sas_tmf_task *tmf = slot->tmf;
|
||||
u8 *buf_cmd;
|
||||
int has_data = 0, hdr_tag = 0;
|
||||
|
@ -2504,9 +2505,9 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
|
|||
else
|
||||
dw0 |= 4 << CMD_HDR_CMD_OFF;
|
||||
|
||||
if (tmf && tmf->force_phy) {
|
||||
if (tmf && ata_task->force_phy) {
|
||||
dw0 |= CMD_HDR_FORCE_PHY_MSK;
|
||||
dw0 |= (1 << tmf->phy_id) << CMD_HDR_PHY_ID_OFF;
|
||||
dw0 |= (1 << ata_task->force_phy_id) << CMD_HDR_PHY_ID_OFF;
|
||||
}
|
||||
|
||||
hdr->dw0 = cpu_to_le32(dw0);
|
||||
|
|
|
@ -852,3 +852,11 @@ void sas_ata_wait_eh(struct domain_device *dev)
|
|||
ap = dev->sata_dev.ap;
|
||||
ata_port_wait_eh(ap);
|
||||
}
|
||||
|
||||
int sas_execute_ata_cmd(struct domain_device *device, u8 *fis, int force_phy_id)
|
||||
{
|
||||
struct sas_tmf_task tmf_task = {};
|
||||
return sas_execute_tmf(device, fis, sizeof(struct host_to_dev_fis),
|
||||
force_phy_id, &tmf_task);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sas_execute_ata_cmd);
|
||||
|
|
|
@ -937,8 +937,16 @@ int sas_execute_tmf(struct domain_device *device, void *parameter,
|
|||
task->dev = device;
|
||||
task->task_proto = device->tproto;
|
||||
|
||||
if (!dev_is_sata(device))
|
||||
if (dev_is_sata(device)) {
|
||||
task->ata_task.device_control_reg_update = 1;
|
||||
if (force_phy_id >= 0) {
|
||||
task->ata_task.force_phy = true;
|
||||
task->ata_task.force_phy_id = force_phy_id;
|
||||
}
|
||||
memcpy(&task->ata_task.fis, parameter, para_len);
|
||||
} else {
|
||||
memcpy(&task->ssp_task, parameter, para_len);
|
||||
}
|
||||
|
||||
task->task_done = sas_task_internal_done;
|
||||
task->tmf = tmf;
|
||||
|
|
|
@ -552,6 +552,9 @@ struct sas_ata_task {
|
|||
u8 stp_affil_pol:1;
|
||||
|
||||
u8 device_control_reg_update:1;
|
||||
|
||||
bool force_phy;
|
||||
int force_phy_id;
|
||||
};
|
||||
|
||||
struct sas_smp_task {
|
||||
|
@ -579,10 +582,6 @@ struct sas_ssp_task {
|
|||
struct sas_tmf_task {
|
||||
u8 tmf;
|
||||
u16 tag_of_task_to_be_managed;
|
||||
|
||||
/* Temp */
|
||||
int force_phy;
|
||||
int phy_id;
|
||||
};
|
||||
|
||||
struct sas_task {
|
||||
|
|
|
@ -32,6 +32,8 @@ void sas_probe_sata(struct asd_sas_port *port);
|
|||
void sas_suspend_sata(struct asd_sas_port *port);
|
||||
void sas_resume_sata(struct asd_sas_port *port);
|
||||
void sas_ata_end_eh(struct ata_port *ap);
|
||||
int sas_execute_ata_cmd(struct domain_device *device, u8 *fis,
|
||||
int force_phy_id);
|
||||
#else
|
||||
|
||||
|
||||
|
@ -83,6 +85,12 @@ static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy
|
|||
static inline void sas_ata_end_eh(struct ata_port *ap)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int sas_execute_ata_cmd(struct domain_device *device, u8 *fis,
|
||||
int force_phy_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SAS_ATA_H_ */
|
||||
|
|
Loading…
Reference in New Issue