1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Vincenzo Maffione <vmaffione@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <net/if.h>
29#include <net/netmap.h>
30#include <net/netmap_virt.h>
31#define NETMAP_WITH_LIBS
32#include <net/netmap_user.h>
33
34#include <assert.h>
35
36#include "debug.h"
37#include "iov.h"
38#include "mevent.h"
39#include "net_backends.h"
40#include "net_backends_priv.h"
41
42/* The virtio-net features supported by netmap. */
43#define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \
44		VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \
45		VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \
46		VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO)
47
48struct netmap_priv {
49	char ifname[IFNAMSIZ];
50	struct nm_desc *nmd;
51	uint16_t memid;
52	struct netmap_ring *rx;
53	struct netmap_ring *tx;
54	struct mevent *mevp;
55	net_be_rxeof_t cb;
56	void *cb_param;
57};
58
59static void
60nmreq_init(struct nmreq *req, char *ifname)
61{
62
63	memset(req, 0, sizeof(*req));
64	strlcpy(req->nr_name, ifname, sizeof(req->nr_name));
65	req->nr_version = NETMAP_API;
66}
67
68static int
69netmap_set_vnet_hdr_len(struct net_backend *be, int vnet_hdr_len)
70{
71	int err;
72	struct nmreq req;
73	struct netmap_priv *priv = NET_BE_PRIV(be);
74
75	nmreq_init(&req, priv->ifname);
76	req.nr_cmd = NETMAP_BDG_VNET_HDR;
77	req.nr_arg1 = vnet_hdr_len;
78	err = ioctl(be->fd, NIOCREGIF, &req);
79	if (err) {
80		EPRINTLN("Unable to set vnet header length %d", vnet_hdr_len);
81		return (err);
82	}
83
84	be->be_vnet_hdr_len = vnet_hdr_len;
85
86	return (0);
87}
88
89static int
90netmap_has_vnet_hdr_len(struct net_backend *be, unsigned vnet_hdr_len)
91{
92	unsigned prev_hdr_len = be->be_vnet_hdr_len;
93	int ret;
94
95	if (vnet_hdr_len == prev_hdr_len) {
96		return (1);
97	}
98
99	ret = netmap_set_vnet_hdr_len(be, vnet_hdr_len);
100	if (ret) {
101		return (0);
102	}
103
104	netmap_set_vnet_hdr_len(be, prev_hdr_len);
105
106	return (1);
107}
108
109static uint64_t
110netmap_get_cap(struct net_backend *be)
111{
112
113	return (netmap_has_vnet_hdr_len(be, VNET_HDR_LEN) ?
114	    NETMAP_FEATURES : 0);
115}
116
117static int
118netmap_set_cap(struct net_backend *be, uint64_t features __unused,
119    unsigned vnet_hdr_len)
120{
121
122	return (netmap_set_vnet_hdr_len(be, vnet_hdr_len));
123}
124
125static int
126netmap_init(struct net_backend *be, const char *devname,
127    nvlist_t *nvl __unused, net_be_rxeof_t cb, void *param)
128{
129	struct netmap_priv *priv = NET_BE_PRIV(be);
130
131	strlcpy(priv->ifname, devname, sizeof(priv->ifname));
132	priv->ifname[sizeof(priv->ifname) - 1] = '\0';
133
134	priv->nmd = nm_open(priv->ifname, NULL, NETMAP_NO_TX_POLL, NULL);
135	if (priv->nmd == NULL) {
136		EPRINTLN("Unable to nm_open(): interface '%s', errno (%s)",
137		    devname, strerror(errno));
138		return (-1);
139	}
140
141	priv->memid = priv->nmd->req.nr_arg2;
142	priv->tx = NETMAP_TXRING(priv->nmd->nifp, 0);
143	priv->rx = NETMAP_RXRING(priv->nmd->nifp, 0);
144	priv->cb = cb;
145	priv->cb_param = param;
146	be->fd = priv->nmd->fd;
147
148	priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
149	if (priv->mevp == NULL) {
150		EPRINTLN("Could not register event");
151		return (-1);
152	}
153
154	return (0);
155}
156
157static void
158netmap_cleanup(struct net_backend *be)
159{
160	struct netmap_priv *priv = NET_BE_PRIV(be);
161
162	if (priv->mevp) {
163		mevent_delete(priv->mevp);
164	}
165	if (priv->nmd) {
166		nm_close(priv->nmd);
167	}
168	be->fd = -1;
169}
170
171static ssize_t
172netmap_send(struct net_backend *be, const struct iovec *iov,
173	    int iovcnt)
174{
175	struct netmap_priv *priv = NET_BE_PRIV(be);
176	struct netmap_ring *ring;
177	ssize_t totlen = 0;
178	int nm_buf_size;
179	int nm_buf_len;
180	uint32_t head;
181	uint8_t *nm_buf;
182	int j;
183
184	ring = priv->tx;
185	head = ring->head;
186	if (head == ring->tail) {
187		EPRINTLN("No space, drop %zu bytes", count_iov(iov, iovcnt));
188		goto txsync;
189	}
190	nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
191	nm_buf_size = ring->nr_buf_size;
192	nm_buf_len = 0;
193
194	for (j = 0; j < iovcnt; j++) {
195		uint8_t *iov_frag_buf = iov[j].iov_base;
196		int iov_frag_size = iov[j].iov_len;
197
198		totlen += iov_frag_size;
199
200		/*
201		 * Split each iovec fragment over more netmap slots, if
202		 * necessary.
203		 */
204		for (;;) {
205			int copylen;
206
207			copylen = iov_frag_size < nm_buf_size ? iov_frag_size : nm_buf_size;
208			memcpy(nm_buf, iov_frag_buf, copylen);
209
210			iov_frag_buf += copylen;
211			iov_frag_size -= copylen;
212			nm_buf += copylen;
213			nm_buf_size -= copylen;
214			nm_buf_len += copylen;
215
216			if (iov_frag_size == 0) {
217				break;
218			}
219
220			ring->slot[head].len = nm_buf_len;
221			ring->slot[head].flags = NS_MOREFRAG;
222			head = nm_ring_next(ring, head);
223			if (head == ring->tail) {
224				/*
225				 * We ran out of netmap slots while
226				 * splitting the iovec fragments.
227				 */
228				EPRINTLN("No space, drop %zu bytes",
229				    count_iov(iov, iovcnt));
230				goto txsync;
231			}
232			nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);
233			nm_buf_size = ring->nr_buf_size;
234			nm_buf_len = 0;
235		}
236	}
237
238	/* Complete the last slot, which must not have NS_MOREFRAG set. */
239	ring->slot[head].len = nm_buf_len;
240	ring->slot[head].flags = 0;
241	head = nm_ring_next(ring, head);
242
243	/* Now update ring->head and ring->cur. */
244	ring->head = ring->cur = head;
245txsync:
246	ioctl(be->fd, NIOCTXSYNC, NULL);
247
248	return (totlen);
249}
250
251static ssize_t
252netmap_peek_recvlen(struct net_backend *be)
253{
254	struct netmap_priv *priv = NET_BE_PRIV(be);
255	struct netmap_ring *ring = priv->rx;
256	uint32_t head = ring->head;
257	ssize_t totlen = 0;
258
259	while (head != ring->tail) {
260		struct netmap_slot *slot = ring->slot + head;
261
262		totlen += slot->len;
263		if ((slot->flags & NS_MOREFRAG) == 0)
264			break;
265		head = nm_ring_next(ring, head);
266	}
267
268	return (totlen);
269}
270
271static ssize_t
272netmap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
273{
274	struct netmap_priv *priv = NET_BE_PRIV(be);
275	struct netmap_slot *slot = NULL;
276	struct netmap_ring *ring;
277	uint8_t *iov_frag_buf;
278	int iov_frag_size;
279	ssize_t totlen = 0;
280	uint32_t head;
281
282	assert(iovcnt);
283
284	ring = priv->rx;
285	head = ring->head;
286	iov_frag_buf = iov->iov_base;
287	iov_frag_size = iov->iov_len;
288
289	do {
290		uint8_t *nm_buf;
291		int nm_buf_len;
292
293		if (head == ring->tail) {
294			return (0);
295		}
296
297		slot = ring->slot + head;
298		nm_buf = NETMAP_BUF(ring, slot->buf_idx);
299		nm_buf_len = slot->len;
300
301		for (;;) {
302			int copylen = nm_buf_len < iov_frag_size ?
303			    nm_buf_len : iov_frag_size;
304
305			memcpy(iov_frag_buf, nm_buf, copylen);
306			nm_buf += copylen;
307			nm_buf_len -= copylen;
308			iov_frag_buf += copylen;
309			iov_frag_size -= copylen;
310			totlen += copylen;
311
312			if (nm_buf_len == 0) {
313				break;
314			}
315
316			iov++;
317			iovcnt--;
318			if (iovcnt == 0) {
319				/* No space to receive. */
320				EPRINTLN("Short iov, drop %zd bytes",
321				    totlen);
322				return (-ENOSPC);
323			}
324			iov_frag_buf = iov->iov_base;
325			iov_frag_size = iov->iov_len;
326		}
327
328		head = nm_ring_next(ring, head);
329
330	} while (slot->flags & NS_MOREFRAG);
331
332	/* Release slots to netmap. */
333	ring->head = ring->cur = head;
334
335	return (totlen);
336}
337
338static void
339netmap_recv_enable(struct net_backend *be)
340{
341	struct netmap_priv *priv = NET_BE_PRIV(be);
342
343	mevent_enable(priv->mevp);
344}
345
346static void
347netmap_recv_disable(struct net_backend *be)
348{
349	struct netmap_priv *priv = NET_BE_PRIV(be);
350
351	mevent_disable(priv->mevp);
352}
353
354static struct net_backend netmap_backend = {
355	.prefix = "netmap",
356	.priv_size = sizeof(struct netmap_priv),
357	.init = netmap_init,
358	.cleanup = netmap_cleanup,
359	.send = netmap_send,
360	.peek_recvlen = netmap_peek_recvlen,
361	.recv = netmap_recv,
362	.recv_enable = netmap_recv_enable,
363	.recv_disable = netmap_recv_disable,
364	.get_cap = netmap_get_cap,
365	.set_cap = netmap_set_cap,
366};
367
368/* A clone of the netmap backend, with a different prefix. */
369static struct net_backend vale_backend = {
370	.prefix = "vale",
371	.priv_size = sizeof(struct netmap_priv),
372	.init = netmap_init,
373	.cleanup = netmap_cleanup,
374	.send = netmap_send,
375	.peek_recvlen = netmap_peek_recvlen,
376	.recv = netmap_recv,
377	.recv_enable = netmap_recv_enable,
378	.recv_disable = netmap_recv_disable,
379	.get_cap = netmap_get_cap,
380	.set_cap = netmap_set_cap,
381};
382
383DATA_SET(net_backend_set, netmap_backend);
384DATA_SET(net_backend_set, vale_backend);
385