embox

Форк
0
/
virtio_net.c 
374 строки · 8.9 Кб
1
/**
2
 * @file
3
 * @brief Virtual High Performance Ethernet card
4
 *
5
 * @date 13.08.13
6
 * @author Ilia Vaprol
7
 */
8

9
#include <util/log.h>
10

11
#include <stdlib.h>
12
#include <string.h>
13
#include <errno.h>
14
#include <assert.h>
15

16
#include <drivers/virtio/virtio.h>
17
#include <drivers/virtio/virtio_ring.h>
18
#include <drivers/virtio/virtio_queue.h>
19

20
#include <drivers/pci/pci.h>
21
#include <drivers/pci/pci_id.h>
22
#include <drivers/pci/pci_driver.h>
23

24
#include <kernel/irq.h>
25
#include <kernel/sched/sched_lock.h>
26

27
#include <net/inetdevice.h>
28
#include <net/l0/net_entry.h>
29
#include <net/l2/ethernet.h>
30
#include <net/netdevice.h>
31

32
#include "virtio_net.h"
33

34
#include <framework/mod/options.h>
35

36
PCI_DRIVER("virtio", virtio_init, PCI_VENDOR_ID_VIRTIO, PCI_DEV_ID_VIRTIO_NET);
37

38
#define MODOPS_PREP_BUFF_CNT OPTION_GET(NUMBER, prep_buff_cnt)
39

40
struct virtio_priv {
41
	struct virtqueue rq;
42
	struct virtqueue tq;
43
};
44

45
static int virtio_xmit(struct net_device *dev, struct sk_buff *skb) {
46
	struct sk_buff_extra *skb_extra;
47
	struct sk_buff_data *skb_data;
48
	struct virtqueue *vq;
49
	struct virtio_net_hdr *hdr;
50
	uint32_t desc_id;
51
	struct vring_desc *desc;
52
	struct virtio_priv *virtio_priv;
53

54
	assert(dev != NULL);
55
	assert(skb != NULL);
56

57
	virtio_priv = netdev_priv(dev);
58

59
	skb_extra = skb_extra_alloc();
60
	if (skb_extra == NULL) {
61
		return -ENOMEM;
62
	}
63

64
	skb_data = skb_data_clone(skb->data);
65
	if (skb_data == NULL) {
66
		skb_extra_free(skb_extra);
67
		return -ENOMEM;
68
	}
69

70
	vq = &virtio_priv->tq;
71

72
	hdr = skb_extra_cast_in(skb_extra);
73
	hdr->flags = 0;
74
	hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
75

76
	sched_lock();
77
	{
78
		desc_id = vq->next_free_desc;
79
		do { desc = virtqueue_alloc_desc(vq); } while (desc == NULL);
80
		vring_desc_init(desc, hdr, sizeof *hdr, VRING_DESC_F_NEXT);
81
		desc->next = vq->next_free_desc;
82

83
		do { desc = virtqueue_alloc_desc(vq); } while (desc == NULL);
84
		vring_desc_init(desc, skb_data_cast_in(skb_data), skb->len, 0);
85

86
		vring_push_desc(desc_id, &vq->ring);
87
	}
88
	sched_unlock();
89

90
	skb_free(skb);
91

92
	virtio_net_notify_queue(VIRTIO_NET_QUEUE_TX, dev);
93

94
	return 0;
95
}
96

97
static irq_return_t virtio_interrupt(unsigned int irq_num,
98
		void *dev_id) {
99
	struct net_device *dev;
100
	struct virtqueue *vq;
101
	struct vring_used_elem *used_elem;
102
	struct sk_buff *skb;
103
	struct sk_buff_data *new_data;
104
	struct vring_desc *desc, *next;
105
	struct virtio_priv *virtio_priv;
106

107
	dev = dev_id;
108

109
	/* it is really? */
110
	if (~virtio_net_get_isr_status(dev) & 1) {
111
		return IRQ_NONE;
112
	}
113

114
	virtio_priv = netdev_priv(dev);
115

116
	/* release outgoing packets */
117
	vq = &virtio_priv->tq;
118
	while (vq->last_seen_used != vq->ring.used->idx) {
119
		used_elem = &vq->ring.used->ring[vq->last_seen_used % vq->ring.num];
120

121
		desc = &vq->ring.desc[used_elem->id];
122
		skb_extra_free(skb_extra_cast_out((void *)(uintptr_t)desc->addr));
123
		desc->addr = 0;
124
		assert(desc->flags & VRING_DESC_F_NEXT);
125

126
		next = &vq->ring.desc[desc->next];
127
		skb_data_free(skb_data_cast_out((void *)(uintptr_t)next->addr));
128
		next->addr = 0;
129
		assert(~next->flags & VRING_DESC_F_NEXT);
130

131
		++vq->last_seen_used;
132
	}
133

134
	/* receive incoming packets */
135
	vq = &virtio_priv->rq;
136
	while (vq->last_seen_used != vq->ring.used->idx) {
137
		used_elem = &vq->ring.used->ring[vq->last_seen_used % vq->ring.num];
138

139
		desc = &vq->ring.desc[used_elem->id];
140
		assert(desc->flags & VRING_DESC_F_NEXT);
141

142
		next = &vq->ring.desc[desc->next];
143
		assert(~next->flags & VRING_DESC_F_NEXT);
144

145
		skb = skb_wrap(used_elem->len - sizeof(struct virtio_net_hdr),
146
				skb_data_cast_out((void *)(uintptr_t)next->addr));
147
		if (skb == NULL) {
148
			log_error("skb_wrap return NULL");
149
			break;
150
		}
151
		skb->dev = dev;
152
		netif_rx(skb);
153

154
		++vq->last_seen_used;
155

156
		new_data = skb_data_alloc(skb_max_size());
157
		if (new_data == NULL) {
158
			skb_extra_free(skb_extra_cast_out((void *)(uintptr_t)desc->addr));
159
			desc->addr = next->addr = 0;
160
			log_error("skb_data_alloc return NULL");
161
			break;
162
		}
163

164
		/* desc->addr = desc->addr; -- the same */
165
		next->addr = (uintptr_t)skb_data_cast_in(new_data);
166

167
		vring_push_desc(used_elem->id, &vq->ring);
168
		virtio_net_notify_queue(VIRTIO_NET_QUEUE_RX, dev);
169
	}
170

171
	return IRQ_HANDLED;
172
}
173

174
static int virtio_open(struct net_device *dev) {
175
	/* device is ready */
176
	virtio_net_add_status(VIRTIO_CONFIG_S_DRIVER_OK, dev);
177
	return 0;
178
}
179

180
static int virtio_stop(struct net_device *dev) {
181
	/* device is not ready */
182
	virtio_net_del_status(VIRTIO_CONFIG_S_DRIVER_OK, dev);
183
	return 0;
184
}
185

186
static int virtio_set_macaddr(struct net_device *dev, const void *addr) {
187
	unsigned char i;
188

189
	/* setup MAC-address */
190
	for (i = 0; i < dev->addr_len; ++i) {
191
		virtio_net_set_mac(dev->dev_addr[i], i, dev);
192
	}
193

194
	return 0;
195
}
196

197
static const struct net_driver virtio_drv_ops = {
198
	.xmit = virtio_xmit,
199
	.start = virtio_open,
200
	.stop = virtio_stop,
201
	.set_macaddr = virtio_set_macaddr
202
};
203

204
static void virtio_config(struct net_device *dev) {
205
	unsigned char i;
206
	uint32_t guest_features;
207

208
	/* check extra header size */
209
	assert(skb_extra_max_size() >= sizeof(struct virtio_net_hdr));
210

211
	/* reset device */
212
	virtio_net_reset(dev);
213

214
	/* it's known device */
215
	virtio_net_add_status(VIRTIO_CONFIG_S_ACKNOWLEDGE
216
			| VIRTIO_CONFIG_S_DRIVER, dev);
217

218
	guest_features = 0;
219

220
	/* load device MAC-address and negotiate MAC bit */
221
	if (virtio_net_has_feature(VIRTIO_NET_F_MAC, dev)) {
222
		for (i = 0; i < dev->addr_len; ++i) {
223
			dev->dev_addr[i] = virtio_net_get_mac(i, dev);
224
		}
225
		guest_features |= VIRTIO_NET_F_MAC;
226
	}
227

228
	/* negotiate STATUS bit */
229
	if (virtio_net_has_feature(VIRTIO_NET_F_STATUS, dev)) {
230
		if (virtio_net_get_status(dev) & VIRTIO_NET_S_LINK_UP) {
231
			netdev_flag_up(dev, IFF_RUNNING);
232
		}
233
		guest_features |= VIRTIO_NET_F_STATUS;
234
	}
235

236
	/* finalize guest features bits */
237
	virtio_net_set_feature(guest_features, dev);
238
}
239

240
static void virtio_priv_fini(struct virtio_priv *dev_priv,
241
		struct net_device *dev) {
242
	struct virtqueue *vq;
243
	struct vring_desc *desc;
244

245
	/* free transmit queue */
246
	vq = &dev_priv->tq;
247
	for (desc = &vq->ring.desc[0];
248
			desc < &vq->ring.desc[vq->ring.num]; ++desc) {
249
		if (desc->addr != 0) {
250
			skb_extra_free(skb_extra_cast_out((void *)(uintptr_t)desc->addr));
251
			desc->addr = 0;
252
			assert(desc->flags & VRING_DESC_F_NEXT);
253

254
			assert(desc + 1 == &vq->ring.desc[desc->next]);
255
			++desc;
256
			skb_data_free(skb_data_cast_out((void *)(uintptr_t)desc->addr));
257
			desc->addr = 0;
258
			assert(~desc->flags & VRING_DESC_F_NEXT);
259
		}
260
	}
261
	virtqueue_net_destroy(vq, dev);
262

263
	/* free receive queue */
264
	vq = &dev_priv->rq;
265
	for (desc = &vq->ring.desc[0];
266
			desc < &vq->ring.desc[vq->ring.num]; ++desc) {
267
		if (desc->addr != 0) {
268
			skb_extra_free(skb_extra_cast_out((void *)(uintptr_t)desc->addr));
269
			desc->addr = 0;
270
			assert(desc->flags & VRING_DESC_F_NEXT);
271

272
			assert(desc + 1 == &vq->ring.desc[desc->next]);
273
			++desc;
274
			skb_data_free(skb_data_cast_out((void *)(uintptr_t)desc->addr));
275
			desc->addr = 0;
276
			assert(~desc->flags & VRING_DESC_F_NEXT);
277
		}
278
	}
279
	virtqueue_net_destroy(vq, dev);
280
}
281

282
static int virtio_priv_init(struct virtio_priv *dev_priv,
283
		struct net_device *dev) {
284
	int ret, i;
285
	struct sk_buff_extra *skb_extra;
286
	struct sk_buff_data *skb_data;
287
	struct virtqueue *vq;
288
	uint32_t desc_id;
289
	struct vring_desc *desc;
290

291
	/* init receive queue */
292
	ret = virtqueue_net_create(&dev_priv->rq, VIRTIO_NET_QUEUE_RX, dev);
293
	if (ret != 0) {
294
		return ret;
295
	}
296

297
	/* init transmit queue */
298
	ret = virtqueue_net_create(&dev_priv->tq,
299
			VIRTIO_NET_QUEUE_TX, dev);
300
	if (ret != 0) {
301
		virtqueue_net_destroy(&dev_priv->rq, dev);
302
		return ret;
303
	}
304

305
	/* add receive buffer */
306
	vq = &dev_priv->rq;
307
	if (MODOPS_PREP_BUFF_CNT * 2 > vq->ring.num) goto out_nomem;
308

309
	for (i = 0; i < MODOPS_PREP_BUFF_CNT; ++i) {
310
		desc_id = vq->next_free_desc;
311
		desc = virtqueue_alloc_desc(vq);
312
		if (desc == NULL) goto out_nomem;
313

314
		skb_extra = skb_extra_alloc();
315
		if (skb_extra == NULL) goto out_nomem;
316

317
		vring_desc_init(desc, skb_extra_cast_in(skb_extra),
318
				sizeof(struct virtio_net_hdr),
319
				VRING_DESC_F_WRITE | VRING_DESC_F_NEXT);
320
		desc->next = vq->next_free_desc;
321

322
		desc = virtqueue_alloc_desc(vq);
323
		if (desc == NULL) goto out_nomem;
324

325
		skb_data = skb_data_alloc(skb_max_size());
326
		if (skb_data == NULL) goto out_nomem;
327

328
		vring_desc_init(desc,
329
				skb_data_cast_in(skb_data), skb_max_size(),
330
				VRING_DESC_F_WRITE);
331

332
		vring_push_desc(desc_id, &vq->ring);
333
	}
334
	virtio_net_notify_queue(VIRTIO_NET_QUEUE_RX, dev);
335

336
	return 0;
337

338
out_nomem:
339
	virtio_priv_fini(dev_priv, dev);
340
	return -ENOMEM;
341
}
342

343
static int virtio_init(struct pci_slot_dev *pci_dev) {
344
	int ret;
345
	struct net_device *nic;
346
	struct virtio_priv *nic_priv;
347

348
	nic = etherdev_alloc(sizeof *nic_priv);
349
	if (nic == NULL) {
350
		log_error("couldn't allocate etherdev");
351
		return -ENOMEM;
352
	}
353
	nic->drv_ops = &virtio_drv_ops;
354
	nic->irq = pci_dev->irq;
355
	nic->base_addr = pci_dev->bar[0] & PCI_BASE_ADDR_IO_MASK;
356
	nic_priv = netdev_priv(nic);
357

358
	virtio_config(nic);
359

360
	ret = virtio_priv_init(nic_priv, nic);
361
	if (ret != 0) {
362
		log_error("virtio_priv_init returned %d", ret);
363
		return ret;
364
	}
365

366
	ret = irq_attach(nic->irq, virtio_interrupt, IF_SHARESUP, nic, "virtio");
367
	if (ret != 0) {
368
		log_error("irq_attach returned %d", ret);
369
		virtio_priv_fini(nic_priv, nic);
370
		return ret;
371
	}
372

373
	return inetdev_register_dev(nic);
374
}
375

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.