qemu_command_builder/
accel.rs

1use std::path::PathBuf;
2
3use bon::Builder;
4
5use crate::common::*;
6use crate::to_command::{ToArg, ToCommand};
7
8#[derive(Default)]
9pub enum OnOffSplit {
10    #[default]
11    On,
12    Off,
13    Split,
14}
15
16impl ToArg for OnOffSplit {
17    fn to_arg(&self) -> &str {
18        match self {
19            OnOffSplit::On => "on",
20            OnOffSplit::Off => "off",
21            OnOffSplit::Split => "split",
22        }
23    }
24}
25
26pub enum TCGThreadType {
27    Single,
28    Multi,
29}
30
31impl ToArg for TCGThreadType {
32    fn to_arg(&self) -> &str {
33        match self {
34            TCGThreadType::Single => "single",
35            TCGThreadType::Multi => "multi",
36        }
37    }
38}
39
40pub enum NotifyVMExitType {
41    Run,
42    InternalError,
43    Disable,
44    NotifyWindow(usize),
45}
46
47/// This is used to enable an accelerator. Depending on the target
48/// architecture, kvm, xen, hvf, nvmm, whpx or tcg can be available. By
49/// default, tcg is used. If there is more than one accelerator
50/// specified, the next one is used if the previous one fails to
51/// initialize.
52#[derive(Default, Builder)]
53pub struct Accel {
54    accel_type: AccelType,
55
56    /// When Xen is in use, this option controls whether Intel
57    /// integrated graphics devices can be passed through to the guest
58    /// (default=off)
59    igd_passthru: Option<OnOffDefaultOff>,
60
61    /// Controls KVM in-kernel irqchip support. The default is full
62    /// acceleration of the interrupt controllers. On x86, split irqchip
63    /// reduces the kernel attack surface, at a performance cost for
64    /// non-MSI interrupts. Disabling the in-kernel irqchip completely
65    /// is not recommended except for debugging purposes.
66    kernel_irqchip: Option<OnOffSplit>,
67
68    /// Defines the size of the KVM shadow MMU.
69    kvm_shadow_mem: Option<usize>, // TODO convert to a byte type
70
71    /// Makes the TCG accelerator put only one guest instruction into
72    /// each translation block. This slows down emulation a lot, but
73    /// can be useful in some situations, such as when trying to analyse
74    /// the logs produced by the ``-d`` option.
75    one_insn_per_tb: Option<OnOff>,
76
77    /// Controls the use of split w^x mapping for the TCG code generation
78    /// buffer. Some operating systems require this to be enabled, and in
79    /// such a case this will default on. On other operating systems, this
80    /// will default off, but one may enable this for testing or debugging.
81    split_wx: Option<OnOff>,
82
83    /// Controls the size (in MiB) of the TCG translation block cache.
84    tb_size: Option<usize>, // TODO convert to a byte type
85
86    /// When the KVM accelerator is used, it controls the size of the per-vCPU
87    /// dirty page ring buffer (number of entries for each vCPU). It should
88    /// be a value that is power of two, and it should be 1024 or bigger (but
89    /// still less than the maximum value that the kernel supports).  4096
90    /// could be a good initial value if you have no idea which is the best.
91    /// Set this value to 0 to disable the feature.  By default, this feature
92    /// is disabled (dirty-ring-size=0).  When enabled, KVM will instead
93    /// record dirty pages in a bitmap.
94    dirty_ring_size: Option<usize>,
95
96    /// KVM implements dirty page logging at the PAGE_SIZE granularity and
97    /// enabling dirty-logging on a huge-page requires breaking it into
98    /// PAGE_SIZE pages in the first place. KVM on ARM does this splitting
99    /// lazily by default. There are performance benefits in doing huge-page
100    /// split eagerly, especially in situations where TLBI costs associated
101    /// with break-before-make sequences are considerable and also if guest
102    /// workloads are read intensive. The size here specifies how many pages
103    /// to break at a time and needs to be a valid block size which is
104    /// 1GB/2MB/4KB, 32MB/16KB and 512MB/64KB for 4KB/16KB/64KB PAGE_SIZE
105    /// respectively. Be wary of specifying a higher size as it will have an
106    /// impact on the memory. By default, this feature is disabled
107    /// (eager-split-size=0).
108    eager_split_size: Option<usize>,
109
110    /// Enables or disables notify VM exit support on x86 host and specify
111    /// the corresponding notify window to trigger the VM exit if enabled.
112    /// ``run`` option enables the feature. It does nothing and continue
113    /// if the exit happens. ``internal-error`` option enables the feature.
114    /// It raises a internal error. ``disable`` option doesn't enable the feature.
115    /// This feature can mitigate the CPU stuck issue due to event windows don't
116    /// open up for a specified of time (i.e. notify-window).
117    /// Default: notify-vmexit=run,notify-window=0.
118    notify_vmexit: Option<Vec<NotifyVMExitType>>,
119
120    /// Enable single or multi-threaded TCG
121    thread: Option<TCGThreadType>,
122
123    /// Sets the path to the KVM device node. Defaults to ``/dev/kvm``. This
124    /// option can be used to pass the KVM device to use via a file descriptor
125    /// by setting the value to ``/dev/fdset/NN``.
126    device: Option<PathBuf>,
127}
128
129impl ToCommand for Accel {
130    fn to_command(&self) -> Vec<String> {
131        let mut cmd = vec![];
132        cmd.push("-accel".to_string());
133
134        let mut args = vec![self.accel_type.to_arg().to_string()];
135
136        if let Some(igd_passthru) = &self.igd_passthru {
137            args.push(format!("igd-passthru={}", igd_passthru.to_arg()));
138        }
139        if let Some(kernel_irqchip) = &self.kernel_irqchip {
140            args.push(format!("kernel_irqchip={}", kernel_irqchip.to_arg()));
141        }
142        if let Some(kvm_shadow_mem) = &self.kvm_shadow_mem {
143            args.push(format!("kvm-shadow-mem={}", kvm_shadow_mem));
144        }
145        if let Some(one_insn_per_tb) = &self.one_insn_per_tb {
146            args.push(format!("one-insn-per-tb={}", one_insn_per_tb.to_arg()));
147        }
148        if let Some(split_wx) = &self.split_wx {
149            args.push(format!("split-wx={}", split_wx.to_arg()));
150        }
151        if let Some(tb_size) = &self.tb_size {
152            args.push(format!("tb-size={}", tb_size));
153        }
154        if let Some(dirty_ring_size) = &self.dirty_ring_size {
155            args.push(format!("dirty-ring-size={}", dirty_ring_size));
156        }
157        if let Some(eager_split_size) = &self.eager_split_size {
158            args.push(format!("eager-split-size={}", eager_split_size));
159        }
160        if let Some(notify_vmexit) = &self.notify_vmexit {
161            let mut nvm_args = vec![];
162            for opt in notify_vmexit {
163                match opt {
164                    NotifyVMExitType::Run => {
165                        nvm_args.push("run".to_string());
166                    }
167                    NotifyVMExitType::InternalError => {
168                        nvm_args.push("internal-error".to_string());
169                    }
170                    NotifyVMExitType::Disable => {
171                        nvm_args.push("disable".to_string());
172                    }
173                    NotifyVMExitType::NotifyWindow(window) => {
174                        nvm_args.push(format!("notify-window={}", window));
175                    }
176                }
177            }
178            args.push(format!("notify-vmexit=={}", nvm_args.join(",")));
179        }
180        if let Some(thread) = &self.thread {
181            args.push(format!("thread={}", thread.to_arg()));
182        }
183        if let Some(device) = &self.device {
184            args.push(format!("device={}", device.display()));
185        }
186        cmd.push(args.join(","));
187        cmd
188    }
189}