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, Debug)]
33pub enum DeviceInfo {
34 Block(BlockInfo),
36 Partition(PartitionInfo),
38 Volume(VolumeInfo),
40}
41
42impl DeviceInfo {
43 pub fn label(&self) -> &str {
44 match self {
45 Self::Block(BlockInfo { .. }) => "",
46 Self::Partition(PartitionInfo { name, .. }) => name,
47 Self::Volume(VolumeInfo { name, .. }) => name,
48 }
49 }
50 pub fn device_flags(&self) -> fblock::DeviceFlag {
51 match self {
52 Self::Block(BlockInfo { device_flags, .. }) => *device_flags,
53 Self::Partition(PartitionInfo { device_flags, .. }) => *device_flags,
54 Self::Volume(VolumeInfo { device_flags, .. }) => *device_flags,
55 }
56 }
57
58 pub fn block_count(&self) -> Option<u64> {
61 match self {
62 Self::Block(BlockInfo { block_count, .. }) => Some(*block_count),
63 Self::Partition(PartitionInfo { block_count, .. }) => Some(*block_count),
64 Self::Volume(VolumeInfo { .. }) => None,
65 }
66 }
67
68 pub fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
69 match self {
70 Self::Block(BlockInfo { max_transfer_blocks, .. }) => max_transfer_blocks.clone(),
71 Self::Partition(PartitionInfo { max_transfer_blocks, .. }) => {
72 max_transfer_blocks.clone()
73 }
74 Self::Volume(VolumeInfo { max_transfer_blocks, .. }) => max_transfer_blocks.clone(),
75 }
76 }
77
78 fn max_transfer_size(&self, block_size: u32) -> u32 {
79 if let Some(max_blocks) = self.max_transfer_blocks() {
80 max_blocks.get() * block_size
81 } else {
82 MAX_TRANSFER_UNBOUNDED
83 }
84 }
85
86 pub fn type_guid(&self) -> Option<[u8; 16]> {
87 match self {
88 Self::Partition(PartitionInfo { type_guid, .. }) => Some(*type_guid),
89 Self::Volume(VolumeInfo { type_guid, .. }) => Some(*type_guid),
90 Self::Block(_) => None,
91 }
92 }
93
94 pub fn instance_guid(&self) -> Option<[u8; 16]> {
95 match self {
96 Self::Partition(PartitionInfo { instance_guid, .. }) => Some(*instance_guid),
97 Self::Volume(VolumeInfo { instance_guid, .. }) => Some(*instance_guid),
98 Self::Block(_) => None,
99 }
100 }
101}
102
103#[derive(Clone, Default, Debug)]
105pub struct BlockInfo {
106 pub device_flags: fblock::DeviceFlag,
107 pub block_count: u64,
108 pub max_transfer_blocks: Option<NonZero<u32>>,
109}
110
111#[derive(Clone, Default, Debug)]
113pub struct PartitionInfo {
114 pub device_flags: fblock::DeviceFlag,
116 pub max_transfer_blocks: Option<NonZero<u32>>,
117 pub start_block_offset: Option<u64>,
120 pub block_count: u64,
121 pub type_guid: [u8; 16],
122 pub instance_guid: [u8; 16],
123 pub name: String,
124 pub flags: Option<u64>,
127}
128
129#[derive(Clone, Default, Debug)]
131pub struct VolumeInfo {
132 pub device_flags: fblock::DeviceFlag,
134 pub max_transfer_blocks: Option<NonZero<u32>>,
135 pub type_guid: [u8; 16],
136 pub instance_guid: [u8; 16],
137 pub name: String,
138 pub flags: u64,
139}
140
141struct ActiveRequest<S> {
144 session: S,
145 group_or_request: GroupOrRequest,
146 trace_flow_id: TraceFlowId,
147 _epoch_guard: EpochGuard<'static>,
148 status: zx::Status,
149 count: u32,
150 req_id: Option<u32>,
151 decompression_info: Option<DecompressionInfo>,
152}
153
154struct DecompressionInfo {
155 compressed_range: Range<usize>,
157
158 uncompressed_range: Range<u64>,
160
161 bytes_so_far: u64,
162 mapping: Arc<VmoMapping>,
163 buffer: Option<Buffer<'static>>,
164}
165
166impl DecompressionInfo {
167 fn uncompressed_slice(&self) -> *mut [u8] {
169 std::ptr::slice_from_raw_parts_mut(
170 (self.mapping.base + self.uncompressed_range.start as usize) as *mut u8,
171 (self.uncompressed_range.end - self.uncompressed_range.start) as usize,
172 )
173 }
174}
175
176pub struct ActiveRequests<S>(Mutex<ActiveRequestsInner<S>>);
177
178impl<S> Default for ActiveRequests<S> {
179 fn default() -> Self {
180 Self(Mutex::new(ActiveRequestsInner { requests: Slab::default() }))
181 }
182}
183
184impl<S> ActiveRequests<S> {
185 fn complete_and_take_response(
186 &self,
187 request_id: RequestId,
188 status: zx::Status,
189 ) -> Option<(S, BlockFifoResponse)> {
190 self.0.lock().complete_and_take_response(request_id, status)
191 }
192
193 fn request(&self, request_id: RequestId) -> MappedMutexGuard<'_, ActiveRequest<S>> {
194 MutexGuard::map(self.0.lock(), |i| &mut i.requests[request_id.0])
195 }
196}
197
198struct ActiveRequestsInner<S> {
199 requests: Slab<ActiveRequest<S>>,
200}
201
202impl<S> ActiveRequestsInner<S> {
204 fn complete(&mut self, request_id: RequestId, status: zx::Status) {
206 let group = &mut self.requests[request_id.0];
207
208 group.count = group.count.checked_sub(1).unwrap();
209 if status != zx::Status::OK && group.status == zx::Status::OK {
210 group.status = status
211 }
212
213 fuchsia_trace::duration!(
214 "storage",
215 "block_server::finish_transaction",
216 "request_id" => request_id.0,
217 "group_completed" => group.count == 0,
218 "status" => status.into_raw());
219 if let Some(trace_flow_id) = group.trace_flow_id {
220 fuchsia_trace::flow_step!(
221 "storage",
222 "block_server::finish_request",
223 trace_flow_id.get().into()
224 );
225 }
226
227 if group.count == 0
228 && group.status == zx::Status::OK
229 && let Some(info) = &mut group.decompression_info
230 {
231 struct RawDCtx(std::ptr::NonNull<zstd::zstd_safe::zstd_sys::ZSTD_DCtx>);
232
233 thread_local! {
234 static RAW_DECOMPRESSOR: std::cell::RefCell<RawDCtx> = {
235 let raw_ptr = unsafe { zstd::zstd_safe::zstd_sys::ZSTD_createDCtx() };
238 let ptr = std::ptr::NonNull::new(raw_ptr).expect("ZSTD_createDCtx failed");
239 std::cell::RefCell::new(RawDCtx(ptr))
240 };
241 }
242
243 impl Drop for RawDCtx {
244 fn drop(&mut self) {
245 unsafe {
247 zstd::zstd_safe::zstd_sys::ZSTD_freeDCtx(self.0.as_ptr());
248 }
249 }
250 }
251
252 RAW_DECOMPRESSOR.with_borrow_mut(|decompressor| {
253 let dctx = decompressor.0.as_ptr();
254 let target = info.uncompressed_slice();
255 let buffer = info.buffer.take().unwrap();
256 let source = buffer.subslice(info.compressed_range.clone());
257
258 unsafe {
261 let result = zstd::zstd_safe::zstd_sys::ZSTD_decompressDCtx(
262 dctx,
263 target as *mut u8 as *mut std::os::raw::c_void,
264 target.len(),
265 source.as_ptr() as *const std::os::raw::c_void,
266 source.len(),
267 );
268 if zstd::zstd_safe::zstd_sys::ZSTD_isError(result) != 0 {
269 let error = zstd::zstd_safe::get_error_name(result);
270 log::warn!(error:?; "Decompression error");
271 group.status = zx::Status::IO_DATA_INTEGRITY;
272 }
273 }
274 });
275 }
276 }
277
278 fn take_response(&mut self, request_id: RequestId) -> Option<(S, BlockFifoResponse)> {
280 let group = &self.requests[request_id.0];
281 match group.req_id {
282 Some(reqid) if group.count == 0 => {
283 let group = self.requests.remove(request_id.0);
284 Some((
285 group.session,
286 BlockFifoResponse {
287 status: group.status.into_raw(),
288 reqid,
289 group: group.group_or_request.group_id().unwrap_or(0),
290 ..Default::default()
291 },
292 ))
293 }
294 _ => None,
295 }
296 }
297
298 fn complete_and_take_response(
300 &mut self,
301 request_id: RequestId,
302 status: zx::Status,
303 ) -> Option<(S, BlockFifoResponse)> {
304 self.complete(request_id, status);
305 self.take_response(request_id)
306 }
307}
308
309pub struct BlockServer<SM: SessionManager> {
312 block_size: u32,
313 orchestrator: Arc<SM::Orchestrator>,
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
317pub struct BlockOffsetMapping {
318 pub target_block_offset: u64,
319 pub length: u64,
320}
321
322pub fn coalesce_mappings(raw_mappings: Vec<BlockOffsetMapping>) -> Vec<BlockOffsetMapping> {
325 let mut mappings: Vec<BlockOffsetMapping> = Vec::with_capacity(raw_mappings.len());
326 for m in raw_mappings {
327 if let Some(last) = mappings.last_mut()
328 && last.target_block_offset + last.length == m.target_block_offset
329 {
330 last.length += m.length;
331 } else {
332 mappings.push(m);
333 }
334 }
335 mappings
336}
337
338#[derive(Clone, Debug, Default, PartialEq, Eq)]
340pub struct OffsetMap {
341 mappings: Vec<BlockOffsetMapping>,
342}
343
344impl OffsetMap {
345 pub fn new(mappings: Vec<BlockOffsetMapping>) -> Result<Self, zx::Status> {
349 let mut total: u64 = 0;
350 for m in &mappings {
351 if m.length == 0 {
352 return Err(zx::Status::INVALID_ARGS);
353 }
354 m.target_block_offset.checked_add(m.length).ok_or(zx::Status::INVALID_ARGS)?;
355 total = total.checked_add(m.length).ok_or(zx::Status::INVALID_ARGS)?;
356 }
357 Ok(Self { mappings })
358 }
359
360 pub fn empty() -> Self {
362 Self { mappings: Vec::new() }
363 }
364
365 pub fn map(&self, logical_offset: u64) -> Option<(u64, u32)> {
371 let mut current_logical_start = 0;
372 for mapping in &self.mappings {
373 let current_logical_end = current_logical_start + mapping.length;
374 if logical_offset < current_logical_end {
375 let delta = logical_offset - current_logical_start;
376 let dev_offset = mapping.target_block_offset + delta;
377 let len = u32::try_from(current_logical_end - logical_offset).unwrap_or(u32::MAX);
378 return Some((dev_offset, len));
379 }
380 current_logical_start = current_logical_end;
381 }
382 None
383 }
384
385 pub fn is_empty(&self) -> bool {
386 self.mappings.is_empty()
387 }
388
389 pub fn total_blocks(&self) -> u64 {
390 self.mappings.iter().map(|m| m.length).sum()
391 }
392
393 pub fn are_blocks_within_source_range(&self, (offset, length): (u64, u32)) -> bool {
396 if self.is_empty() {
397 return true;
398 }
399 let total = self.total_blocks();
400 offset <= total && total - offset >= length as u64
401 }
402
403 pub fn mappings(&self) -> &[BlockOffsetMapping] {
404 &self.mappings
405 }
406}
407
408impl TryFrom<&fblock::BlockOffsetMapping> for BlockOffsetMapping {
409 type Error = zx::Status;
410
411 fn try_from(wire: &fblock::BlockOffsetMapping) -> Result<Self, Self::Error> {
412 if wire.length == 0 {
413 return Err(zx::Status::INVALID_ARGS);
414 }
415 wire.target_block_offset.checked_add(wire.length).ok_or(zx::Status::OUT_OF_RANGE)?;
416 Ok(BlockOffsetMapping {
417 target_block_offset: wire.target_block_offset,
418 length: wire.length,
419 })
420 }
421}
422
423impl TryFrom<fblock::BlockOffsetMapping> for BlockOffsetMapping {
424 type Error = zx::Status;
425
426 fn try_from(wire: fblock::BlockOffsetMapping) -> Result<Self, Self::Error> {
427 BlockOffsetMapping::try_from(&wire)
428 }
429}
430
431impl From<&BlockOffsetMapping> for fblock::BlockOffsetMapping {
432 fn from(m: &BlockOffsetMapping) -> Self {
433 fblock::BlockOffsetMapping { target_block_offset: m.target_block_offset, length: m.length }
434 }
435}
436
437impl From<BlockOffsetMapping> for fblock::BlockOffsetMapping {
438 fn from(m: BlockOffsetMapping) -> Self {
439 fblock::BlockOffsetMapping::from(&m)
440 }
441}
442
443impl TryFrom<&[fblock::BlockOffsetMapping]> for OffsetMap {
444 type Error = zx::Status;
445
446 fn try_from(wire: &[fblock::BlockOffsetMapping]) -> Result<Self, Self::Error> {
447 let raw_mappings: Vec<BlockOffsetMapping> =
448 wire.iter().map(BlockOffsetMapping::try_from).collect::<Result<_, _>>()?;
449 OffsetMap::new(raw_mappings)
450 }
451}
452
453impl TryFrom<Vec<fblock::BlockOffsetMapping>> for OffsetMap {
454 type Error = zx::Status;
455
456 fn try_from(wire: Vec<fblock::BlockOffsetMapping>) -> Result<Self, Self::Error> {
457 OffsetMap::try_from(wire.as_slice())
458 }
459}
460
461impl From<&OffsetMap> for Vec<fblock::BlockOffsetMapping> {
462 fn from(offset_map: &OffsetMap) -> Self {
463 offset_map.mappings.iter().map(fblock::BlockOffsetMapping::from).collect()
464 }
465}
466
467pub trait SessionManager: 'static {
470 type Orchestrator: Borrow<Self> + Send + Sync;
475
476 const SUPPORTS_DECOMPRESSION: bool;
477
478 type Session;
479
480 fn session_eq(a: &Self::Session, b: &Self::Session) -> bool;
484
485 fn on_attach_vmo(
486 orchestrator: Arc<Self::Orchestrator>,
487 vmo: &Arc<zx::Vmo>,
488 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
489
490 fn open_session(
499 orchestrator: Arc<Self::Orchestrator>,
500 stream: fblock::SessionRequestStream,
501 offset_map: OffsetMap,
502 block_size: u32,
503 ) -> impl Future<Output = Result<(), Error>> + Send;
504
505 fn get_info(&self) -> Cow<'_, DeviceInfo>;
507
508 fn get_volume_info(
510 &self,
511 ) -> impl Future<Output = Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status>> + Send
512 {
513 async { Err(zx::Status::NOT_SUPPORTED) }
514 }
515
516 fn query_slices(
518 &self,
519 _start_slices: &[u64],
520 ) -> impl Future<Output = Result<Vec<fblock::VsliceRange>, zx::Status>> + Send {
521 async { Err(zx::Status::NOT_SUPPORTED) }
522 }
523
524 fn extend(
526 &self,
527 _start_slice: u64,
528 _slice_count: u64,
529 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
530 async { Err(zx::Status::NOT_SUPPORTED) }
531 }
532
533 fn shrink(
535 &self,
536 _start_slice: u64,
537 _slice_count: u64,
538 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
539 async { Err(zx::Status::NOT_SUPPORTED) }
540 }
541
542 fn active_requests(&self) -> &ActiveRequests<Self::Session>;
544}
545
546pub trait IntoOrchestrator {
550 type SM: SessionManager;
551
552 fn into_orchestrator(self) -> Arc<<Self::SM as SessionManager>::Orchestrator>;
553}
554
555impl<SM: SessionManager> BlockServer<SM> {
556 pub fn new(block_size: u32, orchestrator: impl IntoOrchestrator<SM = SM>) -> Self {
557 Self { block_size, orchestrator: orchestrator.into_orchestrator() }
558 }
559
560 pub fn session_manager(&self) -> &SM {
561 self.orchestrator.as_ref().borrow()
562 }
563
564 pub async fn handle_requests(
566 &self,
567 mut requests: fblock::BlockRequestStream,
568 ) -> Result<(), Error> {
569 let scope = fasync::Scope::new();
570 loop {
571 match requests.try_next().await {
572 Ok(Some(request)) => {
573 if let Some(session) = self.handle_request(request).await? {
574 scope.spawn(session.map(|_| ()));
575 }
576 }
577 Ok(None) => break,
578 Err(error) => log::warn!(error:?; "Invalid request"),
579 }
580 }
581 scope.await;
582 Ok(())
583 }
584
585 async fn handle_request(
588 &self,
589 request: fblock::BlockRequest,
590 ) -> Result<Option<impl Future<Output = Result<(), Error>> + Send + use<SM>>, Error> {
591 match request {
592 fblock::BlockRequest::GetInfo { responder } => {
593 let info = self.device_info();
594 let max_transfer_size = info.max_transfer_size(self.block_size);
595 let (block_count, mut flags) = match info.as_ref() {
596 DeviceInfo::Block(BlockInfo { block_count, device_flags, .. }) => {
597 (*block_count, *device_flags)
598 }
599 DeviceInfo::Partition(partition_info) => {
600 (partition_info.block_count, partition_info.device_flags)
601 }
602 DeviceInfo::Volume(volume_info) => {
603 let volume_info_fidl = self.session_manager().get_volume_info().await?;
604 let block_count = volume_info_fidl.0.slice_size
605 * volume_info_fidl.1.partition_slice_count
606 / self.block_size as u64;
607 (block_count, volume_info.device_flags)
608 }
609 };
610 if SM::SUPPORTS_DECOMPRESSION {
611 flags |= fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT;
612 }
613 responder.send(Ok(&fblock::BlockInfo {
614 block_count,
615 block_size: self.block_size,
616 max_transfer_size,
617 flags,
618 }))?;
619 }
620 fblock::BlockRequest::OpenSession { session, control_handle: _ } => {
621 return Ok(Some(SM::open_session(
622 self.orchestrator.clone(),
623 session.into_stream(),
624 OffsetMap::empty(),
625 self.block_size,
626 )));
627 }
628 fblock::BlockRequest::OpenSessionWithOptions {
629 session,
630 mappings,
631 control_handle: _,
632 } => {
633 let info = self.device_info();
634 let offset_map: OffsetMap = match mappings.as_slice().try_into() {
635 Ok(map) => map,
636 Err(status) => {
637 session.close_with_epitaph(status)?;
638 return Ok(None);
639 }
640 };
641 if let Some(max) = info.block_count() {
642 for m in offset_map.mappings() {
643 if m.target_block_offset.checked_add(m.length).unwrap_or(u64::MAX) > max {
644 log::warn!("Invalid mapping for session: {m:?} (max blocks {max})");
645 session.close_with_epitaph(zx::Status::OUT_OF_RANGE)?;
646 return Ok(None);
647 }
648 }
649 }
650 return Ok(Some(SM::open_session(
651 self.orchestrator.clone(),
652 session.into_stream(),
653 offset_map,
654 self.block_size,
655 )));
656 }
657 fblock::BlockRequest::GetTypeGuid { responder } => {
658 match self.device_info().type_guid() {
659 Some(guid) => {
660 responder.send(zx::sys::ZX_OK, Some(&fblock::Guid { value: guid }))?
661 }
662 None => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
663 }
664 }
665 fblock::BlockRequest::GetInstanceGuid { responder } => {
666 match self.device_info().instance_guid() {
667 Some(guid) => {
668 responder.send(zx::sys::ZX_OK, Some(&fblock::Guid { value: guid }))?
669 }
670 None => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
671 }
672 }
673 fblock::BlockRequest::GetName { responder } => {
674 let info = self.device_info();
675 match info.as_ref() {
676 DeviceInfo::Partition(_) | DeviceInfo::Volume(_) => {
677 responder.send(zx::sys::ZX_OK, Some(info.label()))?;
678 }
679 _ => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
680 }
681 }
682 fblock::BlockRequest::GetMetadata { responder } => {
683 let device_info = self.device_info();
684 match device_info.as_ref() {
685 DeviceInfo::Partition(info) => {
686 let mut type_guid =
687 fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
688 type_guid.value.copy_from_slice(&info.type_guid);
689 let mut instance_guid =
690 fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
691 instance_guid.value.copy_from_slice(&info.instance_guid);
692 let start_block_offset = info.start_block_offset;
693 let flags = info.flags;
694 responder.send(Ok(&fblock::PartitionInfo {
695 name: Some(info.name.clone()),
696 type_guid: Some(type_guid),
697 instance_guid: Some(instance_guid),
698 start_block_offset,
699 num_blocks: device_info.block_count(),
700 flags,
701 ..Default::default()
702 }))?;
703 }
704 DeviceInfo::Volume(info) => {
705 let mut type_guid =
706 fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
707 type_guid.value.copy_from_slice(&info.type_guid);
708 let mut instance_guid =
709 fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
710 instance_guid.value.copy_from_slice(&info.instance_guid);
711 responder.send(Ok(&fblock::PartitionInfo {
712 name: Some(info.name.clone()),
713 type_guid: Some(type_guid),
714 instance_guid: Some(instance_guid),
715 start_block_offset: None,
716 num_blocks: device_info.block_count(),
717 flags: Some(info.flags),
718 ..Default::default()
719 }))?;
720 }
721 _ => responder.send(Err(zx::sys::ZX_ERR_NOT_SUPPORTED))?,
722 }
723 }
724 fblock::BlockRequest::QuerySlices { responder, start_slices } => {
725 match self.session_manager().query_slices(&start_slices).await {
726 Ok(mut results) => {
727 let results_len = results.len();
728 assert!(results_len <= 16);
729 results.resize(16, fblock::VsliceRange { allocated: false, count: 0 });
730 responder.send(
731 zx::sys::ZX_OK,
732 &results.try_into().unwrap(),
733 results_len as u64,
734 )?;
735 }
736 Err(s) => {
737 responder.send(
738 s.into_raw(),
739 &[fblock::VsliceRange { allocated: false, count: 0 }; 16],
740 0,
741 )?;
742 }
743 }
744 }
745 fblock::BlockRequest::GetVolumeInfo { responder, .. } => {
746 match self.session_manager().get_volume_info().await {
747 Ok((manager_info, volume_info)) => {
748 responder.send(zx::sys::ZX_OK, Some(&manager_info), Some(&volume_info))?
749 }
750 Err(s) => responder.send(s.into_raw(), None, None)?,
751 }
752 }
753 fblock::BlockRequest::Extend { responder, start_slice, slice_count } => {
754 responder.send(
755 zx::Status::from(self.session_manager().extend(start_slice, slice_count).await)
756 .into_raw(),
757 )?;
758 }
759 fblock::BlockRequest::Shrink { responder, start_slice, slice_count } => {
760 responder.send(
761 zx::Status::from(self.session_manager().shrink(start_slice, slice_count).await)
762 .into_raw(),
763 )?;
764 }
765 fblock::BlockRequest::Destroy { responder, .. } => {
766 responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED)?;
767 }
768 }
769 Ok(None)
770 }
771
772 fn device_info(&self) -> Cow<'_, DeviceInfo> {
773 self.session_manager().get_info()
774 }
775}
776
777pub(crate) struct RegisteredVmo {
778 pub vmo: Arc<zx::Vmo>,
779 pub size: u64,
780 pub mapping: Option<Arc<VmoMapping>>,
781}
782
783impl RegisteredVmo {
784 fn validate_request(&self, vmo_offset: u64, length: u64) -> Result<(), zx::Status> {
787 if vmo_offset > self.size || self.size - vmo_offset < length {
788 Err(zx::Status::OUT_OF_RANGE)
789 } else {
790 Ok(())
791 }
792 }
793
794 fn get_or_create_mapping(&mut self) -> Result<Arc<VmoMapping>, zx::Status> {
796 match &self.mapping {
797 Some(mapping) => Ok(mapping.clone()),
798 None => {
799 let mapping = VmoMapping::new(&self.vmo, self.size as usize)?;
800 self.mapping = Some(mapping.clone());
801 Ok(mapping)
802 }
803 }
804 }
805}
806
807struct SessionHelper<SM: SessionManager> {
808 orchestrator: Arc<SM::Orchestrator>,
809 offset_map: OffsetMap,
810 max_transfer_blocks: Option<NonZero<u32>>,
811 block_size: u32,
812 peer_fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest>,
813 vmos: Mutex<BTreeMap<u16, RegisteredVmo>>,
814}
815
816struct VmoMapping {
817 base: usize,
818 size: usize,
819}
820
821impl VmoMapping {
822 fn new(vmo: &zx::Vmo, size: usize) -> Result<Arc<Self>, zx::Status> {
823 Ok(Arc::new(Self {
824 base: fuchsia_runtime::vmar_root_self()
825 .map(0, vmo, 0, size, zx::VmarFlags::PERM_WRITE | zx::VmarFlags::PERM_READ)
826 .inspect_err(|error| {
827 log::warn!(error:?, size; "VmoMapping: unable to map VMO");
828 })?,
829 size,
830 }))
831 }
832}
833
834impl Drop for VmoMapping {
835 fn drop(&mut self) {
836 unsafe {
838 let _ = fuchsia_runtime::vmar_root_self().unmap(self.base, self.size);
839 }
840 }
841}
842
843enum HandleRequestResult {
844 Ok,
846 Closed(Box<dyn FnOnce() + Send + 'static>),
850}
851
852impl<SM: SessionManager> SessionHelper<SM> {
853 fn new(
854 orchestrator: Arc<SM::Orchestrator>,
855 offset_map: OffsetMap,
856 max_transfer_blocks: Option<NonZero<u32>>,
857 block_size: u32,
858 ) -> Result<(Self, zx::Fifo<BlockFifoRequest, BlockFifoResponse>), zx::Status> {
859 let (peer_fifo, fifo) = zx::Fifo::create(16)?;
860 Ok((
861 Self {
862 orchestrator,
863 offset_map,
864 max_transfer_blocks,
865 block_size,
866 peer_fifo,
867 vmos: Mutex::default(),
868 },
869 fifo,
870 ))
871 }
872
873 fn session_manager(&self) -> &SM {
874 self.orchestrator.as_ref().borrow()
875 }
876
877 async fn handle_request(
878 &self,
879 request: fblock::SessionRequest,
880 ) -> Result<HandleRequestResult, Error> {
881 match request {
882 fblock::SessionRequest::GetFifo { responder } => {
883 let rights = zx::Rights::TRANSFER
884 | zx::Rights::READ
885 | zx::Rights::WRITE
886 | zx::Rights::SIGNAL
887 | zx::Rights::WAIT;
888 match self.peer_fifo.duplicate_handle(rights) {
889 Ok(fifo) => responder.send(Ok(fifo.downcast()))?,
890 Err(s) => responder.send(Err(s.into_raw()))?,
891 }
892 Ok(HandleRequestResult::Ok)
893 }
894 fblock::SessionRequest::AttachVmo { vmo, responder } => {
895 let info = vmo.info().map_err(Error::from)?;
896 if info.flags.contains(zx::VmoInfoFlags::RESIZABLE) {
897 responder.send(Err(zx::Status::INVALID_ARGS.into_raw()))?;
898 return Ok(HandleRequestResult::Ok);
899 }
900 let size = info.size_bytes;
901 let vmo = Arc::new(vmo);
902 let vmo_id = {
903 let mut vmos = self.vmos.lock();
904 if vmos.len() == u16::MAX as usize {
905 responder.send(Err(zx::Status::NO_RESOURCES.into_raw()))?;
906 return Ok(HandleRequestResult::Ok);
907 } else {
908 let vmo_id = match vmos.last_entry() {
909 None => 1,
910 Some(o) => {
911 o.key().checked_add(1).unwrap_or_else(|| {
912 let mut vmo_id = 1;
913 for (&id, _) in &*vmos {
915 if id > vmo_id {
916 break;
917 }
918 vmo_id = id + 1;
919 }
920 vmo_id
921 })
922 }
923 };
924 vmos.insert(
925 vmo_id,
926 RegisteredVmo { vmo: vmo.clone(), size, mapping: None },
927 );
928 vmo_id
929 }
930 };
931 SM::on_attach_vmo(self.orchestrator.clone(), &vmo).await?;
932 responder.send(Ok(&fblock::VmoId { id: vmo_id }))?;
933 Ok(HandleRequestResult::Ok)
934 }
935 fblock::SessionRequest::Close { responder } => {
936 Ok(HandleRequestResult::Closed(Box::new(move || {
937 if let Err(error) = responder.send(Ok(())) {
938 log::warn!(error:?; "Error sending close response");
939 }
940 })))
941 }
942 }
943 }
944
945 fn decode_fifo_request(
947 &self,
948 session: SM::Session,
949 request: &BlockFifoRequest,
950 ) -> Result<DecodedRequest, Option<BlockFifoResponse>> {
951 let flags = BlockIoFlag::from_bits_truncate(request.command.flags);
952
953 let request_bytes = request.length as u64 * self.block_size as u64;
954
955 let mut operation = BlockOpcode::from_primitive(request.command.opcode)
956 .ok_or(zx::Status::INVALID_ARGS)
957 .and_then(|code| {
958 if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
959 if code != BlockOpcode::Read {
960 return Err(zx::Status::INVALID_ARGS);
961 }
962 if !SM::SUPPORTS_DECOMPRESSION {
963 return Err(zx::Status::NOT_SUPPORTED);
964 }
965 }
966 if matches!(code, BlockOpcode::Read | BlockOpcode::Write | BlockOpcode::Trim) {
967 if request.length == 0 {
968 return Err(zx::Status::INVALID_ARGS);
969 }
970 if request.dev_offset.checked_add(request.length as u64).is_none() {
972 return Err(zx::Status::OUT_OF_RANGE);
973 }
974 }
975 if matches!(code, BlockOpcode::Read | BlockOpcode::Write) {
976 let vmo_byte_offset = request
977 .vmo_offset
978 .checked_mul(self.block_size as u64)
979 .ok_or(zx::Status::OUT_OF_RANGE)?;
980 if request_bytes.checked_add(vmo_byte_offset).is_none() {
981 return Err(zx::Status::OUT_OF_RANGE);
982 }
983 }
984 Ok(match code {
985 BlockOpcode::Read => Operation::Read {
986 device_block_offset: request.dev_offset,
987 block_count: request.length,
988 _unused: 0,
989 vmo_offset: request
990 .vmo_offset
991 .checked_mul(self.block_size as u64)
992 .ok_or(zx::Status::OUT_OF_RANGE)?,
993 options: ReadOptions {
994 inline_crypto: InlineCryptoOptions {
995 is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
996 dun: request.dun,
997 slot: request.slot,
998 },
999 },
1000 },
1001 BlockOpcode::Write => {
1002 let mut options = WriteOptions {
1003 inline_crypto: InlineCryptoOptions {
1004 is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
1005 dun: request.dun,
1006 slot: request.slot,
1007 },
1008 ..WriteOptions::default()
1009 };
1010 if flags.contains(BlockIoFlag::FORCE_ACCESS) {
1011 options.flags |= WriteFlags::FORCE_ACCESS;
1012 }
1013 if flags.contains(BlockIoFlag::PRE_BARRIER) {
1014 options.flags |= WriteFlags::PRE_BARRIER;
1015 }
1016 Operation::Write {
1017 device_block_offset: request.dev_offset,
1018 block_count: request.length,
1019 _unused: 0,
1020 options,
1021 vmo_offset: request
1022 .vmo_offset
1023 .checked_mul(self.block_size as u64)
1024 .ok_or(zx::Status::OUT_OF_RANGE)?,
1025 }
1026 }
1027 BlockOpcode::Flush => Operation::Flush,
1028 BlockOpcode::Trim => Operation::Trim {
1029 device_block_offset: request.dev_offset,
1030 block_count: request.length,
1031 },
1032 BlockOpcode::CloseVmo => Operation::CloseVmo,
1033 })
1034 });
1035
1036 let group_or_request = if flags.contains(BlockIoFlag::GROUP_ITEM) {
1037 GroupOrRequest::Group(request.group)
1038 } else {
1039 GroupOrRequest::Request(request.reqid)
1040 };
1041
1042 let mut active_requests = self.session_manager().active_requests().0.lock();
1043 let mut request_id = None;
1044
1045 if group_or_request.is_group() {
1056 for (key, group) in &mut active_requests.requests {
1060 if group.group_or_request == group_or_request
1061 && SM::session_eq(&group.session, &session)
1062 {
1063 if group.req_id.is_some() {
1064 if group.status == zx::Status::OK {
1066 group.status = zx::Status::INVALID_ARGS;
1067 }
1068 return Err(None);
1070 }
1071 if group.status == zx::Status::OK
1073 && let Some(info) = &mut group.decompression_info
1074 {
1075 if let Ok(Operation::Read {
1076 device_block_offset,
1077 mut block_count,
1078 options,
1079 vmo_offset: 0,
1080 ..
1081 }) = operation
1082 {
1083 let remaining_bytes = info
1084 .compressed_range
1085 .end
1086 .next_multiple_of(self.block_size as usize)
1087 as u64
1088 - info.bytes_so_far;
1089 if !flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD)
1090 || request.total_compressed_bytes != 0
1091 || request.uncompressed_bytes != 0
1092 || request.compressed_prefix_bytes != 0
1093 || (flags.contains(BlockIoFlag::GROUP_LAST)
1094 && info.bytes_so_far + request_bytes
1095 < info.compressed_range.end as u64)
1096 || (!flags.contains(BlockIoFlag::GROUP_LAST)
1097 && request_bytes >= remaining_bytes)
1098 {
1099 group.status = zx::Status::INVALID_ARGS;
1100 } else {
1101 if request_bytes > remaining_bytes {
1110 block_count = (remaining_bytes / self.block_size as u64) as u32;
1111 }
1112
1113 operation = Ok(Operation::ContinueDecompressedRead {
1114 offset: info.bytes_so_far,
1115 device_block_offset,
1116 block_count,
1117 options,
1118 });
1119
1120 info.bytes_so_far += block_count as u64 * self.block_size as u64;
1121 }
1122 } else {
1123 group.status = zx::Status::INVALID_ARGS;
1124 }
1125 }
1126 if flags.contains(BlockIoFlag::GROUP_LAST) {
1127 group.req_id = Some(request.reqid);
1128 if group.status != zx::Status::OK {
1131 operation = Err(group.status);
1132 }
1133 } else if group.status != zx::Status::OK {
1134 return Err(None);
1137 }
1138 request_id = Some(RequestId(key));
1139 group.count += 1;
1140 break;
1141 }
1142 }
1143 }
1144
1145 let is_single_request =
1146 !flags.contains(BlockIoFlag::GROUP_ITEM) || flags.contains(BlockIoFlag::GROUP_LAST);
1147
1148 let mut decompression_info = None;
1149 let vmo = match operation {
1150 Ok(Operation::Read {
1151 device_block_offset,
1152 mut block_count,
1153 options,
1154 vmo_offset,
1155 ..
1156 }) => match self.vmos.lock().get_mut(&request.vmoid) {
1157 Some(registered_vmo) => {
1158 if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
1159 let compressed_range = request.compressed_prefix_bytes as usize
1160 ..request.compressed_prefix_bytes as usize
1161 + request.total_compressed_bytes as usize;
1162 let required_buffer_size =
1163 compressed_range.end.next_multiple_of(self.block_size as usize);
1164
1165 if compressed_range.start >= compressed_range.end
1167 || vmo_offset.checked_add(request.uncompressed_bytes as u64).is_none()
1168 || (is_single_request && request_bytes < compressed_range.end as u64)
1169 || (!is_single_request && request_bytes >= required_buffer_size as u64)
1170 {
1171 Err(zx::Status::INVALID_ARGS)
1172 } else {
1173 let bytes_so_far = if request_bytes > required_buffer_size as u64 {
1180 block_count =
1181 (required_buffer_size / self.block_size as usize) as u32;
1182 required_buffer_size as u64
1183 } else {
1184 request_bytes
1185 };
1186
1187 registered_vmo
1189 .get_or_create_mapping()
1190 .and_then(|mapping| {
1191 if vmo_offset
1194 .checked_add(request.uncompressed_bytes as u64)
1195 .is_some_and(|end| end <= mapping.size as u64)
1196 {
1197 Ok(mapping)
1198 } else {
1199 Err(zx::Status::OUT_OF_RANGE)
1200 }
1201 })
1202 .map(|mapping| {
1203 operation = Ok(Operation::StartDecompressedRead {
1208 required_buffer_size,
1209 device_block_offset,
1210 block_count,
1211 options,
1212 });
1213 decompression_info = Some(DecompressionInfo {
1216 compressed_range,
1217 bytes_so_far,
1218 mapping,
1219 uncompressed_range: vmo_offset
1220 ..vmo_offset + request.uncompressed_bytes as u64,
1221 buffer: None,
1222 });
1223 None
1224 })
1225 }
1226 } else {
1227 registered_vmo
1228 .validate_request(vmo_offset, request_bytes)
1229 .map(|()| Some(registered_vmo.vmo.clone()))
1230 }
1231 }
1232 None => Err(zx::Status::IO),
1233 },
1234 Ok(Operation::Write { vmo_offset, .. }) => {
1235 self.vmos.lock().get(&request.vmoid).map_or(Err(zx::Status::IO), |registered_vmo| {
1236 registered_vmo
1237 .validate_request(vmo_offset, request_bytes)
1238 .map(|()| Some(registered_vmo.vmo.clone()))
1239 })
1240 }
1241 Ok(Operation::CloseVmo) => {
1242 self.vmos.lock().remove(&request.vmoid).map_or(
1243 Err(zx::Status::IO),
1244 |registered_vmo| {
1245 let vmo_clone = registered_vmo.vmo.clone();
1246 Epoch::global().defer(move || drop(vmo_clone));
1249 Ok(Some(registered_vmo.vmo))
1250 },
1251 )
1252 }
1253 _ => Ok(None),
1254 }
1255 .unwrap_or_else(|e| {
1256 operation = Err(e);
1257 None
1258 });
1259
1260 let trace_flow_id = NonZero::new(request.trace_flow_id);
1261 let request_id = request_id.unwrap_or_else(|| {
1262 RequestId(active_requests.requests.insert(ActiveRequest {
1263 session,
1264 group_or_request,
1265 trace_flow_id,
1266 _epoch_guard: Epoch::global().guard(),
1267 status: zx::Status::OK,
1268 count: 1,
1269 req_id: is_single_request.then_some(request.reqid),
1270 decompression_info,
1271 }))
1272 });
1273
1274 Ok(DecodedRequest {
1275 request_id,
1276 trace_flow_id,
1277 operation: operation.map_err(|status| {
1278 active_requests.complete_and_take_response(request_id, status).map(|(_, r)| r)
1279 })?,
1280 vmo,
1281 })
1282 }
1283
1284 fn take_vmos(&self) -> BTreeMap<u16, RegisteredVmo> {
1285 std::mem::take(&mut *self.vmos.lock())
1286 }
1287
1288 fn map_request(
1290 &self,
1291 mut request: DecodedRequest,
1292 active_request: &mut ActiveRequest<SM::Session>,
1293 ) -> Result<(DecodedRequest, Option<DecodedRequest>), zx::Status> {
1294 if active_request.status != zx::Status::OK {
1295 return Err(zx::Status::BAD_STATE);
1296 }
1297 if let Some(blocks) = request.operation.blocks() {
1298 if !self.offset_map.are_blocks_within_source_range(blocks) {
1299 return Err(zx::Status::OUT_OF_RANGE);
1300 }
1301 }
1302 let remainder =
1303 request.operation.map(&self.offset_map, self.max_transfer_blocks, self.block_size)?;
1304 if remainder.is_some() {
1305 active_request.count += 1;
1306 }
1307 static CACHE: AtomicU64 = AtomicU64::new(0);
1308 if let Some(context) =
1309 fuchsia_trace::TraceCategoryContext::acquire_cached("storage", &CACHE)
1310 {
1311 use fuchsia_trace::ArgValue;
1312 let trace_args = [
1313 ArgValue::of("request_id", request.request_id.0),
1314 ArgValue::of("opcode", request.operation.trace_label()),
1315 ];
1316 let _scope =
1317 fuchsia_trace::duration("storage", "block_server::start_transaction", &trace_args);
1318 if let Some(trace_flow_id) = active_request.trace_flow_id {
1319 fuchsia_trace::flow_step(
1320 &context,
1321 "block_server::start_transaction",
1322 trace_flow_id.get().into(),
1323 &[],
1324 );
1325 }
1326 }
1327 let remainder = remainder.map(|operation| DecodedRequest { operation, ..request.clone() });
1328 Ok((request, remainder))
1329 }
1330
1331 fn drop_active_requests(&self, pred: impl Fn(&SM::Session) -> bool) {
1336 self.session_manager().active_requests().0.lock().requests.retain(|_, r| !pred(&r.session));
1337 }
1338
1339 fn close_active_groups(&self, pred: impl Fn(&SM::Session) -> bool) {
1352 self.session_manager().active_requests().0.lock().requests.retain(|_, request| {
1353 if !pred(&request.session) || request.req_id.is_some() {
1354 return true;
1355 }
1356 request.req_id = Some(u32::MAX);
1359 request.count > 0
1360 });
1361 }
1362}
1363
1364#[repr(transparent)]
1365#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
1366pub struct RequestId(usize);
1367
1368#[derive(Clone, Debug)]
1369struct DecodedRequest {
1370 request_id: RequestId,
1371 trace_flow_id: TraceFlowId,
1372 operation: Operation,
1373 vmo: Option<Arc<zx::Vmo>>,
1374}
1375
1376pub type WriteFlags = block_protocol::WriteFlags;
1378pub type WriteOptions = block_protocol::WriteOptions;
1379pub type ReadOptions = block_protocol::ReadOptions;
1380pub type InlineCryptoOptions = block_protocol::InlineCryptoOptions;
1381
1382#[repr(C)]
1383#[derive(Clone, Debug, PartialEq, Eq)]
1384pub enum Operation {
1385 Read {
1390 device_block_offset: u64,
1391 block_count: u32,
1392 _unused: u32,
1393 vmo_offset: u64,
1394 options: ReadOptions,
1395 },
1396 Write {
1397 device_block_offset: u64,
1398 block_count: u32,
1399 _unused: u32,
1400 vmo_offset: u64,
1401 options: WriteOptions,
1402 },
1403 Flush,
1404 Trim {
1405 device_block_offset: u64,
1406 block_count: u32,
1407 },
1408 CloseVmo,
1410 StartDecompressedRead {
1412 required_buffer_size: usize,
1413 device_block_offset: u64,
1414 block_count: u32,
1415 options: ReadOptions,
1416 },
1417 ContinueDecompressedRead {
1419 offset: u64,
1420 device_block_offset: u64,
1421 block_count: u32,
1422 options: ReadOptions,
1423 },
1424}
1425
1426impl Operation {
1427 fn trace_label(&self) -> &'static str {
1428 match self {
1429 Operation::Read { .. } => "read",
1430 Operation::Write { .. } => "write",
1431 Operation::Flush { .. } => "flush",
1432 Operation::Trim { .. } => "trim",
1433 Operation::CloseVmo { .. } => "close_vmo",
1434 Operation::StartDecompressedRead { .. } => "start_decompressed_read",
1435 Operation::ContinueDecompressedRead { .. } => "continue_decompressed_read",
1436 }
1437 }
1438
1439 pub fn blocks(&self) -> Option<(u64, u32)> {
1441 match self {
1442 Operation::Read { device_block_offset, block_count, .. }
1443 | Operation::Write { device_block_offset, block_count, .. }
1444 | Operation::Trim { device_block_offset, block_count, .. } => {
1445 Some((*device_block_offset, *block_count))
1446 }
1447 _ => None,
1448 }
1449 }
1450
1451 fn blocks_mut(&mut self) -> Option<(&mut u64, &mut u32)> {
1453 match self {
1454 Operation::Read { device_block_offset, block_count, .. }
1455 | Operation::Write { device_block_offset, block_count, .. }
1456 | Operation::Trim { device_block_offset, block_count, .. } => {
1457 Some((device_block_offset, block_count))
1458 }
1459 _ => None,
1460 }
1461 }
1462
1463 fn map(
1466 &mut self,
1467 offset_map: &OffsetMap,
1468 max_transfer_blocks: Option<NonZero<u32>>,
1469 block_size: u32,
1470 ) -> Result<Option<Self>, zx::Status> {
1471 let mut max = match self {
1472 Operation::Read { .. } | Operation::Write { .. } => max_transfer_blocks.map(u32::from),
1473 _ => None,
1474 };
1475 let (offset, length) = match self.blocks_mut() {
1476 Some(b) => b,
1477 None => return Ok(None),
1478 };
1479 let orig_offset = *offset;
1480 if !offset_map.is_empty() {
1481 let (dev_offset, len) = offset_map.map(*offset).ok_or(zx::Status::OUT_OF_RANGE)?;
1482 *offset = dev_offset;
1483 max = match max {
1484 None => Some(len),
1485 Some(m) => Some(std::cmp::min(m, len)),
1486 };
1487 }
1488 if let Some(max) = max {
1489 if *length as u64 > max as u64 {
1490 let rem = *length - max;
1491 *length = max;
1492 return Ok(Some(match self {
1493 Operation::Read {
1494 device_block_offset: _,
1495 block_count: _,
1496 vmo_offset,
1497 _unused,
1498 options,
1499 } => {
1500 let mut options = *options;
1501 options.inline_crypto.dun += max;
1502 Operation::Read {
1503 device_block_offset: orig_offset + max as u64,
1504 block_count: rem,
1505 vmo_offset: *vmo_offset + max as u64 * block_size as u64,
1506 _unused: *_unused,
1507 options: options,
1508 }
1509 }
1510 Operation::Write {
1511 device_block_offset: _,
1512 block_count: _,
1513 _unused,
1514 vmo_offset,
1515 options,
1516 } => {
1517 let mut options = *options;
1518 options.inline_crypto.dun += max;
1519 Operation::Write {
1520 device_block_offset: orig_offset + max as u64,
1521 block_count: rem,
1522 _unused: *_unused,
1523 vmo_offset: *vmo_offset + max as u64 * block_size as u64,
1524 options: options,
1525 }
1526 }
1527 Operation::Trim { device_block_offset: _, block_count: _ } => Operation::Trim {
1528 device_block_offset: orig_offset + max as u64,
1529 block_count: rem,
1530 },
1531 _ => unreachable!(),
1532 }));
1533 }
1534 }
1535 Ok(None)
1536 }
1537
1538 pub fn has_write_flag(&self, value: WriteFlags) -> bool {
1540 if let Operation::Write { options, .. } = self {
1541 options.flags.contains(value)
1542 } else {
1543 false
1544 }
1545 }
1546
1547 pub fn take_write_flag(&mut self, value: WriteFlags) -> bool {
1549 if let Operation::Write { options, .. } = self {
1550 let result = options.flags.contains(value);
1551 options.flags.remove(value);
1552 result
1553 } else {
1554 false
1555 }
1556 }
1557}
1558
1559#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
1560pub enum GroupOrRequest {
1561 Group(u16),
1562 Request(u32),
1563}
1564
1565impl GroupOrRequest {
1566 fn is_group(&self) -> bool {
1567 matches!(self, Self::Group(_))
1568 }
1569
1570 fn group_id(&self) -> Option<u16> {
1571 match self {
1572 Self::Group(id) => Some(*id),
1573 Self::Request(_) => None,
1574 }
1575 }
1576}
1577
1578#[cfg(test)]
1579mod tests {
1580 use super::{
1581 BlockOffsetMapping, BlockServer, DeviceInfo, FIFO_MAX_REQUESTS, OffsetMap, Operation,
1582 PartitionInfo, TraceFlowId,
1583 };
1584 use assert_matches::assert_matches;
1585 use block_protocol::{
1586 BlockFifoCommand, BlockFifoRequest, BlockFifoResponse, InlineCryptoOptions, ReadOptions,
1587 WriteFlags, WriteOptions,
1588 };
1589 use fidl_fuchsia_storage_block as fblock;
1590 use fidl_fuchsia_storage_block::{BlockIoFlag, BlockOpcode};
1591 use fuchsia_async as fasync;
1592 use fuchsia_sync::Mutex;
1593 use futures::FutureExt as _;
1594 use futures::channel::oneshot;
1595 use futures::future::BoxFuture;
1596 use std::borrow::Cow;
1597 use std::future::poll_fn;
1598 use std::num::NonZero;
1599 use std::pin::pin;
1600 use std::sync::Arc;
1601 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
1602 use std::task::{Context, Poll};
1603
1604 #[derive(Default)]
1605 struct MockInterface {
1606 info: Option<DeviceInfo>,
1607 read_hook: Option<
1608 Box<
1609 dyn Fn(u64, u32, &Arc<zx::Vmo>, u64) -> BoxFuture<'static, Result<(), zx::Status>>
1610 + Send
1611 + Sync,
1612 >,
1613 >,
1614 write_hook:
1615 Option<Box<dyn Fn(u64) -> BoxFuture<'static, Result<(), zx::Status>> + Send + Sync>>,
1616 barrier_hook: Option<Box<dyn Fn() -> Result<(), zx::Status> + Send + Sync>>,
1617 }
1618
1619 impl super::async_interface::Interface for MockInterface {
1620 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
1621 Ok(())
1622 }
1623
1624 fn get_info(&self) -> Cow<'_, DeviceInfo> {
1625 match &self.info {
1626 Some(info) => Cow::Borrowed(info),
1627 None => Cow::Owned(test_device_info()),
1628 }
1629 }
1630
1631 async fn read(
1632 &self,
1633 device_block_offset: u64,
1634 block_count: u32,
1635 vmo: &Arc<zx::Vmo>,
1636 vmo_offset: u64,
1637 _opts: ReadOptions,
1638 _trace_flow_id: TraceFlowId,
1639 ) -> Result<(), zx::Status> {
1640 if let Some(total) = self.get_info().block_count() {
1641 if device_block_offset >= total || total - device_block_offset < block_count as u64
1642 {
1643 return Err(zx::Status::OUT_OF_RANGE);
1644 }
1645 }
1646 if let Some(read_hook) = &self.read_hook {
1647 read_hook(device_block_offset, block_count, vmo, vmo_offset).await
1648 } else {
1649 unimplemented!();
1650 }
1651 }
1652
1653 async fn write(
1654 &self,
1655 device_block_offset: u64,
1656 _block_count: u32,
1657 _vmo: &Arc<zx::Vmo>,
1658 _vmo_offset: u64,
1659 opts: WriteOptions,
1660 _trace_flow_id: TraceFlowId,
1661 ) -> Result<(), zx::Status> {
1662 if opts.flags.contains(WriteFlags::PRE_BARRIER)
1663 && let Some(barrier_hook) = &self.barrier_hook
1664 {
1665 barrier_hook()?;
1666 }
1667 if let Some(write_hook) = &self.write_hook {
1668 write_hook(device_block_offset).await
1669 } else {
1670 unimplemented!();
1671 }
1672 }
1673
1674 async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
1675 Ok(())
1676 }
1677
1678 async fn trim(
1679 &self,
1680 _device_block_offset: u64,
1681 _block_count: u32,
1682 _trace_flow_id: TraceFlowId,
1683 ) -> Result<(), zx::Status> {
1684 unreachable!();
1685 }
1686
1687 async fn get_volume_info(
1688 &self,
1689 ) -> Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status> {
1690 let () = std::future::pending().await;
1692 unreachable!();
1693 }
1694 }
1695
1696 const BLOCK_SIZE: u32 = 512;
1697 const MAX_TRANSFER_BLOCKS: u32 = 10;
1698
1699 fn test_device_info() -> DeviceInfo {
1700 DeviceInfo::Partition(PartitionInfo {
1701 device_flags: fblock::DeviceFlag::READONLY
1702 | fblock::DeviceFlag::BARRIER_SUPPORT
1703 | fblock::DeviceFlag::FUA_SUPPORT,
1704 max_transfer_blocks: NonZero::new(MAX_TRANSFER_BLOCKS),
1705 start_block_offset: Some(0),
1706 block_count: 100,
1707 type_guid: [1; 16],
1708 instance_guid: [2; 16],
1709 name: "foo".to_string(),
1710 flags: Some(0xabcd),
1711 })
1712 }
1713
1714 #[fuchsia::test]
1715 async fn test_barriers_ordering() {
1716 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1717 let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
1718 let barrier_called = Arc::new(AtomicBool::new(false));
1719
1720 futures::join!(
1721 async move {
1722 let barrier_called_clone = barrier_called.clone();
1723 let block_server = BlockServer::new(
1724 BLOCK_SIZE,
1725 Arc::new(MockInterface {
1726 barrier_hook: Some(Box::new(move || {
1727 barrier_called.store(true, Ordering::Relaxed);
1728 Ok(())
1729 })),
1730 write_hook: Some(Box::new(move |device_block_offset| {
1731 let barrier_called = barrier_called_clone.clone();
1732 Box::pin(async move {
1733 if device_block_offset % 2 == 0 {
1735 fasync::Timer::new(fasync::MonotonicInstant::after(
1736 zx::MonotonicDuration::from_millis(200),
1737 ))
1738 .await;
1739 }
1740 assert!(barrier_called.load(Ordering::Relaxed));
1741 Ok(())
1742 })
1743 })),
1744 ..MockInterface::default()
1745 }),
1746 );
1747 block_server.handle_requests(stream).await.unwrap();
1748 },
1749 async move {
1750 let (session_proxy, server) = fidl::endpoints::create_proxy();
1751
1752 proxy.open_session(server).unwrap();
1753
1754 let vmo_id = session_proxy
1755 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1756 .await
1757 .unwrap()
1758 .unwrap();
1759 assert_ne!(vmo_id.id, 0);
1760
1761 let mut fifo =
1762 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1763 let (mut reader, mut writer) = fifo.async_io();
1764
1765 writer
1766 .write_entries(&BlockFifoRequest {
1767 command: BlockFifoCommand {
1768 opcode: BlockOpcode::Write.into_primitive(),
1769 flags: BlockIoFlag::PRE_BARRIER.bits(),
1770 ..Default::default()
1771 },
1772 vmoid: vmo_id.id,
1773 dev_offset: 0,
1774 length: 5,
1775 vmo_offset: 6,
1776 ..Default::default()
1777 })
1778 .await
1779 .unwrap();
1780
1781 for i in 0..10 {
1782 writer
1783 .write_entries(&BlockFifoRequest {
1784 command: BlockFifoCommand {
1785 opcode: BlockOpcode::Write.into_primitive(),
1786 ..Default::default()
1787 },
1788 vmoid: vmo_id.id,
1789 dev_offset: i + 1,
1790 length: 5,
1791 vmo_offset: 6,
1792 ..Default::default()
1793 })
1794 .await
1795 .unwrap();
1796 }
1797 for _ in 0..11 {
1798 let mut response = BlockFifoResponse::default();
1799 reader.read_entries(&mut response).await.unwrap();
1800 assert_eq!(response.status, zx::sys::ZX_OK);
1801 }
1802
1803 std::mem::drop(proxy);
1804 }
1805 );
1806 }
1807
1808 #[fuchsia::test]
1809 async fn test_info() {
1810 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1811
1812 futures::join!(
1813 async {
1814 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1815 block_server.handle_requests(stream).await.unwrap();
1816 },
1817 async {
1818 let expected_info = test_device_info();
1819 let partition_info = if let DeviceInfo::Partition(info) = &expected_info {
1820 info
1821 } else {
1822 unreachable!()
1823 };
1824
1825 let block_info = proxy.get_info().await.unwrap().unwrap();
1826 assert_eq!(block_info.block_count, expected_info.block_count().unwrap());
1827 assert_eq!(
1828 block_info.flags,
1829 fblock::DeviceFlag::READONLY
1830 | fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT
1831 | fblock::DeviceFlag::BARRIER_SUPPORT
1832 | fblock::DeviceFlag::FUA_SUPPORT
1833 );
1834
1835 assert_eq!(block_info.max_transfer_size, MAX_TRANSFER_BLOCKS * BLOCK_SIZE);
1836
1837 let (status, type_guid) = proxy.get_type_guid().await.unwrap();
1838 assert_eq!(status, zx::sys::ZX_OK);
1839 assert_eq!(&type_guid.as_ref().unwrap().value, &partition_info.type_guid);
1840
1841 let (status, instance_guid) = proxy.get_instance_guid().await.unwrap();
1842 assert_eq!(status, zx::sys::ZX_OK);
1843 assert_eq!(&instance_guid.as_ref().unwrap().value, &partition_info.instance_guid);
1844
1845 let (status, name) = proxy.get_name().await.unwrap();
1846 assert_eq!(status, zx::sys::ZX_OK);
1847 assert_eq!(name.as_ref(), Some(&partition_info.name));
1848
1849 let metadata = proxy.get_metadata().await.unwrap().expect("get_flags failed");
1850 assert_eq!(metadata.name, name);
1851 assert_eq!(metadata.type_guid.as_ref(), type_guid.as_deref());
1852 assert_eq!(metadata.instance_guid.as_ref(), instance_guid.as_deref());
1853 let expected_start = partition_info.start_block_offset.or(Some(0));
1854 assert_eq!(metadata.start_block_offset, expected_start);
1855 assert_eq!(metadata.num_blocks, Some(partition_info.block_count));
1856 assert_eq!(metadata.flags, partition_info.flags);
1857
1858 std::mem::drop(proxy);
1859 }
1860 );
1861 }
1862
1863 #[fuchsia::test]
1864 async fn test_attach_vmo() {
1865 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1866
1867 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
1868 let koid = vmo.koid().unwrap();
1869
1870 futures::join!(
1871 async {
1872 let block_server = BlockServer::new(
1873 BLOCK_SIZE,
1874 Arc::new(MockInterface {
1875 read_hook: Some(Box::new(move |_, _, vmo, _| {
1876 assert_eq!(vmo.koid().unwrap(), koid);
1877 Box::pin(async { Ok(()) })
1878 })),
1879 ..MockInterface::default()
1880 }),
1881 );
1882 block_server.handle_requests(stream).await.unwrap();
1883 },
1884 async move {
1885 let (session_proxy, server) = fidl::endpoints::create_proxy();
1886
1887 proxy.open_session(server).unwrap();
1888
1889 let vmo_id = session_proxy
1890 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1891 .await
1892 .unwrap()
1893 .unwrap();
1894 assert_ne!(vmo_id.id, 0);
1895
1896 let mut fifo =
1897 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1898 let (mut reader, mut writer) = fifo.async_io();
1899
1900 let mut count = 1;
1902 loop {
1903 match session_proxy
1904 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1905 .await
1906 .unwrap()
1907 {
1908 Ok(vmo_id) => assert_ne!(vmo_id.id, 0),
1909 Err(e) => {
1910 assert_eq!(e, zx::sys::ZX_ERR_NO_RESOURCES);
1911 break;
1912 }
1913 }
1914
1915 if count % 10 == 0 {
1917 writer
1918 .write_entries(&BlockFifoRequest {
1919 command: BlockFifoCommand {
1920 opcode: BlockOpcode::Read.into_primitive(),
1921 ..Default::default()
1922 },
1923 vmoid: vmo_id.id,
1924 length: 1,
1925 ..Default::default()
1926 })
1927 .await
1928 .unwrap();
1929
1930 let mut response = BlockFifoResponse::default();
1931 reader.read_entries(&mut response).await.unwrap();
1932 assert_eq!(response.status, zx::sys::ZX_OK);
1933 }
1934
1935 count += 1;
1936 }
1937
1938 assert_eq!(count, u16::MAX as u64);
1939
1940 writer
1942 .write_entries(&BlockFifoRequest {
1943 command: BlockFifoCommand {
1944 opcode: BlockOpcode::CloseVmo.into_primitive(),
1945 ..Default::default()
1946 },
1947 vmoid: vmo_id.id,
1948 ..Default::default()
1949 })
1950 .await
1951 .unwrap();
1952
1953 let mut response = BlockFifoResponse::default();
1954 reader.read_entries(&mut response).await.unwrap();
1955 assert_eq!(response.status, zx::sys::ZX_OK);
1956
1957 let new_vmo_id = session_proxy
1958 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1959 .await
1960 .unwrap()
1961 .unwrap();
1962 assert_eq!(new_vmo_id.id, vmo_id.id);
1964
1965 std::mem::drop(proxy);
1966 }
1967 );
1968 }
1969
1970 #[fuchsia::test]
1971 async fn test_attach_resizable_vmo_fails() {
1972 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1973 let resizable_vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 4096).unwrap();
1974
1975 futures::join!(
1976 async {
1977 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1978 block_server.handle_requests(stream).await.unwrap();
1979 },
1980 async move {
1981 let (session_proxy, server) = fidl::endpoints::create_proxy();
1982 proxy.open_session(server).unwrap();
1983 let res = session_proxy.attach_vmo(resizable_vmo).await.unwrap();
1984 assert_eq!(res, Err(zx::sys::ZX_ERR_INVALID_ARGS));
1985 std::mem::drop(proxy);
1986 }
1987 );
1988 }
1989
1990 #[fuchsia::test]
1991 async fn test_close() {
1992 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1993
1994 let mut server = std::pin::pin!(
1995 async {
1996 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1997 block_server.handle_requests(stream).await.unwrap();
1998 }
1999 .fuse()
2000 );
2001
2002 let mut client = std::pin::pin!(
2003 async {
2004 let (session_proxy, server) = fidl::endpoints::create_proxy();
2005
2006 proxy.open_session(server).unwrap();
2007
2008 std::mem::drop(proxy);
2011
2012 session_proxy.close().await.unwrap().unwrap();
2013
2014 let _: () = std::future::pending().await;
2016 }
2017 .fuse()
2018 );
2019
2020 futures::select!(
2021 _ = server => {}
2022 _ = client => unreachable!(),
2023 );
2024 }
2025
2026 #[derive(Default)]
2027 struct IoMockInterface {
2028 do_checks: bool,
2029 expected_op: Arc<Mutex<Option<ExpectedOp>>>,
2030 return_errors: bool,
2031 }
2032
2033 #[derive(Debug)]
2034 enum ExpectedOp {
2035 Read(u64, u32, u64),
2036 Write(u64, u32, u64),
2037 Trim(u64, u32),
2038 Flush,
2039 }
2040
2041 impl super::async_interface::Interface for IoMockInterface {
2042 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
2043 Ok(())
2044 }
2045
2046 fn get_info(&self) -> Cow<'_, DeviceInfo> {
2047 Cow::Owned(DeviceInfo::Block(crate::BlockInfo {
2048 block_count: 100,
2049 ..Default::default()
2050 }))
2051 }
2052
2053 async fn read(
2054 &self,
2055 device_block_offset: u64,
2056 block_count: u32,
2057 _vmo: &Arc<zx::Vmo>,
2058 vmo_offset: u64,
2059 _opts: ReadOptions,
2060 _trace_flow_id: TraceFlowId,
2061 ) -> Result<(), zx::Status> {
2062 if self.return_errors {
2063 Err(zx::Status::INTERNAL)
2064 } else {
2065 if self.do_checks {
2066 assert_matches!(
2067 self.expected_op.lock().take(),
2068 Some(ExpectedOp::Read(a, b, c)) if device_block_offset == a &&
2069 block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
2070 "Read {device_block_offset} {block_count} {vmo_offset}"
2071 );
2072 }
2073 Ok(())
2074 }
2075 }
2076
2077 async fn write(
2078 &self,
2079 device_block_offset: u64,
2080 block_count: u32,
2081 _vmo: &Arc<zx::Vmo>,
2082 vmo_offset: u64,
2083 _write_opts: WriteOptions,
2084 _trace_flow_id: TraceFlowId,
2085 ) -> Result<(), zx::Status> {
2086 if self.return_errors {
2087 Err(zx::Status::NOT_SUPPORTED)
2088 } else {
2089 if self.do_checks {
2090 assert_matches!(
2091 self.expected_op.lock().take(),
2092 Some(ExpectedOp::Write(a, b, c)) if device_block_offset == a &&
2093 block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
2094 "Write {device_block_offset} {block_count} {vmo_offset}"
2095 );
2096 }
2097 Ok(())
2098 }
2099 }
2100
2101 async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
2102 if self.return_errors {
2103 Err(zx::Status::NO_RESOURCES)
2104 } else {
2105 if self.do_checks {
2106 assert_matches!(self.expected_op.lock().take(), Some(ExpectedOp::Flush));
2107 }
2108 Ok(())
2109 }
2110 }
2111
2112 async fn trim(
2113 &self,
2114 device_block_offset: u64,
2115 block_count: u32,
2116 _trace_flow_id: TraceFlowId,
2117 ) -> Result<(), zx::Status> {
2118 if self.return_errors {
2119 Err(zx::Status::NO_MEMORY)
2120 } else {
2121 if self.do_checks {
2122 assert_matches!(
2123 self.expected_op.lock().take(),
2124 Some(ExpectedOp::Trim(a, b)) if device_block_offset == a &&
2125 block_count == b,
2126 "Trim {device_block_offset} {block_count}"
2127 );
2128 }
2129 Ok(())
2130 }
2131 }
2132 }
2133
2134 #[fuchsia::test]
2135 async fn test_io() {
2136 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2137
2138 let expected_op = Arc::new(Mutex::new(None));
2139 let expected_op_clone = expected_op.clone();
2140
2141 let server = async {
2142 let block_server = BlockServer::new(
2143 BLOCK_SIZE,
2144 Arc::new(IoMockInterface {
2145 return_errors: false,
2146 do_checks: true,
2147 expected_op: expected_op_clone,
2148 }),
2149 );
2150 block_server.handle_requests(stream).await.unwrap();
2151 };
2152
2153 let client = async move {
2154 let (session_proxy, server) = fidl::endpoints::create_proxy();
2155
2156 proxy.open_session(server).unwrap();
2157
2158 let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
2159 let vmo_id = session_proxy
2160 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2161 .await
2162 .unwrap()
2163 .unwrap();
2164
2165 let mut fifo =
2166 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2167 let (mut reader, mut writer) = fifo.async_io();
2168
2169 *expected_op.lock() = Some(ExpectedOp::Read(1, 2, 3));
2171 writer
2172 .write_entries(&BlockFifoRequest {
2173 command: BlockFifoCommand {
2174 opcode: BlockOpcode::Read.into_primitive(),
2175 ..Default::default()
2176 },
2177 vmoid: vmo_id.id,
2178 dev_offset: 1,
2179 length: 2,
2180 vmo_offset: 3,
2181 ..Default::default()
2182 })
2183 .await
2184 .unwrap();
2185
2186 let mut response = BlockFifoResponse::default();
2187 reader.read_entries(&mut response).await.unwrap();
2188 assert_eq!(response.status, zx::sys::ZX_OK);
2189
2190 *expected_op.lock() = Some(ExpectedOp::Write(4, 5, 6));
2192 writer
2193 .write_entries(&BlockFifoRequest {
2194 command: BlockFifoCommand {
2195 opcode: BlockOpcode::Write.into_primitive(),
2196 ..Default::default()
2197 },
2198 vmoid: vmo_id.id,
2199 dev_offset: 4,
2200 length: 5,
2201 vmo_offset: 6,
2202 ..Default::default()
2203 })
2204 .await
2205 .unwrap();
2206
2207 let mut response = BlockFifoResponse::default();
2208 reader.read_entries(&mut response).await.unwrap();
2209 assert_eq!(response.status, zx::sys::ZX_OK);
2210
2211 *expected_op.lock() = Some(ExpectedOp::Flush);
2213 writer
2214 .write_entries(&BlockFifoRequest {
2215 command: BlockFifoCommand {
2216 opcode: BlockOpcode::Flush.into_primitive(),
2217 ..Default::default()
2218 },
2219 ..Default::default()
2220 })
2221 .await
2222 .unwrap();
2223
2224 reader.read_entries(&mut response).await.unwrap();
2225 assert_eq!(response.status, zx::sys::ZX_OK);
2226
2227 *expected_op.lock() = Some(ExpectedOp::Trim(7, 8));
2229 writer
2230 .write_entries(&BlockFifoRequest {
2231 command: BlockFifoCommand {
2232 opcode: BlockOpcode::Trim.into_primitive(),
2233 ..Default::default()
2234 },
2235 dev_offset: 7,
2236 length: 8,
2237 ..Default::default()
2238 })
2239 .await
2240 .unwrap();
2241
2242 reader.read_entries(&mut response).await.unwrap();
2243 assert_eq!(response.status, zx::sys::ZX_OK);
2244
2245 std::mem::drop(proxy);
2246 };
2247
2248 futures::join!(server, client);
2249 }
2250
2251 #[fuchsia::test]
2252 async fn test_io_errors() {
2253 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2254
2255 futures::join!(
2256 async {
2257 let block_server = BlockServer::new(
2258 BLOCK_SIZE,
2259 Arc::new(IoMockInterface {
2260 return_errors: true,
2261 do_checks: false,
2262 expected_op: Arc::new(Mutex::new(None)),
2263 }),
2264 );
2265 block_server.handle_requests(stream).await.unwrap();
2266 },
2267 async move {
2268 let (session_proxy, server) = fidl::endpoints::create_proxy();
2269
2270 proxy.open_session(server).unwrap();
2271
2272 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2273 let vmo_id = session_proxy
2274 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2275 .await
2276 .unwrap()
2277 .unwrap();
2278
2279 let mut fifo =
2280 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2281 let (mut reader, mut writer) = fifo.async_io();
2282
2283 writer
2285 .write_entries(&BlockFifoRequest {
2286 command: BlockFifoCommand {
2287 opcode: BlockOpcode::Read.into_primitive(),
2288 ..Default::default()
2289 },
2290 vmoid: vmo_id.id,
2291 length: 1,
2292 reqid: 1,
2293 ..Default::default()
2294 })
2295 .await
2296 .unwrap();
2297
2298 let mut response = BlockFifoResponse::default();
2299 reader.read_entries(&mut response).await.unwrap();
2300 assert_eq!(response.status, zx::sys::ZX_ERR_INTERNAL);
2301
2302 writer
2304 .write_entries(&BlockFifoRequest {
2305 command: BlockFifoCommand {
2306 opcode: BlockOpcode::Write.into_primitive(),
2307 ..Default::default()
2308 },
2309 vmoid: vmo_id.id,
2310 length: 1,
2311 reqid: 2,
2312 ..Default::default()
2313 })
2314 .await
2315 .unwrap();
2316
2317 reader.read_entries(&mut response).await.unwrap();
2318 assert_eq!(response.status, zx::sys::ZX_ERR_NOT_SUPPORTED);
2319
2320 writer
2322 .write_entries(&BlockFifoRequest {
2323 command: BlockFifoCommand {
2324 opcode: BlockOpcode::Flush.into_primitive(),
2325 ..Default::default()
2326 },
2327 reqid: 3,
2328 ..Default::default()
2329 })
2330 .await
2331 .unwrap();
2332
2333 reader.read_entries(&mut response).await.unwrap();
2334 assert_eq!(response.status, zx::sys::ZX_ERR_NO_RESOURCES);
2335
2336 writer
2338 .write_entries(&BlockFifoRequest {
2339 command: BlockFifoCommand {
2340 opcode: BlockOpcode::Trim.into_primitive(),
2341 ..Default::default()
2342 },
2343 reqid: 4,
2344 length: 1,
2345 ..Default::default()
2346 })
2347 .await
2348 .unwrap();
2349
2350 reader.read_entries(&mut response).await.unwrap();
2351 assert_eq!(response.status, zx::sys::ZX_ERR_NO_MEMORY);
2352
2353 std::mem::drop(proxy);
2354 }
2355 );
2356 }
2357
2358 #[fuchsia::test]
2359 async fn test_invalid_args() {
2360 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2361
2362 futures::join!(
2363 async {
2364 let block_server = BlockServer::new(
2365 BLOCK_SIZE,
2366 Arc::new(IoMockInterface {
2367 return_errors: false,
2368 do_checks: false,
2369 expected_op: Arc::new(Mutex::new(None)),
2370 }),
2371 );
2372 block_server.handle_requests(stream).await.unwrap();
2373 },
2374 async move {
2375 let (session_proxy, server) = fidl::endpoints::create_proxy();
2376
2377 proxy.open_session(server).unwrap();
2378
2379 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2380 let vmo_id = session_proxy
2381 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2382 .await
2383 .unwrap()
2384 .unwrap();
2385
2386 let mut fifo =
2387 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2388
2389 async fn test(
2390 fifo: &mut fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
2391 request: BlockFifoRequest,
2392 ) -> Result<(), zx::Status> {
2393 let (mut reader, mut writer) = fifo.async_io();
2394 writer.write_entries(&request).await.unwrap();
2395 let mut response = BlockFifoResponse::default();
2396 reader.read_entries(&mut response).await.unwrap();
2397 zx::Status::ok(response.status)
2398 }
2399
2400 let good_read_request = || BlockFifoRequest {
2403 command: BlockFifoCommand {
2404 opcode: BlockOpcode::Read.into_primitive(),
2405 ..Default::default()
2406 },
2407 length: 1,
2408 vmoid: vmo_id.id,
2409 ..Default::default()
2410 };
2411
2412 assert_eq!(
2413 test(
2414 &mut fifo,
2415 BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_read_request() }
2416 )
2417 .await,
2418 Err(zx::Status::IO)
2419 );
2420
2421 assert_eq!(
2422 test(
2423 &mut fifo,
2424 BlockFifoRequest {
2425 vmo_offset: 0xffff_ffff_ffff_ffff,
2426 ..good_read_request()
2427 }
2428 )
2429 .await,
2430 Err(zx::Status::OUT_OF_RANGE)
2431 );
2432
2433 assert_eq!(
2434 test(
2435 &mut fifo,
2436 BlockFifoRequest {
2437 vmo_offset: 0x007f_ffff_ffff_ffff,
2438 length: 2,
2439 ..good_read_request()
2440 }
2441 )
2442 .await,
2443 Err(zx::Status::OUT_OF_RANGE)
2444 );
2445
2446 assert_eq!(
2447 test(&mut fifo, BlockFifoRequest { length: 0, ..good_read_request() }).await,
2448 Err(zx::Status::INVALID_ARGS)
2449 );
2450
2451 assert_eq!(
2452 test(
2453 &mut fifo,
2454 BlockFifoRequest { vmo_offset: 8, length: 1, ..good_read_request() }
2455 )
2456 .await,
2457 Err(zx::Status::OUT_OF_RANGE)
2458 );
2459
2460 let good_write_request = || BlockFifoRequest {
2463 command: BlockFifoCommand {
2464 opcode: BlockOpcode::Write.into_primitive(),
2465 ..Default::default()
2466 },
2467 length: 1,
2468 vmoid: vmo_id.id,
2469 ..Default::default()
2470 };
2471
2472 assert_eq!(
2473 test(
2474 &mut fifo,
2475 BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_write_request() }
2476 )
2477 .await,
2478 Err(zx::Status::IO)
2479 );
2480
2481 assert_eq!(
2482 test(
2483 &mut fifo,
2484 BlockFifoRequest {
2485 vmo_offset: 0xffff_ffff_ffff_ffff,
2486 ..good_write_request()
2487 }
2488 )
2489 .await,
2490 Err(zx::Status::OUT_OF_RANGE)
2491 );
2492
2493 assert_eq!(
2494 test(
2495 &mut fifo,
2496 BlockFifoRequest {
2497 vmo_offset: 0x007f_ffff_ffff_ffff,
2498 length: 2,
2499 ..good_write_request()
2500 }
2501 )
2502 .await,
2503 Err(zx::Status::OUT_OF_RANGE)
2504 );
2505
2506 assert_eq!(
2507 test(&mut fifo, BlockFifoRequest { length: 0, ..good_write_request() }).await,
2508 Err(zx::Status::INVALID_ARGS)
2509 );
2510
2511 assert_eq!(
2512 test(
2513 &mut fifo,
2514 BlockFifoRequest { vmo_offset: 8, length: 1, ..good_write_request() }
2515 )
2516 .await,
2517 Err(zx::Status::OUT_OF_RANGE)
2518 );
2519
2520 assert_eq!(
2523 test(
2524 &mut fifo,
2525 BlockFifoRequest {
2526 command: BlockFifoCommand {
2527 opcode: BlockOpcode::CloseVmo.into_primitive(),
2528 ..Default::default()
2529 },
2530 vmoid: vmo_id.id + 1,
2531 ..Default::default()
2532 }
2533 )
2534 .await,
2535 Err(zx::Status::IO)
2536 );
2537
2538 std::mem::drop(proxy);
2539 }
2540 );
2541 }
2542
2543 #[fuchsia::test]
2544 async fn test_concurrent_requests() {
2545 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2546
2547 let waiting_readers = Arc::new(Mutex::new(Vec::new()));
2548 let waiting_readers_clone = waiting_readers.clone();
2549
2550 futures::join!(
2551 async move {
2552 let block_server = BlockServer::new(
2553 BLOCK_SIZE,
2554 Arc::new(MockInterface {
2555 read_hook: Some(Box::new(move |dev_block_offset, _, _, _| {
2556 let (tx, rx) = oneshot::channel();
2557 waiting_readers_clone.lock().push((dev_block_offset as u32, tx));
2558 Box::pin(async move {
2559 let _ = rx.await;
2560 Ok(())
2561 })
2562 })),
2563 ..MockInterface::default()
2564 }),
2565 );
2566 block_server.handle_requests(stream).await.unwrap();
2567 },
2568 async move {
2569 let (session_proxy, server) = fidl::endpoints::create_proxy();
2570
2571 proxy.open_session(server).unwrap();
2572
2573 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2574 let vmo_id = session_proxy
2575 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2576 .await
2577 .unwrap()
2578 .unwrap();
2579
2580 let mut fifo =
2581 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2582 let (mut reader, mut writer) = fifo.async_io();
2583
2584 writer
2585 .write_entries(&BlockFifoRequest {
2586 command: BlockFifoCommand {
2587 opcode: BlockOpcode::Read.into_primitive(),
2588 ..Default::default()
2589 },
2590 reqid: 1,
2591 dev_offset: 1, vmoid: vmo_id.id,
2593 length: 1,
2594 ..Default::default()
2595 })
2596 .await
2597 .unwrap();
2598
2599 writer
2600 .write_entries(&BlockFifoRequest {
2601 command: BlockFifoCommand {
2602 opcode: BlockOpcode::Read.into_primitive(),
2603 ..Default::default()
2604 },
2605 reqid: 2,
2606 dev_offset: 2,
2607 vmoid: vmo_id.id,
2608 length: 1,
2609 ..Default::default()
2610 })
2611 .await
2612 .unwrap();
2613
2614 poll_fn(|cx: &mut Context<'_>| {
2616 if waiting_readers.lock().len() == 2 {
2617 Poll::Ready(())
2618 } else {
2619 cx.waker().wake_by_ref();
2621 Poll::Pending
2622 }
2623 })
2624 .await;
2625
2626 let mut response = BlockFifoResponse::default();
2627 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2628
2629 let (id, tx) = waiting_readers.lock().pop().unwrap();
2630 tx.send(()).unwrap();
2631
2632 reader.read_entries(&mut response).await.unwrap();
2633 assert_eq!(response.status, zx::sys::ZX_OK);
2634 assert_eq!(response.reqid, id);
2635
2636 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2637
2638 let (id, tx) = waiting_readers.lock().pop().unwrap();
2639 tx.send(()).unwrap();
2640
2641 reader.read_entries(&mut response).await.unwrap();
2642 assert_eq!(response.status, zx::sys::ZX_OK);
2643 assert_eq!(response.reqid, id);
2644 }
2645 );
2646 }
2647
2648 #[fuchsia::test]
2649 async fn test_session_close_is_synchronous() {
2650 use futures::{FutureExt as _, StreamExt as _};
2651
2652 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2653
2654 let (start_tx, mut start_rx) = futures::channel::mpsc::channel(1);
2655 let (finish_tx, finish_rx) = futures::channel::oneshot::channel();
2656 let finish_rx = Arc::new(Mutex::new(Some(finish_rx)));
2657
2658 futures::join!(
2659 async move {
2660 let block_server = BlockServer::new(
2661 BLOCK_SIZE,
2662 Arc::new(MockInterface {
2663 read_hook: Some(Box::new(move |_, _, _, _| {
2664 let mut start_tx = start_tx.clone();
2665 let finish_rx = finish_rx.lock().take().unwrap();
2666 Box::pin(async move {
2667 start_tx.try_send(()).unwrap();
2668 let _ = finish_rx.await;
2669 Ok(())
2670 })
2671 })),
2672 ..MockInterface::default()
2673 }),
2674 );
2675 block_server.handle_requests(stream).await.unwrap();
2676 },
2677 async move {
2678 let (session_proxy, server) = fidl::endpoints::create_proxy();
2679 proxy.open_session(server).unwrap();
2680
2681 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2682 let vmo_id = session_proxy
2683 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2684 .await
2685 .unwrap()
2686 .unwrap();
2687
2688 let mut fifo = fasync::Fifo::<BlockFifoResponse, BlockFifoRequest>::from_fifo(
2689 session_proxy.get_fifo().await.unwrap().unwrap(),
2690 );
2691 let (_reader, mut writer) = fifo.async_io();
2692
2693 writer
2694 .write_entries(&BlockFifoRequest {
2695 command: BlockFifoCommand {
2696 opcode: BlockOpcode::Read.into_primitive(),
2697 ..Default::default()
2698 },
2699 reqid: 1,
2700 vmoid: vmo_id.id,
2701 length: 1,
2702 ..Default::default()
2703 })
2704 .await
2705 .unwrap();
2706
2707 start_rx.next().await.unwrap();
2709
2710 let mut close_fut = std::pin::pin!(session_proxy.close().fuse());
2712 let mut timer_fut = std::pin::pin!(
2713 fasync::Timer::new(std::time::Duration::from_millis(100)).fuse()
2714 );
2715 futures::select! {
2716 res = close_fut => panic!("close completed too early: {:?}", res),
2717 _ = timer_fut => {}
2718 }
2719
2720 finish_tx.send(()).unwrap();
2722
2723 close_fut.await.unwrap().unwrap();
2725
2726 std::mem::drop(proxy);
2727 }
2728 );
2729 }
2730
2731 #[fuchsia::test]
2732 async fn test_groups() {
2733 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2734
2735 futures::join!(
2736 async move {
2737 let block_server = BlockServer::new(
2738 BLOCK_SIZE,
2739 Arc::new(MockInterface {
2740 read_hook: Some(Box::new(move |_, _, _, _| Box::pin(async { Ok(()) }))),
2741 ..MockInterface::default()
2742 }),
2743 );
2744 block_server.handle_requests(stream).await.unwrap();
2745 },
2746 async move {
2747 let (session_proxy, server) = fidl::endpoints::create_proxy();
2748
2749 proxy.open_session(server).unwrap();
2750
2751 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2752 let vmo_id = session_proxy
2753 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2754 .await
2755 .unwrap()
2756 .unwrap();
2757
2758 let mut fifo =
2759 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2760 let (mut reader, mut writer) = fifo.async_io();
2761
2762 writer
2763 .write_entries(&BlockFifoRequest {
2764 command: BlockFifoCommand {
2765 opcode: BlockOpcode::Read.into_primitive(),
2766 flags: BlockIoFlag::GROUP_ITEM.bits(),
2767 ..Default::default()
2768 },
2769 group: 1,
2770 vmoid: vmo_id.id,
2771 length: 1,
2772 ..Default::default()
2773 })
2774 .await
2775 .unwrap();
2776
2777 writer
2778 .write_entries(&BlockFifoRequest {
2779 command: BlockFifoCommand {
2780 opcode: BlockOpcode::Read.into_primitive(),
2781 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2782 ..Default::default()
2783 },
2784 reqid: 2,
2785 group: 1,
2786 vmoid: vmo_id.id,
2787 length: 1,
2788 ..Default::default()
2789 })
2790 .await
2791 .unwrap();
2792
2793 let mut response = BlockFifoResponse::default();
2794 reader.read_entries(&mut response).await.unwrap();
2795 assert_eq!(response.status, zx::sys::ZX_OK);
2796 assert_eq!(response.reqid, 2);
2797 assert_eq!(response.group, 1);
2798 }
2799 );
2800 }
2801
2802 #[fuchsia::test]
2803 async fn test_group_error() {
2804 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2805
2806 let counter = Arc::new(AtomicU64::new(0));
2807 let counter_clone = counter.clone();
2808
2809 futures::join!(
2810 async move {
2811 let block_server = BlockServer::new(
2812 BLOCK_SIZE,
2813 Arc::new(MockInterface {
2814 read_hook: Some(Box::new(move |_, _, _, _| {
2815 counter_clone.fetch_add(1, Ordering::Relaxed);
2816 Box::pin(async { Err(zx::Status::BAD_STATE) })
2817 })),
2818 ..MockInterface::default()
2819 }),
2820 );
2821 block_server.handle_requests(stream).await.unwrap();
2822 },
2823 async move {
2824 let (session_proxy, server) = fidl::endpoints::create_proxy();
2825
2826 proxy.open_session(server).unwrap();
2827
2828 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2829 let vmo_id = session_proxy
2830 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2831 .await
2832 .unwrap()
2833 .unwrap();
2834
2835 let mut fifo =
2836 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2837 let (mut reader, mut writer) = fifo.async_io();
2838
2839 writer
2840 .write_entries(&BlockFifoRequest {
2841 command: BlockFifoCommand {
2842 opcode: BlockOpcode::Read.into_primitive(),
2843 flags: BlockIoFlag::GROUP_ITEM.bits(),
2844 ..Default::default()
2845 },
2846 group: 1,
2847 vmoid: vmo_id.id,
2848 length: 1,
2849 ..Default::default()
2850 })
2851 .await
2852 .unwrap();
2853
2854 poll_fn(|cx: &mut Context<'_>| {
2856 if counter.load(Ordering::Relaxed) == 1 {
2857 Poll::Ready(())
2858 } else {
2859 cx.waker().wake_by_ref();
2861 Poll::Pending
2862 }
2863 })
2864 .await;
2865
2866 let mut response = BlockFifoResponse::default();
2867 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2868
2869 writer
2870 .write_entries(&BlockFifoRequest {
2871 command: BlockFifoCommand {
2872 opcode: BlockOpcode::Read.into_primitive(),
2873 flags: BlockIoFlag::GROUP_ITEM.bits(),
2874 ..Default::default()
2875 },
2876 group: 1,
2877 vmoid: vmo_id.id,
2878 length: 1,
2879 ..Default::default()
2880 })
2881 .await
2882 .unwrap();
2883
2884 writer
2885 .write_entries(&BlockFifoRequest {
2886 command: BlockFifoCommand {
2887 opcode: BlockOpcode::Read.into_primitive(),
2888 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2889 ..Default::default()
2890 },
2891 reqid: 2,
2892 group: 1,
2893 vmoid: vmo_id.id,
2894 length: 1,
2895 ..Default::default()
2896 })
2897 .await
2898 .unwrap();
2899
2900 reader.read_entries(&mut response).await.unwrap();
2901 assert_eq!(response.status, zx::sys::ZX_ERR_BAD_STATE);
2902 assert_eq!(response.reqid, 2);
2903 assert_eq!(response.group, 1);
2904
2905 assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2906
2907 assert_eq!(counter.load(Ordering::Relaxed), 1);
2909 }
2910 );
2911 }
2912
2913 #[fuchsia::test]
2914 async fn test_group_with_two_lasts() {
2915 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2916
2917 let (tx, rx) = oneshot::channel();
2918
2919 futures::join!(
2920 async move {
2921 let rx = Mutex::new(Some(rx));
2922 let block_server = BlockServer::new(
2923 BLOCK_SIZE,
2924 Arc::new(MockInterface {
2925 read_hook: Some(Box::new(move |_, _, _, _| {
2926 let rx = rx.lock().take().unwrap();
2927 Box::pin(async {
2928 let _ = rx.await;
2929 Ok(())
2930 })
2931 })),
2932 ..MockInterface::default()
2933 }),
2934 );
2935 block_server.handle_requests(stream).await.unwrap();
2936 },
2937 async move {
2938 let (session_proxy, server) = fidl::endpoints::create_proxy();
2939
2940 proxy.open_session(server).unwrap();
2941
2942 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2943 let vmo_id = session_proxy
2944 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2945 .await
2946 .unwrap()
2947 .unwrap();
2948
2949 let mut fifo =
2950 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2951 let (mut reader, mut writer) = fifo.async_io();
2952
2953 writer
2954 .write_entries(&BlockFifoRequest {
2955 command: BlockFifoCommand {
2956 opcode: BlockOpcode::Read.into_primitive(),
2957 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2958 ..Default::default()
2959 },
2960 reqid: 1,
2961 group: 1,
2962 vmoid: vmo_id.id,
2963 length: 1,
2964 ..Default::default()
2965 })
2966 .await
2967 .unwrap();
2968
2969 writer
2970 .write_entries(&BlockFifoRequest {
2971 command: BlockFifoCommand {
2972 opcode: BlockOpcode::Read.into_primitive(),
2973 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2974 ..Default::default()
2975 },
2976 reqid: 2,
2977 group: 1,
2978 vmoid: vmo_id.id,
2979 length: 1,
2980 ..Default::default()
2981 })
2982 .await
2983 .unwrap();
2984
2985 writer
2987 .write_entries(&BlockFifoRequest {
2988 command: BlockFifoCommand {
2989 opcode: BlockOpcode::CloseVmo.into_primitive(),
2990 ..Default::default()
2991 },
2992 reqid: 3,
2993 vmoid: vmo_id.id,
2994 ..Default::default()
2995 })
2996 .await
2997 .unwrap();
2998
2999 let mut response = BlockFifoResponse::default();
3001 reader.read_entries(&mut response).await.unwrap();
3002 assert_eq!(response.status, zx::sys::ZX_OK);
3003 assert_eq!(response.reqid, 3);
3004
3005 tx.send(()).unwrap();
3007
3008 let mut response = BlockFifoResponse::default();
3011 reader.read_entries(&mut response).await.unwrap();
3012 assert_eq!(response.status, zx::sys::ZX_ERR_INVALID_ARGS);
3013 assert_eq!(response.reqid, 1);
3014 assert_eq!(response.group, 1);
3015 }
3016 );
3017 }
3018
3019 #[fuchsia::test(allow_stalls = false)]
3020 async fn test_requests_dont_block_sessions() {
3021 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3022
3023 let (tx, rx) = oneshot::channel();
3024
3025 fasync::Task::local(async move {
3026 let rx = Mutex::new(Some(rx));
3027 let block_server = BlockServer::new(
3028 BLOCK_SIZE,
3029 Arc::new(MockInterface {
3030 read_hook: Some(Box::new(move |_, _, _, _| {
3031 let rx = rx.lock().take().unwrap();
3032 Box::pin(async {
3033 let _ = rx.await;
3034 Ok(())
3035 })
3036 })),
3037 ..MockInterface::default()
3038 }),
3039 );
3040 block_server.handle_requests(stream).await.unwrap();
3041 })
3042 .detach();
3043
3044 let mut fut = pin!(async {
3045 let (session_proxy, server) = fidl::endpoints::create_proxy();
3046
3047 proxy.open_session(server).unwrap();
3048
3049 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3050 let vmo_id = session_proxy
3051 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3052 .await
3053 .unwrap()
3054 .unwrap();
3055
3056 let mut fifo =
3057 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3058 let (mut reader, mut writer) = fifo.async_io();
3059
3060 writer
3061 .write_entries(&BlockFifoRequest {
3062 command: BlockFifoCommand {
3063 opcode: BlockOpcode::Read.into_primitive(),
3064 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3065 ..Default::default()
3066 },
3067 reqid: 1,
3068 group: 1,
3069 vmoid: vmo_id.id,
3070 length: 1,
3071 ..Default::default()
3072 })
3073 .await
3074 .unwrap();
3075
3076 let mut response = BlockFifoResponse::default();
3077 reader.read_entries(&mut response).await.unwrap();
3078 assert_eq!(response.status, zx::sys::ZX_OK);
3079 });
3080
3081 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_pending());
3083
3084 let mut fut2 = pin!(proxy.get_volume_info());
3085
3086 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut2).await.is_pending());
3088
3089 let _ = tx.send(());
3092
3093 assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_ready());
3094 }
3095
3096 #[fuchsia::test]
3097 async fn test_request_flow_control() {
3098 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3099
3100 const MAX_REQUESTS: u64 = FIFO_MAX_REQUESTS as u64;
3103 let event = Arc::new((event_listener::Event::new(), AtomicBool::new(false)));
3104 let event_clone = event.clone();
3105 futures::join!(
3106 async move {
3107 let block_server = BlockServer::new(
3108 BLOCK_SIZE,
3109 Arc::new(MockInterface {
3110 info: Some(DeviceInfo::Partition(PartitionInfo {
3111 block_count: 1000,
3112 ..Default::default()
3113 })),
3114 read_hook: Some(Box::new(move |_, _, _, _| {
3115 let event_clone = event_clone.clone();
3116 Box::pin(async move {
3117 if !event_clone.1.load(Ordering::SeqCst) {
3118 event_clone.0.listen().await;
3119 }
3120 Ok(())
3121 })
3122 })),
3123 ..MockInterface::default()
3124 }),
3125 );
3126 block_server.handle_requests(stream).await.unwrap();
3127 },
3128 async move {
3129 let (session_proxy, server) = fidl::endpoints::create_proxy();
3130
3131 proxy.open_session(server).unwrap();
3132
3133 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3134 let vmo_id = session_proxy
3135 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3136 .await
3137 .unwrap()
3138 .unwrap();
3139
3140 let mut fifo =
3141 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3142 let (mut reader, mut writer) = fifo.async_io();
3143
3144 for i in 0..MAX_REQUESTS {
3145 writer
3146 .write_entries(&BlockFifoRequest {
3147 command: BlockFifoCommand {
3148 opcode: BlockOpcode::Read.into_primitive(),
3149 ..Default::default()
3150 },
3151 reqid: (i + 1) as u32,
3152 dev_offset: i,
3153 vmoid: vmo_id.id,
3154 length: 1,
3155 ..Default::default()
3156 })
3157 .await
3158 .unwrap();
3159 }
3160 assert!(
3161 futures::poll!(pin!(writer.write_entries(&BlockFifoRequest {
3162 command: BlockFifoCommand {
3163 opcode: BlockOpcode::Read.into_primitive(),
3164 ..Default::default()
3165 },
3166 reqid: u32::MAX,
3167 dev_offset: MAX_REQUESTS,
3168 vmoid: vmo_id.id,
3169 length: 1,
3170 ..Default::default()
3171 })))
3172 .is_pending()
3173 );
3174 event.1.store(true, Ordering::SeqCst);
3176 event.0.notify(usize::MAX);
3177 let mut finished_reqids = vec![];
3179 for i in MAX_REQUESTS..2 * MAX_REQUESTS {
3180 let mut response = BlockFifoResponse::default();
3181 reader.read_entries(&mut response).await.unwrap();
3182 assert_eq!(response.status, zx::sys::ZX_OK);
3183 finished_reqids.push(response.reqid);
3184 writer
3185 .write_entries(&BlockFifoRequest {
3186 command: BlockFifoCommand {
3187 opcode: BlockOpcode::Read.into_primitive(),
3188 ..Default::default()
3189 },
3190 reqid: (i + 1) as u32,
3191 dev_offset: i,
3192 vmoid: vmo_id.id,
3193 length: 1,
3194 ..Default::default()
3195 })
3196 .await
3197 .unwrap();
3198 }
3199 let mut response = BlockFifoResponse::default();
3200 for _ in 0..MAX_REQUESTS {
3201 reader.read_entries(&mut response).await.unwrap();
3202 assert_eq!(response.status, zx::sys::ZX_OK);
3203 finished_reqids.push(response.reqid);
3204 }
3205 finished_reqids.sort();
3208 assert_eq!(finished_reqids.len(), 2 * MAX_REQUESTS as usize);
3209 let mut i = 1;
3210 for reqid in finished_reqids {
3211 assert_eq!(reqid, i);
3212 i += 1;
3213 }
3214 }
3215 );
3216 }
3217
3218 #[fuchsia::test]
3219 async fn test_passthrough_io_with_fixed_map() {
3220 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3221
3222 let expected_op = Arc::new(Mutex::new(None));
3223 let expected_op_clone = expected_op.clone();
3224 futures::join!(
3225 async {
3226 let block_server = BlockServer::new(
3227 BLOCK_SIZE,
3228 Arc::new(IoMockInterface {
3229 return_errors: false,
3230 do_checks: true,
3231 expected_op: expected_op_clone,
3232 }),
3233 );
3234 block_server.handle_requests(stream).await.unwrap();
3235 },
3236 async move {
3237 let (session_proxy, server) = fidl::endpoints::create_proxy();
3238
3239 let mapping = fblock::BlockOffsetMapping { target_block_offset: 10, length: 20 };
3240 proxy.open_session_with_options(server, &[mapping]).unwrap();
3241
3242 let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
3243 let vmo_id = session_proxy
3244 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3245 .await
3246 .unwrap()
3247 .unwrap();
3248
3249 let mut fifo =
3250 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3251 let (mut reader, mut writer) = fifo.async_io();
3252
3253 *expected_op.lock() = Some(ExpectedOp::Read(11, 2, 3));
3255 writer
3256 .write_entries(&BlockFifoRequest {
3257 command: BlockFifoCommand {
3258 opcode: BlockOpcode::Read.into_primitive(),
3259 ..Default::default()
3260 },
3261 vmoid: vmo_id.id,
3262 dev_offset: 1,
3263 length: 2,
3264 vmo_offset: 3,
3265 ..Default::default()
3266 })
3267 .await
3268 .unwrap();
3269
3270 let mut response = BlockFifoResponse::default();
3271 reader.read_entries(&mut response).await.unwrap();
3272 assert_eq!(response.status, zx::sys::ZX_OK);
3273
3274 *expected_op.lock() = Some(ExpectedOp::Write(14, 5, 6));
3276 writer
3277 .write_entries(&BlockFifoRequest {
3278 command: BlockFifoCommand {
3279 opcode: BlockOpcode::Write.into_primitive(),
3280 ..Default::default()
3281 },
3282 vmoid: vmo_id.id,
3283 dev_offset: 4,
3284 length: 5,
3285 vmo_offset: 6,
3286 ..Default::default()
3287 })
3288 .await
3289 .unwrap();
3290
3291 reader.read_entries(&mut response).await.unwrap();
3292 assert_eq!(response.status, zx::sys::ZX_OK);
3293
3294 *expected_op.lock() = Some(ExpectedOp::Flush);
3296 writer
3297 .write_entries(&BlockFifoRequest {
3298 command: BlockFifoCommand {
3299 opcode: BlockOpcode::Flush.into_primitive(),
3300 ..Default::default()
3301 },
3302 ..Default::default()
3303 })
3304 .await
3305 .unwrap();
3306
3307 reader.read_entries(&mut response).await.unwrap();
3308 assert_eq!(response.status, zx::sys::ZX_OK);
3309
3310 *expected_op.lock() = Some(ExpectedOp::Trim(17, 3));
3312 writer
3313 .write_entries(&BlockFifoRequest {
3314 command: BlockFifoCommand {
3315 opcode: BlockOpcode::Trim.into_primitive(),
3316 ..Default::default()
3317 },
3318 dev_offset: 7,
3319 length: 3,
3320 ..Default::default()
3321 })
3322 .await
3323 .unwrap();
3324
3325 reader.read_entries(&mut response).await.unwrap();
3326 assert_eq!(response.status, zx::sys::ZX_OK);
3327
3328 *expected_op.lock() = None;
3330 writer
3331 .write_entries(&BlockFifoRequest {
3332 command: BlockFifoCommand {
3333 opcode: BlockOpcode::Read.into_primitive(),
3334 ..Default::default()
3335 },
3336 vmoid: vmo_id.id,
3337 dev_offset: 19,
3338 length: 2,
3339 vmo_offset: 3,
3340 ..Default::default()
3341 })
3342 .await
3343 .unwrap();
3344
3345 reader.read_entries(&mut response).await.unwrap();
3346 assert_eq!(response.status, zx::sys::ZX_ERR_OUT_OF_RANGE);
3347
3348 std::mem::drop(proxy);
3349 }
3350 );
3351 }
3352
3353 #[fuchsia::test]
3354 fn operation_map() {
3355 const BLOCK_SIZE: u32 = 512;
3356
3357 #[track_caller]
3358 fn expect_map_result(
3359 mut operation: Operation,
3360 mapping: Option<fblock::BlockOffsetMapping>,
3361 max_blocks: Option<NonZero<u32>>,
3362 expected_operations: Vec<Operation>,
3363 ) {
3364 let offset_map = mapping
3365 .map(|m| {
3366 let map: BlockOffsetMapping = (&m).try_into().unwrap();
3367 OffsetMap::new(vec![map]).unwrap()
3368 })
3369 .unwrap_or_else(OffsetMap::empty);
3370 let mut ops = vec![];
3371 while let Some(remainder) = operation.map(&offset_map, max_blocks, BLOCK_SIZE).unwrap()
3372 {
3373 ops.push(operation);
3374 operation = remainder;
3375 }
3376 ops.push(operation);
3377 assert_eq!(ops, expected_operations);
3378 }
3379
3380 expect_map_result(
3382 Operation::Read {
3383 device_block_offset: 10,
3384 block_count: 200,
3385 _unused: 0,
3386 vmo_offset: 0,
3387 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3388 },
3389 None,
3390 None,
3391 vec![Operation::Read {
3392 device_block_offset: 10,
3393 block_count: 200,
3394 _unused: 0,
3395 vmo_offset: 0,
3396 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3397 }],
3398 );
3399
3400 expect_map_result(
3402 Operation::Read {
3403 device_block_offset: 10,
3404 block_count: 200,
3405 _unused: 0,
3406 vmo_offset: 0,
3407 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3408 },
3409 None,
3410 NonZero::new(120),
3411 vec![
3412 Operation::Read {
3413 device_block_offset: 10,
3414 block_count: 120,
3415 _unused: 0,
3416 vmo_offset: 0,
3417 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3418 },
3419 Operation::Read {
3420 device_block_offset: 130,
3421 block_count: 80,
3422 _unused: 0,
3423 vmo_offset: 120 * BLOCK_SIZE as u64,
3424 options: ReadOptions {
3425 inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3427 },
3428 },
3429 ],
3430 );
3431 expect_map_result(
3432 Operation::Trim { device_block_offset: 10, block_count: 200 },
3433 None,
3434 NonZero::new(120),
3435 vec![Operation::Trim { device_block_offset: 10, block_count: 200 }],
3436 );
3437
3438 expect_map_result(
3440 Operation::Read {
3441 device_block_offset: 0,
3442 block_count: 200,
3443 _unused: 0,
3444 vmo_offset: 0,
3445 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3446 },
3447 Some(fblock::BlockOffsetMapping { target_block_offset: 100, length: 200 }),
3448 NonZero::new(120),
3449 vec![
3450 Operation::Read {
3451 device_block_offset: 100,
3452 block_count: 120,
3453 _unused: 0,
3454 vmo_offset: 0,
3455 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3456 },
3457 Operation::Read {
3458 device_block_offset: 220,
3459 block_count: 80,
3460 _unused: 0,
3461 vmo_offset: 120 * BLOCK_SIZE as u64,
3462 options: ReadOptions {
3463 inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3464 },
3465 },
3466 ],
3467 );
3468 expect_map_result(
3469 Operation::Trim { device_block_offset: 0, block_count: 200 },
3470 Some(fblock::BlockOffsetMapping { target_block_offset: 100, length: 200 }),
3471 NonZero::new(120),
3472 vec![Operation::Trim { device_block_offset: 100, block_count: 200 }],
3473 );
3474
3475 let multi_extent_map = OffsetMap::new(vec![
3477 BlockOffsetMapping { target_block_offset: 100, length: 10 },
3478 BlockOffsetMapping { target_block_offset: 200, length: 20 },
3479 ])
3480 .unwrap();
3481
3482 fn expect_multi_map_result(
3483 mut operation: Operation,
3484 offset_map: &OffsetMap,
3485 max_blocks: Option<NonZero<u32>>,
3486 expected_operations: Vec<Operation>,
3487 ) {
3488 let mut ops = vec![];
3489 while let Some(remainder) = operation.map(offset_map, max_blocks, BLOCK_SIZE).unwrap() {
3490 ops.push(operation);
3491 operation = remainder;
3492 }
3493 ops.push(operation);
3494 assert_eq!(ops, expected_operations);
3495 }
3496
3497 expect_multi_map_result(
3500 Operation::Read {
3501 device_block_offset: 5,
3502 block_count: 15,
3503 _unused: 0,
3504 vmo_offset: 0,
3505 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3506 },
3507 &multi_extent_map,
3508 None,
3509 vec![
3510 Operation::Read {
3511 device_block_offset: 105,
3512 block_count: 5,
3513 _unused: 0,
3514 vmo_offset: 0,
3515 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3516 },
3517 Operation::Read {
3518 device_block_offset: 200,
3519 block_count: 10,
3520 _unused: 0,
3521 vmo_offset: 5 * BLOCK_SIZE as u64,
3522 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1005) },
3523 },
3524 ],
3525 );
3526
3527 expect_multi_map_result(
3529 Operation::Read {
3530 device_block_offset: 5,
3531 block_count: 15,
3532 _unused: 0,
3533 vmo_offset: 0,
3534 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3535 },
3536 &multi_extent_map,
3537 NonZero::new(7),
3538 vec![
3539 Operation::Read {
3540 device_block_offset: 105,
3541 block_count: 5,
3542 _unused: 0,
3543 vmo_offset: 0,
3544 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3545 },
3546 Operation::Read {
3547 device_block_offset: 200,
3548 block_count: 7,
3549 _unused: 0,
3550 vmo_offset: 5 * BLOCK_SIZE as u64,
3551 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1005) },
3552 },
3553 Operation::Read {
3554 device_block_offset: 207,
3555 block_count: 3,
3556 _unused: 0,
3557 vmo_offset: 12 * BLOCK_SIZE as u64,
3558 options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1012) },
3559 },
3560 ],
3561 );
3562
3563 expect_multi_map_result(
3565 Operation::Write {
3566 device_block_offset: 5,
3567 block_count: 15,
3568 _unused: 0,
3569 vmo_offset: 0,
3570 options: WriteOptions {
3571 inline_crypto: InlineCryptoOptions::enabled(1, 2000),
3572 flags: WriteFlags::empty(),
3573 },
3574 },
3575 &multi_extent_map,
3576 None,
3577 vec![
3578 Operation::Write {
3579 device_block_offset: 105,
3580 block_count: 5,
3581 _unused: 0,
3582 vmo_offset: 0,
3583 options: WriteOptions {
3584 inline_crypto: InlineCryptoOptions::enabled(1, 2000),
3585 flags: WriteFlags::empty(),
3586 },
3587 },
3588 Operation::Write {
3589 device_block_offset: 200,
3590 block_count: 10,
3591 _unused: 0,
3592 vmo_offset: 5 * BLOCK_SIZE as u64,
3593 options: WriteOptions {
3594 inline_crypto: InlineCryptoOptions::enabled(1, 2005),
3595 flags: WriteFlags::empty(),
3596 },
3597 },
3598 ],
3599 );
3600
3601 expect_multi_map_result(
3603 Operation::Trim { device_block_offset: 5, block_count: 15 },
3604 &multi_extent_map,
3605 None,
3606 vec![
3607 Operation::Trim { device_block_offset: 105, block_count: 5 },
3608 Operation::Trim { device_block_offset: 200, block_count: 10 },
3609 ],
3610 );
3611
3612 let large_extent_map = OffsetMap::new(vec![BlockOffsetMapping {
3614 target_block_offset: 100,
3615 length: (u32::MAX as u64) + 10,
3616 }])
3617 .unwrap();
3618
3619 assert_eq!(large_extent_map.map(0), Some((100, u32::MAX)));
3620 }
3621
3622 #[fuchsia::test]
3624 async fn test_pre_barrier_flush_failure() {
3625 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3626
3627 struct NoBarrierInterface;
3628 impl super::async_interface::Interface for NoBarrierInterface {
3629 fn get_info(&self) -> Cow<'_, DeviceInfo> {
3630 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3631 device_flags: fblock::DeviceFlag::empty(), max_transfer_blocks: NonZero::new(100),
3633 start_block_offset: Some(0),
3634 block_count: 100,
3635 type_guid: [0; 16],
3636 instance_guid: [0; 16],
3637 name: "test".to_string(),
3638 flags: Some(0),
3639 }))
3640 }
3641 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3642 Ok(())
3643 }
3644 async fn read(
3645 &self,
3646 _: u64,
3647 _: u32,
3648 _: &Arc<zx::Vmo>,
3649 _: u64,
3650 _: ReadOptions,
3651 _: TraceFlowId,
3652 ) -> Result<(), zx::Status> {
3653 unreachable!()
3654 }
3655 async fn write(
3656 &self,
3657 _: u64,
3658 _: u32,
3659 _: &Arc<zx::Vmo>,
3660 _: u64,
3661 _: WriteOptions,
3662 _: TraceFlowId,
3663 ) -> Result<(), zx::Status> {
3664 panic!("Write should not be called");
3665 }
3666 async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3667 Err(zx::Status::IO)
3668 }
3669 async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3670 unreachable!()
3671 }
3672 }
3673
3674 futures::join!(
3675 async move {
3676 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3677 block_server.handle_requests(stream).await.unwrap();
3678 },
3679 async move {
3680 let (session_proxy, server) = fidl::endpoints::create_proxy();
3681 proxy.open_session(server).unwrap();
3682 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3683 let vmo_id = session_proxy
3684 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3685 .await
3686 .unwrap()
3687 .unwrap();
3688
3689 let mut fifo =
3690 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3691 let (mut reader, mut writer) = fifo.async_io();
3692
3693 writer
3694 .write_entries(&BlockFifoRequest {
3695 command: BlockFifoCommand {
3696 opcode: BlockOpcode::Write.into_primitive(),
3697 flags: BlockIoFlag::PRE_BARRIER.bits(),
3698 ..Default::default()
3699 },
3700 vmoid: vmo_id.id,
3701 length: 1,
3702 ..Default::default()
3703 })
3704 .await
3705 .unwrap();
3706
3707 let mut response = BlockFifoResponse::default();
3708 reader.read_entries(&mut response).await.unwrap();
3709 assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3710 }
3711 );
3712 }
3713
3714 #[fuchsia::test]
3717 async fn test_post_barrier_write_failure() {
3718 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3719
3720 struct NoBarrierInterface;
3721 impl super::async_interface::Interface for NoBarrierInterface {
3722 fn get_info(&self) -> Cow<'_, DeviceInfo> {
3723 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3724 device_flags: fblock::DeviceFlag::empty(), max_transfer_blocks: NonZero::new(100),
3726 start_block_offset: Some(0),
3727 block_count: 100,
3728 type_guid: [0; 16],
3729 instance_guid: [0; 16],
3730 name: "test".to_string(),
3731 flags: Some(0),
3732 }))
3733 }
3734 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3735 Ok(())
3736 }
3737 async fn read(
3738 &self,
3739 _: u64,
3740 _: u32,
3741 _: &Arc<zx::Vmo>,
3742 _: u64,
3743 _: ReadOptions,
3744 _: TraceFlowId,
3745 ) -> Result<(), zx::Status> {
3746 unreachable!()
3747 }
3748 async fn write(
3749 &self,
3750 _: u64,
3751 _: u32,
3752 _: &Arc<zx::Vmo>,
3753 _: u64,
3754 _: WriteOptions,
3755 _: TraceFlowId,
3756 ) -> Result<(), zx::Status> {
3757 Err(zx::Status::IO)
3758 }
3759 async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3760 panic!("Flush should not be called")
3761 }
3762 async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3763 unreachable!()
3764 }
3765 }
3766
3767 futures::join!(
3768 async move {
3769 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3770 block_server.handle_requests(stream).await.unwrap();
3771 },
3772 async move {
3773 let (session_proxy, server) = fidl::endpoints::create_proxy();
3774 proxy.open_session(server).unwrap();
3775 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3776 let vmo_id = session_proxy
3777 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3778 .await
3779 .unwrap()
3780 .unwrap();
3781
3782 let mut fifo =
3783 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3784 let (mut reader, mut writer) = fifo.async_io();
3785
3786 writer
3787 .write_entries(&BlockFifoRequest {
3788 command: BlockFifoCommand {
3789 opcode: BlockOpcode::Write.into_primitive(),
3790 flags: BlockIoFlag::FORCE_ACCESS.bits(),
3791 ..Default::default()
3792 },
3793 vmoid: vmo_id.id,
3794 length: 1,
3795 ..Default::default()
3796 })
3797 .await
3798 .unwrap();
3799
3800 let mut response = BlockFifoResponse::default();
3801 reader.read_entries(&mut response).await.unwrap();
3802 assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3803 }
3804 );
3805 }
3806
3807 #[fuchsia::test]
3812 async fn test_group_ids_isolated_per_session() {
3813 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3814
3815 futures::join!(
3816 async {
3817 let block_server = BlockServer::new(
3818 BLOCK_SIZE,
3819 Arc::new(MockInterface::default()),
3821 );
3822 block_server.handle_requests(stream).await.unwrap();
3823 },
3824 async move {
3825 async fn settle() {
3826 for _ in 0..32 {
3828 fasync::yield_now().await;
3829 }
3830 }
3831
3832 let (session_a, server_a) = fidl::endpoints::create_proxy();
3834 proxy.open_session(server_a).unwrap();
3835 let mut fifo_a =
3836 fasync::Fifo::from_fifo(session_a.get_fifo().await.unwrap().unwrap());
3837
3838 let (session_b, server_b) = fidl::endpoints::create_proxy();
3840 proxy.open_session(server_b).unwrap();
3841 let mut fifo_b =
3842 fasync::Fifo::from_fifo(session_b.get_fifo().await.unwrap().unwrap());
3843
3844 {
3848 let (mut reader_a, mut writer_a) = fifo_a.async_io();
3849 writer_a
3850 .write_entries(&BlockFifoRequest {
3851 command: BlockFifoCommand {
3852 opcode: BlockOpcode::Flush.into_primitive(),
3853 flags: BlockIoFlag::GROUP_ITEM.bits(),
3854 ..Default::default()
3855 },
3856 group: 1,
3857 reqid: 0xAAAA,
3858 ..Default::default()
3859 })
3860 .await
3861 .unwrap();
3862 settle().await;
3863 writer_a
3864 .write_entries(&BlockFifoRequest {
3865 command: BlockFifoCommand {
3866 opcode: BlockOpcode::Flush.into_primitive(),
3867 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3868 ..Default::default()
3869 },
3870 group: 1,
3871 reqid: 0xAAAA,
3872 ..Default::default()
3873 })
3874 .await
3875 .unwrap();
3876 let mut response = BlockFifoResponse::default();
3877 reader_a.read_entries(&mut response).await.unwrap();
3878 assert_eq!(response.reqid, 0xAAAA);
3879 assert_eq!(
3880 zx::Status::from_raw(response.status),
3881 zx::Status::OK,
3882 "control: A's valid Flush group must succeed"
3883 );
3884 }
3885
3886 {
3893 let (_reader_a, mut writer_a) = fifo_a.async_io();
3894 writer_a
3895 .write_entries(&BlockFifoRequest {
3896 command: BlockFifoCommand {
3897 opcode: BlockOpcode::Flush.into_primitive(),
3898 flags: BlockIoFlag::GROUP_ITEM.bits(),
3899 ..Default::default()
3900 },
3901 group: 7,
3902 reqid: 100,
3903 ..Default::default()
3904 })
3905 .await
3906 .unwrap();
3907 }
3908 settle().await;
3909
3910 {
3912 let (_reader_b, mut writer_b) = fifo_b.async_io();
3913 writer_b
3914 .write_entries(&BlockFifoRequest {
3915 command: BlockFifoCommand {
3916 opcode: BlockOpcode::Flush.into_primitive(),
3917 flags: BlockIoFlag::GROUP_ITEM.bits(),
3918 ..Default::default()
3919 },
3920 group: 7,
3921 reqid: 200,
3922 ..Default::default()
3923 })
3924 .await
3925 .unwrap();
3926 }
3927 settle().await;
3928
3929 {
3931 let (_reader_a, mut writer_a) = fifo_a.async_io();
3932 writer_a
3933 .write_entries(&BlockFifoRequest {
3934 command: BlockFifoCommand {
3935 opcode: BlockOpcode::Flush.into_primitive(),
3936 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3937 ..Default::default()
3938 },
3939 group: 7,
3940 reqid: 100,
3941 ..Default::default()
3942 })
3943 .await
3944 .unwrap();
3945 }
3946 settle().await;
3947
3948 {
3950 let (_reader_b, mut writer_b) = fifo_b.async_io();
3951 writer_b
3952 .write_entries(&BlockFifoRequest {
3953 command: BlockFifoCommand {
3954 opcode: BlockOpcode::Flush.into_primitive(),
3955 flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3956 ..Default::default()
3957 },
3958 group: 7,
3959 reqid: 200,
3960 ..Default::default()
3961 })
3962 .await
3963 .unwrap();
3964 }
3965 settle().await;
3966
3967 {
3969 let (mut reader_a, _writer_a) = fifo_a.async_io();
3970 let mut response_a = BlockFifoResponse::default();
3971 reader_a.read_entries(&mut response_a).await.unwrap();
3972 assert_eq!(response_a.reqid, 100);
3973 assert_eq!(response_a.group, 7);
3974 assert_eq!(zx::Status::from_raw(response_a.status), zx::Status::OK);
3975 }
3976
3977 {
3979 let (mut reader_b, _writer_b) = fifo_b.async_io();
3980 let mut response_b = BlockFifoResponse::default();
3981 reader_b.read_entries(&mut response_b).await.unwrap();
3982 assert_eq!(response_b.reqid, 200);
3983 assert_eq!(response_b.group, 7);
3984 assert_eq!(zx::Status::from_raw(response_b.status), zx::Status::OK);
3985 }
3986
3987 std::mem::drop(session_a);
3988 std::mem::drop(session_b);
3989 std::mem::drop(proxy);
3990 }
3991 );
3992 }
3993
3994 #[fuchsia::test]
3995 async fn test_unmapped_request_out_of_range() {
3996 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3997 let _server = fasync::Task::spawn(async move {
3998 let block_server = BlockServer::new(4096, Arc::new(MockInterface::default()));
3999 let _ = block_server.handle_requests(stream).await;
4000 });
4001
4002 let (session_proxy, server) = fidl::endpoints::create_proxy();
4003 proxy.open_session(server).unwrap();
4004
4005 let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
4006 let vmo_id = session_proxy
4007 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
4008 .await
4009 .unwrap()
4010 .unwrap();
4011
4012 let mut fifo = fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
4013 let (mut reader, mut writer) = fifo.async_io();
4014
4015 writer
4018 .write_entries(&BlockFifoRequest {
4019 command: BlockFifoCommand {
4020 opcode: BlockOpcode::Read.into_primitive(),
4021 ..Default::default()
4022 },
4023 reqid: 1,
4024 vmoid: vmo_id.id,
4025 length: 1,
4026 dev_offset: 100,
4027 ..Default::default()
4028 })
4029 .await
4030 .unwrap();
4031
4032 let mut response = BlockFifoResponse::default();
4033 reader.read_entries(&mut response).await.unwrap();
4034 assert_eq!(zx::Status::from_raw(response.status), zx::Status::OUT_OF_RANGE);
4035 }
4036
4037 #[test]
4038 fn test_offset_map_coalescing() {
4039 use crate::{BlockOffsetMapping, OffsetMap};
4040
4041 let mappings = vec![
4044 BlockOffsetMapping { target_block_offset: 100, length: 50 },
4045 BlockOffsetMapping { target_block_offset: 150, length: 30 },
4046 BlockOffsetMapping { target_block_offset: 250, length: 20 },
4047 ];
4048 let map = OffsetMap::new(crate::coalesce_mappings(mappings)).unwrap();
4049
4050 assert_eq!(map.mappings().len(), 2);
4051 assert_eq!(map.mappings()[0].target_block_offset, 100);
4052 assert_eq!(map.mappings()[0].length, 80);
4053 assert_eq!(map.mappings()[1].target_block_offset, 250);
4054 assert_eq!(map.mappings()[1].length, 20);
4055
4056 assert_eq!(map.map(10), Some((110, 70)));
4059 assert_eq!(map.map(55), Some((155, 25)));
4062 assert_eq!(map.map(85), Some((255, 15)));
4065 }
4066 #[fuchsia::test]
4067 async fn test_open_session_with_options_errors() {
4068 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
4069
4070 futures::join!(
4071 async {
4072 let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
4073 let _ = block_server.handle_requests(stream).await;
4074 },
4075 async move {
4076 {
4079 let (session_proxy, server) = fidl::endpoints::create_proxy();
4080 proxy
4081 .open_session_with_options(
4082 server,
4083 &[fblock::BlockOffsetMapping { target_block_offset: 0, length: 0 }],
4084 )
4085 .unwrap();
4086 let res = session_proxy.get_fifo().await;
4087 assert_matches!(
4088 res,
4089 Err(fidl::Error::ClientChannelClosed { epitaph, .. })
4090 if epitaph == zx::Status::INVALID_ARGS
4091 );
4092 }
4093
4094 {
4097 let (session_proxy, server) = fidl::endpoints::create_proxy();
4098 let mapping = fblock::BlockOffsetMapping {
4099 target_block_offset: u64::MAX - 10, length: 10,
4101 };
4102 proxy.open_session_with_options(server, &[mapping]).unwrap();
4103 let res = session_proxy.get_fifo().await;
4104 assert_matches!(
4105 res,
4106 Err(fidl::Error::ClientChannelClosed { epitaph, .. })
4107 if epitaph == zx::Status::OUT_OF_RANGE
4108 );
4109 }
4110 }
4111 );
4112 }
4113
4114 #[fuchsia::test]
4115 async fn test_split_request_failure_aborts_subsequent_chunks() {
4116 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
4117
4118 let read_calls = Arc::new(AtomicU64::new(0));
4119 let read_calls_clone = read_calls.clone();
4120
4121 struct MockSplitInterface {
4122 read_calls: Arc<AtomicU64>,
4123 }
4124
4125 impl super::async_interface::Interface for MockSplitInterface {
4126 fn get_info(&self) -> Cow<'_, DeviceInfo> {
4127 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
4128 device_flags: fblock::DeviceFlag::READONLY,
4129 max_transfer_blocks: NonZero::new(5),
4130 start_block_offset: Some(0),
4131 block_count: 100,
4132 type_guid: [1; 16],
4133 instance_guid: [2; 16],
4134 name: "foo".to_string(),
4135 flags: Some(0),
4136 }))
4137 }
4138
4139 async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
4140 Ok(())
4141 }
4142
4143 async fn read(
4144 &self,
4145 _device_block_offset: u64,
4146 _block_count: u32,
4147 _vmo: &Arc<zx::Vmo>,
4148 _vmo_offset: u64,
4149 _opts: ReadOptions,
4150 _trace_flow_id: TraceFlowId,
4151 ) -> Result<(), zx::Status> {
4152 let call_num = self.read_calls.fetch_add(1, Ordering::Relaxed);
4153 if call_num == 0 { Err(zx::Status::IO) } else { Ok(()) }
4154 }
4155
4156 async fn write(
4157 &self,
4158 _device_block_offset: u64,
4159 _block_count: u32,
4160 _vmo: &Arc<zx::Vmo>,
4161 _vmo_offset: u64,
4162 _opts: WriteOptions,
4163 _trace_flow_id: TraceFlowId,
4164 ) -> Result<(), zx::Status> {
4165 unreachable!()
4166 }
4167
4168 async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
4169 Ok(())
4170 }
4171
4172 async fn trim(
4173 &self,
4174 _device_block_offset: u64,
4175 _block_count: u32,
4176 _trace_flow_id: TraceFlowId,
4177 ) -> Result<(), zx::Status> {
4178 unreachable!()
4179 }
4180 }
4181
4182 futures::join!(
4183 async move {
4184 let block_server = BlockServer::new(
4185 BLOCK_SIZE,
4186 Arc::new(MockSplitInterface { read_calls: read_calls_clone }),
4187 );
4188 let _ = block_server.handle_requests(stream).await;
4189 },
4190 async move {
4191 let (session_proxy, server) = fidl::endpoints::create_proxy();
4192 proxy.open_session(server).unwrap();
4193
4194 let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
4195 let vmo_id = session_proxy
4196 .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
4197 .await
4198 .unwrap()
4199 .unwrap();
4200
4201 let mut fifo =
4202 fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
4203 let (mut reader, mut writer) = fifo.async_io();
4204
4205 writer
4208 .write_entries(&BlockFifoRequest {
4209 command: BlockFifoCommand {
4210 opcode: BlockOpcode::Read.into_primitive(),
4211 ..Default::default()
4212 },
4213 vmoid: vmo_id.id,
4214 length: 10,
4215 dev_offset: 0,
4216 reqid: 1,
4217 ..Default::default()
4218 })
4219 .await
4220 .unwrap();
4221
4222 let mut response = BlockFifoResponse::default();
4223 reader.read_entries(&mut response).await.unwrap();
4224 assert_ne!(response.status, zx::sys::ZX_OK);
4225
4226 assert_eq!(read_calls.load(Ordering::Relaxed), 1);
4229
4230 std::mem::drop(proxy);
4231 }
4232 );
4233 }
4234}