1 // Copyright © 2019 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 //! The internal VMM API for Cloud Hypervisor. 7 //! 8 //! This API is a synchronous, [mpsc](https://doc.rust-lang.org/std/sync/mpsc/) 9 //! based IPC for sending commands to the VMM thread, from other 10 //! Cloud Hypervisor threads. The IPC follows a command-response protocol, i.e. 11 //! each command will receive a response back. 12 //! 13 //! The main Cloud Hypervisor thread creates an API event file descriptor 14 //! to notify the VMM thread about pending API commands, together with an 15 //! API mpsc channel. The former is the IPC control plane, the latter is the 16 //! IPC data plane. 17 //! In order to use the IPC, a Cloud Hypervisor thread needs to have a clone 18 //! of both the API event file descriptor and the channel Sender. Then it must 19 //! go through the following steps: 20 //! 21 //! 1. The thread creates an mpsc channel for receiving the command response. 22 //! 2. The thread sends an ApiRequest to the Sender endpoint. The ApiRequest 23 //! contains the response channel Sender, for the VMM API server to be able 24 //! to send the response back. 25 //! 3. The thread writes to the API event file descriptor to notify the VMM 26 //! API server about a pending command. 27 //! 4. The thread reads the response back from the VMM API server, from the 28 //! response channel Receiver. 29 //! 5. The thread handles the response and forwards potential errors. 30 31 #[cfg(feature = "dbus_api")] 32 pub mod dbus; 33 pub mod http; 34 35 #[cfg(feature = "dbus_api")] 36 pub use self::dbus::start_dbus_thread; 37 pub use self::http::start_http_fd_thread; 38 pub use self::http::start_http_path_thread; 39 40 use crate::config::{ 41 DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, UserDeviceConfig, 42 VdpaConfig, VmConfig, VsockConfig, 43 }; 44 use crate::device_tree::DeviceTree; 45 use crate::vm::{Error as VmError, VmState}; 46 use micro_http::Body; 47 use serde::{Deserialize, Serialize}; 48 use std::io; 49 use std::sync::mpsc::{channel, RecvError, SendError, Sender}; 50 use std::sync::{Arc, Mutex}; 51 use vm_migration::MigratableError; 52 use vmm_sys_util::eventfd::EventFd; 53 54 /// API errors are sent back from the VMM API server through the ApiResponse. 55 #[derive(Debug)] 56 pub enum ApiError { 57 /// Cannot write to EventFd. 58 EventFdWrite(io::Error), 59 60 /// API request send error 61 RequestSend(SendError<ApiRequest>), 62 63 /// Wrong response payload type 64 ResponsePayloadType, 65 66 /// API response receive error 67 ResponseRecv(RecvError), 68 69 /// The VM could not boot. 70 VmBoot(VmError), 71 72 /// The VM could not be created. 73 VmCreate(VmError), 74 75 /// The VM could not be deleted. 76 VmDelete(VmError), 77 78 /// The VM info is not available. 79 VmInfo(VmError), 80 81 /// The VM could not be paused. 82 VmPause(VmError), 83 84 /// The VM could not resume. 85 VmResume(VmError), 86 87 /// The VM is not booted. 88 VmNotBooted, 89 90 /// The VM is not created. 91 VmNotCreated, 92 93 /// The VM could not shutdown. 94 VmShutdown(VmError), 95 96 /// The VM could not reboot. 97 VmReboot(VmError), 98 99 /// The VM could not be snapshotted. 100 VmSnapshot(VmError), 101 102 /// The VM could not restored. 103 VmRestore(VmError), 104 105 /// The VM could not be coredumped. 106 VmCoredump(VmError), 107 108 /// The VMM could not shutdown. 109 VmmShutdown(VmError), 110 111 /// The VM could not be resized 112 VmResize(VmError), 113 114 /// The memory zone could not be resized. 115 VmResizeZone(VmError), 116 117 /// The device could not be added to the VM. 118 VmAddDevice(VmError), 119 120 /// The user device could not be added to the VM. 121 VmAddUserDevice(VmError), 122 123 /// The device could not be removed from the VM. 124 VmRemoveDevice(VmError), 125 126 /// Cannot create seccomp filter 127 CreateSeccompFilter(seccompiler::Error), 128 129 /// Cannot apply seccomp filter 130 ApplySeccompFilter(seccompiler::Error), 131 132 /// The disk could not be added to the VM. 133 VmAddDisk(VmError), 134 135 /// The fs could not be added to the VM. 136 VmAddFs(VmError), 137 138 /// The pmem device could not be added to the VM. 139 VmAddPmem(VmError), 140 141 /// The network device could not be added to the VM. 142 VmAddNet(VmError), 143 144 /// The vDPA device could not be added to the VM. 145 VmAddVdpa(VmError), 146 147 /// The vsock device could not be added to the VM. 148 VmAddVsock(VmError), 149 150 /// Error starting migration receiever 151 VmReceiveMigration(MigratableError), 152 153 /// Error starting migration sender 154 VmSendMigration(MigratableError), 155 156 /// Error triggering power button 157 VmPowerButton(VmError), 158 } 159 pub type ApiResult<T> = std::result::Result<T, ApiError>; 160 161 #[derive(Clone, Deserialize, Serialize)] 162 pub struct VmInfo { 163 pub config: Arc<Mutex<VmConfig>>, 164 pub state: VmState, 165 pub memory_actual_size: u64, 166 pub device_tree: Option<Arc<Mutex<DeviceTree>>>, 167 } 168 169 #[derive(Clone, Deserialize, Serialize)] 170 pub struct VmmPingResponse { 171 pub build_version: String, 172 pub version: String, 173 pub pid: i64, 174 pub features: Vec<String>, 175 } 176 177 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 178 pub struct VmResizeData { 179 pub desired_vcpus: Option<u8>, 180 pub desired_ram: Option<u64>, 181 pub desired_balloon: Option<u64>, 182 } 183 184 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 185 pub struct VmResizeZoneData { 186 pub id: String, 187 pub desired_ram: u64, 188 } 189 190 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 191 pub struct VmRemoveDeviceData { 192 pub id: String, 193 } 194 195 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 196 pub struct VmSnapshotConfig { 197 /// The snapshot destination URL 198 pub destination_url: String, 199 } 200 201 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 202 pub struct VmCoredumpData { 203 /// The coredump destination file 204 pub destination_url: String, 205 } 206 207 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 208 pub struct VmReceiveMigrationData { 209 /// URL for the reception of migration state 210 pub receiver_url: String, 211 } 212 213 #[derive(Clone, Deserialize, Serialize, Default, Debug)] 214 pub struct VmSendMigrationData { 215 /// URL to migrate the VM to 216 pub destination_url: String, 217 /// Send memory across socket without copying 218 #[serde(default)] 219 pub local: bool, 220 } 221 222 pub enum ApiResponsePayload { 223 /// No data is sent on the channel. 224 Empty, 225 226 /// Virtual machine information 227 VmInfo(VmInfo), 228 229 /// Vmm ping response 230 VmmPing(VmmPingResponse), 231 232 /// Vm action response 233 VmAction(Option<Vec<u8>>), 234 } 235 236 /// This is the response sent by the VMM API server through the mpsc channel. 237 pub type ApiResponse = std::result::Result<ApiResponsePayload, ApiError>; 238 239 #[derive(Debug)] 240 pub enum ApiRequest { 241 /// Create the virtual machine. This request payload is a VM configuration 242 /// (VmConfig). 243 /// If the VMM API server could not create the VM, it will send a VmCreate 244 /// error back. 245 VmCreate(Arc<Mutex<VmConfig>>, Sender<ApiResponse>), 246 247 /// Boot the previously created virtual machine. 248 /// If the VM was not previously created, the VMM API server will send a 249 /// VmBoot error back. 250 VmBoot(Sender<ApiResponse>), 251 252 /// Delete the previously created virtual machine. 253 /// If the VM was not previously created, the VMM API server will send a 254 /// VmDelete error back. 255 /// If the VM is booted, we shut it down first. 256 VmDelete(Sender<ApiResponse>), 257 258 /// Request the VM information. 259 VmInfo(Sender<ApiResponse>), 260 261 /// Request the VMM API server status 262 VmmPing(Sender<ApiResponse>), 263 264 /// Pause a VM. 265 VmPause(Sender<ApiResponse>), 266 267 /// Resume a VM. 268 VmResume(Sender<ApiResponse>), 269 270 /// Get counters for a VM. 271 VmCounters(Sender<ApiResponse>), 272 273 /// Shut the previously booted virtual machine down. 274 /// If the VM was not previously booted or created, the VMM API server 275 /// will send a VmShutdown error back. 276 VmShutdown(Sender<ApiResponse>), 277 278 /// Reboot the previously booted virtual machine. 279 /// If the VM was not previously booted or created, the VMM API server 280 /// will send a VmReboot error back. 281 VmReboot(Sender<ApiResponse>), 282 283 /// Shut the VMM down. 284 /// This will shutdown and delete the current VM, if any, and then exit the 285 /// VMM process. 286 VmmShutdown(Sender<ApiResponse>), 287 288 /// Resize the VM. 289 VmResize(Arc<VmResizeData>, Sender<ApiResponse>), 290 291 /// Resize the memory zone. 292 VmResizeZone(Arc<VmResizeZoneData>, Sender<ApiResponse>), 293 294 /// Add a device to the VM. 295 VmAddDevice(Arc<DeviceConfig>, Sender<ApiResponse>), 296 297 /// Add a user device to the VM. 298 VmAddUserDevice(Arc<UserDeviceConfig>, Sender<ApiResponse>), 299 300 /// Remove a device from the VM. 301 VmRemoveDevice(Arc<VmRemoveDeviceData>, Sender<ApiResponse>), 302 303 /// Add a disk to the VM. 304 VmAddDisk(Arc<DiskConfig>, Sender<ApiResponse>), 305 306 /// Add a fs to the VM. 307 VmAddFs(Arc<FsConfig>, Sender<ApiResponse>), 308 309 /// Add a pmem device to the VM. 310 VmAddPmem(Arc<PmemConfig>, Sender<ApiResponse>), 311 312 /// Add a network device to the VM. 313 VmAddNet(Arc<NetConfig>, Sender<ApiResponse>), 314 315 /// Add a vDPA device to the VM. 316 VmAddVdpa(Arc<VdpaConfig>, Sender<ApiResponse>), 317 318 /// Add a vsock device to the VM. 319 VmAddVsock(Arc<VsockConfig>, Sender<ApiResponse>), 320 321 /// Take a VM snapshot 322 VmSnapshot(Arc<VmSnapshotConfig>, Sender<ApiResponse>), 323 324 /// Restore from a VM snapshot 325 VmRestore(Arc<RestoreConfig>, Sender<ApiResponse>), 326 327 /// Take a VM coredump 328 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] 329 VmCoredump(Arc<VmCoredumpData>, Sender<ApiResponse>), 330 331 /// Incoming migration 332 VmReceiveMigration(Arc<VmReceiveMigrationData>, Sender<ApiResponse>), 333 334 /// Outgoing migration 335 VmSendMigration(Arc<VmSendMigrationData>, Sender<ApiResponse>), 336 337 // Trigger power button 338 VmPowerButton(Sender<ApiResponse>), 339 } 340 341 pub fn vm_create( 342 api_evt: EventFd, 343 api_sender: Sender<ApiRequest>, 344 config: Arc<Mutex<VmConfig>>, 345 ) -> ApiResult<()> { 346 let (response_sender, response_receiver) = channel(); 347 348 // Send the VM creation request. 349 api_sender 350 .send(ApiRequest::VmCreate(config, response_sender)) 351 .map_err(ApiError::RequestSend)?; 352 api_evt.write(1).map_err(ApiError::EventFdWrite)?; 353 354 response_receiver.recv().map_err(ApiError::ResponseRecv)??; 355 356 Ok(()) 357 } 358 359 /// Represents a VM related action. 360 /// This is mostly used to factorize code between VM routines 361 /// that only differ by the IPC command they send. 362 pub enum VmAction { 363 /// Boot a VM 364 Boot, 365 366 /// Delete a VM 367 Delete, 368 369 /// Shut a VM down 370 Shutdown, 371 372 /// Reboot a VM 373 Reboot, 374 375 /// Pause a VM 376 Pause, 377 378 /// Resume a VM 379 Resume, 380 381 /// Return VM counters 382 Counters, 383 384 /// Add VFIO device 385 AddDevice(Arc<DeviceConfig>), 386 387 /// Add disk 388 AddDisk(Arc<DiskConfig>), 389 390 /// Add filesystem 391 AddFs(Arc<FsConfig>), 392 393 /// Add pmem 394 AddPmem(Arc<PmemConfig>), 395 396 /// Add network 397 AddNet(Arc<NetConfig>), 398 399 /// Add vdpa 400 AddVdpa(Arc<VdpaConfig>), 401 402 /// Add vsock 403 AddVsock(Arc<VsockConfig>), 404 405 /// Add user device 406 AddUserDevice(Arc<UserDeviceConfig>), 407 408 /// Remove VFIO device 409 RemoveDevice(Arc<VmRemoveDeviceData>), 410 411 /// Resize VM 412 Resize(Arc<VmResizeData>), 413 414 /// Resize memory zone 415 ResizeZone(Arc<VmResizeZoneData>), 416 417 /// Restore VM 418 Restore(Arc<RestoreConfig>), 419 420 /// Snapshot VM 421 Snapshot(Arc<VmSnapshotConfig>), 422 423 /// Coredump VM 424 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] 425 Coredump(Arc<VmCoredumpData>), 426 427 /// Incoming migration 428 ReceiveMigration(Arc<VmReceiveMigrationData>), 429 430 /// Outgoing migration 431 SendMigration(Arc<VmSendMigrationData>), 432 433 /// Power Button for clean shutdown 434 PowerButton, 435 } 436 437 fn vm_action( 438 api_evt: EventFd, 439 api_sender: Sender<ApiRequest>, 440 action: VmAction, 441 ) -> ApiResult<Option<Body>> { 442 let (response_sender, response_receiver) = channel(); 443 444 use VmAction::*; 445 let request = match action { 446 Boot => ApiRequest::VmBoot(response_sender), 447 Delete => ApiRequest::VmDelete(response_sender), 448 Shutdown => ApiRequest::VmShutdown(response_sender), 449 Reboot => ApiRequest::VmReboot(response_sender), 450 Pause => ApiRequest::VmPause(response_sender), 451 Resume => ApiRequest::VmResume(response_sender), 452 Counters => ApiRequest::VmCounters(response_sender), 453 AddDevice(v) => ApiRequest::VmAddDevice(v, response_sender), 454 AddDisk(v) => ApiRequest::VmAddDisk(v, response_sender), 455 AddFs(v) => ApiRequest::VmAddFs(v, response_sender), 456 AddPmem(v) => ApiRequest::VmAddPmem(v, response_sender), 457 AddNet(v) => ApiRequest::VmAddNet(v, response_sender), 458 AddVdpa(v) => ApiRequest::VmAddVdpa(v, response_sender), 459 AddVsock(v) => ApiRequest::VmAddVsock(v, response_sender), 460 AddUserDevice(v) => ApiRequest::VmAddUserDevice(v, response_sender), 461 RemoveDevice(v) => ApiRequest::VmRemoveDevice(v, response_sender), 462 Resize(v) => ApiRequest::VmResize(v, response_sender), 463 ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender), 464 Restore(v) => ApiRequest::VmRestore(v, response_sender), 465 Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender), 466 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] 467 Coredump(v) => ApiRequest::VmCoredump(v, response_sender), 468 ReceiveMigration(v) => ApiRequest::VmReceiveMigration(v, response_sender), 469 SendMigration(v) => ApiRequest::VmSendMigration(v, response_sender), 470 PowerButton => ApiRequest::VmPowerButton(response_sender), 471 }; 472 473 // Send the VM request. 474 api_sender.send(request).map_err(ApiError::RequestSend)?; 475 api_evt.write(1).map_err(ApiError::EventFdWrite)?; 476 477 let body = match response_receiver.recv().map_err(ApiError::ResponseRecv)?? { 478 ApiResponsePayload::VmAction(response) => response.map(Body::new), 479 ApiResponsePayload::Empty => None, 480 _ => return Err(ApiError::ResponsePayloadType), 481 }; 482 483 Ok(body) 484 } 485 486 pub fn vm_boot(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 487 vm_action(api_evt, api_sender, VmAction::Boot) 488 } 489 490 pub fn vm_delete(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 491 vm_action(api_evt, api_sender, VmAction::Delete) 492 } 493 494 pub fn vm_shutdown(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 495 vm_action(api_evt, api_sender, VmAction::Shutdown) 496 } 497 498 pub fn vm_reboot(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 499 vm_action(api_evt, api_sender, VmAction::Reboot) 500 } 501 502 pub fn vm_pause(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 503 vm_action(api_evt, api_sender, VmAction::Pause) 504 } 505 506 pub fn vm_resume(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 507 vm_action(api_evt, api_sender, VmAction::Resume) 508 } 509 510 pub fn vm_counters(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<Option<Body>> { 511 vm_action(api_evt, api_sender, VmAction::Counters) 512 } 513 514 pub fn vm_power_button( 515 api_evt: EventFd, 516 api_sender: Sender<ApiRequest>, 517 ) -> ApiResult<Option<Body>> { 518 vm_action(api_evt, api_sender, VmAction::PowerButton) 519 } 520 521 pub fn vm_receive_migration( 522 api_evt: EventFd, 523 api_sender: Sender<ApiRequest>, 524 data: Arc<VmReceiveMigrationData>, 525 ) -> ApiResult<Option<Body>> { 526 vm_action(api_evt, api_sender, VmAction::ReceiveMigration(data)) 527 } 528 529 pub fn vm_send_migration( 530 api_evt: EventFd, 531 api_sender: Sender<ApiRequest>, 532 data: Arc<VmSendMigrationData>, 533 ) -> ApiResult<Option<Body>> { 534 vm_action(api_evt, api_sender, VmAction::SendMigration(data)) 535 } 536 537 pub fn vm_snapshot( 538 api_evt: EventFd, 539 api_sender: Sender<ApiRequest>, 540 data: Arc<VmSnapshotConfig>, 541 ) -> ApiResult<Option<Body>> { 542 vm_action(api_evt, api_sender, VmAction::Snapshot(data)) 543 } 544 545 pub fn vm_restore( 546 api_evt: EventFd, 547 api_sender: Sender<ApiRequest>, 548 data: Arc<RestoreConfig>, 549 ) -> ApiResult<Option<Body>> { 550 vm_action(api_evt, api_sender, VmAction::Restore(data)) 551 } 552 553 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] 554 pub fn vm_coredump( 555 api_evt: EventFd, 556 api_sender: Sender<ApiRequest>, 557 data: Arc<VmCoredumpData>, 558 ) -> ApiResult<Option<Body>> { 559 vm_action(api_evt, api_sender, VmAction::Coredump(data)) 560 } 561 562 pub fn vm_info(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<VmInfo> { 563 let (response_sender, response_receiver) = channel(); 564 565 // Send the VM request. 566 api_sender 567 .send(ApiRequest::VmInfo(response_sender)) 568 .map_err(ApiError::RequestSend)?; 569 api_evt.write(1).map_err(ApiError::EventFdWrite)?; 570 571 let vm_info = response_receiver.recv().map_err(ApiError::ResponseRecv)??; 572 573 match vm_info { 574 ApiResponsePayload::VmInfo(info) => Ok(info), 575 _ => Err(ApiError::ResponsePayloadType), 576 } 577 } 578 579 pub fn vmm_ping(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<VmmPingResponse> { 580 let (response_sender, response_receiver) = channel(); 581 582 api_sender 583 .send(ApiRequest::VmmPing(response_sender)) 584 .map_err(ApiError::RequestSend)?; 585 api_evt.write(1).map_err(ApiError::EventFdWrite)?; 586 587 let vmm_pong = response_receiver.recv().map_err(ApiError::ResponseRecv)??; 588 589 match vmm_pong { 590 ApiResponsePayload::VmmPing(pong) => Ok(pong), 591 _ => Err(ApiError::ResponsePayloadType), 592 } 593 } 594 595 pub fn vmm_shutdown(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResult<()> { 596 let (response_sender, response_receiver) = channel(); 597 598 // Send the VMM shutdown request. 599 api_sender 600 .send(ApiRequest::VmmShutdown(response_sender)) 601 .map_err(ApiError::RequestSend)?; 602 api_evt.write(1).map_err(ApiError::EventFdWrite)?; 603 604 response_receiver.recv().map_err(ApiError::ResponseRecv)??; 605 606 Ok(()) 607 } 608 609 pub fn vm_resize( 610 api_evt: EventFd, 611 api_sender: Sender<ApiRequest>, 612 data: Arc<VmResizeData>, 613 ) -> ApiResult<Option<Body>> { 614 vm_action(api_evt, api_sender, VmAction::Resize(data)) 615 } 616 617 pub fn vm_resize_zone( 618 api_evt: EventFd, 619 api_sender: Sender<ApiRequest>, 620 data: Arc<VmResizeZoneData>, 621 ) -> ApiResult<Option<Body>> { 622 vm_action(api_evt, api_sender, VmAction::ResizeZone(data)) 623 } 624 625 pub fn vm_add_device( 626 api_evt: EventFd, 627 api_sender: Sender<ApiRequest>, 628 data: Arc<DeviceConfig>, 629 ) -> ApiResult<Option<Body>> { 630 vm_action(api_evt, api_sender, VmAction::AddDevice(data)) 631 } 632 633 pub fn vm_add_user_device( 634 api_evt: EventFd, 635 api_sender: Sender<ApiRequest>, 636 data: Arc<UserDeviceConfig>, 637 ) -> ApiResult<Option<Body>> { 638 vm_action(api_evt, api_sender, VmAction::AddUserDevice(data)) 639 } 640 641 pub fn vm_remove_device( 642 api_evt: EventFd, 643 api_sender: Sender<ApiRequest>, 644 data: Arc<VmRemoveDeviceData>, 645 ) -> ApiResult<Option<Body>> { 646 vm_action(api_evt, api_sender, VmAction::RemoveDevice(data)) 647 } 648 649 pub fn vm_add_disk( 650 api_evt: EventFd, 651 api_sender: Sender<ApiRequest>, 652 data: Arc<DiskConfig>, 653 ) -> ApiResult<Option<Body>> { 654 vm_action(api_evt, api_sender, VmAction::AddDisk(data)) 655 } 656 657 pub fn vm_add_fs( 658 api_evt: EventFd, 659 api_sender: Sender<ApiRequest>, 660 data: Arc<FsConfig>, 661 ) -> ApiResult<Option<Body>> { 662 vm_action(api_evt, api_sender, VmAction::AddFs(data)) 663 } 664 665 pub fn vm_add_pmem( 666 api_evt: EventFd, 667 api_sender: Sender<ApiRequest>, 668 data: Arc<PmemConfig>, 669 ) -> ApiResult<Option<Body>> { 670 vm_action(api_evt, api_sender, VmAction::AddPmem(data)) 671 } 672 673 pub fn vm_add_net( 674 api_evt: EventFd, 675 api_sender: Sender<ApiRequest>, 676 data: Arc<NetConfig>, 677 ) -> ApiResult<Option<Body>> { 678 vm_action(api_evt, api_sender, VmAction::AddNet(data)) 679 } 680 681 pub fn vm_add_vdpa( 682 api_evt: EventFd, 683 api_sender: Sender<ApiRequest>, 684 data: Arc<VdpaConfig>, 685 ) -> ApiResult<Option<Body>> { 686 vm_action(api_evt, api_sender, VmAction::AddVdpa(data)) 687 } 688 689 pub fn vm_add_vsock( 690 api_evt: EventFd, 691 api_sender: Sender<ApiRequest>, 692 data: Arc<VsockConfig>, 693 ) -> ApiResult<Option<Body>> { 694 vm_action(api_evt, api_sender, VmAction::AddVsock(data)) 695 } 696