Commit 6936bfe514bbac7af4b24fad9ed9688b78b5be69
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 | 25 | #include "qemu-char.h" |
| 26 | 26 | #include "isa.h" |
| 27 | 27 | #include "pc.h" |
| 28 | +#include "qemu-timer.h" | |
| 28 | 29 | |
| 29 | 30 | //#define DEBUG_SERIAL |
| 30 | 31 | |
| ... | ... | @@ -73,6 +74,13 @@ |
| 73 | 74 | #define UART_LSR_OE 0x02 /* Overrun error indicator */ |
| 74 | 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 | 84 | struct SerialState { |
| 77 | 85 | uint16_t divider; |
| 78 | 86 | uint8_t rbr; /* receive register */ |
| ... | ... | @@ -91,6 +99,8 @@ struct SerialState { |
| 91 | 99 | int last_break_enable; |
| 92 | 100 | target_phys_addr_t base; |
| 93 | 101 | int it_shift; |
| 102 | + QEMUTimer *tx_timer; | |
| 103 | + int tx_burst; | |
| 94 | 104 | }; |
| 95 | 105 | |
| 96 | 106 | static void serial_receive_byte(SerialState *s, int ch); |
| ... | ... | @@ -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 | 146 | static void serial_update_parameters(SerialState *s) |
| 115 | 147 | { |
| 116 | 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 | 198 | if (!(s->mcr & UART_MCR_LOOP)) { |
| 167 | 199 | /* when not in loopback mode, send the char */ |
| 168 | 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 | 202 | /* in loopback mode, say that we just received a char */ |
| 176 | 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 | 214 | break; |
| 180 | 215 | case 1: |
| ... | ... | @@ -387,6 +422,10 @@ SerialState *serial_init(int base, qemu_irq irq, CharDriverState *chr) |
| 387 | 422 | return NULL; |
| 388 | 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 | 429 | qemu_register_reset(serial_reset, s); |
| 391 | 430 | serial_reset(s); |
| 392 | 431 | |
| ... | ... | @@ -486,6 +525,10 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, |
| 486 | 525 | s->base = base; |
| 487 | 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 | 532 | qemu_register_reset(serial_reset, s); |
| 490 | 533 | serial_reset(s); |
| 491 | 534 | ... | ... |