@@ -4,20 +4,24 @@ use std::collections::HashMap;
4
4
5
5
use displaydoc:: Display ;
6
6
use kvm_bindings:: { KVM_ARM_VCPU_PVTIME_CTRL , KVM_ARM_VCPU_PVTIME_IPA } ;
7
- use log :: { debug , info , warn } ;
7
+ use serde :: { Deserialize , Serialize } ;
8
8
use thiserror:: Error ;
9
9
use vm_memory:: GuestAddress ;
10
10
11
11
use crate :: device_manager:: resources:: ResourceAllocator ;
12
+ use crate :: snapshot:: Persist ;
12
13
13
14
/// 64 bytes due to alignment requirement in 3.1 of https://www.kernel.org/doc/html/v5.8/virt/kvm/devices/vcpu.html#attribute-kvm-arm-vcpu-pvtime-ipa
14
15
pub const STEALTIME_STRUCT_MEM_SIZE : u64 = 64 ;
15
16
16
17
/// Represent PVTime device for ARM
18
+ /// TODO: Decide whether we want to keep the hashmap OR the base IPA
17
19
#[ derive( Debug ) ]
18
20
pub struct PVTime {
19
21
/// Maps vCPU index to IPA location of stolen_time struct as defined in DEN0057A
20
22
steal_time_regions : HashMap < u8 , u64 > ,
23
+ /// The base IPA of the shared memory region
24
+ base_ipa : u64 ,
21
25
}
22
26
23
27
/// Errors associated with PVTime operations
@@ -32,31 +36,42 @@ pub enum PVTimeError {
32
36
}
33
37
34
38
impl PVTime {
35
- /// Creates a new PVTime device by allocating system memory for all vCPUs
36
- pub fn new (
37
- resource_allocator : & mut ResourceAllocator ,
38
- vcpu_count : u8 ,
39
- ) -> Result < Self , PVTimeError > {
40
-
41
- // This returns the IPA of the start of our shared memory region for all vCPUs.
42
- let base_addr: GuestAddress = GuestAddress ( resource_allocator
43
- . allocate_system_memory (
44
- STEALTIME_STRUCT_MEM_SIZE * vcpu_count as u64 ,
45
- 64 ,
46
- vm_allocator:: AllocPolicy :: LastMatch ,
47
- )
48
- . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?) ;
49
-
39
+ /// Create a new PVTime device given a base addr
40
+ /// - Assumes total shared memory region from base addr is already allocated
41
+ fn from_base ( base_addr : GuestAddress , vcpu_count : u8 ) -> Self {
42
+ let base_ipa: u64 = base_addr. 0 ;
50
43
51
44
// Now we need to store the base IPA for each vCPU's steal_time struct.
52
45
let mut steal_time_regions = HashMap :: new ( ) ;
53
46
for i in 0 ..vcpu_count {
54
- let ipa = base_addr . 0 + ( i as u64 * STEALTIME_STRUCT_MEM_SIZE ) ;
47
+ let ipa = base_ipa + ( i as u64 * STEALTIME_STRUCT_MEM_SIZE ) ;
55
48
steal_time_regions. insert ( i, ipa) ;
56
49
}
57
50
58
51
// Return the PVTime device with the steal_time region IPAs mapped to vCPU indices.
59
- Ok ( PVTime { steal_time_regions } )
52
+ PVTime {
53
+ steal_time_regions,
54
+ base_ipa,
55
+ }
56
+ }
57
+
58
+ /// Creates a new PVTime device by allocating new system memory for all vCPUs
59
+ pub fn new (
60
+ resource_allocator : & mut ResourceAllocator ,
61
+ vcpu_count : u8 ,
62
+ ) -> Result < Self , PVTimeError > {
63
+ // This returns the IPA of the start of our shared memory region for all vCPUs.
64
+ let base_ipa: GuestAddress = GuestAddress (
65
+ resource_allocator
66
+ . allocate_system_memory (
67
+ STEALTIME_STRUCT_MEM_SIZE * vcpu_count as u64 ,
68
+ 64 ,
69
+ vm_allocator:: AllocPolicy :: LastMatch ,
70
+ )
71
+ . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?,
72
+ ) ;
73
+
74
+ Ok ( Self :: from_base ( base_ipa, vcpu_count) )
60
75
}
61
76
62
77
/// Register a vCPU with its pre-allocated steal time region
@@ -65,7 +80,6 @@ impl PVTime {
65
80
vcpu_index : u8 ,
66
81
vcpu_fd : & kvm_ioctls:: VcpuFd ,
67
82
) -> Result < ( ) , PVTimeError > {
68
-
69
83
// Get IPA of the steal_time region for this vCPU
70
84
let ipa = self
71
85
. steal_time_regions
@@ -80,15 +94,55 @@ impl PVTime {
80
94
flags : 0 ,
81
95
} ;
82
96
83
- vcpu_fd. set_device_attr ( & vcpu_device_attr ) . map_err ( |err| {
84
- PVTimeError :: DeviceAttribute ( err , true , KVM_ARM_VCPU_PVTIME_CTRL )
85
- } ) ?;
97
+ vcpu_fd
98
+ . set_device_attr ( & vcpu_device_attr )
99
+ . map_err ( |err| PVTimeError :: DeviceAttribute ( err , true , KVM_ARM_VCPU_PVTIME_CTRL ) ) ?;
86
100
87
101
Ok ( ( ) )
88
102
}
89
103
}
90
104
91
- // TODO/Q: Would we be correct in implementing Persist for PVTime? Some sort of persistence is
92
- // needed for snapshot capability. We would only need to store base_addr IPA of shared memory region
93
- // assuming the # of vCPUs is constant across snapshots. Also assuming we are correct that
94
- // kvm_set_device_attr takes an IPA and we get an IPA back from resource_allocator.
105
+ /// Logic to save/restore the state of a PVTime device
106
+ #[ derive( Default , Debug , Clone , Serialize , Deserialize ) ]
107
+ pub struct PVTimeState {
108
+ /// base IPA of the total shared memory region
109
+ pub base_ipa : u64 ,
110
+ }
111
+
112
+ #[ derive( Debug ) ]
113
+ pub struct PVTimeConstructorArgs < ' a > {
114
+ pub resource_allocator : & ' a mut ResourceAllocator ,
115
+ pub vcpu_count : u8 ,
116
+ }
117
+
118
+ impl < ' a > Persist < ' a > for PVTime {
119
+ type State = PVTimeState ;
120
+ type ConstructorArgs = PVTimeConstructorArgs < ' a > ;
121
+ type Error = PVTimeError ;
122
+
123
+ /// Save base IPA of PVTime device for persistence
124
+ fn save ( & self ) -> Self :: State {
125
+ PVTimeState {
126
+ base_ipa : self . base_ipa ,
127
+ }
128
+ }
129
+
130
+ /// Restore state of PVTime device from given base IPA
131
+ fn restore (
132
+ constructor_args : Self :: ConstructorArgs ,
133
+ state : & Self :: State ,
134
+ ) -> std:: result:: Result < Self , PVTimeError > {
135
+ constructor_args
136
+ . resource_allocator
137
+ . allocate_system_memory (
138
+ STEALTIME_STRUCT_MEM_SIZE * constructor_args. vcpu_count as u64 ,
139
+ 64 ,
140
+ vm_allocator:: AllocPolicy :: ExactMatch ( state. base_ipa ) ,
141
+ )
142
+ . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?;
143
+ Ok ( Self :: from_base (
144
+ GuestAddress ( state. base_ipa ) ,
145
+ constructor_args. vcpu_count ,
146
+ ) )
147
+ }
148
+ }
0 commit comments