编写策略指南¶
尽量避免事务性。核心代码会小心地避免询问任何正在迁移的内容。这很麻烦,但更容易编写策略。
映射在构造时加载到策略中。
目标映射的每个 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.