2 * Virtio Network Device
4 * Copyright IBM, Corp. 2007
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
17 #include "qemu-timer.h"
19 /* from Linux's virtio_net.h */
21 /* The ID for virtio_net */
22 #define VIRTIO_ID_NET 1
24 /* The feature bitmap for virtio net */
25 #define VIRTIO_NET_F_NO_CSUM 0
26 #define VIRTIO_NET_F_TSO4 1
27 #define VIRTIO_NET_F_UFO 2
28 #define VIRTIO_NET_F_TSO4_ECN 3
29 #define VIRTIO_NET_F_TSO6 4
30 #define VIRTIO_NET_F_MAC 5
34 #define TX_TIMER_INTERVAL (1000 / 500)
36 /* The config defining mac address (6 bytes) */
37 struct virtio_net_config
40 } __attribute__((packed
));
42 /* This is the first element of the scatter-gather list. If you don't
43 * specify GSO or CSUM features, you can simply ignore the header. */
46 #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
48 #define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
49 #define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
50 /* FIXME: Do we need this? If they said they can handle ECN, do they care? */
51 #define VIRTIO_NET_HDR_GSO_TCPV4_ECN 2 // GSO frame, IPv4 TCP w/ ECN
52 #define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
53 #define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
60 typedef struct VirtIONet
69 struct VirtIONet
*next
;
75 static VirtIONet
*VirtIONetHead
= NULL
;
77 static VirtIONet
*to_virtio_net(VirtIODevice
*vdev
)
79 return (VirtIONet
*)vdev
;
82 static void virtio_net_update_config(VirtIODevice
*vdev
, uint8_t *config
)
84 VirtIONet
*n
= to_virtio_net(vdev
);
85 struct virtio_net_config netcfg
;
87 memcpy(netcfg
.mac
, n
->mac
, 6);
88 memcpy(config
, &netcfg
, sizeof(netcfg
));
91 static uint32_t virtio_net_get_features(VirtIODevice
*vdev
)
93 return (1 << VIRTIO_NET_F_MAC
);
98 static void virtio_net_handle_rx(VirtIODevice
*vdev
, VirtQueue
*vq
)
100 VirtIONet
*n
= to_virtio_net(vdev
);
104 static int virtio_net_can_receive(void *opaque
)
106 VirtIONet
*n
= opaque
;
108 return (n
->vdev
.status
& VIRTIO_CONFIG_S_DRIVER_OK
) && n
->can_receive
;
111 /* -net user receive function */
112 static void virtio_net_receive(void *opaque
, const uint8_t *buf
, int size
)
114 VirtIONet
*n
= opaque
;
115 VirtQueueElement elem
;
116 struct virtio_net_hdr
*hdr
;
119 /* FIXME: the drivers really need to set their status better */
120 if (n
->rx_vq
->vring
.avail
== NULL
) {
125 if (virtqueue_pop(n
->rx_vq
, &elem
) == 0) {
126 /* wait until the guest adds some rx bufs */
131 hdr
= (void *)elem
.in_sg
[0].iov_base
;
133 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_NONE
;
135 /* copy in packet. ugh */
138 while (offset
< size
&& i
< elem
.in_num
) {
139 int len
= MIN(elem
.in_sg
[i
].iov_len
, size
- offset
);
140 memcpy(elem
.in_sg
[i
].iov_base
, buf
+ offset
, len
);
145 /* signal other side */
146 virtqueue_push(n
->rx_vq
, &elem
, sizeof(*hdr
) + offset
);
147 virtio_notify(&n
->vdev
, n
->rx_vq
);
150 /* -net tap receive handler */
151 void virtio_net_poll(void)
158 VirtQueueElement elem
;
159 struct virtio_net_hdr
*hdr
;
168 // Prepare the set of device to select from
169 for (vnet
= VirtIONetHead
; vnet
; vnet
= vnet
->next
) {
171 if (vnet
->tap_fd
== -1)
175 //first check if the driver is ok
176 if (!virtio_net_can_receive(vnet
))
179 /* FIXME: the drivers really need to set their status better */
180 if (vnet
->rx_vq
->vring
.avail
== NULL
) {
181 vnet
->can_receive
= 0;
185 FD_SET(vnet
->tap_fd
, &rfds
);
186 if (max_fd
< vnet
->tap_fd
) max_fd
= vnet
->tap_fd
;
189 if (select(max_fd
+ 1, &rfds
, NULL
, NULL
, &tv
) <= 0)
192 // Now check who has data pending in the tap
193 for (vnet
= VirtIONetHead
; vnet
; vnet
= vnet
->next
) {
195 if (!FD_ISSET(vnet
->tap_fd
, &rfds
))
198 if (virtqueue_pop(vnet
->rx_vq
, &elem
) == 0) {
199 vnet
->can_receive
= 0;
203 hdr
= (void *)elem
.in_sg
[0].iov_base
;
205 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_NONE
;
207 len
= readv(vnet
->tap_fd
, &elem
.in_sg
[1], elem
.in_num
- 1);
209 if (errno
== EINTR
|| errno
== EAGAIN
)
212 fprintf(stderr
, "reading network error %d", len
);
214 virtqueue_push(vnet
->rx_vq
, &elem
, sizeof(*hdr
) + len
);
218 /* signal other side */
220 for (vnet
= VirtIONetHead
; vnet
; vnet
= vnet
->next
)
221 if (vnet
->do_notify
) {
222 virtio_notify(&vnet
->vdev
, vnet
->rx_vq
);
232 static void virtio_net_flush_tx(VirtIONet
*n
, VirtQueue
*vq
)
234 VirtQueueElement elem
;
237 if (!(n
->vdev
.status
& VIRTIO_CONFIG_S_DRIVER_OK
))
240 while (virtqueue_pop(vq
, &elem
)) {
244 /* ignore the header for now */
245 for (i
= 1; i
< elem
.out_num
; i
++) {
246 qemu_send_packet(n
->vc
, elem
.out_sg
[i
].iov_base
,
247 elem
.out_sg
[i
].iov_len
);
248 len
+= elem
.out_sg
[i
].iov_len
;
253 virtqueue_push(vq
, &elem
, sizeof(struct virtio_net_hdr
) + len
);
254 virtio_notify(&n
->vdev
, vq
);
258 static void virtio_net_handle_tx(VirtIODevice
*vdev
, VirtQueue
*vq
)
260 VirtIONet
*n
= to_virtio_net(vdev
);
262 if (n
->tx_timer_active
&&
263 (vq
->vring
.avail
->idx
- vq
->last_avail_idx
) == 64) {
264 vq
->vring
.used
->flags
&= ~VRING_USED_F_NOTIFY_ON_FULL
;
265 qemu_del_timer(n
->tx_timer
);
266 n
->tx_timer_active
= 0;
267 virtio_net_flush_tx(n
, vq
);
269 qemu_mod_timer(n
->tx_timer
, qemu_get_clock(vm_clock
) + TX_TIMER_INTERVAL
);
270 n
->tx_timer_active
= 1;
271 vq
->vring
.used
->flags
|= VRING_USED_F_NOTIFY_ON_FULL
;
275 static void virtio_net_tx_timer(void *opaque
)
277 VirtIONet
*n
= opaque
;
279 n
->tx_vq
->vring
.used
->flags
&= ~VRING_USED_F_NOTIFY_ON_FULL
;
280 n
->tx_timer_active
= 0;
281 virtio_net_flush_tx(n
, n
->tx_vq
);
284 void *virtio_net_init(PCIBus
*bus
, NICInfo
*nd
, int devfn
)
288 n
= (VirtIONet
*)virtio_init_pci(bus
, "virtio-net", 6900, 0x1000,
291 6, sizeof(VirtIONet
));
293 n
->vdev
.update_config
= virtio_net_update_config
;
294 n
->vdev
.get_features
= virtio_net_get_features
;
295 n
->rx_vq
= virtio_add_queue(&n
->vdev
, 512, virtio_net_handle_rx
);
296 n
->tx_vq
= virtio_add_queue(&n
->vdev
, 128, virtio_net_handle_tx
);
298 memcpy(n
->mac
, nd
->macaddr
, 6);
299 n
->vc
= qemu_new_vlan_client(nd
->vlan
, virtio_net_receive
,
300 virtio_net_can_receive
, n
);
301 n
->tap_fd
= hack_around_tap(n
->vc
->vlan
->first_client
);
302 if (n
->tap_fd
!= -1) {
303 n
->next
= VirtIONetHead
;
304 //push the device on top of the list
308 n
->tx_timer
= qemu_new_timer(vm_clock
, virtio_net_tx_timer
, n
);
309 n
->tx_timer_active
= 0;