xref: /cloud-hypervisor/vmm/src/api/mod.rs (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
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