diff options
Diffstat (limited to 'platform/linux-generic/pktio/netmap.c')
-rw-r--r-- | platform/linux-generic/pktio/netmap.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/platform/linux-generic/pktio/netmap.c b/platform/linux-generic/pktio/netmap.c new file mode 100644 index 0000000..54e7043 --- /dev/null +++ b/platform/linux-generic/pktio/netmap.c @@ -0,0 +1,303 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifdef ODP_NETMAP + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <odp_packet_netmap.h> +#include <odp_packet_socket.h> +#include <odp_packet_io_internal.h> +#include <odp_debug_internal.h> +#include <odp/helper/eth.h> + +#include <sys/ioctl.h> +#include <poll.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> + +#define NETMAP_WITH_LIBS +#include <net/netmap_user.h> + +static struct nm_desc mmap_desc; /** Used to store the mmap address; + filled in first time, used for + subsequent calls to nm_open */ + +#define NM_INJECT_RETRIES 10 + +struct dispatch_args { + odp_packet_t *pkt_table; + unsigned nb_rx; + pktio_entry_t *pktio_entry; +}; + +static int netmap_do_ioctl(pktio_entry_t *pktio_entry, unsigned long cmd, + int subcmd) +{ + pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm; + struct ethtool_value eval; + struct ifreq ifr; + int err; + int fd = pkt_nm->sockfd; + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", + pktio_entry->s.name); + + switch (cmd) { + case SIOCSIFFLAGS: + ifr.ifr_flags = pkt_nm->if_flags & 0xffff; + break; + case SIOCETHTOOL: + eval.cmd = subcmd; + eval.data = 0; + ifr.ifr_data = (caddr_t)&eval; + break; + default: + break; + } + err = ioctl(fd, cmd, &ifr); + if (err) + goto done; + + switch (cmd) { + case SIOCGIFFLAGS: + pkt_nm->if_flags = (ifr.ifr_flags << 16) | + (0xffff & ifr.ifr_flags); + break; + default: + break; + } +done: + if (err) + ODP_ERR("ioctl err %d %lu: %s\n", err, cmd, strerror(errno)); + + return err; +} + +static int netmap_close(pktio_entry_t *pktio_entry) +{ + pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm; + + if (pkt_nm->desc != NULL) + nm_close(pkt_nm->desc); + + if (pkt_nm->sockfd != -1 && close(pkt_nm->sockfd) != 0) { + __odp_errno = errno; + ODP_ERR("close(sockfd): %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static int netmap_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry, + const char *netdev, odp_pool_t pool) +{ + char ifname[IFNAMSIZ + 7]; /* netmap:<ifname> */ + int err; + int sockfd; + pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm; + + if (getenv("ODP_PKTIO_DISABLE_NETMAP")) + return -1; + + if (pool == ODP_POOL_INVALID) + return -1; + + /* Init pktio entry */ + memset(pkt_nm, 0, sizeof(*pkt_nm)); + pkt_nm->sockfd = -1; + pkt_nm->pool = pool; + + /* max frame len taking into account the l2-offset */ + pkt_nm->max_frame_len = ODP_CONFIG_PACKET_BUF_LEN_MAX - + odp_buffer_pool_headroom(pool) - + odp_buffer_pool_tailroom(pool); + + snprintf(pktio_entry->s.name, sizeof(pktio_entry->s.name), "%s", + netdev); + snprintf(ifname, sizeof(ifname), "netmap:%s", netdev); + + if (mmap_desc.mem == NULL) + pkt_nm->desc = nm_open(ifname, NULL, NETMAP_NO_TX_POLL, NULL); + else + pkt_nm->desc = nm_open(ifname, NULL, NETMAP_NO_TX_POLL | + NM_OPEN_NO_MMAP, &mmap_desc); + if (pkt_nm->desc == NULL) { + ODP_ERR("nm_open(%s) failed\n", ifname); + goto error; + } + + if (mmap_desc.mem == NULL) { + mmap_desc.mem = pkt_nm->desc->mem; + mmap_desc.memsize = pkt_nm->desc->memsize; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd == -1) { + ODP_ERR("Cannot get device control socket\n"); + goto error; + } + pkt_nm->sockfd = sockfd; + + err = netmap_do_ioctl(pktio_entry, SIOCGIFFLAGS, 0); + if (err) + goto error; + if ((pkt_nm->if_flags & IFF_UP) == 0) + ODP_DBG("%s is down\n", pktio_entry->s.name); + + err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_SGSO); + if (err) + ODP_DBG("ETHTOOL_SGSO not supported\n"); + + err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_STSO); + if (err) + ODP_DBG("ETHTOOL_STSO not supported\n"); + + err = mac_addr_get_fd(sockfd, netdev, pkt_nm->if_mac); + if (err) + goto error; + + err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_STXCSUM); + if (err) + ODP_DBG("ETHTOOL_STXCSUM not supported\n"); + + return 0; + +error: + netmap_close(pktio_entry); + return -1; +} + +static void netmap_recv_cb(u_char *arg, const struct nm_pkthdr *hdr, + const u_char *buf) +{ + struct dispatch_args *args = (struct dispatch_args *)arg; + pkt_netmap_t *pkt_nm = &args->pktio_entry->s.pkt_nm; + odp_packet_t pkt; + size_t frame_len = (size_t)hdr->len; + + if (odp_unlikely(frame_len > pkt_nm->max_frame_len)) { + ODP_ERR("RX: frame too big %u %lu!\n", (unsigned)frame_len, + pkt_nm->max_frame_len); + return; + } + + if (odp_unlikely(frame_len < ODPH_ETH_LEN_MIN)) { + ODP_ERR("RX: Frame truncated: %u\n", (unsigned)frame_len); + return; + } + + pkt = odp_packet_alloc(pkt_nm->pool, frame_len); + if (pkt == ODP_PACKET_INVALID) + return; + + /* For now copy the data in the mbuf, + worry about zero-copy later */ + if (odp_packet_copydata_in(pkt, 0, frame_len, buf) != 0) { + odp_packet_free(pkt); + return; + } + + _odp_packet_reset_parse(pkt); + + args->pkt_table[args->nb_rx++] = pkt; +} + +static int netmap_recv(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[], + unsigned num) +{ + struct dispatch_args args; + struct nm_desc *nm_desc = pktio_entry->s.pkt_nm.desc; + struct pollfd polld; + + polld.fd = nm_desc->fd; + polld.events = POLLIN; + + args.pkt_table = pkt_table; + args.nb_rx = 0; + args.pktio_entry = pktio_entry; + + nm_dispatch(nm_desc, num, netmap_recv_cb, (u_char *)&args); + if (args.nb_rx == 0) { + if (odp_unlikely(poll(&polld, 1, 0) < 0)) + ODP_ERR("RX: poll error\n"); + } + return args.nb_rx; +} + +static int netmap_send(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[], + unsigned num) +{ + struct nm_desc *nm_desc = pktio_entry->s.pkt_nm.desc; + struct pollfd polld; + unsigned i, nb_tx; + uint8_t *frame; + uint32_t frame_len; + + polld.fd = nm_desc->fd; + polld.events = POLLOUT; + + for (nb_tx = 0; nb_tx < num; nb_tx++) { + frame_len = 0; + frame = odp_packet_l2_ptr(pkt_table[nb_tx], &frame_len); + for (i = 0; i < NM_INJECT_RETRIES; i++) { + if (nm_inject(nm_desc, frame, frame_len) == 0) + poll(&polld, 1, 0); + else + break; + } + if (odp_unlikely(i == NM_INJECT_RETRIES)) + break; + } + for (i = 0; i < nb_tx; i++) + odp_packet_free(pkt_table[i]); + + return nb_tx; +} + +static int netmap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr) +{ + memcpy(mac_addr, pktio_entry->s.pkt_nm.if_mac, ETH_ALEN); + return ETH_ALEN; +} + +static int netmap_mtu_get(pktio_entry_t *pktio_entry) +{ + return mtu_get_fd(pktio_entry->s.pkt_nm.sockfd, pktio_entry->s.name); +} + +static int netmap_promisc_mode_set(pktio_entry_t *pktio_entry, + odp_bool_t enable) +{ + return promisc_mode_set_fd(pktio_entry->s.pkt_nm.sockfd, + pktio_entry->s.name, enable); +} + +static int netmap_promisc_mode_get(pktio_entry_t *pktio_entry) +{ + return promisc_mode_get_fd(pktio_entry->s.pkt_nm.sockfd, + pktio_entry->s.name); +} + +const pktio_if_ops_t netmap_pktio_ops = { + .init = NULL, + .term = NULL, + .open = netmap_open, + .close = netmap_close, + .start = NULL, + .stop = NULL, + .recv = netmap_recv, + .send = netmap_send, + .mtu_get = netmap_mtu_get, + .promisc_mode_set = netmap_promisc_mode_set, + .promisc_mode_get = netmap_promisc_mode_get, + .mac_get = netmap_mac_addr_get +}; + +#endif /* ODP_NETMAP */ |