net: tcp: Support SACK
[akaros.git] / user / vmm / virtio_net.c
1 /* Virtio helper functions from linux/tools/lguest/lguest.c
2  *
3  * Copyright (C) 1991-2016, the Linux Kernel authors
4  * Copyright (c) 2016 Google Inc.
5  *
6  * Author:
7  *  Rusty Russell <rusty@rustcorp.com.au>
8  *  Kyle Milka <kmilka@google.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * The code from lguest.c has been modified for Akaros.
21  *
22  * Original linux/tools/lguest/lguest.c:
23  *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
24  *   most recent hash on the file as of v4.5 tag:
25  *     e523caa601f4a7c2fa1ecd040db921baf7453798
26  */
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <vmm/virtio.h>
33 #include <vmm/virtio_mmio.h>
34 #include <vmm/virtio_net.h>
35 #include <vmm/net.h>
36 #include <parlib/iovec.h>
37 #include <iplib/iplib.h>
38
39 #define VIRTIO_HEADER_SIZE      12
40
41 void virtio_net_set_mac(struct virtio_vq_dev *vqdev, uint8_t *guest_mac)
42 {
43         memcpy(((struct virtio_net_config*)(vqdev->cfg))->mac, guest_mac,
44                ETH_ADDR_LEN);
45         memcpy(((struct virtio_net_config*)(vqdev->cfg_d))->mac, guest_mac,
46                ETH_ADDR_LEN);
47 }
48
49 /* net_receiveq_fn receives packets for the guest through the virtio networking
50  * device and the _vq virtio queue.
51  */
52 void *net_receiveq_fn(void *_vq)
53 {
54         struct virtio_vq *vq = _vq;
55         uint32_t head;
56         uint32_t olen, ilen;
57         int num_read;
58         struct iovec *iov;
59         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
60         struct virtio_net_hdr_v1 *net_header;
61
62         if (!vq)
63                 VIRTIO_DEV_ERRX(vq->vqdev,
64                         "\n  %s:%d\n"
65                         "  Virtio device: (not sure which one): Error, device behavior.\n"
66                         "  The device must provide a valid virtio_vq as an argument to %s."
67                         , __FILE__, __LINE__, __func__);
68
69         if (vq->qready == 0x0)
70                 VIRTIO_DEV_ERRX(vq->vqdev,
71                         "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
72                         vq->name);
73
74         iov = malloc(vq->qnum_max * sizeof(struct iovec));
75         assert(iov != NULL);
76
77         if (!dev->poke_guest) {
78                 free(iov);
79                 VIRTIO_DEV_ERRX(vq->vqdev,
80                                 "The 'poke_guest' function pointer was not set.");
81         }
82
83         for (;;) {
84                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
85                 if (olen) {
86                         free(iov);
87                         VIRTIO_DRI_ERRX(vq->vqdev,
88                                 "The driver placed a device-readable buffer in the net device's receiveq.\n"
89                                 "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
90                 }
91
92                 /* For receive the virtio header is in iov[0], so we only want
93                  * the packet to be read into iov[1] and above.
94                  */
95                 num_read = vnet_receive_packet(iov + 1, ilen - 1);
96                 if (num_read < 0) {
97                         free(iov);
98                         VIRTIO_DEV_ERRX(vq->vqdev,
99                                 "Encountered an error trying to read input from the ethernet device.");
100                 }
101
102                 /* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device Requirements:
103                  * Setting Up Receive Buffers
104                  *
105                  * VIRTIO_NET_F_MRG_RXBUF is not currently negotiated.
106                  * num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not
107                  * negotiated.
108                  */
109                 net_header = iov[0].iov_base;
110                 net_header->num_buffers = 1;
111                 virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE);
112
113                 virtio_mmio_set_vring_irq(dev);
114                 dev->poke_guest(dev->vec, dev->dest);
115         }
116         return 0;
117 }
118
119 /* net_transmitq_fn transmits packets from the guest through the virtio
120  * networking device through the _vq virtio queue.
121  */
122 void *net_transmitq_fn(void *_vq)
123 {
124         struct virtio_vq *vq = _vq;
125         uint32_t head;
126         uint32_t olen, ilen;
127         struct iovec *iov;
128         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
129         void *stripped;
130
131         iov = malloc(vq->qnum_max * sizeof(struct iovec));
132         assert(iov != NULL);
133
134         if (!dev->poke_guest) {
135                 free(iov);
136                 VIRTIO_DEV_ERRX(vq->vqdev,
137                                 "The 'poke_guest' function pointer was not set.");
138         }
139
140         for (;;) {
141                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
142
143                 if (ilen) {
144                         free(iov);
145                         VIRTIO_DRI_ERRX(vq->vqdev,
146                                         "The driver placed a device-writeable buffer in the network device's transmitq.\n"
147                                     "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation");
148                 }
149
150                 /* Strip off the virtio header (the first 12 bytes), as it is
151                  * not a part of the actual ethernet frame. */
152                 iov_strip_bytes(iov, olen, VIRTIO_HEADER_SIZE);
153                 vnet_transmit_packet(iov, olen);
154
155                 virtio_add_used_desc(vq, head, 0);
156
157                 virtio_mmio_set_vring_irq(dev);
158                 dev->poke_guest(dev->vec, dev->dest);
159         }
160         return 0;
161 }