On Tue, 11/25 18:21, Peter Maydell wrote: > In qemu_poll_ns(), when we convert an int64_t nanosecond timeout into > a struct timespec, we may accidentally run into overflow problems if > the timeout is very long. This happens because the tv_sec field is a > time_t, which is signed, so we might end up setting it to a negative > value by mistake. This will result in what was intended to be a > near-infinite timeout turning into an instantaneous timeout, and we'll > busy loop. Cap the maximum timeout at INT32_MAX seconds (about 68 years) > to avoid this problem. > > This specifically manifested on ARM hosts as an extreme slowdown on > guest shutdown (when the guest reprogrammed the PL031 RTC to not > generate alarms using a very long timeout) but could happen on other > hosts and guests too. > > Reported-by: Christoffer Dall <christoffer.d...@linaro.org> > Signed-off-by: Peter Maydell <peter.mayd...@linaro.org> > --- > It's not quite clear why this only causes problems in some KVM > configurations -- presumably in the others we complete the guest > shutdown reasonably quickly without the busy-waiting QEMU thread > interfering, but in some setups, notably on TC2 host, we go into > an extreme slowdown printing out the final bits of the guest shutdown > to its serial port. Given that (and given that I think this is fairly > safe) I'd like to get this into 2.2 if possible... > > qemu-timer.c | 9 ++++++++- > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/qemu-timer.c b/qemu-timer.c > index 00a5d35..c77de64 100644 > --- a/qemu-timer.c > +++ b/qemu-timer.c > @@ -314,7 +314,14 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t > timeout) > return ppoll((struct pollfd *)fds, nfds, NULL, NULL); > } else { > struct timespec ts; > - ts.tv_sec = timeout / 1000000000LL; > + int64_t tvsec = timeout / 1000000000LL; > + /* Avoid possibly overflowing and specifying a negative number of > + * seconds, which would turn a very long timeout into a busy-wait. > + */ > + if (tvsec > (int64_t)INT32_MAX) { > + tvsec = INT32_MAX; > + } > + ts.tv_sec = tvsec; > ts.tv_nsec = timeout % 1000000000LL; > return ppoll((struct pollfd *)fds, nfds, &ts, NULL); > } > -- > 1.9.1 > >
Reviewed-by: Fam Zheng <f...@redhat.com>