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