编写策略指南

尽量避免事务性。核心代码会谨慎地避免查询任何正在迁移的事物。这虽然很麻烦,但能让策略的编写更容易。

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

目标映射的每个 bio 都将参考策略。策略可以返回简单的“命中 (HIT)”或“未命中 (MISS)”,或发起迁移。

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

因为我们映射的是 bio 而不是请求,所以策略很容易被许多小的 bio 欺骗。因此,核心目标会定期向策略发出“心跳”。建议策略在每个心跳周期内,不要为一个块多次更新状态(例如,命中计数)。核心通过观察 bio 完成来发出心跳,从而尝试了解 I/O 调度程序何时允许 I/O 运行。

提供的缓存替换策略概述

多队列 (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) 策略的一些问题。

与 mq 策略相比,smq 策略有望降低内存使用、提高性能并在工作负载变化时增强适应性。smq 也没有任何繁琐的调优旋钮。

用户只需适当地重新加载使用缓存目标的 DM 表,即可从“mq”切换到“smq”。这样做会导致 mq 策略的所有提示都被丢弃。此外,缓存性能可能会略有下降,直到 smq 重新计算出应缓存的源设备热点。

内存使用

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

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

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

级别平衡

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

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

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

smq 不维护命中计数,因此很多问题都迎刃而解。此外,它还会跟踪热点队列的性能,热点队列用于决定提升哪些块。如果热点队列性能不佳,它会开始更快地在级别之间移动条目。这使得它能够非常快速地适应新的 I/O 模式。

性能

测试表明 smq 的性能显著优于 mq。

清理器

清理器会将缓存中所有脏块写回以将其解除服务。

示例

表的语法是

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.