编写策略指南

尽量避免事务性。核心代码会小心地避免询问任何正在迁移的内容。这很麻烦,但更容易编写策略。

映射在构造时加载到策略中。

目标映射的每个 bio 都被提交给策略。策略可以返回一个简单的 HIT 或 MISS,或者发出迁移。

目前,策略无法发出后台工作,例如开始写回即将被驱逐的脏块。

因为我们映射的是 bio,而不是请求,所以策略很容易被许多小的 bio 欺骗。因此,核心目标会定期向策略发出 tick。建议策略不要为每个 tick 更新一个块的状态(例如,命中计数)超过一次。核心代码通过观察 bio 完成来触发 tick,从而尝试查看 io 调度器何时让 ios 运行。

提供的缓存替换策略概述

multiqueue (mq)

此策略现在是 smq(见下文)的别名。

以下可调参数被接受,但不起作用

'sequential_threshold <#nr_sequential_ios>'
'random_threshold <#nr_random_ios>'
'read_promote_adjustment <value>'
'write_promote_adjustment <value>'
'discard_promote_adjustment <value>'

随机多队列 (smq)

此策略是默认策略。

随机多队列 (smq) 策略解决了多队列 (mq) 策略的一些问题。

smq 策略(与 mq 相比)有望减少内存利用率、提高性能并提高在面对不断变化的工作负载时的适应性。smq 也没有任何繁琐的调整旋钮。

用户可以通过适当地重新加载正在使用缓存目标的 DM 表来简单地从“mq”切换到“smq”。这样做会导致 mq 策略的所有提示都被丢弃。此外,在 smq 重新计算应缓存的原始设备的热点之前,缓存的性能可能会略有下降。

内存使用

mq 策略使用了大量内存;在 64 位机器上每个缓存块 88 字节。

smq 使用 28 位索引来实现其数据结构,而不是指针。它避免为每个块存储显式的命中计数。它有一个“热点”队列,而不是预缓存,它使用四分之一的条目(每个热点块覆盖的区域比单个缓存块大)。

所有这些都意味着 smq 每个缓存块使用约 25 字节。仍然占用大量内存,但仍然是一项重大改进。

级别平衡

mq 根据其命中计数(~ln(命中计数))将条目放置在多队列结构的不同级别中。这意味着底层通常具有最多的条目,而顶层则很少。像这样不平衡的级别降低了多队列的效率。

smq 不会维护命中计数,而是将命中的条目与上一层中最少使用的条目交换。整体排序是这种随机过程的副作用。使用此方案,我们可以决定每个多队列级别占用多少条目,从而产生更好的升级/降级决策。

适应性:mq 策略为每个缓存块维护一个命中计数。要将不同的块升级到缓存,其命中计数必须超过当前缓存中的最低命中计数。这意味着缓存可能需要很长时间才能适应不同的 IO 模式。

smq 不会维护命中计数,因此很多问题都消失了。此外,它还会跟踪热点队列的性能,该队列用于决定要升级哪些块。如果热点队列表现不佳,则它会开始更快地在级别之间移动条目。这使其能够非常快速地适应新的 IO 模式。

性能

对 smq 的测试表明,其性能明显优于 mq。

cleaner

cleaner 会将缓存中的所有脏块写回,以使其退役。

示例

表的语法是

cache <metadata dev> <cache dev> <origin dev> <block size>
<#feature_args> [<feature arg>]*
<policy> <#policy_args> [<policy arg>]*

使用 dmsetup 命令发送消息的语法是

dmsetup message <mapped device> 0 sequential_threshold 1024
dmsetup message <mapped device> 0 random_threshold 8

使用 dmsetup

dmsetup create blah --table "0 268435456 cache /dev/sdb /dev/sdc \
    /dev/sdd 512 0 mq 4 sequential_threshold 1024 random_threshold 8"
creates a 128GB large mapped device named 'blah' with the
sequential threshold set to 1024 and the random_threshold set to 8.