-
Notifications
You must be signed in to change notification settings - Fork 155
Cleanup intercept device shutdown #2140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,28 +167,36 @@ impl<T: SimpleVmbusClientDeviceAsync> SimpleVmbusClientDeviceWrapper<T> { | |
mut self, | ||
driver: impl SpawnDriver, | ||
recv_relay: mesh::Receiver<InterceptChannelRequest>, | ||
) -> Result<mesh::OneshotSender<()>> { | ||
) -> Result<()> { | ||
let (send_disconnected, recv_disconnected) = mesh::oneshot(); | ||
self.vmbus_listener.insert( | ||
&self.spawner, | ||
format!("{}", self.instance_id), | ||
SimpleVmbusClientDeviceTaskState { | ||
offer: None, | ||
recv_relay, | ||
send_disconnected: Some(send_disconnected), | ||
vtl_pages: None, | ||
}, | ||
); | ||
let (driver_send, driver_recv) = mesh::oneshot(); | ||
driver | ||
.spawn( | ||
format!("vmbus_relay_device {}", self.instance_id), | ||
async move { | ||
self.vmbus_listener.start(); | ||
let _ = driver_recv.await; | ||
self.vmbus_listener.stop().await; | ||
let _ = recv_disconnected.await; | ||
assert!(!self.vmbus_listener.stop().await); | ||
if self.vmbus_listener.state().unwrap().vtl_pages.is_some() { | ||
// The VTL pages were not freed. This can occur if an | ||
// error is hit that drops the vmbus parent tasks. Just | ||
// pend here and let the outer error cause the VM to | ||
// exit. | ||
pending::<()>().await; | ||
} | ||
}, | ||
) | ||
.detach(); | ||
Ok(driver_send) | ||
Ok(()) | ||
} | ||
} | ||
|
||
|
@@ -215,6 +223,8 @@ struct SimpleVmbusClientDeviceTaskState { | |
offer: Option<OfferInfo>, | ||
#[inspect(skip)] | ||
recv_relay: mesh::Receiver<InterceptChannelRequest>, | ||
#[inspect(skip)] | ||
send_disconnected: Option<mesh::OneshotSender<()>>, | ||
#[inspect(hex, with = "|x| x.as_ref().map(|x| inspect::iter_by_index(x.pfns()))")] | ||
vtl_pages: Option<MemoryBlock>, | ||
} | ||
|
@@ -234,7 +244,13 @@ impl<T: SimpleVmbusClientDeviceAsync> AsyncRun<SimpleVmbusClientDeviceTaskState> | |
stop: &mut StopTask<'_>, | ||
state: &mut SimpleVmbusClientDeviceTaskState, | ||
) -> Result<(), Cancelled> { | ||
stop.until_stopped(self.process_messages(state)).await | ||
stop.until_stopped(self.process_messages(state)).await?; | ||
state | ||
.send_disconnected | ||
.take() | ||
.expect("task should not be restarted") | ||
.send(()); | ||
Ok(()) | ||
} | ||
} | ||
|
||
|
@@ -351,21 +367,27 @@ impl<T: SimpleVmbusClientDeviceAsync> SimpleVmbusClientDeviceTask<T> { | |
}; | ||
|
||
if state.vtl_pages.is_some() { | ||
if let Err(err) = offer | ||
match offer | ||
.request_send | ||
.call( | ||
ChannelRequest::TeardownGpadl, | ||
GpadlId(state.vtl_pages.as_ref().unwrap().pfns()[1] as u32), | ||
) | ||
.await | ||
{ | ||
tracing::error!( | ||
error = &err as &dyn std::error::Error, | ||
"failed to teardown gpadl" | ||
); | ||
Ok(()) => { | ||
state.vtl_pages = None; | ||
} | ||
Err(err) => { | ||
// If the ring buffer pages are still in use by the host, which | ||
// has to be assumed, the memory pages cannot be used again as | ||
// they have been marked as visible to VTL0. | ||
tracing::error!( | ||
error = &err as &dyn std::error::Error, | ||
"Failed to teardown gpadl -- leaking memory." | ||
); | ||
} | ||
} | ||
|
||
state.vtl_pages = None; | ||
} | ||
} | ||
|
||
|
@@ -504,7 +526,7 @@ impl<T: SimpleVmbusClientDeviceAsync> SimpleVmbusClientDeviceTask<T> { | |
|
||
/// Responds to the channel being revoked by the host. | ||
async fn handle_revoke(&mut self, state: &mut SimpleVmbusClientDeviceTaskState) { | ||
let Some(offer) = state.offer.take() else { | ||
let Some(offer) = state.offer.as_ref() else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change from Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
return; | ||
}; | ||
tracing::info!("device revoked"); | ||
|
@@ -513,6 +535,7 @@ impl<T: SimpleVmbusClientDeviceAsync> SimpleVmbusClientDeviceTask<T> { | |
self.device.task_mut().0.close(offer.offer.subchannel_index); | ||
} | ||
self.cleanup_device_resources(state).await; | ||
drop(state.offer.take()); | ||
} | ||
|
||
fn handle_save(&mut self) -> SavedStateBlob { | ||
|
@@ -545,27 +568,25 @@ impl<T: SimpleVmbusClientDeviceAsync> SimpleVmbusClientDeviceTask<T> { | |
loop { | ||
enum Event { | ||
Request(InterceptChannelRequest), | ||
Revoke(()), | ||
Revoke, | ||
} | ||
let revoke = pin!(async { | ||
if let Some(offer) = &mut state.offer { | ||
(&mut offer.revoke_recv).await.ok(); | ||
} else { | ||
pending().await | ||
} | ||
}); | ||
let Some(r) = ( | ||
(&mut state.recv_relay).map(Event::Request), | ||
futures::stream::once(revoke).map(Event::Revoke), | ||
) | ||
.merge() | ||
.next() | ||
.await | ||
else { | ||
let r = if let Some(offer) = &mut state.offer { | ||
( | ||
(&mut state.recv_relay).map(Event::Request), | ||
futures::stream::once(&mut offer.revoke_recv).map(|_| Event::Revoke), | ||
) | ||
.merge() | ||
.next() | ||
.await | ||
} else { | ||
let mut recv_relay = pin!(&mut state.recv_relay); | ||
recv_relay.next().await.map(Event::Request) | ||
}; | ||
let Some(r) = r else { | ||
break; | ||
}; | ||
match r { | ||
Event::Revoke(()) => { | ||
Event::Revoke => { | ||
self.handle_revoke(state).await; | ||
} | ||
Event::Request(InterceptChannelRequest::Offer(offer)) => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
pending::<()>().await
to intentionally hang the task is an unusual pattern. Consider adding a more explicit comment explaining why this infinite wait is the desired behavior, or using a more conventional approach like returning an error that would cause the VM to exit.Copilot uses AI. Check for mistakes.