一. HPB Overview
HPB全称为Host Performance Booster, 是利用手机内存缓存UFS Devices 主控(这里指的是Ufs Device Controller, 比如群联/慧荣等厂商的Ufs,控制运行FTL程序代码)的L2P表,来提升读性能,尤其是长时间使用后的随机读性能。
HPB技术是Jedec UFS3.1加入的Feature, 用来提升随机读性能,其实这个技术在无缓存的SSD上很常见。
二. 手机HPB方案介绍
目前Qcom/MTK/Sprd都开发了支持Ufs Host IP的手机芯片,现在用户越来越多的用户重视手机平时使用的流畅性,流畅性又和随机读有很大联系,所以支持HPB能提高竞争力。
以下以高通平台来举例:
Qcom Ufs Hos和Ufs Devices进行Probe匹配:
/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
*
* Execute link-startup and verify device initialization
*/
static int ufshcd_probe_hba(struct ufs_hba *hba)
{
ufsf_device_check(hba);
}
检查Ufs Devices Descriptor/Geometry Descriptor/Unit Descriptor 涉及HPB的 属性
void ufsf_device_check(struct ufs_hba *hba)
{
struct ufsf_feature *ufsf = &hba->ufsf;
int ret, lun;
u32 status;
ufsf->slave_conf_cnt = 0;
ufsf->hba = hba;
ufshcd_query_attr(ufsf->hba, UPIU_QUERY_OPCODE_READ_ATTR,
QUERY_ATTR_IDN_SUP_VENDOR_OPTIONS, 0, 0, &status);
INIT_INFO("UFS FEATURE SELECTOR Dev %d - D/D %d", status,
UFSFEATURE_SELECTOR);
ret = ufsf_read_dev_desc(ufsf, UFSFEATURE_SELECTOR);
if (ret)
return;
ret = ufsf_read_geo_desc(ufsf, UFSFEATURE_SELECTOR);
if (ret)
return;
seq_scan_lu(lun) {
ret = ufsf_read_unit_desc(ufsf, lun, UFSFEATURE_SELECTOR);
if (ret == -ENOMEM)
goto out_free_mem;
}
/* huangjianan@TECH.Storage.UFS, 2019/12/09, Add for UFS+ RUS */
create_ufsplus_ctrl_proc(ufsf);
return;
out_free_mem:
#if defined(CONFIG_UFSHPB)
/* yujinghua@TECH.Storage.UFS, 2019/12/31, set the ufsf->ufshpb_lup[lun] as NULL after kfree*/
seq_scan_lu(lun) {
kfree(ufsf->ufshpb_lup[lun]);
ufsf->ufshpb_lup[lun] = NULL;
}
/* don't call init handler */
ufsf->ufshpb_state = HPB_FAILED;
#endif
#if defined(CONFIG_UFSTW)
/* yujinghua@TECH.Storage.UFS, 2019/12/31, set the ufsf->tw_lup[lun] as NULL after kfree*/
seq_scan_lu(lun) {
kfree(ufsf->tw_lup[lun]);
ufsf->tw_lup[lun] = NULL;
}
ufsf->tw_dev_info.tw_device = false;
atomic_set(&ufsf->tw_state, TW_FAILED);
#endif
return;
}
获取Device Descriptor配置,如果Devices Descriptor中0x1F bit[7]置1,说明支持HPB Feature.
void ufshpb_get_dev_info(struct ufshpb_dev_info *hpb_dev_info, u8 *desc_buf)
{
int ret;
hpb_dev_info->hpb_device = false;
if (desc_buf[DEVICE_DESC_PARAM_UFS_FEAT] & UFS_FEATURE_SUPPORT_HPB_BIT)
INIT_INFO("bUFSFeaturesSupport: HPB is set");
else {
INIT_INFO("bUFSFeaturesSupport: HPB not support");
return;
}
hpb_dev_info->hpb_ver = LI_EN_16(desc_buf + DEVICE_DESC_PARAM_HPB_VER);
ret = ufshpb_version_check(hpb_dev_info);
if (!ret)
hpb_dev_info->hpb_device = true;
}
static inline int ufshpb_version_check(struct ufshpb_dev_info *hpb_dev_info)
{
INIT_INFO("Support HPB Spec : Driver = %.4X Device = %.4X",
UFSHPB_VER, hpb_dev_info->hpb_ver);
INIT_INFO("HPB Driver Version : %.4X", UFSHPB_DD_VER);
if (hpb_dev_info->hpb_ver != UFSHPB_VER) {
ERR_MSG("ERROR: HPB Spec Version mismatch. So HPB disabled.");
return -ENODEV;
}
return 0;
}
#define UFSHPB_VER 0x0200
#define UFS_FEATURE_SUPPORT_HPB_BIT 0x80
/* Device descriptor parameters offsets in bytes*/
enum device_desc_param {
DEVICE_DESC_PARAM_LEN = 0x0,
DEVICE_DESC_PARAM_TYPE = 0x1,
DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2,
DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3,
DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4,
DEVICE_DESC_PARAM_PRTCL = 0x5,
DEVICE_DESC_PARAM_NUM_LU = 0x6,
DEVICE_DESC_PARAM_NUM_WLU = 0x7,
DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9,
DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB,
DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC,
DEVICE_DESC_PARAM_SEC_LU = 0xD,
DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE,
DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
DEVICE_DESC_PARAM_SPEC_VER = 0x10,
DEVICE_DESC_PARAM_MANF_DATE = 0x12,
DEVICE_DESC_PARAM_MANF_NAME = 0x14,
DEVICE_DESC_PARAM_PRDCT_NAME = 0x15,
DEVICE_DESC_PARAM_SN = 0x16,
DEVICE_DESC_PARAM_OEM_ID = 0x17,
DEVICE_DESC_PARAM_MANF_ID = 0x18,
DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
DEVICE_DESC_PARAM_UD_LEN = 0x1B,
DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
DEVICE_DESC_PARAM_FFU_TMT = 0x20,
DEVICE_DESC_PARAM_Q_DPTH = 0x21,
DEVICE_DESC_PARAM_DEV_VER = 0x22,
DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
DEVICE_DESC_PARAM_PSA_TMT = 0x29,
DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
#ifdef VENDOR_EDIT
/* Hank.liu@TECH.PLAT.Storage, 2019-10-31, add UFS+ hpb and tw driver*/
#if defined(CONFIG_UFSHPB)
DEVICE_DESC_PARAM_HPB_VER = 0x40,
#endif
#endif
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
#ifdef VENDOR_EDIT
#if defined(CONFIG_UFSTW)
DEVICE_DESC_PARAM_TW_RETURN_TO_USER = 0x53,
DEVICE_DESC_PARAM_TW_BUF_TYPE = 0x54,
DEVICE_DESC_PARAM_TW_VER = 0x55,
#endif
#endif
DEVICE_DESC_PARAM_WB_US_RED_EN = 0x53,
DEVICE_DESC_PARAM_WB_TYPE = 0x54,
DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55,
};
获取Geometry Descriptor关于HPB Feature的配置,获取的信息有:
(1)HPB Number Lun: 支持HPB Feature的Lun的数量
(2)HPB Region Size
(3)HPB SubRegion Size
(4)HPB Devices Max Active regions
static int ufsf_read_geo_desc(struct ufsf_feature *ufsf, u8 selector)
{
u8 geo_buf[UFSF_QUERY_DESC_GEOMETRY_MAX_SIZE];
int ret;
ret = ufsf_read_desc(ufsf->hba, QUERY_DESC_IDN_GEOMETRY, 0, selector,
geo_buf, UFSF_QUERY_DESC_GEOMETRY_MAX_SIZE);
if (ret)
return ret;
#if defined(CONFIG_UFSHPB)
if (ufsf->hpb_dev_info.hpb_device)
ufshpb_get_geo_info(&ufsf->hpb_dev_info, geo_buf);
#endif
#if defined(CONFIG_UFSTW)
if (ufsf->tw_dev_info.tw_device)
ufstw_get_geo_info(&ufsf->tw_dev_info, geo_buf);
#endif
return 0;
}
void ufshpb_get_geo_info(struct ufshpb_dev_info *hpb_dev_info, u8 *geo_buf)
{
hpb_dev_info->hpb_number_lu = geo_buf[GEOMETRY_DESC_HPB_NUMBER_LU];
if (hpb_dev_info->hpb_number_lu == 0) {
ERR_MSG("Don't have a lu for hpb.");
hpb_dev_info->hpb_device = false;
return;
}
hpb_dev_info->hpb_rgn_size = geo_buf[GEOMETRY_DESC_HPB_REGION_SIZE];
hpb_dev_info->hpb_srgn_size = geo_buf[GEOMETRY_DESC_HPB_SUBREGION_SIZE];
hpb_dev_info->hpb_device_max_active_rgns =
LI_EN_16(geo_buf + GEOMETRY_DESC_HPB_DEVICE_MAX_ACTIVE_REGIONS);
INIT_INFO("[48] bHPBRegionSiz %u", hpb_dev_info->hpb_rgn_size);
INIT_INFO("[49] bHPBNumberLU %u", hpb_dev_info->hpb_number_lu);
INIT_INFO("[4A] bHPBSubRegionSize %u", hpb_dev_info->hpb_srgn_size);
INIT_INFO("[4B:4C] wDeviceMaxActiveHPBRegions %u",
hpb_dev_info->hpb_device_max_active_rgns);
ufshpb_init_constant();
}
static void ufshpb_init_constant(void)
{
sects_per_blk_shift = ffs(BLOCK) - ffs(SECTOR);
INIT_INFO("sects_per_blk_shift: %u %u", sects_per_blk_shift,
ffs(SECTORS_PER_BLOCK) - 1);
bits_per_dword_shift = ffs(BITS_PER_DWORD) - 1;
bits_per_dword_mask = BITS_PER_DWORD - 1;
INIT_INFO("bits_per_dword %u shift %u mask 0x%X", BITS_PER_DWORD,
bits_per_dword_shift, bits_per_dword_mask);
bits_per_byte_shift = ffs(BITS_PER_BYTE) - 1;
INIT_INFO("bits_per_byte %u shift %u", BITS_PER_BYTE,
bits_per_byte_shift);
}