From: <rongqing.li@windriver.com>
To: qemu-devel@nongnu.org
Cc: stefanha@linux.vnet.ibm.com
Subject: [Qemu-devel] [PATCH 1/2] Usb: create the receive queue for the virtual USB NIC
Date: Fri, 24 Aug 2012 15:56:23 +0800 [thread overview]
Message-ID: <1345794984-10337-1-git-send-email-rongqing.li@windriver.com> (raw)
From: Roy.Li <rongqing.li@windriver.com>
The virtual USB NIC originally used a fixed buffer to receive packets which
only store 1 packet at a time, which is easy to overrun with packets if the
guest does not consume it quickly, and always lost packets at the below case:
The emulated machine is using an USB-ethernet adapter,
it is connected to the network using SLIRP and I'm
dumping the traffic in a .pcap file. As per the following
command line :
-net nic,model=usb,vlan=1 -net user,vlan=1 -net dump,vlan=1,file=/tmp/pkt.pcap
Every time that two packets are coming in a row from
the host, the usb-net code will receive the first one,
then returns 0 to can_receive call since it has a
1 packet long queue. But as the dump code is always
ready to receive, qemu_can_send_packet will return
true and the next packet will discard the previous
one in the usb-net code.
Commit 60c07d933c(net: fix qemu_can_send_packet logic) is trying to fix,
but the logic is wrong and introduce other bug. Now replace the fixed buffer
with a receive queue which can accept a variable number of packets to fix
this bug.
Signed-off-by: Roy.Li <rongqing.li@windriver.com>
---
hw/usb/dev-network.c | 87 ++++++++++++++++++++++++++++++++++++--------------
1 files changed, 63 insertions(+), 24 deletions(-)
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index c84892c..0c867b7 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -87,6 +87,7 @@ enum usbstring_idx {
#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+#define MAX_RCV_QUEUE 45 /* Max length of receiving queue */
static const USBDescStrings usb_net_stringtable = {
[STRING_MANUFACTURER] = "QEMU",
@@ -622,6 +623,12 @@ struct rndis_response {
uint8_t buf[0];
};
+struct USBNet_buffer {
+ QTAILQ_ENTRY(USBNet_buffer) entries;
+ unsigned int in_ptr, in_len;
+ uint8_t in_buf[0];
+};
+
typedef struct USBNetState {
USBDevice dev;
@@ -636,8 +643,8 @@ typedef struct USBNetState {
uint8_t out_buf[2048];
USBPacket *inpkt;
- unsigned int in_ptr, in_len;
- uint8_t in_buf[2048];
+ QTAILQ_HEAD(USBNet_buffer_head, USBNet_buffer) rndis_netbuf;
+ uint32_t buf_len;
char usbstring_mac[13];
NICState *nic;
@@ -867,6 +874,17 @@ static void rndis_clear_responsequeue(USBNetState *s)
}
}
+static void rndis_clear_netbuffer(USBNetState *s)
+{
+ struct USBNet_buffer *r;
+
+ while ((r = s->rndis_netbuf.tqh_first)) {
+ QTAILQ_REMOVE(&s->rndis_netbuf, r, entries);
+ g_free(r);
+ }
+ s->buf_len = 0;
+}
+
static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf)
{
rndis_init_cmplt_type *resp =
@@ -1025,7 +1043,8 @@ static int rndis_parse(USBNetState *s, uint8_t *data, int length)
case RNDIS_RESET_MSG:
rndis_clear_responsequeue(s);
- s->out_ptr = s->in_ptr = s->in_len = 0;
+ rndis_clear_netbuffer(s);
+ s->out_ptr = 0;
return rndis_reset_response(s, (rndis_reset_msg_type *) data);
case RNDIS_KEEPALIVE_MSG:
@@ -1134,25 +1153,39 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
{
int ret = USB_RET_NAK;
- if (s->in_ptr > s->in_len) {
- s->in_ptr = s->in_len = 0;
- ret = USB_RET_NAK;
+ struct USBNet_buffer *r = s->rndis_netbuf.tqh_first;
+
+ if (!r) {
return ret;
}
- if (!s->in_len) {
- ret = USB_RET_NAK;
+
+ if (r->in_ptr > r->in_len) {
+ s->buf_len--;
+ QTAILQ_REMOVE(&s->rndis_netbuf, r, entries);
+ g_free(r);
+ return ret;
+ }
+
+ if (!r->in_len) {
+ s->buf_len--;
+ QTAILQ_REMOVE(&s->rndis_netbuf, r, entries);
+ g_free(r);
return ret;
}
- ret = s->in_len - s->in_ptr;
+
+ ret = r->in_len - r->in_ptr;
if (ret > p->iov.size) {
ret = p->iov.size;
}
- usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
- s->in_ptr += ret;
- if (s->in_ptr >= s->in_len &&
- (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
+
+ usb_packet_copy(p, &r->in_buf[r->in_ptr], ret);
+ r->in_ptr += ret;
+ if (r->in_ptr >= r->in_len &&
+ (is_rndis(s) || (r->in_len & (64 - 1)) || !ret)) {
/* no short packet necessary */
- s->in_ptr = s->in_len = 0;
+ s->buf_len--;
+ QTAILQ_REMOVE(&s->rndis_netbuf, r, entries);
+ g_free(r);
}
#ifdef TRAFFIC_DEBUG
@@ -1251,15 +1284,16 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
{
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
struct rndis_packet_msg_type *msg;
+ struct USBNet_buffer *r;
if (is_rndis(s)) {
- msg = (struct rndis_packet_msg_type *) s->in_buf;
if (s->rndis_state != RNDIS_DATA_INITIALIZED) {
return -1;
}
- if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
- return -1;
+ r = g_malloc0(sizeof(struct USBNet_buffer) + \
+ sizeof(struct rndis_packet_msg_type) + size);
+ msg = (struct rndis_packet_msg_type *) r->in_buf;
memset(msg, 0, sizeof(struct rndis_packet_msg_type));
msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type));
@@ -1274,14 +1308,16 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
* msg->Reserved;
*/
memcpy(msg + 1, buf, size);
- s->in_len = size + sizeof(struct rndis_packet_msg_type);
+ r->in_len = size + sizeof(struct rndis_packet_msg_type);
} else {
- if (size > sizeof(s->in_buf))
- return -1;
- memcpy(s->in_buf, buf, size);
- s->in_len = size;
+ r = g_malloc0(sizeof(struct USBNet_buffer) + size);
+ memcpy(r->in_buf, buf, size);
+ r->in_len = size;
}
- s->in_ptr = 0;
+ r->in_ptr = 0;
+ s->buf_len++;
+ QTAILQ_INSERT_TAIL(&s->rndis_netbuf, r, entries);
+
return size;
}
@@ -1293,7 +1329,7 @@ static int usbnet_can_receive(NetClientState *nc)
return 1;
}
- return !s->in_len;
+ return s->buf_len < MAX_RCV_QUEUE;
}
static void usbnet_cleanup(NetClientState *nc)
@@ -1309,6 +1345,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
/* TODO: remove the nd_table[] entry */
rndis_clear_responsequeue(s);
+ rndis_clear_netbuffer(s);
qemu_del_net_client(&s->nic->nc);
}
@@ -1329,12 +1366,14 @@ static int usb_net_initfn(USBDevice *dev)
s->rndis_state = RNDIS_UNINITIALIZED;
QTAILQ_INIT(&s->rndis_resp);
+ QTAILQ_INIT(&s->rndis_netbuf);
s->medium = 0; /* NDIS_MEDIUM_802_3 */
s->speed = 1000000; /* 100MBps, in 100Bps units */
s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */;
s->filter = 0;
s->vendorid = 0x1234;
+ s->buf_len = 0;
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
--
1.7.4.1
next reply other threads:[~2012-08-24 7:56 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-24 7:56 rongqing.li [this message]
2012-08-24 7:56 ` [Qemu-devel] [PATCH 2/2] hub: change hub can_receive() strategy rongqing.li
2012-08-24 8:20 ` Paolo Bonzini
2012-08-24 8:33 ` Rongqing Li
2012-08-24 10:11 ` Stefan Hajnoczi
2012-08-24 12:25 ` [Qemu-devel] [PATCH 1/2] Usb: create the receive queue for the virtual USB NIC Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1345794984-10337-1-git-send-email-rongqing.li@windriver.com \
--to=rongqing.li@windriver.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@linux.vnet.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).