From 65347d38dc1e777230933b1052455b813a1183ee Mon Sep 17 00:00:00 2001 From: Conghui Chen Date: Thu, 12 Sep 2019 09:34:20 +0000 Subject: [PATCH] hv: vuart: support reliable communication The orignal communication port will lose data when tx is too fast, and the data in FIFO will be overwritten. Now, when the FIFO is full, stop sending more data. When the FIFO is not full, notify the vuart to send more data. Tracked-On: #3681 Signed-off-by: Conghui Chen Acked-by: Eddie Dong --- hypervisor/dm/vuart.c | 71 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/hypervisor/dm/vuart.c b/hypervisor/dm/vuart.c index d70a66353..dc80fde8c 100644 --- a/hypervisor/dm/vuart.c +++ b/hypervisor/dm/vuart.c @@ -76,6 +76,24 @@ static inline uint32_t fifo_numchars(const struct vuart_fifo *fifo) return fifo->num; } +static inline bool fifo_isfull(const struct vuart_fifo *fifo) +{ + bool ret = false; + /* When the FIFO has less than 16 empty bytes, it should be + * mask as full. As when the 16550 driver in OS receive the + * THRE interrupt, it will directly send 16 bytes without + * checking the LSR(THRE) */ + + /* Desired value should be 16 bytes, but to improve + * fault-tolerant, enlarge 16 to 64. So that even the THRE + * interrupt is raised by mistake, only if it less than 4 + * times, data in FIFO will not be overwritten. */ + if ((fifo->size - fifo->num) < 64U) { + ret = true; + } + return ret; +} + void vuart_putchar(struct acrn_vuart *vu, char ch) { uint64_t rflags; @@ -178,16 +196,21 @@ void vuart_toggle_intr(const struct acrn_vuart *vu) vioapic_set_irqline_lock(vu->vm, vu->irq, operation); } -static void send_to_target(struct acrn_vuart *vu, uint8_t value_u8) +static bool send_to_target(struct acrn_vuart *vu, uint8_t value_u8) { uint64_t rflags; + bool ret = false; vuart_lock(vu, rflags); if (vu->active) { fifo_putchar(&vu->rxfifo, (char)value_u8); + if (fifo_isfull(&vu->rxfifo)) { + ret = true; + } vuart_toggle_intr(vu); } vuart_unlock(vu, rflags); + return ret; } static uint8_t get_modem_status(uint8_t mcr) @@ -358,11 +381,13 @@ static bool vuart_write(struct acrn_vcpu *vcpu, uint16_t offset_arg, if (((vu->mcr & MCR_LOOPBACK) == 0U) && ((vu->lcr & LCR_DLAB) == 0U) && (offset == UART16550_THR) && (target_vu != NULL)) { - send_to_target(target_vu, value_u8); - vuart_lock(vu, rflags); - vu->thre_int_pending = true; - vuart_toggle_intr(vu); - vuart_unlock(vu, rflags); + if (!send_to_target(target_vu, value_u8)) { + /* FIFO is not full, raise THRE interrupt */ + vuart_lock(vu, rflags); + vu->thre_int_pending = true; + vuart_toggle_intr(vu); + vuart_unlock(vu, rflags); + } } else { write_reg(vu, offset, value_u8); } @@ -370,6 +395,22 @@ static bool vuart_write(struct acrn_vcpu *vcpu, uint16_t offset_arg, return true; } +static void notify_target(const struct acrn_vuart *vu) +{ + struct acrn_vuart *t_vu; + uint64_t rflags; + + if (vu != NULL) { + t_vu = vu->target_vu; + if ((t_vu != NULL) && !fifo_isfull(&vu->rxfifo)) { + vuart_lock(t_vu, rflags); + t_vu->thre_int_pending = true; + vuart_toggle_intr(t_vu); + vuart_unlock(t_vu, rflags); + } + } +} + /** * @pre vcpu != NULL * @pre vcpu->vm != NULL @@ -379,10 +420,12 @@ static bool vuart_read(struct acrn_vcpu *vcpu, uint16_t offset_arg, __unused siz uint16_t offset = offset_arg; uint8_t iir, reg, intr_reason; struct acrn_vuart *vu = find_vuart_by_port(vcpu->vm, offset); + struct acrn_vuart *t_vu; struct pio_request *pio_req = &vcpu->req.reqs.pio; uint64_t rflags; if (vu != NULL) { + t_vu = vu->target_vu; offset -= vu->port_base; vuart_lock(vu, rflags); /* @@ -424,8 +467,13 @@ static bool vuart_read(struct acrn_vcpu *vcpu, uint16_t offset_arg, __unused siz reg = vu->mcr; break; case UART16550_LSR: - /* Transmitter is always ready for more data */ - vu->lsr |= LSR_TEMT | LSR_THRE; + if (t_vu != NULL) { + if (!fifo_isfull(&t_vu->rxfifo)) { + vu->lsr |= LSR_TEMT | LSR_THRE; + } + } else { + vu->lsr |= LSR_TEMT | LSR_THRE; + } /* Check for new receive data */ if (fifo_numchars(&vu->rxfifo) > 0U) { vu->lsr |= LSR_DR; @@ -454,6 +502,13 @@ static bool vuart_read(struct acrn_vcpu *vcpu, uint16_t offset_arg, __unused siz vuart_toggle_intr(vu); pio_req->value = (uint32_t)reg; vuart_unlock(vu, rflags); + + } + + /* For commnunication vuart, when the data in FIFO is read out, should + * notify the target vuart to send more data. */ + if (offset == UART16550_RBR) { + notify_target(vu); } return true;