@@ -136,8 +136,8 @@ pub enum Error {
136136 EpollFd ( io:: Error ) ,
137137 /// Cannot read from an Event file descriptor.
138138 EventFd ( std:: io:: Error ) ,
139- /// Describes a logical problem .
140- GeneralFailure , // TODO: there are some cases in which this error should be replaced.
139+ /// An event arrived for a device, but the dispatcher can't find the event (epoll) handler .
140+ DeviceEventHandlerNotFound ,
141141 /// Cannot open /dev/kvm. Either the host does not have KVM or Firecracker does not have
142142 /// permission to open the file descriptor.
143143 Kvm ( io:: Error ) ,
@@ -161,11 +161,21 @@ impl std::fmt::Debug for Error {
161161 use self :: Error :: * ;
162162
163163 match self {
164- // This only implements Debug for the most common Firecracker error
165- // which is the one coming from KVM at the time.
166- // TODO: implement debug for the other errors as well.
164+ ApiChannel => write ! ( f, "ApiChannel: error receiving data from the API server" ) ,
165+ CreateLegacyDevice ( e) => write ! ( f, "Error creating legacy device: {:?}" , e) ,
166+ EpollFd ( e) => write ! ( f, "Epoll fd error: {:?}" , e) ,
167+ EventFd ( e) => write ! ( f, "Event fd error: {}" , e. to_string( ) ) ,
168+ DeviceEventHandlerNotFound => write ! (
169+ f,
170+ "Device event handler not found. This might point to a guest device driver issue."
171+ ) ,
167172 Kvm ( ref os_err) => write ! ( f, "Cannot open /dev/kvm. Error: {}" , os_err. to_string( ) ) ,
168- _ => write ! ( f, "{:?}" , self ) ,
173+ KvmApiVersion ( ver) => write ! ( f, "Bad KVM API version: {}" , ver) ,
174+ KvmCap ( cap) => write ! ( f, "Missing KVM capability: {:?}" , cap) ,
175+ Poll ( e) => write ! ( f, "Epoll wait failed: {}" , e. to_string( ) ) ,
176+ Serial ( e) => write ! ( f, "Error writing to the serial console: {:?}" , e) ,
177+ TimerFd ( e) => write ! ( f, "Error creating timer fd: {}" , e. to_string( ) ) ,
178+ Vm ( e) => write ! ( f, "Error opening VM fd: {:?}" , e) ,
169179 }
170180 }
171181}
@@ -250,6 +260,7 @@ impl Display for VmmActionError {
250260
251261/// This enum represents the public interface of the VMM. Each action contains various
252262/// bits of information (ids, paths, etc.), together with an OutcomeSender, which is always present.
263+ #[ derive( Debug ) ]
253264pub enum VmmAction {
254265 /// Configure the boot source of the microVM using as input the `ConfigureBootSource`. This
255266 /// action can only be called before the microVM has booted. The response is sent using the
@@ -543,7 +554,7 @@ impl EpollContext {
543554 let received = maybe
544555 . receiver
545556 . try_recv ( )
546- . map_err ( |_| Error :: GeneralFailure ) ?;
557+ . map_err ( |_| Error :: DeviceEventHandlerNotFound ) ?;
547558 Ok ( maybe. handler . get_or_insert ( received) . as_mut ( ) )
548559 }
549560 }
@@ -1946,6 +1957,10 @@ impl PartialEq for VmmAction {
19461957 & VmmAction :: InsertNetworkDevice ( ref net_dev, _) ,
19471958 & VmmAction :: InsertNetworkDevice ( ref other_net_dev, _) ,
19481959 ) => net_dev == other_net_dev,
1960+ (
1961+ & VmmAction :: UpdateNetworkInterface ( ref net_dev, _) ,
1962+ & VmmAction :: UpdateNetworkInterface ( ref other_net_dev, _) ,
1963+ ) => net_dev == other_net_dev,
19491964 (
19501965 & VmmAction :: RescanBlockDevice ( ref req, _) ,
19511966 & VmmAction :: RescanBlockDevice ( ref other_req, _) ,
@@ -2019,6 +2034,7 @@ mod tests {
20192034 use devices:: virtio:: ActivateResult ;
20202035 use net_util:: MacAddr ;
20212036 use vmm_config:: machine_config:: CpuFeaturesTemplate ;
2037+ use vmm_config:: { RateLimiterConfig , TokenBucketConfig } ;
20222038
20232039 impl Vmm {
20242040 fn get_kernel_cmdline_str ( & self ) -> & str {
@@ -2312,6 +2328,98 @@ mod tests {
23122328 assert ! ( vmm. insert_net_device( network_interface) . is_err( ) ) ;
23132329 }
23142330
2331+ #[ test]
2332+ fn test_update_net_device ( ) {
2333+ let mut vmm = create_vmm_object ( InstanceState :: Uninitialized ) ;
2334+
2335+ let tbc_1mtps = TokenBucketConfig {
2336+ size : 1024 * 1024 ,
2337+ one_time_burst : None ,
2338+ refill_time : 1000 ,
2339+ } ;
2340+ let tbc_2mtps = TokenBucketConfig {
2341+ size : 2 * 1024 * 1024 ,
2342+ one_time_burst : None ,
2343+ refill_time : 1000 ,
2344+ } ;
2345+
2346+ vmm. insert_net_device ( NetworkInterfaceConfig {
2347+ iface_id : String :: from ( "1" ) ,
2348+ host_dev_name : String :: from ( "hostname5" ) ,
2349+ guest_mac : None ,
2350+ rx_rate_limiter : Some ( RateLimiterConfig {
2351+ bandwidth : Some ( tbc_1mtps) ,
2352+ ops : None ,
2353+ } ) ,
2354+ tx_rate_limiter : None ,
2355+ allow_mmds_requests : false ,
2356+ tap : None ,
2357+ } )
2358+ . unwrap ( ) ;
2359+
2360+ vmm. update_net_device ( NetworkInterfaceUpdateConfig {
2361+ iface_id : "1" . to_string ( ) ,
2362+ rx_rate_limiter : Some ( RateLimiterConfig {
2363+ bandwidth : None ,
2364+ ops : Some ( tbc_2mtps) ,
2365+ } ) ,
2366+ tx_rate_limiter : Some ( RateLimiterConfig {
2367+ bandwidth : None ,
2368+ ops : Some ( tbc_2mtps) ,
2369+ } ) ,
2370+ } )
2371+ . unwrap ( ) ;
2372+
2373+ {
2374+ let nic_1: & mut NetworkInterfaceConfig =
2375+ vmm. network_interface_configs . iter_mut ( ) . next ( ) . unwrap ( ) ;
2376+ // The RX bandwidth should be unaffected.
2377+ assert_eq ! ( nic_1. rx_rate_limiter. unwrap( ) . bandwidth. unwrap( ) , tbc_1mtps) ;
2378+ // The RX ops should be set to 2mtps.
2379+ assert_eq ! ( nic_1. rx_rate_limiter. unwrap( ) . ops. unwrap( ) , tbc_2mtps) ;
2380+ // The TX bandwith should be unlimited (unaffected).
2381+ assert_eq ! ( nic_1. tx_rate_limiter. unwrap( ) . bandwidth, None ) ;
2382+ // The TX ops should be set to 2mtps.
2383+ assert_eq ! ( nic_1. tx_rate_limiter. unwrap( ) . ops. unwrap( ) , tbc_2mtps) ;
2384+ }
2385+
2386+ vmm. init_guest_memory ( ) . unwrap ( ) ;
2387+ vmm. default_kernel_config ( ) ;
2388+ let guest_mem = vmm. guest_memory . clone ( ) . unwrap ( ) ;
2389+ let mut device_manager =
2390+ MMIODeviceManager :: new ( guest_mem. clone ( ) , arch:: get_reserved_mem_addr ( ) as u64 ) ;
2391+ vmm. attach_net_devices ( & mut device_manager) . unwrap ( ) ;
2392+ vmm. set_instance_state ( InstanceState :: Running ) ;
2393+
2394+ // The update should fail before device activation.
2395+ assert ! ( vmm
2396+ . update_net_device( NetworkInterfaceUpdateConfig {
2397+ iface_id: "1" . to_string( ) ,
2398+ rx_rate_limiter: None ,
2399+ tx_rate_limiter: None ,
2400+ } )
2401+ . is_err( ) ) ;
2402+
2403+ // Fake device activation by explicitly setting a dummy epoll handler.
2404+ vmm. epoll_context . device_handlers [ 0 ] . handler = Some ( Box :: new ( DummyEpollHandler {
2405+ evt : None ,
2406+ flags : None ,
2407+ payload : None ,
2408+ } ) ) ;
2409+ vmm. update_net_device ( NetworkInterfaceUpdateConfig {
2410+ iface_id : "1" . to_string ( ) ,
2411+ rx_rate_limiter : Some ( RateLimiterConfig {
2412+ bandwidth : Some ( tbc_2mtps) ,
2413+ ops : None ,
2414+ } ) ,
2415+ tx_rate_limiter : Some ( RateLimiterConfig {
2416+ bandwidth : Some ( tbc_1mtps) ,
2417+ ops : None ,
2418+ } ) ,
2419+ } )
2420+ . unwrap ( ) ;
2421+ }
2422+
23152423 #[ test]
23162424 fn test_machine_configuration ( ) {
23172425 let mut vmm = create_vmm_object ( InstanceState :: Uninitialized ) ;
0 commit comments