diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index d4140dc6c5ba..f3de8c521bbd 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -1685,3 +1685,86 @@ Description: ================ ======================================== The file is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/analysis_trigger +What: /sys/bus/platform/devices/*.ufs/hid/analysis_trigger +Date: May 2025 +Contact: Huan Tang +Description: + The host can enable or disable HID analysis operation. + + ======= ========================================= + disable disable HID analysis operation + enable enable HID analysis operation + ======= ========================================= + + The file is write only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/defrag_trigger +What: /sys/bus/platform/devices/*.ufs/hid/defrag_trigger +Date: May 2025 +Contact: Huan Tang +Description: + The host can enable or disable HID defragmentation operation. + + ======= ========================================= + disable disable HID defragmentation operation + enable enable HID defragmentation operation + ======= ========================================= + + The attribute is write only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/fragmented_size +What: /sys/bus/platform/devices/*.ufs/hid/fragmented_size +Date: May 2025 +Contact: Huan Tang +Description: + The total fragmented size in the device is reported through + this attribute. + + The attribute is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/defrag_size +What: /sys/bus/platform/devices/*.ufs/hid/defrag_size +Date: May 2025 +Contact: Huan Tang +Description: + The host sets the size to be defragmented by an HID + defragmentation operation. + + The attribute is read/write. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/progress_ratio +What: /sys/bus/platform/devices/*.ufs/hid/progress_ratio +Date: May 2025 +Contact: Huan Tang +Description: + Defragmentation progress is reported by this attribute, + indicates the ratio of the completed defragmentation size + over the requested defragmentation size. + + ==== ============================================ + 1 1% + ... + 100 100% + ==== ============================================ + + The attribute is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/hid/state +What: /sys/bus/platform/devices/*.ufs/hid/state +Date: May 2025 +Contact: Huan Tang +Description: + The HID state is reported by this attribute. + + ==================== =========================== + idle Idle (analysis required) + analysis_in_progress Analysis in progress + defrag_required Defrag required + defrag_in_progress Defrag in progress + defrag_completed Defrag completed + defrag_not_required Defrag is not required + ==================== =========================== + + The attribute is read only. diff --git a/Documentation/scsi/scsi_fc_transport.rst b/Documentation/scsi/scsi_fc_transport.rst index e3ddcfb7f8fd..5ef75575924e 100644 --- a/Documentation/scsi/scsi_fc_transport.rst +++ b/Documentation/scsi/scsi_fc_transport.rst @@ -30,7 +30,40 @@ This file is found at Documentation/scsi/scsi_fc_transport.rst FC Remote Ports (rports) ======================== -<< To Be Supplied >> + + In the Fibre Channel (FC) subsystem, a remote port (rport) refers to a + remote Fibre Channel node that the local port can communicate with. + These are typically storage targets (e.g., arrays, tapes) that respond + to SCSI commands over FC transport. + + In Linux, rports are managed by the FC transport class and are + represented in sysfs under: + + /sys/class/fc_remote_ports/ + + Each rport directory contains attributes describing the remote port, + such as port ID, node name, port state, and link speed. + + rports are typically created by the FC transport when a new device is + discovered during a fabric login or scan, and they persist until the + device is removed or the link is lost. + + Common attributes: + - node_name: World Wide Node Name (WWNN). + - port_name: World Wide Port Name (WWPN). + - port_id: FC address of the remote port. + - roles: Indicates if the port is an initiator, target, or both. + - port_state: Shows the current operational state. + + After discovering a remote port, the driver typically populates a + fc_rport_identifiers structure and invokes fc_remote_port_add() to + create and register the remote port with the SCSI subsystem via the + Fibre Channel (FC) transport class. + + rports are also visible via sysfs as children of the FC host adapter. + + For developers: use fc_remote_port_add() and fc_remote_port_delete() when + implementing a driver that interacts with the FC transport class. FC Virtual Ports (vports) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index b911fdb387f3..4912087de10d 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1299,26 +1299,6 @@ static void fcoe_thread_cleanup_local(unsigned int cpu) flush_work(&p->work); } -/** - * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming - * command. - * - * This routine selects next CPU based on cpumask to distribute - * incoming requests in round robin. - * - * Returns: int CPU number - */ -static inline unsigned int fcoe_select_cpu(void) -{ - static unsigned int selected_cpu; - - selected_cpu = cpumask_next(selected_cpu, cpu_online_mask); - if (selected_cpu >= nr_cpu_ids) - selected_cpu = cpumask_first(cpu_online_mask); - - return selected_cpu; -} - /** * fcoe_rcv() - Receive packets from a net device * @skb: The received packet @@ -1405,7 +1385,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; else { if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN) - cpu = fcoe_select_cpu(); + cpu = skb->alloc_cpu; else cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 24cd172905f3..4431698a5d78 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2401,7 +2401,7 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, slot_err_v2_hw(hisi_hba, task, slot, 2); if (ts->stat != SAS_DATA_UNDERRUN) - dev_info(dev, "erroneous completion iptt=%d task=%pK dev id=%d CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n", + dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n", slot->idx, task, sas_dev->device_id, complete_hdr->dw0, complete_hdr->dw1, complete_hdr->act, complete_hdr->dw3, @@ -2467,7 +2467,7 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { spin_unlock_irqrestore(&task->task_state_lock, flags); - dev_info(dev, "slot complete: task(%pK) aborted\n", task); + dev_info(dev, "slot complete: task(%p) aborted\n", task); return; } task->task_state_flags |= SAS_TASK_STATE_DONE; @@ -2478,7 +2478,7 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, spin_lock_irqsave(&device->done_lock, flags); if (test_bit(SAS_HA_FROZEN, &ha->state)) { spin_unlock_irqrestore(&device->done_lock, flags); - dev_info(dev, "slot complete: task(%pK) ignored\n", + dev_info(dev, "slot complete: task(%p) ignored\n", task); return; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index bc5d5356dd00..2f3d61abab3a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2409,7 +2409,7 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, if (slot_err_v3_hw(hisi_hba, task, slot)) { if (ts->stat != SAS_DATA_UNDERRUN) - dev_info(dev, "erroneous completion iptt=%d task=%pK dev id=%d addr=%016llx CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n", + dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d addr=%016llx CQ hdr: 0x%x 0x%x 0x%x 0x%x Error info: 0x%x 0x%x 0x%x 0x%x\n", slot->idx, task, sas_dev->device_id, SAS_ADDR(device->sas_addr), dw0, dw1, complete_hdr->act, dw3, @@ -2470,7 +2470,7 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { spin_unlock_irqrestore(&task->task_state_lock, flags); - dev_info(dev, "slot complete: task(%pK) aborted\n", task); + dev_info(dev, "slot complete: task(%p) aborted\n", task); return; } task->task_state_flags |= SAS_TASK_STATE_DONE; @@ -2481,7 +2481,7 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, spin_lock_irqsave(&device->done_lock, flags); if (test_bit(SAS_HA_FROZEN, &ha->state)) { spin_unlock_irqrestore(&device->done_lock, flags); - dev_info(dev, "slot complete: task(%pK) ignored\n", + dev_info(dev, "slot complete: task(%p) ignored\n", task); return; } diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index ce444efd859e..87983ea4e06e 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -49,6 +49,13 @@ static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, #define MPI3_EVENT_WAIT_FOR_DEVICES_TO_REFRESH (0xFFFE) +/* + * SAS Log info code for a NCQ collateral abort after an NCQ error: + * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR + * See: drivers/message/fusion/lsi/mpi_log_sas.h + */ +#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000 + /** * mpi3mr_host_tag_for_scmd - Get host tag for a scmd * @mrioc: Adapter instance reference @@ -3430,7 +3437,18 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, scmd->result = DID_NO_CONNECT << 16; break; case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED: - scmd->result = DID_SOFT_ERROR << 16; + if (ioc_loginfo == IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR) { + /* + * This is a ATA NCQ command aborted due to another NCQ + * command failure. We must retry this command + * immediately but without incrementing its retry + * counter. + */ + WARN_ON_ONCE(xfer_count != 0); + scmd->result = DID_IMM_RETRY << 16; + } else { + scmd->result = DID_SOFT_ERROR << 16; + } break; case MPI3_IOCSTATUS_SCSI_TASK_TERMINATED: case MPI3_IOCSTATUS_SCSI_EXT_TERMINATED: diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 02fc204b9bf7..3b951589feeb 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -2914,7 +2914,6 @@ int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command) { struct MPT3SAS_ADAPTER *ioc; MPI2RequestHeader_t *mpi_request = NULL, *request; - MPI2DefaultReply_t *mpi_reply; Mpi26MctpPassthroughRequest_t *mctp_passthru_req; u16 smid; unsigned long timeout; @@ -3022,8 +3021,6 @@ int mpt3sas_send_mctp_passthru_req(struct mpt3_passthru_command *command) goto issue_host_reset; } - mpi_reply = ioc->ctl_cmds.reply; - /* copy out xdata to user */ if (data_in_sz) memcpy(command->data_in_buf_ptr, data_in, data_in_sz); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 508861e88d9f..d7d8244dfedc 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -195,6 +195,14 @@ struct sense_info { #define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD) #define MPT3SAS_ABRT_TASK_SET (0xFFFE) #define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) + +/* + * SAS Log info code for a NCQ collateral abort after an NCQ error: + * IOC_LOGINFO_PREFIX_PL | PL_LOGINFO_CODE_SATA_NCQ_FAIL_ALL_CMDS_AFTR_ERR + * See: drivers/message/fusion/lsi/mpi_log_sas.h + */ +#define IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR 0x31080000 + /** * struct fw_event_work - firmware event struct * @list: link list framework @@ -5814,6 +5822,17 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->result = DID_TRANSPORT_DISRUPTED << 16; goto out; } + if (log_info == IOC_LOGINFO_SATA_NCQ_FAIL_AFTER_ERR) { + /* + * This is a ATA NCQ command aborted due to another NCQ + * command failure. We must retry this command + * immediately but without incrementing its retry + * counter. + */ + WARN_ON_ONCE(xfer_cnt != 0); + scmd->result = DID_IMM_RETRY << 16; + break; + } if (log_info == 0x31110630) { if (scmd->retries > 2) { scmd->result = DID_NO_CONNECT << 16; diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 5b373c53c036..c4074f062d93 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -4677,8 +4677,12 @@ pm80xx_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE); payload.sas_identify.phy_id = phy_id; - return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, + ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, sizeof(payload), 0); + if (ret < 0) + pm8001_tag_free(pm8001_ha, tag); + + return ret; } /** @@ -4704,8 +4708,12 @@ static int pm80xx_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, payload.tag = cpu_to_le32(tag); payload.phy_id = cpu_to_le32(phy_id); - return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, + ret = pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, sizeof(payload), 0); + if (ret < 0) + pm8001_tag_free(pm8001_ha, tag); + + return ret; } /* diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 518a252eb6aa..534310224e8f 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -708,20 +708,15 @@ void scsi_cdl_check(struct scsi_device *sdev) int scsi_cdl_enable(struct scsi_device *sdev, bool enable) { char buf[64]; - bool is_ata; int ret; if (!sdev->cdl_supported) return -EOPNOTSUPP; - rcu_read_lock(); - is_ata = rcu_dereference(sdev->vpd_pg89); - rcu_read_unlock(); - /* * For ATA devices, CDL needs to be enabled with a SET FEATURES command. */ - if (is_ata) { + if (sdev->is_ata) { struct scsi_mode_data data; struct scsi_sense_hdr sshdr; char *buf_data; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index aef33d1e346a..0847767d4d43 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -8770,7 +8770,7 @@ static int sdebug_add_store(void) dif_size = sdebug_store_sectors * sizeof(struct t10_pi_tuple); sip->dif_storep = vmalloc(dif_size); - pr_info("dif_storep %u bytes @ %pK\n", dif_size, + pr_info("dif_storep %u bytes @ %p\n", dif_size, sip->dif_storep); if (!sip->dif_storep) { diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 4833b8fe251b..160c2f74c7e7 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -909,7 +909,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->model = (char *) (sdev->inquiry + 16); sdev->rev = (char *) (sdev->inquiry + 32); - if (strncmp(sdev->vendor, "ATA ", 8) == 0) { + sdev->is_ata = strncmp(sdev->vendor, "ATA ", 8) == 0; + if (sdev->is_ata) { /* * sata emulation layer device. This is a hack to work around * the SATL power management specifications which state that diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3f6e87705b62..daddef2e9e87 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3459,19 +3459,14 @@ static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer) } if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, INQUIRY, 0) < 0) { - struct scsi_vpd *vpd; - sdev->no_report_opcodes = 1; - /* Disable WRITE SAME if REPORT SUPPORTED OPERATION - * CODES is unsupported and the device has an ATA - * Information VPD page (SAT). + /* + * Disable WRITE SAME if REPORT SUPPORTED OPERATION CODES is + * unsupported and this is an ATA device. */ - rcu_read_lock(); - vpd = rcu_dereference(sdev->vpd_pg89); - if (vpd) + if (sdev->is_ata) sdev->no_write_same = 1; - rcu_read_unlock(); } if (scsi_report_opcode(sdev, buffer, SD_BUF_SIZE, WRITE_SAME_16, 0) == 1) diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index de8b6acd4058..10006ae5ee35 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -87,6 +87,23 @@ static const char *ufs_wb_resize_status_to_string(enum wb_resize_status status) } } +static const char * const ufs_hid_states[] = { + [HID_IDLE] = "idle", + [ANALYSIS_IN_PROGRESS] = "analysis_in_progress", + [DEFRAG_REQUIRED] = "defrag_required", + [DEFRAG_IN_PROGRESS] = "defrag_in_progress", + [DEFRAG_COMPLETED] = "defrag_completed", + [DEFRAG_NOT_REQUIRED] = "defrag_not_required", +}; + +static const char *ufs_hid_state_to_string(enum ufs_hid_state state) +{ + if (state < NUM_UFS_HID_STATES) + return ufs_hid_states[state]; + + return "unknown"; +} + static const char *ufshcd_uic_link_state_to_string( enum uic_link_state state) { @@ -1763,6 +1780,178 @@ static const struct attribute_group ufs_sysfs_attributes_group = { .attrs = ufs_sysfs_attributes, }; +static int hid_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u32 *attr_val) +{ + int ret; + + down(&hba->host_sem); + if (!ufshcd_is_user_access_allowed(hba)) { + up(&hba->host_sem); + return -EBUSY; + } + + ufshcd_rpm_get_sync(hba); + ret = ufshcd_query_attr(hba, opcode, idn, 0, 0, attr_val); + ufshcd_rpm_put_sync(hba); + + up(&hba->host_sem); + return ret; +} + +static ssize_t analysis_trigger_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int mode; + int ret; + + if (sysfs_streq(buf, "enable")) + mode = HID_ANALYSIS_ENABLE; + else if (sysfs_streq(buf, "disable")) + mode = HID_ANALYSIS_AND_DEFRAG_DISABLE; + else + return -EINVAL; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, &mode); + + return ret < 0 ? ret : count; +} + +static DEVICE_ATTR_WO(analysis_trigger); + +static ssize_t defrag_trigger_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int mode; + int ret; + + if (sysfs_streq(buf, "enable")) + mode = HID_ANALYSIS_AND_DEFRAG_ENABLE; + else if (sysfs_streq(buf, "disable")) + mode = HID_ANALYSIS_AND_DEFRAG_DISABLE; + else + return -EINVAL; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, &mode); + + return ret < 0 ? ret : count; +} + +static DEVICE_ATTR_WO(defrag_trigger); + +static ssize_t fragmented_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int ret; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_HID_AVAILABLE_SIZE, &value); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", value); +} + +static DEVICE_ATTR_RO(fragmented_size); + +static ssize_t defrag_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int ret; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_HID_SIZE, &value); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", value); +} + +static ssize_t defrag_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int ret; + + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_HID_SIZE, &value); + + return ret < 0 ? ret : count; +} + +static DEVICE_ATTR_RW(defrag_size); + +static ssize_t progress_ratio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int ret; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_HID_PROGRESS_RATIO, &value); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", value); +} + +static DEVICE_ATTR_RO(progress_ratio); + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + int ret; + + ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_HID_STATE, &value); + if (ret) + return ret; + + return sysfs_emit(buf, "%s\n", ufs_hid_state_to_string(value)); +} + +static DEVICE_ATTR_RO(state); + +static struct attribute *ufs_sysfs_hid[] = { + &dev_attr_analysis_trigger.attr, + &dev_attr_defrag_trigger.attr, + &dev_attr_fragmented_size.attr, + &dev_attr_defrag_size.attr, + &dev_attr_progress_ratio.attr, + &dev_attr_state.attr, + NULL, +}; + +static umode_t ufs_sysfs_hid_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct ufs_hba *hba = dev_get_drvdata(dev); + + return hba->dev_info.hid_sup ? attr->mode : 0; +} + +static const struct attribute_group ufs_sysfs_hid_group = { + .name = "hid", + .attrs = ufs_sysfs_hid, + .is_visible = ufs_sysfs_hid_is_visible, +}; + static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_default_group, &ufs_sysfs_capabilities_group, @@ -1777,6 +1966,7 @@ static const struct attribute_group *ufs_sysfs_groups[] = { &ufs_sysfs_string_descriptors_group, &ufs_sysfs_flags_group, &ufs_sysfs_attributes_group, + &ufs_sysfs_hid_group, NULL, }; diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 4410e7d93b7d..c2048aca09fc 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -2826,8 +2826,6 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, /* Copy the Descriptor */ if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) memcpy(ucd_req_ptr + 1, query->descriptor, len); - - memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); } static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp) @@ -2840,8 +2838,6 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp) .transaction_code = UPIU_TRANSACTION_NOP_OUT, .task_tag = lrbp->task_tag, }; - - memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); } /** @@ -2867,6 +2863,8 @@ static int ufshcd_compose_devman_upiu(struct ufs_hba *hba, else ret = -EINVAL; + memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + return ret; } @@ -8414,6 +8412,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba) dev_info->rtt_cap = desc_buf[DEVICE_DESC_PARAM_RTT_CAP]; + dev_info->hid_sup = get_unaligned_be32(desc_buf + + DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP) & + UFS_DEV_HID_SUPPORT; + model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; err = ufshcd_read_string_desc(hba, model_index, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 68dd49947d04..6d6500148c4b 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -184,6 +184,11 @@ struct scsi_device { */ unsigned force_runtime_start_on_system_start:1; + /* + * Set if the device is an ATA device. + */ + unsigned is_ata:1; + unsigned removable:1; unsigned changed:1; /* Data invalid due to media change */ unsigned busy:1; /* Used to prevent races */ diff --git a/include/trace/events/scsi.h b/include/trace/events/scsi.h index bf6cc98d9122..c36c72ab7f2b 100644 --- a/include/trace/events/scsi.h +++ b/include/trace/events/scsi.h @@ -200,6 +200,14 @@ TRACE_EVENT(scsi_dispatch_cmd_start, __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len)) ); +#define scsi_rtn_name(result) { result, #result } +#define show_rtn_name(val) \ + __print_symbolic(val, \ + scsi_rtn_name(SCSI_MLQUEUE_HOST_BUSY), \ + scsi_rtn_name(SCSI_MLQUEUE_DEVICE_BUSY), \ + scsi_rtn_name(SCSI_MLQUEUE_EH_RETRY), \ + scsi_rtn_name(SCSI_MLQUEUE_TARGET_BUSY)) + TRACE_EVENT(scsi_dispatch_cmd_error, TP_PROTO(struct scsi_cmnd *cmd, int rtn), @@ -240,14 +248,15 @@ TRACE_EVENT(scsi_dispatch_cmd_error, TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u" \ " prot_op=%s driver_tag=%d scheduler_tag=%d cmnd=(%s %s raw=%s)" \ - " rtn=%d", + " rtn=%s", __entry->host_no, __entry->channel, __entry->id, __entry->lun, __entry->data_sglen, __entry->prot_sglen, show_prot_op_name(__entry->prot_op), __entry->driver_tag, __entry->scheduler_tag, show_opcode_name(__entry->opcode), __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len), - __entry->rtn) + show_rtn_name(__entry->rtn) + ) ); DECLARE_EVENT_CLASS(scsi_cmd_done_timeout_template, diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index c0c59a8f7256..72fd385037a6 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -182,6 +182,11 @@ enum attr_idn { QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F, QUERY_ATTR_IDN_TIMESTAMP = 0x30, QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID = 0x34, + QUERY_ATTR_IDN_HID_DEFRAG_OPERATION = 0x35, + QUERY_ATTR_IDN_HID_AVAILABLE_SIZE = 0x36, + QUERY_ATTR_IDN_HID_SIZE = 0x37, + QUERY_ATTR_IDN_HID_PROGRESS_RATIO = 0x38, + QUERY_ATTR_IDN_HID_STATE = 0x39, QUERY_ATTR_IDN_WB_BUF_RESIZE_HINT = 0x3C, QUERY_ATTR_IDN_WB_BUF_RESIZE_EN = 0x3D, QUERY_ATTR_IDN_WB_BUF_RESIZE_STATUS = 0x3E, @@ -401,6 +406,7 @@ enum { UFS_DEV_HPB_SUPPORT = BIT(7), UFS_DEV_WRITE_BOOSTER_SUP = BIT(8), UFS_DEV_LVL_EXCEPTION_SUP = BIT(12), + UFS_DEV_HID_SUPPORT = BIT(13), }; #define UFS_DEV_HPB_SUPPORT_VERSION 0x310 @@ -466,6 +472,24 @@ enum ufs_ref_clk_freq { REF_CLK_FREQ_INVAL = -1, }; +/* bDefragOperation attribute values */ +enum ufs_hid_defrag_operation { + HID_ANALYSIS_AND_DEFRAG_DISABLE = 0, + HID_ANALYSIS_ENABLE = 1, + HID_ANALYSIS_AND_DEFRAG_ENABLE = 2, +}; + +/* bHIDState attribute values */ +enum ufs_hid_state { + HID_IDLE = 0, + ANALYSIS_IN_PROGRESS = 1, + DEFRAG_REQUIRED = 2, + DEFRAG_IN_PROGRESS = 3, + DEFRAG_COMPLETED = 4, + DEFRAG_NOT_REQUIRED = 5, + NUM_UFS_HID_STATES = 6, +}; + /* bWriteBoosterBufferResizeEn attribute */ enum wb_resize_en { WB_RESIZE_EN_IDLE = 0, @@ -625,6 +649,8 @@ struct ufs_dev_info { u32 rtc_update_period; u8 rtt_cap; /* bDeviceRTTCap */ + + bool hid_sup; }; /*