Commit 6936bfe514bbac7af4b24fad9ed9688b78b5be69

Authored by aurel32
1 parent 7caa33f7

8250: throttle TX-completion IRQs

(Jan Kiszka)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4335 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 49 additions and 6 deletions
hw/serial.c
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 #include "qemu-char.h" 25 #include "qemu-char.h"
26 #include "isa.h" 26 #include "isa.h"
27 #include "pc.h" 27 #include "pc.h"
  28 +#include "qemu-timer.h"
28 29
29 //#define DEBUG_SERIAL 30 //#define DEBUG_SERIAL
30 31
@@ -73,6 +74,13 @@ @@ -73,6 +74,13 @@
73 #define UART_LSR_OE 0x02 /* Overrun error indicator */ 74 #define UART_LSR_OE 0x02 /* Overrun error indicator */
74 #define UART_LSR_DR 0x01 /* Receiver data ready */ 75 #define UART_LSR_DR 0x01 /* Receiver data ready */
75 76
  77 +/*
  78 + * Delay TX IRQ after sending as much characters as the given interval would
  79 + * contain on real hardware. This avoids overloading the guest if it processes
  80 + * its output buffer in a loop inside the TX IRQ handler.
  81 + */
  82 +#define THROTTLE_TX_INTERVAL 10 /* ms */
  83 +
76 struct SerialState { 84 struct SerialState {
77 uint16_t divider; 85 uint16_t divider;
78 uint8_t rbr; /* receive register */ 86 uint8_t rbr; /* receive register */
@@ -91,6 +99,8 @@ struct SerialState { @@ -91,6 +99,8 @@ struct SerialState {
91 int last_break_enable; 99 int last_break_enable;
92 target_phys_addr_t base; 100 target_phys_addr_t base;
93 int it_shift; 101 int it_shift;
  102 + QEMUTimer *tx_timer;
  103 + int tx_burst;
94 }; 104 };
95 105
96 static void serial_receive_byte(SerialState *s, int ch); 106 static void serial_receive_byte(SerialState *s, int ch);
@@ -111,6 +121,28 @@ static void serial_update_irq(SerialState *s) @@ -111,6 +121,28 @@ static void serial_update_irq(SerialState *s)
111 } 121 }
112 } 122 }
113 123
  124 +static void serial_tx_done(void *opaque)
  125 +{
  126 + SerialState *s = opaque;
  127 +
  128 + if (s->tx_burst < 0) {
  129 + uint16_t divider;
  130 +
  131 + if (s->divider)
  132 + divider = s->divider;
  133 + else
  134 + divider = 1;
  135 +
  136 + /* We assume 10 bits/char, OK for this purpose. */
  137 + s->tx_burst = THROTTLE_TX_INTERVAL * 1000 /
  138 + (1000000 * 10 / (115200 / divider));
  139 + }
  140 + s->thr_ipending = 1;
  141 + s->lsr |= UART_LSR_THRE;
  142 + s->lsr |= UART_LSR_TEMT;
  143 + serial_update_irq(s);
  144 +}
  145 +
114 static void serial_update_parameters(SerialState *s) 146 static void serial_update_parameters(SerialState *s)
115 { 147 {
116 int speed, parity, data_bits, stop_bits; 148 int speed, parity, data_bits, stop_bits;
@@ -166,15 +198,18 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -166,15 +198,18 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
166 if (!(s->mcr & UART_MCR_LOOP)) { 198 if (!(s->mcr & UART_MCR_LOOP)) {
167 /* when not in loopback mode, send the char */ 199 /* when not in loopback mode, send the char */
168 qemu_chr_write(s->chr, &ch, 1); 200 qemu_chr_write(s->chr, &ch, 1);
169 - }  
170 - s->thr_ipending = 1;  
171 - s->lsr |= UART_LSR_THRE;  
172 - s->lsr |= UART_LSR_TEMT;  
173 - serial_update_irq(s);  
174 - if (s->mcr & UART_MCR_LOOP) { 201 + } else {
175 /* in loopback mode, say that we just received a char */ 202 /* in loopback mode, say that we just received a char */
176 serial_receive_byte(s, ch); 203 serial_receive_byte(s, ch);
177 } 204 }
  205 + if (s->tx_burst > 0) {
  206 + s->tx_burst--;
  207 + serial_tx_done(s);
  208 + } else if (s->tx_burst == 0) {
  209 + s->tx_burst--;
  210 + qemu_mod_timer(s->tx_timer, qemu_get_clock(vm_clock) +
  211 + ticks_per_sec * THROTTLE_TX_INTERVAL / 1000);
  212 + }
178 } 213 }
179 break; 214 break;
180 case 1: 215 case 1:
@@ -387,6 +422,10 @@ SerialState *serial_init(int base, qemu_irq irq, CharDriverState *chr) @@ -387,6 +422,10 @@ SerialState *serial_init(int base, qemu_irq irq, CharDriverState *chr)
387 return NULL; 422 return NULL;
388 s->irq = irq; 423 s->irq = irq;
389 424
  425 + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s);
  426 + if (!s->tx_timer)
  427 + return NULL;
  428 +
390 qemu_register_reset(serial_reset, s); 429 qemu_register_reset(serial_reset, s);
391 serial_reset(s); 430 serial_reset(s);
392 431
@@ -486,6 +525,10 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, @@ -486,6 +525,10 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
486 s->base = base; 525 s->base = base;
487 s->it_shift = it_shift; 526 s->it_shift = it_shift;
488 527
  528 + s->tx_timer = qemu_new_timer(vm_clock, serial_tx_done, s);
  529 + if (!s->tx_timer)
  530 + return NULL;
  531 +
489 qemu_register_reset(serial_reset, s); 532 qemu_register_reset(serial_reset, s);
490 serial_reset(s); 533 serial_reset(s);
491 534