X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=drivers%2Fscsi%2Fhisi_sas%2Fhisi_sas_main.c;h=9bd98e5be78e1f1a09eac8e1c1b3efaf8d5db4d6;hb=9960a24a1c96a40d6ab984ffefdd0e3003a3377e;hp=61a85ff8e459f429b7090cbaa77d4712a7e854c7;hpb=527d1470744d338c912f94bc1f4dba08ffdff349;p=mirror_ubuntu-focal-kernel.git diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 61a85ff8e459..9bd98e5be78e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -22,6 +22,8 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, struct domain_device *device, int abort_flag, int tag); 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); u8 hisi_sas_get_ata_protocol(u8 cmd, int direction) { @@ -192,7 +194,8 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, if (!sas_protocol_ata(task->task_proto)) if (slot->n_elem) - dma_unmap_sg(dev, task->scatter, slot->n_elem, + dma_unmap_sg(dev, task->scatter, + task->num_scatter, task->data_dir); if (sas_dev) @@ -431,7 +434,8 @@ err_out: dev_err(dev, "task prep: failed[%d]!\n", rc); if (!sas_protocol_ata(task->task_proto)) if (n_elem) - dma_unmap_sg(dev, task->scatter, n_elem, + dma_unmap_sg(dev, task->scatter, + task->num_scatter, task->data_dir); prep_out: return rc; @@ -578,6 +582,9 @@ static int hisi_sas_dev_found(struct domain_device *device) } } + dev_info(dev, "dev[%d:%x] found\n", + sas_dev->device_id, sas_dev->dev_type); + return 0; } @@ -617,7 +624,7 @@ static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) static void hisi_sas_phyup_work(struct work_struct *work) { struct hisi_sas_phy *phy = - container_of(work, struct hisi_sas_phy, phyup_ws); + container_of(work, typeof(*phy), works[HISI_PHYE_PHY_UP]); struct hisi_hba *hisi_hba = phy->hisi_hba; struct asd_sas_phy *sas_phy = &phy->sas_phy; int phy_no = sas_phy->id; @@ -626,10 +633,37 @@ static void hisi_sas_phyup_work(struct work_struct *work) hisi_sas_bytes_dmaed(hisi_hba, phy_no); } +static void hisi_sas_linkreset_work(struct work_struct *work) +{ + struct hisi_sas_phy *phy = + container_of(work, typeof(*phy), works[HISI_PHYE_LINK_RESET]); + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + hisi_sas_control_phy(sas_phy, PHY_FUNC_LINK_RESET, NULL); +} + +static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = { + [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work, + [HISI_PHYE_LINK_RESET] = hisi_sas_linkreset_work, +}; + +bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, + enum hisi_sas_phy_event event) +{ + struct hisi_hba *hisi_hba = phy->hisi_hba; + + if (WARN_ON(event >= HISI_PHYES_NUM)) + return false; + + return queue_work(hisi_hba->wq, &phy->works[event]); +} +EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event); + static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) { struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; + int i; phy->hisi_hba = hisi_hba; phy->port = NULL; @@ -647,7 +681,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata; sas_phy->lldd_phy = phy; - INIT_WORK(&phy->phyup_ws, hisi_sas_phyup_work); + for (i = 0; i < HISI_PHYES_NUM; i++) + INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]); } static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) @@ -733,17 +768,22 @@ static void hisi_sas_dev_gone(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; - dev_info(dev, "found dev[%d:%x] is gone\n", + dev_info(dev, "dev[%d:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); - hisi_sas_internal_task_abort(hisi_hba, device, + if (!test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) { + hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - hisi_sas_dereg_device(hisi_hba, device); + hisi_sas_dereg_device(hisi_hba, device); - hisi_hba->hw->free_device(hisi_hba, sas_dev); - device->lldd_dev = NULL; - memset(sas_dev, 0, sizeof(*sas_dev)); + hisi_hba->hw->clear_itct(hisi_hba, sas_dev); + device->lldd_dev = NULL; + memset(sas_dev, 0, sizeof(*sas_dev)); + } + + if (hisi_hba->hw->free_device) + hisi_hba->hw->free_device(sas_dev); sas_dev->dev_type = SAS_PHY_UNUSED; } @@ -839,7 +879,7 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, } task->task_done = hisi_sas_task_done; - task->slow_task->timer.function = (TIMER_FUNC_TYPE)hisi_sas_tmf_timedout; + task->slow_task->timer.function = hisi_sas_tmf_timedout; task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ; add_timer(&task->slow_task->timer); @@ -859,12 +899,13 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, 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\n"); + dev_err(dev, "abort tmf: TMF task timeout and not done\n"); if (slot) slot->task = NULL; goto ex_err; - } + } else + dev_err(dev, "abort tmf: TMF task timeout\n"); } if (task->task_status.resp == SAS_TASK_COMPLETE && @@ -985,27 +1026,42 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, sizeof(ssp_task), tmf); } -static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba, - struct asd_sas_port *sas_port, enum sas_linkrate linkrate) +static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba) { - struct hisi_sas_device *sas_dev; - struct domain_device *device; + u32 state = hisi_hba->hw->get_phys_state(hisi_hba); int i; for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { - sas_dev = &hisi_hba->devices[i]; - device = sas_dev->sas_device; + struct hisi_sas_device *sas_dev = &hisi_hba->devices[i]; + struct domain_device *device = sas_dev->sas_device; + struct asd_sas_port *sas_port; + struct hisi_sas_port *port; + struct hisi_sas_phy *phy = NULL; + struct asd_sas_phy *sas_phy; + if ((sas_dev->dev_type == SAS_PHY_UNUSED) - || !device || (device->port != sas_port)) + || !device || !device->port) continue; - hisi_hba->hw->free_device(hisi_hba, sas_dev); + sas_port = device->port; + port = to_hisi_sas_port(sas_port); - /* Update linkrate of directly attached device. */ - if (!device->parent) - device->linkrate = linkrate; + list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el) + if (state & BIT(sas_phy->id)) { + phy = sas_phy->lldd_phy; + break; + } + + if (phy) { + port->id = phy->port_id; - hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + /* Update linkrate of directly attached device. */ + if (!device->parent) + device->linkrate = phy->sas_phy.linkrate; + + hisi_hba->hw->setup_itct(hisi_hba, sas_dev); + } else + port->id = 0xff; } } @@ -1020,21 +1076,17 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; struct asd_sas_port *sas_port = sas_phy->port; - struct hisi_sas_port *port = to_hisi_sas_port(sas_port); bool do_port_check = !!(_sas_port != sas_port); if (!sas_phy->phy->enabled) continue; /* Report PHY state change to libsas */ - if (state & (1 << phy_no)) { - if (do_port_check && sas_port) { + if (state & BIT(phy_no)) { + if (do_port_check && sas_port && sas_port->port_dev) { struct domain_device *dev = sas_port->port_dev; _sas_port = sas_port; - port->id = phy->port_id; - hisi_sas_refresh_port_id(hisi_hba, - sas_port, sas_phy->linkrate); if (DEV_IS_EXPANDER(dev->dev_type)) sas_ha->notify_port_event(sas_phy, @@ -1045,8 +1097,6 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, hisi_sas_phy_down(hisi_hba, phy_no, 0); } - - drain_workqueue(hisi_hba->shost->work_q); } static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) @@ -1063,7 +1113,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) return -1; - dev_dbg(dev, "controller resetting...\n"); + dev_info(dev, "controller resetting...\n"); old_state = hisi_hba->hw->get_phys_state(hisi_hba); scsi_block_requests(shost); @@ -1072,6 +1122,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) if (rc) { dev_warn(dev, "controller reset failed (%d)\n", rc); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + scsi_unblock_requests(shost); goto out; } spin_lock_irqsave(&hisi_hba->lock, flags); @@ -1083,15 +1134,14 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) /* Init and wait for PHYs to come up and all libsas event finished. */ hisi_hba->hw->phys_init(hisi_hba); msleep(1000); - drain_workqueue(hisi_hba->wq); - drain_workqueue(shost->work_q); + hisi_sas_refresh_port_id(hisi_hba); + scsi_unblock_requests(shost); state = hisi_hba->hw->get_phys_state(hisi_hba); hisi_sas_rescan_topology(hisi_hba, old_state, state); - dev_dbg(dev, "controller reset complete\n"); + dev_info(dev, "controller reset complete\n"); out: - scsi_unblock_requests(shost); clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags); return rc; @@ -1134,6 +1184,11 @@ static int hisi_sas_abort_task(struct sas_task *task) rc2 = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); + if (rc2 < 0) { + dev_err(dev, "abort task: internal abort (%d)\n", rc2); + return TMF_RESP_FUNC_FAILED; + } + /* * If the TMF finds that the IO is not in the device and also * the internal abort does not succeed, then it is safe to @@ -1151,8 +1206,12 @@ 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) { - hisi_sas_internal_task_abort(hisi_hba, device, - HISI_SAS_INT_ABT_DEV, 0); + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task: internal abort failed\n"); + goto out; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_softreset_ata_disk(device); } @@ -1163,7 +1222,8 @@ static int hisi_sas_abort_task(struct sas_task *task) rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); - if (rc == TMF_RESP_FUNC_FAILED && task->lldd_task) { + if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && + task->lldd_task) { spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_do_release_task(hisi_hba, task, slot); spin_unlock_irqrestore(&hisi_hba->lock, flags); @@ -1178,12 +1238,29 @@ out: static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) { + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct device *dev = hisi_hba->dev; struct hisi_sas_tmf_task tmf_task; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; + + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "abort task set: internal abort rc=%d\n", rc); + return TMF_RESP_FUNC_FAILED; + } + hisi_sas_dereg_device(hisi_hba, device); tmf_task.tmf = TMF_ABORT_TASK_SET; 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); + } + return rc; } @@ -1213,20 +1290,25 @@ 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; + struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; + unsigned long flags; if (sas_dev->dev_status != HISI_SAS_DEV_EH) return TMF_RESP_FUNC_FAILED; sas_dev->dev_status = HISI_SAS_DEV_NORMAL; - hisi_sas_internal_task_abort(hisi_hba, device, + rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc); + return TMF_RESP_FUNC_FAILED; + } hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_debug_I_T_nexus_reset(device); - if (rc == TMF_RESP_FUNC_COMPLETE) { + if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) { spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_release_task(hisi_hba, device); spin_unlock_irqrestore(&hisi_hba->lock, flags); @@ -1249,8 +1331,10 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) /* Clear internal IO and then hardreset */ rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); - if (rc == TMF_RESP_FUNC_FAILED) + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); goto out; + } hisi_sas_dereg_device(hisi_hba, device); phy = sas_get_local_phy(device); @@ -1266,6 +1350,14 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) } else { struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET }; + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) { + dev_err(dev, "lu_reset: internal abort failed\n"); + goto out; + } + hisi_sas_dereg_device(hisi_hba, device); + rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); if (rc == TMF_RESP_FUNC_COMPLETE) { spin_lock_irqsave(&hisi_hba->lock, flags); @@ -1283,8 +1375,14 @@ out: static int hisi_sas_clear_nexus_ha(struct sas_ha_struct *sas_ha) { struct hisi_hba *hisi_hba = sas_ha->lldd_ha; + HISI_SAS_DECLARE_RST_WORK_ON_STACK(r); + + queue_work(hisi_hba->wq, &r.work); + wait_for_completion(r.completion); + if (r.done) + return TMF_RESP_FUNC_COMPLETE; - return hisi_sas_controller_reset(hisi_hba); + return TMF_RESP_FUNC_FAILED; } static int hisi_sas_query_task(struct sas_task *task) @@ -1441,8 +1539,14 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, 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 -EOPNOTSUPP; + return TMF_RESP_FUNC_FAILED; task = sas_alloc_slow_task(GFP_KERNEL); if (!task) @@ -1451,7 +1555,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, task->dev = device; task->task_proto = device->tproto; task->task_done = hisi_sas_task_done; - task->slow_task->timer.function = (TIMER_FUNC_TYPE)hisi_sas_tmf_timedout; + task->slow_task->timer.function = hisi_sas_tmf_timedout; task->slow_task->timer.expires = jiffies + msecs_to_jiffies(110); add_timer(&task->slow_task->timer); @@ -1473,9 +1577,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, if (slot) slot->task = NULL; - dev_err(dev, "internal task abort: timeout.\n"); + 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 && @@ -1657,6 +1763,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) cq->hisi_hba = hisi_hba; /* Delivery queue structure */ + spin_lock_init(&dq->lock); dq->id = i; dq->hisi_hba = hisi_hba; @@ -1803,6 +1910,17 @@ void hisi_sas_rst_work_handler(struct work_struct *work) } EXPORT_SYMBOL_GPL(hisi_sas_rst_work_handler); +void hisi_sas_sync_rst_work_handler(struct work_struct *work) +{ + struct hisi_sas_rst *rst = + container_of(work, struct hisi_sas_rst, work); + + if (!hisi_sas_controller_reset(rst->hisi_hba)) + rst->done = true; + complete(rst->completion); +} +EXPORT_SYMBOL_GPL(hisi_sas_sync_rst_work_handler); + int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev;