84 lines
3.0 KiB
Diff
84 lines
3.0 KiB
Diff
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||
|
Date: Tue, 15 Feb 2011 18:27:55 +0200
|
||
|
Subject: e1000: verify we have buffers, upfront
|
||
|
Patch-mainline: v0.15.0-rc0
|
||
|
Git-commit: 322fd48afbed1ef7b834ac343a0c8687bcb33695
|
||
|
References: bnc#840196
|
||
|
|
||
|
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>
|
||
|
Acked-by: Michal Kubecek <mkubecek@suse.cz>
|
||
|
---
|
||
|
tools/qemu-xen-traditional-dir-remote/hw/e1000.c | 28 +++++++++++++++++++-----
|
||
|
1 file changed, 22 insertions(+), 6 deletions(-)
|
||
|
|
||
|
diff --git a/tools/qemu-xen-traditional-dir-remote/hw/e1000.c b/tools/qemu-xen-traditional-dir-remote/hw/e1000.c
|
||
|
index 7e791dc..18d7597 100644
|
||
|
--- a/tools/qemu-xen-traditional-dir-remote/hw/e1000.c
|
||
|
+++ b/tools/qemu-xen-traditional-dir-remote/hw/e1000.c
|
||
|
@@ -622,6 +622,24 @@ e1000_can_receive(void *opaque)
|
||
|
return (s->mac_reg[RCTL] & E1000_RCTL_EN && s->mac_reg[RDLEN] != 0);
|
||
|
}
|
||
|
|
||
|
+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 void
|
||
|
e1000_receive(void *opaque, const uint8_t *buf, int size)
|
||
|
{
|
||
|
@@ -667,17 +685,15 @@ e1000_receive(void *opaque, const uint8_t *buf, int 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;
|
||
|
+ }
|
||
|
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;
|
||
|
- }
|
||
|
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));
|
||
|
--
|
||
|
1.8.1.4
|
||
|
|