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