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