1use anyhow::Error;
5use block_protocol::{BlockFifoRequest, BlockFifoResponse};
6use fblock::{BlockIoFlag, BlockOpcode, MAX_TRANSFER_UNBOUNDED};
7use fidl_fuchsia_storage_block as fblock;
8use fuchsia_async as fasync;
9use fuchsia_async::epoch::{Epoch, EpochGuard};
10use fuchsia_sync::{MappedMutexGuard, Mutex, MutexGuard};
11use futures::{Future, FutureExt as _, TryStreamExt as _};
12use slab::Slab;
13use std::borrow::{Borrow, Cow};
14use std::collections::BTreeMap;
15use std::num::NonZero;
16use std::ops::Range;
17use std::sync::Arc;
18use std::sync::atomic::AtomicU64;
19use storage_device::buffer::Buffer;
20
21pub mod async_interface;
22pub mod c_interface;
23pub mod callback_interface;
24
25#[cfg(test)]
26mod decompression_tests;
27
28pub(crate) const FIFO_MAX_REQUESTS: usize = 64;
29
30type TraceFlowId = Option<NonZero<u64>>;
31
32#[derive(Clone)]
33pub enum DeviceInfo {
34 Block(BlockInfo),
35 Partition(PartitionInfo),
36}
37
38impl DeviceInfo {
39 pub fn label(&self) -> &str {
40 match self {
41 Self::Block(BlockInfo { .. }) => "",
42 Self::Partition(PartitionInfo { name, .. }) => name,
43 }
44 }
45 pub fn device_flags(&self) -> fblock::DeviceFlag {
46 match self {
47 Self::Block(BlockInfo { device_flags, .. }) => *device_flags,
48 Self::Partition(PartitionInfo { device_flags, .. }) => *device_flags,
49 }
50 }
51
52 pub fn block_count(&self) -> Option<u64> {
53 match self {
54 Self::Block(BlockInfo { block_count, .. }) => Some(*block_count),
55 Self::Partition(PartitionInfo { block_range, .. }) => {
56 block_range.as_ref().map(|range| range.end - range.start)
57 }
58 }
59 }
60
61 pub fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
62 match self {
63 Self::Block(BlockInfo { max_transfer_blocks, .. }) => max_transfer_blocks.clone(),
64 Self::Partition(PartitionInfo { max_transfer_blocks, .. }) => {
65 max_transfer_blocks.clone()
66 }
67 }
68 }
69
70 fn max_transfer_size(&self, block_size: u32) -> u32 {
71 if let Some(max_blocks) = self.max_transfer_blocks() {
72 max_blocks.get() * block_size
73 } else {
74 MAX_TRANSFER_UNBOUNDED
75 }
76 }
77}
78
79#[derive(Clone, Default)]
81pub struct BlockInfo {
82 pub device_flags: fblock::DeviceFlag,
83 pub block_count: u64,
84 pub max_transfer_blocks: Option<NonZero<u32>>,
85}
86
87#[derive(Clone, Default)]
89pub struct PartitionInfo {
90 pub device_flags: fblock::DeviceFlag,
92 pub max_transfer_blocks: Option<NonZero<u32>>,
93 pub block_range: Option<Range<u64>>,
97 pub type_guid: [u8; 16],
98 pub instance_guid: [u8; 16],
99 pub name: String,
100 pub flags: u64,
101}
102
103struct ActiveRequest<S> {
106 session: S,
107 group_or_request: GroupOrRequest,
108 trace_flow_id: TraceFlowId,
109 _epoch_guard: EpochGuard<'static>,
110 status: zx::Status,
111 count: u32,
112 req_id: Option<u32>,
113 decompression_info: Option<DecompressionInfo>,
114}
115
116struct DecompressionInfo {
117 compressed_range: Range<usize>,
119
120 uncompressed_range: Range<u64>,
122
123 bytes_so_far: u64,
124 mapping: Arc<VmoMapping>,
125 buffer: Option<Buffer<'static>>,
126}
127
128impl DecompressionInfo {
129 fn uncompressed_slice(&self) -> *mut [u8] {
131 std::ptr::slice_from_raw_parts_mut(
132 (self.mapping.base + self.uncompressed_range.start as usize) as *mut u8,
133 (self.uncompressed_range.end - self.uncompressed_range.start) as usize,
134 )
135 }
136}
137
138pub struct ActiveRequests<S>(Mutex<ActiveRequestsInner<S>>);
139
140impl<S> Default for ActiveRequests<S> {
141 fn default() -> Self {
142 Self(Mutex::new(ActiveRequestsInner { requests: Slab::default() }))
143 }
144}
145
146impl<S> ActiveRequests<S> {
147 fn complete_and_take_response(
148 &self,
149 request_id: RequestId,
150 status: zx::Status,
151 ) -> Option<(S, BlockFifoResponse)> {
152 self.0.lock().complete_and_take_response(request_id, status)
153 }
154
155 fn request(&self, request_id: RequestId) -> MappedMutexGuard<'_, ActiveRequest<S>> {
156 MutexGuard::map(self.0.lock(), |i| &mut i.requests[request_id.0])
157 }
158}
159
160struct ActiveRequestsInner<S> {
161 requests: Slab<ActiveRequest<S>>,
162}
163
164impl<S> ActiveRequestsInner<S> {
166 fn complete(&mut self, request_id: RequestId, status: zx::Status) {
168 let group = &mut self.requests[request_id.0];
169
170 group.count = group.count.checked_sub(1).unwrap();
171 if status != zx::Status::OK && group.status == zx::Status::OK {
172 group.status = status
173 }
174
175 fuchsia_trace::duration!(
176 "storage",
177 "block_server::finish_transaction",
178 "request_id" => request_id.0,
179 "group_completed" => group.count == 0,
180 "status" => status.into_raw());
181 if let Some(trace_flow_id) = group.trace_flow_id {
182 fuchsia_trace::flow_step!(
183 "storage",
184 "block_server::finish_request",
185 trace_flow_id.get().into()
186 );
187 }
188
189 if group.count == 0
190 && group.status == zx::Status::OK
191 && let Some(info) = &mut group.decompression_info
192 {
193 thread_local! {
194 static DECOMPRESSOR: std::cell::RefCell<zstd::bulk::Decompressor<'static>> =
195 std::cell::RefCell::new(zstd::bulk::Decompressor::new().unwrap());
196 }
197 DECOMPRESSOR.with(|decompressor| {
198 let target_slice = unsafe { info.uncompressed_slice().as_mut().unwrap() };
200 let mut decompressor = decompressor.borrow_mut();
201 if let Err(error) = decompressor.decompress_to_buffer(
202 &info.buffer.take().unwrap().as_slice()[info.compressed_range.clone()],
203 target_slice,
204 ) {
205 log::warn!(error:?; "Decompression error");
206 group.status = zx::Status::IO_DATA_INTEGRITY;
207 };
208 });
209 }
210 }
211
212 fn take_response(&mut self, request_id: RequestId) -> Option<(S, BlockFifoResponse)> {
214 let group = &self.requests[request_id.0];
215 match group.req_id {
216 Some(reqid) if group.count == 0 => {
217 let group = self.requests.remove(request_id.0);
218 Some((
219 group.session,
220 BlockFifoResponse {
221 status: group.status.into_raw(),
222 reqid,
223 group: group.group_or_request.group_id().unwrap_or(0),
224 ..Default::default()
225 },
226 ))
227 }
228 _ => None,
229 }
230 }
231
232 fn complete_and_take_response(
234 &mut self,
235 request_id: RequestId,
236 status: zx::Status,
237 ) -> Option<(S, BlockFifoResponse)> {
238 self.complete(request_id, status);
239 self.take_response(request_id)
240 }
241}
242
243pub struct BlockServer<SM: SessionManager> {
246 block_size: u32,
247 orchestrator: Arc<SM::Orchestrator>,
248}
249
250#[derive(Debug)]
252pub struct BlockOffsetMapping {
253 source_block_offset: u64,
254 target_block_offset: u64,
255 length: u64,
256}
257
258impl BlockOffsetMapping {
259 fn are_blocks_within_source_range(&self, blocks: (u64, u32)) -> bool {
260 blocks.0 >= self.source_block_offset
261 && blocks.0 + blocks.1 as u64 - self.source_block_offset <= self.length
262 }
263}
264
265impl std::convert::TryFrom<fblock::BlockOffsetMapping> for BlockOffsetMapping {
266 type Error = zx::Status;
267
268 fn try_from(wire: fblock::BlockOffsetMapping) -> Result<Self, Self::Error> {
269 wire.source_block_offset.checked_add(wire.length).ok_or(zx::Status::INVALID_ARGS)?;
270 wire.target_block_offset.checked_add(wire.length).ok_or(zx::Status::INVALID_ARGS)?;
271 Ok(Self {
272 source_block_offset: wire.source_block_offset,
273 target_block_offset: wire.target_block_offset,
274 length: wire.length,
275 })
276 }
277}
278
279pub struct OffsetMap {
281 mapping: Option<BlockOffsetMapping>,
282 max_transfer_blocks: Option<NonZero<u32>>,
283}
284
285impl OffsetMap {
286 pub fn new(mapping: BlockOffsetMapping, max_transfer_blocks: Option<NonZero<u32>>) -> Self {
288 Self { mapping: Some(mapping), max_transfer_blocks }
289 }
290
291 pub fn empty(max_transfer_blocks: Option<NonZero<u32>>) -> Self {
293 Self { mapping: None, max_transfer_blocks }
294 }
295
296 pub fn is_empty(&self) -> bool {
297 self.mapping.is_none()
298 }
299
300 fn mapping(&self) -> Option<&BlockOffsetMapping> {
301 self.mapping.as_ref()
302 }
303
304 fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
305 self.max_transfer_blocks
306 }
307}
308
309pub trait SessionManager: 'static {
312 type Orchestrator: Borrow<Self> + Send + Sync;
317
318 const SUPPORTS_DECOMPRESSION: bool;
319
320 type Session;
321
322 fn session_eq(a: &Self::Session, b: &Self::Session) -> bool;
326
327 fn on_attach_vmo(
328 orchestrator: Arc<Self::Orchestrator>,
329 vmo: &Arc<zx::Vmo>,
330 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
331
332 fn open_session(
337 orchestrator: Arc<Self::Orchestrator>,
338 stream: fblock::SessionRequestStream,
339 offset_map: OffsetMap,
340 block_size: u32,
341 ) -> impl Future<Output = Result<(), Error>> + Send;
342
343 fn get_info(&self) -> Cow<'_, DeviceInfo>;
345
346 fn get_volume_info(
348 &self,
349 ) -> impl Future<Output = Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status>> + Send
350 {
351 async { Err(zx::Status::NOT_SUPPORTED) }
352 }
353
354 fn query_slices(
356 &self,
357 _start_slices: &[u64],
358 ) -> impl Future<Output = Result<Vec<fblock::VsliceRange>, zx::Status>> + Send {
359 async { Err(zx::Status::NOT_SUPPORTED) }
360 }
361
362 fn extend(
364 &self,
365 _start_slice: u64,
366 _slice_count: u64,
367 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
368 async { Err(zx::Status::NOT_SUPPORTED) }
369 }
370
371 fn shrink(
373 &self,
374 _start_slice: u64,
375 _slice_count: u64,
376 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
377 async { Err(zx::Status::NOT_SUPPORTED) }
378 }
379
380 fn active_requests(&self) -> &ActiveRequests<Self::Session>;
382}
383
384pub trait IntoOrchestrator {
388 type SM: SessionManager;
389
390 fn into_orchestrator(self) -> Arc<<Self::SM as SessionManager>::Orchestrator>;
391}
392
393impl<SM: SessionManager> BlockServer<SM> {
394 pub fn new(block_size: u32, orchestrator: impl IntoOrchestrator<SM = SM>) -> Self {
395 Self { block_size, orchestrator: orchestrator.into_orchestrator() }
396 }
397
398 pub fn session_manager(&self) -> &SM {
399 self.orchestrator.as_ref().borrow()
400 }
401
402 pub async fn handle_requests(
404 &self,
405 mut requests: fblock::BlockRequestStream,
406 ) -> Result<(), Error> {
407 let scope = fasync::Scope::new();
408 loop {
409 match requests.try_next().await {
410 Ok(Some(request)) => {
411 if let Some(session) = self.handle_request(request).await? {
412 scope.spawn(session.map(|_| ()));
413 }
414 }
415 Ok(None) => break,
416 Err(err) => log::warn!(err:?; "Invalid request"),
417 }
418 }
419 scope.await;
420 Ok(())
421 }
422
423 async fn handle_request(
426 &self,
427 request: fblock::BlockRequest,
428 ) -> Result<Option<impl Future<Output = Result<(), Error>> + Send + use<SM>>, Error> {
429 match request {
430 fblock::BlockRequest::GetInfo { responder } => {
431 let info = self.device_info();
432 let max_transfer_size = info.max_transfer_size(self.block_size);
433 let (block_count, mut flags) = match info.as_ref() {
434 DeviceInfo::Block(BlockInfo { block_count, device_flags, .. }) => {
435 (*block_count, *device_flags)
436 }
437 DeviceInfo::Partition(partition_info) => {
438 let block_count = if let Some(range) = partition_info.block_range.as_ref() {
439 range.end - range.start
440 } else {
441 let volume_info = self.session_manager().get_volume_info().await?;
442 volume_info.0.slice_size * volume_info.1.partition_slice_count
443 / self.block_size as u64
444 };
445 (block_count, partition_info.device_flags)
446 }
447 };
448 if SM::SUPPORTS_DECOMPRESSION {
449 flags |= fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT;
450 }
451 responder.send(Ok(&fblock::BlockInfo {
452 block_count,
453 block_size: self.block_size,
454 max_transfer_size,
455 flags,
456 }))?;
457 }
458 fblock::BlockRequest::OpenSession { session, control_handle: _ } => {
459 let info = self.device_info();
460 return Ok(Some(SM::open_session(
461 self.orchestrator.clone(),
462 session.into_stream(),
463 OffsetMap::empty(info.max_transfer_blocks()),
464 self.block_size,
465 )));
466 }
467 fblock::BlockRequest::OpenSessionWithOffsetMap {
468 session,
469 mapping,
470 control_handle: _,
471 } => {
472 let info = self.device_info();
473 let initial_mapping: BlockOffsetMapping = match mapping.try_into() {
474 Ok(m) => m,
475 Err(status) => {
476 session.close_with_epitaph(status)?;
477 return Ok(None);
478 }
479 };
480 if let Some(max) = info.block_count() {
481 if initial_mapping.target_block_offset + initial_mapping.length > max {
482 log::warn!("Invalid mapping for session: {initial_mapping:?} (max {max})");
483 session.close_with_epitaph(zx::Status::INVALID_ARGS)?;
484 return Ok(None);
485 }
486 }
487 return Ok(Some(SM::open_session(
488 self.orchestrator.clone(),
489 session.into_stream(),
490 OffsetMap::new(initial_mapping, info.max_transfer_blocks()),
491 self.block_size,
492 )));
493 }
494 fblock::BlockRequest::GetTypeGuid { responder } => {
495 let info = self.device_info();
496 if let DeviceInfo::Partition(partition_info) = info.as_ref() {
497 let mut guid = fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
498 guid.value.copy_from_slice(&partition_info.type_guid);
499 responder.send(zx::sys::ZX_OK, Some(&guid))?;
500 } else {
501 responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?;
502 }
503 }
504 fblock::BlockRequest::GetInstanceGuid { responder } => {
505 let info = self.device_info();
506 if let DeviceInfo::Partition(partition_info) = info.as_ref() {
507 let mut guid = fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
508 guid.value.copy_from_slice(&partition_info.instance_guid);
509 responder.send(zx::sys::ZX_OK, Some(&guid))?;
510 } else {
511 responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?;
512 }
513 }
514 fblock::BlockRequest::GetName { responder } => {
515 let info = self.device_info();
516 if let DeviceInfo::Partition(partition_info) = info.as_ref() {
517 responder.send(zx::sys::ZX_OK, Some(&partition_info.name))?;
518 } else {
519 responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?;
520 }
521 }
522 fblock::BlockRequest::GetMetadata { responder } => {
523 let info = self.device_info();
524 if let DeviceInfo::Partition(info) = info.as_ref() {
525 let mut type_guid = fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
526 type_guid.value.copy_from_slice(&info.type_guid);
527 let mut instance_guid =
528 fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
529 instance_guid.value.copy_from_slice(&info.instance_guid);
530 responder.send(Ok(&fblock::BlockGetMetadataResponse {
531 name: Some(info.name.clone()),
532 type_guid: Some(type_guid),
533 instance_guid: Some(instance_guid),
534 start_block_offset: info.block_range.as_ref().map(|range| range.start),
535 num_blocks: info.block_range.as_ref().map(|range| range.end - range.start),
536 flags: Some(info.flags),
537 ..Default::default()
538 }))?;
539 } else {
540 responder.send(Err(zx::sys::ZX_ERR_NOT_SUPPORTED))?;
541 }
542 }
543 fblock::BlockRequest::QuerySlices { responder, start_slices } => {
544 match self.session_manager().query_slices(&start_slices).await {
545 Ok(mut results) => {
546 let results_len = results.len();
547 assert!(results_len <= 16);
548 results.resize(16, fblock::VsliceRange { allocated: false, count: 0 });
549 responder.send(
550 zx::sys::ZX_OK,
551 &results.try_into().unwrap(),
552 results_len as u64,
553 )?;
554 }
555 Err(s) => {
556 responder.send(
557 s.into_raw(),
558 &[fblock::VsliceRange { allocated: false, count: 0 }; 16],
559 0,
560 )?;
561 }
562 }
563 }
564 fblock::BlockRequest::GetVolumeInfo { responder, .. } => {
565 match self.session_manager().get_volume_info().await {
566 Ok((manager_info, volume_info)) => {
567 responder.send(zx::sys::ZX_OK, Some(&manager_info), Some(&volume_info))?
568 }
569 Err(s) => responder.send(s.into_raw(), None, None)?,
570 }
571 }
572 fblock::BlockRequest::Extend { responder, start_slice, slice_count } => {
573 responder.send(
574 zx::Status::from(self.session_manager().extend(start_slice, slice_count).await)
575 .into_raw(),
576 )?;
577 }
578 fblock::BlockRequest::Shrink { responder, start_slice, slice_count } => {
579 responder.send(
580 zx::Status::from(self.session_manager().shrink(start_slice, slice_count).await)
581 .into_raw(),
582 )?;
583 }
584 fblock::BlockRequest::Destroy { responder, .. } => {
585 responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED)?;
586 }
587 }
588 Ok(None)
589 }
590
591 fn device_info(&self) -> Cow<'_, DeviceInfo> {
592 self.session_manager().get_info()
593 }
594}
595
596struct SessionHelper<SM: SessionManager> {
597 orchestrator: Arc<SM::Orchestrator>,
598 offset_map: OffsetMap,
599 block_size: u32,
600 peer_fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest>,
601 vmos: Mutex<BTreeMap<u16, (Arc<zx::Vmo>, Option<Arc<VmoMapping>>)>>,
602}
603
604struct VmoMapping {
605 base: usize,
606 size: usize,
607}
608
609impl VmoMapping {
610 fn new(vmo: &zx::Vmo) -> Result<Arc<Self>, zx::Status> {
611 let size = vmo.get_size().unwrap() as usize;
612 Ok(Arc::new(Self {
613 base: fuchsia_runtime::vmar_root_self()
614 .map(0, vmo, 0, size, zx::VmarFlags::PERM_WRITE | zx::VmarFlags::PERM_READ)
615 .inspect_err(|error| {
616 log::warn!(error:?, size; "VmoMapping: unable to map VMO");
617 })?,
618 size,
619 }))
620 }
621}
622
623impl Drop for VmoMapping {
624 fn drop(&mut self) {
625 unsafe {
627 let _ = fuchsia_runtime::vmar_root_self().unmap(self.base, self.size);
628 }
629 }
630}
631
632enum HandleRequestResult {
633 Ok,
635 Closed(Box<dyn FnOnce() + Send + 'static>),
639}
640
641impl<SM: SessionManager> SessionHelper<SM> {
642 fn new(
643 orchestrator: Arc<SM::Orchestrator>,
644 offset_map: OffsetMap,
645 block_size: u32,
646 ) -> Result<(Self, zx::Fifo<BlockFifoRequest, BlockFifoResponse>), zx::Status> {
647 let (peer_fifo, fifo) = zx::Fifo::create(16)?;
648 Ok((Self { orchestrator, offset_map, block_size, peer_fifo, vmos: Mutex::default() }, fifo))
649 }
650
651 fn session_manager(&self) -> &SM {
652 self.orchestrator.as_ref().borrow()
653 }
654
655 async fn handle_request(
656 &self,
657 request: fblock::SessionRequest,
658 ) -> Result<HandleRequestResult, Error> {
659 match request {
660 fblock::SessionRequest::GetFifo { responder } => {
661 let rights = zx::Rights::TRANSFER
662 | zx::Rights::READ
663 | zx::Rights::WRITE
664 | zx::Rights::SIGNAL
665 | zx::Rights::WAIT;
666 match self.peer_fifo.duplicate_handle(rights) {
667 Ok(fifo) => responder.send(Ok(fifo.downcast()))?,
668 Err(s) => responder.send(Err(s.into_raw()))?,
669 }
670 Ok(HandleRequestResult::Ok)
671 }
672 fblock::SessionRequest::AttachVmo { vmo, responder } => {
673 let vmo = Arc::new(vmo);
674 let vmo_id = {
675 let mut vmos = self.vmos.lock();
676 if vmos.len() == u16::MAX as usize {
677 responder.send(Err(zx::Status::NO_RESOURCES.into_raw()))?;
678 return Ok(HandleRequestResult::Ok);
679 } else {
680 let vmo_id = match vmos.last_entry() {
681 None => 1,
682 Some(o) => {
683 o.key().checked_add(1).unwrap_or_else(|| {
684 let mut vmo_id = 1;
685 for (&id, _) in &*vmos {
687 if id > vmo_id {
688 break;
689 }
690 vmo_id = id + 1;
691 }
692 vmo_id
693 })
694 }
695 };
696 vmos.insert(vmo_id, (vmo.clone(), None));
697 vmo_id
698 }
699 };
700 SM::on_attach_vmo(self.orchestrator.clone(), &vmo).await?;
701 responder.send(Ok(&fblock::VmoId { id: vmo_id }))?;
702 Ok(HandleRequestResult::Ok)
703 }
704 fblock::SessionRequest::Close { responder } => {
705 Ok(HandleRequestResult::Closed(Box::new(move || {
706 if let Err(err) = responder.send(Ok(())) {
707 log::warn!(err:?; "Error sending close response");
708 }
709 })))
710 }
711 }
712 }
713
714 fn decode_fifo_request(
716 &self,
717 session: SM::Session,
718 request: &BlockFifoRequest,
719 ) -> Result<DecodedRequest, Option<BlockFifoResponse>> {
720 let flags = BlockIoFlag::from_bits_truncate(request.command.flags);
721
722 let request_bytes = request.length as u64 * self.block_size as u64;
723
724 let mut operation = BlockOpcode::from_primitive(request.command.opcode)
725 .ok_or(zx::Status::INVALID_ARGS)
726 .and_then(|code| {
727 if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
728 if code != BlockOpcode::Read {
729 return Err(zx::Status::INVALID_ARGS);
730 }
731 if !SM::SUPPORTS_DECOMPRESSION {
732 return Err(zx::Status::NOT_SUPPORTED);
733 }
734 }
735 if matches!(code, BlockOpcode::Read | BlockOpcode::Write | BlockOpcode::Trim) {
736 if request.length == 0 {
737 return Err(zx::Status::INVALID_ARGS);
738 }
739 if request.dev_offset.checked_add(request.length as u64).is_none() {
741 return Err(zx::Status::OUT_OF_RANGE);
742 }
743 }
744 if matches!(code, BlockOpcode::Read | BlockOpcode::Write) {
745 let vmo_byte_offset = request
746 .vmo_offset
747 .checked_mul(self.block_size as u64)
748 .ok_or(zx::Status::OUT_OF_RANGE)?;
749 if request_bytes.checked_add(vmo_byte_offset).is_none() {
750 return Err(zx::Status::OUT_OF_RANGE);
751 }
752 }
753 Ok(match code {
754 BlockOpcode::Read => Operation::Read {
755 device_block_offset: request.dev_offset,
756 block_count: request.length,
757 _unused: 0,
758 vmo_offset: request
759 .vmo_offset
760 .checked_mul(self.block_size as u64)
761 .ok_or(zx::Status::OUT_OF_RANGE)?,
762 options: ReadOptions {
763 inline_crypto: InlineCryptoOptions {
764 is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
765 dun: request.dun,
766 slot: request.slot,
767 },
768 },
769 },
770 BlockOpcode::Write => {
771 let mut options = WriteOptions {
772 inline_crypto: InlineCryptoOptions {
773 is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
774 dun: request.dun,
775 slot: request.slot,
776 },
777 ..WriteOptions::default()
778 };
779 if flags.contains(BlockIoFlag::FORCE_ACCESS) {
780 options.flags |= WriteFlags::FORCE_ACCESS;
781 }
782 if flags.contains(BlockIoFlag::PRE_BARRIER) {
783 options.flags |= WriteFlags::PRE_BARRIER;
784 }
785 Operation::Write {
786 device_block_offset: request.dev_offset,
787 block_count: request.length,
788 _unused: 0,
789 options,
790 vmo_offset: request
791 .vmo_offset
792 .checked_mul(self.block_size as u64)
793 .ok_or(zx::Status::OUT_OF_RANGE)?,
794 }
795 }
796 BlockOpcode::Flush => Operation::Flush,
797 BlockOpcode::Trim => Operation::Trim {
798 device_block_offset: request.dev_offset,
799 block_count: request.length,
800 },
801 BlockOpcode::CloseVmo => Operation::CloseVmo,
802 })
803 });
804
805 let group_or_request = if flags.contains(BlockIoFlag::GROUP_ITEM) {
806 GroupOrRequest::Group(request.group)
807 } else {
808 GroupOrRequest::Request(request.reqid)
809 };
810
811 let mut active_requests = self.session_manager().active_requests().0.lock();
812 let mut request_id = None;
813
814 if group_or_request.is_group() {
825 for (key, group) in &mut active_requests.requests {
829 if group.group_or_request == group_or_request
830 && SM::session_eq(&group.session, &session)
831 {
832 if group.req_id.is_some() {
833 if group.status == zx::Status::OK {
835 group.status = zx::Status::INVALID_ARGS;
836 }
837 return Err(None);
839 }
840 if group.status == zx::Status::OK
842 && let Some(info) = &mut group.decompression_info
843 {
844 if let Ok(Operation::Read {
845 device_block_offset,
846 mut block_count,
847 options,
848 vmo_offset: 0,
849 ..
850 }) = operation
851 {
852 let remaining_bytes = info
853 .compressed_range
854 .end
855 .next_multiple_of(self.block_size as usize)
856 as u64
857 - info.bytes_so_far;
858 if !flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD)
859 || request.total_compressed_bytes != 0
860 || request.uncompressed_bytes != 0
861 || request.compressed_prefix_bytes != 0
862 || (flags.contains(BlockIoFlag::GROUP_LAST)
863 && info.bytes_so_far + request_bytes
864 < info.compressed_range.end as u64)
865 || (!flags.contains(BlockIoFlag::GROUP_LAST)
866 && request_bytes >= remaining_bytes)
867 {
868 group.status = zx::Status::INVALID_ARGS;
869 } else {
870 if request_bytes > remaining_bytes {
879 block_count = (remaining_bytes / self.block_size as u64) as u32;
880 }
881
882 operation = Ok(Operation::ContinueDecompressedRead {
883 offset: info.bytes_so_far,
884 device_block_offset,
885 block_count,
886 options,
887 });
888
889 info.bytes_so_far += block_count as u64 * self.block_size as u64;
890 }
891 } else {
892 group.status = zx::Status::INVALID_ARGS;
893 }
894 }
895 if flags.contains(BlockIoFlag::GROUP_LAST) {
896 group.req_id = Some(request.reqid);
897 if group.status != zx::Status::OK {
900 operation = Err(group.status);
901 }
902 } else if group.status != zx::Status::OK {
903 return Err(None);
906 }
907 request_id = Some(RequestId(key));
908 group.count += 1;
909 break;
910 }
911 }
912 }
913
914 let is_single_request =
915 !flags.contains(BlockIoFlag::GROUP_ITEM) || flags.contains(BlockIoFlag::GROUP_LAST);
916
917 let mut decompression_info = None;
918 let vmo = match operation {
919 Ok(Operation::Read {
920 device_block_offset,
921 mut block_count,
922 options,
923 vmo_offset,
924 ..
925 }) => match self.vmos.lock().get_mut(&request.vmoid) {
926 Some((vmo, mapping)) => {
927 if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
928 let compressed_range = request.compressed_prefix_bytes as usize
929 ..request.compressed_prefix_bytes as usize
930 + request.total_compressed_bytes as usize;
931 let required_buffer_size =
932 compressed_range.end.next_multiple_of(self.block_size as usize);
933
934 if compressed_range.start >= compressed_range.end
936 || vmo_offset.checked_add(request.uncompressed_bytes as u64).is_none()
937 || (is_single_request && request_bytes < compressed_range.end as u64)
938 || (!is_single_request && request_bytes >= required_buffer_size as u64)
939 {
940 Err(zx::Status::INVALID_ARGS)
941 } else {
942 let bytes_so_far = if request_bytes > required_buffer_size as u64 {
949 block_count =
950 (required_buffer_size / self.block_size as usize) as u32;
951 required_buffer_size as u64
952 } else {
953 request_bytes
954 };
955
956 match mapping {
958 Some(mapping) => Ok(mapping.clone()),
959 None => {
960 VmoMapping::new(&vmo).inspect(|m| *mapping = Some(m.clone()))
961 }
962 }
963 .and_then(|mapping| {
964 if vmo_offset
967 .checked_add(request.uncompressed_bytes as u64)
968 .is_some_and(|end| end <= mapping.size as u64)
969 {
970 Ok(mapping)
971 } else {
972 Err(zx::Status::OUT_OF_RANGE)
973 }
974 })
975 .map(|mapping| {
976 operation = Ok(Operation::StartDecompressedRead {
981 required_buffer_size,
982 device_block_offset,
983 block_count,
984 options,
985 });
986 decompression_info = Some(DecompressionInfo {
989 compressed_range,
990 bytes_so_far,
991 mapping,
992 uncompressed_range: vmo_offset
993 ..vmo_offset + request.uncompressed_bytes as u64,
994 buffer: None,
995 });
996 None
997 })
998 }
999 } else {
1000 Ok(Some(vmo.clone()))
1001 }
1002 }
1003 None => Err(zx::Status::IO),
1004 },
1005 Ok(Operation::Write { .. }) => self
1006 .vmos
1007 .lock()
1008 .get(&request.vmoid)
1009 .cloned()
1010 .map_or(Err(zx::Status::IO), |(vmo, _)| Ok(Some(vmo))),
1011 Ok(Operation::CloseVmo) => {
1012 self.vmos.lock().remove(&request.vmoid).map_or(Err(zx::Status::IO), |(vmo, _)| {
1013 let vmo_clone = vmo.clone();
1014 Epoch::global().defer(move || drop(vmo_clone));
1017 Ok(Some(vmo))
1018 })
1019 }
1020 _ => Ok(None),
1021 }
1022 .unwrap_or_else(|e| {
1023 operation = Err(e);
1024 None
1025 });
1026
1027 let trace_flow_id = NonZero::new(request.trace_flow_id);
1028 let request_id = request_id.unwrap_or_else(|| {
1029 RequestId(active_requests.requests.insert(ActiveRequest {
1030 session,
1031 group_or_request,
1032 trace_flow_id,
1033 _epoch_guard: Epoch::global().guard(),
1034 status: zx::Status::OK,
1035 count: 1,
1036 req_id: is_single_request.then_some(request.reqid),
1037 decompression_info,
1038 }))
1039 });
1040
1041 Ok(DecodedRequest {
1042 request_id,
1043 trace_flow_id,
1044 operation: operation.map_err(|status| {
1045 active_requests.complete_and_take_response(request_id, status).map(|(_, r)| r)
1046 })?,
1047 vmo,
1048 })
1049 }
1050
1051 fn take_vmos(&self) -> BTreeMap<u16, (Arc<zx::Vmo>, Option<Arc<VmoMapping>>)> {
1052 std::mem::take(&mut *self.vmos.lock())
1053 }
1054
1055 fn map_request(
1057 &self,
1058 mut request: DecodedRequest,
1059 active_request: &mut ActiveRequest<SM::Session>,
1060 ) -> Result<(DecodedRequest, Option<DecodedRequest>), zx::Status> {
1061 if active_request.status != zx::Status::OK {
1062 return Err(zx::Status::BAD_STATE);
1063 }
1064 let mapping = self.offset_map.mapping();
1065 match (mapping, request.operation.blocks()) {
1066 (Some(mapping), Some(blocks)) if !mapping.are_blocks_within_source_range(blocks) => {
1067 return Err(zx::Status::OUT_OF_RANGE);
1068 }
1069 _ => {}
1070 }
1071 let remainder = request.operation.map(
1072 self.offset_map.mapping(),
1073 self.offset_map.max_transfer_blocks(),
1074 self.block_size,
1075 );
1076 if remainder.is_some() {
1077 active_request.count += 1;
1078 }
1079 static CACHE: AtomicU64 = AtomicU64::new(0);
1080 if let Some(context) =
1081 fuchsia_trace::TraceCategoryContext::acquire_cached("storage", &CACHE)
1082 {
1083 use fuchsia_trace::ArgValue;
1084 let trace_args = [
1085 ArgValue::of("request_id", request.request_id.0),
1086 ArgValue::of("opcode", request.operation.trace_label()),
1087 ];
1088 let _scope =
1089 fuchsia_trace::duration("storage", "block_server::start_transaction", &trace_args);
1090 if let Some(trace_flow_id) = active_request.trace_flow_id {
1091 fuchsia_trace::flow_step(
1092 &context,
1093 "block_server::start_transaction",
1094 trace_flow_id.get().into(),
1095 &[],
1096 );
1097 }
1098 }
1099 let remainder = remainder.map(|operation| DecodedRequest { operation, ..request.clone() });
1100 Ok((request, remainder))
1101 }
1102
1103 fn drop_active_requests(&self, pred: impl Fn(&SM::Session) -> bool) {
1108 self.session_manager().active_requests().0.lock().requests.retain(|_, r| !pred(&r.session));
1109 }
1110
1111 fn close_active_groups(&self, pred: impl Fn(&SM::Session) -> bool) {
1124 self.session_manager().active_requests().0.lock().requests.retain(|_, request| {
1125 if !pred(&request.session) || request.req_id.is_some() {
1126 return true;
1127 }
1128 request.req_id = Some(u32::MAX);
1131 request.count > 0
1132 });
1133 }
1134}
1135
1136#[repr(transparent)]
1137#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
1138pub struct RequestId(usize);
1139
1140#[derive(Clone, Debug)]
1141struct DecodedRequest {
1142 request_id: RequestId,
1143 trace_flow_id: TraceFlowId,
1144 operation: Operation,
1145 vmo: Option<Arc<zx::Vmo>>,
1146}
1147
1148pub type WriteFlags = block_protocol::WriteFlags;
1150pub type WriteOptions = block_protocol::WriteOptions;
1151pub type ReadOptions = block_protocol::ReadOptions;
1152pub type InlineCryptoOptions = block_protocol::InlineCryptoOptions;
1153
1154#[repr(C)]
1155#[derive(Clone, Debug, PartialEq, Eq)]
1156pub enum Operation {
1157 Read {
1162 device_block_offset: u64,
1163 block_count: u32,
1164 _unused: u32,
1165 vmo_offset: u64,
1166 options: ReadOptions,
1167 },
1168 Write {
1169 device_block_offset: u64,
1170 block_count: u32,
1171 _unused: u32,
1172 vmo_offset: u64,
1173 options: WriteOptions,
1174 },
1175 Flush,
1176 Trim {
1177 device_block_offset: u64,
1178 block_count: u32,
1179 },
1180 CloseVmo,
1182 StartDecompressedRead {
1184 required_buffer_size: usize,
1185 device_block_offset: u64,
1186 block_count: u32,
1187 options: ReadOptions,
1188 },
1189 ContinueDecompressedRead {
1191 offset: u64,
1192 device_block_offset: u64,
1193 block_count: u32,
1194 options: ReadOptions,
1195 },
1196}
1197
1198impl Operation {
1199 fn trace_label(&self) -> &'static str {
1200 match self {
1201 Operation::Read { .. } => "read",
1202 Operation::Write { .. } => "write",
1203 Operation::Flush { .. } => "flush",
1204 Operation::Trim { .. } => "trim",
1205 Operation::CloseVmo { .. } => "close_vmo",
1206 Operation::StartDecompressedRead { .. } => "start_decompressed_read",
1207 Operation::ContinueDecompressedRead { .. } => "continue_decompressed_read",
1208 }
1209 }
1210
1211 fn blocks(&self) -> Option<(u64, u32)> {
1213 match self {
1214 Operation::Read { device_block_offset, block_count, .. }
1215 | Operation::Write { device_block_offset, block_count, .. }
1216 | Operation::Trim { device_block_offset, block_count, .. } => {
1217 Some((*device_block_offset, *block_count))
1218 }
1219 _ => None,
1220 }
1221 }
1222
1223 fn blocks_mut(&mut self) -> Option<(&mut u64, &mut u32)> {
1225 match self {
1226 Operation::Read { device_block_offset, block_count, .. }
1227 | Operation::Write { device_block_offset, block_count, .. }
1228 | Operation::Trim { device_block_offset, block_count, .. } => {
1229 Some((device_block_offset, block_count))
1230 }
1231 _ => None,
1232 }
1233 }
1234
1235 fn map(
1238 &mut self,
1239 mapping: Option<&BlockOffsetMapping>,
1240 max_blocks: Option<NonZero<u32>>,
1241 block_size: u32,
1242 ) -> Option<Self> {
1243 let mut max = match self {
1244 Operation::Read { .. } | Operation::Write { .. } => max_blocks.map(|m| m.get() as u64),
1245 _ => None,
1246 };
1247 let (offset, length) = self.blocks_mut()?;
1248 let orig_offset = *offset;
1249 if let Some(mapping) = mapping {
1250 let delta = *offset - mapping.source_block_offset;
1251 debug_assert!(*offset - mapping.source_block_offset < mapping.length);
1252 *offset = mapping.target_block_offset + delta;
1253 let mapping_max = mapping.target_block_offset + mapping.length - *offset;
1254 max = match max {
1255 None => Some(mapping_max),
1256 Some(m) => Some(std::cmp::min(m, mapping_max)),
1257 };
1258 };
1259 if let Some(max) = max {
1260 if *length as u64 > max {
1261 let rem = (*length as u64 - max) as u32;
1262 *length = max as u32;
1263 return Some(match self {
1264 Operation::Read {
1265 device_block_offset: _,
1266 block_count: _,
1267 vmo_offset,
1268 _unused,
1269 options,
1270 } => {
1271 let mut options = *options;
1272 options.inline_crypto.dun += max as u32;
1273 Operation::Read {
1274 device_block_offset: orig_offset + max,
1275 block_count: rem,
1276 vmo_offset: *vmo_offset + max * block_size as u64,
1277 _unused: *_unused,
1278 options: options,
1279 }
1280 }
1281 Operation::Write {
1282 device_block_offset: _,
1283 block_count: _,
1284 _unused,
1285 vmo_offset,
1286 options,
1287 } => {
1288 let mut options = *options;
1289 options.inline_crypto.dun += max as u32;
1290 Operation::Write {
1291 device_block_offset: orig_offset + max,
1292 block_count: rem,
1293 _unused: *_unused,
1294 vmo_offset: *vmo_offset + max * block_size as u64,
1295 options: options,
1296 }
1297 }
1298 Operation::Trim { device_block_offset: _, block_count: _ } => {
1299 Operation::Trim { device_block_offset: orig_offset + max, block_count: rem }
1300 }
1301 _ => unreachable!(),
1302 });
1303 }
1304 }
1305 None
1306 }
1307
1308 pub fn has_write_flag(&self, value: WriteFlags) -> bool {
1310 if let Operation::Write { options, .. } = self {
1311 options.flags.contains(value)
1312 } else {
1313 false
1314 }
1315 }
1316
1317 pub fn take_write_flag(&mut self, value: WriteFlags) -> bool {
1319 if let Operation::Write { options, .. } = self {
1320 let result = options.flags.contains(value);
1321 options.flags.remove(value);
1322 result
1323 } else {
1324 false
1325 }
1326 }
1327}
1328
1329#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
1330pub enum GroupOrRequest {
1331 Group(u16),
1332 Request(u32),
1333}
1334
1335impl GroupOrRequest {
1336 fn is_group(&self) -> bool {
1337 matches!(self, Self::Group(_))
1338 }
1339
1340 fn group_id(&self) -> Option<u16> {
1341 match self {
1342 Self::Group(id) => Some(*id),
1343 Self::Request(_) => None,
1344 }
1345 }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350 use super::{
1351 BlockOffsetMapping, BlockServer, DeviceInfo, FIFO_MAX_REQUESTS, Operation, PartitionInfo,
1352 TraceFlowId,
1353 };
1354 use assert_matches::assert_matches;
1355 use block_protocol::{
1356 BlockFifoCommand, BlockFifoRequest, BlockFifoResponse, InlineCryptoOptions, ReadOptions,
1357 WriteFlags, WriteOptions,
1358 };
1359 use fidl_fuchsia_storage_block as fblock;
1360 use fidl_fuchsia_storage_block::{BlockIoFlag, BlockOpcode};
1361 use fuchsia_async as fasync;
1362 use fuchsia_sync::Mutex;
1363 use futures::FutureExt as _;
1364 use futures::channel::oneshot;
1365 use futures::future::BoxFuture;
1366 use std::borrow::Cow;
1367 use std::future::poll_fn;
1368 use std::num::NonZero;
1369 use std::pin::pin;
1370 use std::sync::Arc;
1371 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
1372 use std::task::{Context, Poll};
1373
1374 #[derive(Default)]
1375 struct MockInterface {
1376 read_hook: Option<
1377 Box<
1378 dyn Fn(u64, u32, &Arc<zx::Vmo>, u64) -> BoxFuture<'static, Result<(), zx::Status>>
1379 + Send
1380 + Sync,
1381 >,
1382 >,
1383 write_hook:
1384 Option<Box<dyn Fn(u64) -> BoxFuture<'static, Result<(), zx::Status>> + Send + Sync>>,
1385 barrier_hook: Option<Box<dyn Fn() -> Result<(), zx::Status> + Send + Sync>>,
1386 }
1387
1388 impl super::async_interface::Interface for MockInterface {
1389 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
1390 Ok(())
1391 }
1392
1393 fn get_info(&self) -> Cow<'_, DeviceInfo> {
1394 Cow::Owned(test_device_info())
1395 }
1396
1397 async fn read(
1398 &self,
1399 device_block_offset: u64,
1400 block_count: u32,
1401 vmo: &Arc<zx::Vmo>,
1402 vmo_offset: u64,
1403 _opts: ReadOptions,
1404 _trace_flow_id: TraceFlowId,
1405 ) -> Result<(), zx::Status> {
1406 if let Some(read_hook) = &self.read_hook {
1407 read_hook(device_block_offset, block_count, vmo, vmo_offset).await
1408 } else {
1409 unimplemented!();
1410 }
1411 }
1412
1413 async fn write(
1414 &self,
1415 device_block_offset: u64,
1416 _block_count: u32,
1417 _vmo: &Arc<zx::Vmo>,
1418 _vmo_offset: u64,
1419 opts: WriteOptions,
1420 _trace_flow_id: TraceFlowId,
1421 ) -> Result<(), zx::Status> {
1422 if opts.flags.contains(WriteFlags::PRE_BARRIER)
1423 && let Some(barrier_hook) = &self.barrier_hook
1424 {
1425 barrier_hook()?;
1426 }
1427 if let Some(write_hook) = &self.write_hook {
1428 write_hook(device_block_offset).await
1429 } else {
1430 unimplemented!();
1431 }
1432 }
1433
1434 async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
1435 Ok(())
1436 }
1437
1438 async fn trim(
1439 &self,
1440 _device_block_offset: u64,
1441 _block_count: u32,
1442 _trace_flow_id: TraceFlowId,
1443 ) -> Result<(), zx::Status> {
1444 unreachable!();
1445 }
1446
1447 async fn get_volume_info(
1448 &self,
1449 ) -> Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status> {
1450 let () = std::future::pending().await;
1452 unreachable!();
1453 }
1454 }
1455
1456 const BLOCK_SIZE: u32 = 512;
1457 const MAX_TRANSFER_BLOCKS: u32 = 10;
1458
1459 fn test_device_info() -> DeviceInfo {
1460 DeviceInfo::Partition(PartitionInfo {
1461 device_flags: fblock::DeviceFlag::READONLY
1462 | fblock::DeviceFlag::BARRIER_SUPPORT
1463 | fblock::DeviceFlag::FUA_SUPPORT,
1464 max_transfer_blocks: NonZero::new(MAX_TRANSFER_BLOCKS),
1465 block_range: Some(0..100),
1466 type_guid: [1; 16],
1467 instance_guid: [2; 16],
1468 name: "foo".to_string(),
1469 flags: 0xabcd,
1470 })
1471 }
1472
1473 #[fuchsia::test]
1474 async fn test_barriers_ordering() {
1475 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1476 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
1477 let barrier_called = Arc::new(AtomicBool::new(false));
1478
1479 futures::join!(
1480 async move {
1481 let barrier_called_clone = barrier_called.clone();
1482 let block_server = BlockServer::new(
1483 BLOCK_SIZE,
1484 Arc::new(MockInterface {
1485 barrier_hook: Some(Box::new(move || {
1486 barrier_called.store(true, Ordering::Relaxed);
1487 Ok(())
1488 })),
1489 write_hook: Some(Box::new(move |device_block_offset| {
1490 let barrier_called = barrier_called_clone.clone();
1491 Box::pin(async move {
1492 if device_block_offset % 2 == 0 {
1494 fasync::Timer::new(fasync::MonotonicInstant::after(
1495 zx::MonotonicDuration::from_millis(200),
1496 ))
1497 .await;
1498 }
1499 assert!(barrier_called.load(Ordering::Relaxed));
1500 Ok(())
1501 })
1502 })),
1503 ..MockInterface::default()
1504 }),
1505 );
1506 block_server.handle_requests(stream).await.unwrap();
1507 },
1508 async move {
1509 let (session_proxy, server) = fidl::endpoints::create_proxy();
1510
1511 proxy.open_session(server).unwrap();
1512
1513 let vmo_id = session_proxy
1514 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1515 .await
1516 .unwrap()
1517 .unwrap();
1518 assert_ne!(vmo_id.id, 0);
1519
1520 let mut fifo =
1521 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1522 let (mut reader, mut writer) = fifo.async_io();
1523
1524 writer
1525 .write_entries(&BlockFifoRequest {
1526 command: BlockFifoCommand {
1527 opcode: BlockOpcode::Write.into_primitive(),
1528 flags: BlockIoFlag::PRE_BARRIER.bits(),
1529 ..Default::default()
1530 },
1531 vmoid: vmo_id.id,
1532 dev_offset: 0,
1533 length: 5,
1534 vmo_offset: 6,
1535 ..Default::default()
1536 })
1537 .await
1538 .unwrap();
1539
1540 for i in 0..10 {
1541 writer
1542 .write_entries(&BlockFifoRequest {
1543 command: BlockFifoCommand {
1544 opcode: BlockOpcode::Write.into_primitive(),
1545 ..Default::default()
1546 },
1547 vmoid: vmo_id.id,
1548 dev_offset: i + 1,
1549 length: 5,
1550 vmo_offset: 6,
1551 ..Default::default()
1552 })
1553 .await
1554 .unwrap();
1555 }
1556 for _ in 0..11 {
1557 let mut response = BlockFifoResponse::default();
1558 reader.read_entries(&mut response).await.unwrap();
1559 assert_eq!(response.status, zx::sys::ZX_OK);
1560 }
1561
1562 std::mem::drop(proxy);
1563 }
1564 );
1565 }
1566
1567 #[fuchsia::test]
1568 async fn test_info() {
1569 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1570
1571 futures::join!(
1572 async {
1573 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1574 block_server.handle_requests(stream).await.unwrap();
1575 },
1576 async {
1577 let expected_info = test_device_info();
1578 let partition_info = if let DeviceInfo::Partition(info) = &expected_info {
1579 info
1580 } else {
1581 unreachable!()
1582 };
1583
1584 let block_info = proxy.get_info().await.unwrap().unwrap();
1585 assert_eq!(
1586 block_info.block_count,
1587 partition_info.block_range.as_ref().unwrap().end
1588 - partition_info.block_range.as_ref().unwrap().start
1589 );
1590 assert_eq!(
1591 block_info.flags,
1592 fblock::DeviceFlag::READONLY
1593 | fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT
1594 | fblock::DeviceFlag::BARRIER_SUPPORT
1595 | fblock::DeviceFlag::FUA_SUPPORT
1596 );
1597
1598 assert_eq!(block_info.max_transfer_size, MAX_TRANSFER_BLOCKS * BLOCK_SIZE);
1599
1600 let (status, type_guid) = proxy.get_type_guid().await.unwrap();
1601 assert_eq!(status, zx::sys::ZX_OK);
1602 assert_eq!(&type_guid.as_ref().unwrap().value, &partition_info.type_guid);
1603
1604 let (status, instance_guid) = proxy.get_instance_guid().await.unwrap();
1605 assert_eq!(status, zx::sys::ZX_OK);
1606 assert_eq!(&instance_guid.as_ref().unwrap().value, &partition_info.instance_guid);
1607
1608 let (status, name) = proxy.get_name().await.unwrap();
1609 assert_eq!(status, zx::sys::ZX_OK);
1610 assert_eq!(name.as_ref(), Some(&partition_info.name));
1611
1612 let metadata = proxy.get_metadata().await.unwrap().expect("get_flags failed");
1613 assert_eq!(metadata.name, name);
1614 assert_eq!(metadata.type_guid.as_ref(), type_guid.as_deref());
1615 assert_eq!(metadata.instance_guid.as_ref(), instance_guid.as_deref());
1616 assert_eq!(
1617 metadata.start_block_offset,
1618 Some(partition_info.block_range.as_ref().unwrap().start)
1619 );
1620 assert_eq!(metadata.num_blocks, Some(block_info.block_count));
1621 assert_eq!(metadata.flags, Some(partition_info.flags));
1622
1623 std::mem::drop(proxy);
1624 }
1625 );
1626 }
1627
1628 #[fuchsia::test]
1629 async fn test_attach_vmo() {
1630 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1631
1632 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
1633 let koid = vmo.koid().unwrap();
1634
1635 futures::join!(
1636 async {
1637 let block_server = BlockServer::new(
1638 BLOCK_SIZE,
1639 Arc::new(MockInterface {
1640 read_hook: Some(Box::new(move |_, _, vmo, _| {
1641 assert_eq!(vmo.koid().unwrap(), koid);
1642 Box::pin(async { Ok(()) })
1643 })),
1644 ..MockInterface::default()
1645 }),
1646 );
1647 block_server.handle_requests(stream).await.unwrap();
1648 },
1649 async move {
1650 let (session_proxy, server) = fidl::endpoints::create_proxy();
1651
1652 proxy.open_session(server).unwrap();
1653
1654 let vmo_id = session_proxy
1655 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1656 .await
1657 .unwrap()
1658 .unwrap();
1659 assert_ne!(vmo_id.id, 0);
1660
1661 let mut fifo =
1662 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1663 let (mut reader, mut writer) = fifo.async_io();
1664
1665 let mut count = 1;
1667 loop {
1668 match session_proxy
1669 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1670 .await
1671 .unwrap()
1672 {
1673 Ok(vmo_id) => assert_ne!(vmo_id.id, 0),
1674 Err(e) => {
1675 assert_eq!(e, zx::sys::ZX_ERR_NO_RESOURCES);
1676 break;
1677 }
1678 }
1679
1680 if count % 10 == 0 {
1682 writer
1683 .write_entries(&BlockFifoRequest {
1684 command: BlockFifoCommand {
1685 opcode: BlockOpcode::Read.into_primitive(),
1686 ..Default::default()
1687 },
1688 vmoid: vmo_id.id,
1689 length: 1,
1690 ..Default::default()
1691 })
1692 .await
1693 .unwrap();
1694
1695 let mut response = BlockFifoResponse::default();
1696 reader.read_entries(&mut response).await.unwrap();
1697 assert_eq!(response.status, zx::sys::ZX_OK);
1698 }
1699
1700 count += 1;
1701 }
1702
1703 assert_eq!(count, u16::MAX as u64);
1704
1705 writer
1707 .write_entries(&BlockFifoRequest {
1708 command: BlockFifoCommand {
1709 opcode: BlockOpcode::CloseVmo.into_primitive(),
1710 ..Default::default()
1711 },
1712 vmoid: vmo_id.id,
1713 ..Default::default()
1714 })
1715 .await
1716 .unwrap();
1717
1718 let mut response = BlockFifoResponse::default();
1719 reader.read_entries(&mut response).await.unwrap();
1720 assert_eq!(response.status, zx::sys::ZX_OK);
1721
1722 let new_vmo_id = session_proxy
1723 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1724 .await
1725 .unwrap()
1726 .unwrap();
1727 assert_eq!(new_vmo_id.id, vmo_id.id);
1729
1730 std::mem::drop(proxy);
1731 }
1732 );
1733 }
1734
1735 #[fuchsia::test]
1736 async fn test_close() {
1737 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1738
1739 let mut server = std::pin::pin!(
1740 async {
1741 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1742 block_server.handle_requests(stream).await.unwrap();
1743 }
1744 .fuse()
1745 );
1746
1747 let mut client = std::pin::pin!(
1748 async {
1749 let (session_proxy, server) = fidl::endpoints::create_proxy();
1750
1751 proxy.open_session(server).unwrap();
1752
1753 std::mem::drop(proxy);
1756
1757 session_proxy.close().await.unwrap().unwrap();
1758
1759 let _: () = std::future::pending().await;
1761 }
1762 .fuse()
1763 );
1764
1765 futures::select!(
1766 _ = server => {}
1767 _ = client => unreachable!(),
1768 );
1769 }
1770
1771 #[derive(Default)]
1772 struct IoMockInterface {
1773 do_checks: bool,
1774 expected_op: Arc<Mutex<Option<ExpectedOp>>>,
1775 return_errors: bool,
1776 }
1777
1778 #[derive(Debug)]
1779 enum ExpectedOp {
1780 Read(u64, u32, u64),
1781 Write(u64, u32, u64),
1782 Trim(u64, u32),
1783 Flush,
1784 }
1785
1786 impl super::async_interface::Interface for IoMockInterface {
1787 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
1788 Ok(())
1789 }
1790
1791 fn get_info(&self) -> Cow<'_, DeviceInfo> {
1792 Cow::Owned(test_device_info())
1793 }
1794
1795 async fn read(
1796 &self,
1797 device_block_offset: u64,
1798 block_count: u32,
1799 _vmo: &Arc<zx::Vmo>,
1800 vmo_offset: u64,
1801 _opts: ReadOptions,
1802 _trace_flow_id: TraceFlowId,
1803 ) -> Result<(), zx::Status> {
1804 if self.return_errors {
1805 Err(zx::Status::INTERNAL)
1806 } else {
1807 if self.do_checks {
1808 assert_matches!(
1809 self.expected_op.lock().take(),
1810 Some(ExpectedOp::Read(a, b, c)) if device_block_offset == a &&
1811 block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
1812 "Read {device_block_offset} {block_count} {vmo_offset}"
1813 );
1814 }
1815 Ok(())
1816 }
1817 }
1818
1819 async fn write(
1820 &self,
1821 device_block_offset: u64,
1822 block_count: u32,
1823 _vmo: &Arc<zx::Vmo>,
1824 vmo_offset: u64,
1825 _write_opts: WriteOptions,
1826 _trace_flow_id: TraceFlowId,
1827 ) -> Result<(), zx::Status> {
1828 if self.return_errors {
1829 Err(zx::Status::NOT_SUPPORTED)
1830 } else {
1831 if self.do_checks {
1832 assert_matches!(
1833 self.expected_op.lock().take(),
1834 Some(ExpectedOp::Write(a, b, c)) if device_block_offset == a &&
1835 block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
1836 "Write {device_block_offset} {block_count} {vmo_offset}"
1837 );
1838 }
1839 Ok(())
1840 }
1841 }
1842
1843 async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
1844 if self.return_errors {
1845 Err(zx::Status::NO_RESOURCES)
1846 } else {
1847 if self.do_checks {
1848 assert_matches!(self.expected_op.lock().take(), Some(ExpectedOp::Flush));
1849 }
1850 Ok(())
1851 }
1852 }
1853
1854 async fn trim(
1855 &self,
1856 device_block_offset: u64,
1857 block_count: u32,
1858 _trace_flow_id: TraceFlowId,
1859 ) -> Result<(), zx::Status> {
1860 if self.return_errors {
1861 Err(zx::Status::NO_MEMORY)
1862 } else {
1863 if self.do_checks {
1864 assert_matches!(
1865 self.expected_op.lock().take(),
1866 Some(ExpectedOp::Trim(a, b)) if device_block_offset == a &&
1867 block_count == b,
1868 "Trim {device_block_offset} {block_count}"
1869 );
1870 }
1871 Ok(())
1872 }
1873 }
1874 }
1875
1876 #[fuchsia::test]
1877 async fn test_io() {
1878 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1879
1880 let expected_op = Arc::new(Mutex::new(None));
1881 let expected_op_clone = expected_op.clone();
1882
1883 let server = async {
1884 let block_server = BlockServer::new(
1885 BLOCK_SIZE,
1886 Arc::new(IoMockInterface {
1887 return_errors: false,
1888 do_checks: true,
1889 expected_op: expected_op_clone,
1890 }),
1891 );
1892 block_server.handle_requests(stream).await.unwrap();
1893 };
1894
1895 let client = async move {
1896 let (session_proxy, server) = fidl::endpoints::create_proxy();
1897
1898 proxy.open_session(server).unwrap();
1899
1900 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
1901 let vmo_id = session_proxy
1902 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1903 .await
1904 .unwrap()
1905 .unwrap();
1906
1907 let mut fifo =
1908 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1909 let (mut reader, mut writer) = fifo.async_io();
1910
1911 *expected_op.lock() = Some(ExpectedOp::Read(1, 2, 3));
1913 writer
1914 .write_entries(&BlockFifoRequest {
1915 command: BlockFifoCommand {
1916 opcode: BlockOpcode::Read.into_primitive(),
1917 ..Default::default()
1918 },
1919 vmoid: vmo_id.id,
1920 dev_offset: 1,
1921 length: 2,
1922 vmo_offset: 3,
1923 ..Default::default()
1924 })
1925 .await
1926 .unwrap();
1927
1928 let mut response = BlockFifoResponse::default();
1929 reader.read_entries(&mut response).await.unwrap();
1930 assert_eq!(response.status, zx::sys::ZX_OK);
1931
1932 *expected_op.lock() = Some(ExpectedOp::Write(4, 5, 6));
1934 writer
1935 .write_entries(&BlockFifoRequest {
1936 command: BlockFifoCommand {
1937 opcode: BlockOpcode::Write.into_primitive(),
1938 ..Default::default()
1939 },
1940 vmoid: vmo_id.id,
1941 dev_offset: 4,
1942 length: 5,
1943 vmo_offset: 6,
1944 ..Default::default()
1945 })
1946 .await
1947 .unwrap();
1948
1949 let mut response = BlockFifoResponse::default();
1950 reader.read_entries(&mut response).await.unwrap();
1951 assert_eq!(response.status, zx::sys::ZX_OK);
1952
1953 *expected_op.lock() = Some(ExpectedOp::Flush);
1955 writer
1956 .write_entries(&BlockFifoRequest {
1957 command: BlockFifoCommand {
1958 opcode: BlockOpcode::Flush.into_primitive(),
1959 ..Default::default()
1960 },
1961 ..Default::default()
1962 })
1963 .await
1964 .unwrap();
1965
1966 reader.read_entries(&mut response).await.unwrap();
1967 assert_eq!(response.status, zx::sys::ZX_OK);
1968
1969 *expected_op.lock() = Some(ExpectedOp::Trim(7, 8));
1971 writer
1972 .write_entries(&BlockFifoRequest {
1973 command: BlockFifoCommand {
1974 opcode: BlockOpcode::Trim.into_primitive(),
1975 ..Default::default()
1976 },
1977 dev_offset: 7,
1978 length: 8,
1979 ..Default::default()
1980 })
1981 .await
1982 .unwrap();
1983
1984 reader.read_entries(&mut response).await.unwrap();
1985 assert_eq!(response.status, zx::sys::ZX_OK);
1986
1987 std::mem::drop(proxy);
1988 };
1989
1990 futures::join!(server, client);
1991 }
1992
1993 #[fuchsia::test]
1994 async fn test_io_errors() {
1995 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1996
1997 futures::join!(
1998 async {
1999 let block_server = BlockServer::new(
2000 BLOCK_SIZE,
2001 Arc::new(IoMockInterface {
2002 return_errors: true,
2003 do_checks: false,
2004 expected_op: Arc::new(Mutex::new(None)),
2005 }),
2006 );
2007 block_server.handle_requests(stream).await.unwrap();
2008 },
2009 async move {
2010 let (session_proxy, server) = fidl::endpoints::create_proxy();
2011
2012 proxy.open_session(server).unwrap();
2013
2014 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2015 let vmo_id = session_proxy
2016 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2017 .await
2018 .unwrap()
2019 .unwrap();
2020
2021 let mut fifo =
2022 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2023 let (mut reader, mut writer) = fifo.async_io();
2024
2025 writer
2027 .write_entries(&BlockFifoRequest {
2028 command: BlockFifoCommand {
2029 opcode: BlockOpcode::Read.into_primitive(),
2030 ..Default::default()
2031 },
2032 vmoid: vmo_id.id,
2033 length: 1,
2034 reqid: 1,
2035 ..Default::default()
2036 })
2037 .await
2038 .unwrap();
2039
2040 let mut response = BlockFifoResponse::default();
2041 reader.read_entries(&mut response).await.unwrap();
2042 assert_eq!(response.status, zx::sys::ZX_ERR_INTERNAL);
2043
2044 writer
2046 .write_entries(&BlockFifoRequest {
2047 command: BlockFifoCommand {
2048 opcode: BlockOpcode::Write.into_primitive(),
2049 ..Default::default()
2050 },
2051 vmoid: vmo_id.id,
2052 length: 1,
2053 reqid: 2,
2054 ..Default::default()
2055 })
2056 .await
2057 .unwrap();
2058
2059 reader.read_entries(&mut response).await.unwrap();
2060 assert_eq!(response.status, zx::sys::ZX_ERR_NOT_SUPPORTED);
2061
2062 writer
2064 .write_entries(&BlockFifoRequest {
2065 command: BlockFifoCommand {
2066 opcode: BlockOpcode::Flush.into_primitive(),
2067 ..Default::default()
2068 },
2069 reqid: 3,
2070 ..Default::default()
2071 })
2072 .await
2073 .unwrap();
2074
2075 reader.read_entries(&mut response).await.unwrap();
2076 assert_eq!(response.status, zx::sys::ZX_ERR_NO_RESOURCES);
2077
2078 writer
2080 .write_entries(&BlockFifoRequest {
2081 command: BlockFifoCommand {
2082 opcode: BlockOpcode::Trim.into_primitive(),
2083 ..Default::default()
2084 },
2085 reqid: 4,
2086 length: 1,
2087 ..Default::default()
2088 })
2089 .await
2090 .unwrap();
2091
2092 reader.read_entries(&mut response).await.unwrap();
2093 assert_eq!(response.status, zx::sys::ZX_ERR_NO_MEMORY);
2094
2095 std::mem::drop(proxy);
2096 }
2097 );
2098 }
2099
2100 #[fuchsia::test]
2101 async fn test_invalid_args() {
2102 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2103
2104 futures::join!(
2105 async {
2106 let block_server = BlockServer::new(
2107 BLOCK_SIZE,
2108 Arc::new(IoMockInterface {
2109 return_errors: false,
2110 do_checks: false,
2111 expected_op: Arc::new(Mutex::new(None)),
2112 }),
2113 );
2114 block_server.handle_requests(stream).await.unwrap();
2115 },
2116 async move {
2117 let (session_proxy, server) = fidl::endpoints::create_proxy();
2118
2119 proxy.open_session(server).unwrap();
2120
2121 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2122 let vmo_id = session_proxy
2123 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2124 .await
2125 .unwrap()
2126 .unwrap();
2127
2128 let mut fifo =
2129 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2130
2131 async fn test(
2132 fifo: &mut fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
2133 request: BlockFifoRequest,
2134 ) -> Result<(), zx::Status> {
2135 let (mut reader, mut writer) = fifo.async_io();
2136 writer.write_entries(&request).await.unwrap();
2137 let mut response = BlockFifoResponse::default();
2138 reader.read_entries(&mut response).await.unwrap();
2139 zx::Status::ok(response.status)
2140 }
2141
2142 let good_read_request = || BlockFifoRequest {
2145 command: BlockFifoCommand {
2146 opcode: BlockOpcode::Read.into_primitive(),
2147 ..Default::default()
2148 },
2149 length: 1,
2150 vmoid: vmo_id.id,
2151 ..Default::default()
2152 };
2153
2154 assert_eq!(
2155 test(
2156 &mut fifo,
2157 BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_read_request() }
2158 )
2159 .await,
2160 Err(zx::Status::IO)
2161 );
2162
2163 assert_eq!(
2164 test(
2165 &mut fifo,
2166 BlockFifoRequest {
2167 vmo_offset: 0xffff_ffff_ffff_ffff,
2168 ..good_read_request()
2169 }
2170 )
2171 .await,
2172 Err(zx::Status::OUT_OF_RANGE)
2173 );
2174
2175 assert_eq!(
2176 test(
2177 &mut fifo,
2178 BlockFifoRequest {
2179 vmo_offset: 0x007f_ffff_ffff_ffff,
2180 length: 2,
2181 ..good_read_request()
2182 }
2183 )
2184 .await,
2185 Err(zx::Status::OUT_OF_RANGE)
2186 );
2187
2188 assert_eq!(
2189 test(&mut fifo, BlockFifoRequest { length: 0, ..good_read_request() }).await,
2190 Err(zx::Status::INVALID_ARGS)
2191 );
2192
2193 let good_write_request = || BlockFifoRequest {
2196 command: BlockFifoCommand {
2197 opcode: BlockOpcode::Write.into_primitive(),
2198 ..Default::default()
2199 },
2200 length: 1,
2201 vmoid: vmo_id.id,
2202 ..Default::default()
2203 };
2204
2205 assert_eq!(
2206 test(
2207 &mut fifo,
2208 BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_write_request() }
2209 )
2210 .await,
2211 Err(zx::Status::IO)
2212 );
2213
2214 assert_eq!(
2215 test(
2216 &mut fifo,
2217 BlockFifoRequest {
2218 vmo_offset: 0xffff_ffff_ffff_ffff,
2219 ..good_write_request()
2220 }
2221 )
2222 .await,
2223 Err(zx::Status::OUT_OF_RANGE)
2224 );
2225
2226 assert_eq!(
2227 test(
2228 &mut fifo,
2229 BlockFifoRequest {
2230 vmo_offset: 0x007f_ffff_ffff_ffff,
2231 length: 2,
2232 ..good_write_request()
2233 }
2234 )
2235 .await,
2236 Err(zx::Status::OUT_OF_RANGE)
2237 );
2238
2239 assert_eq!(
2240 test(&mut fifo, BlockFifoRequest { length: 0, ..good_write_request() }).await,
2241 Err(zx::Status::INVALID_ARGS)
2242 );
2243
2244 assert_eq!(
2247 test(
2248 &mut fifo,
2249 BlockFifoRequest {
2250 command: BlockFifoCommand {
2251 opcode: BlockOpcode::CloseVmo.into_primitive(),
2252 ..Default::default()
2253 },
2254 vmoid: vmo_id.id + 1,
2255 ..Default::default()
2256 }
2257 )
2258 .await,
2259 Err(zx::Status::IO)
2260 );
2261
2262 std::mem::drop(proxy);
2263 }
2264 );
2265 }
2266
2267 #[fuchsia::test]
2268 async fn test_concurrent_requests() {
2269 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2270
2271 let waiting_readers = Arc::new(Mutex::new(Vec::new()));
2272 let waiting_readers_clone = waiting_readers.clone();
2273
2274 futures::join!(
2275 async move {
2276 let block_server = BlockServer::new(
2277 BLOCK_SIZE,
2278 Arc::new(MockInterface {
2279 read_hook: Some(Box::new(move |dev_block_offset, _, _, _| {
2280 let (tx, rx) = oneshot::channel();
2281 waiting_readers_clone.lock().push((dev_block_offset as u32, tx));
2282 Box::pin(async move {
2283 let _ = rx.await;
2284 Ok(())
2285 })
2286 })),
2287 ..MockInterface::default()
2288 }),
2289 );
2290 block_server.handle_requests(stream).await.unwrap();
2291 },
2292 async move {
2293 let (session_proxy, server) = fidl::endpoints::create_proxy();
2294
2295 proxy.open_session(server).unwrap();
2296
2297 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2298 let vmo_id = session_proxy
2299 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2300 .await
2301 .unwrap()
2302 .unwrap();
2303
2304 let mut fifo =
2305 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2306 let (mut reader, mut writer) = fifo.async_io();
2307
2308 writer
2309 .write_entries(&BlockFifoRequest {
2310 command: BlockFifoCommand {
2311 opcode: BlockOpcode::Read.into_primitive(),
2312 ..Default::default()
2313 },
2314 reqid: 1,
2315 dev_offset: 1, vmoid: vmo_id.id,
2317 length: 1,
2318 ..Default::default()
2319 })
2320 .await
2321 .unwrap();
2322
2323 writer
2324 .write_entries(&BlockFifoRequest {
2325 command: BlockFifoCommand {
2326 opcode: BlockOpcode::Read.into_primitive(),
2327 ..Default::default()
2328 },
2329 reqid: 2,
2330 dev_offset: 2,
2331 vmoid: vmo_id.id,
2332 length: 1,
2333 ..Default::default()
2334 })
2335 .await
2336 .unwrap();
2337
2338 poll_fn(|cx: &mut Context<'_>| {
2340 if waiting_readers.lock().len() == 2 {
2341 Poll::Ready(())
2342 } else {
2343 cx.waker().wake_by_ref();
2345 Poll::Pending
2346 }
2347 })
2348 .await;
2349
2350 let mut response = BlockFifoResponse::default();
2351 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2352
2353 let (id, tx) = waiting_readers.lock().pop().unwrap();
2354 tx.send(()).unwrap();
2355
2356 reader.read_entries(&mut response).await.unwrap();
2357 assert_eq!(response.status, zx::sys::ZX_OK);
2358 assert_eq!(response.reqid, id);
2359
2360 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2361
2362 let (id, tx) = waiting_readers.lock().pop().unwrap();
2363 tx.send(()).unwrap();
2364
2365 reader.read_entries(&mut response).await.unwrap();
2366 assert_eq!(response.status, zx::sys::ZX_OK);
2367 assert_eq!(response.reqid, id);
2368 }
2369 );
2370 }
2371
2372 #[fuchsia::test]
2373 async fn test_session_close_is_synchronous() {
2374 use futures::{FutureExt as _, StreamExt as _};
2375
2376 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2377
2378 let (start_tx, mut start_rx) = futures::channel::mpsc::channel(1);
2379 let (finish_tx, finish_rx) = futures::channel::oneshot::channel();
2380 let finish_rx = Arc::new(Mutex::new(Some(finish_rx)));
2381
2382 futures::join!(
2383 async move {
2384 let block_server = BlockServer::new(
2385 BLOCK_SIZE,
2386 Arc::new(MockInterface {
2387 read_hook: Some(Box::new(move |_, _, _, _| {
2388 let mut start_tx = start_tx.clone();
2389 let finish_rx = finish_rx.lock().take().unwrap();
2390 Box::pin(async move {
2391 start_tx.try_send(()).unwrap();
2392 let _ = finish_rx.await;
2393 Ok(())
2394 })
2395 })),
2396 ..MockInterface::default()
2397 }),
2398 );
2399 block_server.handle_requests(stream).await.unwrap();
2400 },
2401 async move {
2402 let (session_proxy, server) = fidl::endpoints::create_proxy();
2403 proxy.open_session(server).unwrap();
2404
2405 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2406 let vmo_id = session_proxy
2407 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2408 .await
2409 .unwrap()
2410 .unwrap();
2411
2412 let mut fifo = fasync::Fifo::<BlockFifoResponse, BlockFifoRequest>::from_fifo(
2413 session_proxy.get_fifo().await.unwrap().unwrap(),
2414 );
2415 let (_reader, mut writer) = fifo.async_io();
2416
2417 writer
2418 .write_entries(&BlockFifoRequest {
2419 command: BlockFifoCommand {
2420 opcode: BlockOpcode::Read.into_primitive(),
2421 ..Default::default()
2422 },
2423 reqid: 1,
2424 vmoid: vmo_id.id,
2425 length: 1,
2426 ..Default::default()
2427 })
2428 .await
2429 .unwrap();
2430
2431 start_rx.next().await.unwrap();
2433
2434 let mut close_fut = std::pin::pin!(session_proxy.close().fuse());
2436 let mut timer_fut = std::pin::pin!(
2437 fasync::Timer::new(std::time::Duration::from_millis(100)).fuse()
2438 );
2439 futures::select! {
2440 res = close_fut => panic!("close completed too early: {:?}", res),
2441 _ = timer_fut => {}
2442 }
2443
2444 finish_tx.send(()).unwrap();
2446
2447 close_fut.await.unwrap().unwrap();
2449
2450 std::mem::drop(proxy);
2451 }
2452 );
2453 }
2454
2455 #[fuchsia::test]
2456 async fn test_groups() {
2457 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2458
2459 futures::join!(
2460 async move {
2461 let block_server = BlockServer::new(
2462 BLOCK_SIZE,
2463 Arc::new(MockInterface {
2464 read_hook: Some(Box::new(move |_, _, _, _| Box::pin(async { Ok(()) }))),
2465 ..MockInterface::default()
2466 }),
2467 );
2468 block_server.handle_requests(stream).await.unwrap();
2469 },
2470 async move {
2471 let (session_proxy, server) = fidl::endpoints::create_proxy();
2472
2473 proxy.open_session(server).unwrap();
2474
2475 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2476 let vmo_id = session_proxy
2477 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2478 .await
2479 .unwrap()
2480 .unwrap();
2481
2482 let mut fifo =
2483 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2484 let (mut reader, mut writer) = fifo.async_io();
2485
2486 writer
2487 .write_entries(&BlockFifoRequest {
2488 command: BlockFifoCommand {
2489 opcode: BlockOpcode::Read.into_primitive(),
2490 flags: BlockIoFlag::GROUP_ITEM.bits(),
2491 ..Default::default()
2492 },
2493 group: 1,
2494 vmoid: vmo_id.id,
2495 length: 1,
2496 ..Default::default()
2497 })
2498 .await
2499 .unwrap();
2500
2501 writer
2502 .write_entries(&BlockFifoRequest {
2503 command: BlockFifoCommand {
2504 opcode: BlockOpcode::Read.into_primitive(),
2505 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2506 ..Default::default()
2507 },
2508 reqid: 2,
2509 group: 1,
2510 vmoid: vmo_id.id,
2511 length: 1,
2512 ..Default::default()
2513 })
2514 .await
2515 .unwrap();
2516
2517 let mut response = BlockFifoResponse::default();
2518 reader.read_entries(&mut response).await.unwrap();
2519 assert_eq!(response.status, zx::sys::ZX_OK);
2520 assert_eq!(response.reqid, 2);
2521 assert_eq!(response.group, 1);
2522 }
2523 );
2524 }
2525
2526 #[fuchsia::test]
2527 async fn test_group_error() {
2528 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2529
2530 let counter = Arc::new(AtomicU64::new(0));
2531 let counter_clone = counter.clone();
2532
2533 futures::join!(
2534 async move {
2535 let block_server = BlockServer::new(
2536 BLOCK_SIZE,
2537 Arc::new(MockInterface {
2538 read_hook: Some(Box::new(move |_, _, _, _| {
2539 counter_clone.fetch_add(1, Ordering::Relaxed);
2540 Box::pin(async { Err(zx::Status::BAD_STATE) })
2541 })),
2542 ..MockInterface::default()
2543 }),
2544 );
2545 block_server.handle_requests(stream).await.unwrap();
2546 },
2547 async move {
2548 let (session_proxy, server) = fidl::endpoints::create_proxy();
2549
2550 proxy.open_session(server).unwrap();
2551
2552 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2553 let vmo_id = session_proxy
2554 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2555 .await
2556 .unwrap()
2557 .unwrap();
2558
2559 let mut fifo =
2560 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2561 let (mut reader, mut writer) = fifo.async_io();
2562
2563 writer
2564 .write_entries(&BlockFifoRequest {
2565 command: BlockFifoCommand {
2566 opcode: BlockOpcode::Read.into_primitive(),
2567 flags: BlockIoFlag::GROUP_ITEM.bits(),
2568 ..Default::default()
2569 },
2570 group: 1,
2571 vmoid: vmo_id.id,
2572 length: 1,
2573 ..Default::default()
2574 })
2575 .await
2576 .unwrap();
2577
2578 poll_fn(|cx: &mut Context<'_>| {
2580 if counter.load(Ordering::Relaxed) == 1 {
2581 Poll::Ready(())
2582 } else {
2583 cx.waker().wake_by_ref();
2585 Poll::Pending
2586 }
2587 })
2588 .await;
2589
2590 let mut response = BlockFifoResponse::default();
2591 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2592
2593 writer
2594 .write_entries(&BlockFifoRequest {
2595 command: BlockFifoCommand {
2596 opcode: BlockOpcode::Read.into_primitive(),
2597 flags: BlockIoFlag::GROUP_ITEM.bits(),
2598 ..Default::default()
2599 },
2600 group: 1,
2601 vmoid: vmo_id.id,
2602 length: 1,
2603 ..Default::default()
2604 })
2605 .await
2606 .unwrap();
2607
2608 writer
2609 .write_entries(&BlockFifoRequest {
2610 command: BlockFifoCommand {
2611 opcode: BlockOpcode::Read.into_primitive(),
2612 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2613 ..Default::default()
2614 },
2615 reqid: 2,
2616 group: 1,
2617 vmoid: vmo_id.id,
2618 length: 1,
2619 ..Default::default()
2620 })
2621 .await
2622 .unwrap();
2623
2624 reader.read_entries(&mut response).await.unwrap();
2625 assert_eq!(response.status, zx::sys::ZX_ERR_BAD_STATE);
2626 assert_eq!(response.reqid, 2);
2627 assert_eq!(response.group, 1);
2628
2629 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2630
2631 assert_eq!(counter.load(Ordering::Relaxed), 1);
2633 }
2634 );
2635 }
2636
2637 #[fuchsia::test]
2638 async fn test_group_with_two_lasts() {
2639 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2640
2641 let (tx, rx) = oneshot::channel();
2642
2643 futures::join!(
2644 async move {
2645 let rx = Mutex::new(Some(rx));
2646 let block_server = BlockServer::new(
2647 BLOCK_SIZE,
2648 Arc::new(MockInterface {
2649 read_hook: Some(Box::new(move |_, _, _, _| {
2650 let rx = rx.lock().take().unwrap();
2651 Box::pin(async {
2652 let _ = rx.await;
2653 Ok(())
2654 })
2655 })),
2656 ..MockInterface::default()
2657 }),
2658 );
2659 block_server.handle_requests(stream).await.unwrap();
2660 },
2661 async move {
2662 let (session_proxy, server) = fidl::endpoints::create_proxy();
2663
2664 proxy.open_session(server).unwrap();
2665
2666 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2667 let vmo_id = session_proxy
2668 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2669 .await
2670 .unwrap()
2671 .unwrap();
2672
2673 let mut fifo =
2674 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2675 let (mut reader, mut writer) = fifo.async_io();
2676
2677 writer
2678 .write_entries(&BlockFifoRequest {
2679 command: BlockFifoCommand {
2680 opcode: BlockOpcode::Read.into_primitive(),
2681 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2682 ..Default::default()
2683 },
2684 reqid: 1,
2685 group: 1,
2686 vmoid: vmo_id.id,
2687 length: 1,
2688 ..Default::default()
2689 })
2690 .await
2691 .unwrap();
2692
2693 writer
2694 .write_entries(&BlockFifoRequest {
2695 command: BlockFifoCommand {
2696 opcode: BlockOpcode::Read.into_primitive(),
2697 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2698 ..Default::default()
2699 },
2700 reqid: 2,
2701 group: 1,
2702 vmoid: vmo_id.id,
2703 length: 1,
2704 ..Default::default()
2705 })
2706 .await
2707 .unwrap();
2708
2709 writer
2711 .write_entries(&BlockFifoRequest {
2712 command: BlockFifoCommand {
2713 opcode: BlockOpcode::CloseVmo.into_primitive(),
2714 ..Default::default()
2715 },
2716 reqid: 3,
2717 vmoid: vmo_id.id,
2718 ..Default::default()
2719 })
2720 .await
2721 .unwrap();
2722
2723 let mut response = BlockFifoResponse::default();
2725 reader.read_entries(&mut response).await.unwrap();
2726 assert_eq!(response.status, zx::sys::ZX_OK);
2727 assert_eq!(response.reqid, 3);
2728
2729 tx.send(()).unwrap();
2731
2732 let mut response = BlockFifoResponse::default();
2735 reader.read_entries(&mut response).await.unwrap();
2736 assert_eq!(response.status, zx::sys::ZX_ERR_INVALID_ARGS);
2737 assert_eq!(response.reqid, 1);
2738 assert_eq!(response.group, 1);
2739 }
2740 );
2741 }
2742
2743 #[fuchsia::test(allow_stalls = false)]
2744 async fn test_requests_dont_block_sessions() {
2745 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2746
2747 let (tx, rx) = oneshot::channel();
2748
2749 fasync::Task::local(async move {
2750 let rx = Mutex::new(Some(rx));
2751 let block_server = BlockServer::new(
2752 BLOCK_SIZE,
2753 Arc::new(MockInterface {
2754 read_hook: Some(Box::new(move |_, _, _, _| {
2755 let rx = rx.lock().take().unwrap();
2756 Box::pin(async {
2757 let _ = rx.await;
2758 Ok(())
2759 })
2760 })),
2761 ..MockInterface::default()
2762 }),
2763 );
2764 block_server.handle_requests(stream).await.unwrap();
2765 })
2766 .detach();
2767
2768 let mut fut = pin!(async {
2769 let (session_proxy, server) = fidl::endpoints::create_proxy();
2770
2771 proxy.open_session(server).unwrap();
2772
2773 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2774 let vmo_id = session_proxy
2775 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2776 .await
2777 .unwrap()
2778 .unwrap();
2779
2780 let mut fifo =
2781 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2782 let (mut reader, mut writer) = fifo.async_io();
2783
2784 writer
2785 .write_entries(&BlockFifoRequest {
2786 command: BlockFifoCommand {
2787 opcode: BlockOpcode::Read.into_primitive(),
2788 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2789 ..Default::default()
2790 },
2791 reqid: 1,
2792 group: 1,
2793 vmoid: vmo_id.id,
2794 length: 1,
2795 ..Default::default()
2796 })
2797 .await
2798 .unwrap();
2799
2800 let mut response = BlockFifoResponse::default();
2801 reader.read_entries(&mut response).await.unwrap();
2802 assert_eq!(response.status, zx::sys::ZX_OK);
2803 });
2804
2805 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_pending());
2807
2808 let mut fut2 = pin!(proxy.get_volume_info());
2809
2810 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut2).await.is_pending());
2812
2813 let _ = tx.send(());
2816
2817 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_ready());
2818 }
2819
2820 #[fuchsia::test]
2821 async fn test_request_flow_control() {
2822 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2823
2824 const MAX_REQUESTS: u64 = FIFO_MAX_REQUESTS as u64;
2827 let event = Arc::new((event_listener::Event::new(), AtomicBool::new(false)));
2828 let event_clone = event.clone();
2829 futures::join!(
2830 async move {
2831 let block_server = BlockServer::new(
2832 BLOCK_SIZE,
2833 Arc::new(MockInterface {
2834 read_hook: Some(Box::new(move |_, _, _, _| {
2835 let event_clone = event_clone.clone();
2836 Box::pin(async move {
2837 if !event_clone.1.load(Ordering::SeqCst) {
2838 event_clone.0.listen().await;
2839 }
2840 Ok(())
2841 })
2842 })),
2843 ..MockInterface::default()
2844 }),
2845 );
2846 block_server.handle_requests(stream).await.unwrap();
2847 },
2848 async move {
2849 let (session_proxy, server) = fidl::endpoints::create_proxy();
2850
2851 proxy.open_session(server).unwrap();
2852
2853 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2854 let vmo_id = session_proxy
2855 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2856 .await
2857 .unwrap()
2858 .unwrap();
2859
2860 let mut fifo =
2861 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2862 let (mut reader, mut writer) = fifo.async_io();
2863
2864 for i in 0..MAX_REQUESTS {
2865 writer
2866 .write_entries(&BlockFifoRequest {
2867 command: BlockFifoCommand {
2868 opcode: BlockOpcode::Read.into_primitive(),
2869 ..Default::default()
2870 },
2871 reqid: (i + 1) as u32,
2872 dev_offset: i,
2873 vmoid: vmo_id.id,
2874 length: 1,
2875 ..Default::default()
2876 })
2877 .await
2878 .unwrap();
2879 }
2880 assert!(
2881 futures::poll!(pin!(writer.write_entries(&BlockFifoRequest {
2882 command: BlockFifoCommand {
2883 opcode: BlockOpcode::Read.into_primitive(),
2884 ..Default::default()
2885 },
2886 reqid: u32::MAX,
2887 dev_offset: MAX_REQUESTS,
2888 vmoid: vmo_id.id,
2889 length: 1,
2890 ..Default::default()
2891 })))
2892 .is_pending()
2893 );
2894 event.1.store(true, Ordering::SeqCst);
2896 event.0.notify(usize::MAX);
2897 let mut finished_reqids = vec![];
2899 for i in MAX_REQUESTS..2 * MAX_REQUESTS {
2900 let mut response = BlockFifoResponse::default();
2901 reader.read_entries(&mut response).await.unwrap();
2902 assert_eq!(response.status, zx::sys::ZX_OK);
2903 finished_reqids.push(response.reqid);
2904 writer
2905 .write_entries(&BlockFifoRequest {
2906 command: BlockFifoCommand {
2907 opcode: BlockOpcode::Read.into_primitive(),
2908 ..Default::default()
2909 },
2910 reqid: (i + 1) as u32,
2911 dev_offset: i,
2912 vmoid: vmo_id.id,
2913 length: 1,
2914 ..Default::default()
2915 })
2916 .await
2917 .unwrap();
2918 }
2919 let mut response = BlockFifoResponse::default();
2920 for _ in 0..MAX_REQUESTS {
2921 reader.read_entries(&mut response).await.unwrap();
2922 assert_eq!(response.status, zx::sys::ZX_OK);
2923 finished_reqids.push(response.reqid);
2924 }
2925 finished_reqids.sort();
2928 assert_eq!(finished_reqids.len(), 2 * MAX_REQUESTS as usize);
2929 let mut i = 1;
2930 for reqid in finished_reqids {
2931 assert_eq!(reqid, i);
2932 i += 1;
2933 }
2934 }
2935 );
2936 }
2937
2938 #[fuchsia::test]
2939 async fn test_passthrough_io_with_fixed_map() {
2940 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2941
2942 let expected_op = Arc::new(Mutex::new(None));
2943 let expected_op_clone = expected_op.clone();
2944 futures::join!(
2945 async {
2946 let block_server = BlockServer::new(
2947 BLOCK_SIZE,
2948 Arc::new(IoMockInterface {
2949 return_errors: false,
2950 do_checks: true,
2951 expected_op: expected_op_clone,
2952 }),
2953 );
2954 block_server.handle_requests(stream).await.unwrap();
2955 },
2956 async move {
2957 let (session_proxy, server) = fidl::endpoints::create_proxy();
2958
2959 let mapping = fblock::BlockOffsetMapping {
2960 source_block_offset: 0,
2961 target_block_offset: 10,
2962 length: 20,
2963 };
2964 proxy.open_session_with_offset_map(server, &mapping).unwrap();
2965
2966 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2967 let vmo_id = session_proxy
2968 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2969 .await
2970 .unwrap()
2971 .unwrap();
2972
2973 let mut fifo =
2974 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2975 let (mut reader, mut writer) = fifo.async_io();
2976
2977 *expected_op.lock() = Some(ExpectedOp::Read(11, 2, 3));
2979 writer
2980 .write_entries(&BlockFifoRequest {
2981 command: BlockFifoCommand {
2982 opcode: BlockOpcode::Read.into_primitive(),
2983 ..Default::default()
2984 },
2985 vmoid: vmo_id.id,
2986 dev_offset: 1,
2987 length: 2,
2988 vmo_offset: 3,
2989 ..Default::default()
2990 })
2991 .await
2992 .unwrap();
2993
2994 let mut response = BlockFifoResponse::default();
2995 reader.read_entries(&mut response).await.unwrap();
2996 assert_eq!(response.status, zx::sys::ZX_OK);
2997
2998 *expected_op.lock() = Some(ExpectedOp::Write(14, 5, 6));
3000 writer
3001 .write_entries(&BlockFifoRequest {
3002 command: BlockFifoCommand {
3003 opcode: BlockOpcode::Write.into_primitive(),
3004 ..Default::default()
3005 },
3006 vmoid: vmo_id.id,
3007 dev_offset: 4,
3008 length: 5,
3009 vmo_offset: 6,
3010 ..Default::default()
3011 })
3012 .await
3013 .unwrap();
3014
3015 reader.read_entries(&mut response).await.unwrap();
3016 assert_eq!(response.status, zx::sys::ZX_OK);
3017
3018 *expected_op.lock() = Some(ExpectedOp::Flush);
3020 writer
3021 .write_entries(&BlockFifoRequest {
3022 command: BlockFifoCommand {
3023 opcode: BlockOpcode::Flush.into_primitive(),
3024 ..Default::default()
3025 },
3026 ..Default::default()
3027 })
3028 .await
3029 .unwrap();
3030
3031 reader.read_entries(&mut response).await.unwrap();
3032 assert_eq!(response.status, zx::sys::ZX_OK);
3033
3034 *expected_op.lock() = Some(ExpectedOp::Trim(17, 3));
3036 writer
3037 .write_entries(&BlockFifoRequest {
3038 command: BlockFifoCommand {
3039 opcode: BlockOpcode::Trim.into_primitive(),
3040 ..Default::default()
3041 },
3042 dev_offset: 7,
3043 length: 3,
3044 ..Default::default()
3045 })
3046 .await
3047 .unwrap();
3048
3049 reader.read_entries(&mut response).await.unwrap();
3050 assert_eq!(response.status, zx::sys::ZX_OK);
3051
3052 *expected_op.lock() = None;
3054 writer
3055 .write_entries(&BlockFifoRequest {
3056 command: BlockFifoCommand {
3057 opcode: BlockOpcode::Read.into_primitive(),
3058 ..Default::default()
3059 },
3060 vmoid: vmo_id.id,
3061 dev_offset: 19,
3062 length: 2,
3063 vmo_offset: 3,
3064 ..Default::default()
3065 })
3066 .await
3067 .unwrap();
3068
3069 reader.read_entries(&mut response).await.unwrap();
3070 assert_eq!(response.status, zx::sys::ZX_ERR_OUT_OF_RANGE);
3071
3072 std::mem::drop(proxy);
3073 }
3074 );
3075 }
3076
3077 #[fuchsia::test]
3078 fn operation_map() {
3079 const BLOCK_SIZE: u32 = 512;
3080
3081 #[track_caller]
3082 fn expect_map_result(
3083 mut operation: Operation,
3084 mapping: Option<BlockOffsetMapping>,
3085 max_blocks: Option<NonZero<u32>>,
3086 expected_operations: Vec<Operation>,
3087 ) {
3088 let mut ops = vec![];
3089 while let Some(remainder) =
3090 operation.map(mapping.as_ref(), max_blocks.clone(), BLOCK_SIZE)
3091 {
3092 ops.push(operation);
3093 operation = remainder;
3094 }
3095 ops.push(operation);
3096 assert_eq!(ops, expected_operations);
3097 }
3098
3099 expect_map_result(
3101 Operation::Read {
3102 device_block_offset: 10,
3103 block_count: 200,
3104 _unused: 0,
3105 vmo_offset: 0,
3106 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3107 },
3108 None,
3109 None,
3110 vec![Operation::Read {
3111 device_block_offset: 10,
3112 block_count: 200,
3113 _unused: 0,
3114 vmo_offset: 0,
3115 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3116 }],
3117 );
3118
3119 expect_map_result(
3121 Operation::Read {
3122 device_block_offset: 10,
3123 block_count: 200,
3124 _unused: 0,
3125 vmo_offset: 0,
3126 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3127 },
3128 None,
3129 NonZero::new(120),
3130 vec![
3131 Operation::Read {
3132 device_block_offset: 10,
3133 block_count: 120,
3134 _unused: 0,
3135 vmo_offset: 0,
3136 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3137 },
3138 Operation::Read {
3139 device_block_offset: 130,
3140 block_count: 80,
3141 _unused: 0,
3142 vmo_offset: 120 * BLOCK_SIZE as u64,
3143 options: ReadOptions {
3144 inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3146 },
3147 },
3148 ],
3149 );
3150 expect_map_result(
3151 Operation::Trim { device_block_offset: 10, block_count: 200 },
3152 None,
3153 NonZero::new(120),
3154 vec![Operation::Trim { device_block_offset: 10, block_count: 200 }],
3155 );
3156
3157 expect_map_result(
3159 Operation::Read {
3160 device_block_offset: 10,
3161 block_count: 200,
3162 _unused: 0,
3163 vmo_offset: 0,
3164 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3165 },
3166 Some(BlockOffsetMapping {
3167 source_block_offset: 10,
3168 target_block_offset: 100,
3169 length: 200,
3170 }),
3171 NonZero::new(120),
3172 vec![
3173 Operation::Read {
3174 device_block_offset: 100,
3175 block_count: 120,
3176 _unused: 0,
3177 vmo_offset: 0,
3178 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3179 },
3180 Operation::Read {
3181 device_block_offset: 220,
3182 block_count: 80,
3183 _unused: 0,
3184 vmo_offset: 120 * BLOCK_SIZE as u64,
3185 options: ReadOptions {
3186 inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3187 },
3188 },
3189 ],
3190 );
3191 expect_map_result(
3192 Operation::Trim { device_block_offset: 10, block_count: 200 },
3193 Some(BlockOffsetMapping {
3194 source_block_offset: 10,
3195 target_block_offset: 100,
3196 length: 200,
3197 }),
3198 NonZero::new(120),
3199 vec![Operation::Trim { device_block_offset: 100, block_count: 200 }],
3200 );
3201 }
3202
3203 #[fuchsia::test]
3205 async fn test_pre_barrier_flush_failure() {
3206 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3207
3208 struct NoBarrierInterface;
3209 impl super::async_interface::Interface for NoBarrierInterface {
3210 fn get_info(&self) -> Cow<'_, DeviceInfo> {
3211 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3212 device_flags: fblock::DeviceFlag::empty(), max_transfer_blocks: NonZero::new(100),
3214 block_range: Some(0..100),
3215 type_guid: [0; 16],
3216 instance_guid: [0; 16],
3217 name: "test".to_string(),
3218 flags: 0,
3219 }))
3220 }
3221 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3222 Ok(())
3223 }
3224 async fn read(
3225 &self,
3226 _: u64,
3227 _: u32,
3228 _: &Arc<zx::Vmo>,
3229 _: u64,
3230 _: ReadOptions,
3231 _: TraceFlowId,
3232 ) -> Result<(), zx::Status> {
3233 unreachable!()
3234 }
3235 async fn write(
3236 &self,
3237 _: u64,
3238 _: u32,
3239 _: &Arc<zx::Vmo>,
3240 _: u64,
3241 _: WriteOptions,
3242 _: TraceFlowId,
3243 ) -> Result<(), zx::Status> {
3244 panic!("Write should not be called");
3245 }
3246 async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3247 Err(zx::Status::IO)
3248 }
3249 async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3250 unreachable!()
3251 }
3252 }
3253
3254 futures::join!(
3255 async move {
3256 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3257 block_server.handle_requests(stream).await.unwrap();
3258 },
3259 async move {
3260 let (session_proxy, server) = fidl::endpoints::create_proxy();
3261 proxy.open_session(server).unwrap();
3262 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3263 let vmo_id = session_proxy
3264 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3265 .await
3266 .unwrap()
3267 .unwrap();
3268
3269 let mut fifo =
3270 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3271 let (mut reader, mut writer) = fifo.async_io();
3272
3273 writer
3274 .write_entries(&BlockFifoRequest {
3275 command: BlockFifoCommand {
3276 opcode: BlockOpcode::Write.into_primitive(),
3277 flags: BlockIoFlag::PRE_BARRIER.bits(),
3278 ..Default::default()
3279 },
3280 vmoid: vmo_id.id,
3281 length: 1,
3282 ..Default::default()
3283 })
3284 .await
3285 .unwrap();
3286
3287 let mut response = BlockFifoResponse::default();
3288 reader.read_entries(&mut response).await.unwrap();
3289 assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3290 }
3291 );
3292 }
3293
3294 #[fuchsia::test]
3297 async fn test_post_barrier_write_failure() {
3298 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3299
3300 struct NoBarrierInterface;
3301 impl super::async_interface::Interface for NoBarrierInterface {
3302 fn get_info(&self) -> Cow<'_, DeviceInfo> {
3303 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3304 device_flags: fblock::DeviceFlag::empty(), max_transfer_blocks: NonZero::new(100),
3306 block_range: Some(0..100),
3307 type_guid: [0; 16],
3308 instance_guid: [0; 16],
3309 name: "test".to_string(),
3310 flags: 0,
3311 }))
3312 }
3313 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3314 Ok(())
3315 }
3316 async fn read(
3317 &self,
3318 _: u64,
3319 _: u32,
3320 _: &Arc<zx::Vmo>,
3321 _: u64,
3322 _: ReadOptions,
3323 _: TraceFlowId,
3324 ) -> Result<(), zx::Status> {
3325 unreachable!()
3326 }
3327 async fn write(
3328 &self,
3329 _: u64,
3330 _: u32,
3331 _: &Arc<zx::Vmo>,
3332 _: u64,
3333 _: WriteOptions,
3334 _: TraceFlowId,
3335 ) -> Result<(), zx::Status> {
3336 Err(zx::Status::IO)
3337 }
3338 async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3339 panic!("Flush should not be called")
3340 }
3341 async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3342 unreachable!()
3343 }
3344 }
3345
3346 futures::join!(
3347 async move {
3348 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3349 block_server.handle_requests(stream).await.unwrap();
3350 },
3351 async move {
3352 let (session_proxy, server) = fidl::endpoints::create_proxy();
3353 proxy.open_session(server).unwrap();
3354 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3355 let vmo_id = session_proxy
3356 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3357 .await
3358 .unwrap()
3359 .unwrap();
3360
3361 let mut fifo =
3362 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3363 let (mut reader, mut writer) = fifo.async_io();
3364
3365 writer
3366 .write_entries(&BlockFifoRequest {
3367 command: BlockFifoCommand {
3368 opcode: BlockOpcode::Write.into_primitive(),
3369 flags: BlockIoFlag::FORCE_ACCESS.bits(),
3370 ..Default::default()
3371 },
3372 vmoid: vmo_id.id,
3373 length: 1,
3374 ..Default::default()
3375 })
3376 .await
3377 .unwrap();
3378
3379 let mut response = BlockFifoResponse::default();
3380 reader.read_entries(&mut response).await.unwrap();
3381 assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3382 }
3383 );
3384 }
3385
3386 #[fuchsia::test]
3391 async fn test_group_ids_isolated_per_session() {
3392 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3393
3394 futures::join!(
3395 async {
3396 let block_server = BlockServer::new(
3397 BLOCK_SIZE,
3398 Arc::new(MockInterface::default()),
3400 );
3401 block_server.handle_requests(stream).await.unwrap();
3402 },
3403 async move {
3404 async fn settle() {
3405 for _ in 0..32 {
3407 fasync::yield_now().await;
3408 }
3409 }
3410
3411 let (session_a, server_a) = fidl::endpoints::create_proxy();
3413 proxy.open_session(server_a).unwrap();
3414 let mut fifo_a =
3415 fasync::Fifo::from_fifo(session_a.get_fifo().await.unwrap().unwrap());
3416
3417 let (session_b, server_b) = fidl::endpoints::create_proxy();
3419 proxy.open_session(server_b).unwrap();
3420 let mut fifo_b =
3421 fasync::Fifo::from_fifo(session_b.get_fifo().await.unwrap().unwrap());
3422
3423 {
3427 let (mut reader_a, mut writer_a) = fifo_a.async_io();
3428 writer_a
3429 .write_entries(&BlockFifoRequest {
3430 command: BlockFifoCommand {
3431 opcode: BlockOpcode::Flush.into_primitive(),
3432 flags: BlockIoFlag::GROUP_ITEM.bits(),
3433 ..Default::default()
3434 },
3435 group: 1,
3436 reqid: 0xAAAA,
3437 ..Default::default()
3438 })
3439 .await
3440 .unwrap();
3441 settle().await;
3442 writer_a
3443 .write_entries(&BlockFifoRequest {
3444 command: BlockFifoCommand {
3445 opcode: BlockOpcode::Flush.into_primitive(),
3446 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3447 ..Default::default()
3448 },
3449 group: 1,
3450 reqid: 0xAAAA,
3451 ..Default::default()
3452 })
3453 .await
3454 .unwrap();
3455 let mut response = BlockFifoResponse::default();
3456 reader_a.read_entries(&mut response).await.unwrap();
3457 assert_eq!(response.reqid, 0xAAAA);
3458 assert_eq!(
3459 zx::Status::from_raw(response.status),
3460 zx::Status::OK,
3461 "control: A's valid Flush group must succeed"
3462 );
3463 }
3464
3465 {
3472 let (_reader_a, mut writer_a) = fifo_a.async_io();
3473 writer_a
3474 .write_entries(&BlockFifoRequest {
3475 command: BlockFifoCommand {
3476 opcode: BlockOpcode::Flush.into_primitive(),
3477 flags: BlockIoFlag::GROUP_ITEM.bits(),
3478 ..Default::default()
3479 },
3480 group: 7,
3481 reqid: 100,
3482 ..Default::default()
3483 })
3484 .await
3485 .unwrap();
3486 }
3487 settle().await;
3488
3489 {
3491 let (_reader_b, mut writer_b) = fifo_b.async_io();
3492 writer_b
3493 .write_entries(&BlockFifoRequest {
3494 command: BlockFifoCommand {
3495 opcode: BlockOpcode::Flush.into_primitive(),
3496 flags: BlockIoFlag::GROUP_ITEM.bits(),
3497 ..Default::default()
3498 },
3499 group: 7,
3500 reqid: 200,
3501 ..Default::default()
3502 })
3503 .await
3504 .unwrap();
3505 }
3506 settle().await;
3507
3508 {
3510 let (_reader_a, mut writer_a) = fifo_a.async_io();
3511 writer_a
3512 .write_entries(&BlockFifoRequest {
3513 command: BlockFifoCommand {
3514 opcode: BlockOpcode::Flush.into_primitive(),
3515 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3516 ..Default::default()
3517 },
3518 group: 7,
3519 reqid: 100,
3520 ..Default::default()
3521 })
3522 .await
3523 .unwrap();
3524 }
3525 settle().await;
3526
3527 {
3529 let (_reader_b, mut writer_b) = fifo_b.async_io();
3530 writer_b
3531 .write_entries(&BlockFifoRequest {
3532 command: BlockFifoCommand {
3533 opcode: BlockOpcode::Flush.into_primitive(),
3534 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3535 ..Default::default()
3536 },
3537 group: 7,
3538 reqid: 200,
3539 ..Default::default()
3540 })
3541 .await
3542 .unwrap();
3543 }
3544 settle().await;
3545
3546 {
3548 let (mut reader_a, _writer_a) = fifo_a.async_io();
3549 let mut response_a = BlockFifoResponse::default();
3550 reader_a.read_entries(&mut response_a).await.unwrap();
3551 assert_eq!(response_a.reqid, 100);
3552 assert_eq!(response_a.group, 7);
3553 assert_eq!(zx::Status::from_raw(response_a.status), zx::Status::OK);
3554 }
3555
3556 {
3558 let (mut reader_b, _writer_b) = fifo_b.async_io();
3559 let mut response_b = BlockFifoResponse::default();
3560 reader_b.read_entries(&mut response_b).await.unwrap();
3561 assert_eq!(response_b.reqid, 200);
3562 assert_eq!(response_b.group, 7);
3563 assert_eq!(zx::Status::from_raw(response_b.status), zx::Status::OK);
3564 }
3565
3566 std::mem::drop(session_a);
3567 std::mem::drop(session_b);
3568 std::mem::drop(proxy);
3569 }
3570 );
3571 }
3572}