blob: 5eca19cd7865128be94cbc6bc88df0952353f2b3 [file] [log] [blame]
aliguorifbe78f42008-12-17 19:13:11 +00001/*
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
19typedef struct VirtIONet
20{
21 VirtIODevice vdev;
22 uint8_t mac[6];
aliguori554c97d2009-01-08 19:46:33 +000023 uint16_t status;
aliguorifbe78f42008-12-17 19:13:11 +000024 VirtQueue *rx_vq;
25 VirtQueue *tx_vq;
26 VLANClientState *vc;
27 QEMUTimer *tx_timer;
28 int tx_timer_active;
29 int mergeable_rx_bufs;
30} VirtIONet;
31
32/* TODO
33 * - we could suppress RX interrupt if we were so inclined.
34 */
35
36static VirtIONet *to_virtio_net(VirtIODevice *vdev)
37{
38 return (VirtIONet *)vdev;
39}
40
41static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
42{
43 VirtIONet *n = to_virtio_net(vdev);
44 struct virtio_net_config netcfg;
45
aliguori554c97d2009-01-08 19:46:33 +000046 netcfg.status = n->status;
aliguorifbe78f42008-12-17 19:13:11 +000047 memcpy(netcfg.mac, n->mac, 6);
48 memcpy(config, &netcfg, sizeof(netcfg));
49}
50
aliguori554c97d2009-01-08 19:46:33 +000051static void virtio_net_set_link_status(VLANClientState *vc)
52{
53 VirtIONet *n = vc->opaque;
54 uint16_t old_status = n->status;
55
56 if (vc->link_down)
57 n->status &= ~VIRTIO_NET_S_LINK_UP;
58 else
59 n->status |= VIRTIO_NET_S_LINK_UP;
60
61 if (n->status != old_status)
62 virtio_notify_config(&n->vdev);
63}
64
aliguorifbe78f42008-12-17 19:13:11 +000065static uint32_t virtio_net_get_features(VirtIODevice *vdev)
66{
aliguori554c97d2009-01-08 19:46:33 +000067 uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
aliguorifbe78f42008-12-17 19:13:11 +000068
69 return features;
70}
71
72static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
73{
74 VirtIONet *n = to_virtio_net(vdev);
75
76 n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
77}
78
79/* RX */
80
81static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
82{
83}
84
85static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
86{
87 if (!virtio_queue_ready(n->rx_vq) ||
88 !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
89 return 0;
90
91 if (virtio_queue_empty(n->rx_vq) ||
92 (n->mergeable_rx_bufs &&
93 !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
94 virtio_queue_set_notification(n->rx_vq, 1);
95 return 0;
96 }
97
98 virtio_queue_set_notification(n->rx_vq, 0);
99 return 1;
100}
101
102static int virtio_net_can_receive(void *opaque)
103{
104 VirtIONet *n = opaque;
105
106 return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
107}
108
109static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)
110{
111 int offset, i;
112
113 offset = i = 0;
114 while (offset < count && i < iovcnt) {
115 int len = MIN(iov[i].iov_len, count - offset);
116 memcpy(iov[i].iov_base, buf + offset, len);
117 offset += len;
118 i++;
119 }
120
121 return offset;
122}
123
124static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
aliguori4689f4b2008-12-17 19:45:40 +0000125 const void *buf, size_t size, size_t hdr_len)
aliguorifbe78f42008-12-17 19:13:11 +0000126{
127 struct virtio_net_hdr *hdr = iov[0].iov_base;
128 int offset = 0;
129
130 hdr->flags = 0;
131 hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
132
133 /* We only ever receive a struct virtio_net_hdr from the tapfd,
134 * but we may be passing along a larger header to the guest.
135 */
136 iov[0].iov_base += hdr_len;
137 iov[0].iov_len -= hdr_len;
138
139 return offset;
140}
141
142static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
143{
144 VirtIONet *n = opaque;
145 struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
aliguori4689f4b2008-12-17 19:45:40 +0000146 size_t hdr_len, offset, i;
aliguorifbe78f42008-12-17 19:13:11 +0000147
148 if (!do_virtio_net_can_receive(n, size))
149 return;
150
151 /* hdr_len refers to the header we supply to the guest */
152 hdr_len = n->mergeable_rx_bufs ?
153 sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
154
155 offset = i = 0;
156
157 while (offset < size) {
158 VirtQueueElement elem;
159 int len, total;
160 struct iovec sg[VIRTQUEUE_MAX_SIZE];
161
162 len = total = 0;
163
164 if ((i != 0 && !n->mergeable_rx_bufs) ||
165 virtqueue_pop(n->rx_vq, &elem) == 0) {
166 if (i == 0)
167 return;
168 fprintf(stderr, "virtio-net truncating packet\n");
169 exit(1);
170 }
171
172 if (elem.in_num < 1) {
173 fprintf(stderr, "virtio-net receive queue contains no in buffers\n");
174 exit(1);
175 }
176
177 if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != hdr_len) {
178 fprintf(stderr, "virtio-net header not in first element\n");
179 exit(1);
180 }
181
182 memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
183
184 if (i == 0) {
185 if (n->mergeable_rx_bufs)
186 mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
187
188 offset += receive_header(n, sg, elem.in_num,
189 buf + offset, size - offset, hdr_len);
190 total += hdr_len;
191 }
192
193 /* copy in packet. ugh */
194 len = iov_fill(sg, elem.in_num,
195 buf + offset, size - offset);
196 total += len;
197
198 /* signal other side */
199 virtqueue_fill(n->rx_vq, &elem, total, i++);
200
201 offset += len;
202 }
203
204 if (mhdr)
205 mhdr->num_buffers = i;
206
207 virtqueue_flush(n->rx_vq, i);
208 virtio_notify(&n->vdev, n->rx_vq);
209}
210
211/* TX */
212static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
213{
214 VirtQueueElement elem;
215 int has_vnet_hdr = 0;
216
217 if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
218 return;
219
220 while (virtqueue_pop(vq, &elem)) {
221 ssize_t len = 0;
222 unsigned int out_num = elem.out_num;
223 struct iovec *out_sg = &elem.out_sg[0];
224 unsigned hdr_len;
225
226 /* hdr_len refers to the header received from the guest */
227 hdr_len = n->mergeable_rx_bufs ?
228 sizeof(struct virtio_net_hdr_mrg_rxbuf) :
229 sizeof(struct virtio_net_hdr);
230
231 if (out_num < 1 || out_sg->iov_len != hdr_len) {
232 fprintf(stderr, "virtio-net header not in first element\n");
233 exit(1);
234 }
235
236 /* ignore the header if GSO is not supported */
237 if (!has_vnet_hdr) {
238 out_num--;
239 out_sg++;
240 len += hdr_len;
241 } else if (n->mergeable_rx_bufs) {
242 /* tapfd expects a struct virtio_net_hdr */
243 hdr_len -= sizeof(struct virtio_net_hdr);
244 out_sg->iov_len -= hdr_len;
245 len += hdr_len;
246 }
247
248 len += qemu_sendv_packet(n->vc, out_sg, out_num);
249
250 virtqueue_push(vq, &elem, len);
251 virtio_notify(&n->vdev, vq);
252 }
253}
254
255static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
256{
257 VirtIONet *n = to_virtio_net(vdev);
258
259 if (n->tx_timer_active) {
260 virtio_queue_set_notification(vq, 1);
261 qemu_del_timer(n->tx_timer);
262 n->tx_timer_active = 0;
263 virtio_net_flush_tx(n, vq);
264 } else {
265 qemu_mod_timer(n->tx_timer,
266 qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
267 n->tx_timer_active = 1;
268 virtio_queue_set_notification(vq, 0);
269 }
270}
271
272static void virtio_net_tx_timer(void *opaque)
273{
274 VirtIONet *n = opaque;
275
276 n->tx_timer_active = 0;
277
278 /* Just in case the driver is not ready on more */
279 if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
280 return;
281
282 virtio_queue_set_notification(n->tx_vq, 1);
283 virtio_net_flush_tx(n, n->tx_vq);
284}
285
286static void virtio_net_save(QEMUFile *f, void *opaque)
287{
288 VirtIONet *n = opaque;
289
290 virtio_save(&n->vdev, f);
291
292 qemu_put_buffer(f, n->mac, 6);
293 qemu_put_be32(f, n->tx_timer_active);
aliguorie46cb382009-01-07 17:50:45 +0000294 qemu_put_be32(f, n->mergeable_rx_bufs);
aliguorifbe78f42008-12-17 19:13:11 +0000295}
296
297static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
298{
299 VirtIONet *n = opaque;
300
aliguorie46cb382009-01-07 17:50:45 +0000301 if (version_id != 2)
aliguorifbe78f42008-12-17 19:13:11 +0000302 return -EINVAL;
303
304 virtio_load(&n->vdev, f);
305
306 qemu_get_buffer(f, n->mac, 6);
307 n->tx_timer_active = qemu_get_be32(f);
aliguorie46cb382009-01-07 17:50:45 +0000308 n->mergeable_rx_bufs = qemu_get_be32(f);
aliguorifbe78f42008-12-17 19:13:11 +0000309
310 if (n->tx_timer_active) {
311 qemu_mod_timer(n->tx_timer,
312 qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
313 }
314
315 return 0;
316}
317
318PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
319{
320 VirtIONet *n;
321 static int virtio_net_id;
322
323 n = (VirtIONet *)virtio_init_pci(bus, "virtio-net", 6900, 0x1000,
324 0, VIRTIO_ID_NET,
325 0x02, 0x00, 0x00,
aliguori554c97d2009-01-08 19:46:33 +0000326 sizeof(struct virtio_net_config),
327 sizeof(VirtIONet));
aliguorifbe78f42008-12-17 19:13:11 +0000328 if (!n)
329 return NULL;
330
331 n->vdev.get_config = virtio_net_update_config;
332 n->vdev.get_features = virtio_net_get_features;
333 n->vdev.set_features = virtio_net_set_features;
334 n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
335 n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
336 memcpy(n->mac, nd->macaddr, 6);
aliguori554c97d2009-01-08 19:46:33 +0000337 n->status = VIRTIO_NET_S_LINK_UP;
aliguori7a9f6e42009-01-07 17:48:51 +0000338 n->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
aliguoribf38c1a2009-01-07 17:42:25 +0000339 virtio_net_receive, virtio_net_can_receive, n);
aliguori554c97d2009-01-08 19:46:33 +0000340 n->vc->link_status_changed = virtio_net_set_link_status;
aliguorifbe78f42008-12-17 19:13:11 +0000341
aliguori96d5e202009-01-07 17:47:15 +0000342 qemu_format_nic_info_str(n->vc, n->mac);
343
aliguorifbe78f42008-12-17 19:13:11 +0000344 n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
345 n->tx_timer_active = 0;
346 n->mergeable_rx_bufs = 0;
347
aliguorie46cb382009-01-07 17:50:45 +0000348 register_savevm("virtio-net", virtio_net_id++, 2,
aliguorifbe78f42008-12-17 19:13:11 +0000349 virtio_net_save, virtio_net_load, n);
350
351 return (PCIDevice *)n;
352}