All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 0efff300 authored by John Garry's avatar John Garry Committed by Martin K. Petersen

hisi_sas: Add tmf methods

Add function methods for tmf's.
Signed-off-by: default avatarJohn Garry <john.garry@huawei.com>
Reviewed-by: default avatarArnd Bergmann <arnd@arndb.de>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 701f75ec
......@@ -553,6 +553,309 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
}
static void hisi_sas_task_done(struct sas_task *task)
{
if (!del_timer(&task->slow_task->timer))
return;
complete(&task->slow_task->completion);
}
static void hisi_sas_tmf_timedout(unsigned long data)
{
struct sas_task *task = (struct sas_task *)data;
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
complete(&task->slow_task->completion);
}
#define TASK_TIMEOUT 20
#define TASK_RETRY 3
static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
void *parameter, u32 para_len,
struct hisi_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->pdev->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;
memcpy(&task->ssp_task, parameter, para_len);
task->task_done = hisi_sas_task_done;
task->slow_task->timer.data = (unsigned long) task;
task->slow_task->timer.function = hisi_sas_tmf_timedout;
task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ;
add_timer(&task->slow_task->timer);
res = hisi_sas_task_exec(task, GFP_KERNEL, 1, tmf);
if (res) {
del_timer(&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)) {
dev_err(dev, "abort tmf: TMF task[%d] timeout\n",
tmf->tag_of_task_to_be_managed);
if (task->lldd_task) {
struct hisi_sas_slot *slot =
task->lldd_task;
hisi_sas_slot_task_free(hisi_hba,
task, slot);
}
goto ex_err;
}
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAM_STAT_GOOD) {
res = TMF_RESP_FUNC_COMPLETE;
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;
}
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:
WARN_ON(retry == TASK_RETRY);
sas_free_task(task);
return res;
}
static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device,
u8 *lun, struct hisi_sas_tmf_task *tmf)
{
struct sas_ssp_task ssp_task;
if (!(device->tproto & SAS_PROTOCOL_SSP))
return TMF_RESP_FUNC_ESUPP;
memcpy(ssp_task.LUN, lun, 8);
return hisi_sas_exec_internal_tmf_task(device, &ssp_task,
sizeof(ssp_task), tmf);
}
static int hisi_sas_abort_task(struct sas_task *task)
{
struct scsi_lun lun;
struct hisi_sas_tmf_task tmf_task;
struct domain_device *device = task->dev;
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev);
struct device *dev = &hisi_hba->pdev->dev;
int rc = TMF_RESP_FUNC_FAILED;
unsigned long flags;
if (!sas_dev) {
dev_warn(dev, "Device has been removed\n");
return TMF_RESP_FUNC_FAILED;
}
spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
spin_unlock_irqrestore(&task->task_state_lock, flags);
rc = TMF_RESP_FUNC_COMPLETE;
goto out;
}
spin_unlock_irqrestore(&task->task_state_lock, flags);
sas_dev->dev_status = HISI_SAS_DEV_EH;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
struct scsi_cmnd *cmnd = task->uldd_task;
struct hisi_sas_slot *slot = task->lldd_task;
u32 tag = slot->idx;
int_to_scsilun(cmnd->device->lun, &lun);
tmf_task.tmf = TMF_ABORT_TASK;
tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
rc = hisi_sas_debug_issue_ssp_tmf(task->dev, lun.scsi_lun,
&tmf_task);
/* if successful, clear the task and callback forwards.*/
if (rc == TMF_RESP_FUNC_COMPLETE) {
if (task->lldd_task) {
struct hisi_sas_slot *slot;
slot = &hisi_hba->slot_info
[tmf_task.tag_of_task_to_be_managed];
spin_lock_irqsave(&hisi_hba->lock, flags);
hisi_hba->hw->slot_complete(hisi_hba, slot, 1);
spin_unlock_irqrestore(&hisi_hba->lock, flags);
}
}
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
task->task_proto & SAS_PROTOCOL_STP) {
if (task->dev->dev_type == SAS_SATA_DEV) {
struct hisi_slot_info *slot = task->lldd_task;
dev_notice(dev, "abort task: hba=%p task=%p slot=%p\n",
hisi_hba, task, slot);
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
rc = TMF_RESP_FUNC_COMPLETE;
goto out;
}
}
out:
if (rc != TMF_RESP_FUNC_COMPLETE)
dev_notice(dev, "abort task: rc=%d\n", rc);
return rc;
}
static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
{
struct hisi_sas_tmf_task tmf_task;
int rc = TMF_RESP_FUNC_FAILED;
tmf_task.tmf = TMF_ABORT_TASK_SET;
rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
return rc;
}
static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun)
{
int rc = TMF_RESP_FUNC_FAILED;
struct hisi_sas_tmf_task tmf_task;
tmf_task.tmf = TMF_CLEAR_ACA;
rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
return rc;
}
static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
{
struct sas_phy *phy = sas_get_local_phy(device);
int rc, reset_type = (device->dev_type == SAS_SATA_DEV ||
(device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
rc = sas_phy_reset(phy, reset_type);
sas_put_local_phy(phy);
msleep(2000);
return rc;
}
static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
{
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
unsigned long flags;
int rc = TMF_RESP_FUNC_FAILED;
if (sas_dev->dev_status != HISI_SAS_DEV_EH)
return TMF_RESP_FUNC_FAILED;
sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
rc = hisi_sas_debug_I_T_nexus_reset(device);
spin_lock_irqsave(&hisi_hba->lock, flags);
hisi_sas_release_task(hisi_hba, device);
spin_unlock_irqrestore(&hisi_hba->lock, flags);
return 0;
}
static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
{
struct hisi_sas_tmf_task tmf_task;
struct hisi_sas_device *sas_dev = device->lldd_dev;
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
struct device *dev = &hisi_hba->pdev->dev;
unsigned long flags;
int rc = TMF_RESP_FUNC_FAILED;
tmf_task.tmf = TMF_LU_RESET;
sas_dev->dev_status = HISI_SAS_DEV_EH;
rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
if (rc == TMF_RESP_FUNC_COMPLETE) {
spin_lock_irqsave(&hisi_hba->lock, flags);
hisi_sas_release_task(hisi_hba, device);
spin_unlock_irqrestore(&hisi_hba->lock, flags);
}
/* If failed, fall-through I_T_Nexus reset */
dev_err(dev, "lu_reset: for device[%llx]:rc= %d\n",
sas_dev->device_id, rc);
return rc;
}
static int hisi_sas_query_task(struct sas_task *task)
{
struct scsi_lun lun;
struct hisi_sas_tmf_task tmf_task;
int rc = TMF_RESP_FUNC_FAILED;
if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
struct scsi_cmnd *cmnd = task->uldd_task;
struct domain_device *device = task->dev;
struct hisi_sas_slot *slot = task->lldd_task;
u32 tag = slot->idx;
int_to_scsilun(cmnd->device->lun, &lun);
tmf_task.tmf = TMF_QUERY_TASK;
tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
rc = hisi_sas_debug_issue_ssp_tmf(device,
lun.scsi_lun,
&tmf_task);
switch (rc) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC:
/* The task is not in Lun or failed, reset the phy */
case TMF_RESP_FUNC_FAILED:
case TMF_RESP_FUNC_COMPLETE:
break;
}
}
return rc;
}
static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
{
hisi_sas_port_notify_formed(sas_phy);
......@@ -629,6 +932,12 @@ static struct sas_domain_function_template hisi_sas_transport_ops = {
.lldd_dev_found = hisi_sas_dev_found,
.lldd_dev_gone = hisi_sas_dev_gone,
.lldd_execute_task = hisi_sas_queue_command,
.lldd_abort_task = hisi_sas_abort_task,
.lldd_abort_task_set = hisi_sas_abort_task_set,
.lldd_clear_aca = hisi_sas_clear_aca,
.lldd_I_T_nexus_reset = hisi_sas_I_T_nexus_reset,
.lldd_lu_reset = hisi_sas_lu_reset,
.lldd_query_task = hisi_sas_query_task,
.lldd_port_formed = hisi_sas_port_formed,
.lldd_port_deformed = hisi_sas_port_deformed,
};
......
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