Skip to content

Commit 2254d48

Browse files
dgageotslp
authored andcommitted
Remove contention on the gic
And move it down to each vcpu Building the Linux Kernel: 7m21s -> 6:58s Building Wasmer: 3m13s -> 2m30s Signed-off-by: David Gageot <[email protected]>
1 parent ddbd2fd commit 2254d48

File tree

4 files changed

+108
-117
lines changed

4 files changed

+108
-117
lines changed

src/devices/src/legacy/gic.rs

Lines changed: 88 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use crossbeam_channel::Sender;
5-
use std::collections::btree_map::Entry;
6-
use std::collections::{BTreeMap, VecDeque};
5+
use std::collections::VecDeque;
76
use std::convert::TryInto;
7+
use std::sync::{Arc, Mutex};
88

99
use arch::aarch64::gicv2::GICv2;
1010
use arch::aarch64::layout::GTIMER_VIRT;
@@ -23,32 +23,101 @@ enum VcpuStatus {
2323
struct VcpuInfo {
2424
status: VcpuStatus,
2525
pending_irqs: VecDeque<u32>,
26-
wfe_sender: Sender<u32>,
26+
wfe_sender: Option<Sender<u32>>,
27+
}
28+
29+
pub struct VcpuList {
30+
vcpus: Vec<Mutex<VcpuInfo>>,
31+
vtimer_irq: u32,
32+
}
33+
34+
impl VcpuList {
35+
pub fn new() -> Self {
36+
let mut vcpus = Vec::with_capacity(MAX_CPUS as usize);
37+
for _ in 0..MAX_CPUS {
38+
vcpus.push(Mutex::new(VcpuInfo {
39+
status: VcpuStatus::Running,
40+
pending_irqs: VecDeque::new(),
41+
wfe_sender: None,
42+
}));
43+
}
44+
45+
Self {
46+
vcpus,
47+
vtimer_irq: GTIMER_VIRT + 16,
48+
}
49+
}
50+
51+
fn set_irq_common(&self, vcpuid: u8, irq_line: u32) {
52+
let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap();
53+
vcpu.pending_irqs.push_back(irq_line);
54+
55+
match vcpu.status {
56+
VcpuStatus::Waiting => {
57+
vcpu.wfe_sender
58+
.as_mut()
59+
.unwrap()
60+
.send(vcpuid as u32)
61+
.unwrap();
62+
vcpu.status = VcpuStatus::Running;
63+
}
64+
VcpuStatus::Running => {
65+
vcpu_request_exit(vcpuid as u64).unwrap();
66+
}
67+
}
68+
}
69+
70+
pub fn set_vtimer_irq(&self, vcpuid: u64) {
71+
assert!(vcpuid < MAX_CPUS);
72+
self.set_irq_common(vcpuid as u8, self.vtimer_irq);
73+
}
74+
75+
pub fn register(&self, vcpuid: u64, wfe_sender: Sender<u32>) {
76+
assert!(vcpuid < MAX_CPUS);
77+
let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap();
78+
vcpu.wfe_sender = Some(wfe_sender);
79+
}
80+
81+
pub fn should_wait(&self, vcpuid: u64) -> bool {
82+
assert!(vcpuid < MAX_CPUS);
83+
let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap();
84+
if vcpu.pending_irqs.is_empty() {
85+
vcpu.status = VcpuStatus::Waiting;
86+
true
87+
} else {
88+
false
89+
}
90+
}
91+
92+
pub fn has_pending_irq(&self, vcpuid: u64) -> bool {
93+
assert!(vcpuid < MAX_CPUS);
94+
let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap();
95+
!vcpu.pending_irqs.is_empty()
96+
}
97+
98+
pub fn get_pending_irq(&self, vcpuid: u8) -> u32 {
99+
let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap();
100+
vcpu.pending_irqs.pop_front().unwrap_or(1023)
101+
}
27102
}
28103

29104
pub struct Gic {
30105
cpu_size: u64,
31106
ctlr: u32,
32107
irq_cfg: [u8; IRQ_NUM as usize],
33-
vcpus: BTreeMap<u8, VcpuInfo>,
108+
vcpu_list: Arc<VcpuList>,
34109
vcpu_count: u8,
35110
irq_target: [u8; IRQ_NUM as usize],
36111
vtimer_irq: u32,
37112
}
38113

39-
impl Default for Gic {
40-
fn default() -> Self {
41-
Self::new()
42-
}
43-
}
44-
45114
impl Gic {
46-
pub fn new() -> Self {
115+
pub fn new(vcpu_list: Arc<VcpuList>) -> Self {
47116
Self {
48117
cpu_size: GICv2::get_cpu_size(),
49118
ctlr: 0,
50119
irq_cfg: [0; IRQ_NUM as usize],
51-
vcpus: BTreeMap::new(),
120+
vcpu_list,
52121
vcpu_count: 0,
53122
irq_target: [0; IRQ_NUM as usize],
54123
vtimer_irq: GTIMER_VIRT + 16,
@@ -66,105 +135,23 @@ impl Gic {
66135
GICv2::get_dist_size() + GICv2::get_cpu_size()
67136
}
68137

69-
fn set_irq_common(&mut self, vcpuid: u8, irq_line: u32) {
70-
match self.vcpus.entry(vcpuid) {
71-
Entry::Vacant(_) => {
72-
panic!("Unknown vCPU id: {}", vcpuid);
73-
}
74-
Entry::Occupied(mut vcpu_entry) => {
75-
let vcpu = vcpu_entry.get_mut();
76-
77-
vcpu.pending_irqs.push_back(irq_line);
78-
79-
match vcpu.status {
80-
VcpuStatus::Waiting => {
81-
vcpu.wfe_sender.send(vcpuid as u32).unwrap();
82-
vcpu.status = VcpuStatus::Running;
83-
}
84-
VcpuStatus::Running => {
85-
vcpu_request_exit(vcpuid as u64).unwrap();
86-
}
87-
}
88-
}
89-
}
138+
pub fn add_vcpu(&mut self) {
139+
self.vcpu_count += 1;
90140
}
91141

92-
pub fn set_sgi_irq(&mut self, vcpuid: u8, irq_line: u32) {
142+
fn set_sgi_irq(&self, vcpuid: u8, irq_line: u32) {
93143
assert!(irq_line < 16);
94-
self.set_irq_common(vcpuid, irq_line);
144+
self.vcpu_list.set_irq_common(vcpuid, irq_line);
95145
}
96146

97-
pub fn set_vtimer_irq(&mut self, vcpuid: u64) {
98-
assert!(vcpuid < MAX_CPUS);
99-
self.set_irq_common(vcpuid as u8, self.vtimer_irq);
100-
}
101-
102-
pub fn set_irq(&mut self, irq_line: u32) {
147+
pub fn set_irq(&self, irq_line: u32) {
103148
for vcpuid in 0..self.vcpu_count {
104149
if (self.irq_target[irq_line as usize] & (1 << vcpuid)) == 0 {
105150
continue;
106151
}
107152

108153
debug!("signaling irq={} to vcpuid={}", irq_line, vcpuid);
109-
110-
self.set_irq_common(vcpuid, irq_line);
111-
}
112-
}
113-
114-
pub fn register_vcpu(&mut self, vcpuid: u64, wfe_sender: Sender<u32>) {
115-
assert!(vcpuid < MAX_CPUS);
116-
self.vcpus.insert(
117-
vcpuid as u8,
118-
VcpuInfo {
119-
status: VcpuStatus::Running,
120-
wfe_sender,
121-
pending_irqs: VecDeque::new(),
122-
},
123-
);
124-
assert!(self.vcpus.len() <= MAX_CPUS as usize);
125-
self.vcpu_count = self.vcpus.len() as u8;
126-
}
127-
128-
pub fn vcpu_should_wait(&mut self, vcpuid: u64) -> bool {
129-
assert!(vcpuid < MAX_CPUS);
130-
match self.vcpus.entry(vcpuid as u8) {
131-
Entry::Vacant(_) => {
132-
panic!("Unknown vCPU id: {}", vcpuid);
133-
}
134-
Entry::Occupied(mut vcpu_entry) => {
135-
let vcpu = vcpu_entry.get_mut();
136-
if vcpu.pending_irqs.is_empty() {
137-
vcpu.status = VcpuStatus::Waiting;
138-
true
139-
} else {
140-
false
141-
}
142-
}
143-
}
144-
}
145-
146-
pub fn vcpu_has_pending_irq(&mut self, vcpuid: u64) -> bool {
147-
assert!(vcpuid < MAX_CPUS);
148-
match self.vcpus.entry(vcpuid as u8) {
149-
Entry::Vacant(_) => {
150-
panic!("Unknown vCPU id: {}", vcpuid);
151-
}
152-
Entry::Occupied(mut vcpu_entry) => {
153-
let vcpu = vcpu_entry.get_mut();
154-
!vcpu.pending_irqs.is_empty()
155-
}
156-
}
157-
}
158-
159-
fn get_pending_irq(&mut self, vcpuid: u8) -> u32 {
160-
match self.vcpus.entry(vcpuid) {
161-
Entry::Vacant(_) => {
162-
panic!("Unknown vCPU id: {}", vcpuid);
163-
}
164-
Entry::Occupied(mut vcpu_entry) => {
165-
let vcpu = vcpu_entry.get_mut();
166-
vcpu.pending_irqs.pop_front().unwrap_or(1023)
167-
}
154+
self.vcpu_list.set_irq_common(vcpuid, irq_line);
168155
}
169156
}
170157

@@ -280,7 +267,7 @@ impl Gic {
280267

281268
let mut val = 0;
282269
if offset == 0xc {
283-
val = self.get_pending_irq(vcpuid as u8);
270+
val = self.vcpu_list.get_pending_irq(vcpuid as u8);
284271
}
285272
for (i, b) in val.to_le_bytes().iter().enumerate() {
286273
data[i] = *b;

src/devices/src/legacy/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use aarch64::gpio;
2323
use aarch64::serial;
2424

2525
#[cfg(target_os = "macos")]
26-
pub use self::gic::Gic;
26+
pub use self::gic::{Gic, VcpuList};
2727
#[cfg(target_arch = "aarch64")]
2828
pub use self::gpio::Gpio;
2929
pub use self::i8042::Error as I8042DeviceError;

src/vmm/src/builder.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ use super::{Error, Vmm};
1818
#[cfg(target_arch = "x86_64")]
1919
use crate::device_manager::legacy::PortIODeviceManager;
2020
use crate::device_manager::mmio::MMIODeviceManager;
21-
use devices::legacy::Gic;
22-
use devices::legacy::Serial;
21+
#[cfg(target_os = "macos")]
22+
use devices::legacy::VcpuList;
23+
use devices::legacy::{Gic, Serial};
2324
#[cfg(feature = "net")]
2425
use devices::virtio::Net;
2526
#[cfg(not(feature = "tee"))]
@@ -472,10 +473,13 @@ pub fn build_microvm(
472473
(arch::IRQ_BASE, arch::IRQ_MAX),
473474
);
474475

476+
#[cfg(target_os = "macos")]
477+
let vcpu_list = Arc::new(VcpuList::new());
478+
475479
#[cfg(target_os = "linux")]
476480
let intc = None;
477481
#[cfg(target_os = "macos")]
478-
let intc = Some(Arc::new(Mutex::new(devices::legacy::Gic::new())));
482+
let intc = Some(Arc::new(Mutex::new(Gic::new(vcpu_list.clone()))));
479483

480484
#[cfg(all(target_os = "linux", target_arch = "x86_64", not(feature = "tee")))]
481485
let boot_ip: GuestAddress = GuestAddress(kernel_bundle.entry_addr);
@@ -539,6 +543,7 @@ pub fn build_microvm(
539543
start_addr,
540544
&exit_evt,
541545
intc.clone().unwrap(),
546+
vcpu_list.clone(),
542547
)
543548
.map_err(StartMicrovmError::Internal)?;
544549

@@ -1039,6 +1044,7 @@ fn create_vcpus_aarch64(
10391044
entry_addr: GuestAddress,
10401045
exit_evt: &EventFd,
10411046
intc: Arc<Mutex<Gic>>,
1047+
vcpu_list: Arc<VcpuList>,
10421048
) -> super::Result<Vec<Vcpu>> {
10431049
let mut vcpus = Vec::with_capacity(vcpu_config.vcpu_count as usize);
10441050
let mut boot_senders = Vec::with_capacity(vcpu_config.vcpu_count as usize - 1);
@@ -1058,6 +1064,7 @@ fn create_vcpus_aarch64(
10581064
boot_receiver,
10591065
exit_evt.try_clone().map_err(Error::EventFd)?,
10601066
intc.clone(),
1067+
vcpu_list.clone(),
10611068
)
10621069
.map_err(Error::Vcpu)?;
10631070

src/vmm/src/macos/vstate.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::vmm_config::machine_config::CpuFeaturesTemplate;
2020
use arch;
2121
use arch::aarch64::gic::GICDevice;
2222
use crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender};
23-
use devices::legacy::Gic;
23+
use devices::legacy::{Gic, VcpuList};
2424
use hvf::{HvfVcpu, HvfVm, VcpuExit};
2525
use utils::eventfd::EventFd;
2626
use vm_memory::{
@@ -220,6 +220,7 @@ pub struct Vcpu {
220220
response_sender: Sender<VcpuResponse>,
221221

222222
intc: Arc<Mutex<Gic>>,
223+
vcpu_list: Arc<VcpuList>,
223224
}
224225

225226
impl Vcpu {
@@ -293,6 +294,7 @@ impl Vcpu {
293294
boot_receiver: Option<Receiver<u64>>,
294295
exit_evt: EventFd,
295296
intc: Arc<Mutex<Gic>>,
297+
vcpu_list: Arc<VcpuList>,
296298
) -> Result<Self> {
297299
let (event_sender, event_receiver) = unbounded();
298300
let (response_sender, response_receiver) = unbounded();
@@ -311,6 +313,7 @@ impl Vcpu {
311313
response_receiver: Some(response_receiver),
312314
response_sender,
313315
intc,
316+
vcpu_list,
314317
})
315318
}
316319

@@ -382,11 +385,7 @@ impl Vcpu {
382385
/// Returns error or enum specifying whether emulation was handled or interrupted.
383386
fn run_emulation(&mut self, hvf_vcpu: &mut HvfVcpu) -> Result<VcpuEmulation> {
384387
let vcpuid = hvf_vcpu.id();
385-
let pending_irq = self
386-
.intc
387-
.lock()
388-
.unwrap()
389-
.vcpu_has_pending_irq(hvf_vcpu.id());
388+
let pending_irq = self.vcpu_list.has_pending_irq(vcpuid);
390389

391390
match hvf_vcpu.run(pending_irq) {
392391
Ok(exit) => match exit {
@@ -441,7 +440,7 @@ impl Vcpu {
441440
}
442441
VcpuExit::VtimerActivated => {
443442
debug!("vCPU {} VtimerActivated", vcpuid);
444-
self.intc.lock().unwrap().set_vtimer_irq(vcpuid);
443+
self.vcpu_list.set_vtimer_irq(vcpuid);
445444
Ok(VcpuEmulation::Handled)
446445
}
447446
VcpuExit::WaitForEvent => {
@@ -469,10 +468,8 @@ impl Vcpu {
469468
let hvf_vcpuid = hvf_vcpu.id();
470469

471470
let (wfe_sender, wfe_receiver) = unbounded();
472-
self.intc
473-
.lock()
474-
.unwrap()
475-
.register_vcpu(hvf_vcpuid, wfe_sender);
471+
self.vcpu_list.register(hvf_vcpuid, wfe_sender);
472+
self.intc.lock().unwrap().add_vcpu();
476473

477474
let entry_addr = if let Some(boot_receiver) = &self.boot_receiver {
478475
boot_receiver.recv().unwrap()
@@ -518,7 +515,7 @@ impl Vcpu {
518515
receiver: &Receiver<u32>,
519516
timeout: Option<Duration>,
520517
) {
521-
if self.intc.lock().unwrap().vcpu_should_wait(hvf_vcpuid) {
518+
if self.vcpu_list.should_wait(hvf_vcpuid) {
522519
if let Some(timeout) = timeout {
523520
match receiver.recv_timeout(timeout) {
524521
Ok(_) => {}

0 commit comments

Comments
 (0)