先来看看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)