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