diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 8fef598b49..46e156a5dd 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1392,10 +1392,13 @@ e1000e_write_payload_frag_to_rx_buffers(E1000ECore *core, dma_addr_t data_len) { while (data_len > 0) { - uint32_t cur_buf_len = core->rxbuf_sizes[bastate->cur_idx]; - uint32_t cur_buf_bytes_left = cur_buf_len - - bastate->written[bastate->cur_idx]; - uint32_t bytes_to_write = MIN(data_len, cur_buf_bytes_left); + uint32_t cur_buf_len, cur_buf_bytes_left, bytes_to_write; + + assert(bastate->cur_idx < MAX_PS_BUFFERS); + + cur_buf_len = core->rxbuf_sizes[bastate->cur_idx]; + cur_buf_bytes_left = cur_buf_len - bastate->written[bastate->cur_idx]; + bytes_to_write = MIN(data_len, cur_buf_bytes_left); trace_e1000e_rx_desc_buff_write(bastate->cur_idx, ba[bastate->cur_idx], @@ -1414,8 +1417,6 @@ e1000e_write_payload_frag_to_rx_buffers(E1000ECore *core, if (bastate->written[bastate->cur_idx] == cur_buf_len) { bastate->cur_idx++; } - - assert(bastate->cur_idx < MAX_PS_BUFFERS); } } @@ -1481,7 +1482,6 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, PCIDevice *d = core->owner; dma_addr_t base; union e1000_rx_desc_union desc; - size_t desc_size; size_t desc_offset = 0; size_t iov_ofs = 0; @@ -1496,16 +1496,17 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, rxi = rxr->i; do { + /* + * Loop processing descriptors while we have packet data to + * DMA to the guest. desc_offset tracks how much data we have + * sent to the guest in total over all descriptors, and goes + * from 0 up to total_size (the size of everything to send to + * the guest including possible trailing 4 bytes of CRC data). + */ hwaddr ba[MAX_PS_BUFFERS]; E1000EBAState bastate = { { 0 } }; bool is_last = false; - desc_size = total_size - desc_offset; - - if (desc_size > core->rx_desc_buf_size) { - desc_size = core->rx_desc_buf_size; - } - if (e1000e_ring_empty(core, rxi)) { return; } @@ -1519,17 +1520,27 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, e1000e_read_rx_descr(core, &desc, ba); if (ba[0]) { + /* Total amount of data DMA'd to the guest in this iteration */ + size_t desc_size = 0; + /* + * Total space available in this descriptor (we will update + * this as we use it up) + */ + size_t rx_desc_buf_size = core->rx_desc_buf_size; + if (desc_offset < size) { - static const uint32_t fcs_pad; size_t iov_copy; + /* Amount of data to copy from the incoming packet */ size_t copy_size = size - desc_offset; - if (copy_size > core->rx_desc_buf_size) { - copy_size = core->rx_desc_buf_size; - } /* For PS mode copy the packet header first */ if (do_ps) { if (is_first) { + /* + * e1000e_do_ps() guarantees that buffer 0 has enough + * space for the header; otherwise we will not split + * the packet (i.e. do_ps is false). + */ size_t ps_hdr_copied = 0; do { iov_copy = MIN(ps_hdr_len - ps_hdr_copied, @@ -1551,14 +1562,26 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, } while (ps_hdr_copied < ps_hdr_len); is_first = false; + desc_size += ps_hdr_len; } else { /* Leave buffer 0 of each descriptor except first */ /* empty as per spec 7.1.5.1 */ e1000e_write_hdr_frag_to_rx_buffers(core, ba, &bastate, NULL, 0); } + rx_desc_buf_size -= core->rxbuf_sizes[0]; } + /* + * Clamp the amount of packet data we copy into what will fit + * into the remaining buffers in the descriptor. + */ + if (copy_size > rx_desc_buf_size) { + copy_size = rx_desc_buf_size; + } + desc_size += copy_size; + rx_desc_buf_size -= copy_size; + /* Copy packet payload */ while (copy_size) { iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); @@ -1575,20 +1598,30 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, iov_ofs = 0; } } + } - if (desc_offset + desc_size >= total_size) { - /* Simulate FCS checksum presence in the last descriptor */ - e1000e_write_payload_frag_to_rx_buffers(core, ba, &bastate, - (const char *) &fcs_pad, e1000x_fcs_len(core->mac)); - } + if (rx_desc_buf_size && + desc_offset >= size && desc_offset < total_size) { + /* + * We are in the last 4 bytes corresponding to the FCS checksum. + * We only ever write zeroes here (unlike the hardware). + */ + static const uint32_t fcs_pad; + /* Amount of space for the trailing checksum */ + size_t fcs_len = MIN(rx_desc_buf_size, + total_size - desc_offset); + e1000e_write_payload_frag_to_rx_buffers(core, ba, &bastate, + (const char *)&fcs_pad, + fcs_len); + desc_size += fcs_len; + } + desc_offset += desc_size; + if (desc_offset >= total_size) { + is_last = true; } } else { /* as per intel docs; skip descriptors with null buf addr */ trace_e1000e_rx_null_descriptor(); } - desc_offset += desc_size; - if (desc_offset >= total_size) { - is_last = true; - } e1000e_write_rx_descr(core, &desc, is_last ? core->rx_pkt : NULL, rss_info, do_ps ? ps_hdr_len : 0, &bastate.written); diff --git a/net/hub.c b/net/hub.c index e3b58b1c4f..ee5881f6d5 100644 --- a/net/hub.c +++ b/net/hub.c @@ -34,6 +34,7 @@ typedef struct NetHubPort { QLIST_ENTRY(NetHubPort) next; NetHub *hub; int id; + bool listed; } NetHubPort; struct NetHub { @@ -129,7 +130,10 @@ static void net_hub_port_cleanup(NetClientState *nc) { NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); - QLIST_REMOVE(port, next); + if (port->listed) { + QLIST_REMOVE(port, next); + port->listed = false; + } } static NetClientInfo net_hub_port_info = { @@ -159,8 +163,10 @@ static NetHubPort *net_hub_port_new(NetHub *hub, const char *name, port = DO_UPCAST(NetHubPort, nc, nc); port->id = id; port->hub = hub; + port->listed = false; QLIST_INSERT_HEAD(&hub->ports, port, next); + port->listed = true; return port; } diff --git a/net/net.c b/net/net.c index 27e0d27807..8aefdb3424 100644 --- a/net/net.c +++ b/net/net.c @@ -775,10 +775,20 @@ ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size) ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size) { + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz = sizeof(min_pkt); + if (!qemu_can_receive_packet(nc)) { return 0; } + if (net_peer_needs_padding(nc)) { + if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { + buf = min_pkt; + size = min_pktsz; + } + } + return qemu_net_queue_receive(nc->incoming_queue, buf, size); }