e1000: verify we have buffers, upfront
The spec says: Any descriptor with a non-zero status byte has been processed by the hardware, and is ready to be handled by the software. Thus, once we change a descriptor status to non-zero we should never move the head backwards and try to reuse this descriptor from hardware. This actually happened with a multibuffer packet that arrives when we don't have enough buffers. Fix by checking that we have enough buffers upfront so we never need to discard the packet midway through. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Alex Williamson <alex.williamson@redhat.com> Acked-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
parent
ee912ccfa0
commit
322fd48afb
28
hw/e1000.c
28
hw/e1000.c
@ -631,6 +631,24 @@ e1000_can_receive(VLANClientState *nc)
|
||||
return (s->mac_reg[RCTL] & E1000_RCTL_EN);
|
||||
}
|
||||
|
||||
static bool e1000_has_rxbufs(E1000State *s, size_t total_size)
|
||||
{
|
||||
int bufs;
|
||||
/* Fast-path short packets */
|
||||
if (total_size <= s->rxbuf_size) {
|
||||
return s->mac_reg[RDH] != s->mac_reg[RDT] || !s->check_rxov;
|
||||
}
|
||||
if (s->mac_reg[RDH] < s->mac_reg[RDT]) {
|
||||
bufs = s->mac_reg[RDT] - s->mac_reg[RDH];
|
||||
} else if (s->mac_reg[RDH] > s->mac_reg[RDT] || !s->check_rxov) {
|
||||
bufs = s->mac_reg[RDLEN] / sizeof(struct e1000_rx_desc) +
|
||||
s->mac_reg[RDT] - s->mac_reg[RDH];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return total_size <= bufs * s->rxbuf_size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
|
||||
{
|
||||
@ -671,17 +689,15 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
|
||||
rdh_start = s->mac_reg[RDH];
|
||||
desc_offset = 0;
|
||||
total_size = size + fcs_len(s);
|
||||
if (!e1000_has_rxbufs(s, total_size)) {
|
||||
set_ics(s, 0, E1000_ICS_RXO);
|
||||
return -1;
|
||||
}
|
||||
do {
|
||||
desc_size = total_size - desc_offset;
|
||||
if (desc_size > s->rxbuf_size) {
|
||||
desc_size = s->rxbuf_size;
|
||||
}
|
||||
if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
|
||||
/* Discard all data written so far */
|
||||
s->mac_reg[RDH] = rdh_start;
|
||||
set_ics(s, 0, E1000_ICS_RXO);
|
||||
return -1;
|
||||
}
|
||||
base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] +
|
||||
sizeof(desc) * s->mac_reg[RDH];
|
||||
cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
|
||||
|
Loading…
Reference in New Issue
Block a user