|
|
|
@ -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 @@ out:
|
|
|
|
|
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 @@ out:
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|