@@ -302,12 +302,31 @@ static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu)
302302 */
303303static uint32_t kvm_mips_read_count_running (struct kvm_vcpu * vcpu , ktime_t now )
304304{
305- ktime_t expires ;
305+ struct mips_coproc * cop0 = vcpu -> arch .cop0 ;
306+ ktime_t expires , threshold ;
307+ uint32_t count , compare ;
306308 int running ;
307309
308- /* Is the hrtimer pending? */
310+ /* Calculate the biased and scaled guest CP0_Count */
311+ count = vcpu -> arch .count_bias + kvm_mips_ktime_to_count (vcpu , now );
312+ compare = kvm_read_c0_guest_compare (cop0 );
313+
314+ /*
315+ * Find whether CP0_Count has reached the closest timer interrupt. If
316+ * not, we shouldn't inject it.
317+ */
318+ if ((int32_t )(count - compare ) < 0 )
319+ return count ;
320+
321+ /*
322+ * The CP0_Count we're going to return has already reached the closest
323+ * timer interrupt. Quickly check if it really is a new interrupt by
324+ * looking at whether the interval until the hrtimer expiry time is
325+ * less than 1/4 of the timer period.
326+ */
309327 expires = hrtimer_get_expires (& vcpu -> arch .comparecount_timer );
310- if (ktime_compare (now , expires ) >= 0 ) {
328+ threshold = ktime_add_ns (now , vcpu -> arch .count_period / 4 );
329+ if (ktime_before (expires , threshold )) {
311330 /*
312331 * Cancel it while we handle it so there's no chance of
313332 * interference with the timeout handler.
@@ -329,8 +348,7 @@ static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
329348 }
330349 }
331350
332- /* Return the biased and scaled guest CP0_Count */
333- return vcpu -> arch .count_bias + kvm_mips_ktime_to_count (vcpu , now );
351+ return count ;
334352}
335353
336354/**
@@ -419,32 +437,6 @@ static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
419437 hrtimer_start (& vcpu -> arch .comparecount_timer , expire , HRTIMER_MODE_ABS );
420438}
421439
422- /**
423- * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer.
424- * @vcpu: Virtual CPU.
425- *
426- * Recalculates and updates the expiry time of the hrtimer. This can be used
427- * after timer parameters have been altered which do not depend on the time that
428- * the change occurs (in those cases kvm_mips_freeze_hrtimer() and
429- * kvm_mips_resume_hrtimer() are used directly).
430- *
431- * It is guaranteed that no timer interrupts will be lost in the process.
432- *
433- * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
434- */
435- static void kvm_mips_update_hrtimer (struct kvm_vcpu * vcpu )
436- {
437- ktime_t now ;
438- uint32_t count ;
439-
440- /*
441- * freeze_hrtimer takes care of a timer interrupts <= count, and
442- * resume_hrtimer the hrtimer takes care of a timer interrupts > count.
443- */
444- now = kvm_mips_freeze_hrtimer (vcpu , & count );
445- kvm_mips_resume_hrtimer (vcpu , now , count );
446- }
447-
448440/**
449441 * kvm_mips_write_count() - Modify the count and update timer.
450442 * @vcpu: Virtual CPU.
@@ -540,23 +532,42 @@ int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
540532 * kvm_mips_write_compare() - Modify compare and update timer.
541533 * @vcpu: Virtual CPU.
542534 * @compare: New CP0_Compare value.
535+ * @ack: Whether to acknowledge timer interrupt.
543536 *
544537 * Update CP0_Compare to a new value and update the timeout.
538+ * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure
539+ * any pending timer interrupt is preserved.
545540 */
546- void kvm_mips_write_compare (struct kvm_vcpu * vcpu , uint32_t compare )
541+ void kvm_mips_write_compare (struct kvm_vcpu * vcpu , uint32_t compare , bool ack )
547542{
548543 struct mips_coproc * cop0 = vcpu -> arch .cop0 ;
544+ int dc ;
545+ u32 old_compare = kvm_read_c0_guest_compare (cop0 );
546+ ktime_t now ;
547+ uint32_t count ;
549548
550549 /* if unchanged, must just be an ack */
551- if (kvm_read_c0_guest_compare (cop0 ) == compare )
550+ if (old_compare == compare ) {
551+ if (!ack )
552+ return ;
553+ kvm_mips_callbacks -> dequeue_timer_int (vcpu );
554+ kvm_write_c0_guest_compare (cop0 , compare );
552555 return ;
556+ }
557+
558+ /* freeze_hrtimer() takes care of timer interrupts <= count */
559+ dc = kvm_mips_count_disabled (vcpu );
560+ if (!dc )
561+ now = kvm_mips_freeze_hrtimer (vcpu , & count );
562+
563+ if (ack )
564+ kvm_mips_callbacks -> dequeue_timer_int (vcpu );
553565
554- /* Update compare */
555566 kvm_write_c0_guest_compare (cop0 , compare );
556567
557- /* Update timeout if count enabled */
558- if (!kvm_mips_count_disabled ( vcpu ) )
559- kvm_mips_update_hrtimer (vcpu );
568+ /* resume_hrtimer() takes care of timer interrupts > count */
569+ if (!dc )
570+ kvm_mips_resume_hrtimer (vcpu , now , count );
560571}
561572
562573/**
@@ -1095,9 +1106,9 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
10951106
10961107 /* If we are writing to COMPARE */
10971108 /* Clear pending timer interrupt, if any */
1098- kvm_mips_callbacks -> dequeue_timer_int (vcpu );
10991109 kvm_mips_write_compare (vcpu ,
1100- vcpu -> arch .gprs [rt ]);
1110+ vcpu -> arch .gprs [rt ],
1111+ true);
11011112 } else if ((rd == MIPS_CP0_STATUS ) && (sel == 0 )) {
11021113 unsigned int old_val , val , change ;
11031114
0 commit comments