RFC-0226:Zircon 寻呼机回写 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 内核支持跟踪和回写对分页器支持的 VMO 所做的修改 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2023-04-13 |
审核日期(年-月-日) | 2023-09-19 |
摘要
本文档介绍了 Zircon 内核对分页器支持内存的支持, 修改,然后写回(同步)到分页器源,例如存储空间 磁盘。
设计初衷
Zircon 支持创建由 用户空间分页器服务,通常由文件系统托管。单个文件 在内存中以 VMO 表示当 VMO 的页面被访问时,它们会 按需触发故障,而用户寻呼机读取网页的内容来自 磁盘。
Zircon pager API 最初仅支持只读文件;没有
被写入内存中被脏污染的 VMO 页面被写回的机制,
磁盘。这种设计足以托管 blobfs
等不可变文件系统,
提供 Fuchsia 上的所有可执行文件和其他只读文件。不过,
通用文件系统需要回写支持,其中文件
内容可由客户端修改,因此需要同步回磁盘。
如果没有回写支持,minfs
和 fxfs
等可变文件系统便无法实现
充分利用需求分页功能解决方法是,将可变文件系统
使用匿名(非分页器支持的)VMO 将文件缓存在内存中,并管理
这些 VMO 本身的内容。这些 VMO 可能需要
即使这些页面内的某些页面很少或从不
。将这些匿名 VMO 切换为由分页器支持的 VMO,这样网页可以
出现故障并按需逐出,让可变文件系统能够更好地利用
内存。回写支持还允许可变文件系统的客户端直接
对 VMO 执行读写操作,而不是依靠通道获取数据
因为受信道限制的限制,速度可能会极其慢。
在本章的其余部分,“用户分页器”和“文件系统”这两个术语可以互换使用 具体取决于上下文。
利益相关方
教员:
- cpu@google.com
审核者:
- adanis@google.com、csuter@google.com
已咨询:
- brettw@google.com、cdrllrd@google.com、godtamit@google.com、 maniscalco@google.com、travisg@google.com
社交化:
此 RFC 已由本地存储团队进行了设计审核。
要求
提议的设计旨在实现以下目标:
- 增强 Zircon,以支持对分页器支持的 VMO 进行回写,从而允许构建 高性能可变文件系统(使用 Zircon 流)。
- 支持通过虚拟机映射读取和写入文件(考虑使用
mmap
的文件)。 - 提供用户分页器尽最大努力清除脏页,以减少 因意外关停而导致数据丢失的风险。
- 在未来的迭代中,允许内核(通过用户分页器)逐出脏页 以应对系统内存压力。
以及一些非目标:
- 在用户分页器发起的刷新之外,内核不会执行任何 脏页的清理速率尽管如此, 未来的发展可能会增加一项功能, 数据并让内核根据该数据量发起回写请求。
- 防止因违反内核/分页器协议而导致数据丢失 非目标。如果用户分页器无法查询脏页并将其写回 然后再将其句柄关闭到 VMO(或终止),那么数据丢失 。
设计
概览
所提议的设计旨在支持两个直接的用例:
- 文件系统客户端可以通过作为内核对象的流访问文件
封装文件 VMO这大致可以视为类似于
zx_vmo_read()
和zx_vmo_write()
,因为流在内部进行系统调用 封装 VMO 读/写内核例程。 - 文件系统客户端还可以对文件执行
mmap
操作,这大致相当于 将文件 VMO 映射(使用zx_vmar_map
)到客户端进程地址 空间。
为简单起见,本文档的其余部分将讨论直接交互
文件 VMO,通过系统调用 (zx_vmo_read/write
) 或通过虚拟机
映射 (zx_vmar_map
)。
下面几个示例展示了哪些互动涉及回写 可能是什么样子
示例 1
- 文件系统客户端会对给定范围内的文件 VMO 执行
zx_vmo_read()
。 - 由于 VMO 受分页器支持,因此内核会生成一个读取请求, 关联的用户寻呼机。
- 由文件系统托管的用户寻呼机满足了此请求。它提供 包含从磁盘读取的内容的页面。
- 文件系统客户端会对 VMO 执行
zx_vmo_write()
操作, 范围。VMO 的页面之前已在第 3 步中填充,因此可以 写入数据。所做的修改目前仅在内存中,但 在某个时间点需要反映在磁盘上 - 用户分页器向内核查询 VMO 中已被 脏乱 / 被修改。这可以作为定期背景刷新的一部分来实现 文件系统执行或响应文件系统请求的刷新 客户端。
- 用户分页器将查询的脏范围写回磁盘。目前 即修改后的文件内容已成功保存到磁盘
示例 2
- 文件系统客户端使用
zx_vmar_map()
映射文件 VMO。映射开始时 位于addr
。客户端读取从addr
开始的范围。 - 与示例 1 相同。
- 与示例 1 相同。
- 文件系统客户端现在会从
addr
开始向同一范围写入数据。通过 基础页面已经填充过,因此内容 在内存中进行了修改。您需要在 2024 年 3 月 8 日之前 在某个时间点 - 与示例 1 相同。
- 与示例 1 相同。
此处的示例从执行写入之前的 VMO 读取开始。请注意, 这样做只是为了将用户分页器的页面填充分为一个 为清晰起见,这是单独的步骤。客户端可以直接写入到 记忆功能写入将阻塞,直到用户分页器提供页面 。
上述两个示例均假定文件系统遵循覆盖模型
表示可以直接写入已填充(已提交)的页面,而无需
来请求额外的存储空间修改后的内容将写回原来的
因此无需为修改分配额外的空间。
不过,fxfs
和 minfs
等文件系统使用的是写入时复制 (CoW) 模型,
每次修改都需要在磁盘上分配新空间。所以我们
需要一种机制来为已经写入的页面预留空间
承诺;第 4 步已修改为等待该预留,然后才能执行写入操作
继续。
为了执行回写,Zircon 寻呼机 API 进行了扩展,可支持以下各项:
- 内核会屏蔽用户分页器指示应写入的 VMO 遵循写时复制方案,并在用户传呼机获得 已确认写入。
- 内核会跟踪 VMO 中的脏页,并具有一种机制来发现 传递给用户传呼机。
- 用户分页器在同步 VMO 及其完成时间,以便内核可以更新脏跟踪 状态。
- 内核还会根据用户分页器显示有关 VMO 大小调整的信息。
- 用户分页器可以查询内核跟踪的相关信息 代表其 ID,例如上次修改 VMO 的时间。
分页器和 VMO 创建
分页器创建系统调用保持不变,即使用 zx_pager_create()
以创建将 options
设置为 0 的分页器。
由分页器支持的 VMO 使用 zx_pager_create_vmo()
创建,
以及一个密钥(用于该端口的页面请求数据包),
VMO。zx_pager_create_vmo()
系统调用还支持新的 options
标志
ZX_VMO_TRAP_DIRTY
。这表示内核应捕获对
VMO,并首先从用户分页器请求确认写入。
此标志适用于在“写入时复制”模式下运行的文件。更多
稍后查找关于此标志的详细信息
// Create a VMO (returned via |out|) backed by |pager|. Pager requests will be
// queued on |port| and will contain the provided |key| as an identifier.
// |size| will be rounded up to the page boundary.
//
// |options| must be 0 or a combination of the following flags:
// ZX_VMO_RESIZABLE - if the VMO can be resized.
// ZX_VMO_TRAP_DIRTY - if writes to clean pages in the VMO should be trapped by the
// kernel and forwarded to the pager service for acknowledgement before proceeding
// with the write.
zx_status_t zx_pager_create_vmo(zx_handle_t pager,
uint32_t options,
zx_handle_t port,
uint64_t key,
uint64_t size,
zx_handle_t* out);
默认情况下,所有由分页器支持的 VMO 都被视为可变;这也适用于 实现只读文件系统,无需支付额外费用。需要转换的代码路径 修改页面假定不适用于只读 VMO。如果 VMO 被修改(可能是意外滥用),但用户传呼机绝不会 会查询其中的脏页并尝试写回它们, 内容只会保留在内存中。将来,当内核 回写请求,用户分页器可以处理此类请求的回写请求, VMO 报告为错误,或者直接忽略它们。
提供 VMO 页面
由分页器支持的 VMO 中的页面由用户分页器按需填充
zx_pager_supply_pages()
。此系统调用
已存在,可与只读 VMO 搭配使用。
回写的页面状态
由分页器支持的 VMO 可以让页面处于三种状态:Dirty
、Clean
和
AwaitingClean
。这些状态在 vm_page_t
中进行编码。
三种状态之间的转换遵循以下步骤:
- 为新提供
zx_pager_supply_pages()
的网页 名称:Clean
。 - 当页面被写入时,它会转换为
Dirty
。如果 VMO 已被 使用ZX_VMO_TRAP_DIRTY
创建,则内核会在确认 先从用户寻呼机请求DIRTY
寻呼机请求。更多详情 互动。 - 用户分页器通过 系统调用。
- 对于内核返回的每个脏页,用户分页器都会调用系统调用
来指示内核开始回写页面
将其状态更改为
AwaitingClean
。 - 如果页面写入到此时间点之后,其状态会切换回
Dirty
。 - 当完成回写页面的操作时,用户寻呼机将发出另一个系统调用。
如果此时页面状态为
AwaitingClean
,则会转换为Clean
。 - 如果用户在回写过程中遇到错误,则该网页会保留
AwaitingClean
。日后查询脏页时,系统会同时返回AwaitingClean
和Dirty
页,以便用户分页器可以尝试回写相应页 。
下面的状态图展示了这些状态之间的转换。
AwaitingClean
需要作为几种不同状态的单独状态进行跟踪
原因:
即使同时处于
Clean
和AwaitingClean
状态的网页转换为Dirty
(当写入到用户分页器正在处理的页面时) 与Clean
网页的处理方式不同。Clean
页 能够在内存紧张时回收 需要受到保护,以防被收回。内核需要知道哪个版本的网页已被写回, 可以在用户分页器完成后将其正确转换为
Clean
。这是 请务必区分 flush 之前发生的页面写入( 而后进入的磁盘(需要 )。我们可以在回写开始时避免来自用户分页器的系统调用, 而内核只需在返回
AwaitingClean
作为用户分页器的脏页查询的一部分。但也可能是 刷新网页后,用户分页器 而这会留出较长的时间窗口,以便系统再次清理这些数据。拥有一个 将回写窗口括起来越紧,用户就越有可能 分页器就能够成功将页面变为Clean
状态。
为了更新脏状态,内核会跟踪将页面写入
包括通过 VMO 和流式写入系统调用,以及通过虚拟机映射。请注意,
这适用于通过虚拟机映射发生的任何写入
也就是说,此政策也适用于内核执行的写入操作
在系统调用中包含 user_copy
,例如 zx_channel_read()
。
在系统调用期间推断脏页(例如 zx_vmo_write()
)非常简单,
因为指定了范围。访问 VMO 的另一种方式是通过虚拟机
进程中的映射”地址空间为了在
由分页器支持的 VMO,可写映射开始时会移除写入权限
。写入会生成保护
错误,可通过恢复写入权限并将页面标记为
状态为 Dirty
。
此处提到的 Dirty
状态是由
vm_page_t
,即软件跟踪的脏状态。脏位跟踪
x86 上的硬件分页表,但我们不打算使用它
为初始实现派生页面的脏状态。我们需要跟踪
反之,对于不支持脏页的旧版 arm64 平台,软件中的脏位
位。因此,为了保持一致性和简易性
选择在开始时不使用硬件脏位来推断网页的
脏状态 / 干净状态。依赖硬件页面表格位也会造成
页表回收方面的复杂问题,需要考虑到这一点
未来我们确实要依赖硬件位
值得注意的是,只有由分页器直接支持的 VMO 才有资格进入脏页 跟踪。换句话说,由分页器支持的 VMO 的 CoW 克隆不会选择 脏跟踪,并且看不到任何回写请求。符合以下条件的网页: 以副本形式编写的网页是从父网页副本派生而来,而克隆 直接拥有这些页面的所有权。
为待处理的写入操作预留空间
写入标记为使用
ZX_VMO_TRAP_DIRTY
创建选项标志,需要获得
文件系统此解决方案分为两部分提出,其中 v1 先是简单的
并更加注重正确性,而 v2 是在 v1 的基础上构建的,
性能v1 提案主要采用同步模式,
从而为新写入操作预留空间。对于第 2 版,
表示内核中的脏预留配额以及它们如何应用于 VMO,因此
让内核可以自行跟踪预留这将有助于提升
减少内核
和文件系统
ZX_VMO_TRAP_DIRTY
v1
ZX_VMO_TRAP_DIRTY
VMO 创建标志表示内核应捕获
页面中的任何 Clean->Dirty
页面转换(或 AwaitingClean->Dirty
转换)
VMO。当有写入操作进入尚未脏页时,内核会生成
ZX_PAGER_VMO_DIRTY
寻呼请求。对于通过虚拟机映射执行的写入操作,请求
跨越包含错误地址的单个页面。对于流式/VMO 写入,
内核会在每次连续运行非脏页时发送一个请求,
一个范围。
范围 [start, end)
的 dirty 请求如下所示。
zx_packet_page_request_t request {
.command = ZX_PAGER_VMO_DIRTY,
.flags = 0,
// |offset| and |length| will be page-aligned.
.offset = start,
.length = end - start,
};
ZX_VMO_TRAP_DIRTY
创建标志适用于在 CoW 中写入的文件
模式;对于稀疏文件,则设为“覆盖”模式。如果未指定该标志
页面被标记为 Dirty
,并且无需用户寻呼机参与即可进行写入;这个
适用于以覆盖模式写入的非稀疏文件。
用户寻呼机使用ZX_PAGER_VMO_DIRTY
zx_pager_op_range()
:
ZX_PAGER_OP_DIRTY
会将尚未Dirty
的页面的状态设置为Dirty
, 并且内核继续执行阻塞的写入。ZX_PAGER_OP_FAIL
不会更改网页的当前状态,并且会使zx_vmo_write()
调用发起了写入,生成严重页面错误 虚拟机映射的例外情况;如果是zx_stream_write()
,则返回 部分写入成功完成。
// |pager| is the pager handle.
// |pager_vmo| is the VMO handle.
// |offset| and |length| specify the range, i.e. [|offset|, |offset| + |length|).
//
// |op| can be:
//
// ZX_PAGER_OP_DIRTY - The userspace pager wants to transition pages in the range
// [offset, offset + length) from clean to dirty. This will unblock any writes that
// were waiting on ZX_PAGER_VMO_DIRTY page requests for the specified range.
// |data| must be 0.
//
// ZX_PAGER_OP_FAIL - The userspace pager failed to fulfill page requests for
// |pager_vmo| in the range [offset, offset + length) with command
// ZX_PAGER_VMO_READ or ZX_PAGER_VMO_DIRTY.
//
// |data| contains the error encountered, a zx_status_t error code sign-extended
// to a |uint64_t| value - permitted values are ZX_ERR_IO, ZX_ERR_IO_DATA_INTEGRITY,
// ZX_ERR_BAD_STATE and ZX_ERR_NO_SPACE.
zx_status_t zx_pager_op_range(zx_handle_t pager,
uint32_t op,
zx_handle_t pager_vmo,
uint64_t offset,
uint64_t length,
uint64_t data);
取决于文件系统刷新脏数据以及标记页面的频率
Clean
,那么在以下情况下,此方法可能会导致性能下降:
由客户端编写为避免此类开销,文件系统可能需要延迟刷新
尽可能多地保留脏数据,这并不是提供
脏页无法逐出,这将增加内存压力,
两次刷新之间的间隔也会增加数据丢失的可能性。通过
v2 提案会尝试减少面向客户的部分性能成本
写入。
ZX_VMO_TRAP_DIRTY
v2
将添加新的系统调用 zx_pager_set_dirty_pages_limit()
以指定
允许内核累积一定数量的脏页。通过
那么文件系统为这些工作负载预留了空间
脏页。这是一个每页的限制,默认设为 0。
可以使用
zx_pager_set_dirty_pages_limit()
。从本质上讲,v1 设计
设置为零。
zx_status_t zx_pager_set_dirty_pages_limit(zx_handle_t pager_handle,
uint64_t num_dirty_pages);
内核会跟踪每个分页器的脏页数(关于
符合稍后跟踪条件的网页),这就会增加
过渡到 Dirty
,并在过渡到 Clean
时递减。内核
仍会像在 v1 中一样,捕获每个 Dirty 转换,
未完成的脏页数,前提是在不超出
分配的脏内存量。如果新计数未超过该限制,则内核
将在不涉及用户寻呼机的情况下继续写入。这是正常现象
作为正常操作模式,从而节省往返时间,
用户分页器。
如果使用此方法,用户寻呼机需要向 内核:
- 分页器范围内的脏数据量限制
- 脏污网页将被计入该限制
对于 2),我们再次依赖 ZX_VMO_TRAP_DIRTY
VMO 创建标志。这个
标志现在会触发生成新类型的寻呼机请求:
ZX_VMO_DIRTY_MODE
。当内核 trap 现在写入内容时,它会查询
以确定是否应选择将这些网页纳入统计范围
使其低于脏上限用户寻呼机使用 zx_pager_op_range
进行响应,并返回
是两种新运算类型之一
ZX_PAGER_OP_DIRTY_MODE_POOLED
用于告知内核,该范围内的页面将 不计入每页的脏数据量限制。此字段适用于文件 在 CoW 模式下运行,在覆盖模式下运行稀疏文件区域。ZX_PAGER_OP_DIRTY_MODE_UNPOOLED
用于告知内核,该范围内的页面 不会计入脏限制。它适用于非稀疏集群 以覆盖模式运行的稀疏文件所在的区域。
标记为 ZX_PAGER_OP_DIRTY_MODE_POOLED
的网页已转换为 Dirty
,并且
待处理的分页器脏页数将递增,前提是它不超过
分页器脏页限制。如果对页面进行脏化,则会超出分页器脏污量限制
不过,内核开始生成 ZX_PAGER_VMO_DIRTY
数据包,即
默认模式(如 v1 中所述)。您可以提供一个可选标志,
在页面被写回(转换为 Clean
)时进入脏模式,
可节省捕获未来写入操作以生成 ZX_VMO_DIRTY_MODE
的费用
寻呼机请求。
这种设计可实现灵活的模型,在该模型中,文件系统可以混合
各种写入模式类型以 CoW 模式写入的文件
他们使用 ZX_VMO_TRAP_DIRTY
创建的 VMO,并且他们的页面可以使用共用
模式。同样,您也可以使用
ZX_VMO_TRAP_DIRTY
标志,并将池化和取消池模式用于稀疏和
非稀疏区域。始终使用“覆盖”模式的文件可以省略
ZX_VMO_TRAP_DIRTY
标志,这样他们就永远无需向
写入时分页请求的费用。
用户寻呼机开始接收 ZX_PAGER_VMO_DIRTY
请求后,
脏配额,应开始清理页面,以便为
新的脏页。当使用
zx_pager_set_dirty_pages_limit()
(使用与之前相同的限制或新的限制)。
在此调用后,内核将继续检查累积的脏计数
不超出未来写入时的脏数据量限制,并会生成 ZX_PAGER_VMO_DIRTY
则只有在再次达到脏页数限制时才会发出请求。
ZX_VMO_TRAP_DIRTY
v1 与 v2 之间的区别
v1 和 v2 之间的主要区别在于,负责 来跟踪预订数量在 v1 中,文件系统负责 同时,内核会告知其 增加预留数量由于负责拦截的实体 预留(内核)的潜在更改与实体不同 执行实际的簿记操作(文件系统),因此需要紧密耦合 。在 v2 中,我们尝试让内核跟踪 预留计数本身因此需要与文件系统进行通信 仅当 - 1) 需要设置 VMO 范围以选择加入(或退出)内核时 预留跟踪;2) 内核耗尽预留配额,并且 需要文件系统进行干预。
我们预计 2) 属于极端情况,因为文件系统会定期 将脏页刷新到磁盘。预计大部分的沟通 原因:1)。内核可以多次请求同一范围的信息 (例如,对于跨越重叠范围的写入),同样,文件系统可以 多次向内核提供关于同一范围的冗余信息。 在页面上设置脏模式实际上并不会 因为脏污染计数仅在页面实际存在 写入。因此,文件系统还可以在页面上设置脏模式 推测性降低未来寻呼请求的性能成本 (不过,请注意,这可能会产生影响的页面逐出)。
发现脏范围
用户分页器需要一种机制来查找 VMO 中的脏页,因此它 因此您可以将它们写回去这种情况下,需要考虑两种不同的模式: 模型。 推送模型,其中内核通过发送用户分页器来指示脏页 回写请求。初始设计始于比较简单的拉取模型 并引入了查询系统调用的脏范围,可能如下所示:
// |pager| is the pager handle.
// |pager_vmo| is the vmo handle.
// |offset| and |length| specify the VMO range to query dirty pages within.
// Must be page-aligned.
//
// |buffer| points to an array of type |zx_vmo_dirty_range_t| defined as follows.
// typedef struct zx_vmo_dirty_range {
// // Represents the range [offset, offset + length).
// uint64_t offset;
// uint64_t length;
// // Any options applicable to the range.
// // ZX_VMO_DIRTY_RANGE_IS_ZERO indicates that the range contains all zeros.
// uint64_t options;
// } zx_vmo_dirty_range_t;
//
// |buffer_size| is the size of |buffer|.
//
// |actual| is an optional pointer to return the number of dirty ranges that were
// written to |buffer|.
//
// |avail| is an optional pointer to return the number of dirty ranges that are
// available to read. If |buffer| is insufficiently large, |avail| will be larger
// than |actual|.
//
// Upon success, |actual| will contain the number of dirty ranges that were copied
// out to |buffer|. The number of dirty ranges that are copied out to |buffer| is
// constrained by |buffer_size|, i.e. it is possible for there to exist more dirty
// ranges in [offset, offset + length) that could not be accommodated in |buffer|.
// The caller can assume than any range that had been made dirty prior to
// making the call will either be contained in |buffer|, or will have a start
// offset strictly greater than the last range in |buffer|. Therefore, the caller
// can advance |offset| and make another query to discover further dirty ranges,
// until |avail| is zero.
//
zx_status_t zx_pager_query_dirty_ranges(zx_handle_t pager,
zx_handle_t pager_vmo,
uint64_t offset,
uint64_t length,
void* buffer,
size_t buffer_size,
size_t* actual,
size_t* avail);
用户寻呼机应该会多次调用此查询,从而推进 查询延迟,直到处理完所有脏页。
使用拉取模型时,网页清理速度完全取决于 文件系统选择查询脏范围并尝试 回写。但在少数情况下,内核本身需要 来发起回写页面的请求(例如在内存紧张时), 以便清理脏页,随后将其释放内核可能会向 在本例中,按 LRU 顺序针对脏页发出回写请求。这样做的目的是 作为对用户寻呼机的提示,以便其可以提高 刷新页面,例如是否有延迟处理请求
VMO 中对脏范围 [start, end)
的回写请求可能如下所示:
。
zx_packet_page_request_t request {
.command = ZX_PAGER_VMO_WRITEBACK,
.flags = ZX_PAGER_MEMORY_PRESSURE,
// |offset| and |length| will be page-aligned.
.offset = start,
.length = end - start,
};
写回脏范围
zx_pager_op_range()
系统调用经过扩展,可支持两个额外的操作:
ZX_PAGER_OP_WRITEBACK_BEGIN
和 ZX_PAGER_OP_WRITEBACK_END
,用于在以下情况下发出信号:
分别表示用户分页器开始刷新页面和刷新完成后。
ZX_PAGER_OP_WRITEBACK_BEGIN
会更改Dirty
指定范围为AwaitingClean
。对于AwaitingClean
或Clean
状态,并保持这些状态不变。ZX_PAGER_OP_WRITEBACK_END
可更改以下集群中任何AwaitingClean
页面的状态: 将指定范围设置为Clean
。对于任何已经Clean
或处于Dirty
状态且状态保持不变的网页。
如果在执行刷新时遇到任何错误,即
ZX_PAGER_OP_WRITEBACK_BEGIN
,但在 ZX_PAGER_OP_WRITEBACK_END
之前,
只需分页器即可执行更多操作。这些网页将保留在AwaitingClean
中
状态(假设没有进行另一次写入)。当
内核再次查询脏页,将包括 AwaitingClean
页
以及 Dirty
网页,然后用户寻呼机可以尝试回写这些网页
失败的网页。
// Supported |op| values are:
// ZX_PAGER_OP_WRITEBACK_BEGIN indicates that the user pager is about to
// begin writing back the specified range and the pages are marked |AwaitingClean|.
// ZX_PAGER_OP_WRITEBACK_END indicates that that user pager is done writing
// back the specified range and the pages are marked |Clean|.
//
// |pager| is the pager handle.
// |pager_vmo| is the VMO handle.
// |offset| and |length| specify the range to apply the |op| to, i.e. [|offset|,
// |offset| + |length|).
// For ZX_PAGER_OP_WRITEBACK_*, |data| is unused and should be 0.
zx_status_t zx_pager_op_range(zx_handle_t pager,
uint32_t op,
zx_handle_t pager_vmo,
uint64_t offset,
uint64_t length,
uint64_t data);
对于 ZX_PAGER_OP_WRITEBACK_BEGIN
,可以选择将 data
设置为
ZX_VMO_DIRTY_RANGE_IS_ZERO
,以指示调用方想要回写
指定范围为零。此属性应在调用方
正在处理由 zx_pager_query_dirty_ranges()
返回的范围及其
options
已设为 ZX_VMO_DIRTY_RANGE_IS_ZERO
。它可确保任何非零值
在查询之后但在回写之前范围内创建的内容
并没有丢失,因为错误地假定它仍然为零,并将其标记为
清除(因此可逐出)。
调整 VMO 的大小
由分页器支持的 VMO 与匿名(非分页器支持的)VMO 的不同之处在于: VMO 中没有内容的处理。匿名 VMO 具有隐式初始值 内容为零,因此未承诺页面表示为零。对于这种情况,并非如此 由分页器支持的 VMO,其中未提交的网页不表示零;它们仅仅是指 。将大小调整为 更大的大小,但是分页器无法提供新扩展中的页面 范围,只是因为在后备源(例如 所以就不需要分页了内核可以为 零。
通过跟踪跨新大小调整的零间隔来处理大小调整
范围,内核隐式地为其提供零个页面。用户分页器
还不知道这个零区间,因此当
用户分页器查询脏范围。此外,
对于此范围,zx_vmo_dirty_range_t
已设为 ZX_VMO_DIRTY_RANGE_IS_ZERO
表明该位置全部为零。
如果使用 ZX_VMO_TRAP_DIRTY
标志创建了 VMO,并且页面
写入到这个新扩展的范围内,内核会生成
在提交前先收到 ZX_PAGER_VMO_DIRTY
个寻呼请求。这是
因为文件系统可能需要为实际(非零)页面预留空间。
该模型假设 0 可以在磁盘上通过
稀疏区域,因此仅当
都在新的扩展范围内投入使用
将 VMO 与分页器分离
zx_pager_detach_vmo()
会将 ZX_PAGER_COMPLETE
数据包加入队列,这指示
用户寻呼机应该不会再收到针对该 VMO 的寻呼请求
。这也表示用户分页器应查询并写回任何
未完成的脏页。请注意,分离操作不会阻塞,
网页已被写回;它只是通知用户分页器
可能性。
分离后,zx_vmo_read()
/ zx_vmo_write()
(本来需要
要生成的寻呼机请求失败并显示 ZX_ERR_BAD_STATE
。读取和写入
通过映射,这些映射同样会导致所需的寻呼请求生成
严重页面错误异常。内核可以随意从
VMO。不过,内核会保留脏页,直到脏页被
用户寻呼机。也就是说,ZX_PAGER_OP_WRITEBACK_BEGIN
和
即使在该日期之后,VMO 仍继续支持ZX_PAGER_OP_WRITEBACK_END
已分离。在所有其他操作上计算zx_pager_op_range()
已分离的 vmo 上的 zx_pager_supply_pages()
失败并显示 ZX_ERR_BAD_STATE
。
如果分页器因相关 VMO 中的脏页而被销毁,则内核 因此可以自由移除这些网页,而不管是否存在 未完成的回写请求。也就是说,脏页肯定是 它们才会保存在内存中。
查询分页器 VMO 统计信息
内核还会跟踪 VMO 是否已被修改,可由
用户寻呼机。用户寻呼机需要使用此字段来跟踪 mtime
。
// |pager| is the pager handle.
// |pager_vmo| is the VMO handle.
// |options| can be ZX_PAGER_RESET_VMO_STATS to reset the queried stats.
// |buffer| points to a struct of type |zx_pager_vmo_stats_t|.
// |buffer_size| is the size of the buffer and should be large enough to
// accommodate |zx_pager_vmo_stats_t|.
//
// typedef struct zx_pager_vmo_stats {
// uint32_t modified;
// } zx_pager_vmo_stats_t;
zx_status_t zx_pager_query_vmo_stats(zx_handle_t pager,
zx_handle_t pager_vmo,
uint32_t options,
void* buffer,
size_t buffer_size);
返回的 zx_pager_vmo_stats_t
的 modified
字段设置为
如果 VMO 已修改,则为 ZX_PAGER_VMO_STATS_MODIFIED
,否则为 0。通过
将来可以使用更多字段扩展 zx_pager_vmo_stats_t
结构体
用户寻呼机可能认为有用的查询字词。
在修改 VMO 的系统调用中,modified
状态会更新,例如
zx_vmo_write()
和 zx_vmo_set_size()
,以及首次写入页面时
是通过映射来实现故障的页面上的第一个写入错误是
正确管理从 Clean
到 Dirty
的转换,因此
然后,modified
状态会更新。通过
但不会跟踪脏页这样做会显著降低
写入映射的 VMO。因此,modified
状态可能并不完全准确
映射的 VMO。
options
可以为 ZX_PAGER_RESET_VMO_STATS
如果用户寻呼机也希望
重置查询的统计信息。options
值为 0 不会重置任何状态,
会执行纯查询。请注意,此调用可能会影响未来的
如果存在以下情况,则使用可查询状态调用 zx_pager_query_vmo_stats()
:
已指定 ZX_PAGER_RESET_VMO_STATS
选项。例如,如果
“zx_vmo_write()
”后跟两个连续的 zx_pager_query_vmo_stats()
使用 ZX_PAGER_RESET_VMO_STATS
选项的调用,只有第一个调用
查看 modified
集。由于在首次更新后没有进行任何其他修改,
zx_pager_query_vmo_stats()
,第二个 zx_pager_query_vmo_stats()
将
返回 modified
为 0。
实现
分页器回写已经开发了一段时间,其中包含了所有新的 API 部分,
适用于 @next
vDSO。fxfs
已采用回写 API 来支持
数据流读/写和 mmap
。
性能
借助分页器回写,fxfs
能够从执行 I/O 切换到
这使得效果提升约 40-60 倍
改进了各种基准测试
安全注意事项
无。
隐私注意事项
无。
测试
已编写内核核心测试和压力测试来练习分页器 系统调用。此外还有存储测试和性能基准测试。
文档
更新了内核系统调用文档。
缺点、替代方案和未知问题
速率限制回写请求
在未来的迭代中,当内核生成回写请求时(在 内存压力或稳定的后台速率),我们需要某种 用于控制在寻呼机端口排队的回写请求数的策略。 一种选择是让内核跟踪 并尽量将其控制在一定的限制范围内
另一种方法是让用户分页器配置可调优参数,以确定 直接或间接地回写请求生成速率。例如: 用户寻呼机可以指定建议的网页保留时长 分页器可以支持写入。可能有文件系统需要后台运行 回写速率远高于全局系统默认值。也可能是 有助于用户分页器指定粒度(系统页面的倍数) 以最大尺寸处理请求。然后,内核会将此 计算范围时,可能可以生成 。
跟踪和查询网页存在时间信息
对于初始实现,内核会跟踪页面队列中的脏页, 排序依据是页面首次更新的时间此队列可用于 这样,如果以后生成回写请求 它们可以从脏队列移至(干净)分页器支持的队列中, 目前可跟踪只读页面并存在时间限制。我们可能需要更精细地 还可以跟踪脏页的存在时间;统一脏话和整洁 将页面移到一个通用池中,以利用老化和已访问位跟踪 w.r.t.一个全局工作集。我们还可能会泄露这些年龄信息 通过新的 API 传递给用户分页器,以便在处理时将其考虑在内 回写请求。
在回写期间阻止进一步的写入
此处提出的设计不会阻止
正在回写,即在 ZX_PAGER_OP_WRITEBACK_BEGIN
和
ZX_PAGER_OP_WRITEBACK_END
。相反,被写入的网页简单地
重新标记为脏状态某些文件系统可能希望
AwaitingClean
状态的网页。我们可以考虑添加一个
ZX_PAGER_OP_WRITEBACK_BEING_SYNC
(将来会屏蔽),这会在
回写。请注意,ZX_VMO_TRAP_DIRTY
v1 确实提供了一种解决此问题的方法
若是 ZX_PAGER_VMO_DIRTY
分页请求,文件系统可以暂缓发送
。