Skip to content

Commit 957f5c4

Browse files
authored
viona: do not retain peak nqueues across device reset (#1051)
1df49a4, also, was too eager to reset viona rings. the issue here is more subtle. the "peak" count for used virtqueues was retained across device resets, so if a guest ever negotiates multiqueue, resets the NIC as for reboot (where we SET_PAIRS down to 1), and then does a virtio reset with the `device_status` register, we'll try to reset all queues even though the pairs after 1 are not actually in use. this EINVAL's and we get the device into `NEEDS_RESET`.
1 parent a28bee1 commit 957f5c4

4 files changed

Lines changed: 44 additions & 1 deletion

File tree

lib/propolis/src/hw/virtio/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,8 @@ mod probes {
252252
fn virtio_vq_notify(virtio_dev_addr: u64, virtqueue_id: u16) {}
253253
fn virtio_vq_pop(vq_addr: u64, desc_idx: u16, avail_idx: u16) {}
254254
fn virtio_vq_push(vq_addr: u64, used_idx: u16, used_len: u32) {}
255+
256+
fn virtio_viona_mq_set_use_pairs(cause: u8, npairs: u16) {}
257+
258+
fn virtio_device_needs_reset() {}
255259
}

lib/propolis/src/hw/virtio/pci.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,11 @@ impl PciVirtioState {
10721072
_dev: &dyn VirtioDevice,
10731073
state: &mut MutexGuard<VirtioState>,
10741074
) {
1075+
// TODO: would be *great* to know which device needs a reset.. compare
1076+
// with device_id in nvme and how we can give out per-device IDs when
1077+
// setting things up.
1078+
probes::virtio_device_needs_reset!(|| ());
1079+
10751080
if !state.status.contains(Status::NEEDS_RESET) {
10761081
state.status.insert(Status::NEEDS_RESET);
10771082
// XXX: interrupt needed?

lib/propolis/src/hw/virtio/queue.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,11 @@ impl VirtQueues {
10031003
self.peak.load(Ordering::Relaxed)
10041004
}
10051005

1006+
pub fn reset_peak(&self) {
1007+
let current = self.len.load(Ordering::Relaxed);
1008+
self.peak.store(current, Ordering::Relaxed);
1009+
}
1010+
10061011
pub const fn max_capacity(&self) -> usize {
10071012
self.queues.len()
10081013
}

lib/propolis/src/hw/virtio/viona.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ pub const fn max_num_queues() -> usize {
5151

5252
const ETHERADDRL: usize = 6;
5353

54+
/// The caller of `set_use_pairs` will probably be inlined into a larger
55+
/// function that is difficult to spot in a ustack(). This gives us a hint
56+
/// about why we were `set_usepairs()`'ing.
57+
#[repr(u8)]
58+
enum MqSetPairsCause {
59+
Reset = 0,
60+
MqEnabled = 1,
61+
Commanded = 2,
62+
}
63+
64+
#[usdt::provider(provider = "propolis")]
65+
mod probes {
66+
fn virtio_viona_mq_set_use_pairs(cause: u8, npairs: u16) {}
67+
}
68+
5469
/// Types and so forth for supporting the control queue.
5570
/// Note that these come from the VirtIO spec, section
5671
/// 5.1.6.2 in VirtIO 1.2.
@@ -510,7 +525,12 @@ impl PciVirtioViona {
510525
if !chain.read(&mut msg, &mem) {
511526
return Err(());
512527
}
513-
self.set_use_pairs(msg.npairs)
528+
let npairs = msg.npairs;
529+
probes::virtio_viona_mq_set_use_pairs!(|| (
530+
MqSetPairsCause::Commanded as u8,
531+
npairs
532+
));
533+
self.set_use_pairs(npairs)
514534
}
515535
MqCmd::RssConfig => Err(()),
516536
MqCmd::HashConfig => Err(()),
@@ -717,6 +737,10 @@ impl VirtioDevice for PciVirtioViona {
717737
self.hdl.set_features(feat).map_err(|_| ())?;
718738
if (feat & VIRTIO_NET_F_MQ) != 0 {
719739
self.hdl.set_pairs(PROPOLIS_MAX_MQ_PAIRS).map_err(|_| ())?;
740+
probes::virtio_viona_mq_set_use_pairs!(|| (
741+
MqSetPairsCause::MqEnabled as u8,
742+
PROPOLIS_MAX_MQ_PAIRS
743+
));
720744
self.set_use_pairs(PROPOLIS_MAX_MQ_PAIRS)?;
721745
}
722746
Ok(())
@@ -808,8 +832,13 @@ impl Lifecycle for PciVirtioViona {
808832
}
809833
fn reset(&self) {
810834
self.virtio_state.reset(self);
835+
probes::virtio_viona_mq_set_use_pairs!(|| (
836+
MqSetPairsCause::Reset as u8,
837+
1
838+
));
811839
self.set_use_pairs(1).expect("can set viona back to one queue pair");
812840
self.hdl.set_pairs(1).expect("can set viona back to one queue pair");
841+
self.virtio_state.queues.reset_peak();
813842
}
814843
fn start(&self) -> anyhow::Result<()> {
815844
self.run();

0 commit comments

Comments
 (0)