Statistics
| Branch: | Revision:

root / hw / virtio-net.c @ 291c6ff9

History | View | Annotate | Download (9.1 kB)

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
    uint16_t status;
24
    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

    
36
static VirtIONet *to_virtio_net(VirtIODevice *vdev)
37
{
38
    return (VirtIONet *)vdev;
39
}
40

    
41
static 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

    
46
    netcfg.status = n->status;
47
    memcpy(netcfg.mac, n->mac, 6);
48
    memcpy(config, &netcfg, sizeof(netcfg));
49
}
50

    
51
static 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

    
65
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
66
{
67
    uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
68

    
69
    return features;
70
}
71

    
72
static 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

    
81
static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
82
{
83
}
84

    
85
static 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

    
102
static 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

    
109
static 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

    
124
static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
125
                          const void *buf, size_t size, size_t hdr_len)
126
{
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

    
142
static 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;
146
    size_t hdr_len, offset, i;
147

    
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 */
212
static 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

    
255
static 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

    
272
static 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

    
286
static 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);
294
    qemu_put_be32(f, n->mergeable_rx_bufs);
295
}
296

    
297
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
298
{
299
    VirtIONet *n = opaque;
300

    
301
    if (version_id != 2)
302
        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);
308
    n->mergeable_rx_bufs = qemu_get_be32(f);
309

    
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

    
318
void 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,
326
                                     sizeof(struct virtio_net_config),
327
                                     sizeof(VirtIONet));
328
    if (!n)
329
        return;
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);
337
    n->status = VIRTIO_NET_S_LINK_UP;
338
    n->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
339
                                 virtio_net_receive, virtio_net_can_receive, n);
340
    n->vc->link_status_changed = virtio_net_set_link_status;
341

    
342
    qemu_format_nic_info_str(n->vc, n->mac);
343

    
344
    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

    
348
    register_savevm("virtio-net", virtio_net_id++, 2,
349
                    virtio_net_save, virtio_net_load, n);
350
}