1use crate::file::ErofsFile;
6use fuchsia_async as fasync;
7use std::collections::HashMap;
8use std::ops::Range;
9use std::sync::{Arc, Mutex, Weak};
10use zx::sys::zx_page_request_command_t::{ZX_PAGER_VMO_COMPLETE, ZX_PAGER_VMO_READ};
11
12const READAHEAD_ALIGNMENT: u64 = 128 * 1024;
13
14pub enum FileHolder {
22 Strong(Arc<ErofsFile>),
23 Weak(Weak<ErofsFile>),
24}
25
26pub struct ErofsPager {
28 pager: Arc<zx::Pager>,
29 registrations: Mutex<HashMap<u64, fasync::ReceiverRegistration<ErofsPacketReceiver>>>,
30}
31
32impl ErofsPager {
33 pub fn new() -> Result<Self, zx::Status> {
35 let pager = Arc::new(zx::Pager::create(zx::PagerOptions::empty())?);
36 Ok(Self { pager, registrations: Mutex::new(HashMap::new()) })
37 }
38
39 pub fn create_vmo(
42 &self,
43 file: Weak<ErofsFile>,
44 initial_size: u64,
45 ) -> Result<(zx::Vmo, u64), zx::Status> {
46 storage_trace::duration!("storage", "ErofsPager::create_vmo", "size" => initial_size);
47 let receiver = ErofsPacketReceiver {
48 file: Mutex::new(FileHolder::Weak(file)),
49 pager: self.pager.clone(),
50 };
51
52 let registration = fasync::EHandle::local().register_receiver(receiver);
53 let key = registration.key();
54
55 let vmo = self
56 .pager
57 .create_vmo(zx::VmoOptions::empty(), fasync::EHandle::local().port(), key, initial_size)
58 .map_err(|e| {
59 log::error!("self.pager.create_vmo failed: {:?}", e);
60 e
61 })?;
62
63 self.registrations.lock().unwrap().insert(key, registration);
64 Ok((vmo, key))
65 }
66
67 pub fn watch_for_zero_children(&self, key: u64, vmo: &zx::Vmo) -> Result<bool, zx::Status> {
74 let registrations = self.registrations.lock().unwrap();
75 let registration = registrations.get(&key).ok_or(zx::Status::BAD_STATE)?;
76 let mut file_holder = registration.receiver().file.lock().unwrap();
77 match &*file_holder {
78 FileHolder::Weak(weak) => {
79 let strong = weak.upgrade().ok_or(zx::Status::BAD_STATE)?;
80 vmo.wait_async(
81 fasync::EHandle::local().port(),
82 key,
83 zx::Signals::VMO_ZERO_CHILDREN,
84 zx::WaitAsyncOpts::empty(),
85 )?;
86 *file_holder = FileHolder::Strong(strong);
87 Ok(true)
88 }
89 FileHolder::Strong(_) => Ok(false),
90 }
91 }
92
93 pub fn unregister(&self, key: u64) {
95 let registration = self.registrations.lock().unwrap().remove(&key);
96 drop(registration);
97 }
98
99 pub fn unregister_all(&self) {
101 let registrations = std::mem::take(&mut *self.registrations.lock().unwrap());
102 for (_, registration) in registrations {
103 let prev = std::mem::replace(
104 &mut *registration.receiver().file.lock().unwrap(),
105 FileHolder::Weak(Weak::new()),
106 );
107 drop(prev);
108 drop(registration);
109 }
110 }
111}
112
113pub struct ErofsPacketReceiver {
115 pub(crate) file: Mutex<FileHolder>,
116 pager: Arc<zx::Pager>,
117}
118
119impl ErofsPacketReceiver {
120 fn get_file(&self) -> Result<Arc<ErofsFile>, zx::Status> {
121 let holder = self.file.lock().unwrap();
122 match &*holder {
123 FileHolder::Strong(strong) => Ok(strong.clone()),
124 FileHolder::Weak(weak) => weak.upgrade().ok_or(zx::Status::BAD_STATE),
125 }
126 }
127
128 fn page_in(&self, range: Range<u64>) -> Result<(), zx::Status> {
129 let file = self.get_file()?;
130 storage_trace::duration!(
131 "storage",
132 "ErofsPager::page_in",
133 "nid" => file.node().nid(),
134 "offset" => range.start,
135 "len" => range.end - range.start
136 );
137
138 let readahead_start = (range.start / READAHEAD_ALIGNMENT) * READAHEAD_ALIGNMENT;
140 let mut readahead_end =
141 ((range.end + READAHEAD_ALIGNMENT - 1) / READAHEAD_ALIGNMENT) * READAHEAD_ALIGNMENT;
142
143 let vmo_size = file.vmo().get_size()?;
145 readahead_end = std::cmp::min(readahead_end, vmo_size);
146
147 if readahead_end <= readahead_start {
148 return Ok(());
149 }
150
151 let len = readahead_end - readahead_start;
152
153 let mut buf = vec![0u8; len as usize];
156 let read_bytes =
157 file.fs().read_file_range(file.node(), readahead_start, &mut buf).map_err(|e| {
158 log::error!("Read EROFS file range failed: {:?}", e);
159 e.to_status()
160 })?;
161
162 if read_bytes < buf.len() {
163 buf[read_bytes..].fill(0);
164 }
165
166 let aux_vmo = zx::Vmo::create(len)?;
167 aux_vmo.write(&buf, 0)?;
168
169 self.pager.supply_pages(file.vmo(), readahead_start..readahead_end, &aux_vmo, 0)?;
170 Ok(())
171 }
172
173 fn receive_signal_packet(&self, signals: zx::SignalPacket) {
180 storage_trace::duration!("storage", "ErofsPager::signal_zero_children");
181 assert!(signals.observed().contains(zx::Signals::VMO_ZERO_CHILDREN));
182
183 let mut file_holder = self.file.lock().unwrap();
184 let strong = match &*file_holder {
185 FileHolder::Strong(strong) => strong.clone(),
186 FileHolder::Weak(_) => return,
187 };
188
189 match strong.vmo().info() {
190 Ok(info) => {
191 if info.num_children == 0 {
192 let weak = FileHolder::Weak(Arc::downgrade(&strong));
193 *file_holder = weak;
194 } else {
195 if let Err(e) = strong.register_zero_children_wait() {
197 log::error!("Failed to re-register VMO_ZERO_CHILDREN wait: {:?}", e);
198 }
199 }
200 }
201 Err(e) => {
202 log::error!("Failed to query VMO info: {:?}", e);
203 }
204 }
205 }
206
207 fn receive_pager_packet(&self, contents: zx::PagerPacket) {
208 let command = contents.command();
209 if command == ZX_PAGER_VMO_COMPLETE {
210 storage_trace::duration!("storage", "ErofsPager::vmo_complete");
211 return;
212 }
213
214 let range = contents.range();
215 if command != ZX_PAGER_VMO_READ {
217 if let Ok(file) = self.get_file() {
218 let _ = self.pager.op_range(
219 zx::PagerOp::Fail(zx::Status::NOT_SUPPORTED),
220 file.vmo(),
221 range,
222 );
223 }
224 return;
225 }
226
227 if let Err(e) = self.page_in(range.clone()) {
228 log::error!("Page fault handler failed: {:?}", e);
229 if let Ok(file) = self.get_file() {
230 let _ = self.pager.op_range(zx::PagerOp::Fail(e), file.vmo(), range);
231 }
232 }
233 }
234}
235
236impl fasync::PacketReceiver for ErofsPacketReceiver {
237 fn receive_packet(&self, packet: zx::Packet) {
238 match packet.contents() {
239 zx::PacketContents::Pager(contents) => {
240 self.receive_pager_packet(contents);
241 }
242 zx::PacketContents::SignalOne(signals) => {
243 self.receive_signal_packet(signals);
244 }
245 _ => {}
246 }
247 }
248}