Skip to content

Commit fbe78f4

Browse files
author
aliguori
committed
virtio-net support
This adds virtio-net support. This is based on the virtio-net driver that exists in kvm-userspace. This also adds a new qemu_sendv_packet which virtio-net requires. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6073 c046a42c-6fe2-441c-8c8c-71466251a162
1 parent fc9902d commit fbe78f4

File tree

6 files changed

+466
-3
lines changed

6 files changed

+466
-3
lines changed

Makefile.target

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
637637
OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
638638
OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
639639
# virtio support
640-
OBJS+= virtio.o virtio-blk.o virtio-balloon.o
640+
OBJS+= virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
641641
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
642642
endif
643643
ifeq ($(TARGET_BASE_ARCH), ppc)
@@ -664,7 +664,7 @@ ifdef CONFIG_KVM
664664
OBJS+= kvm_ppc.o
665665
endif
666666
# virtio support
667-
OBJS+= virtio.o virtio-blk.o virtio-balloon.o
667+
OBJS+= virtio.o virtio-blk.o virtio-balloon.o virtio-net.o
668668
endif
669669
ifeq ($(TARGET_BASE_ARCH), mips)
670670
OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o

hw/pci.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "pci.h"
2626
#include "console.h"
2727
#include "net.h"
28+
#include "virtio-net.h"
2829

2930
//#define DEBUG_PCI
3031

@@ -654,9 +655,11 @@ void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
654655
pci_e1000_init(bus, nd, devfn);
655656
} else if (strcmp(nd->model, "pcnet") == 0) {
656657
pci_pcnet_init(bus, nd, devfn);
658+
} else if (strcmp(nd->model, "virtio") == 0) {
659+
virtio_net_init(bus, nd, devfn);
657660
} else if (strcmp(nd->model, "?") == 0) {
658661
fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
659-
" ne2k_pci pcnet rtl8139 e1000\n");
662+
" ne2k_pci pcnet rtl8139 e1000 virtio\n");
660663
exit (1);
661664
} else {
662665
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);

hw/virtio-net.c

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/*
2+
* Virtio Network Device
3+
*
4+
* Copyright IBM, Corp. 2007
5+
*
6+
* Authors:
7+
* Anthony Liguori <aliguori@us.ibm.com>
8+
*
9+
* This work is licensed under the terms of the GNU GPL, version 2. See
10+
* the COPYING file in the top-level directory.
11+
*
12+
*/
13+
14+
#include "virtio.h"
15+
#include "net.h"
16+
#include "qemu-timer.h"
17+
#include "virtio-net.h"
18+
19+
typedef struct VirtIONet
20+
{
21+
VirtIODevice vdev;
22+
uint8_t mac[6];
23+
VirtQueue *rx_vq;
24+
VirtQueue *tx_vq;
25+
VLANClientState *vc;
26+
QEMUTimer *tx_timer;
27+
int tx_timer_active;
28+
int mergeable_rx_bufs;
29+
} VirtIONet;
30+
31+
/* TODO
32+
* - we could suppress RX interrupt if we were so inclined.
33+
*/
34+
35+
static VirtIONet *to_virtio_net(VirtIODevice *vdev)
36+
{
37+
return (VirtIONet *)vdev;
38+
}
39+
40+
static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
41+
{
42+
VirtIONet *n = to_virtio_net(vdev);
43+
struct virtio_net_config netcfg;
44+
45+
memcpy(netcfg.mac, n->mac, 6);
46+
memcpy(config, &netcfg, sizeof(netcfg));
47+
}
48+
49+
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
50+
{
51+
uint32_t features = (1 << VIRTIO_NET_F_MAC);
52+
53+
return features;
54+
}
55+
56+
static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
57+
{
58+
VirtIONet *n = to_virtio_net(vdev);
59+
60+
n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
61+
}
62+
63+
/* RX */
64+
65+
static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
66+
{
67+
}
68+
69+
static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
70+
{
71+
if (!virtio_queue_ready(n->rx_vq) ||
72+
!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
73+
return 0;
74+
75+
if (virtio_queue_empty(n->rx_vq) ||
76+
(n->mergeable_rx_bufs &&
77+
!virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
78+
virtio_queue_set_notification(n->rx_vq, 1);
79+
return 0;
80+
}
81+
82+
virtio_queue_set_notification(n->rx_vq, 0);
83+
return 1;
84+
}
85+
86+
static int virtio_net_can_receive(void *opaque)
87+
{
88+
VirtIONet *n = opaque;
89+
90+
return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
91+
}
92+
93+
static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)
94+
{
95+
int offset, i;
96+
97+
offset = i = 0;
98+
while (offset < count && i < iovcnt) {
99+
int len = MIN(iov[i].iov_len, count - offset);
100+
memcpy(iov[i].iov_base, buf + offset, len);
101+
offset += len;
102+
i++;
103+
}
104+
105+
return offset;
106+
}
107+
108+
static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
109+
const void *buf, int size, int hdr_len)
110+
{
111+
struct virtio_net_hdr *hdr = iov[0].iov_base;
112+
int offset = 0;
113+
114+
hdr->flags = 0;
115+
hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
116+
117+
/* We only ever receive a struct virtio_net_hdr from the tapfd,
118+
* but we may be passing along a larger header to the guest.
119+
*/
120+
iov[0].iov_base += hdr_len;
121+
iov[0].iov_len -= hdr_len;
122+
123+
return offset;
124+
}
125+
126+
static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
127+
{
128+
VirtIONet *n = opaque;
129+
struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
130+
int hdr_len, offset, i;
131+
132+
if (!do_virtio_net_can_receive(n, size))
133+
return;
134+
135+
/* hdr_len refers to the header we supply to the guest */
136+
hdr_len = n->mergeable_rx_bufs ?
137+
sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
138+
139+
offset = i = 0;
140+
141+
while (offset < size) {
142+
VirtQueueElement elem;
143+
int len, total;
144+
struct iovec sg[VIRTQUEUE_MAX_SIZE];
145+
146+
len = total = 0;
147+
148+
if ((i != 0 && !n->mergeable_rx_bufs) ||
149+
virtqueue_pop(n->rx_vq, &elem) == 0) {
150+
if (i == 0)
151+
return;
152+
fprintf(stderr, "virtio-net truncating packet\n");
153+
exit(1);
154+
}
155+
156+
if (elem.in_num < 1) {
157+
fprintf(stderr, "virtio-net receive queue contains no in buffers\n");
158+
exit(1);
159+
}
160+
161+
if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != hdr_len) {
162+
fprintf(stderr, "virtio-net header not in first element\n");
163+
exit(1);
164+
}
165+
166+
memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
167+
168+
if (i == 0) {
169+
if (n->mergeable_rx_bufs)
170+
mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
171+
172+
offset += receive_header(n, sg, elem.in_num,
173+
buf + offset, size - offset, hdr_len);
174+
total += hdr_len;
175+
}
176+
177+
/* copy in packet. ugh */
178+
len = iov_fill(sg, elem.in_num,
179+
buf + offset, size - offset);
180+
total += len;
181+
182+
/* signal other side */
183+
virtqueue_fill(n->rx_vq, &elem, total, i++);
184+
185+
offset += len;
186+
}
187+
188+
if (mhdr)
189+
mhdr->num_buffers = i;
190+
191+
virtqueue_flush(n->rx_vq, i);
192+
virtio_notify(&n->vdev, n->rx_vq);
193+
}
194+
195+
/* TX */
196+
static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
197+
{
198+
VirtQueueElement elem;
199+
int has_vnet_hdr = 0;
200+
201+
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
202+
return;
203+
204+
while (virtqueue_pop(vq, &elem)) {
205+
ssize_t len = 0;
206+
unsigned int out_num = elem.out_num;
207+
struct iovec *out_sg = &elem.out_sg[0];
208+
unsigned hdr_len;
209+
210+
/* hdr_len refers to the header received from the guest */
211+
hdr_len = n->mergeable_rx_bufs ?
212+
sizeof(struct virtio_net_hdr_mrg_rxbuf) :
213+
sizeof(struct virtio_net_hdr);
214+
215+
if (out_num < 1 || out_sg->iov_len != hdr_len) {
216+
fprintf(stderr, "virtio-net header not in first element\n");
217+
exit(1);
218+
}
219+
220+
/* ignore the header if GSO is not supported */
221+
if (!has_vnet_hdr) {
222+
out_num--;
223+
out_sg++;
224+
len += hdr_len;
225+
} else if (n->mergeable_rx_bufs) {
226+
/* tapfd expects a struct virtio_net_hdr */
227+
hdr_len -= sizeof(struct virtio_net_hdr);
228+
out_sg->iov_len -= hdr_len;
229+
len += hdr_len;
230+
}
231+
232+
len += qemu_sendv_packet(n->vc, out_sg, out_num);
233+
234+
virtqueue_push(vq, &elem, len);
235+
virtio_notify(&n->vdev, vq);
236+
}
237+
}
238+
239+
static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
240+
{
241+
VirtIONet *n = to_virtio_net(vdev);
242+
243+
if (n->tx_timer_active) {
244+
virtio_queue_set_notification(vq, 1);
245+
qemu_del_timer(n->tx_timer);
246+
n->tx_timer_active = 0;
247+
virtio_net_flush_tx(n, vq);
248+
} else {
249+
qemu_mod_timer(n->tx_timer,
250+
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
251+
n->tx_timer_active = 1;
252+
virtio_queue_set_notification(vq, 0);
253+
}
254+
}
255+
256+
static void virtio_net_tx_timer(void *opaque)
257+
{
258+
VirtIONet *n = opaque;
259+
260+
n->tx_timer_active = 0;
261+
262+
/* Just in case the driver is not ready on more */
263+
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
264+
return;
265+
266+
virtio_queue_set_notification(n->tx_vq, 1);
267+
virtio_net_flush_tx(n, n->tx_vq);
268+
}
269+
270+
static void virtio_net_save(QEMUFile *f, void *opaque)
271+
{
272+
VirtIONet *n = opaque;
273+
274+
virtio_save(&n->vdev, f);
275+
276+
qemu_put_buffer(f, n->mac, 6);
277+
qemu_put_be32(f, n->tx_timer_active);
278+
}
279+
280+
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
281+
{
282+
VirtIONet *n = opaque;
283+
284+
if (version_id != 1)
285+
return -EINVAL;
286+
287+
virtio_load(&n->vdev, f);
288+
289+
qemu_get_buffer(f, n->mac, 6);
290+
n->tx_timer_active = qemu_get_be32(f);
291+
292+
if (n->tx_timer_active) {
293+
qemu_mod_timer(n->tx_timer,
294+
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
295+
}
296+
297+
return 0;
298+
}
299+
300+
PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
301+
{
302+
VirtIONet *n;
303+
static int virtio_net_id;
304+
305+
n = (VirtIONet *)virtio_init_pci(bus, "virtio-net", 6900, 0x1000,
306+
0, VIRTIO_ID_NET,
307+
0x02, 0x00, 0x00,
308+
6, sizeof(VirtIONet));
309+
if (!n)
310+
return NULL;
311+
312+
n->vdev.get_config = virtio_net_update_config;
313+
n->vdev.get_features = virtio_net_get_features;
314+
n->vdev.set_features = virtio_net_set_features;
315+
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
316+
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
317+
memcpy(n->mac, nd->macaddr, 6);
318+
n->vc = qemu_new_vlan_client(nd->vlan, virtio_net_receive,
319+
virtio_net_can_receive, n);
320+
321+
n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
322+
n->tx_timer_active = 0;
323+
n->mergeable_rx_bufs = 0;
324+
325+
register_savevm("virtio-net", virtio_net_id++, 1,
326+
virtio_net_save, virtio_net_load, n);
327+
328+
return (PCIDevice *)n;
329+
}

0 commit comments

Comments
 (0)