1use crate::{
8 AsHandleRef, HandleBased, HandleRef, NullableHandle, Port, Status, Vmo, VmoOptions, ok, sys,
9};
10use bitflags::bitflags;
11
12#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
17#[repr(transparent)]
18pub struct Pager(NullableHandle);
19impl_handle_based!(Pager);
20
21bitflags! {
22 #[repr(transparent)]
24 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
25 pub struct PagerOptions: u32 {
26 }
27}
28
29bitflags! {
30 #[repr(transparent)]
31 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
32 pub struct PagerWritebackBeginOptions: u64 {
33 const DIRTY_RANGE_IS_ZERO = sys::ZX_VMO_DIRTY_RANGE_IS_ZERO;
34 }
35}
36
37pub enum PagerOp {
38 Fail(Status),
39 Dirty,
40 WritebackBegin(PagerWritebackBeginOptions),
41 WritebackEnd,
42}
43
44impl Pager {
45 pub fn create(options: PagerOptions) -> Result<Pager, Status> {
47 let mut out = 0;
48 let status = unsafe { sys::zx_pager_create(options.bits(), &mut out) };
49 ok(status)?;
50 Ok(Pager::from(unsafe { NullableHandle::from_raw(out) }))
51 }
52
53 pub fn create_vmo(
55 &self,
56 options: VmoOptions,
57 port: &Port,
58 key: u64,
59 size: u64,
60 ) -> Result<Vmo, Status> {
61 let mut out = 0;
62 let status = unsafe {
63 sys::zx_pager_create_vmo(
64 self.raw_handle(),
65 options.bits(),
66 port.raw_handle(),
67 key,
68 size,
69 &mut out,
70 )
71 };
72 ok(status)?;
73 Ok(Vmo::from(unsafe { NullableHandle::from_raw(out) }))
74 }
75
76 pub fn detach_vmo(&self, vmo: &Vmo) -> Result<(), Status> {
78 let status = unsafe { sys::zx_pager_detach_vmo(self.raw_handle(), vmo.raw_handle()) };
79 ok(status)
80 }
81
82 pub fn supply_pages(
84 &self,
85 vmo: &Vmo,
86 range: std::ops::Range<u64>,
87 aux_vmo: &Vmo,
88 aux_offset: u64,
89 ) -> Result<(), Status> {
90 let status = unsafe {
91 sys::zx_pager_supply_pages(
92 self.raw_handle(),
93 vmo.raw_handle(),
94 range.start,
95 range.end - range.start,
96 aux_vmo.raw_handle(),
97 aux_offset,
98 )
99 };
100 ok(status)
101 }
102
103 pub fn op_range(
105 &self,
106 op: PagerOp,
107 pager_vmo: &Vmo,
108 range: std::ops::Range<u64>,
109 ) -> Result<(), Status> {
110 let (op, data) = match op {
111 PagerOp::Fail(status) => (sys::ZX_PAGER_OP_FAIL, status.into_raw() as u64),
112 PagerOp::Dirty => (sys::ZX_PAGER_OP_DIRTY, 0),
113 PagerOp::WritebackBegin(options) => (sys::ZX_PAGER_OP_WRITEBACK_BEGIN, options.bits()),
114 PagerOp::WritebackEnd => (sys::ZX_PAGER_OP_WRITEBACK_END, 0),
115 };
116 let status = unsafe {
117 sys::zx_pager_op_range(
118 self.raw_handle(),
119 op,
120 pager_vmo.raw_handle(),
121 range.start,
122 range.end - range.start,
123 data,
124 )
125 };
126 ok(status)
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use crate as zx;
133 use std::sync::Arc;
134
135 const KEY: u64 = 5;
136
137 #[test]
138 fn create_vmo() {
139 let port = zx::Port::create();
140 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
141 let vmo = pager.create_vmo(zx::VmoOptions::RESIZABLE, &port, KEY, 100).unwrap();
142 let vmo_info = vmo.info().unwrap();
143 assert!(vmo_info.flags.contains(zx::VmoInfoFlags::PAGER_BACKED));
144 assert!(vmo_info.flags.contains(zx::VmoInfoFlags::RESIZABLE));
145 }
146
147 #[test]
148 fn detach_vmo() {
149 let port = zx::Port::create();
150 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
151 let vmo = pager.create_vmo(zx::VmoOptions::empty(), &port, KEY, 100).unwrap();
152 pager.detach_vmo(&vmo).unwrap();
153
154 let mut data = [0u8; 100];
157 let e = vmo.read(&mut data, 0).expect_err("A detached vmo should fail reads");
158 assert_eq!(e, zx::Status::BAD_STATE);
159 }
160
161 #[test]
162 fn supply_pages() {
163 let page_size: u64 = zx::system_get_page_size().into();
164 let port = zx::Port::create();
165 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
166 let vmo =
167 Arc::new(pager.create_vmo(zx::VmoOptions::empty(), &port, KEY, page_size).unwrap());
168
169 let read_thread = std::thread::spawn({
170 let vmo = vmo.clone();
171 move || {
172 let mut data = [0u8; 100];
173 vmo.read(&mut data, 0).unwrap();
174 }
175 });
176
177 let packet = port.wait(zx::MonotonicInstant::INFINITE).unwrap();
178 assert_eq!(packet.key(), KEY);
179 match packet.contents() {
180 zx::PacketContents::Pager(request) => {
181 assert_eq!(
182 request.command(),
183 zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ
184 );
185 assert_eq!(request.range(), 0..page_size);
186 let aux_vmo = zx::Vmo::create(page_size).unwrap();
187 pager.supply_pages(vmo.as_ref(), request.range(), &aux_vmo, 0).unwrap();
188 }
189 packet => panic!("Unexpected packet: {:?}", packet),
190 }
191 read_thread.join().unwrap();
192 }
193
194 #[test]
195 fn fail_page_request() {
196 let page_size: u64 = zx::system_get_page_size().into();
197 let port = zx::Port::create();
198 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
199 let vmo =
200 Arc::new(pager.create_vmo(zx::VmoOptions::empty(), &port, KEY, page_size).unwrap());
201
202 let read_thread = std::thread::spawn({
203 let vmo = vmo.clone();
204 move || {
205 let mut data = [0u8; 100];
206 let e = vmo.read(&mut data, 0).expect_err("Request should have failed");
207 assert_eq!(e, zx::Status::NO_SPACE);
208 }
209 });
210
211 let packet = port.wait(zx::MonotonicInstant::INFINITE).unwrap();
212 assert_eq!(packet.key(), KEY);
213 match packet.contents() {
214 zx::PacketContents::Pager(request) => {
215 assert_eq!(
216 request.command(),
217 zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ
218 );
219 assert_eq!(request.range(), 0..page_size);
220 pager
221 .op_range(
222 zx::PagerOp::Fail(zx::Status::NO_SPACE),
223 vmo.as_ref(),
224 request.range(),
225 )
226 .unwrap();
227 }
228 packet => panic!("Unexpected packet: {:?}", packet),
229 }
230 read_thread.join().unwrap();
231 }
232
233 #[test]
234 fn pager_writeback() {
235 let page_size: u64 = zx::system_get_page_size().into();
236 let port = zx::Port::create();
237 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
238 let vmo =
239 Arc::new(pager.create_vmo(zx::VmoOptions::TRAP_DIRTY, &port, KEY, page_size).unwrap());
240
241 let write_thread = std::thread::spawn({
242 let vmo = vmo.clone();
243 move || {
244 let data = [0u8; 100];
245 vmo.write(&data, 0).unwrap();
246 }
247 });
248
249 let packet = port.wait(zx::MonotonicInstant::INFINITE).unwrap();
250 assert_eq!(packet.key(), KEY);
251 match packet.contents() {
252 zx::PacketContents::Pager(request) => {
253 assert_eq!(
254 request.command(),
255 zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_READ
256 );
257 assert_eq!(request.range(), 0..page_size);
258 let aux_vmo = zx::Vmo::create(page_size).unwrap();
259 pager.supply_pages(vmo.as_ref(), request.range(), &aux_vmo, 0).unwrap();
260 }
261 packet => panic!("Unexpected packet: {:?}", packet),
262 }
263
264 let packet = port.wait(zx::MonotonicInstant::INFINITE).unwrap();
265 assert_eq!(packet.key(), KEY);
266 match packet.contents() {
267 zx::PacketContents::Pager(request) => {
268 assert_eq!(
269 request.command(),
270 zx::sys::zx_page_request_command_t::ZX_PAGER_VMO_DIRTY
271 );
272 assert_eq!(request.range(), 0..page_size);
273 pager.op_range(zx::PagerOp::Dirty, vmo.as_ref(), request.range()).unwrap();
274 }
275 packet => panic!("Unexpected packet: {:?}", packet),
276 }
277
278 write_thread.join().unwrap();
279
280 pager
282 .op_range(
283 zx::PagerOp::WritebackBegin(zx::PagerWritebackBeginOptions::empty()),
284 vmo.as_ref(),
285 0..page_size,
286 )
287 .unwrap();
288 pager.op_range(zx::PagerOp::WritebackEnd, vmo.as_ref(), 0..page_size).unwrap();
289 }
291
292 #[test]
293 fn pager_writeback_begin_with_dirtied_zero_range() {
294 let page_size: u64 = zx::system_get_page_size().into();
295 let port = zx::Port::create();
296 let pager = zx::Pager::create(zx::PagerOptions::empty()).unwrap();
297 let vmo = pager.create_vmo(zx::VmoOptions::RESIZABLE, &port, KEY, page_size).unwrap();
298 let aux_vmo = zx::Vmo::create(page_size).unwrap();
299 pager.supply_pages(&vmo, 0..page_size, &aux_vmo, 0).unwrap();
300 vmo.set_size(page_size * 3).unwrap();
301
302 vmo.write(&[0, 1, 2, 3], page_size * 2).unwrap();
306 pager
307 .op_range(
308 zx::PagerOp::WritebackBegin(zx::PagerWritebackBeginOptions::DIRTY_RANGE_IS_ZERO),
309 &vmo,
310 page_size..(page_size * 2),
311 )
312 .unwrap();
313 pager.op_range(zx::PagerOp::WritebackEnd, &vmo, page_size..(page_size * 2)).unwrap();
314
315 }
318}