From 12b6fcd0ea7f3cb7c3b34668fc678779924123ae Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Mon, 18 Oct 2021 16:57:53 +0300 Subject: [PATCH 01/46] scsi: target: core: Remove from tmr_list during LUN unlink Currently TMF commands are removed from de_device.dev_tmf_list at the very end of se_cmd lifecycle. However, se_lun unlinks from se_cmd upon a command status (response) being queued in transport layer. This means that LUN and backend device can be deleted in the meantime and a panic will occur: target_tmr_work() cmd->se_tfo->queue_tm_rsp(cmd); // send abort_rsp to a wire transport_lun_remove_cmd(cmd) // unlink se_cmd from se_lun - // - // - // - <<<--- lun remove <<<--- core backend device remove - // - // - // - qlt_handle_abts_completion() tfo->free_mcmd() transport_generic_free_cmd() target_put_sess_cmd() core_tmr_release_req() { if (dev) { // backend device, can not be null spin_lock_irqsave(&dev->se_tmr_lock, flags); //<<<--- CRASH Call Trace: NIP [c000000000e1683c] _raw_spin_lock_irqsave+0x2c/0xc0 LR [c00800000e433338] core_tmr_release_req+0x40/0xa0 [target_core_mod] Call Trace: (unreliable) 0x0 target_put_sess_cmd+0x2a0/0x370 [target_core_mod] transport_generic_free_cmd+0x6c/0x1b0 [target_core_mod] tcm_qla2xxx_complete_mcmd+0x28/0x50 [tcm_qla2xxx] process_one_work+0x2c4/0x5c0 worker_thread+0x88/0x690 For the iSCSI protocol this is easily reproduced: - Send some SCSI sommand - Send Abort of that command over iSCSI - Remove LUN on target - Send next iSCSI command to acknowledge the Abort_Response - Target panics There is no need to keep the command in tmr_list until response completion, so move the removal from tmr_list from the response completion to the response queueing when the LUN is unlinked. Move the removal from state list too as it is a subject to the same race condition. Link: https://lore.kernel.org/r/20211018135753.15297-1-d.bogdanov@yadro.com Fixes: c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0-rc6") Reviewed-by: Roman Bolshakov Reviewed-by: Mike Christie Signed-off-by: Dmitry Bogdanov Signed-off-by: Martin K. Petersen --- drivers/target/target_core_tmr.c | 17 +-------------- drivers/target/target_core_transport.c | 30 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index e7fcbc09f9db..bac111456fa1 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -50,15 +50,6 @@ EXPORT_SYMBOL(core_tmr_alloc_req); void core_tmr_release_req(struct se_tmr_req *tmr) { - struct se_device *dev = tmr->tmr_dev; - unsigned long flags; - - if (dev) { - spin_lock_irqsave(&dev->se_tmr_lock, flags); - list_del_init(&tmr->tmr_list); - spin_unlock_irqrestore(&dev->se_tmr_lock, flags); - } - kfree(tmr); } @@ -156,13 +147,6 @@ void core_tmr_abort_task( se_cmd->state_active = false; spin_unlock_irqrestore(&dev->queues[i].lock, flags); - /* - * Ensure that this ABORT request is visible to the LU - * RESET code. - */ - if (!tmr->tmr_dev) - WARN_ON_ONCE(transport_lookup_tmr_lun(tmr->task_cmd) < 0); - if (dev->transport->tmr_notify) dev->transport->tmr_notify(dev, TMR_ABORT_TASK, &aborted_list); @@ -234,6 +218,7 @@ static void core_tmr_drain_tmr_list( } list_move_tail(&tmr_p->tmr_list, &drain_tmr_list); + tmr_p->tmr_dev = NULL; } spin_unlock_irqrestore(&dev->se_tmr_lock, flags); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4a2e749eb182..7838dc20f713 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -676,6 +676,21 @@ static void target_remove_from_state_list(struct se_cmd *cmd) spin_unlock_irqrestore(&dev->queues[cmd->cpuid].lock, flags); } +static void target_remove_from_tmr_list(struct se_cmd *cmd) +{ + struct se_device *dev = NULL; + unsigned long flags; + + if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) + dev = cmd->se_tmr_req->tmr_dev; + + if (dev) { + spin_lock_irqsave(&dev->se_tmr_lock, flags); + if (cmd->se_tmr_req->tmr_dev) + list_del_init(&cmd->se_tmr_req->tmr_list); + spin_unlock_irqrestore(&dev->se_tmr_lock, flags); + } +} /* * This function is called by the target core after the target core has * finished processing a SCSI command or SCSI TMF. Both the regular command @@ -687,13 +702,6 @@ static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd) { unsigned long flags; - target_remove_from_state_list(cmd); - - /* - * Clear struct se_cmd->se_lun before the handoff to FE. - */ - cmd->se_lun = NULL; - spin_lock_irqsave(&cmd->t_state_lock, flags); /* * Determine if frontend context caller is requesting the stopping of @@ -728,8 +736,16 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) if (!lun) return; + target_remove_from_state_list(cmd); + target_remove_from_tmr_list(cmd); + if (cmpxchg(&cmd->lun_ref_active, true, false)) percpu_ref_put(&lun->lun_ref); + + /* + * Clear struct se_cmd->se_lun before the handoff to FE. + */ + cmd->se_lun = NULL; } static void target_complete_failure_work(struct work_struct *work) From 11682523573c1c37f8d7a6ff1096c456267ebfff Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:15 -0700 Subject: [PATCH 02/46] scsi: ufs: Revert "Retry aborted SCSI commands instead of completing these successfully" Commit 73dc3c4ac703 ("scsi: ufs: Retry aborted SCSI commands instead of completing these successfully") is not necessary. If a SCSI command is aborted successfully the UFS controller has not modified the command status and the command status still has the value assigned by ufshcd_prepare_req_desc_hdr(), namely OCS_INVALID_COMMAND_STATUS. The function ufshcd_transfer_rsp_status() requeues commands that have an invalid command status. Hence revert commit 73dc3c4ac703. Link: https://lore.kernel.org/r/20211020214024.2007615-2-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 10f5805ae3f6..53dd42f95eed 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5248,11 +5248,9 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) * __ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance * @completed_reqs: bitmask that indicates which requests to complete - * @retry_requests: whether to ask the SCSI core to retry completed requests */ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, - unsigned long completed_reqs, - bool retry_requests) + unsigned long completed_reqs) { struct ufshcd_lrb *lrbp; struct scsi_cmnd *cmd; @@ -5268,8 +5266,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, if (unlikely(ufshcd_should_inform_monitor(hba, lrbp))) ufshcd_update_monitor(hba, lrbp); ufshcd_add_command_trace(hba, index, UFS_CMD_COMP); - result = retry_requests ? DID_BUS_BUSY << 16 : - ufshcd_transfer_rsp_status(hba, lrbp); + result = ufshcd_transfer_rsp_status(hba, lrbp); scsi_dma_unmap(cmd); cmd->result = result; /* Mark completed command as NULL in LRB */ @@ -5295,14 +5292,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, /** * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance - * @retry_requests: whether or not to ask to retry requests * * Returns * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ -static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba, - bool retry_requests) +static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba) { unsigned long completed_reqs, flags; u32 tr_doorbell; @@ -5331,8 +5326,7 @@ static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba, spin_unlock_irqrestore(&hba->outstanding_lock, flags); if (completed_reqs) { - __ufshcd_transfer_req_compl(hba, completed_reqs, - retry_requests); + __ufshcd_transfer_req_compl(hba, completed_reqs); return IRQ_HANDLED; } else { return IRQ_NONE; @@ -5831,13 +5825,7 @@ out: /* Complete requests that have door-bell cleared */ static void ufshcd_complete_requests(struct ufs_hba *hba) { - ufshcd_transfer_req_compl(hba, /*retry_requests=*/false); - ufshcd_tmc_handler(hba); -} - -static void ufshcd_retry_aborted_requests(struct ufs_hba *hba) -{ - ufshcd_transfer_req_compl(hba, /*retry_requests=*/true); + ufshcd_transfer_req_compl(hba); ufshcd_tmc_handler(hba); } @@ -6186,7 +6174,8 @@ again: } lock_skip_pending_xfer_clear: - ufshcd_retry_aborted_requests(hba); + /* Complete the requests that are cleared by s/w */ + ufshcd_complete_requests(hba); spin_lock_irqsave(hba->host->host_lock, flags); hba->silence_err_logs = false; @@ -6478,7 +6467,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) retval |= ufshcd_tmc_handler(hba); if (intr_status & UTP_TRANSFER_REQ_COMPL) - retval |= ufshcd_transfer_req_compl(hba, /*retry_requests=*/false); + retval |= ufshcd_transfer_req_compl(hba); return retval; } @@ -6898,7 +6887,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) err = ufshcd_clear_cmd(hba, pos); if (err) break; - __ufshcd_transfer_req_compl(hba, 1U << pos, false); + __ufshcd_transfer_req_compl(hba, 1U << pos); } } @@ -7060,7 +7049,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) dev_err(hba->dev, "%s: cmd was completed, but without a notifying intr, tag = %d", __func__, tag); - __ufshcd_transfer_req_compl(hba, 1UL << tag, /*retry_requests=*/false); + __ufshcd_transfer_req_compl(hba, 1UL << tag); goto release; } @@ -7126,7 +7115,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) ufshpb_reset_host(hba); ufshcd_hba_stop(hba); hba->silence_err_logs = true; - ufshcd_retry_aborted_requests(hba); + ufshcd_complete_requests(hba); hba->silence_err_logs = false; /* scale up clocks to max frequency before full reinitialization */ From 91bb765ccab1a067e646c9dbed0b5ed0bb7c39c9 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:16 -0700 Subject: [PATCH 03/46] scsi: ufs: core: Improve source code comments Make the descriptions above data structures that come from the UFS specification match the terminology from that specification. This makes it easier to find these data structures while reading the UFS specification. Link: https://lore.kernel.org/r/20211020214024.2007615-3-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshci.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index de95be5d11d4..9a754fab8908 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -425,7 +425,7 @@ struct ufshcd_sg_entry { }; /** - * struct utp_transfer_cmd_desc - UFS Command Descriptor structure + * struct utp_transfer_cmd_desc - UTP Command Descriptor (UCD) * @command_upiu: Command UPIU Frame address * @response_upiu: Response UPIU Frame address * @prd_table: Physical Region Descriptor @@ -451,7 +451,7 @@ struct request_desc_header { }; /** - * struct utp_transfer_req_desc - UTRD structure + * struct utp_transfer_req_desc - UTP Transfer Request Descriptor (UTRD) * @header: UTRD header DW-0 to DW-3 * @command_desc_base_addr_lo: UCD base address low DW-4 * @command_desc_base_addr_hi: UCD base address high DW-5 From 957d63e77a9ce9ef63b2a6b4d1d84d549e427cd8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:17 -0700 Subject: [PATCH 04/46] scsi: ufs: core: Improve static type checking Introduce an enumeration type for the overall command status to allow the compiler to perform more static type checking. Link: https://lore.kernel.org/r/20211020214024.2007615-4-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 9 +++++---- drivers/scsi/ufs/ufshci.h | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 53dd42f95eed..610be6746f74 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -711,7 +711,7 @@ static inline bool ufshcd_is_device_present(struct ufs_hba *hba) * This function is used to get the OCS field from UTRD * Returns the OCS field in the UTRD */ -static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) +static enum utp_ocs ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) { return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS; } @@ -5089,7 +5089,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { int result = 0; int scsi_status; - int ocs; + enum utp_ocs ocs; /* overall command status of utrd */ ocs = ufshcd_get_tr_ocs(lrbp); @@ -6631,7 +6631,8 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, u8 tm_function, u8 *tm_response) { struct utp_task_req_desc treq = { { 0 }, }; - int ocs_value, err; + enum utp_ocs ocs_value; + int err; /* Configure task request descriptor */ treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD); @@ -6809,7 +6810,7 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, int err; enum dev_cmd_type cmd_type = DEV_CMD_TYPE_QUERY; struct utp_task_req_desc treq = { { 0 }, }; - int ocs_value; + enum utp_ocs ocs_value; u8 tm_f = be32_to_cpu(req_upiu->header.dword_1) >> 16 & MASK_TM_FUNC; switch (msgcode) { diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index 9a754fab8908..f66cf9e477cb 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -389,7 +389,7 @@ enum { }; /* Overall command status values */ -enum { +enum utp_ocs { OCS_SUCCESS = 0x0, OCS_INVALID_CMD_TABLE_ATTR = 0x1, OCS_INVALID_PRDT_ATTR = 0x2, @@ -402,6 +402,9 @@ enum { OCS_INVALID_CRYPTO_CONFIG = 0x9, OCS_GENERAL_CRYPTO_ERROR = 0xA, OCS_INVALID_COMMAND_STATUS = 0x0F, +}; + +enum { MASK_OCS = 0x0F, }; From 4693fad7d6d4c26fc60ec43fc50f9f3d74e11dbe Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:18 -0700 Subject: [PATCH 05/46] scsi: ufs: core: Log error handler activity Kernel logs are hard to comprehend without information about what the UFS error handler is doing. Hence this patch that logs information about error handler activity. Link: https://lore.kernel.org/r/20211020214024.2007615-5-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 610be6746f74..49623bf3e876 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -132,6 +132,14 @@ enum { UFSHCD_CAN_QUEUE = 32, }; +static const char *const ufshcd_state_name[] = { + [UFSHCD_STATE_RESET] = "reset", + [UFSHCD_STATE_OPERATIONAL] = "operational", + [UFSHCD_STATE_ERROR] = "error", + [UFSHCD_STATE_EH_SCHEDULED_FATAL] = "eh_fatal", + [UFSHCD_STATE_EH_SCHEDULED_NON_FATAL] = "eh_non_fatal", +}; + /* UFSHCD error handling flags */ enum { UFSHCD_EH_IN_PROGRESS = (1 << 0), @@ -6069,6 +6077,13 @@ static void ufshcd_err_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eh_work); + dev_info(hba->dev, + "%s started; HBA state %s; powered %d; shutting down %d; saved_err = %d; saved_uic_err = %d; force_reset = %d%s\n", + __func__, ufshcd_state_name[hba->ufshcd_state], + hba->is_powered, hba->shutting_down, hba->saved_err, + hba->saved_uic_err, hba->force_reset, + ufshcd_is_link_broken(hba) ? "; link is broken" : ""); + down(&hba->host_sem); spin_lock_irqsave(hba->host->host_lock, flags); if (ufshcd_err_handling_should_stop(hba)) { @@ -6163,6 +6178,8 @@ again: err_xfer = true; goto lock_skip_pending_xfer_clear; } + dev_err(hba->dev, "Aborted tag %d / CDB %#02x\n", tag, + hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1); } /* Clear pending task management requests */ @@ -6243,6 +6260,9 @@ skip_err_handling: spin_unlock_irqrestore(hba->host->host_lock, flags); ufshcd_err_handling_unprepare(hba); up(&hba->host_sem); + + dev_info(hba->dev, "%s finished; HBA state %s\n", __func__, + ufshcd_state_name[hba->ufshcd_state]); } /** @@ -6539,6 +6559,10 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) err = ufshcd_wait_for_register(hba, REG_UTP_TASK_REQ_DOOR_BELL, mask, 0, 1000, 1000); + + dev_err(hba->dev, "Clearing task management function with tag %d %s\n", + tag, err ? "succeeded" : "failed"); + out: return err; } From 267a59f6a5e4e5e3cb5b0f88c4a58a738833d8b3 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:19 -0700 Subject: [PATCH 06/46] scsi: ufs: core: Export ufshcd_schedule_eh_work() Make it possible to call ufshcd_schedule_eh_work() from other source files than ufshcd.c. Additionally, convert a source code comment into a lockdep_assert_held() call. Link: https://lore.kernel.org/r/20211020214024.2007615-6-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 6 +++--- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 49623bf3e876..0739aa566725 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -244,7 +244,6 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up); static irqreturn_t ufshcd_intr(int irq, void *__hba); static int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode); -static void ufshcd_schedule_eh_work(struct ufs_hba *hba); static int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on); static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on); static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, @@ -5915,9 +5914,10 @@ static inline bool ufshcd_is_saved_err_fatal(struct ufs_hba *hba) (hba->saved_err & (INT_FATAL_ERRORS | UFSHCD_UIC_HIBERN8_MASK)); } -/* host lock must be held before calling this func */ -static inline void ufshcd_schedule_eh_work(struct ufs_hba *hba) +void ufshcd_schedule_eh_work(struct ufs_hba *hba) { + lockdep_assert_held(hba->host->host_lock); + /* handle fatal errors only when link is not in error state */ if (hba->ufshcd_state != UFSHCD_STATE_ERROR) { if (hba->force_reset || ufshcd_is_link_broken(hba) || diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 64ce723327b9..3f5dc6732fe1 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -1023,6 +1023,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk); void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val); void ufshcd_hba_stop(struct ufs_hba *hba); +void ufshcd_schedule_eh_work(struct ufs_hba *hba); static inline void check_upiu_size(void) { From e0022c6c2906ddd85416c793948118d1b6b86b36 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:20 -0700 Subject: [PATCH 07/46] scsi: ufs: core: Make it easier to add new debugfs attributes Introduce an array for debugfs attributes to make it easier to add new debugfs attributes. Change the value of the inode.i_private pointer for debugfs attributes from a pointer to the HBA data structure to a pointer to the attribute description for the stats attribute. Store the HBA pointer in the private data of the parent inode instead. Link: https://lore.kernel.org/r/20211020214024.2007615-7-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-debugfs.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 4e1ff209b933..1d4e5aa4b762 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -8,6 +8,18 @@ static struct dentry *ufs_debugfs_root; +struct ufs_debugfs_attr { + const char *name; + mode_t mode; + const struct file_operations *fops; +}; + +/* @file corresponds to a debugfs attribute in directory hba->debugfs_root. */ +static inline struct ufs_hba *hba_from_file(const struct file *file) +{ + return d_inode(file->f_path.dentry->d_parent)->i_private; +} + void __init ufs_debugfs_init(void) { ufs_debugfs_root = debugfs_create_dir("ufshcd", NULL); @@ -20,7 +32,7 @@ void ufs_debugfs_exit(void) static int ufs_debugfs_stats_show(struct seq_file *s, void *data) { - struct ufs_hba *hba = s->private; + struct ufs_hba *hba = hba_from_file(s->file); struct ufs_event_hist *e = hba->ufs_stats.event; #define PRT(fmt, typ) \ @@ -126,13 +138,28 @@ static void ufs_debugfs_restart_ee(struct work_struct *work) ufs_debugfs_put_user_access(hba); } +static const struct ufs_debugfs_attr ufs_attrs[] = { + { "stats", 0400, &ufs_debugfs_stats_fops }, + { } +}; + void ufs_debugfs_hba_init(struct ufs_hba *hba) { + const struct ufs_debugfs_attr *attr; + struct dentry *root; + /* Set default exception event rate limit period to 20ms */ hba->debugfs_ee_rate_limit_ms = 20; INIT_DELAYED_WORK(&hba->debugfs_ee_work, ufs_debugfs_restart_ee); - hba->debugfs_root = debugfs_create_dir(dev_name(hba->dev), ufs_debugfs_root); - debugfs_create_file("stats", 0400, hba->debugfs_root, hba, &ufs_debugfs_stats_fops); + + root = debugfs_create_dir(dev_name(hba->dev), ufs_debugfs_root); + if (IS_ERR_OR_NULL(root)) + return; + hba->debugfs_root = root; + d_inode(root)->i_private = hba; + for (attr = ufs_attrs; attr->name; attr++) + debugfs_create_file(attr->name, attr->mode, root, (void *)attr, + attr->fops); debugfs_create_file("exception_event_mask", 0600, hba->debugfs_root, hba, &ee_usr_mask_fops); debugfs_create_u32("exception_event_rate_limit_ms", 0600, hba->debugfs_root, From 7340faae947400907e5e7581444712110d2811d5 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:21 -0700 Subject: [PATCH 08/46] scsi: ufs: core: Add debugfs attributes for triggering the UFS EH Make it easier to test the impact of the UFS error handler on software that submits SCSI commands to the UFS driver. Link: https://lore.kernel.org/r/20211020214024.2007615-8-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-debugfs.c | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 1d4e5aa4b762..4a0bbcf1757a 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -138,8 +138,73 @@ static void ufs_debugfs_restart_ee(struct work_struct *work) ufs_debugfs_put_user_access(hba); } +static int ufs_saved_err_show(struct seq_file *s, void *data) +{ + struct ufs_debugfs_attr *attr = s->private; + struct ufs_hba *hba = hba_from_file(s->file); + const int *p; + + if (strcmp(attr->name, "saved_err") == 0) { + p = &hba->saved_err; + } else if (strcmp(attr->name, "saved_uic_err") == 0) { + p = &hba->saved_uic_err; + } else { + return -ENOENT; + } + + seq_printf(s, "%d\n", *p); + return 0; +} + +static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ufs_debugfs_attr *attr = file->f_inode->i_private; + struct ufs_hba *hba = hba_from_file(file); + char val_str[16] = { }; + int val, ret; + + if (count > sizeof(val_str)) + return -EINVAL; + if (copy_from_user(val_str, buf, count)) + return -EFAULT; + ret = kstrtoint(val_str, 0, &val); + if (ret < 0) + return ret; + + spin_lock_irq(hba->host->host_lock); + if (strcmp(attr->name, "saved_err") == 0) { + hba->saved_err = val; + } else if (strcmp(attr->name, "saved_uic_err") == 0) { + hba->saved_uic_err = val; + } else { + ret = -ENOENT; + } + if (ret == 0) + ufshcd_schedule_eh_work(hba); + spin_unlock_irq(hba->host->host_lock); + + return ret < 0 ? ret : count; +} + +static int ufs_saved_err_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufs_saved_err_show, inode->i_private); +} + +static const struct file_operations ufs_saved_err_fops = { + .owner = THIS_MODULE, + .open = ufs_saved_err_open, + .read = seq_read, + .write = ufs_saved_err_write, + .llseek = seq_lseek, + .release = single_release, +}; + static const struct ufs_debugfs_attr ufs_attrs[] = { { "stats", 0400, &ufs_debugfs_stats_fops }, + { "saved_err", 0600, &ufs_saved_err_fops }, + { "saved_uic_err", 0600, &ufs_saved_err_fops }, { } }; From 3ad317a1f932f3dfdedfba405f7b2d02154e20e1 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:22 -0700 Subject: [PATCH 09/46] scsi: ufs: core: Remove three superfluous casts Casting an int explicitly to u16 when passed as an argument to a function is not necessary. Since prd_table and ucd_prdt_ptr both have type struct ufshcd_sg_entry *, remove the casts from assignments of these two to each other. This patch does not change any functionality. Link: https://lore.kernel.org/r/20211020214024.2007615-9-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 0739aa566725..7ea0588247b0 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2374,9 +2374,9 @@ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) sizeof(struct ufshcd_sg_entry))); else lrbp->utr_descriptor_ptr->prd_table_length = - cpu_to_le16((u16) (sg_segments)); + cpu_to_le16(sg_segments); - prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr; + prd_table = lrbp->ucd_prdt_ptr; scsi_for_each_sg(cmd, sg, sg_segments, i) { prd_table[i].size = @@ -2668,7 +2668,7 @@ static void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i) lrb->ucd_req_dma_addr = cmd_desc_element_addr; lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp[i].response_upiu; lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset; - lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp[i].prd_table; + lrb->ucd_prdt_ptr = cmd_descp[i].prd_table; lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset; } From 9a868c8ad3f467fe3487e3d96fc0e2f3c90f34f9 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:23 -0700 Subject: [PATCH 10/46] scsi: ufs: core: Add a compile-time structure size check Before modifying struct ufshcd_sg_entry, add a compile-time structure size check. Link: https://lore.kernel.org/r/20211020214024.2007615-10-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7ea0588247b0..dde4d3f607f2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -9791,6 +9791,11 @@ static int __init ufshcd_core_init(void) { int ret; + /* Verify that there are no gaps in struct utp_transfer_cmd_desc. */ + static_assert(sizeof(struct utp_transfer_cmd_desc) == + 2 * ALIGNED_UPIU_SIZE + + SG_ALL * sizeof(struct ufshcd_sg_entry)); + ufs_debugfs_init(); ret = scsi_register_driver(&ufs_dev_wlun_template.gendrv); From 1ea7d8026300f72344bb574a12667f9543f73348 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 20 Oct 2021 14:40:24 -0700 Subject: [PATCH 11/46] scsi: ufs: core: Micro-optimize ufshcd_map_sg() Replace two cpu_to_le32() calls by a single cpu_to_le64() call. Additionally, issue a warning if the length of an scatter gather list element exceeds what is allowed by the UFSHCI specification. Link: https://lore.kernel.org/r/20211020214024.2007615-11-bvanassche@acm.org Acked-by: Avri Altman Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 19 +++++++++++++------ drivers/scsi/ufs/ufshci.h | 6 ++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index dde4d3f607f2..04cb67995750 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2379,12 +2379,19 @@ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) prd_table = lrbp->ucd_prdt_ptr; scsi_for_each_sg(cmd, sg, sg_segments, i) { - prd_table[i].size = - cpu_to_le32(((u32) sg_dma_len(sg))-1); - prd_table[i].base_addr = - cpu_to_le32(lower_32_bits(sg->dma_address)); - prd_table[i].upper_addr = - cpu_to_le32(upper_32_bits(sg->dma_address)); + const unsigned int len = sg_dma_len(sg); + + /* + * From the UFSHCI spec: "Data Byte Count (DBC): A '0' + * based value that indicates the length, in bytes, of + * the data block. A maximum of length of 256KB may + * exist for any entry. Bits 1:0 of this field shall be + * 11b to indicate Dword granularity. A value of '3' + * indicates 4 bytes, '7' indicates 8 bytes, etc." + */ + WARN_ONCE(len > 256 * 1024, "len = %#x\n", len); + prd_table[i].size = cpu_to_le32(len - 1); + prd_table[i].addr = cpu_to_le64(sg->dma_address); prd_table[i].reserved = 0; } } else { diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index f66cf9e477cb..6a295c88d850 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -415,14 +415,12 @@ enum { /** * struct ufshcd_sg_entry - UFSHCI PRD Entry - * @base_addr: Lower 32bit physical address DW-0 - * @upper_addr: Upper 32bit physical address DW-1 + * @addr: Physical address; DW-0 and DW-1. * @reserved: Reserved for future use DW-2 * @size: size of physical segment DW-3 */ struct ufshcd_sg_entry { - __le32 base_addr; - __le32 upper_addr; + __le64 addr; __le32 reserved; __le32 size; }; From 2c2934c80e139ad4528fda2197c17938249e8eff Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 24 Oct 2021 21:48:09 +0200 Subject: [PATCH 12/46] scsi: elx: Use 'bitmap_zalloc()' when applicable 'sli4->ext[i].use_map' is a bitmap. Use 'bitmap_zalloc()' to simplify code, improve the semantic and avoid some open-coded arithmetic in allocator arguments. Also change the corresponding 'kfree()' into 'bitmap_free()' to keep consistency. Link: https://lore.kernel.org/r/2a0a83949fb896a0a236dcca94dfdc8486d489f5.1635104793.git.christophe.jaillet@wanadoo.fr Signed-off-by: Christophe JAILLET Signed-off-by: Martin K. Petersen --- drivers/scsi/elx/libefc_sli/sli4.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c index 6c6c04e1b74d..907d67aeac23 100644 --- a/drivers/scsi/elx/libefc_sli/sli4.c +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -4145,7 +4145,7 @@ static int sli_get_read_config(struct sli4 *sli4) { struct sli4_rsp_read_config *conf = sli4->bmbx.virt; - u32 i, total, total_size; + u32 i, total; u32 *base; if (sli_cmd_read_config(sli4, sli4->bmbx.virt)) { @@ -4203,8 +4203,7 @@ sli_get_read_config(struct sli4 *sli4) for (i = 0; i < SLI4_RSRC_MAX; i++) { total = sli4->ext[i].number * sli4->ext[i].size; - total_size = BITS_TO_LONGS(total) * sizeof(long); - sli4->ext[i].use_map = kzalloc(total_size, GFP_KERNEL); + sli4->ext[i].use_map = bitmap_zalloc(total, GFP_KERNEL); if (!sli4->ext[i].use_map) { efc_log_err(sli4, "bitmap memory allocation failed %d\n", i); @@ -4743,7 +4742,7 @@ sli_reset(struct sli4 *sli4) sli4->ext[0].base = NULL; for (i = 0; i < SLI4_RSRC_MAX; i++) { - kfree(sli4->ext[i].use_map); + bitmap_free(sli4->ext[i].use_map); sli4->ext[i].use_map = NULL; sli4->ext[i].base = NULL; } @@ -4784,7 +4783,7 @@ sli_teardown(struct sli4 *sli4) for (i = 0; i < SLI4_RSRC_MAX; i++) { sli4->ext[i].base = NULL; - kfree(sli4->ext[i].use_map); + bitmap_free(sli4->ext[i].use_map); sli4->ext[i].use_map = NULL; } From bb2ca6b3f09ac20e8357d257d0557ab5ddf6adcd Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:00 -0700 Subject: [PATCH 13/46] scsi: qla2xxx: Relogin during fabric disturbance For RSCN of type "Area, Domain, or Fabric", which indicate a portion or entire fabric was disturbed, current driver does not set the scan_need flag to indicate a session was affected by the disturbance. This in turn can lead to I/O timeout and delay of relogin. Hence initiate relogin in the event of fabric disturbance. Link: https://lore.kernel.org/r/20211026115412.27691-2-njavali@marvell.com Fixes: 1560bafdff9e ("scsi: qla2xxx: Use complete switch scan for RSCN events") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 54 +++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 908a72ebf7c2..2438191d0ad6 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1786,16 +1786,52 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) fc_port_t *fcport; unsigned long flags; - fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); - if (fcport) { - if (fcport->flags & FCF_FCP2_DEVICE) { - ql_dbg(ql_dbg_disc, vha, 0x2115, - "Delaying session delete for FCP2 portid=%06x %8phC ", - fcport->d_id.b24, fcport->port_name); - return; + switch (ea->id.b.rsvd_1) { + case RSCN_PORT_ADDR: + fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); + if (fcport) { + if (fcport->flags & FCF_FCP2_DEVICE) { + ql_dbg(ql_dbg_disc, vha, 0x2115, + "Delaying session delete for FCP2 portid=%06x %8phC ", + fcport->d_id.b24, fcport->port_name); + return; + } + fcport->scan_needed = 1; + fcport->rscn_gen++; } - fcport->scan_needed = 1; - fcport->rscn_gen++; + break; + case RSCN_AREA_ADDR: + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->flags & FCF_FCP2_DEVICE) + continue; + + if ((ea->id.b24 & 0xffff00) == (fcport->d_id.b24 & 0xffff00)) { + fcport->scan_needed = 1; + fcport->rscn_gen++; + } + } + break; + case RSCN_DOM_ADDR: + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->flags & FCF_FCP2_DEVICE) + continue; + + if ((ea->id.b24 & 0xff0000) == (fcport->d_id.b24 & 0xff0000)) { + fcport->scan_needed = 1; + fcport->rscn_gen++; + } + } + break; + case RSCN_FAB_ADDR: + default: + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->flags & FCF_FCP2_DEVICE) + continue; + + fcport->scan_needed = 1; + fcport->rscn_gen++; + } + break; } spin_lock_irqsave(&vha->work_lock, flags); From c98c5daaa24b583cba1369b7d167f93c6ae7299c Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:01 -0700 Subject: [PATCH 14/46] scsi: qla2xxx: Fix gnl list corruption Current code does list element deletion and addition in and out of lock protection. This patch moves deletion behind lock. list_add double add: new=ffff9130b5eb89f8, prev=ffff9130b5eb89f8, next=ffff9130c6a715f0. ------------[ cut here ]------------ kernel BUG at lib/list_debug.c:31! invalid opcode: 0000 [#1] SMP PTI CPU: 1 PID: 182395 Comm: kworker/1:37 Kdump: loaded Tainted: G W OE --------- - - 4.18.0-193.el8.x86_64 #1 Hardware name: HP ProLiant DL160 Gen8, BIOS J03 02/10/2014 Workqueue: qla2xxx_wq qla2x00_iocb_work_fn [qla2xxx] RIP: 0010:__list_add_valid+0x41/0x50 Code: 85 94 00 00 00 48 39 c7 74 0b 48 39 d7 74 06 b8 01 00 00 00 c3 48 89 f2 4c 89 c1 48 89 fe 48 c7 c7 60 83 ad 97 e8 4d bd ce ff <0f> 0b 0f 1f 00 66 2e 0f 1f 84 00 00 00 00 00 48 8b 07 48 8b 57 08 RSP: 0018:ffffaba306f47d68 EFLAGS: 00010046 RAX: 0000000000000058 RBX: ffff9130b5eb8800 RCX: 0000000000000006 RDX: 0000000000000000 RSI: 0000000000000096 RDI: ffff9130b7456a00 RBP: ffff9130c6a70a58 R08: 000000000008d7be R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000001 R12: ffff9130c6a715f0 R13: ffff9130b5eb8824 R14: ffff9130b5eb89f8 R15: ffff9130b5eb89f8 FS: 0000000000000000(0000) GS:ffff9130b7440000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007efcaaef11a0 CR3: 000000005200a002 CR4: 00000000000606e0 Call Trace: qla24xx_async_gnl+0x113/0x3c0 [qla2xxx] ? qla2x00_iocb_work_fn+0x53/0x80 [qla2xxx] ? process_one_work+0x1a7/0x3b0 ? worker_thread+0x30/0x390 ? create_worker+0x1a0/0x1a0 ? kthread+0x112/0x130 Link: https://lore.kernel.org/r/20211026115412.27691-3-njavali@marvell.com Fixes: 726b85487067 ("qla2xxx: Add framework for async fabric discovery") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 2438191d0ad6..762d7aec7fa0 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -987,8 +987,6 @@ static void qla24xx_async_gnl_sp_done(srb_t *sp, int res) sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1], sp->u.iocb_cmd.u.mbx.in_mb[2]); - if (res == QLA_FUNCTION_TIMEOUT) - return; sp->fcport->flags &= ~(FCF_ASYNC_SENT|FCF_ASYNC_ACTIVE); memset(&ea, 0, sizeof(ea)); @@ -1026,8 +1024,8 @@ static void qla24xx_async_gnl_sp_done(srb_t *sp, int res) spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); list_for_each_entry_safe(fcport, tf, &h, gnl_entry) { - list_del_init(&fcport->gnl_entry); spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + list_del_init(&fcport->gnl_entry); fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); ea.fcport = fcport; From 0b7a9fd934a68ebfc1019811b7bdc1742072ad7b Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:02 -0700 Subject: [PATCH 15/46] scsi: qla2xxx: Turn off target reset during issue_lip When user uses issue_lip to do link bounce, driver sends additional target reset to remote device before resetting the link. The target reset would affect other paths with active I/Os. This patch will remove the unnecessary target reset. Link: https://lore.kernel.org/r/20211026115412.27691-4-njavali@marvell.com Fixes: 5854771e314e ("[SCSI] qla2xxx: Add ISPFX00 specific bus reset routine") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_gbl.h | 2 -- drivers/scsi/qla2xxx/qla_mr.c | 23 ----------------------- drivers/scsi/qla2xxx/qla_os.c | 27 ++------------------------- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 3c4fa8bac88d..4fd59e29cbd3 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -171,7 +171,6 @@ extern int ql2xasynctmfenable; extern int ql2xgffidenable; extern int ql2xenabledif; extern int ql2xenablehba_err_chk; -extern int ql2xtargetreset; extern int ql2xdontresethba; extern uint64_t ql2xmaxlun; extern int ql2xmdcapmask; @@ -818,7 +817,6 @@ extern void qlafx00_abort_iocb(srb_t *, struct abort_iocb_entry_fx00 *); extern void qlafx00_fxdisc_iocb(srb_t *, struct fxdisc_entry_fx00 *); extern void qlafx00_timer_routine(scsi_qla_host_t *); extern int qlafx00_rescan_isp(scsi_qla_host_t *); -extern int qlafx00_loop_reset(scsi_qla_host_t *vha); /* qla82xx related functions */ diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index 6e920da64863..350b0c4346fb 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -738,29 +738,6 @@ qlafx00_lun_reset(fc_port_t *fcport, uint64_t l, int tag) return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); } -int -qlafx00_loop_reset(scsi_qla_host_t *vha) -{ - int ret; - struct fc_port *fcport; - struct qla_hw_data *ha = vha->hw; - - if (ql2xtargetreset) { - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->port_type != FCT_TARGET) - continue; - - ret = ha->isp_ops->target_reset(fcport, 0, 0); - if (ret != QLA_SUCCESS) { - ql_dbg(ql_dbg_taskm, vha, 0x803d, - "Bus Reset failed: Reset=%d " - "d_id=%x.\n", ret, fcport->d_id.b24); - } - } - } - return QLA_SUCCESS; -} - int qlafx00_iospace_config(struct qla_hw_data *ha) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 04fd98f60c91..452c74b83f4c 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -202,12 +202,6 @@ MODULE_PARM_DESC(ql2xdbwr, " 0 -- Regular doorbell.\n" " 1 -- CAMRAM doorbell (faster).\n"); -int ql2xtargetreset = 1; -module_param(ql2xtargetreset, int, S_IRUGO); -MODULE_PARM_DESC(ql2xtargetreset, - "Enable target reset." - "Default is 1 - use hw defaults."); - int ql2xgffidenable; module_param(ql2xgffidenable, int, S_IRUGO); MODULE_PARM_DESC(ql2xgffidenable, @@ -1695,27 +1689,10 @@ int qla2x00_loop_reset(scsi_qla_host_t *vha) { int ret; - struct fc_port *fcport; struct qla_hw_data *ha = vha->hw; - if (IS_QLAFX00(ha)) { - return qlafx00_loop_reset(vha); - } - - if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) { - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->port_type != FCT_TARGET) - continue; - - ret = ha->isp_ops->target_reset(fcport, 0, 0); - if (ret != QLA_SUCCESS) { - ql_dbg(ql_dbg_taskm, vha, 0x802c, - "Bus Reset failed: Reset=%d " - "d_id=%x.\n", ret, fcport->d_id.b24); - } - } - } - + if (IS_QLAFX00(ha)) + return QLA_SUCCESS; if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) { atomic_set(&vha->loop_state, LOOP_DOWN); From 8e6d5df3cb32dddf558a52414d29febecb660396 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:03 -0700 Subject: [PATCH 16/46] scsi: qla2xxx: edif: Fix app start fail On app start, all sessions need to be reset to see if secure connection can be made. Fix the broken check which prevents that process. Link: https://lore.kernel.org/r/20211026115412.27691-5-njavali@marvell.com Fixes: 4de067e5df12 ("scsi: qla2xxx: edif: Add N2N support for EDIF") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index ad746c62f0d4..615596becb7a 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -529,7 +529,8 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) struct app_start_reply appreply; struct fc_port *fcport, *tf; - ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app start\n", __func__); + ql_log(ql_log_info, vha, 0x1313, + "EDIF application registration with driver, FC device connections will be re-established.\n"); sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, &appstart, @@ -554,37 +555,36 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) qla2xxx_wake_dpc(vha); } else { list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { + ql_dbg(ql_dbg_edif, vha, 0x2058, + "FCSP - nn %8phN pn %8phN portid=%06x.\n", + fcport->node_name, fcport->port_name, + fcport->d_id.b24); ql_dbg(ql_dbg_edif, vha, 0xf084, - "%s: sess %p %8phC lid %#04x s_id %06x logout %d\n", - __func__, fcport, fcport->port_name, - fcport->loop_id, fcport->d_id.b24, - fcport->logout_on_delete); - - ql_dbg(ql_dbg_edif, vha, 0xf084, - "keep %d els_logo %d disc state %d auth state %d stop state %d\n", - fcport->keep_nport_handle, - fcport->send_els_logo, fcport->disc_state, - fcport->edif.auth_state, fcport->edif.app_stop); + "%s: se_sess %p / sess %p from port %8phC " + "loop_id %#04x s_id %06x logout %d " + "keep %d els_logo %d disc state %d auth state %d" + "stop state %d\n", + __func__, fcport->se_sess, fcport, + fcport->port_name, fcport->loop_id, + fcport->d_id.b24, fcport->logout_on_delete, + fcport->keep_nport_handle, fcport->send_els_logo, + fcport->disc_state, fcport->edif.auth_state, + fcport->edif.app_stop); if (atomic_read(&vha->loop_state) == LOOP_DOWN) break; - if (!(fcport->flags & FCF_FCSP_DEVICE)) - continue; fcport->edif.app_started = 1; - if (fcport->edif.app_stop || - (fcport->disc_state != DSC_LOGIN_COMPLETE && - fcport->disc_state != DSC_LOGIN_PEND && - fcport->disc_state != DSC_DELETED)) { - /* no activity */ - fcport->edif.app_stop = 0; + fcport->login_retry = vha->hw->login_retry_count; - ql_dbg(ql_dbg_edif, vha, 0x911e, - "%s wwpn %8phC calling qla_edif_reset_auth_wait\n", - __func__, fcport->port_name); - fcport->edif.app_sess_online = 1; - qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0); - } + /* no activity */ + fcport->edif.app_stop = 0; + + ql_dbg(ql_dbg_edif, vha, 0x911e, + "%s wwpn %8phC calling qla_edif_reset_auth_wait\n", + __func__, fcport->port_name); + fcport->edif.app_sess_online = 1; + qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0); qla_edif_sa_ctl_init(vha, fcport); } } From b492d6a4880fddce098472dec5086d37802c68d3 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:04 -0700 Subject: [PATCH 17/46] scsi: qla2xxx: edif: Fix app start delay Current driver does unnecessary pause for each session to get to certain state before allowing the app start call to return. In larger environment, this introduces a long delay. Originally the delay was meant to synchronize app and driver. However, the with current implementation the two sides use various events to synchronize their state. The same is applied to the authentication failure call. Link: https://lore.kernel.org/r/20211026115412.27691-6-njavali@marvell.com Fixes: 4de067e5df12 ("scsi: qla2xxx: edif: Add N2N support for EDIF") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 64 ++------------------------------- 1 file changed, 3 insertions(+), 61 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index 615596becb7a..cf62f26ce27d 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -290,63 +290,6 @@ qla_edif_app_check(scsi_qla_host_t *vha, struct app_id appid) return false; } -static void qla_edif_reset_auth_wait(struct fc_port *fcport, int state, - int waitonly) -{ - int cnt, max_cnt = 200; - bool traced = false; - - fcport->keep_nport_handle = 1; - - if (!waitonly) { - qla2x00_set_fcport_disc_state(fcport, state); - qlt_schedule_sess_for_deletion(fcport); - } else { - qla2x00_set_fcport_disc_state(fcport, state); - } - - ql_dbg(ql_dbg_edif, fcport->vha, 0xf086, - "%s: waiting for session, max_cnt=%u\n", - __func__, max_cnt); - - cnt = 0; - - if (waitonly) { - /* Marker wait min 10 msecs. */ - msleep(50); - cnt += 50; - } - while (1) { - if (!traced) { - ql_dbg(ql_dbg_edif, fcport->vha, 0xf086, - "%s: session sleep.\n", - __func__); - traced = true; - } - msleep(20); - cnt++; - if (waitonly && (fcport->disc_state == state || - fcport->disc_state == DSC_LOGIN_COMPLETE)) - break; - if (fcport->disc_state == DSC_LOGIN_AUTH_PEND) - break; - if (cnt > max_cnt) - break; - } - - if (!waitonly) { - ql_dbg(ql_dbg_edif, fcport->vha, 0xf086, - "%s: waited for session - %8phC, loopid=%x portid=%06x fcport=%p state=%u, cnt=%u\n", - __func__, fcport->port_name, fcport->loop_id, - fcport->d_id.b24, fcport, fcport->disc_state, cnt); - } else { - ql_dbg(ql_dbg_edif, fcport->vha, 0xf086, - "%s: waited ONLY for session - %8phC, loopid=%x portid=%06x fcport=%p state=%u, cnt=%u\n", - __func__, fcport->port_name, fcport->loop_id, - fcport->d_id.b24, fcport, fcport->disc_state, cnt); - } -} - static void qla_edif_free_sa_ctl(fc_port_t *fcport, struct edif_sa_ctl *sa_ctl, int index) @@ -583,8 +526,8 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) ql_dbg(ql_dbg_edif, vha, 0x911e, "%s wwpn %8phC calling qla_edif_reset_auth_wait\n", __func__, fcport->port_name); - fcport->edif.app_sess_online = 1; - qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0); + fcport->edif.app_sess_online = 0; + qlt_schedule_sess_for_deletion(fcport); qla_edif_sa_ctl_init(vha, fcport); } } @@ -800,7 +743,6 @@ qla_edif_app_authok(scsi_qla_host_t *vha, struct bsg_job *bsg_job) ql_dbg(ql_dbg_edif, vha, 0x911e, "%s AUTH complete - RESUME with prli for wwpn %8phC\n", __func__, fcport->port_name); - qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 1); qla24xx_post_prli_work(vha, fcport); } @@ -873,7 +815,7 @@ qla_edif_app_authfail(scsi_qla_host_t *vha, struct bsg_job *bsg_job) if (qla_ini_mode_enabled(fcport->vha)) { fcport->send_els_logo = 1; - qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0); + qlt_schedule_sess_for_deletion(fcport); } } From b1af26c245545a289b331c7b71996ecd88321540 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:05 -0700 Subject: [PATCH 18/46] scsi: qla2xxx: edif: Flush stale events and msgs on session down On session down, driver will flush all stale messages and doorbell events. This prevents authentication application from having to process stale data. Link: https://lore.kernel.org/r/20211026115412.27691-7-njavali@marvell.com Fixes: 4de067e5df12 ("scsi: qla2xxx: edif: Add N2N support for EDIF") Reviewed-by: Himanshu Madhani Co-developed-by: Karunakara Merugu Signed-off-by: Karunakara Merugu Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 96 ++++++++++++++++++++++++++++++- drivers/scsi/qla2xxx/qla_gbl.h | 2 + drivers/scsi/qla2xxx/qla_target.c | 1 + 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index cf62f26ce27d..3931bae3222b 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -1593,6 +1593,40 @@ qla_enode_stop(scsi_qla_host_t *vha) spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); } +static void qla_enode_clear(scsi_qla_host_t *vha, port_id_t portid) +{ + unsigned long flags; + struct enode *e, *tmp; + struct purexevent *purex; + LIST_HEAD(enode_list); + + if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { + ql_dbg(ql_dbg_edif, vha, 0x09102, + "%s enode not active\n", __func__); + return; + } + spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); + list_for_each_entry_safe(e, tmp, &vha->pur_cinfo.head, list) { + purex = &e->u.purexinfo; + if (purex->pur_info.pur_sid.b24 == portid.b24) { + ql_dbg(ql_dbg_edif, vha, 0x911d, + "%s free ELS sid=%06x. xchg %x, nb=%xh\n", + __func__, portid.b24, + purex->pur_info.pur_rx_xchg_address, + purex->pur_info.pur_bytes_rcvd); + + list_del_init(&e->list); + list_add_tail(&e->list, &enode_list); + } + } + spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); + + list_for_each_entry_safe(e, tmp, &enode_list, list) { + list_del_init(&e->list); + qla_enode_free(vha, e); + } +} + /* * allocate enode struct and populate buffer * returns: enode pointer with buffers @@ -1792,6 +1826,57 @@ qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node) node->ntype = N_UNDEF; } +static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid) +{ + unsigned long flags; + struct edb_node *e, *tmp; + port_id_t sid; + LIST_HEAD(edb_list); + + if (vha->e_dbell.db_flags != EDB_ACTIVE) { + /* doorbell list not enabled */ + ql_dbg(ql_dbg_edif, vha, 0x09102, + "%s doorbell not enabled\n", __func__); + return; + } + + /* grab lock so list doesn't move */ + spin_lock_irqsave(&vha->e_dbell.db_lock, flags); + list_for_each_entry_safe(e, tmp, &vha->e_dbell.head, list) { + switch (e->ntype) { + case VND_CMD_AUTH_STATE_NEEDED: + case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: + sid = e->u.plogi_did; + break; + case VND_CMD_AUTH_STATE_ELS_RCVD: + sid = e->u.els_sid; + break; + case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: + /* app wants to see this */ + continue; + default: + ql_log(ql_log_warn, vha, 0x09102, + "%s unknown node type: %x\n", __func__, e->ntype); + sid.b24 = 0; + break; + } + if (sid.b24 == portid.b24) { + ql_dbg(ql_dbg_edif, vha, 0x910f, + "%s free doorbell event : node type = %x %p\n", + __func__, e->ntype, e); + list_del_init(&e->list); + list_add_tail(&e->list, &edb_list); + } + } + spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); + + list_for_each_entry_safe(e, tmp, &edb_list, list) { + qla_edb_node_free(vha, e); + list_del_init(&e->list); + kfree(e); + } +} + /* function called when app is stopping */ void @@ -2378,7 +2463,7 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) ql_dbg(ql_dbg_edif, host, 0x0910c, "%s COMPLETE purex->pur_info.pur_bytes_rcvd =%xh s:%06x -> d:%06x xchg=%xh\n", __func__, purex->pur_info.pur_bytes_rcvd, purex->pur_info.pur_sid.b24, - purex->pur_info.pur_did.b24, p->rx_xchg_addr); + purex->pur_info.pur_did.b24, purex->pur_info.pur_rx_xchg_address); qla_edb_eventcreate(host, VND_CMD_AUTH_STATE_ELS_RCVD, sid, 0, NULL); } @@ -3401,3 +3486,12 @@ void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess) qla2x00_post_aen_work(vha, FCH_EVT_PORT_OFFLINE, sess->d_id.b24); } } + +void qla_edif_clear_appdata(struct scsi_qla_host *vha, struct fc_port *fcport) +{ + if (!(fcport->flags & FCF_FCSP_DEVICE)) + return; + + qla_edb_clear(vha, fcport->d_id); + qla_enode_clear(vha, fcport->d_id); +} diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 4fd59e29cbd3..8d8503a28479 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -142,6 +142,8 @@ void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport, void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport); int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsgjob); void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess); +void qla_edif_clear_appdata(struct scsi_qla_host *vha, + struct fc_port *fcport); const char *sc_to_str(uint16_t cmd); /* diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index b3478ed9b12e..edc34e69d75b 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1003,6 +1003,7 @@ void qlt_free_session_done(struct work_struct *work) "%s bypassing release_all_sadb\n", __func__); } + qla_edif_clear_appdata(vha, sess); qla_edif_sess_down(vha, sess); } qla2x00_mark_device_lost(vha, sess, 0); From 8062b742d3bd336ca10ab5a1db1629d33700f9c6 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:06 -0700 Subject: [PATCH 19/46] scsi: qla2xxx: edif: Replace list_for_each_safe with list_for_each_entry_safe This patch is per review comment by Hannes Reinecke from previous submission to replace list_for_each_safe with list_for_each_entry_safe. Link: https://lore.kernel.org/r/20211026115412.27691-8-njavali@marvell.com Reviewed-by: Hannes Reinecke Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 39 ++++++++------------------------- drivers/scsi/qla2xxx/qla_edif.h | 1 - drivers/scsi/qla2xxx/qla_os.c | 8 +++---- 3 files changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index 3931bae3222b..8c855d66b9e3 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -1671,41 +1671,25 @@ static struct enode * qla_enode_find(scsi_qla_host_t *vha, uint32_t ntype, uint32_t p1, uint32_t p2) { struct enode *node_rtn = NULL; - struct enode *list_node = NULL; + struct enode *list_node, *q; unsigned long flags; - struct list_head *pos, *q; uint32_t sid; - uint32_t rw_flag; struct purexevent *purex; /* secure the list from moving under us */ spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); - list_for_each_safe(pos, q, &vha->pur_cinfo.head) { - list_node = list_entry(pos, struct enode, list); + list_for_each_entry_safe(list_node, q, &vha->pur_cinfo.head, list) { /* node type determines what p1 and p2 are */ purex = &list_node->u.purexinfo; sid = p1; - rw_flag = p2; if (purex->pur_info.pur_sid.b24 == sid) { - if (purex->pur_info.pur_pend == 1 && - rw_flag == PUR_GET) { - /* - * if the receive is in progress - * and its a read/get then can't - * transfer yet - */ - ql_dbg(ql_dbg_edif, vha, 0x9106, - "%s purex xfer in progress for sid=%x\n", - __func__, sid); - } else { - /* found it and its complete */ - node_rtn = list_node; - list_del(pos); - break; - } + /* found it and its complete */ + node_rtn = list_node; + list_del(&list_node->list); + break; } } @@ -2414,7 +2398,6 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) purex = &ptr->u.purexinfo; purex->pur_info.pur_sid = a.did; - purex->pur_info.pur_pend = 0; purex->pur_info.pur_bytes_rcvd = totlen; purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr); purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle); @@ -3166,18 +3149,14 @@ static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport, /* release any sadb entries -- only done at teardown */ void qla_edif_sadb_release(struct qla_hw_data *ha) { - struct list_head *pos; - struct list_head *tmp; - struct edif_sa_index_entry *entry; + struct edif_sa_index_entry *entry, *tmp; - list_for_each_safe(pos, tmp, &ha->sadb_rx_index_list) { - entry = list_entry(pos, struct edif_sa_index_entry, next); + list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) { list_del(&entry->next); kfree(entry); } - list_for_each_safe(pos, tmp, &ha->sadb_tx_index_list) { - entry = list_entry(pos, struct edif_sa_index_entry, next); + list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) { list_del(&entry->next); kfree(entry); } diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h index 9e8f28d0caa1..cd54c1dfe3cb 100644 --- a/drivers/scsi/qla2xxx/qla_edif.h +++ b/drivers/scsi/qla2xxx/qla_edif.h @@ -102,7 +102,6 @@ struct dinfo { }; struct pur_ninfo { - unsigned int pur_pend:1; port_id_t pur_sid; port_id_t pur_did; uint8_t vp_idx; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 452c74b83f4c..33427a89e91f 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3885,13 +3885,13 @@ qla2x00_remove_one(struct pci_dev *pdev) static inline void qla24xx_free_purex_list(struct purex_list *list) { - struct list_head *item, *next; + struct purex_item *item, *next; ulong flags; spin_lock_irqsave(&list->lock, flags); - list_for_each_safe(item, next, &list->head) { - list_del(item); - kfree(list_entry(item, struct purex_item, list)); + list_for_each_entry_safe(item, next, &list->head, list) { + list_del(&item->list); + kfree(item); } spin_unlock_irqrestore(&list->lock, flags); } From 6c9998ce4be2adddfb6753a73ade9df1e912550c Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:07 -0700 Subject: [PATCH 20/46] scsi: qla2xxx: edif: Tweak trace message Modify trace messages for additional debugability. Link: https://lore.kernel.org/r/20211026115412.27691-9-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 4 ++-- drivers/scsi/qla2xxx/qla_edif.c | 6 +++++- drivers/scsi/qla2xxx/qla_init.c | 19 +++++++++++-------- drivers/scsi/qla2xxx/qla_isr.c | 4 ++++ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 8924eeb9367d..9ebf4a234d9a 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -639,9 +639,9 @@ struct qla_els_pt_arg { u8 els_opcode; u8 vp_idx; __le16 nport_handle; - u16 control_flags; + u16 control_flags, ox_id; __le32 rx_xchg_address; - port_id_t did; + port_id_t did, sid; u32 tx_len, tx_byte_count, rx_len, rx_byte_count; dma_addr_t tx_addr, rx_addr; diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index 8c855d66b9e3..af739d2d1607 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -1762,7 +1762,8 @@ qla_els_reject_iocb(scsi_qla_host_t *vha, struct qla_qpair *qp, qla_els_pt_iocb(vha, els_iocb, a); ql_dbg(ql_dbg_edif, vha, 0x0183, - "Sending ELS reject...\n"); + "Sending ELS reject ox_id %04x s:%06x -> d:%06x\n", + a->ox_id, a->sid.b24, a->did.b24); ql_dump_buffer(ql_dbg_edif + ql_dbg_verbose, vha, 0x0185, vha->hw->elsrej.c, sizeof(*vha->hw->elsrej.c)); /* flush iocb to mem before notifying hw doorbell */ @@ -2357,6 +2358,7 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) a.tx_addr = vha->hw->elsrej.cdma; a.vp_idx = vha->vp_idx; a.control_flags = EPD_ELS_RJT; + a.ox_id = le16_to_cpu(p->ox_id); sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16); @@ -2406,6 +2408,8 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) purex->pur_info.pur_did.b.al_pa = p->d_id[0]; purex->pur_info.vp_idx = p->vp_idx; + a.sid = purex->pur_info.pur_did; + rc = __qla_copy_purex_to_buffer(vha, pkt, rsp, purex->msgp, purex->msgp_len); if (rc) { diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 762d7aec7fa0..2f42abbea44d 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -333,9 +333,6 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, vha->e_dbell.db_flags & EDB_ACTIVE) { lio->u.logio.flags |= (SRB_LOGIN_FCSP | SRB_LOGIN_SKIP_PRLI); - ql_dbg(ql_dbg_disc, vha, 0x2072, - "Async-login: w/ FCSP %8phC hdl=%x, loopid=%x portid=%06x\n", - fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24); } else { lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; } @@ -344,12 +341,14 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, if (NVME_TARGET(vha->hw, fcport)) lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI; - ql_dbg(ql_dbg_disc, vha, 0x2072, - "Async-login - %8phC hdl=%x, loopid=%x portid=%06x retries=%d.\n", - fcport->port_name, sp->handle, fcport->loop_id, - fcport->d_id.b24, fcport->login_retry); - rval = qla2x00_start_sp(sp); + + ql_dbg(ql_dbg_disc, vha, 0x2072, + "Async-login - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n", + fcport->port_name, sp->handle, fcport->loop_id, + fcport->d_id.b24, fcport->login_retry, + lio->u.logio.flags & SRB_LOGIN_FCSP ? "FCSP" : ""); + if (rval != QLA_SUCCESS) { fcport->flags |= FCF_LOGIN_NEEDED; set_bit(RELOGIN_NEEDED, &vha->dpc_flags); @@ -5867,6 +5866,10 @@ void qla_register_fcport_fn(struct work_struct *work) qla2x00_update_fcport(fcport->vha, fcport); + ql_dbg(ql_dbg_disc, fcport->vha, 0x911e, + "%s rscn gen %d/%d next DS %d\n", __func__, + rscn_gen, fcport->rscn_gen, fcport->next_disc_state); + if (rscn_gen != fcport->rscn_gen) { /* RSCN(s) came in while registration */ switch (fcport->next_disc_state) { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index b26f2699adb2..aaf6504570fd 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2233,6 +2233,10 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, } } else if (comp_status == CS_PORT_LOGGED_OUT) { + ql_dbg(ql_dbg_disc, vha, 0x911e, + "%s %d schedule session deletion\n", + __func__, __LINE__); + els->u.els_plogi.len = 0; res = DID_IMM_RETRY << 16; qlt_schedule_sess_for_deletion(sp->fcport); From 91f6f5fbe87ba834133fcc61d34881cb8ec9e518 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:08 -0700 Subject: [PATCH 21/46] scsi: qla2xxx: edif: Reduce connection thrash On ipsec start by remote port, target port may use RSCN to trigger initiator to relogin. If driver is already in the process of a relogin, then ignore the RSCN and allow the current relogin to continue. This reduces thrashing of the connection. Link: https://lore.kernel.org/r/20211026115412.27691-10-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_attr.c | 7 ++++++- drivers/scsi/qla2xxx/qla_edif.h | 4 ++++ drivers/scsi/qla2xxx/qla_init.c | 24 ++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 30f9545d2285..032efb294ee5 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2762,7 +2762,12 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) if (fcport->loop_id != FC_NO_LOOP_ID) fcport->logout_on_delete = 1; - qlt_schedule_sess_for_deletion(fcport); + if (!EDIF_NEGOTIATION_PENDING(fcport)) { + ql_dbg(ql_dbg_disc, fcport->vha, 0x911e, + "%s %d schedule session deletion\n", __func__, + __LINE__); + qlt_schedule_sess_for_deletion(fcport); + } } else { qla2x00_port_logout(fcport->vha, fcport); } diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h index cd54c1dfe3cb..920b1eace40f 100644 --- a/drivers/scsi/qla2xxx/qla_edif.h +++ b/drivers/scsi/qla2xxx/qla_edif.h @@ -132,4 +132,8 @@ struct enode { _s->disc_state == DSC_DELETED || \ !_s->edif.app_sess_online)) +#define EDIF_NEGOTIATION_PENDING(_fcport) \ + ((_fcport->vha.e_dbell.db_flags & EDB_ACTIVE) && \ + (_fcport->disc_state == DSC_LOGIN_AUTH_PEND)) + #endif /* __QLA_EDIF_H */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 2f42abbea44d..ba589dd1d0d7 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1793,8 +1793,28 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) fcport->d_id.b24, fcport->port_name); return; } - fcport->scan_needed = 1; - fcport->rscn_gen++; + + if (vha->hw->flags.edif_enabled && vha->e_dbell.db_flags & EDB_ACTIVE) { + /* + * On ipsec start by remote port, Target port + * may use RSCN to trigger initiator to + * relogin. If driver is already in the + * process of a relogin, then ignore the RSCN + * and allow the current relogin to continue. + * This reduces thrashing of the connection. + */ + if (atomic_read(&fcport->state) == FCS_ONLINE) { + /* + * If state = online, then set scan_needed=1 to do relogin. + * Otherwise we're already in the middle of a relogin + */ + fcport->scan_needed = 1; + fcport->rscn_gen++; + } + } else { + fcport->scan_needed = 1; + fcport->rscn_gen++; + } } break; case RSCN_AREA_ADDR: From 0f6d600a26e89d31d8381b324fc970f72579a126 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:09 -0700 Subject: [PATCH 22/46] scsi: qla2xxx: edif: Increase ELS payload Currently, firmware limits ELS payload to FC frame size/2112. This patch adjusts memory buffer size to be able to handle max ELS payload. Link: https://lore.kernel.org/r/20211026115412.27691-11-njavali@marvell.com Fixes: 84318a9f01ce ("scsi: qla2xxx: edif: Add send, receive, and accept for auth_els") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 2 +- drivers/scsi/qla2xxx/qla_edif.h | 3 ++- drivers/scsi/qla2xxx/qla_edif_bsg.h | 2 +- drivers/scsi/qla2xxx/qla_init.c | 4 ++++ drivers/scsi/qla2xxx/qla_os.c | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index af739d2d1607..e6ff24badc4c 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -2370,7 +2370,7 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) return; } - if (totlen > MAX_PAYLOAD) { + if (totlen > ELS_MAX_PAYLOAD) { ql_dbg(ql_dbg_edif, vha, 0x0910d, "%s WARNING: verbose ELS frame received (totlen=%x)\n", __func__, totlen); diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h index 920b1eace40f..2517005fb08c 100644 --- a/drivers/scsi/qla2xxx/qla_edif.h +++ b/drivers/scsi/qla2xxx/qla_edif.h @@ -93,7 +93,6 @@ struct sa_update_28xx { }; #define NUM_ENTRIES 256 -#define MAX_PAYLOAD 1024 #define PUR_GET 1 struct dinfo { @@ -127,6 +126,8 @@ struct enode { } u; }; +#define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES)) + #define EDIF_SESSION_DOWN(_s) \ (qla_ini_mode_enabled(_s->vha) && (_s->disc_state == DSC_DELETE_PEND || \ _s->disc_state == DSC_DELETED || \ diff --git a/drivers/scsi/qla2xxx/qla_edif_bsg.h b/drivers/scsi/qla2xxx/qla_edif_bsg.h index 58b718d35d19..53026d82ebff 100644 --- a/drivers/scsi/qla2xxx/qla_edif_bsg.h +++ b/drivers/scsi/qla2xxx/qla_edif_bsg.h @@ -8,7 +8,7 @@ #define __QLA_EDIF_BSG_H /* BSG Vendor specific commands */ -#define ELS_MAX_PAYLOAD 1024 +#define ELS_MAX_PAYLOAD 2112 #ifndef WWN_SIZE #define WWN_SIZE 8 #endif diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index ba589dd1d0d7..3a3370f296c9 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4486,6 +4486,10 @@ qla2x00_init_rings(scsi_qla_host_t *vha) (ha->flags.fawwpn_enabled) ? "enabled" : "disabled"); } + /* ELS pass through payload is limit by frame size. */ + if (ha->flags.edif_enabled) + mid_init_cb->init_cb.frame_payload_size = cpu_to_le16(ELS_MAX_PAYLOAD); + rval = qla2x00_init_firmware(vha, ha->init_cb_size); next_check: if (rval) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 33427a89e91f..e8e93111fede 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -4352,7 +4352,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, /* allocate the purex dma pool */ ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev, - MAX_PAYLOAD, 8, 0); + ELS_MAX_PAYLOAD, 8, 0); if (!ha->purex_dma_pool) { ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b, From 36f468bfe98c7de7916ab3391ee5dd6fd2549979 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:10 -0700 Subject: [PATCH 23/46] scsi: qla2xxx: edif: Fix inconsistent check of db_flags db_flags field is a bit field. Replace value check with bit flag check. Link: https://lore.kernel.org/r/20211026115412.27691-12-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 26 +++++++++++++------------- drivers/scsi/qla2xxx/qla_edif.h | 7 +++++-- drivers/scsi/qla2xxx/qla_init.c | 13 ++++++------- drivers/scsi/qla2xxx/qla_iocb.c | 3 +-- drivers/scsi/qla2xxx/qla_target.c | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index e6ff24badc4c..1a16588433c7 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -218,7 +218,7 @@ fc_port_t *fcport) "%s edif not enabled\n", __func__); goto done; } - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { ql_dbg(ql_dbg_edif, vha, 0x09102, "%s doorbell not enabled\n", __func__); goto done; @@ -482,9 +482,9 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app_vid=%x app_start_flags %x\n", __func__, appstart.app_info.app_vid, appstart.app_start_flags); - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { /* mark doorbell as active since an app is now present */ - vha->e_dbell.db_flags = EDB_ACTIVE; + vha->e_dbell.db_flags |= EDB_ACTIVE; } else { ql_dbg(ql_dbg_edif, vha, 0x911e, "%s doorbell already active\n", __func__); @@ -1272,7 +1272,7 @@ qla24xx_sadb_update(struct bsg_job *bsg_job) goto done; } - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { ql_log(ql_log_warn, vha, 0x70a1, "App not started\n"); rval = -EIO; SET_DID_STATUS(bsg_reply->result, DID_ERROR); @@ -1775,7 +1775,7 @@ qla_els_reject_iocb(scsi_qla_host_t *vha, struct qla_qpair *qp, void qla_edb_init(scsi_qla_host_t *vha) { - if (vha->e_dbell.db_flags == EDB_ACTIVE) { + if (DBELL_ACTIVE(vha)) { /* list already init'd - error */ ql_dbg(ql_dbg_edif, vha, 0x09102, "edif db already initialized, cannot reinit\n"); @@ -1818,7 +1818,7 @@ static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid) port_id_t sid; LIST_HEAD(edb_list); - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { /* doorbell list not enabled */ ql_dbg(ql_dbg_edif, vha, 0x09102, "%s doorbell not enabled\n", __func__); @@ -1870,7 +1870,7 @@ qla_edb_stop(scsi_qla_host_t *vha) unsigned long flags; struct edb_node *node, *q; - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { /* doorbell list not enabled */ ql_dbg(ql_dbg_edif, vha, 0x09102, "%s doorbell not enabled\n", __func__); @@ -1921,7 +1921,7 @@ qla_edb_node_add(scsi_qla_host_t *vha, struct edb_node *ptr) { unsigned long flags; - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { /* doorbell list not enabled */ ql_dbg(ql_dbg_edif, vha, 0x09102, "%s doorbell not enabled\n", __func__); @@ -1952,7 +1952,7 @@ qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype, return; } - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { if (fcport) fcport->edif.auth_state = dbtype; /* doorbell list not enabled */ @@ -2047,7 +2047,7 @@ qla_edif_timer(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; if (!vha->vp_idx && N2N_TOPO(ha) && ha->flags.n2n_fw_acc_sec) { - if (vha->e_dbell.db_flags != EDB_ACTIVE && + if (DBELL_INACTIVE(vha) && ha->edif_post_stop_cnt_down) { ha->edif_post_stop_cnt_down--; @@ -2085,7 +2085,7 @@ edif_doorbell_show(struct device *dev, struct device_attribute *attr, sz = 256; /* stop new threads from waiting if we're not init'd */ - if (vha->e_dbell.db_flags != EDB_ACTIVE) { + if (DBELL_INACTIVE(vha)) { ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x09122, "%s error - edif db not enabled\n", __func__); return 0; @@ -2433,7 +2433,7 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) fcport = qla2x00_find_fcport_by_pid(host, &purex->pur_info.pur_sid); - if (host->e_dbell.db_flags != EDB_ACTIVE || + if (DBELL_INACTIVE(vha) || (fcport && EDIF_SESSION_DOWN(fcport))) { ql_dbg(ql_dbg_edif, host, 0x0910c, "%s e_dbell.db_flags =%x %06x\n", __func__, host->e_dbell.db_flags, @@ -3459,7 +3459,7 @@ done: void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess) { - if (sess->edif.app_sess_online && vha->e_dbell.db_flags & EDB_ACTIVE) { + if (sess->edif.app_sess_online && DBELL_ACTIVE(vha)) { ql_dbg(ql_dbg_disc, vha, 0xf09c, "%s: sess %8phN send port_offline event\n", __func__, sess->port_name); diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h index 2517005fb08c..a965ca8e47ce 100644 --- a/drivers/scsi/qla2xxx/qla_edif.h +++ b/drivers/scsi/qla2xxx/qla_edif.h @@ -41,9 +41,12 @@ struct pur_core { }; enum db_flags_t { - EDB_ACTIVE = 0x1, + EDB_ACTIVE = BIT_0, }; +#define DBELL_ACTIVE(_v) (_v->e_dbell.db_flags & EDB_ACTIVE) +#define DBELL_INACTIVE(_v) (!(_v->e_dbell.db_flags & EDB_ACTIVE)) + struct edif_dbell { enum db_flags_t db_flags; spinlock_t db_lock; @@ -134,7 +137,7 @@ struct enode { !_s->edif.app_sess_online)) #define EDIF_NEGOTIATION_PENDING(_fcport) \ - ((_fcport->vha.e_dbell.db_flags & EDB_ACTIVE) && \ + (DBELL_ACTIVE(_fcport->vha) && \ (_fcport->disc_state == DSC_LOGIN_AUTH_PEND)) #endif /* __QLA_EDIF_H */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 3a3370f296c9..070b636802d0 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -330,7 +330,7 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY; } else { if (vha->hw->flags.edif_enabled && - vha->e_dbell.db_flags & EDB_ACTIVE) { + DBELL_ACTIVE(vha)) { lio->u.logio.flags |= (SRB_LOGIN_FCSP | SRB_LOGIN_SKIP_PRLI); } else { @@ -861,7 +861,7 @@ static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, break; case DSC_LS_PLOGI_COMP: if (vha->hw->flags.edif_enabled && - vha->e_dbell.db_flags & EDB_ACTIVE) { + DBELL_ACTIVE(vha)) { /* check to see if App support secure or not */ qla24xx_post_gpdb_work(vha, fcport, 0); break; @@ -1451,7 +1451,7 @@ static int qla_chk_secure_login(scsi_qla_host_t *vha, fc_port_t *fcport, qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE, fcport->d_id.b24); - if (vha->e_dbell.db_flags == EDB_ACTIVE) { + if (DBELL_ACTIVE(vha)) { ql_dbg(ql_dbg_disc, vha, 0x20ef, "%s %d %8phC EDIF: post DB_AUTH: AUTH needed\n", __func__, __LINE__, fcport->port_name); @@ -1794,7 +1794,7 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) return; } - if (vha->hw->flags.edif_enabled && vha->e_dbell.db_flags & EDB_ACTIVE) { + if (vha->hw->flags.edif_enabled && DBELL_ACTIVE(vha)) { /* * On ipsec start by remote port, Target port * may use RSCN to trigger initiator to @@ -4240,7 +4240,7 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) * fw shal not send PRLI after PLOGI Acc */ if (ha->flags.edif_enabled && - vha->e_dbell.db_flags & EDB_ACTIVE) { + DBELL_ACTIVE(vha)) { ha->fw_options[3] |= BIT_15; ha->flags.n2n_fw_acc_sec = 1; } else { @@ -5396,8 +5396,7 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) * use link up to wake up app to get ready for * authentication. */ - if (ha->flags.edif_enabled && - !(vha->e_dbell.db_flags & EDB_ACTIVE)) + if (ha->flags.edif_enabled && DBELL_INACTIVE(vha)) qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate); diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 9d4ad1d2b00a..ed604f2185bf 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -3034,8 +3034,7 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode, elsio->u.els_plogi.els_cmd = els_opcode; elsio->u.els_plogi.els_plogi_pyld->opcode = els_opcode; - if (els_opcode == ELS_DCMD_PLOGI && vha->hw->flags.edif_enabled && - vha->e_dbell.db_flags & EDB_ACTIVE) { + if (els_opcode == ELS_DCMD_PLOGI && DBELL_ACTIVE(vha)) { struct fc_els_flogi *p = ptr; p->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_SEC); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index edc34e69d75b..031233729ff4 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4817,7 +4817,7 @@ static int qlt_handle_login(struct scsi_qla_host *vha, } if (vha->hw->flags.edif_enabled) { - if (!(vha->e_dbell.db_flags & EDB_ACTIVE)) { + if (DBELL_INACTIVE(vha)) { ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d Term INOT due to app not started lid=%d, NportID %06X ", __func__, __LINE__, loop_id, port_id.b24); From 9fd26c633e8ab5a291c0241533efff161bbe5570 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 26 Oct 2021 04:54:11 -0700 Subject: [PATCH 24/46] scsi: qla2xxx: edif: Fix EDIF bsg Various EDIF bsgs did not properly fill out the reply_payload_rcv_len field. This causes app to parse empty data in the return payload. Link: https://lore.kernel.org/r/20211026115412.27691-13-njavali@marvell.com Fixes: 7ebb336e45ef ("scsi: qla2xxx: edif: Add start + stop bsgs") Reviewed-by: Himanshu Madhani Signed-off-by: Quinn Tran Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_edif.c | 49 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index 1a16588433c7..2e37b189cb75 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -544,14 +544,14 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) appreply.edif_enode_active = vha->pur_cinfo.enode_flags; appreply.edif_edb_active = vha->e_dbell.db_flags; - bsg_job->reply_len = sizeof(struct fc_bsg_reply) + - sizeof(struct app_start_reply); + bsg_job->reply_len = sizeof(struct fc_bsg_reply); SET_DID_STATUS(bsg_reply->result, DID_OK); - sg_copy_from_buffer(bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, &appreply, - sizeof(struct app_start_reply)); + bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + &appreply, + sizeof(struct app_start_reply)); ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app start completed with 0x%x\n", @@ -748,9 +748,10 @@ qla_edif_app_authok(scsi_qla_host_t *vha, struct bsg_job *bsg_job) errstate_exit: bsg_job->reply_len = sizeof(struct fc_bsg_reply); - sg_copy_from_buffer(bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, &appplogireply, - sizeof(struct app_plogi_reply)); + bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + &appplogireply, + sizeof(struct app_plogi_reply)); return rval; } @@ -833,7 +834,7 @@ static int qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) { int32_t rval = 0; - int32_t num_cnt; + int32_t pcnt = 0; struct fc_bsg_reply *bsg_reply = bsg_job->reply; struct app_pinfo_req app_req; struct app_pinfo_reply *app_reply; @@ -845,16 +846,14 @@ qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) bsg_job->request_payload.sg_cnt, &app_req, sizeof(struct app_pinfo_req)); - num_cnt = app_req.num_ports; /* num of ports alloc'd by app */ - app_reply = kzalloc((sizeof(struct app_pinfo_reply) + - sizeof(struct app_pinfo) * num_cnt), GFP_KERNEL); + sizeof(struct app_pinfo) * app_req.num_ports), GFP_KERNEL); + if (!app_reply) { SET_DID_STATUS(bsg_reply->result, DID_ERROR); rval = -1; } else { struct fc_port *fcport = NULL, *tf; - uint32_t pcnt = 0; list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { if (!(fcport->flags & FCF_FCSP_DEVICE)) @@ -923,9 +922,11 @@ qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) SET_DID_STATUS(bsg_reply->result, DID_OK); } - sg_copy_from_buffer(bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, app_reply, - sizeof(struct app_pinfo_reply) + sizeof(struct app_pinfo) * num_cnt); + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + app_reply, + sizeof(struct app_pinfo_reply) + sizeof(struct app_pinfo) * pcnt); kfree(app_reply); @@ -942,10 +943,11 @@ qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) { int32_t rval = 0; struct fc_bsg_reply *bsg_reply = bsg_job->reply; - uint32_t ret_size, size; + uint32_t size; struct app_sinfo_req app_req; struct app_stats_reply *app_reply; + uint32_t pcnt = 0; sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, &app_req, @@ -961,18 +963,12 @@ qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) size = sizeof(struct app_stats_reply) + (sizeof(struct app_sinfo) * app_req.num_ports); - if (size > bsg_job->reply_payload.payload_len) - ret_size = bsg_job->reply_payload.payload_len; - else - ret_size = size; - app_reply = kzalloc(size, GFP_KERNEL); if (!app_reply) { SET_DID_STATUS(bsg_reply->result, DID_ERROR); rval = -1; } else { struct fc_port *fcport = NULL, *tf; - uint32_t pcnt = 0; list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { if (fcport->edif.enable) { @@ -996,9 +992,11 @@ qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) SET_DID_STATUS(bsg_reply->result, DID_OK); } + bsg_job->reply_len = sizeof(struct fc_bsg_reply); bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, app_reply, ret_size); + bsg_job->reply_payload.sg_cnt, app_reply, + sizeof(struct app_stats_reply) + (sizeof(struct app_sinfo) * pcnt)); kfree(app_reply); @@ -1072,8 +1070,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[1]); rval = EXT_STATUS_INVALID_PARAM; - bsg_job->reply_len = sizeof(struct fc_bsg_reply); - SET_DID_STATUS(bsg_reply->result, DID_ERROR); + done = false; break; } From feadce93e668840790d8f7e7bf679779355d66f8 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Tue, 26 Oct 2021 04:54:12 -0700 Subject: [PATCH 25/46] scsi: qla2xxx: Update version to 10.02.07.200-k Link: https://lore.kernel.org/r/20211026115412.27691-14-njavali@marvell.com Reviewed-by: Himanshu Madhani Signed-off-by: Nilesh Javali Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 4b117165bf8b..27e440f8a702 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -6,9 +6,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.02.07.100-k" +#define QLA2XXX_VERSION "10.02.07.200-k" #define QLA_DRIVER_MAJOR_VER 10 #define QLA_DRIVER_MINOR_VER 2 #define QLA_DRIVER_PATCH_VER 7 -#define QLA_DRIVER_BETA_VER 100 +#define QLA_DRIVER_BETA_VER 200 From 3a71f0f7a51259b3cb95d79cac1e19dcc5e89ce9 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Tue, 26 Oct 2021 03:42:40 +0200 Subject: [PATCH 26/46] scsi: core: Fix early registration of sysfs attributes for scsi_device v4.17 commit 86b87cde0b55 ("scsi: core: host template attribute groups") introduced explicit sysfs_create_groups() in scsi_sysfs_add_sdev() and sysfs_remove_groups() in __scsi_remove_device(), both for sdev_gendev, based on a new field const struct attribute_group **sdev_groups of struct scsi_host_template. Commit 92c4b58b15c5 ("scsi: core: Register sysfs attributes earlier") removed above explicit (de)registration of scsi_device attribute groups. It also converted all scsi_device attributes and attribute_groups to end up in a new field const struct attribute_group *gendev_attr_groups[6] of struct scsi_device. However, that new field was not used anywhere. Surprisingly, this only caused missing LLDD specific scsi_device sysfs attributes. Whereas, scsi core attributes from scsi_sdev_attr_groups did continue to exist because of scsi_dev_type.groups. We separate scsi core attibutes from LLDD specific attributes. Hence, we keep the initializing assignment scsi_dev_type = { .groups = scsi_sdev_attr_groups, } as this takes care of core attributes. Without the separation, it would cause attribute double registration due to scsi_dev_type.groups and sdev_gendev.groups. Julian suggested to assign the sdev_groups pointer of the scsi_host_template directly to the groups pointer of sdev_gendev. This way we can delete the container scsi_device.gendev_attr_groups and the loop copying each entry from hostt->sdev_groups to sdev->gendev_attr_groups. Alternative approaches ruled out: Assigning gendev_attr_groups to sdev_dev has no visible effect. Assigning sdev->gendev_attr_groups to scsi_dev_type.groups caused scsi_device of all scsi host types to get LLDD specific attributes of the LLDD for which the last sdev alloc happened to occur, as that overwrote scsi_dev_type.groups, e.g. scsi_debug had zfcp-specific scsi_device attributes. Link: https://lore.kernel.org/r/20211026014240.4098365-1-maier@linux.ibm.com Fixes: 92c4b58b15c5 ("scsi: core: Register sysfs attributes earlier") Suggested-by: Julian Wiedmann Reviewed-by: Bart Van Assche Signed-off-by: Steffen Maier Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_sysfs.c | 11 +---------- include/scsi/scsi_device.h | 6 ------ 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c26f0e29e8cd..fa064bf9a55c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1571,7 +1571,6 @@ static struct device_type scsi_dev_type = { void scsi_sysfs_device_initialize(struct scsi_device *sdev) { - int i, j = 0; unsigned long flags; struct Scsi_Host *shost = sdev->host; struct scsi_host_template *hostt = shost->hostt; @@ -1583,15 +1582,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) scsi_enable_async_suspend(&sdev->sdev_gendev); dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%llu", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); - sdev->gendev_attr_groups[j++] = &scsi_sdev_attr_group; - if (hostt->sdev_groups) { - for (i = 0; hostt->sdev_groups[i] && - j < ARRAY_SIZE(sdev->gendev_attr_groups); - i++, j++) { - sdev->gendev_attr_groups[j] = hostt->sdev_groups[i]; - } - } - WARN_ON_ONCE(j >= ARRAY_SIZE(sdev->gendev_attr_groups)); + sdev->sdev_gendev.groups = hostt->sdev_groups; device_initialize(&sdev->sdev_dev); sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b1e9b3bd3a60..b97e142a7ca9 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -225,12 +225,6 @@ struct scsi_device { struct device sdev_gendev, sdev_dev; - /* - * The array size 6 provides space for one attribute group for the - * SCSI core, four attribute groups defined by SCSI LLDs and one - * terminating NULL pointer. - */ - const struct attribute_group *gendev_attr_groups[6]; struct execute_work ew; /* used to get process context on put */ struct work_struct requeue_work; From a22bcfdbf10b4182e94ae98a641efa676170feaf Mon Sep 17 00:00:00 2001 From: jongmin jeong Date: Mon, 18 Oct 2021 21:42:02 +0900 Subject: [PATCH 27/46] scsi: ufs: Add quirk to handle broken UIC command Samsung ExynosAuto v9 SoC has two types of host controller interface to support the virtualization of UFS Device. One is the physical host (PH) that is the same as conventional UFSHCI, and the other is the virtual host (VH) that supports data transfer function only. In this configuration the virtual host does not support UIC commands. Add a quirk to return 0 when the UIC command send function is called. Link: https://lore.kernel.org/r/20211018124216.153072-2-chanho61.park@samsung.com Cc: Alim Akhtar Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: Bart Van Assche Reviewed-by: Alim Akhtar Signed-off-by: jongmin jeong Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 3 +++ drivers/scsi/ufs/ufshcd.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 04cb67995750..dc7ce9eef0c7 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2330,6 +2330,9 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) int ret; unsigned long flags; + if (hba->quirks & UFSHCD_QUIRK_BROKEN_UIC_CMD) + return 0; + ufshcd_hold(hba, false); mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 3f5dc6732fe1..e67181a013e3 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -589,6 +589,12 @@ enum ufshcd_quirks { * This quirk allows only sg entries aligned with page size. */ UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE = 1 << 14, + + /* + * This quirk needs to be enabled if the host controller does not + * support UIC command + */ + UFSHCD_QUIRK_BROKEN_UIC_CMD = 1 << 15, }; enum ufshcd_caps { From 10fb4f87438d99b2ef06aea38ca2f0454725e8b5 Mon Sep 17 00:00:00 2001 From: jongmin jeong Date: Mon, 18 Oct 2021 21:42:03 +0900 Subject: [PATCH 28/46] scsi: ufs: Add quirk to enable host controller without PH configuration Samsung ExynosAuto v9 SoC virtual hosts do not support device management. Add a quirk to skip the physical host interface configuration part that cannot be performed in the virtual host. Link: https://lore.kernel.org/r/20211018124216.153072-3-chanho61.park@samsung.com Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: Bart Van Assche Suggested-by: Alim Akhtar Reviewed-by: Alim Akhtar Signed-off-by: jongmin jeong Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 3 +++ drivers/scsi/ufs/ufshcd.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index dc7ce9eef0c7..f5ba8f953b87 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -8031,6 +8031,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) if (ret) goto out; + if (hba->quirks & UFSHCD_QUIRK_SKIP_PH_CONFIGURATION) + goto out; + /* Debug counters initialization */ ufshcd_clear_dbg_ufs_stats(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index e67181a013e3..4ceb3c7e65b4 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -595,6 +595,12 @@ enum ufshcd_quirks { * support UIC command */ UFSHCD_QUIRK_BROKEN_UIC_CMD = 1 << 15, + + /* + * This quirk needs to be enabled if the host controller cannot + * support physical host configuration. + */ + UFSHCD_QUIRK_SKIP_PH_CONFIGURATION = 1 << 16, }; enum ufshcd_caps { From e387d448e4899f4bbf7c8151472a2fed72063d82 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:04 +0900 Subject: [PATCH 29/46] scsi: ufs: ufs-exynos: Change pclk available max value To support 167MHz PCLK, we need to adjust the maximum value. Link: https://lore.kernel.org/r/20211018124216.153072-4-chanho61.park@samsung.com Reviewed-by: Alim Akhtar Reviewed-by: Inki Dae Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index dadf4fd10dd8..0a31f77a5f48 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -99,7 +99,7 @@ struct exynos_ufs; #define PA_HIBERN8TIME_VAL 0x20 #define PCLK_AVAIL_MIN 70000000 -#define PCLK_AVAIL_MAX 133000000 +#define PCLK_AVAIL_MAX 167000000 struct exynos_ufs_uic_attr { /* TX Attributes */ From 51cc3bb54286639e364061c36998f43d00fdd2b4 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:05 +0900 Subject: [PATCH 30/46] scsi: ufs: ufs-exynos: Simplify drv_data retrieval The compatible field of exynos_ufs_drv_data is not necessary because of_device_id already has it. Thus, we don't need it anymore and we can get drv_data by device_get_match_data. Link: https://lore.kernel.org/r/20211018124216.153072-5-chanho61.park@samsung.com Reviewed-by: Alim Akhtar Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 10 +--------- drivers/scsi/ufs/ufs-exynos.h | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index b2ec9e20b14d..853c83fd8242 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -893,17 +893,10 @@ static int exynos_ufs_post_link(struct ufs_hba *hba) static int exynos_ufs_parse_dt(struct device *dev, struct exynos_ufs *ufs) { struct device_node *np = dev->of_node; - struct exynos_ufs_drv_data *drv_data = &exynos_ufs_drvs; struct exynos_ufs_uic_attr *attr; int ret = 0; - while (drv_data->compatible) { - if (of_device_is_compatible(np, drv_data->compatible)) { - ufs->drv_data = drv_data; - break; - } - drv_data++; - } + ufs->drv_data = device_get_match_data(dev); if (ufs->drv_data && ufs->drv_data->uic_attr) { attr = ufs->drv_data->uic_attr; @@ -1262,7 +1255,6 @@ static struct exynos_ufs_uic_attr exynos7_uic_attr = { }; static struct exynos_ufs_drv_data exynos_ufs_drvs = { - .compatible = "samsung,exynos7-ufs", .uic_attr = &exynos7_uic_attr, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR | diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index 0a31f77a5f48..2e72aabaa673 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -142,7 +142,6 @@ struct exynos_ufs_uic_attr { }; struct exynos_ufs_drv_data { - char *compatible; struct exynos_ufs_uic_attr *uic_attr; unsigned int quirks; unsigned int opts; @@ -191,7 +190,7 @@ struct exynos_ufs { struct ufs_pa_layer_attr dev_req_params; struct ufs_phy_time_cfg t_cfg; ktime_t entry_hibern8_t; - struct exynos_ufs_drv_data *drv_data; + const struct exynos_ufs_drv_data *drv_data; u32 opts; #define EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL BIT(0) From e1f3e22e93e620a7ba1b2ab74c9e8ed618a87de7 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:06 +0900 Subject: [PATCH 31/46] scsi: ufs: ufs-exynos: Add refclkout_stop control This patch adds REFCLKOUT_STOP control to CLK_STOP_MASK. This permits enabling/disabling reference clock out control for the UFS device. Link: https://lore.kernel.org/r/20211018124216.153072-6-chanho61.park@samsung.com Reviewed-by: Alim Akhtar Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index 853c83fd8242..f320b5689830 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -48,10 +48,11 @@ #define HCI_ERR_EN_T_LAYER 0x84 #define HCI_ERR_EN_DME_LAYER 0x88 #define HCI_CLKSTOP_CTRL 0xB0 +#define REFCLKOUT_STOP BIT(4) #define REFCLK_STOP BIT(2) #define UNIPRO_MCLK_STOP BIT(1) #define UNIPRO_PCLK_STOP BIT(0) -#define CLK_STOP_MASK (REFCLK_STOP |\ +#define CLK_STOP_MASK (REFCLKOUT_STOP | REFCLK_STOP |\ UNIPRO_MCLK_STOP |\ UNIPRO_PCLK_STOP) #define HCI_MISC 0xB4 From 91c49e7e82d739a7e14f883422e1003ee7a447d8 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:07 +0900 Subject: [PATCH 32/46] scsi: ufs: ufs-exynos: Add setup_clocks callback Add setup_clocks callback to control/gate clocks by ufshcd. To avoid calling before initialization, check whether UFS is on or not and call it initially from pre_link callback. Link: https://lore.kernel.org/r/20211018124216.153072-7-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index f320b5689830..d81a2eb894ad 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -794,6 +794,27 @@ static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index) } } +static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on, + enum ufs_notify_change_status status) +{ + struct exynos_ufs *ufs = ufshcd_get_variant(hba); + + if (!ufs) + return 0; + + if (on && status == PRE_CHANGE) { + if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL) + exynos_ufs_disable_auto_ctrl_hcc(ufs); + exynos_ufs_ungate_clks(ufs); + } else if (!on && status == POST_CHANGE) { + exynos_ufs_gate_clks(ufs); + if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL) + exynos_ufs_enable_auto_ctrl_hcc(ufs); + } + + return 0; +} + static int exynos_ufs_pre_link(struct ufs_hba *hba) { struct exynos_ufs *ufs = ufshcd_get_variant(hba); @@ -812,6 +833,8 @@ static int exynos_ufs_pre_link(struct ufs_hba *hba) exynos_ufs_config_phy_time_attr(ufs); exynos_ufs_config_phy_cap_attr(ufs); + exynos_ufs_setup_clocks(hba, true, PRE_CHANGE); + if (ufs->drv_data->pre_link) ufs->drv_data->pre_link(ufs); @@ -1202,6 +1225,7 @@ static struct ufs_hba_variant_ops ufs_hba_exynos_ops = { .hce_enable_notify = exynos_ufs_hce_enable_notify, .link_startup_notify = exynos_ufs_link_startup_notify, .pwr_change_notify = exynos_ufs_pwr_change_notify, + .setup_clocks = exynos_ufs_setup_clocks, .setup_xfer_req = exynos_ufs_specify_nexus_t_xfer_req, .setup_task_mgmt = exynos_ufs_specify_nexus_t_tm_req, .hibern8_notify = exynos_ufs_hibern8_notify, From 533b81d674452d180c5801290fd84a5c7ddeaaeb Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:08 +0900 Subject: [PATCH 33/46] scsi: ufs: ufs-exynos: Support custom version of ufs_hba_variant_ops By default, ufs_hba_exynos_ops will be used. Add support for a custom version of ufs_hba_variant_ops because some variants of exynos-ufs will use only few callbacks. Link: https://lore.kernel.org/r/20211018124216.153072-8-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Reviewed-by: Alim Akhtar Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 8 +++++++- drivers/scsi/ufs/ufs-exynos.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index d81a2eb894ad..b9257d72e9c8 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -1237,8 +1237,14 @@ static int exynos_ufs_probe(struct platform_device *pdev) { int err; struct device *dev = &pdev->dev; + const struct ufs_hba_variant_ops *vops = &ufs_hba_exynos_ops; + const struct exynos_ufs_drv_data *drv_data = + device_get_match_data(dev); - err = ufshcd_pltfrm_init(pdev, &ufs_hba_exynos_ops); + if (drv_data && drv_data->vops) + vops = drv_data->vops; + + err = ufshcd_pltfrm_init(pdev, vops); if (err) dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err); diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index 2e72aabaa673..74f556d8a003 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -142,6 +142,7 @@ struct exynos_ufs_uic_attr { }; struct exynos_ufs_drv_data { + const struct ufs_hba_variant_ops *vops; struct exynos_ufs_uic_attr *uic_attr; unsigned int quirks; unsigned int opts; From a271885ac6b2b5655d42eb8e13fdbf4b45a35d83 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:09 +0900 Subject: [PATCH 34/46] scsi: ufs: ufs-exynos: Add EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR option To skip exynos_ufs_config_phy_*_attr settings for exynos-ufs variant, provide EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR as an opts flag. For the ExynosAuto v9 SoC's controller, M-Phy timing setting is not required and most of vendor-specific configuration will be performed in the pre_link callback function. Link: https://lore.kernel.org/r/20211018124216.153072-9-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 6 ++++-- drivers/scsi/ufs/ufs-exynos.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index b9257d72e9c8..2c15fde5c8d0 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -830,8 +830,10 @@ static int exynos_ufs_pre_link(struct ufs_hba *hba) /* m-phy */ exynos_ufs_phy_init(ufs); - exynos_ufs_config_phy_time_attr(ufs); - exynos_ufs_config_phy_cap_attr(ufs); + if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR)) { + exynos_ufs_config_phy_time_attr(ufs); + exynos_ufs_config_phy_cap_attr(ufs); + } exynos_ufs_setup_clocks(hba, true, PRE_CHANGE); diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index 74f556d8a003..89955ae226dc 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -199,6 +199,7 @@ struct exynos_ufs { #define EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL BIT(2) #define EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX BIT(3) #define EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER BIT(4) +#define EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR BIT(5) }; #define for_each_ufs_rx_lane(ufs, i) \ From 3f02cc9ea7bd7dda313543001a4de0bf935d0b1e Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:10 +0900 Subject: [PATCH 35/46] scsi: ufs: ufs-exynos: Factor out priv data init To leverage the initialization code for the other variant of the exynos-ufs driver, factor out the assignment part. Link: https://lore.kernel.org/r/20211018124216.153072-10-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index 2c15fde5c8d0..91988ff24fb2 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -946,6 +946,18 @@ out: return ret; } +static inline void exynos_ufs_priv_init(struct ufs_hba *hba, + struct exynos_ufs *ufs) +{ + ufs->hba = hba; + ufs->opts = ufs->drv_data->opts; + ufs->rx_sel_idx = PA_MAXDATALANES; + if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX) + ufs->rx_sel_idx = 0; + hba->priv = (void *)ufs; + hba->quirks = ufs->drv_data->quirks; +} + static int exynos_ufs_init(struct ufs_hba *hba) { struct device *dev = hba->dev; @@ -995,13 +1007,8 @@ static int exynos_ufs_init(struct ufs_hba *hba) if (ret) goto phy_off; - ufs->hba = hba; - ufs->opts = ufs->drv_data->opts; - ufs->rx_sel_idx = PA_MAXDATALANES; - if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX) - ufs->rx_sel_idx = 0; - hba->priv = (void *)ufs; - hba->quirks = ufs->drv_data->quirks; + exynos_ufs_priv_init(hba, ufs); + if (ufs->drv_data->drv_init) { ret = ufs->drv_data->drv_init(dev, ufs); if (ret) { From 52e5035f7b079be7cc1db3eb06e1fd78d0ec9341 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:11 +0900 Subject: [PATCH 36/46] scsi: ufs: ufs-exynos: Add pre/post_hce_enable drv callbacks Add driver-specific pre/post_hce_enable callbacks to execute extra initializations before and after hce_enable_notify callback. Link: https://lore.kernel.org/r/20211018124216.153072-11-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Reviewed-by: Alim Akhtar Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 10 ++++++++++ drivers/scsi/ufs/ufs-exynos.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index 91988ff24fb2..f7d14cf075c7 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -1136,6 +1136,12 @@ static int exynos_ufs_hce_enable_notify(struct ufs_hba *hba, switch (status) { case PRE_CHANGE: + if (ufs->drv_data->pre_hce_enable) { + ret = ufs->drv_data->pre_hce_enable(ufs); + if (ret) + return ret; + } + ret = exynos_ufs_host_reset(hba); if (ret) return ret; @@ -1145,6 +1151,10 @@ static int exynos_ufs_hce_enable_notify(struct ufs_hba *hba, exynos_ufs_calc_pwm_clk_div(ufs); if (!(ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)) exynos_ufs_enable_auto_ctrl_hcc(ufs); + + if (ufs->drv_data->post_hce_enable) + ret = ufs->drv_data->post_hce_enable(ufs); + break; } diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index 89955ae226dc..02308faea422 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -154,6 +154,8 @@ struct exynos_ufs_drv_data { struct ufs_pa_layer_attr *pwr); int (*post_pwr_change)(struct exynos_ufs *ufs, struct ufs_pa_layer_attr *pwr); + int (*pre_hce_enable)(struct exynos_ufs *ufs); + int (*post_hce_enable)(struct exynos_ufs *ufs); }; struct ufs_phy_time_cfg { From cc52e15397cc5dc773d3c6792b98352d3209f93f Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:12 +0900 Subject: [PATCH 37/46] scsi: ufs: ufs-exynos: Support ExynosAuto v9 UFS Add support for the ExynosAuto v9 SoC. This requires controlling the UFS IP shareability register via syscon and regmap. The offset of the register can be different according to the UFS instance and SoC specific offset value. As a result, we need to get the offset value from DT property. Unlike exynos7, this implementation has a different M-PHY setting which must be configured via exynosauto_ufs_pre_link. Link: https://lore.kernel.org/r/20211018124216.153072-12-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 118 ++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-exynos.h | 18 ++++++ 2 files changed, 136 insertions(+) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index f7d14cf075c7..3851447e2436 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include "ufshcd.h" #include "ufshcd-pltfrm.h" @@ -75,6 +77,12 @@ UIC_TRANSPORT_NO_CONNECTION_RX |\ UIC_TRANSPORT_BAD_TC) +/* FSYS UFS Shareability */ +#define UFS_WR_SHARABLE BIT(2) +#define UFS_RD_SHARABLE BIT(1) +#define UFS_SHARABLE (UFS_WR_SHARABLE | UFS_RD_SHARABLE) +#define UFS_SHAREABILITY_OFFSET 0x710 + enum { UNIPRO_L1_5 = 0,/* PHY Adapter */ UNIPRO_L2, /* Data Link */ @@ -150,6 +158,89 @@ static int exynos7_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) return 0; } +static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) +{ + struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr; + + /* IO Coherency setting */ + if (ufs->sysreg) { + return regmap_update_bits(ufs->sysreg, + ufs->shareability_reg_offset, + UFS_SHARABLE, UFS_SHARABLE); + } + + attr->tx_dif_p_nsec = 3200000; + + return 0; +} + +static int exynosauto_ufs_pre_link(struct exynos_ufs *ufs) +{ + struct ufs_hba *hba = ufs->hba; + int i; + u32 tx_line_reset_period, rx_line_reset_period; + + rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC; + tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC; + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40); + for_each_ufs_rx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0); + + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i), + (rx_line_reset_period >> 16) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i), + (rx_line_reset_period >> 8) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i), + (rx_line_reset_period) & 0xFF); + + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x79); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6); + } + + for_each_ufs_tx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + /* Not to affect VND_TX_LINERESET_PVALUE to VND_TX_CLK_PRD */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i), + 0x02); + + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i), + (tx_line_reset_period >> 16) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i), + (tx_line_reset_period >> 8) & 0xFF); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i), + (tx_line_reset_period) & 0xFF); + + /* TX PWM Gear Capability / PWM_G1_ONLY */ + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 0x1); + } + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0); + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0); + + ufshcd_dme_set(hba, UIC_ARG_MIB(0xa011), 0x8000); + + return 0; +} + +static int exynosauto_ufs_pre_pwr_change(struct exynos_ufs *ufs, + struct ufs_pa_layer_attr *pwr) +{ + struct ufs_hba *hba = ufs->hba; + + /* PACP_PWR_req and delivered to the remote DME */ + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000); + + return 0; +} + static int exynos7_ufs_pre_link(struct exynos_ufs *ufs) { struct ufs_hba *hba = ufs->hba; @@ -932,6 +1023,17 @@ static int exynos_ufs_parse_dt(struct device *dev, struct exynos_ufs *ufs) goto out; } + ufs->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg"); + if (IS_ERR(ufs->sysreg)) + ufs->sysreg = NULL; + else { + if (of_property_read_u32_index(np, "samsung,sysreg", 1, + &ufs->shareability_reg_offset)) { + dev_warn(dev, "can't get an offset from sysreg. Set to default value\n"); + ufs->shareability_reg_offset = UFS_SHAREABILITY_OFFSET; + } + } + ufs->pclk_avail_min = PCLK_AVAIL_MIN; ufs->pclk_avail_max = PCLK_AVAIL_MAX; @@ -1304,6 +1406,20 @@ static struct exynos_ufs_uic_attr exynos7_uic_attr = { .pa_dbg_option_suite = 0x30103, }; +static struct exynos_ufs_drv_data exynosauto_ufs_drvs = { + .uic_attr = &exynos7_uic_attr, + .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | + UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR | + UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR | + UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING, + .opts = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL | + EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | + EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX, + .drv_init = exynosauto_ufs_drv_init, + .pre_link = exynosauto_ufs_pre_link, + .pre_pwr_change = exynosauto_ufs_pre_pwr_change, +}; + static struct exynos_ufs_drv_data exynos_ufs_drvs = { .uic_attr = &exynos7_uic_attr, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | @@ -1329,6 +1445,8 @@ static struct exynos_ufs_drv_data exynos_ufs_drvs = { static const struct of_device_id exynos_ufs_of_match[] = { { .compatible = "samsung,exynos7-ufs", .data = &exynos_ufs_drvs }, + { .compatible = "samsung,exynosautov9-ufs", + .data = &exynosauto_ufs_drvs }, {}, }; diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h index 02308faea422..1c33e5466082 100644 --- a/drivers/scsi/ufs/ufs-exynos.h +++ b/drivers/scsi/ufs/ufs-exynos.h @@ -56,6 +56,22 @@ #define TX_GRAN_NVAL_10_08 0x0296 #define TX_GRAN_NVAL_H(v) (((v) >> 8) & 0x3) +#define VND_TX_CLK_PRD 0xAA +#define VND_TX_CLK_PRD_EN 0xA9 +#define VND_TX_LINERESET_PVALUE0 0xAD +#define VND_TX_LINERESET_PVALUE1 0xAC +#define VND_TX_LINERESET_PVALUE2 0xAB + +#define TX_LINE_RESET_TIME 3200 + +#define VND_RX_CLK_PRD 0x12 +#define VND_RX_CLK_PRD_EN 0x11 +#define VND_RX_LINERESET_VALUE0 0x1D +#define VND_RX_LINERESET_VALUE1 0x1C +#define VND_RX_LINERESET_VALUE2 0x1B + +#define RX_LINE_RESET_TIME 1000 + #define RX_FILLER_ENABLE 0x0316 #define RX_FILLER_EN (1 << 1) #define RX_LINERESET_VAL 0x0317 @@ -194,6 +210,8 @@ struct exynos_ufs { struct ufs_phy_time_cfg t_cfg; ktime_t entry_hibern8_t; const struct exynos_ufs_drv_data *drv_data; + struct regmap *sysreg; + u32 shareability_reg_offset; u32 opts; #define EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL BIT(0) From b52aea54b6bf740b31f675cc1c637a6c5806b37b Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:14 +0900 Subject: [PATCH 38/46] scsi: ufs: ufs-exynos: Multi-host configuration for ExynosAuto v9 The UFS controller of the ExynosAuto v9 SoC supports a multi-host interface for I/O virtualization. In general, we're using para-virtualized driver to support a block device by several virtual machines. Multi-host functionality extends the host controller by providing register interfaces that can be used by each VM's UFS drivers respectively. This way we can provide direct access to the UFS device for multiple VMs similar to PCIe SR-IOV. We divide this M-HCI as PH (Physical Host) and VHs (Virtual Host). The PH supports all UFSHCI functions (all SAPs) like a conventional UFSHCI but the VH only supports data transfer functions. Thus, except UTP_CMD_SAP and UTP_TMPSAP, the PH should handle all the physical features. Provide an initial implementation of PH part. M-HCI can support up to four interfaces (1 for a PH and 3 for VHs) but this patch initially supports only 1 PH and 1 VH. For this, we uses TASK_TAG[7:5] field so TASK_TAG[4:0] for 32 doorbell will be supported. After the PH is initiated, this will send a ready message to VHs through a mailbox register. The message handler is not fully implemented yet such as supporting reset / abort cases. Link: https://lore.kernel.org/r/20211018124216.153072-14-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Reviewed-by: Inki Dae Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index 3851447e2436..617995b94ef0 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -83,6 +83,44 @@ #define UFS_SHARABLE (UFS_WR_SHARABLE | UFS_RD_SHARABLE) #define UFS_SHAREABILITY_OFFSET 0x710 +/* Multi-host registers */ +#define MHCTRL 0xC4 +#define MHCTRL_EN_VH_MASK (0xE) +#define MHCTRL_EN_VH(vh) (vh << 1) +#define PH2VH_MBOX 0xD8 + +#define MH_MSG_MASK (0xFF) + +#define MH_MSG(id, msg) ((id << 8) | (msg & 0xFF)) +#define MH_MSG_PH_READY 0x1 +#define MH_MSG_VH_READY 0x2 + +#define ALLOW_INQUIRY BIT(25) +#define ALLOW_MODE_SELECT BIT(24) +#define ALLOW_MODE_SENSE BIT(23) +#define ALLOW_PRE_FETCH GENMASK(22, 21) +#define ALLOW_READ_CMD_ALL GENMASK(20, 18) /* read_6/10/16 */ +#define ALLOW_READ_BUFFER BIT(17) +#define ALLOW_READ_CAPACITY GENMASK(16, 15) +#define ALLOW_REPORT_LUNS BIT(14) +#define ALLOW_REQUEST_SENSE BIT(13) +#define ALLOW_SYNCHRONIZE_CACHE GENMASK(8, 7) +#define ALLOW_TEST_UNIT_READY BIT(6) +#define ALLOW_UNMAP BIT(5) +#define ALLOW_VERIFY BIT(4) +#define ALLOW_WRITE_CMD_ALL GENMASK(3, 1) /* write_6/10/16 */ + +#define ALLOW_TRANS_VH_DEFAULT (ALLOW_INQUIRY | ALLOW_MODE_SELECT | \ + ALLOW_MODE_SENSE | ALLOW_PRE_FETCH | \ + ALLOW_READ_CMD_ALL | ALLOW_READ_BUFFER | \ + ALLOW_READ_CAPACITY | ALLOW_REPORT_LUNS | \ + ALLOW_REQUEST_SENSE | ALLOW_SYNCHRONIZE_CACHE | \ + ALLOW_TEST_UNIT_READY | ALLOW_UNMAP | \ + ALLOW_VERIFY | ALLOW_WRITE_CMD_ALL) + +#define HCI_MH_ALLOWABLE_TRAN_OF_VH 0x30C +#define HCI_MH_IID_IN_TASK_TAG 0X308 + enum { UNIPRO_L1_5 = 0,/* PHY Adapter */ UNIPRO_L2, /* Data Link */ @@ -174,6 +212,20 @@ static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs) return 0; } +static int exynosauto_ufs_post_hce_enable(struct exynos_ufs *ufs) +{ + struct ufs_hba *hba = ufs->hba; + + /* Enable Virtual Host #1 */ + ufshcd_rmwl(hba, MHCTRL_EN_VH_MASK, MHCTRL_EN_VH(1), MHCTRL); + /* Default VH Transfer permissions */ + hci_writel(ufs, ALLOW_TRANS_VH_DEFAULT, HCI_MH_ALLOWABLE_TRAN_OF_VH); + /* IID information is replaced in TASKTAG[7:5] instead of IID in UCD */ + hci_writel(ufs, 0x1, HCI_MH_IID_IN_TASK_TAG); + + return 0; +} + static int exynosauto_ufs_pre_link(struct exynos_ufs *ufs) { struct ufs_hba *hba = ufs->hba; @@ -241,6 +293,20 @@ static int exynosauto_ufs_pre_pwr_change(struct exynos_ufs *ufs, return 0; } +static int exynosauto_ufs_post_pwr_change(struct exynos_ufs *ufs, + struct ufs_pa_layer_attr *pwr) +{ + struct ufs_hba *hba = ufs->hba; + u32 enabled_vh; + + enabled_vh = ufshcd_readl(hba, MHCTRL) & MHCTRL_EN_VH_MASK; + + /* Send physical host ready message to virtual hosts */ + ufshcd_writel(hba, MH_MSG(enabled_vh, MH_MSG_PH_READY), PH2VH_MBOX); + + return 0; +} + static int exynos7_ufs_pre_link(struct exynos_ufs *ufs) { struct ufs_hba *hba = ufs->hba; @@ -1416,8 +1482,10 @@ static struct exynos_ufs_drv_data exynosauto_ufs_drvs = { EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX, .drv_init = exynosauto_ufs_drv_init, + .post_hce_enable = exynosauto_ufs_post_hce_enable, .pre_link = exynosauto_ufs_pre_link, .pre_pwr_change = exynosauto_ufs_pre_pwr_change, + .post_pwr_change = exynosauto_ufs_post_pwr_change, }; static struct exynos_ufs_drv_data exynos_ufs_drvs = { From be39f4fd8dd430053304b2ff6db712c0b3168e7f Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Mon, 18 Oct 2021 21:42:15 +0900 Subject: [PATCH 39/46] scsi: ufs: ufs-exynos: Introduce ExynosAuto v9 virtual host Introduce virtual host driver for ExynosAuto v9 UFS mHCI. The VH (Virtual Host) only supports data transfer functions so most of physical features must be disabled by setting the following quirks: - UFSHCD_QUIRK_BROKEN_UIC_CMD - UFSHCD_QUIRK_SKIP_PH_CONFIGURATION Before initialization, the VH must wait until PH is ready. This wait is currently implemented via polling. Link: https://lore.kernel.org/r/20211018124216.153072-15-chanho61.park@samsung.com Cc: Alim Akhtar Cc: Kiwoong Kim Cc: Krzysztof Kozlowski Signed-off-by: Chanho Park Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index 617995b94ef0..f893976dba9d 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -121,6 +121,8 @@ #define HCI_MH_ALLOWABLE_TRAN_OF_VH 0x30C #define HCI_MH_IID_IN_TASK_TAG 0X308 +#define PH_READY_TIMEOUT_MS (5 * MSEC_PER_SEC) + enum { UNIPRO_L1_5 = 0,/* PHY Adapter */ UNIPRO_L2, /* Data Link */ @@ -1406,6 +1408,70 @@ static int exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) return 0; } +static int exynosauto_ufs_vh_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + if (status == POST_CHANGE) { + ufshcd_set_link_active(hba); + ufshcd_set_ufs_dev_active(hba); + } + + return 0; +} + +static int exynosauto_ufs_vh_wait_ph_ready(struct ufs_hba *hba) +{ + u32 mbox; + ktime_t start, stop; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(PH_READY_TIMEOUT_MS)); + + do { + mbox = ufshcd_readl(hba, PH2VH_MBOX); + /* TODO: Mailbox message protocols between the PH and VHs are + * not implemented yet. This will be supported later + */ + if ((mbox & MH_MSG_MASK) == MH_MSG_PH_READY) + return 0; + + usleep_range(40, 50); + } while (ktime_before(ktime_get(), stop)); + + return -ETIME; +} + +static int exynosauto_ufs_vh_init(struct ufs_hba *hba) +{ + struct device *dev = hba->dev; + struct platform_device *pdev = to_platform_device(dev); + struct exynos_ufs *ufs; + int ret; + + ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL); + if (!ufs) + return -ENOMEM; + + /* exynos-specific hci */ + ufs->reg_hci = devm_platform_ioremap_resource_byname(pdev, "vs_hci"); + if (IS_ERR(ufs->reg_hci)) { + dev_err(dev, "cannot ioremap for hci vendor register\n"); + return PTR_ERR(ufs->reg_hci); + } + + ret = exynosauto_ufs_vh_wait_ph_ready(hba); + if (ret) + return ret; + + ufs->drv_data = device_get_match_data(dev); + if (!ufs->drv_data) + return -ENODEV; + + exynos_ufs_priv_init(hba, ufs); + + return 0; +} + static struct ufs_hba_variant_ops ufs_hba_exynos_ops = { .name = "exynos_ufs", .init = exynos_ufs_init, @@ -1420,6 +1486,12 @@ static struct ufs_hba_variant_ops ufs_hba_exynos_ops = { .resume = exynos_ufs_resume, }; +static struct ufs_hba_variant_ops ufs_hba_exynosauto_vh_ops = { + .name = "exynosauto_ufs_vh", + .init = exynosauto_ufs_vh_init, + .link_startup_notify = exynosauto_ufs_vh_link_startup_notify, +}; + static int exynos_ufs_probe(struct platform_device *pdev) { int err; @@ -1488,6 +1560,18 @@ static struct exynos_ufs_drv_data exynosauto_ufs_drvs = { .post_pwr_change = exynosauto_ufs_post_pwr_change, }; +static struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = { + .vops = &ufs_hba_exynosauto_vh_ops, + .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | + UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR | + UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR | + UFSHCI_QUIRK_BROKEN_HCE | + UFSHCD_QUIRK_BROKEN_UIC_CMD | + UFSHCD_QUIRK_SKIP_PH_CONFIGURATION | + UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING, + .opts = EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX, +}; + static struct exynos_ufs_drv_data exynos_ufs_drvs = { .uic_attr = &exynos7_uic_attr, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | @@ -1515,6 +1599,8 @@ static const struct of_device_id exynos_ufs_of_match[] = { .data = &exynos_ufs_drvs }, { .compatible = "samsung,exynosautov9-ufs", .data = &exynosauto_ufs_drvs }, + { .compatible = "samsung,exynosautov9-ufs-vh", + .data = &exynosauto_ufs_vh_drvs }, {}, }; From a1efc896cb8a2831de3cf5a9747e030b494f1a26 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 26 Oct 2021 19:53:01 +0300 Subject: [PATCH 40/46] scsi: sr: Remove duplicate assignment Link: https://lore.kernel.org/r/YXgybUhYN+Vrruj8@localhost.localdomain Signed-off-by: Alexey Dobriyan Signed-off-by: Martin K. Petersen --- drivers/scsi/sr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index d769057b25a0..e2bb7f790057 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -692,7 +692,6 @@ static int sr_probe(struct device *dev) cd->device = sdev; cd->disk = disk; cd->driver = &sr_template; - cd->disk = disk; cd->capacity = 0x1fffff; cd->device->changed = 1; /* force recheck CD type */ cd->media_present = 1; From 5f7cf82c1d7373fcf9e1062f5654efd5fa2b9211 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Fri, 22 Oct 2021 09:02:01 +0800 Subject: [PATCH 41/46] scsi: bsg: Fix errno when scsi_bsg_register_queue() fails When the value of error is printed, it will always be 0. We should print the correct error code when scsi_bsg_register_queue() fails. Link: https://lore.kernel.org/r/20211022010201.426746-1-liu.yun@linux.dev Fixes: ead09dd3aed5 ("scsi: bsg: Simplify device registration") Cc: Jens Axboe Cc: Christoph Hellwig Reviewed-by: Christoph Hellwig Signed-off-by: Jackie Liu Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_sysfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index fa064bf9a55c..f06639d21e11 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1374,6 +1374,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) * We're treating error on bsg register as non-fatal, so * pretend nothing went wrong. */ + error = PTR_ERR(sdev->bsg_dev); sdev_printk(KERN_INFO, sdev, "Failed to register bsg queue, errno=%d\n", error); From 5ae17501bc62a49b0b193dcce003f16375f16654 Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Fri, 29 Oct 2021 15:43:10 -0400 Subject: [PATCH 42/46] scsi: core: Avoid leaving shost->last_reset with stale value if EH does not run The changes to issue the abort from the scmd->abort_work instead of the EH thread introduced a problem if eh_deadline is used. If aborting the command(s) is successful, and there are never any scmds added to the shost->eh_cmd_q, there is no code path which will reset the ->last_reset value back to zero. The effect of this is that after a successful abort with no EH thread activity, a subsequent timeout, perhaps a long time later, might immediately be considered past a user-set eh_deadline time, and the host will be reset with no attempt at recovery. Fix this by resetting ->last_reset back to zero in scmd_eh_abort_handler() if it is determined that the EH thread will not run to do this. Thanks to Gopinath Marappan for investigating this problem. Link: https://lore.kernel.org/r/20211029194311.17504-2-emilne@redhat.com Fixes: e494f6a72839 ("[SCSI] improved eh timeout handler") Cc: stable@vger.kernel.org Signed-off-by: Ewan D. Milne Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 1 + drivers/scsi/scsi_error.c | 25 +++++++++++++++++++++++++ drivers/scsi/scsi_lib.c | 1 + include/scsi/scsi_cmnd.h | 2 +- include/scsi/scsi_host.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 17aef936bc90..2cb7163e24cc 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -387,6 +387,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); + INIT_LIST_HEAD(&shost->eh_abort_list); INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 3de03925550e..bdf782d9cb86 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -133,6 +133,23 @@ static bool scsi_eh_should_retry_cmd(struct scsi_cmnd *cmd) return true; } +static void scsi_eh_complete_abort(struct scsi_cmnd *scmd, struct Scsi_Host *shost) +{ + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + list_del_init(&scmd->eh_entry); + /* + * If the abort succeeds, and there is no further + * EH action, clear the ->last_reset time. + */ + if (list_empty(&shost->eh_abort_list) && + list_empty(&shost->eh_cmd_q)) + if (shost->eh_deadline != -1) + shost->last_reset = 0; + spin_unlock_irqrestore(shost->host_lock, flags); +} + /** * scmd_eh_abort_handler - Handle command aborts * @work: command to be aborted. @@ -150,6 +167,7 @@ scmd_eh_abort_handler(struct work_struct *work) container_of(work, struct scsi_cmnd, abort_work.work); struct scsi_device *sdev = scmd->device; enum scsi_disposition rtn; + unsigned long flags; if (scsi_host_eh_past_deadline(sdev->host)) { SCSI_LOG_ERROR_RECOVERY(3, @@ -173,12 +191,14 @@ scmd_eh_abort_handler(struct work_struct *work) SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_WARNING, scmd, "retry aborted command\n")); + scsi_eh_complete_abort(scmd, sdev->host); scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY); return; } else { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_WARNING, scmd, "finish aborted command\n")); + scsi_eh_complete_abort(scmd, sdev->host); scsi_finish_command(scmd); return; } @@ -191,6 +211,9 @@ scmd_eh_abort_handler(struct work_struct *work) } } + spin_lock_irqsave(sdev->host->host_lock, flags); + list_del_init(&scmd->eh_entry); + spin_unlock_irqrestore(sdev->host->host_lock, flags); scsi_eh_scmd_add(scmd); } @@ -221,6 +244,8 @@ scsi_abort_command(struct scsi_cmnd *scmd) spin_lock_irqsave(shost->host_lock, flags); if (shost->eh_deadline != -1 && !shost->last_reset) shost->last_reset = jiffies; + BUG_ON(!list_empty(&scmd->eh_entry)); + list_add_tail(&scmd->eh_entry, &shost->eh_abort_list); spin_unlock_irqrestore(shost->host_lock, flags); scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d0b7c6dc74f8..c851c05d6091 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1143,6 +1143,7 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) cmd->sense_buffer = buf; cmd->prot_sdb = prot; cmd->flags = flags; + INIT_LIST_HEAD(&cmd->eh_entry); INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler); cmd->jiffies_at_alloc = jiffies_at_alloc; cmd->retries = retries; diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 7958a604f979..29ac40cf1aae 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -73,7 +73,7 @@ enum scsi_cmnd_submitter { struct scsi_cmnd { struct scsi_request req; struct scsi_device *device; - struct list_head eh_entry; /* entry for the host eh_cmd_q */ + struct list_head eh_entry; /* entry for the host eh_abort_list/eh_cmd_q */ struct delayed_work abort_work; struct rcu_head rcu; diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index ae715959f886..ebe059badba0 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -551,6 +551,7 @@ struct Scsi_Host { struct mutex scan_mutex;/* serialize scanning activity */ + struct list_head eh_abort_list; struct list_head eh_cmd_q; struct task_struct * ehandler; /* Error recovery thread. */ struct completion * eh_action; /* Wait for specific actions on the From 9ec5128a8b5631d652ed06b37e0166f337802f90 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Sun, 31 Oct 2021 14:36:54 +0200 Subject: [PATCH 43/46] scsi: ufs: ufshpb: Properly handle max-single-cmd The spec recommends that for transfer length larger than the max-single-cmd attribute (bMAX_DATA_SIZE_FOR_HPB_SINGLE_CMD) it is possible to couple pre-requests with the HPB-READ command. Being a recommendation, using pre-requests can be perceived merely as a means of optimization. A common practice was to send pre-requests for chunks within some interval, and leave the READ10 untouched if larger. Now that the pre-request flows have been removed, all the commands are single commands. Properly handle this attribute and do not send HPB-READ for transfer lengths larger than max-single-cmd. [mkp: resolve conflict] Fixes: 09d9e4d04187 ("scsi: ufs: ufshpb: Remove HPB2.0 flows") Link: https://lore.kernel.org/r/20211031123654.17719-1-avri.altman@wdc.com Reviewed-by: Daejun Park Signed-off-by: Avri Altman Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshpb.c | 24 +++++++++++++----------- drivers/scsi/ufs/ufshpb.h | 1 - 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/ufs/ufshpb.c b/drivers/scsi/ufs/ufshpb.c index 3b1a90b1d82a..a86d0cc50de2 100644 --- a/drivers/scsi/ufs/ufshpb.c +++ b/drivers/scsi/ufs/ufshpb.c @@ -394,8 +394,6 @@ int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) if (!ufshpb_is_supported_chunk(hpb, transfer_len)) return 0; - WARN_ON_ONCE(transfer_len > HPB_MULTI_CHUNK_HIGH); - if (hpb->is_hcm) { /* * in host control mode, reads are the main source for @@ -1572,7 +1570,7 @@ static void ufshpb_lu_parameter_init(struct ufs_hba *hba, if (ufshpb_is_legacy(hba)) hpb->pre_req_max_tr_len = HPB_LEGACY_CHUNK_HIGH; else - hpb->pre_req_max_tr_len = HPB_MULTI_CHUNK_HIGH; + hpb->pre_req_max_tr_len = hpb_dev_info->max_hpb_single_cmd; hpb->lu_pinned_start = hpb_lu_info->pinned_start; hpb->lu_pinned_end = hpb_lu_info->num_pinned ? @@ -2582,7 +2580,7 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) { struct ufshpb_dev_info *hpb_dev_info = &hba->ufshpb_dev; int version, ret; - u32 max_hpb_single_cmd = HPB_MULTI_CHUNK_LOW; + int max_single_cmd; hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL]; @@ -2598,18 +2596,22 @@ void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) if (version == HPB_SUPPORT_LEGACY_VERSION) hpb_dev_info->is_legacy = true; - ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, - QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD, 0, 0, &max_hpb_single_cmd); - if (ret) - dev_err(hba->dev, "%s: idn: read max size of single hpb cmd query request failed", - __func__); - hpb_dev_info->max_hpb_single_cmd = max_hpb_single_cmd; - /* * Get the number of user logical unit to check whether all * scsi_device finish initialization */ hpb_dev_info->num_lu = desc_buf[DEVICE_DESC_PARAM_NUM_LU]; + + if (hpb_dev_info->is_legacy) + return; + + ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD, 0, 0, &max_single_cmd); + + if (ret) + hpb_dev_info->max_hpb_single_cmd = HPB_LEGACY_CHUNK_HIGH; + else + hpb_dev_info->max_hpb_single_cmd = min(max_single_cmd + 1, HPB_MULTI_CHUNK_HIGH); } void ufshpb_init(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/ufshpb.h b/drivers/scsi/ufs/ufshpb.h index f15d8fdbce2e..b475dbd78988 100644 --- a/drivers/scsi/ufs/ufshpb.h +++ b/drivers/scsi/ufs/ufshpb.h @@ -31,7 +31,6 @@ /* hpb support chunk size */ #define HPB_LEGACY_CHUNK_HIGH 1 -#define HPB_MULTI_CHUNK_LOW 7 #define HPB_MULTI_CHUNK_HIGH 255 /* hpb vender defined opcode */ From 20aaef52eb08f1d987d46ad26edb8f142f74d83a Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Wed, 3 Nov 2021 10:06:58 -0700 Subject: [PATCH 44/46] scsi: scsi_ioctl: Validate command size Need to make sure the command size is valid before copying the command from user space. Link: https://lore.kernel.org/r/20211103170659.22151-1-tadeusz.struk@linaro.org Cc: Bart Van Assche Cc: Christoph Hellwig Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: Cc: Cc: # 5.15, 5.14, 5.10 Signed-off-by: Tadeusz Struk Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 6ff2207bd45a..a06c61f22742 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -347,6 +347,8 @@ static int scsi_fill_sghdr_rq(struct scsi_device *sdev, struct request *rq, { struct scsi_request *req = scsi_req(rq); + if (hdr->cmd_len < 6) + return -EMSGSIZE; if (copy_from_user(req->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; if (!scsi_cmd_allowed(req->cmd, mode)) From 703535e6ae1e94c89a9c1396b4c7b6b41160ef0c Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Wed, 3 Nov 2021 10:06:59 -0700 Subject: [PATCH 45/46] scsi: core: Remove command size deduction from scsi_setup_scsi_cmnd() No need to deduce command size in scsi_setup_scsi_cmnd() anymore as appropriate checks have been added to scsi_fill_sghdr_rq() function and the cmd_len should never be zero here. The code to do that wasn't correct anyway, as it used uninitialized cmd->cmnd, which caused a null-ptr-deref if the command size was zero as in the trace below. Fix this by removing the unneeded code. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 PID: 1822 Comm: repro Not tainted 5.15.0 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-4.fc34 04/01/2014 Call Trace: blk_mq_dispatch_rq_list+0x7c7/0x12d0 __blk_mq_sched_dispatch_requests+0x244/0x380 blk_mq_sched_dispatch_requests+0xf0/0x160 __blk_mq_run_hw_queue+0xe8/0x160 __blk_mq_delay_run_hw_queue+0x252/0x5d0 blk_mq_run_hw_queue+0x1dd/0x3b0 blk_mq_sched_insert_request+0x1ff/0x3e0 blk_execute_rq_nowait+0x173/0x1e0 blk_execute_rq+0x15c/0x540 sg_io+0x97c/0x1370 scsi_ioctl+0xe16/0x28e0 sd_ioctl+0x134/0x170 blkdev_ioctl+0x362/0x6e0 block_ioctl+0xb0/0xf0 vfs_ioctl+0xa7/0xf0 do_syscall_64+0x3d/0xb0 entry_SYSCALL_64_after_hwframe+0x44/0xae ---[ end trace 8b086e334adef6d2 ]--- Kernel panic - not syncing: Fatal exception Link: https://lore.kernel.org/r/20211103170659.22151-2-tadeusz.struk@linaro.org Fixes: 2ceda20f0a99 ("scsi: core: Move command size detection out of the fast path") Cc: Bart Van Assche Cc: Christoph Hellwig Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: Cc: Cc: # 5.15, 5.14, 5.10 Reported-by: syzbot+5516b30f5401d4dcbcae@syzkaller.appspotmail.com Reviewed-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Tadeusz Struk Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index c851c05d6091..43151e5d07ab 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1175,8 +1175,6 @@ static blk_status_t scsi_setup_scsi_cmnd(struct scsi_device *sdev, } cmd->cmd_len = scsi_req(req)->cmd_len; - if (cmd->cmd_len == 0) - cmd->cmd_len = scsi_command_size(cmd->cmnd); cmd->cmnd = scsi_req(req)->cmd; cmd->transfersize = blk_rq_bytes(req); cmd->allowed = scsi_req(req)->retries; From 3344b58b53a76199dae48faa396e9fc37bf86992 Mon Sep 17 00:00:00 2001 From: George Kennedy Date: Thu, 4 Nov 2021 15:06:37 -0500 Subject: [PATCH 46/46] scsi: scsi_debug: Don't call kcalloc() if size arg is zero If the size arg to kcalloc() is zero, it returns ZERO_SIZE_PTR. Because of that, for a following NULL pointer check to work on the returned pointer, kcalloc() must not be called with the size arg equal to zero. Return early without error before the kcalloc() call if size arg is zero. BUG: KASAN: null-ptr-deref in memcpy include/linux/fortify-string.h:191 [inline] BUG: KASAN: null-ptr-deref in sg_copy_buffer+0x138/0x240 lib/scatterlist.c:974 Write of size 4 at addr 0000000000000010 by task syz-executor.1/22789 CPU: 1 PID: 22789 Comm: syz-executor.1 Not tainted 5.15.0-syzk #1 Hardware name: Red Hat KVM, BIOS 1.13.0-2 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x89/0xb5 lib/dump_stack.c:106 __kasan_report mm/kasan/report.c:446 [inline] kasan_report.cold.14+0x112/0x117 mm/kasan/report.c:459 check_region_inline mm/kasan/generic.c:183 [inline] kasan_check_range+0x1a3/0x210 mm/kasan/generic.c:189 memcpy+0x3b/0x60 mm/kasan/shadow.c:66 memcpy include/linux/fortify-string.h:191 [inline] sg_copy_buffer+0x138/0x240 lib/scatterlist.c:974 do_dout_fetch drivers/scsi/scsi_debug.c:2954 [inline] do_dout_fetch drivers/scsi/scsi_debug.c:2946 [inline] resp_verify+0x49e/0x930 drivers/scsi/scsi_debug.c:4276 schedule_resp+0x4d8/0x1a70 drivers/scsi/scsi_debug.c:5478 scsi_debug_queuecommand+0x8c9/0x1ec0 drivers/scsi/scsi_debug.c:7533 scsi_dispatch_cmd drivers/scsi/scsi_lib.c:1520 [inline] scsi_queue_rq+0x16b0/0x2d40 drivers/scsi/scsi_lib.c:1699 blk_mq_dispatch_rq_list+0xb9b/0x2700 block/blk-mq.c:1639 __blk_mq_sched_dispatch_requests+0x28f/0x590 block/blk-mq-sched.c:325 blk_mq_sched_dispatch_requests+0x105/0x190 block/blk-mq-sched.c:358 __blk_mq_run_hw_queue+0xe5/0x150 block/blk-mq.c:1761 __blk_mq_delay_run_hw_queue+0x4f8/0x5c0 block/blk-mq.c:1838 blk_mq_run_hw_queue+0x18d/0x350 block/blk-mq.c:1891 blk_mq_sched_insert_request+0x3db/0x4e0 block/blk-mq-sched.c:474 blk_execute_rq_nowait+0x16b/0x1c0 block/blk-exec.c:62 blk_execute_rq+0xdb/0x360 block/blk-exec.c:102 sg_scsi_ioctl drivers/scsi/scsi_ioctl.c:621 [inline] scsi_ioctl+0x8bb/0x15c0 drivers/scsi/scsi_ioctl.c:930 sg_ioctl_common+0x172d/0x2710 drivers/scsi/sg.c:1112 sg_ioctl+0xa2/0x180 drivers/scsi/sg.c:1165 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:874 [inline] __se_sys_ioctl fs/ioctl.c:860 [inline] __x64_sys_ioctl+0x19d/0x220 fs/ioctl.c:860 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3a/0x80 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae Link: https://lore.kernel.org/r/1636056397-13151-1-git-send-email-george.kennedy@oracle.com Reported-by: syzkaller Acked-by: Douglas Gilbert Signed-off-by: George Kennedy Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index e008950c3e7f..8c66e8a1a1f0 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4259,6 +4259,8 @@ static int resp_verify(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) mk_sense_invalid_opcode(scp); return check_condition_result; } + if (vnum == 0) + return 0; /* not an error */ a_num = is_bytchk3 ? 1 : vnum; /* Treat following check like one for read (i.e. no write) access */ ret = check_device_access_params(scp, lba, a_num, false); @@ -4322,6 +4324,8 @@ static int resp_report_zones(struct scsi_cmnd *scp, } zs_lba = get_unaligned_be64(cmd + 2); alloc_len = get_unaligned_be32(cmd + 10); + if (alloc_len == 0) + return 0; /* not an error */ rep_opts = cmd[14] & 0x3f; partial = cmd[14] & 0x80;