On 2020-08-06 02:11, Daejun Park wrote: > This is a patch for the HPB feature. > This patch adds HPB function calls to UFS core driver. > > The mininum size of the memory pool used in the HPB is implemented as a ^^^^^^^ minimum? > Kconfig parameter (SCSI_UFS_HPB_HOST_MEM), so that it can be configurable. > +config SCSI_UFS_HPB > + bool "Support UFS Host Performance Booster" > + depends on SCSI_UFSHCD > + help > + A UFS HPB Feature improves random read performance. It caches ^ ^^^^^^^ The? feature? > + L2P map of UFS to host DRAM. The driver uses HPB read command > + by piggybacking physical page number for bypassing FTL's L2P address > + translation. Please explain what L2P and FTL mean. Not everyone is familiar with SSD internals. > +config SCSI_UFS_HPB_HOST_MEM > + int "Host-side cached memory size (KB) for HPB support" > + default 32 > + depends on SCSI_UFS_HPB > + help > + The mininum size of the memory pool used in the HPB module. It can > + be configurable by the user. If this value is larger than required > + memory size, kernel resizes cached memory size. ^^^^^^^ ^^^^^^^^^^^^^^^^^^ reduces? cache size? Please make this a kernel module parameter instead of a compile-time constant. > +#ifndef CONFIG_SCSI_UFS_HPB > +static void ufshpb_resume(struct ufs_hba *hba) {} > +static void ufshpb_suspend(struct ufs_hba *hba) {} > +static void ufshpb_reset(struct ufs_hba *hba) {} > +static void ufshpb_reset_host(struct ufs_hba *hba) {} > +static void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {} > +static void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {} > +static void ufshpb_remove(struct ufs_hba *hba) {} > +static void ufshpb_scan_feature(struct ufs_hba *hba) {} > +#endif Please move these definitions into ufshpb.h since that is the recommended Linux kernel coding style. > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h > index b2ef18f1b746..904c19796e09 100644 > --- a/drivers/scsi/ufs/ufshcd.h > +++ b/drivers/scsi/ufs/ufshcd.h > @@ -47,6 +47,9 @@ > #include "ufs.h" > #include "ufs_quirks.h" > #include "ufshci.h" > +#ifdef CONFIG_SCSI_UFS_HPB > +#include "ufshpb.h" > +#endif Please move #ifdef CONFIG_SCSI_UFS_HPB / #endif into ufshpb.h. From Documentation/process/4.Coding.rst: "As a general rule, #ifdef use should be confined to header files whenever possible." > +struct ufsf_feature_info { > + atomic_t slave_conf_cnt; > + wait_queue_head_t sdev_wait; > +}; Please add a comment above this data structure that explains the role of this data structure and also what "ufsf" stands for. > +static int ufshpb_create_sysfs(struct ufs_hba *hba, struct ufshpb_lu *hpb); I don't think that this forward declaration is necessary so please leave it out. > +static inline int ufshpb_is_valid_srgn(struct ufshpb_region *rgn, > + struct ufshpb_subregion *srgn) > +{ > + return rgn->rgn_state != HPB_RGN_INACTIVE && > + srgn->srgn_state == HPB_SRGN_VALID; > +} Please do not declare functions inside .c files inline but instead let the compiler decide which functions to inline. Modern compilers are really good at this. > +static struct kobj_type ufshpb_ktype = { > + .sysfs_ops = &ufshpb_sysfs_ops, > + .release = NULL, > +}; If the release method of a kobj_type is NULL that is a strong sign that there is something wrong ... > +static int ufshpb_create_sysfs(struct ufs_hba *hba, struct ufshpb_lu *hpb) > +{ > + int ret; > + > + ufshpb_stat_init(hpb); > + > + kobject_init(&hpb->kobj, &ufshpb_ktype); > + mutex_init(&hpb->sysfs_lock); > + > + ret = kobject_add(&hpb->kobj, kobject_get(&hba->dev->kobj), > + "ufshpb_lu%d", hpb->lun); > + > + if (ret) > + return ret; > + > + ret = sysfs_create_group(&hpb->kobj, &ufshpb_sysfs_group); > + > + if (ret) { > + dev_err(hba->dev, "ufshpb_lu%d create file error\n", hpb->lun); > + return ret; > + } > + > + dev_info(hba->dev, "ufshpb_lu%d sysfs adds uevent", hpb->lun); > + kobject_uevent(&hpb->kobj, KOBJ_ADD); > + > + return 0; > +} Please attach these sysfs attributes to /sys/class/scsi_device/*/device instead of creating a new kobject. Consider using the following scsi_host_template member to define LUN sysfs attributes: /* * Pointer to the SCSI device attribute groups for this host, * NULL terminated. */ const struct attribute_group **sdev_groups; > +static void ufshpb_scan_hpb_lu(struct ufs_hba *hba, > + struct ufshpb_dev_info *hpb_dev_info, > + u8 *desc_buf) > +{ > + struct scsi_device *sdev; > + struct ufshpb_lu *hpb; > + int find_hpb_lu = 0; > + int ret; > + > + shost_for_each_device(sdev, hba->host) { > + struct ufshpb_lu_info hpb_lu_info = { 0 }; > + int lun = sdev->lun; > + > + if (lun >= hba->dev_info.max_lu_supported) > + continue; > + > + ret = ufshpb_get_lu_info(hba, lun, &hpb_lu_info, desc_buf); > + if (ret) > + continue; > + > + hpb = ufshpb_alloc_hpb_lu(hba, lun, hpb_dev_info, > + &hpb_lu_info); > + if (!hpb) > + continue; > + > + hpb->sdev_ufs_lu = sdev; > + sdev->hostdata = hpb; > + > + list_add_tail(&hpb->list_hpb_lu, &lh_hpb_lu); > + find_hpb_lu++; > + } > + > + if (!find_hpb_lu) > + return; > + > + ufshpb_check_hpb_reset_query(hba); > + > + list_for_each_entry(hpb, &lh_hpb_lu, list_hpb_lu) { > + dev_info(hba->dev, "set state to present\n"); > + ufshpb_set_state(hpb, HPB_PRESENT); > + } > +} Please remove the loop from the above function, make this function accept a SCSI device pointer as argument and call this function from ufshcd_slave_configure() or ufshcd_hpb_configure(). > +static void ufshpb_init(void *data, async_cookie_t cookie) > +{ > + struct ufsf_feature_info *ufsf = (struct ufsf_feature_info *)data; > + struct ufs_hba *hba; > + struct ufshpb_dev_info hpb_dev_info = { 0 }; > + char *desc_buf; > + int ret; > + > + hba = container_of(ufsf, struct ufs_hba, ufsf); > + > + desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_KERNEL); > + if (!desc_buf) > + goto release_desc_buf; > + > + ret = ufshpb_get_dev_info(hba, &hpb_dev_info, desc_buf); > + if (ret) > + goto release_desc_buf; > + > + /* > + * Because HPB driver uses scsi_device data structure, > + * we should wait at this point until finishing initialization of all > + * scsi devices. Even if timeout occurs, HPB driver will search > + * the scsi_device list on struct scsi_host (shost->__host list_head) > + * and can find out HPB logical units in all scsi_devices > + */ > + wait_event_timeout(hba->ufsf.sdev_wait, > + (atomic_read(&hba->ufsf.slave_conf_cnt) > + == hpb_dev_info.num_lu), > + SDEV_WAIT_TIMEOUT); > + > + ufshpb_issue_hpb_reset_query(hba); > + > + dev_dbg(hba->dev, "ufshpb: slave count %d, lu count %d\n", > + atomic_read(&hba->ufsf.slave_conf_cnt), hpb_dev_info.num_lu); > + > + ufshpb_scan_hpb_lu(hba, &hpb_dev_info, desc_buf); > + > +release_desc_buf: > + kfree(desc_buf); > +} Since the UFS driver calls scsi_scan_host() from ufshcd_add_lus(), do you agree that the above wait_event_timeout() call can be eliminated by splitting ufshpb_init() into two functions and by calling the part below wait_event_timeout() after scsi_scan_host() has finished? > +void ufshpb_remove(struct ufs_hba *hba) > +{ > + struct ufshpb_lu *hpb, *n_hpb; > + struct ufsf_feature_info *ufsf; > + struct scsi_device *sdev; > + > + ufsf = &hba->ufsf; > + > + list_for_each_entry_safe(hpb, n_hpb, &lh_hpb_lu, list_hpb_lu) { > + ufshpb_set_state(hpb, HPB_FAILED); > + > + sdev = hpb->sdev_ufs_lu; > + sdev->hostdata = NULL; > + > + ufshpb_destroy_region_tbl(hpb); > + > + list_del_init(&hpb->list_hpb_lu); > + ufshpb_remove_sysfs(hpb); > + > + kfree(hpb); > + } > + > + dev_info(hba->dev, "ufshpb: remove success\n"); > +} Should the code in the body of the above loop perhaps be called from inside ufshcd_slave_destroy()? > +void ufshpb_scan_feature(struct ufs_hba *hba) > +{ > + init_waitqueue_head(&hba->ufsf.sdev_wait); > + atomic_set(&hba->ufsf.slave_conf_cnt, 0); > + > + if (hba->dev_info.wspecversion >= HPB_SUPPORT_VERSION && > + (hba->dev_info.b_ufs_feature_sup & UFS_DEV_HPB_SUPPORT)) > + async_schedule(ufshpb_init, &hba->ufsf); > +} Why does this function use async_schedule()? Thanks, Bart.