先来看看JEDEC有关WriteBooster(WB)的描述:

TLC NAND比SLC NAND慢的原因就是TLC里面有更多的programming steps。ok,为了提高性能,那就把TLC NAND的一部分配置成SLC NAND不就行了。rt,WriteBooster特性由此而来,就是把这部分SLC NAND用作WriteBooster Buffer。有个概率图:

+------+                   +---------------------------------------------+
| | 1.Write data in | +--------------+ +---------+ |
| | WriteBooster mode | | | | | |
| Host |-------------------|->| WriteBooster | 3.Flush | Normal | |
| |<------------------|--| Buffer |------------->| Storage | |
| | 2.fast response | | (SLC) | | (TLC) | |
+------+ | +--------------+ | | |
| Device +---------+ |
+---------------------------------------------+

Writing data to WriteBooster Buffer

这里有两个WriteBooster mode,一个是LU dedicated buffer mode,一个是shared buffer mode。

bSupportedWriteBoosterBuferTypes暗示了哪一个模式被设备支持。

LU dedicated buffer mode就是指定某个LU为这个WB Buffer。shared buffer mode就是所有的LU共享这个WB Buffer了,不过well-known LUs例外。

注意这个WB Buffer是有生命周期的,你懂的:)

Flushing WriteBooster Buffer

当整个WriteBooster buffer被用完了,device就通过WRITEBOOSTER_FLAUSH_NEEDED事件通知Host: need flush( to normal storage)。如果flush有错,那就直接写到normal storage了。

有两个方法flush,一个是flush命令,另一个是在hibernate时。只有当command queue为空时才执行flush。当正在flush时要是有host cmd过来,那就suspend flush,等处理完host cmd后, consume之前suspend的flush。

User space configurations

既然有一部分用作WB Buffer,那用户可用空间肯定少了啊,rt,这就是user space reduction。但还有个配置可以保留这个空间,当normal storage都用完了,那就用WB Buffer,此时WB Buffer需要回到normal storage,类似flush操作,性能就会下降。

Linux代码分析

参考内核5.x, 先看下struct ufs_dev_info,关注WB code。

struct ufs_dev_info {
bool f_power_on_wp_en;
/* Keeps information if any of the LU is power on write protected */
bool is_lu_power_on_wp;
/* Maximum number of general LU supported by the UFS device */
u8 max_lu_supported;
u16 wmanufacturerid;
/*UFS device Product Name */
u8 *model;
u16 wspecversion;
u32 clk_gating_wait_us;

/* UFS WB related flags */
bool wb_enabled; //tj: 特性使能
bool wb_buf_flush_enabled; //tj: flush操作
u8 wb_dedicated_lu; //tj: LU dedicated buffer mode
u8 wb_buffer_type; //tj: WriteBooster模式

bool b_rpm_dev_flush_capable;
u8 b_presrv_uspc_en; //tj: 保留用户空间
};

获取设备描述符时会check WB,入口是ufshcd_wb_probe():

static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf)
{
...
/*
* Probe WB only for UFS-2.2 and UFS-3.1 (and later) devices or
* UFS devices with quirk UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES
* enabled
*/
if (!(dev_info->wspecversion >= 0x310 ||
dev_info->wspecversion == 0x220 ||
(hba->dev_quirks & UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES)))
goto wb_disabled;

UFS-2.2和UFS-3.1(and later)有这个能力。

注意还有个不管标准了,只要有UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES这个quirk也行。来看下这个quirk:

/*
* Some pre-3.1 UFS devices can support extended features by upgrading
* the firmware. Enable this quirk to make UFS core driver probe and enable
* supported features on such devices.
*/
#define UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES (1 << 10)

好家伙,通过升级固件的方式也可以具备WB。So,市面上出现的UFS-3.0 + WriteBooster就是这个喽?

继续看code, WB device支持的条件需要:

ext_ufs_feature = get_unaligned_be32(desc_buf +
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP);

if (!(ext_ufs_feature & UFS_DEV_WRITE_BOOSTER_SUP))
goto wb_disabled;
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP	= 0x4F,

继续:

	if (dev_info->wb_buffer_type == WB_BUF_MODE_SHARED) {
if (!get_unaligned_be32(desc_buf +
DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS))
goto wb_disabled;
} else {
for (lun = 0; lun < UFS_UPIU_MAX_WB_LUN_ID; lun++) {
d_lu_wb_buf_alloc = 0;
ufshcd_read_unit_desc_param(hba,
lun,
UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS,
(u8 *)&d_lu_wb_buf_alloc,
sizeof(d_lu_wb_buf_alloc));
if (d_lu_wb_buf_alloc) {
dev_info->wb_dedicated_lu = lun;
break;
}
}

if (!d_lu_wb_buf_alloc)
goto wb_disabled;
}
return;

wb_disabled:
hba->caps &= ~UFSHCD_CAP_WB_EN;
}

共享模式,查看分配大小DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS(dNumSharedWriteBoosterBufferAllocUnits),如果没分配那就disable WB。

LU专有模式就是从0-7个LU里找看谁分配了WB Buffer就用那个了。

WB特性使能:ufshcd_wb_ctrl()

两种flush使能: ufshcd_wb_toggle_flush()ufshcd_wb_toggle_flush_during_h8()

对于user space reduction,可用buffer大小<10%那就需要flush了。

static bool ufshcd_wb_need_flush(struct ufs_hba *hba)
{
...
if (!hba->dev_info.b_presrv_uspc_en) {
if (avail_buf <= UFS_WB_BUF_REMAIN_PERCENT(10))
return true;
return false;
}

对于 preserve user space,多了个当前WB buffer大小的check:

static bool ufshcd_wb_presrv_usrspc_keep_vcc_on(struct ufs_hba *hba,
u32 avail_buf)
{
...
if (!cur_buf) {
dev_info(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n",
cur_buf);
return false;
}
/* Let it continue to flush when available buffer exceeds threshold */
if (avail_buf < hba->vps->wb_flush_threshold)
return true;

return false;

前面说过因为perserve,WB buffer大小是可能变化的,当WB buffer全部都回到user space,那就等于没有WB了,也就不需要flush了。

这里可用的buffer门限取决于host侧? 默认是40。

static struct ufs_hba_variant_params ufs_hba_vps = {
.hba_enable_delay_us = 1000,
.wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(40),

mtk改变了flush策略,门限定了80%。

/* Enable WriteBooster */
hba->caps |= UFSHCD_CAP_WB_EN;
hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL;
hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80);

也就是说,使用了>20%就要flush了,才这么点?

Refer doc

JEDEC standard UFS version 2.2 (free)