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}