Skip to content

Commit d806090

Browse files
erfrimodsunilmut
andauthored
netvsp: handle RNDIS packet filter OID for stopping Rx (#1873) (#1927)
Linux netvsc sends an OID to stop receiving packets on vmbus channel close. Example scenarios: hibernation and MTU change. Prior to opening a new channel and processing the packets, netvsc checks that there are no pending packets. If there are, netvsc logs and error and is unable to recover. We observe the error: `hv_netvsc eth0: Ring buffer not empty after closing rndis` in the guest syslog. Modifying netvsp to handle the OID and stop processing RX traffic. This will allow for netvsc to successfully close and re-open the vmbus channel, even under heavy incoming traffic. Cherry pick of #1873 Co-authored-by: Sunil Muthuswamy <[email protected]>
1 parent 4fba8c6 commit d806090

File tree

4 files changed

+382
-18
lines changed

4 files changed

+382
-18
lines changed

vm/devices/net/netvsp/src/lib.rs

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,19 @@ const LINK_DELAY_DURATION: Duration = Duration::from_secs(5);
147147
#[cfg(test)]
148148
const LINK_DELAY_DURATION: Duration = Duration::from_millis(333);
149149

150-
#[derive(PartialEq)]
151-
enum CoordinatorMessage {
150+
#[derive(Default, PartialEq)]
151+
struct CoordinatorMessageUpdateType {
152152
/// Update guest VF state based on current availability and the guest VF state tracked by the primary channel.
153153
/// This includes adding the guest VF device and switching the data path.
154-
UpdateGuestVfState,
154+
guest_vf_state: bool,
155+
/// Update the receive filter for all channels.
156+
filter_state: bool,
157+
}
158+
159+
#[derive(PartialEq)]
160+
enum CoordinatorMessage {
161+
/// Update network state.
162+
Update(CoordinatorMessageUpdateType),
155163
/// Restart endpoints and resume processing. This will also attempt to set VF and data path state to match current
156164
/// expectations.
157165
Restart,
@@ -382,6 +390,7 @@ struct NetChannel<T: RingMem> {
382390
pending_send_size: usize,
383391
restart: Option<CoordinatorMessage>,
384392
can_use_ring_size_opt: bool,
393+
packet_filter: u32,
385394
}
386395

387396
/// Buffers used during packet processing.
@@ -1364,6 +1373,7 @@ impl Nic {
13641373
pending_send_size: 0,
13651374
restart: None,
13661375
can_use_ring_size_opt,
1376+
packet_filter: rndisprot::NDIS_PACKET_TYPE_NONE,
13671377
},
13681378
state,
13691379
coordinator_send: self.coordinator_send.clone().unwrap(),
@@ -1453,6 +1463,7 @@ impl Nic {
14531463
mut control: RestoreControl<'_>,
14541464
state: saved_state::SavedState,
14551465
) -> Result<(), NetRestoreError> {
1466+
let mut saved_packet_filter = 0u32;
14561467
if let Some(state) = state.open {
14571468
let open = match &state.primary {
14581469
saved_state::Primary::Version => vec![true],
@@ -1537,8 +1548,12 @@ impl Nic {
15371548
tx_spread_sent,
15381549
guest_link_down,
15391550
pending_link_action,
1551+
packet_filter,
15401552
} = ready;
15411553

1554+
// If saved state does not have a packet filter set, default to directed, multicast, and broadcast.
1555+
saved_packet_filter = packet_filter.unwrap_or(rndisprot::NPROTO_PACKET_FILTER);
1556+
15421557
let version = check_version(version)
15431558
.ok_or(NetRestoreError::UnsupportedVersion(version))?;
15441559

@@ -1621,6 +1636,11 @@ impl Nic {
16211636
self.insert_worker(channel_idx as u16, &request.unwrap(), state, false)?;
16221637
}
16231638
}
1639+
for worker in self.coordinator.state_mut().unwrap().workers.iter_mut() {
1640+
if let Some(worker_state) = worker.state_mut() {
1641+
worker_state.channel.packet_filter = saved_packet_filter;
1642+
}
1643+
}
16241644
} else {
16251645
control
16261646
.restore(&[false])
@@ -1782,6 +1802,11 @@ impl Nic {
17821802
PrimaryChannelGuestVfState::Restoring(saved_state) => saved_state,
17831803
};
17841804

1805+
let worker_0_packet_filter = coordinator.workers[0]
1806+
.state()
1807+
.unwrap()
1808+
.channel
1809+
.packet_filter;
17851810
saved_state::Primary::Ready(saved_state::ReadyPrimary {
17861811
version: ready.buffers.version as u32,
17871812
receive_buffer: ready.buffers.recv_buffer.saved_state(),
@@ -1811,6 +1836,7 @@ impl Nic {
18111836
tx_spread_sent: primary.tx_spread_sent,
18121837
guest_link_down: !primary.guest_link_up,
18131838
pending_link_action,
1839+
packet_filter: Some(worker_0_packet_filter),
18141840
})
18151841
}
18161842
};
@@ -2594,7 +2620,12 @@ impl<T: RingMem> NetChannel<T> {
25942620
if primary.rndis_state == RndisState::Operational {
25952621
if self.guest_vf_is_available(Some(vfid), buffers.version, buffers.ndis_config)? {
25962622
primary.guest_vf_state = PrimaryChannelGuestVfState::AvailableAdvertised;
2597-
return Ok(Some(CoordinatorMessage::UpdateGuestVfState));
2623+
return Ok(Some(CoordinatorMessage::Update(
2624+
CoordinatorMessageUpdateType {
2625+
guest_vf_state: true,
2626+
..Default::default()
2627+
},
2628+
)));
25982629
} else if let Some(true) = primary.is_data_path_switched {
25992630
tracing::error!(
26002631
"Data path switched, but current guest negotiation does not support VTL0 VF"
@@ -2734,10 +2765,7 @@ impl<T: RingMem> NetChannel<T> {
27342765
// flag on inband packets and won't send a completion
27352766
// packet.
27362767
primary.guest_vf_state = PrimaryChannelGuestVfState::AvailableAdvertised;
2737-
// restart will also add the VF based on the guest_vf_state
2738-
if self.restart.is_none() {
2739-
self.restart = Some(CoordinatorMessage::UpdateGuestVfState);
2740-
}
2768+
self.send_coordinator_update_vf();
27412769
} else if let Some(true) = primary.is_data_path_switched {
27422770
tracing::error!(
27432771
"Data path switched, but current guest negotiation does not support VTL0 VF"
@@ -2785,12 +2813,18 @@ impl<T: RingMem> NetChannel<T> {
27852813
tracing::trace!(?request, "handling control message MESSAGE_TYPE_SET_MSG");
27862814

27872815
let status = match self.adapter.handle_oid_set(primary, request.oid, reader) {
2788-
Ok(restart_endpoint) => {
2816+
Ok((restart_endpoint, packet_filter)) => {
27892817
// Restart the endpoint if the OID changed some critical
27902818
// endpoint property.
27912819
if restart_endpoint {
27922820
self.restart = Some(CoordinatorMessage::Restart);
27932821
}
2822+
if let Some(filter) = packet_filter {
2823+
if self.packet_filter != filter {
2824+
self.packet_filter = filter;
2825+
self.send_coordinator_update_filter();
2826+
}
2827+
}
27942828
rndisprot::STATUS_SUCCESS
27952829
}
27962830
Err(err) => {
@@ -2974,6 +3008,31 @@ impl<T: RingMem> NetChannel<T> {
29743008
}
29753009
Ok(())
29763010
}
3011+
3012+
fn send_coordinator_update_message(&mut self, guest_vf: bool, packet_filter: bool) {
3013+
if self.restart.is_none() {
3014+
self.restart = Some(CoordinatorMessage::Update(CoordinatorMessageUpdateType {
3015+
guest_vf_state: guest_vf,
3016+
filter_state: packet_filter,
3017+
}));
3018+
} else if let Some(CoordinatorMessage::Restart) = self.restart {
3019+
// If a restart message is pending, do nothing.
3020+
// A restart will try to switch the data path based on primary.guest_vf_state.
3021+
// A restart will apply packet filter changes.
3022+
} else if let Some(CoordinatorMessage::Update(ref mut update)) = self.restart {
3023+
// Add the new update to the existing message.
3024+
update.guest_vf_state |= guest_vf;
3025+
update.filter_state |= packet_filter;
3026+
}
3027+
}
3028+
3029+
fn send_coordinator_update_vf(&mut self) {
3030+
self.send_coordinator_update_message(true, false);
3031+
}
3032+
3033+
fn send_coordinator_update_filter(&mut self) {
3034+
self.send_coordinator_update_message(false, true);
3035+
}
29773036
}
29783037

29793038
/// Writes an RNDIS message to `writer`.
@@ -3291,13 +3350,14 @@ impl Adapter {
32913350
primary: &mut PrimaryChannelState,
32923351
oid: rndisprot::Oid,
32933352
reader: impl MemoryRead + Clone,
3294-
) -> Result<bool, OidError> {
3353+
) -> Result<(bool, Option<u32>), OidError> {
32953354
tracing::debug!(?oid, "oid set");
32963355

32973356
let mut restart_endpoint = false;
3357+
let mut packet_filter = None;
32983358
match oid {
32993359
rndisprot::Oid::OID_GEN_CURRENT_PACKET_FILTER => {
3300-
// TODO
3360+
packet_filter = self.oid_set_packet_filter(reader)?;
33013361
}
33023362
rndisprot::Oid::OID_TCP_OFFLOAD_PARAMETERS => {
33033363
self.oid_set_offload_parameters(reader, primary)?;
@@ -3324,7 +3384,7 @@ impl Adapter {
33243384
return Err(OidError::UnknownOid);
33253385
}
33263386
}
3327-
Ok(restart_endpoint)
3387+
Ok((restart_endpoint, packet_filter))
33283388
}
33293389

33303390
fn oid_set_rss_parameters(
@@ -3382,6 +3442,15 @@ impl Adapter {
33823442
Ok(())
33833443
}
33843444

3445+
fn oid_set_packet_filter(
3446+
&self,
3447+
reader: impl MemoryRead + Clone,
3448+
) -> Result<Option<u32>, OidError> {
3449+
let filter: rndisprot::RndisPacketFilterOidValue = reader.clone().read_plain()?;
3450+
tracing::debug!(filter, "set packet filter");
3451+
Ok(Some(filter))
3452+
}
3453+
33853454
fn oid_set_offload_parameters(
33863455
&self,
33873456
reader: impl MemoryRead + Clone,
@@ -3872,8 +3941,26 @@ impl Coordinator {
38723941
}
38733942
sleep_duration = None;
38743943
}
3875-
Message::Internal(CoordinatorMessage::UpdateGuestVfState) => {
3876-
self.update_guest_vf_state(state).await;
3944+
Message::Internal(CoordinatorMessage::Update(update_type)) => {
3945+
if update_type.filter_state {
3946+
self.stop_workers().await;
3947+
let worker_0_packet_filter =
3948+
self.workers[0].state().unwrap().channel.packet_filter;
3949+
self.workers.iter_mut().skip(1).for_each(|worker| {
3950+
if let Some(state) = worker.state_mut() {
3951+
state.channel.packet_filter = worker_0_packet_filter;
3952+
tracing::debug!(
3953+
packet_filter = ?worker_0_packet_filter,
3954+
channel_idx = state.channel_idx,
3955+
"update packet filter"
3956+
);
3957+
}
3958+
});
3959+
}
3960+
3961+
if update_type.guest_vf_state {
3962+
self.update_guest_vf_state(state).await;
3963+
}
38773964
}
38783965
Message::UpdateFromEndpoint(EndpointAction::RestartRequired) => self.restart = true,
38793966
Message::UpdateFromEndpoint(EndpointAction::LinkStatusNotify(connect)) => {
@@ -4306,13 +4393,18 @@ impl Coordinator {
43064393
self.num_queues = num_queues;
43074394
}
43084395

4396+
let worker_0_packet_filter = self.workers[0].state().unwrap().channel.packet_filter;
43094397
// Provide the queue and receive buffer ranges for each worker.
43104398
for ((worker, queue), rx_buffer) in self.workers.iter_mut().zip(queues).zip(rx_buffers) {
43114399
worker.task_mut().queue_state = Some(QueueState {
43124400
queue,
43134401
target_vp_set: false,
43144402
rx_buffer_range: rx_buffer,
43154403
});
4404+
// Update the receive packet filter for the subchannel worker.
4405+
if let Some(worker) = worker.state_mut() {
4406+
worker.channel.packet_filter = worker_0_packet_filter;
4407+
}
43164408
}
43174409

43184410
Ok(())
@@ -4920,6 +5012,13 @@ impl<T: 'static + RingMem> NetChannel<T> {
49205012
data: &mut ProcessingData,
49215013
epqueue: &mut dyn net_backend::Queue,
49225014
) -> Result<bool, WorkerError> {
5015+
if self.packet_filter == rndisprot::NDIS_PACKET_TYPE_NONE {
5016+
tracing::trace!(
5017+
packet_filter = self.packet_filter,
5018+
"rx packet not processed"
5019+
);
5020+
return Ok(false);
5021+
}
49235022
let n = epqueue
49245023
.rx_poll(&mut data.rx_ready)
49255024
.map_err(WorkerError::Endpoint)?;
@@ -5062,10 +5161,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
50625161
_ => (),
50635162
};
50645163
if queue_switch_operation {
5065-
// A restart will also try to switch the data path based on primary.guest_vf_state.
5066-
if self.restart.is_none() {
5067-
self.restart = Some(CoordinatorMessage::UpdateGuestVfState)
5068-
};
5164+
self.send_coordinator_update_vf();
50695165
} else {
50705166
self.send_completion(transaction_id, &[])?;
50715167
}

vm/devices/net/netvsp/src/rndisprot.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ open_enum! {
751751
DEFAULT = 0x80,
752752
RSS_CAPABILITIES = 0x88,
753753
RSS_PARAMETERS = 0x89,
754+
OID_REQUEST = 0x96,
754755
OFFLOAD = 0xA7,
755756
OFFLOAD_ENCAPSULATION = 0xA8,
756757
}
@@ -1082,3 +1083,14 @@ open_enum! {
10821083
BINARY = 4,
10831084
}
10841085
}
1086+
1087+
pub type RndisPacketFilterOidValue = u32;
1088+
1089+
// Rndis Packet Filter Flags (OID_GEN_CURRENT_PACKET_FILTER)
1090+
pub const NDIS_PACKET_TYPE_NONE: u32 = 0x00000000;
1091+
pub const NDIS_PACKET_TYPE_DIRECTED: u32 = 0x00000001;
1092+
pub const NDIS_PACKET_TYPE_MULTICAST: u32 = 0x00000002;
1093+
pub const NDIS_PACKET_TYPE_ALL_MULTICAST: u32 = 0x00000004;
1094+
pub const NDIS_PACKET_TYPE_BROADCAST: u32 = 0x00000008;
1095+
pub const NPROTO_PACKET_FILTER: u32 =
1096+
NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_BROADCAST;

vm/devices/net/netvsp/src/saved_state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub struct ReadyPrimary {
120120
pub guest_link_down: bool,
121121
#[mesh(15)]
122122
pub pending_link_action: Option<bool>,
123+
#[mesh(16)]
124+
pub packet_filter: Option<u32>,
123125
}
124126

125127
#[derive(Debug, Protobuf)]

0 commit comments

Comments
 (0)