diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-07-28 16:40:43 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-07-28 16:40:43 +1000 |
commit | 89baee2ac0068eba3e8c8e23abad84c580a14cd0 (patch) | |
tree | 244bc853acb8da22969ee7ad14f6cad6879bb5db | |
parent | 20509d895d637912eb02b13f1326ebff2ba68dcc (diff) | |
parent | 8fcdc238ce1b492e1f57a73a2ce4131d63f45397 (diff) |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
-rw-r--r-- | Documentation/devicetree/bindings/ufs/qcom,ufs.yaml | 2 | ||||
-rw-r--r-- | Documentation/scsi/ufs.rst | 15 | ||||
-rw-r--r-- | drivers/scsi/ch.c | 2 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_main.c | 49 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 2 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 2 | ||||
-rw-r--r-- | drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 16 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 4 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_mbox.c | 2 | ||||
-rw-r--r-- | drivers/scsi/mpi3mr/mpi3mr.h | 71 | ||||
-rw-r--r-- | drivers/scsi/mpi3mr/mpi3mr_fw.c | 67 | ||||
-rw-r--r-- | drivers/scsi/mpi3mr/mpi3mr_os.c | 289 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target.c | 57 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_auth.c | 122 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_login.c | 17 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_nego.c | 15 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_nego.h | 3 | ||||
-rw-r--r-- | drivers/target/target_core_sbc.c | 99 | ||||
-rw-r--r-- | drivers/ufs/core/ufshcd.c | 16 |
19 files changed, 664 insertions, 186 deletions
diff --git a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml index dcd32c10205a..f2d6298d926c 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml @@ -26,6 +26,7 @@ properties: - qcom,msm8994-ufshc - qcom,msm8996-ufshc - qcom,msm8998-ufshc + - qcom,sc8280xp-ufshc - qcom,sdm845-ufshc - qcom,sm6350-ufshc - qcom,sm8150-ufshc @@ -98,6 +99,7 @@ allOf: contains: enum: - qcom,msm8998-ufshc + - qcom,sc8280xp-ufshc - qcom,sm8250-ufshc - qcom,sm8350-ufshc - qcom,sm8450-ufshc diff --git a/Documentation/scsi/ufs.rst b/Documentation/scsi/ufs.rst index fbac745b783c..885b1a736e3f 100644 --- a/Documentation/scsi/ufs.rst +++ b/Documentation/scsi/ufs.rst @@ -17,6 +17,8 @@ Universal Flash Storage 3.2 UTP Transfer requests 3.3 UFS error handling 3.4 SCSI Error handling + 4. BSG Support + 5. UFS Reference Clock Frequency configuration 1. Overview @@ -193,3 +195,16 @@ UFS specifications can be found at: - UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf - UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf + +5. UFS Reference Clock Frequency configuration +============================================== + +Devicetree can define a clock named "ref_clk" under the UFS controller node +to specify the intended reference clock frequency for the UFS storage +parts. ACPI-based system can specify the frequency using ACPI +Device-Specific Data property named "ref-clk-freq". In both ways the value +is interpreted as frequency in Hz and must match one of the values given in +the UFS specification. UFS subsystem will attempt to read the value when +executing common controller initialization. If the value is available, UFS +subsytem will ensure the bRefClkFreq attribute of the UFS storage device is +set accordingly and will modify it if there is a mismatch. diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 908854869864..7ab29eaec6f3 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -63,7 +63,7 @@ static int verbose = 1; module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose,"be verbose (default: on)"); -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more " "detailed sense codes on scsi errors (default: off)"); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 764e859d0106..33af5b8dede2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -219,10 +219,15 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, task->lldd_task = NULL; if (!sas_protocol_ata(task->task_proto)) { - if (slot->n_elem) - dma_unmap_sg(dev, task->scatter, - task->num_scatter, - task->data_dir); + if (slot->n_elem) { + if (task->task_proto & SAS_PROTOCOL_SSP) + dma_unmap_sg(dev, task->scatter, + task->num_scatter, + task->data_dir); + else + dma_unmap_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + } if (slot->n_elem_dif) { struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; @@ -269,28 +274,23 @@ static void hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, } static void hisi_sas_dma_unmap(struct hisi_hba *hisi_hba, - struct sas_task *task, int n_elem, - int n_elem_req) + struct sas_task *task, int n_elem) { struct device *dev = hisi_hba->dev; - if (!sas_protocol_ata(task->task_proto)) { + if (!sas_protocol_ata(task->task_proto) && n_elem) { if (task->num_scatter) { - if (n_elem) - dma_unmap_sg(dev, task->scatter, - task->num_scatter, - task->data_dir); + dma_unmap_sg(dev, task->scatter, task->num_scatter, + task->data_dir); } else if (task->task_proto & SAS_PROTOCOL_SMP) { - if (n_elem_req) - dma_unmap_sg(dev, &task->smp_task.smp_req, - 1, DMA_TO_DEVICE); + dma_unmap_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); } } } static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, - struct sas_task *task, int *n_elem, - int *n_elem_req) + struct sas_task *task, int *n_elem) { struct device *dev = hisi_hba->dev; int rc; @@ -308,9 +308,9 @@ static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, goto prep_out; } } else if (task->task_proto & SAS_PROTOCOL_SMP) { - *n_elem_req = dma_map_sg(dev, &task->smp_task.smp_req, - 1, DMA_TO_DEVICE); - if (!*n_elem_req) { + *n_elem = dma_map_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + if (!*n_elem) { rc = -ENOMEM; goto prep_out; } @@ -332,8 +332,7 @@ static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, err_out_dma_unmap: /* It would be better to call dma_unmap_sg() here, but it's messy */ - hisi_sas_dma_unmap(hisi_hba, task, *n_elem, - *n_elem_req); + hisi_sas_dma_unmap(hisi_hba, task, *n_elem); prep_out: return rc; } @@ -457,7 +456,7 @@ void hisi_sas_task_deliver(struct hisi_hba *hisi_hba, static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) { - int n_elem = 0, n_elem_dif = 0, n_elem_req = 0; + int n_elem = 0, n_elem_dif = 0; struct domain_device *device = task->dev; struct asd_sas_port *sas_port = device->port; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -568,8 +567,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return -EINVAL; } - rc = hisi_sas_dma_map(hisi_hba, task, &n_elem, - &n_elem_req); + rc = hisi_sas_dma_map(hisi_hba, task, &n_elem); if (rc < 0) goto prep_out; @@ -605,8 +603,7 @@ err_out_dif_dma_unmap: if (!sas_protocol_ata(task->task_proto)) hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif); err_out_dma_unmap: - hisi_sas_dma_unmap(hisi_hba, task, n_elem, - n_elem_req); + hisi_sas_dma_unmap(hisi_hba, task, n_elem); prep_out: dev_err(dev, "task exec: failed[%d]!\n", rc); return rc; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 4582791def32..349546bacb2b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1282,8 +1282,6 @@ static void slot_complete_v1_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 18297ab5a32b..70e401fd432a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2428,8 +2428,6 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index eb86afb21aab..efe8c5be5870 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -481,6 +481,9 @@ struct hisi_sas_err_record_v3 { #define RX_DATA_LEN_UNDERFLOW_OFF 6 #define RX_DATA_LEN_UNDERFLOW_MSK (1 << RX_DATA_LEN_UNDERFLOW_OFF) +#define RX_FIS_STATUS_ERR_OFF 0 +#define RX_FIS_STATUS_ERR_MSK (1 << RX_FIS_STATUS_ERR_OFF) + #define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096 #define HISI_SAS_MSI_COUNT_V3_HW 32 @@ -2161,6 +2164,7 @@ slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task, hisi_sas_status_buf_addr_mem(slot); u32 dma_rx_err_type = le32_to_cpu(record->dma_rx_err_type); u32 trans_tx_fail_type = le32_to_cpu(record->trans_tx_fail_type); + u16 sipc_rx_err_type = le16_to_cpu(record->sipc_rx_err_type); u32 dw3 = le32_to_cpu(complete_hdr->dw3); switch (task->task_proto) { @@ -2188,7 +2192,10 @@ slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task, case SAS_PROTOCOL_SATA: case SAS_PROTOCOL_STP: case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: - if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) { + if ((complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) && + (sipc_rx_err_type & RX_FIS_STATUS_ERR_MSK)) { + ts->stat = SAS_PROTO_RESPONSE; + } else if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) { ts->residual = trans_tx_fail_type; ts->stat = SAS_DATA_UNDERRUN; } else if (dw3 & CMPLT_HDR_IO_IN_TARGET_MSK) { @@ -2311,8 +2318,6 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), @@ -2778,16 +2783,13 @@ static DEVICE_ATTR_RW(intr_coal_count_v3_hw); static int slave_configure_v3_hw(struct scsi_device *sdev) { struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev); - struct domain_device *ddev = sdev_to_domain_dev(sdev); struct hisi_hba *hisi_hba = shost_priv(shost); + int ret = hisi_sas_slave_configure(sdev); struct device *dev = hisi_hba->dev; - int ret = sas_slave_configure(sdev); unsigned int max_sectors; if (ret) return ret; - if (!dev_is_sata(ddev)) - sas_change_queue_depth(sdev, 64); if (sdev->type == TYPE_ENCLOSURE) return 0; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index dc35f0f8eae3..e4f77072a58d 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -531,6 +531,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->reset_result = 0; @@ -544,6 +545,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (rc == 0) rc = d->reset_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } @@ -558,6 +560,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->enable_result = 0; @@ -571,6 +574,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (rc == 0) rc = d->enable_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 2a339d4a7e9d..157c3bdb50be 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -181,7 +181,7 @@ MODULE_PARM_DESC(cmd_per_lun, * This would result in non-disk devices being skipped during driver load * time. These can be later added though, using /proc/scsi/scsi */ -static unsigned int megaraid_fast_load = 0; +static unsigned int megaraid_fast_load; module_param_named(fast_load, megaraid_fast_load, int, 0); MODULE_PARM_DESC(fast_load, "Faster loading of the driver, skips physical devices! (default=0)"); diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index 0557dbfb2221..0935b2e80662 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -66,6 +66,8 @@ extern atomic64_t event_counter; #define MPI3MR_NAME_LENGTH 32 #define IOCNAME "%s: " +#define MPI3MR_MAX_SECTORS 2048 + /* Definitions for internal SGL and Chain SGL buffers */ #define MPI3MR_PAGE_SIZE_4K 4096 #define MPI3MR_SG_DEPTH (MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common)) @@ -333,6 +335,12 @@ struct mpi3mr_ioc_facts { u8 sge_mod_mask; u8 sge_mod_value; u8 sge_mod_shift; + u8 max_dev_per_tg; + u16 max_io_throttle_group; + u16 io_throttle_data_length; + u16 io_throttle_low; + u16 io_throttle_high; + }; /** @@ -425,6 +433,31 @@ struct mpi3mr_intr_info { }; /** + * struct mpi3mr_throttle_group_info - Throttle group info + * + * @io_divert: Flag indicates io divert is on or off for the TG + * @need_qd_reduction: Flag to indicate QD reduction is needed + * @qd_reduction: Queue Depth reduction in units of 10% + * @fw_qd: QueueDepth value reported by the firmware + * @modified_qd: Modified QueueDepth value due to throttling + * @id: Throttle Group ID. + * @high: High limit to turn on throttling in 512 byte blocks + * @low: Low limit to turn off throttling in 512 byte blocks + * @pend_large_data_sz: Counter to track pending large data + */ +struct mpi3mr_throttle_group_info { + u8 io_divert; + u8 need_qd_reduction; + u8 qd_reduction; + u16 fw_qd; + u16 modified_qd; + u16 id; + u32 high; + u32 low; + atomic_t pend_large_data_sz; +}; + +/** * struct tgt_dev_sas_sata - SAS/SATA device specific * information cached from firmware given data * @@ -457,22 +490,33 @@ struct tgt_dev_pcie { }; /** - * struct tgt_dev_volume - virtual device specific information + * struct tgt_dev_vd - virtual device specific information * cached from firmware given data * * @state: State of the VD + * @tg_qd_reduction: Queue Depth reduction in units of 10% + * @tg_id: VDs throttle group ID + * @high: High limit to turn on throttling in 512 byte blocks + * @low: Low limit to turn off throttling in 512 byte blocks + * @tg: Pointer to throttle group info */ -struct tgt_dev_volume { +struct tgt_dev_vd { u8 state; + u8 tg_qd_reduction; + u16 tg_id; + u32 tg_high; + u32 tg_low; + struct mpi3mr_throttle_group_info *tg; }; + /** * union _form_spec_inf - union of device specific information */ union _form_spec_inf { struct tgt_dev_sas_sata sas_sata_inf; struct tgt_dev_pcie pcie_inf; - struct tgt_dev_volume vol_inf; + struct tgt_dev_vd vd_inf; }; @@ -490,6 +534,7 @@ union _form_spec_inf { * @dev_type: SAS/SATA/PCIE device type * @is_hidden: Should be exposed to upper layers or not * @host_exposed: Already exposed to host or not + * @io_throttle_enabled: I/O throttling needed or not * @q_depth: Device specific Queue Depth * @wwid: World wide ID * @dev_spec: Device type specific information @@ -506,6 +551,7 @@ struct mpi3mr_tgt_dev { u8 dev_type; u8 is_hidden; u8 host_exposed; + u8 io_throttle_enabled; u16 q_depth; u64 wwid; union _form_spec_inf dev_spec; @@ -557,6 +603,9 @@ static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s) * @dev_removed: Device removed in the Firmware * @dev_removedelay: Device is waiting to be removed in FW * @dev_type: Device type + * @io_throttle_enabled: I/O throttling needed or not + * @io_divert: Flag indicates io divert is on or off for the dev + * @throttle_group: Pointer to throttle group info * @tgt_dev: Internal target device pointer * @pend_count: Counter to track pending I/Os during error * handling @@ -570,6 +619,9 @@ struct mpi3mr_stgt_priv_data { u8 dev_removed; u8 dev_removedelay; u8 dev_type; + u8 io_throttle_enabled; + u8 io_divert; + struct mpi3mr_throttle_group_info *throttle_group; struct mpi3mr_tgt_dev *tgt_dev; u32 pend_count; }; @@ -796,6 +848,12 @@ struct scmd_priv { * @logdata_buf: Circular buffer to store log data entries * @logdata_buf_idx: Index of entry in buffer to store * @logdata_entry_sz: log data entry size + * @pend_large_data_sz: Counter to track pending large data + * @io_throttle_data_length: I/O size to track in 512b blocks + * @io_throttle_high: I/O size to start throttle in 512b blocks + * @io_throttle_low: I/O size to stop throttle in 512b blocks + * @num_io_throttle_group: Maximum number of throttle groups + * @throttle_groups: Pointer to throttle group info structures */ struct mpi3mr_ioc { struct list_head list; @@ -960,6 +1018,13 @@ struct mpi3mr_ioc { u8 *logdata_buf; u16 logdata_buf_idx; u16 logdata_entry_sz; + + atomic_t pend_large_data_sz; + u32 io_throttle_data_length; + u32 io_throttle_high; + u32 io_throttle_low; + u16 num_io_throttle_group; + struct mpi3mr_throttle_group_info *throttle_groups; }; /** diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index f1d4ea8ba989..0866dfd43318 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -2785,6 +2785,27 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, mrioc->facts.shutdown_timeout = le16_to_cpu(facts_data->shutdown_timeout); + mrioc->facts.max_dev_per_tg = + facts_data->max_devices_per_throttle_group; + mrioc->facts.io_throttle_data_length = + le16_to_cpu(facts_data->io_throttle_data_length); + mrioc->facts.max_io_throttle_group = + le16_to_cpu(facts_data->max_io_throttle_group); + mrioc->facts.io_throttle_low = le16_to_cpu(facts_data->io_throttle_low); + mrioc->facts.io_throttle_high = + le16_to_cpu(facts_data->io_throttle_high); + + /* Store in 512b block count */ + if (mrioc->facts.io_throttle_data_length) + mrioc->io_throttle_data_length = + (mrioc->facts.io_throttle_data_length * 2 * 4); + else + /* set the length to 1MB + 1K to disable throttle */ + mrioc->io_throttle_data_length = MPI3MR_MAX_SECTORS + 2; + + mrioc->io_throttle_high = (mrioc->facts.io_throttle_high * 2 * 1024); + mrioc->io_throttle_low = (mrioc->facts.io_throttle_low * 2 * 1024); + ioc_info(mrioc, "ioc_num(%d), maxopQ(%d), maxopRepQ(%d), maxdh(%d),", mrioc->facts.ioc_num, mrioc->facts.max_op_req_q, mrioc->facts.max_op_reply_q, mrioc->facts.max_devhandle); @@ -2798,6 +2819,13 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x\n", mrioc->facts.dma_mask, (facts_flags & MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK)); + ioc_info(mrioc, + "max_dev_per_throttle_group(%d), max_throttle_groups(%d)\n", + mrioc->facts.max_dev_per_tg, mrioc->facts.max_io_throttle_group); + ioc_info(mrioc, + "io_throttle_data_len(%dKiB), io_throttle_high(%dMiB), io_throttle_low(%dMiB)\n", + mrioc->facts.io_throttle_data_length * 4, + mrioc->facts.io_throttle_high, mrioc->facts.io_throttle_low); } /** @@ -3666,6 +3694,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc) int retval = 0; u8 retry = 0; struct mpi3_ioc_facts_data facts_data; + u32 sz; retry_init: retval = mpi3mr_bring_ioc_ready(mrioc); @@ -3691,6 +3720,9 @@ retry_init: mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD; + mrioc->num_io_throttle_group = mrioc->facts.max_io_throttle_group; + atomic_set(&mrioc->pend_large_data_sz, 0); + if (reset_devices) mrioc->max_host_ios = min_t(int, mrioc->max_host_ios, MPI3MR_HOST_IOS_KDUMP); @@ -3760,6 +3792,15 @@ retry_init: } } + if (!mrioc->throttle_groups && mrioc->num_io_throttle_group) { + dprint_init(mrioc, "allocating memory for throttle groups\n"); + sz = sizeof(struct mpi3mr_throttle_group_info); + mrioc->throttle_groups = (struct mpi3mr_throttle_group_info *) + kcalloc(mrioc->num_io_throttle_group, sz, GFP_KERNEL); + if (!mrioc->throttle_groups) + goto out_failed_noretry; + } + retval = mpi3mr_enable_events(mrioc); if (retval) { ioc_err(mrioc, "failed to enable events %d\n", @@ -3981,6 +4022,7 @@ static void mpi3mr_memset_op_req_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx) void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) { u16 i; + struct mpi3mr_throttle_group_info *tg; mrioc->change_count = 0; mrioc->active_poll_qcount = 0; @@ -4029,6 +4071,22 @@ void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) spin_lock_init(&mrioc->req_qinfo[i].q_lock); mpi3mr_memset_op_req_q_buffers(mrioc, i); } + + atomic_set(&mrioc->pend_large_data_sz, 0); + if (mrioc->throttle_groups) { + tg = mrioc->throttle_groups; + for (i = 0; i < mrioc->num_io_throttle_group; i++, tg++) { + tg->id = 0; + tg->fw_qd = 0; + tg->modified_qd = 0; + tg->io_divert = 0; + tg->need_qd_reduction = 0; + tg->high = 0; + tg->low = 0; + tg->qd_reduction = 0; + atomic_set(&tg->pend_large_data_sz, 0); + } + } } /** @@ -4663,6 +4721,15 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc, ioc_err(mrioc, "Failed to issue soft reset to the ioc\n"); goto out; } + if (mrioc->num_io_throttle_group != + mrioc->facts.max_io_throttle_group) { + ioc_err(mrioc, + "max io throttle group doesn't match old(%d), new(%d)\n", + mrioc->num_io_throttle_group, + mrioc->facts.max_io_throttle_group); + retval = -EPERM; + goto out; + } mpi3mr_flush_delayed_cmd_lists(mrioc); mpi3mr_flush_drv_cmds(mrioc); diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index 80164262af58..bfa1165e23b6 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -38,6 +38,8 @@ MODULE_PARM_DESC(logging_level, static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx); +#define MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION (0xFFFF) + /** * mpi3mr_host_tag_for_scmd - Get host tag for a scmd * @mrioc: Adapter instance reference @@ -355,6 +357,50 @@ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) } /** + * mpi3mr_queue_qd_reduction_event - Queue TG QD reduction event + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * + * Accessor to queue on synthetically generated driver event to + * the event worker thread, the driver event will be used to + * reduce the QD of all VDs in the TG from the worker thread. + * + * Return: None. + */ +static void mpi3mr_queue_qd_reduction_event(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg) +{ + struct mpi3mr_fwevt *fwevt; + u16 sz = sizeof(struct mpi3mr_throttle_group_info *); + + /* + * If the QD reduction event is already queued due to throttle and if + * the QD is not restored through device info change event + * then dont queue further reduction events + */ + if (tg->fw_qd != tg->modified_qd) + return; + + fwevt = mpi3mr_alloc_fwevt(sz); + if (!fwevt) { + ioc_warn(mrioc, "failed to queue TG QD reduction event\n"); + return; + } + *(struct mpi3mr_throttle_group_info **)fwevt->event_data = tg; + fwevt->mrioc = mrioc; + fwevt->event_id = MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION; + fwevt->send_ack = 0; + fwevt->process_evt = 1; + fwevt->evt_ctx = 0; + fwevt->event_data_size = sz; + tg->modified_qd = max_t(u16, (tg->fw_qd * tg->qd_reduction) / 10, 8); + + dprint_event_bh(mrioc, "qd reduction event queued for tg_id(%d)\n", + tg->id); + mpi3mr_fwevt_add_to_list(mrioc, fwevt); +} + +/** * mpi3mr_invalidate_devhandles -Invalidate device handles * @mrioc: Adapter instance reference * @@ -373,6 +419,9 @@ void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc) if (tgtdev->starget && tgtdev->starget->hostdata) { tgt_priv = tgtdev->starget->hostdata; tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + tgt_priv->io_throttle_enabled = 0; + tgt_priv->io_divert = 0; + tgt_priv->throttle_group = NULL; } } } @@ -710,6 +759,35 @@ static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv( } /** + * mpi3mr_set_io_divert_for_all_vd_in_tg -set divert for TG VDs + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * @divert_value: 1 or 0 + * + * Accessor to set io_divert flag for each device associated + * with the given throttle group with the given value. + * + * Return: None. + */ +static void mpi3mr_set_io_divert_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg, u8 divert_value) +{ + unsigned long flags; + struct mpi3mr_tgt_dev *tgtdev; + struct mpi3mr_stgt_priv_data *tgt_priv; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + if (tgt_priv->throttle_group == tg) + tgt_priv->io_divert = divert_value; + } + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + +/** * mpi3mr_print_device_event_notice - print notice related to post processing of * device event after controller reset. * @@ -840,6 +918,7 @@ static int mpi3mr_change_queue_depth(struct scsi_device *sdev, else if (!q_depth) q_depth = MPI3MR_DEFAULT_SDEV_QD; retval = scsi_change_queue_depth(sdev, q_depth); + sdev->max_queue_depth = sdev->queue_depth; return retval; } @@ -926,6 +1005,7 @@ void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) * @mrioc: Adapter instance reference * @tgtdev: Target device internal structure * @dev_pg0: New device page0 + * @is_added: Flag to indicate the device is just added * * Update the information from the device page0 into the driver * cached target device structure. @@ -933,10 +1013,11 @@ void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) * Return: Nothing. */ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, - struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0) + struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0, + bool is_added) { u16 flags = 0; - struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; u8 prot_mask = 0; tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id); @@ -951,12 +1032,19 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, flags = le16_to_cpu(dev_pg0->flags); tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN); + if (is_added == true) + tgtdev->io_throttle_enabled = + (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0; + + if (tgtdev->starget && tgtdev->starget->hostdata) { scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) tgtdev->starget->hostdata; scsi_tgt_priv_data->perst_id = tgtdev->perst_id; scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle; scsi_tgt_priv_data->dev_type = tgtdev->dev_type; + scsi_tgt_priv_data->io_throttle_enabled = + tgtdev->io_throttle_enabled; } switch (dev_pg0->access_status) { @@ -1034,10 +1122,32 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, { struct mpi3_device0_vd_format *vdinf = &dev_pg0->device_specific.vd_format; + struct mpi3mr_throttle_group_info *tg = NULL; + u16 vdinf_io_throttle_group = + le16_to_cpu(vdinf->io_throttle_group); - tgtdev->dev_spec.vol_inf.state = vdinf->vd_state; + tgtdev->dev_spec.vd_inf.state = vdinf->vd_state; if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE) tgtdev->is_hidden = 1; + tgtdev->dev_spec.vd_inf.tg_id = vdinf_io_throttle_group; + tgtdev->dev_spec.vd_inf.tg_high = + le16_to_cpu(vdinf->io_throttle_group_high) * 2048; + tgtdev->dev_spec.vd_inf.tg_low = + le16_to_cpu(vdinf->io_throttle_group_low) * 2048; + if (vdinf_io_throttle_group < mrioc->num_io_throttle_group) { + tg = mrioc->throttle_groups + vdinf_io_throttle_group; + tg->id = vdinf_io_throttle_group; + tg->high = tgtdev->dev_spec.vd_inf.tg_high; + tg->low = tgtdev->dev_spec.vd_inf.tg_low; + tg->qd_reduction = + tgtdev->dev_spec.vd_inf.tg_qd_reduction; + if (is_added == true) + tg->fw_qd = tgtdev->q_depth; + tg->modified_qd = tgtdev->q_depth; + } + tgtdev->dev_spec.vd_inf.tg = tg; + if (scsi_tgt_priv_data) + scsi_tgt_priv_data->throttle_group = tg; break; } default: @@ -1134,7 +1244,7 @@ static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle); if (!tgtdev) goto out; - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, false); if (!tgtdev->is_hidden && !tgtdev->host_exposed) mpi3mr_report_tgtdev_to_host(mrioc, perst_id); if (tgtdev->is_hidden && tgtdev->host_exposed) @@ -1428,6 +1538,60 @@ static void mpi3mr_logdata_evt_bh(struct mpi3mr_ioc *mrioc, } /** + * mpi3mr_update_sdev_qd - Update SCSI device queue depath + * @sdev: SCSI device reference + * @data: Queue depth reference + * + * This is an iterator function called for each SCSI device in a + * target to update the QD of each SCSI device. + * + * Return: Nothing. + */ +static void mpi3mr_update_sdev_qd(struct scsi_device *sdev, void *data) +{ + u16 *q_depth = (u16 *)data; + + scsi_change_queue_depth(sdev, (int)*q_depth); + sdev->max_queue_depth = sdev->queue_depth; +} + +/** + * mpi3mr_set_qd_for_all_vd_in_tg -set QD for TG VDs + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * + * Accessor to reduce QD for each device associated with the + * given throttle group. + * + * Return: None. + */ +static void mpi3mr_set_qd_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg) +{ + unsigned long flags; + struct mpi3mr_tgt_dev *tgtdev; + struct mpi3mr_stgt_priv_data *tgt_priv; + + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + if (tgt_priv->throttle_group == tg) { + dprint_event_bh(mrioc, + "updating qd due to throttling for persist_id(%d) original_qd(%d), reduced_qd (%d)\n", + tgt_priv->perst_id, tgtdev->q_depth, + tg->modified_qd); + starget_for_each_device(tgtdev->starget, + (void *)&tg->modified_qd, + mpi3mr_update_sdev_qd); + } + } + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + +/** * mpi3mr_fwevt_bh - Firmware event bottomhalf handler * @mrioc: Adapter instance reference * @fwevt: Firmware event reference @@ -1484,6 +1648,20 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, mpi3mr_logdata_evt_bh(mrioc, fwevt); break; } + case MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION: + { + struct mpi3mr_throttle_group_info *tg; + + tg = *(struct mpi3mr_throttle_group_info **)fwevt->event_data; + dprint_event_bh(mrioc, + "qd reduction event processed for tg_id(%d) reduction_needed(%d)\n", + tg->id, tg->need_qd_reduction); + if (tg->need_qd_reduction) { + mpi3mr_set_qd_for_all_vd_in_tg(mrioc, tg); + tg->need_qd_reduction = 0; + } + break; + } default: break; } @@ -1540,13 +1718,13 @@ static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc, perst_id = le16_to_cpu(dev_pg0->persistent_id); tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id); if (tgtdev) { - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true); mpi3mr_tgtdev_put(tgtdev); } else { tgtdev = mpi3mr_alloc_tgtdev(); if (!tgtdev) return -ENOMEM; - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true); mpi3mr_tgtdev_add_to_list(mrioc, tgtdev); } @@ -2558,6 +2736,11 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, u32 xfer_count = 0, sense_count = 0, resp_data = 0; u16 dev_handle = 0xFFFF; struct scsi_sense_hdr sshdr; + struct mpi3mr_stgt_priv_data *stgt_priv_data = NULL; + struct mpi3mr_sdev_priv_data *sdev_priv_data = NULL; + u32 ioc_pend_data_len = 0, tg_pend_data_len = 0, data_len_blks = 0; + struct mpi3mr_throttle_group_info *tg = NULL; + u8 throttle_enabled_dev = 0; *reply_dma = 0; reply_desc_type = le16_to_cpu(reply_desc->reply_flags) & @@ -2614,6 +2797,51 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, goto out; } priv = scsi_cmd_priv(scmd); + + data_len_blks = scsi_bufflen(scmd) >> 9; + sdev_priv_data = scmd->device->hostdata; + if (sdev_priv_data) { + stgt_priv_data = sdev_priv_data->tgt_priv_data; + if (stgt_priv_data) { + tg = stgt_priv_data->throttle_group; + throttle_enabled_dev = + stgt_priv_data->io_throttle_enabled; + } + } + if (unlikely((data_len_blks >= mrioc->io_throttle_data_length) && + throttle_enabled_dev)) { + ioc_pend_data_len = atomic_sub_return(data_len_blks, + &mrioc->pend_large_data_sz); + if (tg) { + tg_pend_data_len = atomic_sub_return(data_len_blks, + &tg->pend_large_data_sz); + if (tg->io_divert && ((ioc_pend_data_len <= + mrioc->io_throttle_low) && + (tg_pend_data_len <= tg->low))) { + tg->io_divert = 0; + mpi3mr_set_io_divert_for_all_vd_in_tg( + mrioc, tg, 0); + } + } else { + if (ioc_pend_data_len <= mrioc->io_throttle_low) + stgt_priv_data->io_divert = 0; + } + } else if (unlikely((stgt_priv_data && stgt_priv_data->io_divert))) { + ioc_pend_data_len = atomic_read(&mrioc->pend_large_data_sz); + if (!tg) { + if (ioc_pend_data_len <= mrioc->io_throttle_low) + stgt_priv_data->io_divert = 0; + + } else if (ioc_pend_data_len <= mrioc->io_throttle_low) { + tg_pend_data_len = atomic_read(&tg->pend_large_data_sz); + if (tg->io_divert && (tg_pend_data_len <= tg->low)) { + tg->io_divert = 0; + mpi3mr_set_io_divert_for_all_vd_in_tg( + mrioc, tg, 0); + } + } + } + if (success_desc) { scmd->result = DID_OK << 16; goto out_success; @@ -3834,6 +4062,11 @@ static int mpi3mr_target_alloc(struct scsi_target *starget) tgt_dev->starget = starget; atomic_set(&scsi_tgt_priv_data->block_io, 0); retval = 0; + scsi_tgt_priv_data->io_throttle_enabled = + tgt_dev->io_throttle_enabled; + if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_VD) + scsi_tgt_priv_data->throttle_group = + tgt_dev->dev_spec.vd_inf.tg; } else retval = -ENXIO; spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); @@ -3989,10 +4222,13 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost, int retval = 0; u16 dev_handle; u16 host_tag; - u32 scsiio_flags = 0; + u32 scsiio_flags = 0, data_len_blks = 0; struct request *rq = scsi_cmd_to_rq(scmd); int iprio_class; u8 is_pcie_dev = 0; + u32 tracked_io_sz = 0; + u32 ioc_pend_data_len = 0, tg_pend_data_len = 0; + struct mpi3mr_throttle_group_info *tg = NULL; if (mrioc->unrecoverable) { scmd->result = DID_ERROR << 16; @@ -4096,11 +4332,50 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost, goto out; } op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx]; + data_len_blks = scsi_bufflen(scmd) >> 9; + if ((data_len_blks >= mrioc->io_throttle_data_length) && + stgt_priv_data->io_throttle_enabled) { + tracked_io_sz = data_len_blks; + tg = stgt_priv_data->throttle_group; + if (tg) { + ioc_pend_data_len = atomic_add_return(data_len_blks, + &mrioc->pend_large_data_sz); + tg_pend_data_len = atomic_add_return(data_len_blks, + &tg->pend_large_data_sz); + if (!tg->io_divert && ((ioc_pend_data_len >= + mrioc->io_throttle_high) || + (tg_pend_data_len >= tg->high))) { + tg->io_divert = 1; + tg->need_qd_reduction = 1; + mpi3mr_set_io_divert_for_all_vd_in_tg(mrioc, + tg, 1); + mpi3mr_queue_qd_reduction_event(mrioc, tg); + } + } else { + ioc_pend_data_len = atomic_add_return(data_len_blks, + &mrioc->pend_large_data_sz); + if (ioc_pend_data_len >= mrioc->io_throttle_high) + stgt_priv_data->io_divert = 1; + } + } + + if (stgt_priv_data->io_divert) { + scsiio_req->msg_flags |= + MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE; + scsiio_flags |= MPI3_SCSIIO_FLAGS_DIVERT_REASON_IO_THROTTLING; + } + scsiio_req->flags = cpu_to_le32(scsiio_flags); if (mpi3mr_op_request_post(mrioc, op_req_q, scmd_priv_data->mpi3mr_scsiio_req)) { mpi3mr_clear_scmd_priv(mrioc, scmd); retval = SCSI_MLQUEUE_HOST_BUSY; + if (tracked_io_sz) { + atomic_sub(tracked_io_sz, &mrioc->pend_large_data_sz); + if (tg) + atomic_sub(tracked_io_sz, + &tg->pend_large_data_sz); + } goto out; } diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index e368f038ff5c..baf4da7bb3b4 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1004,8 +1004,10 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { int data_direction, payload_length; + struct iscsi_ecdb_ahdr *ecdb_ahdr; struct iscsi_scsi_req *hdr; int iscsi_task_attr; + unsigned char *cdb; int sam_task_attr; atomic_long_inc(&conn->sess->cmd_pdus); @@ -1106,6 +1108,27 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, ISCSI_REASON_BOOKMARK_INVALID, buf); } + cdb = hdr->cdb; + + if (hdr->hlength) { + ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1); + if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) { + pr_err("Additional Header Segment type %d not supported!\n", + ecdb_ahdr->ahstype); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_CMD_NOT_SUPPORTED, buf); + } + + cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15, + GFP_KERNEL); + if (cdb == NULL) + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE); + memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb, + be16_to_cpu(ecdb_ahdr->ahslength) - 1); + } + data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE : DMA_NONE; @@ -1153,9 +1176,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_datain_req *dr; dr = iscsit_allocate_datain_req(); - if (!dr) + if (!dr) { + if (cdb != hdr->cdb) + kfree(cdb); return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + } iscsit_attach_datain_req(cmd, dr); } @@ -1176,9 +1202,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, target_get_sess_cmd(&cmd->se_cmd, true); cmd->se_cmd.tag = (__force u32)cmd->init_task_tag; - cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, hdr->cdb, + cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, cdb, GFP_KERNEL); + if (cdb != hdr->cdb) + kfree(cdb); + if (cmd->sense_reason) { if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) { return iscsit_add_reject_cmd(cmd, @@ -4036,8 +4065,9 @@ static bool iscsi_target_check_conn_state(struct iscsit_conn *conn) static void iscsit_get_rx_pdu(struct iscsit_conn *conn) { int ret; - u8 *buffer, opcode; + u8 *buffer, *tmp_buf, opcode; u32 checksum = 0, digest = 0; + struct iscsi_hdr *hdr; struct kvec iov; buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL); @@ -4062,6 +4092,25 @@ static void iscsit_get_rx_pdu(struct iscsit_conn *conn) break; } + hdr = (struct iscsi_hdr *) buffer; + if (hdr->hlength) { + iov.iov_len = hdr->hlength * 4; + tmp_buf = krealloc(buffer, + ISCSI_HDR_LEN + iov.iov_len, + GFP_KERNEL); + if (!tmp_buf) + break; + + buffer = tmp_buf; + iov.iov_base = &buffer[ISCSI_HDR_LEN]; + + ret = rx_data(conn, &iov, 1, iov.iov_len); + if (ret != iov.iov_len) { + iscsit_rx_thread_wait_for_tcp(conn); + break; + } + } + if (conn->conn_ops->HeaderDigest) { iov.iov_base = &digest; iov.iov_len = ISCSI_CRC_LEN; @@ -4361,7 +4410,7 @@ int iscsit_close_connection( spin_lock_bh(&sess->conn_lock); atomic_dec(&sess->nconn); - pr_debug("Decremented iSCSI connection count to %hu from node:" + pr_debug("Decremented iSCSI connection count to %d from node:" " %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); /* diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 6e5611d8f51b..c8a248bd11be 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -205,6 +205,38 @@ static struct iscsi_chap *chap_server_open( return chap; } +static const char base64_lookup_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int chap_base64_decode(u8 *dst, const char *src, size_t len) +{ + int i, bits = 0, ac = 0; + const char *p; + u8 *cp = dst; + + for (i = 0; i < len; i++) { + if (src[i] == '=') + return cp - dst; + + p = strchr(base64_lookup_table, src[i]); + if (p == NULL || src[i] == 0) + return -2; + + ac <<= 6; + ac += (p - base64_lookup_table); + bits += 6; + if (bits >= 8) { + *cp++ = (ac >> (bits - 8)) & 0xff; + ac &= ~(BIT(16) - BIT(bits - 8)); + bits -= 8; + } + } + if (ac) + return -1; + + return cp - dst; +} + static int chap_server_compute_hash( struct iscsit_conn *conn, struct iscsi_node_auth *auth, @@ -295,16 +327,27 @@ static int chap_server_compute_hash( pr_err("Could not find CHAP_R.\n"); goto out; } - if (type != HEX) { - pr_err("Could not find CHAP_R.\n"); - goto out; - } - if (strlen(chap_r) != chap->digest_size * 2) { - pr_err("Malformed CHAP_R\n"); - goto out; - } - if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) { - pr_err("Malformed CHAP_R\n"); + + switch (type) { + case HEX: + if (strlen(chap_r) != chap->digest_size * 2) { + pr_err("Malformed CHAP_R\n"); + goto out; + } + if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) { + pr_err("Malformed CHAP_R: invalid HEX\n"); + goto out; + } + break; + case BASE64: + if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != + chap->digest_size) { + pr_err("Malformed CHAP_R: invalid BASE64\n"); + goto out; + } + break; + default: + pr_err("Could not find CHAP_R\n"); goto out; } @@ -373,7 +416,13 @@ static int chap_server_compute_hash( /* * Get CHAP_I. */ - if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { + ret = extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type); + if (ret == -ENOENT) { + pr_debug("Could not find CHAP_I. Initiator uses One way authentication.\n"); + auth_ret = 0; + goto out; + } + if (ret < 0) { pr_err("Could not find CHAP_I.\n"); goto out; } @@ -404,23 +453,46 @@ static int chap_server_compute_hash( goto out; } - if (type != HEX) { + switch (type) { + case HEX: + initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2); + if (!initiatorchg_len) { + pr_err("Unable to convert incoming challenge\n"); + goto out; + } + if (initiatorchg_len > 1024) { + pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); + goto out; + } + + if (hex2bin(initiatorchg_binhex, initiatorchg, + initiatorchg_len) < 0) { + pr_err("Malformed CHAP_C: invalid HEX\n"); + goto out; + } + break; + case BASE64: + initiatorchg_len = chap_base64_decode(initiatorchg_binhex, + initiatorchg, + strlen(initiatorchg)); + if (initiatorchg_len < 0) { + pr_err("Malformed CHAP_C: invalid BASE64\n"); + goto out; + } + if (!initiatorchg_len) { + pr_err("Unable to convert incoming challenge\n"); + goto out; + } + if (initiatorchg_len > 1024) { + pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); + goto out; + } + break; + default: pr_err("Could not find CHAP_C.\n"); goto out; } - initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2); - if (!initiatorchg_len) { - pr_err("Unable to convert incoming challenge\n"); - goto out; - } - if (initiatorchg_len > 1024) { - pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); - goto out; - } - if (hex2bin(initiatorchg_binhex, initiatorchg, initiatorchg_len) < 0) { - pr_err("Malformed CHAP_C\n"); - goto out; - } + pr_debug("[server] Got CHAP_C=%s\n", initiatorchg); /* * During mutual authentication, the CHAP_C generated by the diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 6b94eecc4790..27e448c2d066 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -341,6 +341,7 @@ static int iscsi_login_zero_tsih_s2( { struct iscsi_node_attrib *na; struct iscsit_session *sess = conn->sess; + struct iscsi_param *param; bool iser = false; sess->tpg = conn->tpg; @@ -375,6 +376,18 @@ static int iscsi_login_zero_tsih_s2( na = iscsit_tpg_get_node_attrib(sess); /* + * If ACL allows non-authorized access in TPG with CHAP, + * then set None to AuthMethod. + */ + param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); + if (param && !strstr(param->value, NONE)) { + if (!iscsi_conn_auth_required(conn)) + if (iscsi_change_param_sprintf(conn, "AuthMethod=%s", + NONE)) + return -1; + } + + /* * Need to send TargetPortalGroupTag back in first login response * on any iSCSI connection where the Initiator provides TargetName. * See 5.3.1. Login Phase Start @@ -715,7 +728,7 @@ void iscsi_post_login_handler( list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); - pr_debug("Incremented iSCSI Connection count to %hu" + pr_debug("Incremented iSCSI Connection count to %d" " from node: %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); @@ -763,7 +776,7 @@ void iscsi_post_login_handler( spin_lock_bh(&sess->conn_lock); list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); - pr_debug("Incremented iSCSI Connection count to %hu from node:" + pr_debug("Incremented iSCSI Connection count to %d from node:" " %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index fb93a1173954..f2919319ad38 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -62,31 +62,34 @@ int extract_param( int len; if (!in_buf || !pattern || !out_buf || !type) - return -1; + return -EINVAL; ptr = strstr(in_buf, pattern); if (!ptr) - return -1; + return -ENOENT; ptr = strstr(ptr, "="); if (!ptr) - return -1; + return -EINVAL; ptr += 1; if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) { ptr += 2; /* skip 0x */ *type = HEX; + } else if (*ptr == '0' && (*(ptr+1) == 'b' || *(ptr+1) == 'B')) { + ptr += 2; /* skip 0b */ + *type = BASE64; } else *type = DECIMAL; len = strlen_semi(ptr); if (len < 0) - return -1; + return -EINVAL; if (len >= max_length) { pr_err("Length of input: %d exceeds max_length:" " %d\n", len, max_length); - return -1; + return -EINVAL; } memcpy(out_buf, ptr, len); out_buf[len] = '\0'; @@ -811,7 +814,7 @@ static int iscsi_target_do_authentication( return 0; } -static bool iscsi_conn_auth_required(struct iscsit_conn *conn) +bool iscsi_conn_auth_required(struct iscsit_conn *conn) { struct iscsi_node_acl *nacl; struct se_node_acl *se_nacl; diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h index ed30b9ee75e6..41c3db3ddeaa 100644 --- a/drivers/target/iscsi/iscsi_target_nego.h +++ b/drivers/target/iscsi/iscsi_target_nego.h @@ -4,6 +4,7 @@ #define DECIMAL 0 #define HEX 1 +#define BASE64 2 struct iscsit_conn; struct iscsi_login; @@ -21,5 +22,5 @@ extern int iscsi_target_locate_portal(struct iscsi_np *, struct iscsit_conn *, extern int iscsi_target_start_negotiation( struct iscsi_login *, struct iscsit_conn *); extern void iscsi_target_nego_release(struct iscsit_conn *); - +extern bool iscsi_conn_auth_required(struct iscsit_conn *conn); #endif /* ISCSI_TARGET_NEGO_H */ diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index f6132836eb38..1e3216de1e04 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -345,68 +345,6 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char flags, struct sbc_ops *op return 0; } -static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success, - int *post_ret) -{ - unsigned char *buf, *addr; - struct scatterlist *sg; - unsigned int offset; - sense_reason_t ret = TCM_NO_SENSE; - int i, count; - - if (!success) - return 0; - - /* - * From sbc3r22.pdf section 5.48 XDWRITEREAD (10) command - * - * 1) read the specified logical block(s); - * 2) transfer logical blocks from the data-out buffer; - * 3) XOR the logical blocks transferred from the data-out buffer with - * the logical blocks read, storing the resulting XOR data in a buffer; - * 4) if the DISABLE WRITE bit is set to zero, then write the logical - * blocks transferred from the data-out buffer; and - * 5) transfer the resulting XOR data to the data-in buffer. - */ - buf = kmalloc(cmd->data_length, GFP_KERNEL); - if (!buf) { - pr_err("Unable to allocate xor_callback buf\n"); - return TCM_OUT_OF_RESOURCES; - } - /* - * Copy the scatterlist WRITE buffer located at cmd->t_data_sg - * into the locally allocated *buf - */ - sg_copy_to_buffer(cmd->t_data_sg, - cmd->t_data_nents, - buf, - cmd->data_length); - - /* - * Now perform the XOR against the BIDI read memory located at - * cmd->t_mem_bidi_list - */ - - offset = 0; - for_each_sg(cmd->t_bidi_data_sg, sg, cmd->t_bidi_data_nents, count) { - addr = kmap_atomic(sg_page(sg)); - if (!addr) { - ret = TCM_OUT_OF_RESOURCES; - goto out; - } - - for (i = 0; i < sg->length; i++) - *(addr + sg->offset + i) ^= *(buf + offset + i); - - offset += sg->length; - kunmap_atomic(addr); - } - -out: - kfree(buf); - return ret; -} - static sense_reason_t sbc_execute_rw(struct se_cmd *cmd) { @@ -933,47 +871,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; cmd->execute_cmd = sbc_execute_rw; break; - case XDWRITEREAD_10: - if (cmd->data_direction != DMA_TO_DEVICE || - !(cmd->se_cmd_flags & SCF_BIDI)) - return TCM_INVALID_CDB_FIELD; - sectors = transport_get_sectors_10(cdb); - - if (sbc_check_dpofua(dev, cmd, cdb)) - return TCM_INVALID_CDB_FIELD; - - cmd->t_task_lba = transport_lba_32(cdb); - cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; - - /* - * Setup BIDI XOR callback to be run after I/O completion. - */ - cmd->execute_cmd = sbc_execute_rw; - cmd->transport_complete_callback = &xdreadwrite_callback; - break; case VARIABLE_LENGTH_CMD: { u16 service_action = get_unaligned_be16(&cdb[8]); switch (service_action) { - case XDWRITEREAD_32: - sectors = transport_get_sectors_32(cdb); - - if (sbc_check_dpofua(dev, cmd, cdb)) - return TCM_INVALID_CDB_FIELD; - /* - * Use WRITE_32 and READ_32 opcodes for the emulated - * XDWRITE_READ_32 logic. - */ - cmd->t_task_lba = transport_lba_64_ext(cdb); - cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; - - /* - * Setup BIDI XOR callback to be run during after I/O - * completion. - */ - cmd->execute_cmd = sbc_execute_rw; - cmd->transport_complete_callback = &xdreadwrite_callback; - break; case WRITE_SAME_32: sectors = transport_get_sectors_32(cdb); if (!sectors) { diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d142d68baf0e..3bc0709a5dc2 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -8566,6 +8566,19 @@ out: return ret; } +static enum ufs_ref_clk_freq ufshcd_parse_ref_clk_property(struct ufs_hba *hba) +{ + u32 freq; + int ret = device_property_read_u32(hba->dev, "ref-clk-freq", &freq); + + if (ret) { + dev_dbg(hba->dev, "Cannot query 'ref-clk-freq' property = %d", ret); + return REF_CLK_FREQ_INVAL; + } + + return ufs_get_bref_clk_from_hz(freq); +} + static int ufshcd_init_clocks(struct ufs_hba *hba) { int ret = 0; @@ -8659,6 +8672,9 @@ static int ufshcd_hba_init(struct ufs_hba *hba) if (err) goto out_disable_hba_vreg; + if (hba->dev_ref_clk_freq == REF_CLK_FREQ_INVAL) + hba->dev_ref_clk_freq = ufshcd_parse_ref_clk_property(hba); + err = ufshcd_setup_clocks(hba, true); if (err) goto out_disable_hba_vreg; |