mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
scsi: hisi_sas: Use libsas internal abort support
Use the common libsas internal abort functionality. In addition, this driver has special handling for internal abort timeouts - specifically whether to reset the controller in that instance, so extend the API for that. Timeout is now increased to 20 * Hz from 6 * Hz. We also retry for failure now, but this should not make a difference. Link: https://lore.kernel.org/r/1647001432-239276-5-git-send-email-john.garry@huawei.com Tested-by: Damien Le Moal <damien.lemoal@opensource.wdc.com> Acked-by: Jack Wang <jinpu.wang@ionos.com> 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
2cbbf48977
commit
095478a6e5
@ -133,11 +133,6 @@ struct hisi_sas_rst {
|
||||
bool done;
|
||||
};
|
||||
|
||||
struct hisi_sas_internal_abort {
|
||||
unsigned int flag;
|
||||
unsigned int tag;
|
||||
};
|
||||
|
||||
#define HISI_SAS_RST_WORK_INIT(r, c) \
|
||||
{ .hisi_hba = hisi_hba, \
|
||||
.completion = &c, \
|
||||
@ -325,8 +320,7 @@ struct hisi_sas_hw {
|
||||
void (*prep_stp)(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_slot *slot);
|
||||
void (*prep_abort)(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_slot *slot,
|
||||
int device_id, int abort_flag, int tag_to_abort);
|
||||
struct hisi_sas_slot *slot);
|
||||
void (*phys_init)(struct hisi_hba *hisi_hba);
|
||||
void (*phy_start)(struct hisi_hba *hisi_hba, int phy_no);
|
||||
void (*phy_disable)(struct hisi_hba *hisi_hba, int phy_no);
|
||||
|
@ -10,10 +10,6 @@
|
||||
#define DEV_IS_GONE(dev) \
|
||||
((!dev) || (dev->dev_type == SAS_PHY_UNUSED))
|
||||
|
||||
static int
|
||||
hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
|
||||
struct domain_device *device,
|
||||
int abort_flag, int tag, bool rst_to_recover);
|
||||
static int hisi_sas_softreset_ata_disk(struct domain_device *device);
|
||||
static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
|
||||
void *funcdata);
|
||||
@ -21,6 +17,10 @@ static void hisi_sas_release_task(struct hisi_hba *hisi_hba,
|
||||
struct domain_device *device);
|
||||
static void hisi_sas_dev_gone(struct domain_device *device);
|
||||
|
||||
struct hisi_sas_internal_abort_data {
|
||||
bool rst_ha_timeout; /* reset the HA for timeout */
|
||||
};
|
||||
|
||||
u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction)
|
||||
{
|
||||
switch (fis->command) {
|
||||
@ -263,11 +263,9 @@ static void hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba,
|
||||
}
|
||||
|
||||
static void hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_internal_abort *abort,
|
||||
struct hisi_sas_slot *slot, int device_id)
|
||||
struct hisi_sas_slot *slot)
|
||||
{
|
||||
hisi_hba->hw->prep_abort(hisi_hba, slot,
|
||||
device_id, abort->flag, abort->tag);
|
||||
hisi_hba->hw->prep_abort(hisi_hba, slot);
|
||||
}
|
||||
|
||||
static void hisi_sas_dma_unmap(struct hisi_hba *hisi_hba,
|
||||
@ -397,8 +395,7 @@ static
|
||||
void hisi_sas_task_deliver(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_slot *slot,
|
||||
struct hisi_sas_dq *dq,
|
||||
struct hisi_sas_device *sas_dev,
|
||||
struct hisi_sas_internal_abort *abort)
|
||||
struct hisi_sas_device *sas_dev)
|
||||
{
|
||||
struct hisi_sas_cmd_hdr *cmd_hdr_base;
|
||||
int dlvry_queue_slot, dlvry_queue;
|
||||
@ -439,19 +436,15 @@ void hisi_sas_task_deliver(struct hisi_hba *hisi_hba,
|
||||
break;
|
||||
case SAS_PROTOCOL_SATA:
|
||||
case SAS_PROTOCOL_STP:
|
||||
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
|
||||
case SAS_PROTOCOL_STP_ALL:
|
||||
hisi_sas_task_prep_ata(hisi_hba, slot);
|
||||
break;
|
||||
case SAS_PROTOCOL_NONE:
|
||||
if (abort) {
|
||||
hisi_sas_task_prep_abort(hisi_hba, abort, slot, sas_dev->device_id);
|
||||
break;
|
||||
}
|
||||
case SAS_PROTOCOL_INTERNAL_ABORT:
|
||||
hisi_sas_task_prep_abort(hisi_hba, slot);
|
||||
break;
|
||||
fallthrough;
|
||||
default:
|
||||
dev_err(hisi_hba->dev, "task prep: unknown/unsupported proto (0x%x)\n",
|
||||
task->task_proto);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
WRITE_ONCE(slot->ready, 1);
|
||||
@ -467,6 +460,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
|
||||
struct domain_device *device = task->dev;
|
||||
struct asd_sas_port *sas_port = device->port;
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
bool internal_abort = sas_is_internal_abort(task);
|
||||
struct scsi_cmnd *scmd = NULL;
|
||||
struct hisi_sas_dq *dq = NULL;
|
||||
struct hisi_sas_port *port;
|
||||
@ -484,7 +478,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
|
||||
* libsas will use dev->port, should
|
||||
* not call task_done for sata
|
||||
*/
|
||||
if (device->dev_type != SAS_SATA_DEV)
|
||||
if (device->dev_type != SAS_SATA_DEV && !internal_abort)
|
||||
task->task_done(task);
|
||||
return -ECOMM;
|
||||
}
|
||||
@ -492,59 +486,85 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
|
||||
hisi_hba = dev_to_hisi_hba(device);
|
||||
dev = hisi_hba->dev;
|
||||
|
||||
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) {
|
||||
if (!gfpflags_allow_blocking(gfp_flags))
|
||||
switch (task->task_proto) {
|
||||
case SAS_PROTOCOL_SSP:
|
||||
case SAS_PROTOCOL_SMP:
|
||||
case SAS_PROTOCOL_SATA:
|
||||
case SAS_PROTOCOL_STP:
|
||||
case SAS_PROTOCOL_STP_ALL:
|
||||
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) {
|
||||
if (!gfpflags_allow_blocking(gfp_flags))
|
||||
return -EINVAL;
|
||||
|
||||
down(&hisi_hba->sem);
|
||||
up(&hisi_hba->sem);
|
||||
}
|
||||
|
||||
if (DEV_IS_GONE(sas_dev)) {
|
||||
if (sas_dev)
|
||||
dev_info(dev, "task prep: device %d not ready\n",
|
||||
sas_dev->device_id);
|
||||
else
|
||||
dev_info(dev, "task prep: device %016llx not ready\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
port = to_hisi_sas_port(sas_port);
|
||||
if (!port->port_attached) {
|
||||
dev_info(dev, "task prep: %s port%d not attach device\n",
|
||||
dev_is_sata(device) ? "SATA/STP" : "SAS",
|
||||
device->port->id);
|
||||
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
if (task->uldd_task) {
|
||||
struct ata_queued_cmd *qc;
|
||||
|
||||
if (dev_is_sata(device)) {
|
||||
qc = task->uldd_task;
|
||||
scmd = qc->scsicmd;
|
||||
} else {
|
||||
scmd = task->uldd_task;
|
||||
}
|
||||
}
|
||||
|
||||
if (scmd) {
|
||||
unsigned int dq_index;
|
||||
u32 blk_tag;
|
||||
|
||||
blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
|
||||
dq_index = blk_mq_unique_tag_to_hwq(blk_tag);
|
||||
dq = &hisi_hba->dq[dq_index];
|
||||
} else {
|
||||
struct Scsi_Host *shost = hisi_hba->shost;
|
||||
struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
|
||||
int queue = qmap->mq_map[raw_smp_processor_id()];
|
||||
|
||||
dq = &hisi_hba->dq[queue];
|
||||
}
|
||||
break;
|
||||
case SAS_PROTOCOL_INTERNAL_ABORT:
|
||||
if (!hisi_hba->hw->prep_abort)
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
|
||||
if (test_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags))
|
||||
return -EIO;
|
||||
|
||||
hisi_hba = dev_to_hisi_hba(device);
|
||||
|
||||
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags)))
|
||||
return -EINVAL;
|
||||
|
||||
down(&hisi_hba->sem);
|
||||
up(&hisi_hba->sem);
|
||||
}
|
||||
|
||||
if (DEV_IS_GONE(sas_dev)) {
|
||||
if (sas_dev)
|
||||
dev_info(dev, "task prep: device %d not ready\n",
|
||||
sas_dev->device_id);
|
||||
else
|
||||
dev_info(dev, "task prep: device %016llx not ready\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
if (task->uldd_task) {
|
||||
struct ata_queued_cmd *qc;
|
||||
|
||||
if (dev_is_sata(device)) {
|
||||
qc = task->uldd_task;
|
||||
scmd = qc->scsicmd;
|
||||
} else {
|
||||
scmd = task->uldd_task;
|
||||
}
|
||||
}
|
||||
|
||||
if (scmd) {
|
||||
unsigned int dq_index;
|
||||
u32 blk_tag;
|
||||
|
||||
blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
|
||||
dq_index = blk_mq_unique_tag_to_hwq(blk_tag);
|
||||
dq = &hisi_hba->dq[dq_index];
|
||||
} else {
|
||||
struct Scsi_Host *shost = hisi_hba->shost;
|
||||
struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
|
||||
int queue = qmap->mq_map[raw_smp_processor_id()];
|
||||
|
||||
dq = &hisi_hba->dq[queue];
|
||||
}
|
||||
|
||||
port = to_hisi_sas_port(sas_port);
|
||||
if (port && !port->port_attached) {
|
||||
dev_info(dev, "task prep: %s port%d not attach device\n",
|
||||
(dev_is_sata(device)) ?
|
||||
"SATA/STP" : "SAS",
|
||||
device->port->id);
|
||||
|
||||
return -ECOMM;
|
||||
port = to_hisi_sas_port(sas_port);
|
||||
dq = &hisi_hba->dq[task->abort_task.qid];
|
||||
break;
|
||||
default:
|
||||
dev_err(hisi_hba->dev, "task prep: unknown/unsupported proto (0x%x)\n",
|
||||
task->task_proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = hisi_sas_dma_map(hisi_hba, task, &n_elem,
|
||||
@ -558,7 +578,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
|
||||
goto err_out_dma_unmap;
|
||||
}
|
||||
|
||||
if (hisi_hba->hw->slot_index_alloc)
|
||||
if (!internal_abort && hisi_hba->hw->slot_index_alloc)
|
||||
rc = hisi_hba->hw->slot_index_alloc(hisi_hba, device);
|
||||
else
|
||||
rc = hisi_sas_slot_index_alloc(hisi_hba, scmd);
|
||||
@ -573,10 +593,10 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
|
||||
slot->port = port;
|
||||
|
||||
slot->tmf = task->tmf;
|
||||
slot->is_internal = task->tmf;
|
||||
slot->is_internal = !!task->tmf || internal_abort;
|
||||
|
||||
/* protect task_prep and start_delivery sequence */
|
||||
hisi_sas_task_deliver(hisi_hba, slot, dq, sas_dev, NULL);
|
||||
hisi_sas_task_deliver(hisi_hba, slot, dq, sas_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1088,6 +1108,29 @@ static void hisi_sas_dereg_device(struct hisi_hba *hisi_hba,
|
||||
hisi_hba->hw->dereg_device(hisi_hba, device);
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_sas_internal_task_abort_dev(struct hisi_sas_device *sas_dev,
|
||||
bool rst_ha_timeout)
|
||||
{
|
||||
struct hisi_sas_internal_abort_data data = { rst_ha_timeout };
|
||||
struct domain_device *device = sas_dev->sas_device;
|
||||
struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < hisi_hba->cq_nvecs; i++) {
|
||||
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
|
||||
const struct cpumask *mask = cq->irq_mask;
|
||||
|
||||
if (mask && !cpumask_intersects(cpu_online_mask, mask))
|
||||
continue;
|
||||
rc = sas_execute_internal_abort_dev(device, i, &data);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hisi_sas_dev_gone(struct domain_device *device)
|
||||
{
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
@ -1100,8 +1143,7 @@ static void hisi_sas_dev_gone(struct domain_device *device)
|
||||
|
||||
down(&hisi_hba->sem);
|
||||
if (!test_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags)) {
|
||||
hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV, 0, true);
|
||||
hisi_sas_internal_task_abort_dev(sas_dev, true);
|
||||
|
||||
hisi_sas_dereg_device(hisi_hba, device);
|
||||
|
||||
@ -1216,32 +1258,6 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hisi_sas_task_done(struct sas_task *task)
|
||||
{
|
||||
del_timer_sync(&task->slow_task->timer);
|
||||
complete(&task->slow_task->completion);
|
||||
}
|
||||
|
||||
static void hisi_sas_tmf_timedout(struct timer_list *t)
|
||||
{
|
||||
struct sas_task_slow *slow = from_timer(slow, t, timer);
|
||||
struct sas_task *task = slow->task;
|
||||
unsigned long flags;
|
||||
bool is_completed = true;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
||||
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
|
||||
is_completed = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
if (!is_completed)
|
||||
complete(&task->slow_task->completion);
|
||||
}
|
||||
|
||||
#define INTERNAL_ABORT_TIMEOUT (6 * HZ)
|
||||
|
||||
static void hisi_sas_fill_ata_reset_cmd(struct ata_device *dev,
|
||||
bool reset, int pmp, u8 *fis)
|
||||
{
|
||||
@ -1426,9 +1442,7 @@ static void hisi_sas_terminate_stp_reject(struct hisi_hba *hisi_hba)
|
||||
if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device)
|
||||
continue;
|
||||
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV, 0,
|
||||
false);
|
||||
rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
|
||||
if (rc < 0)
|
||||
dev_err(dev, "STP reject: abort dev failed %d\n", rc);
|
||||
}
|
||||
@ -1536,6 +1550,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
|
||||
|
||||
static int hisi_sas_abort_task(struct sas_task *task)
|
||||
{
|
||||
struct hisi_sas_internal_abort_data internal_abort_data = { false };
|
||||
struct domain_device *device = task->dev;
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
struct hisi_hba *hisi_hba;
|
||||
@ -1575,9 +1590,8 @@ static int hisi_sas_abort_task(struct sas_task *task)
|
||||
int rc2;
|
||||
|
||||
rc = sas_abort_task(task, tag);
|
||||
rc2 = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_CMD, tag,
|
||||
false);
|
||||
rc2 = sas_execute_internal_abort_single(device, tag,
|
||||
slot->dlvry_queue, &internal_abort_data);
|
||||
if (rc2 < 0) {
|
||||
dev_err(dev, "abort task: internal abort (%d)\n", rc2);
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
@ -1597,9 +1611,7 @@ static int hisi_sas_abort_task(struct sas_task *task)
|
||||
} else if (task->task_proto & SAS_PROTOCOL_SATA ||
|
||||
task->task_proto & SAS_PROTOCOL_STP) {
|
||||
if (task->dev->dev_type == SAS_SATA_DEV) {
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV,
|
||||
0, false);
|
||||
rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "abort task: internal abort failed\n");
|
||||
goto out;
|
||||
@ -1613,9 +1625,9 @@ static int hisi_sas_abort_task(struct sas_task *task)
|
||||
u32 tag = slot->idx;
|
||||
struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
|
||||
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_CMD, tag,
|
||||
false);
|
||||
rc = sas_execute_internal_abort_single(device,
|
||||
tag, slot->dlvry_queue,
|
||||
&internal_abort_data);
|
||||
if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
|
||||
task->lldd_task) {
|
||||
/*
|
||||
@ -1635,12 +1647,12 @@ static int hisi_sas_abort_task(struct sas_task *task)
|
||||
|
||||
static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
|
||||
{
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
|
||||
struct device *dev = hisi_hba->dev;
|
||||
int rc;
|
||||
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV, 0, false);
|
||||
rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "abort task set: internal abort rc=%d\n", rc);
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
@ -1713,12 +1725,12 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
|
||||
|
||||
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);
|
||||
struct device *dev = hisi_hba->dev;
|
||||
int rc;
|
||||
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV, 0, false);
|
||||
rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
@ -1766,8 +1778,7 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
|
||||
int rc = TMF_RESP_FUNC_FAILED;
|
||||
|
||||
/* Clear internal IO and then lu reset */
|
||||
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
HISI_SAS_INT_ABT_DEV, 0, false);
|
||||
rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "lu_reset: internal abort failed\n");
|
||||
goto out;
|
||||
@ -1862,203 +1873,48 @@ static int hisi_sas_query_task(struct sas_task *task)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
|
||||
struct hisi_sas_internal_abort *abort,
|
||||
struct sas_task *task,
|
||||
struct hisi_sas_dq *dq)
|
||||
static bool hisi_sas_internal_abort_timeout(struct sas_task *task,
|
||||
void *data)
|
||||
{
|
||||
struct domain_device *device = task->dev;
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
struct device *dev = hisi_hba->dev;
|
||||
struct hisi_sas_port *port;
|
||||
struct asd_sas_port *sas_port = device->port;
|
||||
struct hisi_sas_slot *slot;
|
||||
int slot_idx;
|
||||
struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
|
||||
struct hisi_sas_internal_abort_data *timeout = data;
|
||||
|
||||
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags)))
|
||||
return -EINVAL;
|
||||
if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct[0].itct)
|
||||
queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
|
||||
|
||||
if (!device->port)
|
||||
return -1;
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
pr_err("Internal abort: timeout %016llx\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
} else {
|
||||
struct hisi_sas_slot *slot = task->lldd_task;
|
||||
|
||||
port = to_hisi_sas_port(sas_port);
|
||||
set_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags);
|
||||
|
||||
/* simply get a slot and send abort command */
|
||||
slot_idx = hisi_sas_slot_index_alloc(hisi_hba, NULL);
|
||||
if (slot_idx < 0)
|
||||
goto err_out;
|
||||
|
||||
slot = &hisi_hba->slot_info[slot_idx];
|
||||
slot->n_elem = 0;
|
||||
slot->task = task;
|
||||
slot->port = port;
|
||||
slot->is_internal = true;
|
||||
|
||||
hisi_sas_task_deliver(hisi_hba, slot, dq, sas_dev, abort);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
dev_err(dev, "internal abort task prep: failed[%d]!\n", slot_idx);
|
||||
|
||||
return slot_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* _hisi_sas_internal_task_abort -- execute an internal
|
||||
* abort command for single IO command or a device
|
||||
* @hisi_hba: host controller struct
|
||||
* @device: domain device
|
||||
* @abort_flag: mode of operation, device or single IO
|
||||
* @tag: tag of IO to be aborted (only relevant to single
|
||||
* IO mode)
|
||||
* @dq: delivery queue for this internal abort command
|
||||
* @rst_to_recover: If rst_to_recover set, queue a controller
|
||||
* reset if an internal abort times out.
|
||||
*/
|
||||
static int
|
||||
_hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
|
||||
struct domain_device *device, int abort_flag,
|
||||
int tag, struct hisi_sas_dq *dq, bool rst_to_recover)
|
||||
{
|
||||
struct sas_task *task;
|
||||
struct hisi_sas_device *sas_dev = device->lldd_dev;
|
||||
struct hisi_sas_internal_abort abort = {
|
||||
.flag = abort_flag,
|
||||
.tag = tag,
|
||||
};
|
||||
struct device *dev = hisi_hba->dev;
|
||||
int res;
|
||||
/*
|
||||
* The interface is not realized means this HW don't support internal
|
||||
* abort, or don't need to do internal abort. Then here, we return
|
||||
* TMF_RESP_FUNC_FAILED and let other steps go on, which depends that
|
||||
* the internal abort has been executed and returned CQ.
|
||||
*/
|
||||
if (!hisi_hba->hw->prep_abort)
|
||||
return TMF_RESP_FUNC_FAILED;
|
||||
|
||||
if (test_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags))
|
||||
return -EIO;
|
||||
|
||||
task = sas_alloc_slow_task(GFP_KERNEL);
|
||||
if (!task)
|
||||
return -ENOMEM;
|
||||
|
||||
task->dev = device;
|
||||
task->task_proto = SAS_PROTOCOL_NONE;
|
||||
task->task_done = hisi_sas_task_done;
|
||||
task->slow_task->timer.function = hisi_sas_tmf_timedout;
|
||||
task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT;
|
||||
add_timer(&task->slow_task->timer);
|
||||
|
||||
res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id,
|
||||
&abort, task, dq);
|
||||
if (res) {
|
||||
del_timer_sync(&task->slow_task->timer);
|
||||
dev_err(dev, "internal task abort: executing internal task failed: %d\n",
|
||||
res);
|
||||
goto exit;
|
||||
}
|
||||
wait_for_completion(&task->slow_task->completion);
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
|
||||
/* Internal abort timed out */
|
||||
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
||||
if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct[0].itct)
|
||||
queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
|
||||
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
||||
struct hisi_sas_slot *slot = task->lldd_task;
|
||||
|
||||
set_bit(HISI_SAS_HW_FAULT_BIT, &hisi_hba->flags);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (rst_to_recover) {
|
||||
dev_err(dev, "internal task abort: timeout and not done. Queuing reset.\n");
|
||||
queue_work(hisi_hba->wq, &hisi_hba->rst_work);
|
||||
} else {
|
||||
dev_err(dev, "internal task abort: timeout and not done.\n");
|
||||
}
|
||||
|
||||
res = -EIO;
|
||||
goto exit;
|
||||
} else
|
||||
dev_err(dev, "internal task abort: timeout.\n");
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
task->task_status.stat == TMF_RESP_FUNC_SUCC) {
|
||||
res = TMF_RESP_FUNC_SUCC;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
dev_dbg(dev, "internal task abort: task to dev %016llx task=%pK resp: 0x%x sts 0x%x\n",
|
||||
SAS_ADDR(device->sas_addr), task,
|
||||
task->task_status.resp, /* 0 is complete, -1 is undelivered */
|
||||
task->task_status.stat);
|
||||
sas_free_task(task);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
|
||||
struct domain_device *device,
|
||||
int abort_flag, int tag, bool rst_to_recover)
|
||||
{
|
||||
struct hisi_sas_slot *slot;
|
||||
struct device *dev = hisi_hba->dev;
|
||||
struct hisi_sas_dq *dq;
|
||||
int i, rc;
|
||||
|
||||
switch (abort_flag) {
|
||||
case HISI_SAS_INT_ABT_CMD:
|
||||
slot = &hisi_hba->slot_info[tag];
|
||||
dq = &hisi_hba->dq[slot->dlvry_queue];
|
||||
return _hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
abort_flag, tag, dq,
|
||||
rst_to_recover);
|
||||
case HISI_SAS_INT_ABT_DEV:
|
||||
for (i = 0; i < hisi_hba->cq_nvecs; i++) {
|
||||
struct hisi_sas_cq *cq = &hisi_hba->cq[i];
|
||||
const struct cpumask *mask = cq->irq_mask;
|
||||
|
||||
if (mask && !cpumask_intersects(cpu_online_mask, mask))
|
||||
continue;
|
||||
dq = &hisi_hba->dq[i];
|
||||
rc = _hisi_sas_internal_task_abort(hisi_hba, device,
|
||||
abort_flag, tag,
|
||||
dq, rst_to_recover);
|
||||
if (rc)
|
||||
return rc;
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unrecognised internal abort flag (%d)\n",
|
||||
abort_flag);
|
||||
return -EINVAL;
|
||||
|
||||
if (timeout->rst_ha_timeout) {
|
||||
pr_err("Internal abort: timeout and not done %016llx. Queuing reset.\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
queue_work(hisi_hba->wq, &hisi_hba->rst_work);
|
||||
} else {
|
||||
pr_err("Internal abort: timeout and not done %016llx.\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
|
||||
@ -2176,6 +2032,7 @@ static struct sas_domain_function_template hisi_sas_transport_ops = {
|
||||
.lldd_port_formed = hisi_sas_port_formed,
|
||||
.lldd_write_gpio = hisi_sas_write_gpio,
|
||||
.lldd_tmf_aborted = hisi_sas_tmf_aborted,
|
||||
.lldd_abort_timeout = hisi_sas_internal_abort_timeout,
|
||||
};
|
||||
|
||||
void hisi_sas_init_mem(struct hisi_hba *hisi_hba)
|
||||
|
@ -2603,14 +2603,15 @@ static void hisi_sas_internal_abort_quirk_timeout(struct timer_list *t)
|
||||
}
|
||||
|
||||
static void prep_abort_v2_hw(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_slot *slot,
|
||||
int device_id, int abort_flag, int tag_to_abort)
|
||||
struct hisi_sas_slot *slot)
|
||||
{
|
||||
struct sas_task *task = slot->task;
|
||||
struct sas_internal_abort_task *abort = &task->abort_task;
|
||||
struct domain_device *dev = task->dev;
|
||||
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
|
||||
struct hisi_sas_port *port = slot->port;
|
||||
struct timer_list *timer = &slot->internal_abort_timer;
|
||||
struct hisi_sas_device *sas_dev = dev->lldd_dev;
|
||||
|
||||
/* setup the quirk timer */
|
||||
timer_setup(timer, hisi_sas_internal_abort_quirk_timeout, 0);
|
||||
@ -2622,13 +2623,13 @@ static void prep_abort_v2_hw(struct hisi_hba *hisi_hba,
|
||||
(port->id << CMD_HDR_PORT_OFF) |
|
||||
(dev_is_sata(dev) <<
|
||||
CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
|
||||
(abort_flag << CMD_HDR_ABORT_FLAG_OFF));
|
||||
(abort->type << CMD_HDR_ABORT_FLAG_OFF));
|
||||
|
||||
/* dw1 */
|
||||
hdr->dw1 = cpu_to_le32(device_id << CMD_HDR_DEV_ID_OFF);
|
||||
hdr->dw1 = cpu_to_le32(sas_dev->device_id << CMD_HDR_DEV_ID_OFF);
|
||||
|
||||
/* dw7 */
|
||||
hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF);
|
||||
hdr->dw7 = cpu_to_le32(abort->tag << CMD_HDR_ABORT_IPTT_OFF);
|
||||
hdr->transfer_tags = cpu_to_le32(slot->idx);
|
||||
}
|
||||
|
||||
|
@ -1452,28 +1452,28 @@ static void prep_ata_v3_hw(struct hisi_hba *hisi_hba,
|
||||
}
|
||||
|
||||
static void prep_abort_v3_hw(struct hisi_hba *hisi_hba,
|
||||
struct hisi_sas_slot *slot,
|
||||
int device_id, int abort_flag, int tag_to_abort)
|
||||
struct hisi_sas_slot *slot)
|
||||
{
|
||||
struct sas_task *task = slot->task;
|
||||
struct sas_internal_abort_task *abort = &task->abort_task;
|
||||
struct domain_device *dev = task->dev;
|
||||
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
|
||||
struct hisi_sas_port *port = slot->port;
|
||||
struct hisi_sas_device *sas_dev = dev->lldd_dev;
|
||||
bool sata = dev_is_sata(dev);
|
||||
|
||||
/* dw0 */
|
||||
hdr->dw0 = cpu_to_le32((5U << CMD_HDR_CMD_OFF) | /*abort*/
|
||||
hdr->dw0 = cpu_to_le32((5U << CMD_HDR_CMD_OFF) | /* abort */
|
||||
(port->id << CMD_HDR_PORT_OFF) |
|
||||
(dev_is_sata(dev)
|
||||
<< CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
|
||||
(abort_flag
|
||||
<< CMD_HDR_ABORT_FLAG_OFF));
|
||||
(sata << CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
|
||||
(abort->type << CMD_HDR_ABORT_FLAG_OFF));
|
||||
|
||||
/* dw1 */
|
||||
hdr->dw1 = cpu_to_le32(device_id
|
||||
hdr->dw1 = cpu_to_le32(sas_dev->device_id
|
||||
<< CMD_HDR_DEV_ID_OFF);
|
||||
|
||||
/* dw7 */
|
||||
hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF);
|
||||
hdr->dw7 = cpu_to_le32(abort->tag << CMD_HDR_ABORT_IPTT_OFF);
|
||||
hdr->transfer_tags = cpu_to_le32(slot->idx);
|
||||
}
|
||||
|
||||
|
@ -943,6 +943,7 @@ static int sas_execute_internal_abort(struct domain_device *device,
|
||||
|
||||
task->abort_task.tag = tag;
|
||||
task->abort_task.type = type;
|
||||
task->abort_task.qid = qid;
|
||||
|
||||
res = i->dft->lldd_execute_task(task, GFP_KERNEL);
|
||||
if (res) {
|
||||
@ -957,10 +958,16 @@ static int sas_execute_internal_abort(struct domain_device *device,
|
||||
|
||||
/* Even if the internal abort timed out, return direct. */
|
||||
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
|
||||
pr_err("Internal abort: timeout %016llx\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
bool quit = true;
|
||||
|
||||
if (i->dft->lldd_abort_timeout)
|
||||
quit = i->dft->lldd_abort_timeout(task, data);
|
||||
else
|
||||
pr_err("Internal abort: timeout %016llx\n",
|
||||
SAS_ADDR(device->sas_addr));
|
||||
res = -EIO;
|
||||
break;
|
||||
if (quit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
||||
|
@ -565,6 +565,7 @@ enum sas_internal_abort {
|
||||
|
||||
struct sas_internal_abort_task {
|
||||
enum sas_internal_abort type;
|
||||
unsigned int qid;
|
||||
u16 tag;
|
||||
};
|
||||
|
||||
@ -671,6 +672,7 @@ struct sas_domain_function_template {
|
||||
/* Special TMF callbacks */
|
||||
void (*lldd_tmf_exec_complete)(struct domain_device *dev);
|
||||
void (*lldd_tmf_aborted)(struct sas_task *task);
|
||||
bool (*lldd_abort_timeout)(struct sas_task *task, void *data);
|
||||
|
||||
/* Port and Adapter management */
|
||||
int (*lldd_clear_nexus_port)(struct asd_sas_port *);
|
||||
|
Loading…
Reference in New Issue
Block a user