Statistics
| Branch: | Revision:

root / hw / virtio-net.c @ bf38c1a0

History | View | Annotate | Download (8.4 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
    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, size_t size, size_t 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
    size_t 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, nd->model,
319
                                 virtio_net_receive, 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
}