From d1e2791123f7f126162da1f5306f1eb037aa2651 Mon Sep 17 00:00:00 2001
From: Xiaohe Yang <yangxiaohe2@huawei.com>
Date: Mon, 24 Aug 2020 16:41:58 +0800
Subject: [PATCH 2/2] address_space: fix Region's IoEventFd address error.

1. Problem: When the layout of regions is as follows:
             0      1000   2000   3000   4000   5000   6000   7000   8000
             |------|------|------|------|------|------|------|------|
       a:    [                                                       ]
       b:           [                                  ]
       c:                  [CCCCCC]
   The FlatView is expected to be:
                           [CCCCCC]

   If Region-c contains a IoEventFd at offset 0, then the FlatRange[2000, 3000) in
   FlatView is expected to contains an IoEventFd at address 2000. But the result is,
   no IoEventFd is in the range of FlatRange[2000, 3000).

2. Region: The current IoEventFd's address calculation method only take account of
   current region's offset in parent region, but ignore that parent region also
   has an offset in grandparent region.

3. Fix: Only save IoEventFd's offset in current Region. Move the calculation of
   IoEventFd's address after generating FlatView.

Signed-off-by: Xiaohe Yang <yangxiaohe2@huawei.com>
---
 address_space/src/address_space.rs | 51 ++++++++++++++++++++++++++++--
 address_space/src/region.rs        | 16 ++--------
 2 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs
index 7428938..f69811e 100644
--- a/address_space/src/address_space.rs
+++ b/address_space/src/address_space.rs
@@ -229,9 +229,17 @@ impl AddressSpace {
         let mut ioeventfds = Vec::<RegionIoEventFd>::new();
 
         for fr in flatview.0.iter() {
+            let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region).0;
             for evtfd in fr.owner.ioeventfds().iter() {
-                if fr.addr_range.find_intersection(evtfd.addr_range).is_some() {
-                    ioeventfds.push(evtfd.try_clone()?);
+                let mut evtfd_clone = evtfd.try_clone()?;
+                evtfd_clone.addr_range.base =
+                    evtfd_clone.addr_range.base.unchecked_add(region_base);
+                if fr
+                    .addr_range
+                    .find_intersection(evtfd_clone.addr_range)
+                    .is_some()
+                {
+                    ioeventfds.push(evtfd_clone);
                 }
             }
         }
@@ -598,6 +606,45 @@ mod test {
         );
     }
 
+    #[test]
+    fn test_subregion_ioeventfd() {
+        let ioeventfds = vec![RegionIoEventFd {
+            fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
+            addr_range: AddressRange::from((0, 4)),
+            data_match: true,
+            data: 0_64,
+        }];
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
+
+        // region layout
+        //        0      1000   2000   3000   4000   5000   6000   7000   8000
+        //        |------|------|------|------|------|------|------|------|
+        //  b:           [                                  ]
+        //  c:                  [CCCCCC]
+        // the flat_view is as follows,
+        //                      [CCCCCC]
+        let root = Region::init_container_region(8000);
+        let space = AddressSpace::new(root.clone()).unwrap();
+        let listener = TestListener::default();
+        space.register_listener(Box::new(listener.clone())).unwrap();
+
+        let region_b = Region::init_container_region(5000);
+        let region_c = Region::init_io_region(1000, default_ops);
+        region_c.set_ioeventfds(&ioeventfds);
+        region_b.add_subregion(region_c, 1000).unwrap();
+
+        root.add_subregion(region_b, 1000).unwrap();
+
+        assert!(listener.reqs.lock().unwrap().get(1).is_some());
+        assert_eq!(
+            listener.reqs.lock().unwrap().get(1).unwrap().1,
+            AddressRange::new(GuestAddress(2000), 4)
+        );
+    }
+
     #[test]
     fn test_get_ram_info() {
         let root = Region::init_container_region(8000);
diff --git a/address_space/src/region.rs b/address_space/src/region.rs
index d8fbca6..b270ac4 100644
--- a/address_space/src/region.rs
+++ b/address_space/src/region.rs
@@ -378,15 +378,7 @@ impl Region {
 
     /// Return the IoEvent of a `Region`.
     pub fn set_ioeventfds(&self, new_fds: &[RegionIoEventFd]) {
-        let region_evtfds = new_fds
-            .iter()
-            .map(|e| {
-                let mut evt_cloned = e.try_clone().unwrap();
-                evt_cloned.addr_range.base.0 += self.offset().raw_value();
-                evt_cloned
-            })
-            .collect();
-        *self.io_evtfds.lock().unwrap() = region_evtfds;
+        *self.io_evtfds.lock().unwrap() = new_fds.iter().map(|e| e.try_clone().unwrap()).collect();
     }
 
     /// Set the ioeventfds within this Region,
@@ -396,11 +388,7 @@ impl Region {
             .lock()
             .unwrap()
             .iter()
-            .map(|e| {
-                let mut evt_cloned = e.try_clone().unwrap();
-                evt_cloned.addr_range.base.0 += self.offset().raw_value();
-                evt_cloned
-            })
+            .map(|e| e.try_clone().unwrap())
             .collect()
     }
 
-- 
2.27.0

