1#![expect(unsafe_code)]
9
10use crate::mapping_manager::Mappable;
11use crate::mapping_manager::MappingBacking;
12use crate::mapping_manager::MappingManagerClient;
13use crate::mapping_manager::MappingParams;
14use crate::mapping_manager::VaMapper;
15use crate::partition_mapper::PartitionMapper;
16use anyhow::Context as _;
17use futures::StreamExt;
18use inspect::Inspect;
19use inspect::InspectMut;
20use memory_range::MemoryRange;
21use mesh::MeshPayload;
22use mesh::error::RemoteError;
23use mesh::rpc::FailableRpc;
24use mesh::rpc::Rpc;
25use mesh::rpc::RpcSend;
26use pal_async::task::Spawn;
27use std::cmp::Ordering;
28use std::sync::Arc;
29use thiserror::Error;
30use vmcore::local_only::LocalOnly;
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect, MeshPayload)]
34pub enum MappingType {
35 Ram,
38 Device,
42}
43
44pub struct DmaMapRequest<'a> {
46 pub range: MemoryRange,
48 pub host_va: *const u8,
50 pub mappable: Option<&'a Mappable>,
54 pub file_offset: u64,
58 pub writable: bool,
61 pub mapping_type: MappingType,
63}
64
65pub trait DmaTarget: Send + Sync {
82 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()>;
92
93 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()>;
102}
103
104struct DmaMapper {
110 id: DmaMapperId,
111 target: Arc<dyn DmaTarget>,
112 va_mapper: Arc<VaMapper>,
113 needs_fd: bool,
118}
119
120#[derive(Debug, Copy, Clone, PartialEq, Eq)]
121struct DmaMapperId(u64);
122
123struct SubMapping<'a> {
127 range: MemoryRange,
128 backing: &'a MappingBacking,
129 writable: bool,
130 mapping_type: MappingType,
131}
132
133impl DmaMapper {
134 fn map_dma(&self, mapping: SubMapping<'_>) -> anyhow::Result<()> {
136 let host_va = unsafe {
143 self.va_mapper
144 .as_ptr()
145 .add(mapping.range.start() as usize)
146 .cast_const()
147 };
148 let request = DmaMapRequest {
149 range: mapping.range,
150 host_va,
151 mappable: mapping.backing.mappable(),
152 file_offset: mapping.backing.file_offset(),
153 writable: mapping.writable,
154 mapping_type: mapping.mapping_type,
155 };
156 unsafe { self.target.map_dma(request) }
161 }
162
163 fn unmap_dma(&self, range: MemoryRange) {
165 if let Err(e) = self.target.unmap_dma(range) {
166 tracing::warn!(
167 error = &*e as &dyn std::error::Error,
168 %range,
169 "DMA unmap failed"
170 );
171 }
172 }
173}
174
175#[derive(Debug, Inspect)]
177pub struct RegionManager {
178 #[inspect(
179 flatten,
180 with = "|x| inspect::send(&x.req_send, RegionRequest::Inspect)"
181 )]
182 client: RegionManagerClient,
183}
184
185#[derive(Debug, MeshPayload, Clone)]
187pub struct RegionManagerClient {
188 req_send: mesh::Sender<RegionRequest>,
189}
190
191struct Region {
192 id: RegionId,
193 map_params: Option<MapParams>,
194 is_active: bool,
195 params: RegionParams,
196 mappings: Vec<RegionMapping>,
197}
198
199#[derive(Debug, MeshPayload)]
200struct RegionParams {
201 name: String,
202 range: MemoryRange,
203 priority: u8,
204 mapping_type: MappingType,
206}
207
208#[derive(Copy, Clone, Debug, MeshPayload, PartialEq, Eq, Inspect)]
209pub struct MapParams {
210 pub writable: bool,
211 pub executable: bool,
212 pub prefetch: bool,
213}
214
215impl Region {
216 fn active_range(&self) -> Option<MemoryRange> {
217 if self.is_active {
218 Some(self.params.range)
219 } else {
220 None
221 }
222 }
223}
224
225#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, MeshPayload)]
227pub struct RegionId(u64);
228
229#[derive(InspectMut)]
230struct RegionManagerTask {
231 #[inspect(with = "inspect_regions")]
232 regions: Vec<Region>,
233 #[inspect(skip)]
234 next_region_id: u64,
235 #[inspect(skip)]
236 inner: RegionManagerTaskInner,
237}
238
239fn inspect_regions(regions: &Vec<Region>) -> impl '_ + Inspect {
240 inspect::adhoc(move |req| {
241 let mut resp = req.respond();
242 for region in regions {
243 resp.field(
244 &format!("{}:{}", region.params.range, region.params.name),
245 inspect::adhoc(|req| {
246 req.respond()
247 .field("map_params", region.map_params)
248 .field("is_active", region.is_active)
249 .field("priority", region.params.priority)
250 .field(
251 "mappings",
252 inspect::adhoc(|req| {
253 inspect_mappings(req, region.params.range.start(), ®ion.mappings)
254 }),
255 );
256 }),
257 );
258 }
259 })
260}
261
262fn inspect_mappings(req: inspect::Request<'_>, region_start: u64, mappings: &[RegionMapping]) {
263 let mut resp = req.respond();
264 for mapping in mappings {
265 let range = MemoryRange::new(
266 region_start + mapping.params.range_in_region.start()
267 ..region_start + mapping.params.range_in_region.end(),
268 )
269 .to_string();
270
271 resp.field(
272 &range,
273 inspect::adhoc(|req| {
274 req.respond()
275 .field("writable", mapping.params.writable)
276 .field("backed_by_fd", mapping.params.backing.mappable().is_some())
277 .hex("file_offset", mapping.params.backing.file_offset());
278 }),
279 );
280 }
281}
282
283struct RegionManagerTaskInner {
284 partitions: Vec<PartitionMapper>,
285 dma_mappers: Vec<DmaMapper>,
286 next_dma_mapper_id: u64,
287 mapping_manager: MappingManagerClient,
288}
289
290#[derive(MeshPayload)]
291enum RegionRequest {
292 AddRegion(Rpc<RegionParams, Result<RegionId, AddRegionError>>),
293 RemoveRegion(Rpc<RegionId, ()>),
294 MapRegion(FailableRpc<(RegionId, MapParams), ()>),
295 UnmapRegion(Rpc<RegionId, ()>),
296 AddMapping(FailableRpc<(RegionId, RegionMappingParams), ()>),
297 RemoveMappings(Rpc<(RegionId, MemoryRange), ()>),
298 AddPartition(
299 LocalOnly<Rpc<PartitionMapper, Result<(), crate::partition_mapper::PartitionMapperError>>>,
300 ),
301 AddDmaMapper(LocalOnly<Rpc<(Arc<dyn DmaTarget>, bool), anyhow::Result<DmaMapperId>>>),
302 RemoveDmaMapper(LocalOnly<DmaMapperId>),
303 Inspect(inspect::Deferred),
304}
305
306struct RegionMapping {
307 params: RegionMappingParams,
308}
309
310#[derive(MeshPayload)]
311struct RegionMappingParams {
312 range_in_region: MemoryRange,
313 backing: MappingBacking,
318 writable: bool,
319 numa_node: Option<u32>,
320}
321
322fn range_within(outer: MemoryRange, inner: MemoryRange) -> MemoryRange {
323 assert!(inner.end() <= outer.len());
324 MemoryRange::new(outer.start() + inner.start()..outer.start() + inner.end())
325}
326
327#[derive(Debug, Error, MeshPayload)]
328pub enum AddRegionError {
329 #[error("memory region {new} overlaps with existing region {existing}")]
330 OverlapError { existing: String, new: String },
331}
332
333impl RegionManagerTask {
334 fn new(mapping_manager: MappingManagerClient) -> Self {
335 Self {
336 regions: Vec::new(),
337 next_region_id: 1,
338 inner: RegionManagerTaskInner {
339 mapping_manager,
340 partitions: Vec::new(),
341 dma_mappers: Vec::new(),
342 next_dma_mapper_id: 0,
343 },
344 }
345 }
346
347 async fn run(&mut self, req_recv: &mut mesh::Receiver<RegionRequest>) {
348 while let Some(req) = req_recv.next().await {
349 match req {
350 RegionRequest::AddMapping(rpc) => {
351 rpc.handle_failable(async |(id, params)| self.add_mapping(id, params).await)
352 .await
353 }
354 RegionRequest::RemoveMappings(rpc) => {
355 rpc.handle(async |(id, range)| self.remove_mappings(id, range).await)
356 .await
357 }
358 RegionRequest::AddPartition(LocalOnly(rpc)) => {
359 rpc.handle(async |partition| self.add_partition(partition).await)
360 .await
361 }
362 RegionRequest::AddDmaMapper(LocalOnly(rpc)) => {
363 let ((target, needs_fd), rpc) = rpc.split();
364 let result = self.add_dma_mapper(target, needs_fd).await;
365 rpc.complete(result);
366 }
367 RegionRequest::RemoveDmaMapper(LocalOnly(id)) => {
368 self.remove_dma_mapper(id);
369 }
370 RegionRequest::AddRegion(rpc) => rpc.handle_sync(|params| self.add_region(params)),
371 RegionRequest::RemoveRegion(rpc) => {
372 rpc.handle(async |id| self.unmap_region(id, true).await)
373 .await
374 }
375 RegionRequest::MapRegion(rpc) => {
376 rpc.handle_failable(async |(id, params)| self.map_region(id, params).await)
377 .await
378 }
379 RegionRequest::UnmapRegion(rpc) => {
380 rpc.handle(async |id| self.unmap_region(id, false).await)
381 .await
382 }
383 RegionRequest::Inspect(deferred) => {
384 deferred.inspect(&mut *self);
385 }
386 }
387 }
388 }
389
390 async fn add_partition(
391 &mut self,
392 partition: PartitionMapper,
393 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
394 for region in &self.regions {
397 if region.is_active {
398 partition
399 .map_region(region.params.range, region.map_params.unwrap())
400 .await?;
401 }
402 }
403 self.inner.partitions.push(partition);
404 Ok(())
405 }
406
407 async fn add_dma_mapper(
408 &mut self,
409 target: Arc<dyn DmaTarget>,
410 needs_fd: bool,
411 ) -> anyhow::Result<DmaMapperId> {
412 let va_mapper = self.inner.mapping_manager.new_mapper(true).await?;
415 assert!(
416 va_mapper.is_eager(),
417 "DMA mapper requires an eager VaMapper"
418 );
419
420 if needs_fd {
424 if let Some(region) = self.regions.iter().find(|r| {
425 r.mappings
426 .iter()
427 .any(|m| m.params.backing.mappable().is_none())
428 }) {
429 anyhow::bail!(
430 "cannot register a DMA mapper that requires a backing fd: \
431 region {} contains a mapping with no backing fd (private RAM)",
432 region.params.range
433 );
434 }
435 }
436
437 let id = DmaMapperId(self.inner.next_dma_mapper_id);
438 self.inner.next_dma_mapper_id += 1;
439
440 let mapper = DmaMapper {
441 id,
442 target,
443 va_mapper,
444 needs_fd,
445 };
446
447 for region in &self.regions {
450 if region.is_active {
451 for mapping in ®ion.mappings {
452 let range = range_within(region.params.range, mapping.params.range_in_region);
453 let writable = mapping.params.writable && region.map_params.unwrap().writable;
454 mapper.map_dma(SubMapping {
455 range,
456 backing: &mapping.params.backing,
457 writable,
458 mapping_type: region.params.mapping_type,
459 })?;
460 }
461 }
462 }
463
464 self.inner.dma_mappers.push(mapper);
465 Ok(id)
466 }
467
468 fn remove_dma_mapper(&mut self, id: DmaMapperId) {
469 if let Some(pos) = self.inner.dma_mappers.iter().position(|m| m.id == id) {
470 let mapper = &self.inner.dma_mappers[pos];
471 for region in &self.regions {
473 if region.is_active {
474 for mapping in ®ion.mappings {
475 let range =
476 range_within(region.params.range, mapping.params.range_in_region);
477 mapper.unmap_dma(range);
478 }
479 }
480 }
481 self.inner.dma_mappers.swap_remove(pos);
482 }
483 }
484
485 fn region_index(&self, id: RegionId) -> usize {
486 self.regions.iter().position(|r| r.id == id).unwrap()
487 }
488
489 fn add_region(&mut self, params: RegionParams) -> Result<RegionId, AddRegionError> {
490 let range = params.range;
493 for other_region in &self.regions {
494 let other_range = other_region.params.range;
495 if !range.overlaps(&other_range) {
496 continue;
497 };
498 let ok = match params.priority.cmp(&other_region.params.priority) {
499 Ordering::Less => other_range.contains(&range),
500 Ordering::Equal => other_range == range,
501 Ordering::Greater => range.contains(&other_range),
502 };
503 if !ok {
504 return Err(AddRegionError::OverlapError {
505 existing: other_region.params.name.clone(),
506 new: params.name,
507 });
508 }
509 }
510
511 tracing::debug!(
512 range = %params.range,
513 name = params.name,
514 priority = params.priority,
515 "new region"
516 );
517
518 let id = RegionId(self.next_region_id);
519 self.next_region_id += 1;
520 self.regions.push(Region {
521 id,
522 map_params: None,
523 is_active: false,
524 params,
525 mappings: Vec::new(),
526 });
527 Ok(id)
528 }
529
530 async fn enable_best_region(&mut self, mut range: MemoryRange) -> anyhow::Result<()> {
533 while !range.is_empty() {
534 if let Some(region) = self
539 .regions
540 .iter_mut()
541 .filter_map(|region| {
542 region.map_params?;
543 if !range.contains(®ion.params.range) {
544 assert!(
545 !range.overlaps(®ion.params.range),
546 "no overlap invariant violated"
547 );
548 return None;
549 }
550 assert!(!region.is_active);
551 Some(region)
552 })
553 .min_by_key(|region| {
554 (
555 region.params.range.start(),
556 u8::MAX - region.params.priority,
557 )
558 })
559 {
560 self.inner.enable_region(region).await?;
561 range = MemoryRange::new(region.params.range.end()..range.end());
562 } else {
563 range = MemoryRange::EMPTY;
564 }
565 }
566 Ok(())
567 }
568
569 async fn map_region(&mut self, id: RegionId, map_params: MapParams) -> anyhow::Result<()> {
570 let index = self.region_index(id);
571 let region = &mut self.regions[index];
572 let range = region.params.range;
573 let priority = region.params.priority;
574 if region.map_params == Some(map_params) {
575 return Ok(());
576 }
577
578 tracing::debug!(
579 name = region.params.name,
580 range = %region.params.range,
581 writable = map_params.writable,
582 "mapping region"
583 );
584
585 let mut enable = true;
588 for (other_index, other_region) in self.regions.iter_mut().enumerate() {
589 if !other_region.is_active || !other_region.params.range.overlaps(&range) {
590 continue;
591 }
592 if other_region.params.priority > priority
593 || (other_region.params.priority == priority && other_index < index)
594 {
595 enable = false;
596 } else {
597 assert!(enable);
598 self.inner.disable_region(other_region, true).await;
602 }
603 }
604
605 self.regions[index].map_params = Some(map_params);
606 if enable {
607 self.enable_best_region(range).await?;
608 }
609 Ok(())
610 }
611
612 async fn unmap_region(&mut self, id: RegionId, remove: bool) {
613 let index = self.region_index(id);
614 let region = &mut self.regions[index];
615 tracing::debug!(
616 name = region.params.name,
617 range = %region.params.range,
618 remove,
619 "unmapping region"
620 );
621
622 let active_range = region.is_active.then_some(region.params.range);
623 if active_range.is_some() {
624 self.inner.disable_region(region, !remove).await;
627 }
628
629 if remove {
630 self.regions.remove(index);
631 } else {
632 region.map_params = None;
633 }
634 if let Some(range) = active_range {
635 self.enable_best_region(range).await.expect(
636 "failed to re-enable region after unmap; \
637 this should not fail because the region was previously active",
638 );
639 }
640 }
641
642 async fn add_mapping(
643 &mut self,
644 id: RegionId,
645 params: RegionMappingParams,
646 ) -> anyhow::Result<()> {
647 if params.backing.mappable().is_none() && self.inner.dma_mappers.iter().any(|m| m.needs_fd)
652 {
653 anyhow::bail!(
654 "cannot add a mapping with no backing fd: a registered DMA mapper requires one"
655 );
656 }
657
658 let index = self.region_index(id);
659 let region = &mut self.regions[index];
660
661 assert!(
664 !region
665 .mappings
666 .iter()
667 .any(|m| m.params.range_in_region.overlaps(¶ms.range_in_region))
668 );
669
670 if let Some(region_range) = region.active_range() {
671 let range = range_within(region_range, params.range_in_region);
672 let writable = params.writable && region.map_params.unwrap().writable;
673 self.inner
678 .mapping_manager
679 .add_mapping(MappingParams {
680 range,
681 backing: params.backing.clone(),
682 writable,
683 mapping_type: region.params.mapping_type,
684 numa_node: params.numa_node,
685 })
686 .await?;
687
688 for (dma_idx, dma_mapper) in self.inner.dma_mappers.iter().enumerate() {
689 if let Err(e) = dma_mapper.map_dma(SubMapping {
690 range,
691 backing: ¶ms.backing,
692 writable,
693 mapping_type: region.params.mapping_type,
694 }) {
695 for dm in &self.inner.dma_mappers[..dma_idx] {
698 dm.unmap_dma(range);
699 }
700 self.inner.mapping_manager.remove_mappings(range).await;
701 return Err(e);
702 }
703 }
704 }
705
706 region.mappings.push(RegionMapping { params });
707 Ok(())
708 }
709
710 async fn remove_mappings(&mut self, id: RegionId, range_in_region: MemoryRange) {
711 let index = self.region_index(id);
712 let region = &mut self.regions[index];
713 let active_range = region.active_range();
714
715 let removed_ranges: Vec<MemoryRange> = if active_range.is_some() {
718 let region_range = region.params.range;
719 region
720 .mappings
721 .iter()
722 .filter(|m| range_in_region.contains(&m.params.range_in_region))
723 .map(|m| range_within(region_range, m.params.range_in_region))
724 .collect()
725 } else {
726 Vec::new()
727 };
728
729 region.mappings.retain_mut(|mapping| {
730 if !range_in_region.contains(&mapping.params.range_in_region) {
731 assert!(
732 !range_in_region.overlaps(&mapping.params.range_in_region),
733 "no partial unmappings allowed"
734 );
735 return true;
736 }
737 false
738 });
739 if let Some(region_range) = active_range {
740 for &removed in &removed_ranges {
743 for dma_mapper in &self.inner.dma_mappers {
744 dma_mapper.unmap_dma(removed);
745 }
746 }
747
748 self.inner
749 .mapping_manager
750 .remove_mappings(range_within(region_range, range_in_region))
751 .await;
752
753 }
757 }
758}
759
760impl RegionManagerTaskInner {
761 async fn enable_region(&mut self, region: &mut Region) -> anyhow::Result<()> {
762 assert!(!region.is_active);
763 let map_params = region.map_params.unwrap();
764
765 tracing::debug!(
766 name = region.params.name,
767 range = %region.params.range,
768 writable = map_params.writable,
769 "enabling region"
770 );
771
772 for (mapped_count, mapping) in region.mappings.iter().enumerate() {
775 if let Err(e) = self
780 .mapping_manager
781 .add_mapping(MappingParams {
782 range: range_within(region.params.range, mapping.params.range_in_region),
783 backing: mapping.params.backing.clone(),
784 writable: mapping.params.writable && map_params.writable,
785 mapping_type: region.params.mapping_type,
786 numa_node: mapping.params.numa_node,
787 })
788 .await
789 {
790 for prev in ®ion.mappings[..mapped_count] {
792 let range = range_within(region.params.range, prev.params.range_in_region);
793 for dma_mapper in &self.dma_mappers {
794 dma_mapper.unmap_dma(range);
795 }
796 }
797 self.mapping_manager
798 .remove_mappings(region.params.range)
799 .await;
800 return Err(e).context(format!(
801 "failed to map {} during region enable",
802 range_within(region.params.range, mapping.params.range_in_region),
803 ));
804 }
805
806 let range = range_within(region.params.range, mapping.params.range_in_region);
808 let writable = mapping.params.writable && map_params.writable;
809 for (dma_idx, dma_mapper) in self.dma_mappers.iter().enumerate() {
810 if let Err(e) = dma_mapper.map_dma(SubMapping {
811 range,
812 backing: &mapping.params.backing,
813 writable,
814 mapping_type: region.params.mapping_type,
815 }) {
816 for dm in &self.dma_mappers[..dma_idx] {
819 dm.unmap_dma(range);
820 }
821 for prev in ®ion.mappings[..mapped_count] {
824 let prev_range =
825 range_within(region.params.range, prev.params.range_in_region);
826 for dm in &self.dma_mappers {
827 dm.unmap_dma(prev_range);
828 }
829 }
830 self.mapping_manager
831 .remove_mappings(region.params.range)
832 .await;
833 return Err(e).context(format!(
834 "DMA mapper failed to map {range} during region enable"
835 ));
836 }
837 }
838 }
839
840 for partition in &mut self.partitions {
842 partition
843 .map_region(region.params.range, map_params)
844 .await
845 .expect("cannot recover from failed mapping");
846 }
847
848 region.is_active = true;
849 Ok(())
850 }
851
852 async fn disable_region(&mut self, region: &mut Region, transient: bool) {
864 assert!(region.is_active);
865 assert!(
866 !transient
867 || region
868 .mappings
869 .iter()
870 .all(|m| m.params.backing.mappable().is_some()),
871 "transiently disabling region {} would decommit private RAM and lose its contents",
872 region.params.range,
873 );
874
875 tracing::debug!(
876 name = region.params.name,
877 range = %region.params.range,
878 "disabling region"
879 );
880
881 let region_range = region.params.range;
886 for dma_mapper in &mut self.dma_mappers {
887 dma_mapper.unmap_dma(region_range);
888 }
889
890 for partition in &mut self.partitions {
891 partition.unmap_region(region_range);
892 }
893 self.mapping_manager.remove_mappings(region_range).await;
894 region.is_active = false;
895 }
896}
897
898impl RegionManager {
899 pub fn new(spawn: impl Spawn, mapping_manager: MappingManagerClient) -> Self {
901 let (req_send, mut req_recv) = mesh::mpsc_channel();
902 spawn
903 .spawn("region_manager", {
904 let mut task = RegionManagerTask::new(mapping_manager);
905 async move {
906 task.run(&mut req_recv).await;
907 }
908 })
909 .detach();
910 Self {
911 client: RegionManagerClient { req_send },
912 }
913 }
914
915 pub fn client(&self) -> &RegionManagerClient {
917 &self.client
918 }
919}
920
921impl RegionManagerClient {
922 pub async fn add_partition(
926 &self,
927 partition: PartitionMapper,
928 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
929 self.req_send
930 .call(|x| RegionRequest::AddPartition(LocalOnly(x)), partition)
931 .await
932 .unwrap()
933 }
934
935 pub async fn new_region(
939 &self,
940 name: String,
941 range: MemoryRange,
942 priority: u8,
943 mapping_type: MappingType,
944 ) -> Result<RegionHandle, AddRegionError> {
945 let params = RegionParams {
946 name,
947 range,
948 priority,
949 mapping_type,
950 };
951
952 let id = self
953 .req_send
954 .call(RegionRequest::AddRegion, params)
955 .await
956 .unwrap()?;
957
958 Ok(RegionHandle {
959 id: Some(id),
960 req_send: self.req_send.clone(),
961 })
962 }
963}
964
965#[derive(Clone)]
971pub struct DmaMapperClient {
972 req_send: mesh::Sender<RegionRequest>,
973}
974
975impl DmaMapperClient {
976 pub(crate) fn new(region_manager: &RegionManagerClient) -> Self {
977 Self {
978 req_send: region_manager.req_send.clone(),
979 }
980 }
981
982 pub async fn add_dma_mapper(
1006 &self,
1007 target: Arc<dyn DmaTarget>,
1008 needs_fd: bool,
1009 ) -> anyhow::Result<DmaMapperHandle> {
1010 let id = self
1011 .req_send
1012 .call(
1013 |x| RegionRequest::AddDmaMapper(LocalOnly(x)),
1014 (target, needs_fd),
1015 )
1016 .await
1017 .unwrap()?;
1018 Ok(DmaMapperHandle {
1019 id: Some(id),
1020 req_send: self.req_send.clone(),
1021 })
1022 }
1023}
1024
1025pub struct DmaMapperHandle {
1030 id: Option<DmaMapperId>,
1031 req_send: mesh::Sender<RegionRequest>,
1032}
1033
1034impl Drop for DmaMapperHandle {
1035 fn drop(&mut self) {
1036 if let Some(id) = self.id {
1037 self.req_send
1038 .send(RegionRequest::RemoveDmaMapper(LocalOnly(id)));
1039 }
1040 }
1041}
1042
1043#[derive(Debug)]
1047#[must_use]
1048pub struct RegionHandle {
1049 id: Option<RegionId>,
1050 req_send: mesh::Sender<RegionRequest>,
1051}
1052
1053impl RegionHandle {
1054 pub async fn map(&self, params: MapParams) -> Result<(), RemoteError> {
1056 self.req_send
1057 .call(RegionRequest::MapRegion, (self.id.unwrap(), params))
1058 .await
1059 .map_err(RemoteError::new)?
1060 }
1061
1062 pub async fn unmap(&self) {
1064 let _ = self
1065 .req_send
1066 .call(RegionRequest::UnmapRegion, self.id.unwrap())
1067 .await;
1068 }
1069
1070 pub async fn add_mapping(
1082 &self,
1083 range_in_region: MemoryRange,
1084 backing: MappingBacking,
1085 writable: bool,
1086 numa_node: Option<u32>,
1087 ) -> Result<(), RemoteError> {
1088 self.req_send
1089 .call(
1090 RegionRequest::AddMapping,
1091 (
1092 self.id.unwrap(),
1093 RegionMappingParams {
1094 range_in_region,
1095 backing,
1096 writable,
1097 numa_node,
1098 },
1099 ),
1100 )
1101 .await
1102 .map_err(RemoteError::new)?
1103 }
1104
1105 pub async fn remove_mappings(&self, range: MemoryRange) {
1109 let _ = self
1110 .req_send
1111 .call(RegionRequest::RemoveMappings, (self.id.unwrap(), range))
1112 .await;
1113 }
1114
1115 pub async fn teardown(mut self) {
1117 let _ = self
1118 .req_send
1119 .call(RegionRequest::RemoveRegion, self.id.take().unwrap())
1120 .await;
1121 }
1122}
1123
1124impl Drop for RegionHandle {
1125 fn drop(&mut self) {
1126 if let Some(id) = self.id {
1127 let _recv = self.req_send.call(RegionRequest::RemoveRegion, id);
1128 }
1130 }
1131}
1132
1133#[cfg(test)]
1134mod tests {
1135 use super::MapParams;
1136 use super::RegionManagerTask;
1137 use crate::mapping_manager::Mappable;
1138 use crate::mapping_manager::MappingBacking;
1139 use crate::mapping_manager::MappingManager;
1140 use crate::region_manager::AddRegionError;
1141 use crate::region_manager::DmaMapRequest;
1142 use crate::region_manager::DmaTarget;
1143 use crate::region_manager::MappingType;
1144 use crate::region_manager::RegionId;
1145 use crate::region_manager::RegionMappingParams;
1146 use crate::region_manager::RegionParams;
1147 use memory_range::MemoryRange;
1148 use pal_async::async_test;
1149 use pal_async::task::Spawn;
1150 use parking_lot::Mutex;
1151 use std::ops::Range;
1152 use std::sync::Arc;
1153
1154 #[derive(Default)]
1156 struct RecordingDmaTarget {
1157 events: Mutex<Vec<DmaEvent>>,
1158 backingless_maps: Mutex<Vec<MemoryRange>>,
1161 }
1162
1163 #[derive(Debug, Clone, PartialEq, Eq)]
1164 enum DmaEvent {
1165 Map(MemoryRange),
1166 Unmap(MemoryRange),
1167 }
1168
1169 impl DmaTarget for RecordingDmaTarget {
1170 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()> {
1171 if request.mappable.is_none() {
1172 self.backingless_maps.lock().push(request.range);
1173 }
1174 self.events.lock().push(DmaEvent::Map(request.range));
1175 Ok(())
1176 }
1177
1178 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()> {
1179 self.events.lock().push(DmaEvent::Unmap(range));
1180 Ok(())
1181 }
1182 }
1183
1184 impl RecordingDmaTarget {
1185 fn take_events(&self) -> Vec<DmaEvent> {
1186 std::mem::take(&mut self.events.lock())
1187 }
1188
1189 fn take_backingless_maps(&self) -> Vec<MemoryRange> {
1190 std::mem::take(&mut self.backingless_maps.lock())
1191 }
1192 }
1193
1194 fn test_mappable() -> Mappable {
1196 sparse_mmap::alloc_shared_memory(0x10000, "test-dma")
1197 .unwrap()
1198 .into()
1199 }
1200
1201 #[async_test]
1202 async fn test_region_overlap(spawn: impl Spawn) {
1203 struct TestTask(RegionManagerTask);
1204 impl TestTask {
1205 async fn add(
1206 &mut self,
1207 priority: u8,
1208 range: Range<u64>,
1209 ) -> Result<RegionId, AddRegionError> {
1210 let id = self.0.add_region(RegionParams {
1211 priority,
1212 name: priority.to_string(),
1213 range: MemoryRange::new(range),
1214 mapping_type: MappingType::Device,
1215 })?;
1216 self.0
1217 .map_region(
1218 id,
1219 MapParams {
1220 executable: true,
1221 writable: true,
1222 prefetch: false,
1223 },
1224 )
1225 .await
1226 .unwrap();
1227 Ok(id)
1228 }
1229
1230 async fn remove(&mut self, id: RegionId) {
1231 self.0.unmap_region(id, true).await;
1232 }
1233 }
1234
1235 let mm = MappingManager::new(spawn, 0x200000, Vec::new(), None);
1236 let mut task = TestTask(RegionManagerTask::new(mm.client().clone()));
1237
1238 let high = task.add(1, 0x1000..0x3000).await.unwrap();
1239
1240 task.add(0, 0x2000..0x4000).await.unwrap_err();
1241
1242 let low = task.add(0, 0x1000..0x3000).await.unwrap();
1243
1244 task.remove(high).await;
1245
1246 task.add(1, 0x2000..0x4000).await.unwrap_err();
1247 task.add(1, 0x2000..0x3000).await.unwrap_err();
1248
1249 let _high = task.add(1, 0..0x10000).await.unwrap();
1250
1251 task.remove(low).await;
1252
1253 task.add(0, 0..0x20000).await.unwrap_err();
1254
1255 let _low = task.add(0, 0x1000..0x8000).await.unwrap();
1256 }
1257
1258 struct DmaTestTask {
1260 task: RegionManagerTask,
1261 mappable: Mappable,
1262 }
1263
1264 impl DmaTestTask {
1265 fn new(spawn: impl Spawn) -> Self {
1266 let mm = MappingManager::new(spawn, 0x200000, Vec::new(), None);
1267 Self {
1268 task: RegionManagerTask::new(mm.client().clone()),
1269 mappable: test_mappable(),
1270 }
1271 }
1272
1273 async fn add_region(&mut self, range: Range<u64>) -> RegionId {
1274 let id = self
1275 .task
1276 .add_region(RegionParams {
1277 priority: 0,
1278 name: format!("{range:x?}"),
1279 range: MemoryRange::new(range),
1280 mapping_type: MappingType::Device,
1281 })
1282 .unwrap();
1283 self.task
1284 .map_region(
1285 id,
1286 MapParams {
1287 executable: true,
1288 writable: true,
1289 prefetch: false,
1290 },
1291 )
1292 .await
1293 .unwrap();
1294 id
1295 }
1296
1297 async fn add_mapping(&mut self, id: RegionId, range_in_region: Range<u64>) {
1298 self.task
1299 .add_mapping(
1300 id,
1301 RegionMappingParams {
1302 range_in_region: MemoryRange::new(range_in_region),
1303 backing: MappingBacking::File {
1304 mappable: self.mappable.clone(),
1305 file_offset: 0,
1306 },
1307 writable: true,
1308 numa_node: None,
1309 },
1310 )
1311 .await
1312 .unwrap();
1313 }
1314
1315 async fn add_private_mapping(&mut self, id: RegionId, range_in_region: Range<u64>) {
1317 self.task
1318 .add_mapping(
1319 id,
1320 RegionMappingParams {
1321 range_in_region: MemoryRange::new(range_in_region),
1322 backing: MappingBacking::Private {
1323 transparent_hugepages: false,
1324 },
1325 writable: true,
1326 numa_node: None,
1327 },
1328 )
1329 .await
1330 .unwrap();
1331 }
1332 }
1333
1334 #[async_test]
1335 async fn test_dma_replay_on_registration(spawn: impl Spawn) {
1336 let mut t = DmaTestTask::new(&spawn);
1337 let r = t.add_region(0x0..0x10000).await;
1338 t.add_mapping(r, 0x0..0x4000).await;
1339 t.add_mapping(r, 0x8000..0xC000).await;
1340
1341 let target = Arc::new(RecordingDmaTarget::default());
1343 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1344
1345 assert_eq!(
1346 target.take_events(),
1347 vec![
1348 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1349 DmaEvent::Map(MemoryRange::new(0x8000..0xC000)),
1350 ]
1351 );
1352
1353 t.task.remove_dma_mapper(id);
1355 }
1356
1357 #[async_test]
1358 async fn test_dma_live_map_unmap(spawn: impl Spawn) {
1359 let mut t = DmaTestTask::new(&spawn);
1360 let r = t.add_region(0x0..0x10000).await;
1361
1362 let target = Arc::new(RecordingDmaTarget::default());
1363 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1364 target.take_events(); t.add_mapping(r, 0x0..0x4000).await;
1368 assert_eq!(
1369 target.take_events(),
1370 vec![DmaEvent::Map(MemoryRange::new(0x0..0x4000))]
1371 );
1372
1373 t.task
1375 .remove_mappings(r, MemoryRange::new(0x0..0x4000))
1376 .await;
1377 assert_eq!(
1378 target.take_events(),
1379 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x4000))]
1380 );
1381 }
1382
1383 #[async_test]
1389 async fn test_dma_private_mapping_maps_by_va(spawn: impl Spawn) {
1390 let mut t = DmaTestTask::new(&spawn);
1391 let r = t.add_region(0x0..0x10000).await;
1392 t.add_private_mapping(r, 0x0..0x4000).await;
1394
1395 let target = Arc::new(RecordingDmaTarget::default());
1396 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1397
1398 assert_eq!(
1400 target.take_events(),
1401 vec![DmaEvent::Map(MemoryRange::new(0x0..0x4000))]
1402 );
1403 assert_eq!(
1404 target.take_backingless_maps(),
1405 vec![MemoryRange::new(0x0..0x4000)]
1406 );
1407
1408 t.add_private_mapping(r, 0x8000..0xC000).await;
1411 assert_eq!(
1412 target.take_events(),
1413 vec![DmaEvent::Map(MemoryRange::new(0x8000..0xC000))]
1414 );
1415 t.task.remove_dma_mapper(id);
1416 }
1417
1418 #[async_test]
1422 async fn test_needs_fd_rejects_existing_private_mapping(spawn: impl Spawn) {
1423 let mut t = DmaTestTask::new(&spawn);
1424 let r = t.add_region(0x0..0x10000).await;
1425 t.add_private_mapping(r, 0x0..0x4000).await;
1426
1427 let target = Arc::new(RecordingDmaTarget::default());
1428 let result = t.task.add_dma_mapper(target.clone(), true).await;
1429 assert!(
1430 result.is_err(),
1431 "needs_fd mapper must fail to register when private RAM exists"
1432 );
1433 }
1434
1435 #[async_test]
1438 async fn test_needs_fd_rejects_new_private_mapping(spawn: impl Spawn) {
1439 let mut t = DmaTestTask::new(&spawn);
1440 let r = t.add_region(0x0..0x10000).await;
1441 t.add_mapping(r, 0x0..0x4000).await;
1443 let target = Arc::new(RecordingDmaTarget::default());
1444 let _id = t.task.add_dma_mapper(target.clone(), true).await.unwrap();
1445
1446 t.add_mapping(r, 0x4000..0x8000).await;
1448
1449 let result = t
1451 .task
1452 .add_mapping(
1453 r,
1454 RegionMappingParams {
1455 range_in_region: MemoryRange::new(0x8000..0xC000),
1456 backing: MappingBacking::Private {
1457 transparent_hugepages: false,
1458 },
1459 writable: true,
1460 numa_node: None,
1461 },
1462 )
1463 .await;
1464 assert!(
1465 result.is_err(),
1466 "creating a private mapping must fail while a needs_fd mapper is registered"
1467 );
1468 }
1469
1470 #[async_test]
1471 async fn test_dma_disable_region_unmaps(spawn: impl Spawn) {
1472 let mut t = DmaTestTask::new(&spawn);
1473 let r = t.add_region(0x0..0x10000).await;
1474 t.add_mapping(r, 0x0..0x4000).await;
1475 t.add_mapping(r, 0x8000..0xC000).await;
1476
1477 let target = Arc::new(RecordingDmaTarget::default());
1478 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1479 target.take_events(); t.task.unmap_region(r, false).await;
1483 assert_eq!(
1484 target.take_events(),
1485 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x10000))]
1486 );
1487 }
1488
1489 #[async_test]
1490 async fn test_dma_remove_mapper_unmaps_all(spawn: impl Spawn) {
1491 let mut t = DmaTestTask::new(&spawn);
1492 let r = t.add_region(0x0..0x10000).await;
1493 t.add_mapping(r, 0x0..0x4000).await;
1494 t.add_mapping(r, 0x8000..0xC000).await;
1495
1496 let target = Arc::new(RecordingDmaTarget::default());
1497 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1498 target.take_events(); t.task.remove_dma_mapper(id);
1502 assert_eq!(
1503 target.take_events(),
1504 vec![
1505 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1506 DmaEvent::Unmap(MemoryRange::new(0x8000..0xC000)),
1507 ]
1508 );
1509 }
1510
1511 #[async_test]
1512 async fn test_dma_inactive_region_no_notifications(spawn: impl Spawn) {
1513 let mut t = DmaTestTask::new(&spawn);
1514 let r = t.add_region(0x0..0x10000).await;
1515 t.add_mapping(r, 0x0..0x4000).await;
1516
1517 t.task.unmap_region(r, false).await;
1519
1520 let target = Arc::new(RecordingDmaTarget::default());
1521 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1522
1523 assert_eq!(target.take_events(), vec![]);
1525
1526 t.add_mapping(r, 0x8000..0xC000).await;
1528 assert_eq!(target.take_events(), vec![]);
1529 }
1530
1531 struct FailAfterDmaTarget {
1534 fail_after: usize,
1536 inner: RecordingDmaTarget,
1537 call_count: Mutex<usize>,
1538 }
1539
1540 impl FailAfterDmaTarget {
1541 fn new(fail_after: usize) -> Self {
1542 Self {
1543 fail_after,
1544 inner: RecordingDmaTarget::default(),
1545 call_count: Mutex::new(0),
1546 }
1547 }
1548
1549 fn take_events(&self) -> Vec<DmaEvent> {
1550 self.inner.take_events()
1551 }
1552 }
1553
1554 impl DmaTarget for FailAfterDmaTarget {
1555 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()> {
1556 let mut count = self.call_count.lock();
1557 if *count >= self.fail_after {
1558 anyhow::bail!("simulated DMA mapping failure at {}", request.range);
1559 }
1560 *count += 1;
1561 drop(count);
1562 unsafe { self.inner.map_dma(request) }
1564 }
1565
1566 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()> {
1567 self.inner.unmap_dma(range)
1568 }
1569 }
1570
1571 #[async_test]
1572 async fn test_add_mapping_dma_failure_propagates(spawn: impl Spawn) {
1573 let mut t = DmaTestTask::new(&spawn);
1574 let r = t.add_region(0x0..0x10000).await;
1575
1576 let target = Arc::new(FailAfterDmaTarget::new(0));
1578 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1579 target.take_events();
1580
1581 let result = t
1584 .task
1585 .add_mapping(
1586 r,
1587 RegionMappingParams {
1588 range_in_region: MemoryRange::new(0x0..0x4000),
1589 backing: MappingBacking::File {
1590 mappable: t.mappable.clone(),
1591 file_offset: 0,
1592 },
1593 writable: true,
1594 numa_node: None,
1595 },
1596 )
1597 .await;
1598
1599 assert!(result.is_err(), "add_mapping should propagate DMA failure");
1600 }
1601
1602 #[async_test]
1603 async fn test_enable_region_rollback_unmaps_current_sub_mapping_from_earlier_dma_mappers(
1604 spawn: impl Spawn,
1605 ) {
1606 let mut t = DmaTestTask::new(&spawn);
1616
1617 let r = t.add_region(0x0..0x10000).await;
1618 t.add_mapping(r, 0x0..0x4000).await;
1619
1620 t.task.unmap_region(r, false).await;
1622
1623 let good_target = Arc::new(RecordingDmaTarget::default());
1624 let _good_id = t
1625 .task
1626 .add_dma_mapper(good_target.clone(), false)
1627 .await
1628 .unwrap();
1629
1630 let bad_target = Arc::new(FailAfterDmaTarget::new(0)); let _bad_id = t
1632 .task
1633 .add_dma_mapper(bad_target.clone(), false)
1634 .await
1635 .unwrap();
1636
1637 good_target.take_events();
1639 bad_target.take_events();
1640
1641 let result = t
1642 .task
1643 .map_region(
1644 r,
1645 MapParams {
1646 writable: true,
1647 executable: true,
1648 prefetch: false,
1649 },
1650 )
1651 .await;
1652
1653 assert!(result.is_err(), "enable should fail");
1654
1655 assert_eq!(
1657 good_target.take_events(),
1658 vec![
1659 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1660 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1661 ],
1662 "the successful DMA mapper must have its mapping rolled back"
1663 );
1664 }
1665
1666 #[async_test]
1667 async fn test_enable_region_rollback_unmaps_dma_sub_mappings(spawn: impl Spawn) {
1668 let mut t = DmaTestTask::new(&spawn);
1669
1670 let r = t.add_region(0x0..0x18000).await;
1672 t.add_mapping(r, 0x0..0x4000).await;
1673 t.add_mapping(r, 0x4000..0x8000).await;
1674 t.add_mapping(r, 0x8000..0xC000).await;
1675
1676 t.task.unmap_region(r, false).await;
1678
1679 let target = Arc::new(FailAfterDmaTarget::new(2));
1682 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1683
1684 let result = t
1687 .task
1688 .map_region(
1689 r,
1690 MapParams {
1691 writable: true,
1692 executable: true,
1693 prefetch: false,
1694 },
1695 )
1696 .await;
1697
1698 assert!(
1699 result.is_err(),
1700 "enable should fail on third DMA sub-mapping"
1701 );
1702
1703 let region = t.task.regions.iter().find(|reg| reg.id == r).unwrap();
1704 assert!(
1705 !region.is_active,
1706 "region should not be active after failed enable"
1707 );
1708
1709 assert_eq!(
1710 target.take_events(),
1711 vec![
1712 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1713 DmaEvent::Map(MemoryRange::new(0x4000..0x8000)),
1714 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1715 DmaEvent::Unmap(MemoryRange::new(0x4000..0x8000)),
1716 ],
1717 "successful DMA sub-mappings should be rolled back exactly once"
1718 );
1719 }
1720}