`read_poll_timeout` can potentially sleep for the specified `sleep_delta` if `timeout_delta` is `Delta::ZERO` and the system clock is coarse enough to not register a tick between the time the function has been called and the time the loop test is done. `wait_for_msg` has a short test condition and is particularly vulnerable to this condition.
Add a dedicated branch when `timeout` is `Delta::ZERO` to perform the test without calling `read_poll_timeout` and avoid potentially sleeping for 1ms. Signed-off-by: Alexandre Courbot <[email protected]> --- drivers/gpu/nova-core/gsp/cmdq.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 9e678715c624..9c2c8fbad045 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -909,12 +909,24 @@ fn send_command<M>(&mut self, command: M) -> Result /// - `EIO` if a message fails framing or checksum validation. fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { // Wait for a message to arrive from the GSP. - let (slice_1, slice_2) = read_poll_timeout( - || Ok(self.gsp_mem.driver_read_area()), - |driver_area| !driver_area.0.is_empty(), - Delta::from_millis(1), - timeout, - ) + let (slice_1, slice_2) = if timeout == Delta::ZERO { + // If `timeout == Delta::ZERO`, check the message queue once as `read_poll_timeout` can + // potentially sleep if the clock hasn't moved forward while the condition was being + // checked. + let slices = self.gsp_mem.driver_read_area(); + if !slices.0.is_empty() { + Ok(slices) + } else { + Err(ETIMEDOUT) + } + } else { + read_poll_timeout( + || Ok(self.gsp_mem.driver_read_area()), + |driver_area| !driver_area.0.is_empty(), + Delta::from_millis(1), + timeout, + ) + } .map(|(slice_1, slice_2)| (slice_1.as_flattened(), slice_2.as_flattened()))?; // Extract the `GspMsgElement`. -- 2.56.0
