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,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 |