diff options
Diffstat (limited to 'platform/linux-generic/pktio/socket.c')
-rw-r--r-- | platform/linux-generic/pktio/socket.c | 947 |
1 files changed, 373 insertions, 574 deletions
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c index a383adc6a..bd4ffc94f 100644 --- a/platform/linux-generic/pktio/socket.c +++ b/platform/linux-generic/pktio/socket.c @@ -1,53 +1,56 @@ -/* Copyright (c) 2013, Linaro Limited - * Copyright (c) 2013, Nokia Solutions and Networks - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2013-2018 Linaro Limited + * Copyright (c) 2013-2023 Nokia Solutions and Networks */ #include <odp_posix_extensions.h> +#include <odp/api/align.h> +#include <odp/api/debug.h> +#include <odp/api/hints.h> +#include <odp/api/packet.h> +#include <odp/api/packet_io.h> +#include <odp/api/ticketlock.h> + +#include <odp_socket_common.h> +#include <odp_parse_internal.h> +#include <odp_packet_internal.h> +#include <odp_packet_io_internal.h> +#include <odp_packet_io_stats.h> +#include <odp_debug_internal.h> +#include <odp_classification_internal.h> +#include <odp_macros_internal.h> + #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> #include <linux/if_packet.h> -#include <linux/filter.h> -#include <ctype.h> -#include <fcntl.h> #include <unistd.h> -#include <bits/wordsize.h> -#include <net/ethernet.h> -#include <netinet/ip.h> #include <arpa/inet.h> #include <stdint.h> #include <string.h> #include <net/if.h> -#include <inttypes.h> -#include <poll.h> #include <sys/ioctl.h> #include <errno.h> #include <sys/syscall.h> -#include <linux/ethtool.h> -#include <linux/sockios.h> -#include <odp_api.h> -#include <odp_packet_socket.h> -#include <odp_packet_internal.h> -#include <odp_packet_io_internal.h> -#include <odp_align_internal.h> -#include <odp_debug_internal.h> -#include <odp_classification_datamodel.h> -#include <odp_classification_inlines.h> -#include <odp_classification_internal.h> -#include <odp/api/hints.h> +typedef struct { + odp_ticketlock_t rx_lock ODP_ALIGNED_CACHE; + odp_ticketlock_t tx_lock ODP_ALIGNED_CACHE; + int sockfd; /**< socket descriptor */ + odp_pool_t pool; /**< pool to alloc packets from */ + uint32_t mtu; /**< maximum transmission unit */ + uint32_t mtu_max; /**< maximum supported MTU value */ + unsigned char if_mac[ETH_ALEN]; /**< IF eth mac addr */ +} pkt_sock_t; -#include <protocols/eth.h> -#include <protocols/ip.h> +ODP_STATIC_ASSERT(PKTIO_PRIVATE_SIZE >= sizeof(pkt_sock_t), + "PKTIO_PRIVATE_SIZE too small"); -#define MAX_SEGS CONFIG_PACKET_MAX_SEGS -#define PACKET_JUMBO_LEN (9 * 1024) +static inline pkt_sock_t *pkt_priv(pktio_entry_t *pktio_entry) +{ + return (pkt_sock_t *)(uintptr_t)(pktio_entry->pkt_priv); +} static int disable_pktio; /** !0 this pktio disabled, 0 enabled */ @@ -85,389 +88,18 @@ int sendmmsg(int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags) #endif } - -/** Eth buffer start offset from u32-aligned address to make sure the following - * header (e.g. IP) starts at a 32-bit aligned address. - */ -#define ETHBUF_OFFSET (ODP_ALIGN_ROUNDUP(_ODP_ETHHDR_LEN, sizeof(uint32_t)) \ - - _ODP_ETHHDR_LEN) - -/** Round up buffer address to get a properly aliged eth buffer, i.e. aligned - * so that the next header always starts at a 32bit aligned address. - */ -#define ETHBUF_ALIGN(buf_ptr) ((uint8_t *)ODP_ALIGN_ROUNDUP_PTR((buf_ptr), \ - sizeof(uint32_t)) + ETHBUF_OFFSET) - -/** - * ODP_PACKET_SOCKET_MMSG: - * ODP_PACKET_SOCKET_MMAP: - * ODP_PACKET_NETMAP: - */ -int mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[]) -{ - struct ifreq ethreq; - int ret; - - memset(ðreq, 0, sizeof(ethreq)); - snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", name); - ret = ioctl(fd, SIOCGIFHWADDR, ðreq); - if (ret != 0) { - __odp_errno = errno; - ODP_ERR("ioctl(SIOCGIFHWADDR): %s: \"%s\".\n", strerror(errno), - ethreq.ifr_name); - return -1; - } - - memcpy(mac_dst, (unsigned char *)ethreq.ifr_ifru.ifru_hwaddr.sa_data, - ETH_ALEN); - return 0; -} - -/* - * ODP_PACKET_SOCKET_MMSG: - * ODP_PACKET_SOCKET_MMAP: - * ODP_PACKET_NETMAP: - */ -uint32_t mtu_get_fd(int fd, const char *name) -{ - struct ifreq ifr; - int ret; - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - ret = ioctl(fd, SIOCGIFMTU, &ifr); - if (ret < 0) { - __odp_errno = errno; - ODP_DBG("ioctl(SIOCGIFMTU): %s: \"%s\".\n", strerror(errno), - ifr.ifr_name); - return 0; - } - return ifr.ifr_mtu; -} - -/* - * ODP_PACKET_SOCKET_MMSG: - * ODP_PACKET_SOCKET_MMAP: - * ODP_PACKET_NETMAP: - */ -int promisc_mode_set_fd(int fd, const char *name, int enable) -{ - struct ifreq ifr; - int ret; - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - ret = ioctl(fd, SIOCGIFFLAGS, &ifr); - if (ret < 0) { - __odp_errno = errno; - ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), - ifr.ifr_name); - return -1; - } - - if (enable) - ifr.ifr_flags |= IFF_PROMISC; - else - ifr.ifr_flags &= ~(IFF_PROMISC); - - ret = ioctl(fd, SIOCSIFFLAGS, &ifr); - if (ret < 0) { - __odp_errno = errno; - ODP_DBG("ioctl(SIOCSIFFLAGS): %s: \"%s\".\n", strerror(errno), - ifr.ifr_name); - return -1; - } - return 0; -} - -/* - * ODP_PACKET_SOCKET_MMSG: - * ODP_PACKET_SOCKET_MMAP: - * ODP_PACKET_NETMAP: - */ -int promisc_mode_get_fd(int fd, const char *name) -{ - struct ifreq ifr; - int ret; - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - ret = ioctl(fd, SIOCGIFFLAGS, &ifr); - if (ret < 0) { - __odp_errno = errno; - ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), - ifr.ifr_name); - return -1; - } - - return !!(ifr.ifr_flags & IFF_PROMISC); -} - -int link_status_fd(int fd, const char *name) -{ - struct ifreq ifr; - int ret; - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - ret = ioctl(fd, SIOCGIFFLAGS, &ifr); - if (ret < 0) { - __odp_errno = errno; - ODP_DBG("ioctl(SIOCGIFFLAGS): %s: \"%s\".\n", strerror(errno), - ifr.ifr_name); - return -1; - } - - return !!(ifr.ifr_flags & IFF_RUNNING); -} - -/** - * Get enabled hash options of a packet socket - * - * @param fd Socket file descriptor - * @param name Interface name - * @param flow_type Packet flow type - * @param options[out] Enabled hash options - * - * @retval 0 on success - * @retval <0 on failure - */ -static inline int get_rss_hash_options(int fd, const char *name, - uint32_t flow_type, uint64_t *options) -{ - struct ifreq ifr; - struct ethtool_rxnfc rsscmd; - - memset(&ifr, 0, sizeof(ifr)); - memset(&rsscmd, 0, sizeof(rsscmd)); - *options = 0; - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - - rsscmd.cmd = ETHTOOL_GRXFH; - rsscmd.flow_type = flow_type; - - ifr.ifr_data = (caddr_t)&rsscmd; - - if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) - return -1; - - *options = rsscmd.data; - return 0; -} - -int rss_conf_get_fd(int fd, const char *name, - odp_pktin_hash_proto_t *hash_proto) -{ - uint64_t options; - int rss_enabled = 0; - - memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t)); - - get_rss_hash_options(fd, name, IPV4_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) { - hash_proto->proto.ipv4 = 1; - rss_enabled++; - } - get_rss_hash_options(fd, name, TCP_V4_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) && - (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) { - hash_proto->proto.ipv4_tcp = 1; - rss_enabled++; - } - get_rss_hash_options(fd, name, UDP_V4_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) && - (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) { - hash_proto->proto.ipv4_udp = 1; - rss_enabled++; - } - get_rss_hash_options(fd, name, IPV6_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST)) { - hash_proto->proto.ipv6 = 1; - rss_enabled++; - } - get_rss_hash_options(fd, name, TCP_V6_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) && - (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) { - hash_proto->proto.ipv6_tcp = 1; - rss_enabled++; - } - get_rss_hash_options(fd, name, UDP_V6_FLOW, &options); - if ((options & RXH_IP_SRC) && (options & RXH_IP_DST) && - (options & RXH_L4_B_0_1) && (options & RXH_L4_B_2_3)) { - hash_proto->proto.ipv6_udp = 1; - rss_enabled++; - } - return rss_enabled; -} - -/** - * Set hash options of a packet socket - * - * @param fd Socket file descriptor - * @param name Interface name - * @param flow_type Packet flow type - * @param options Hash options - * - * @retval 0 on success - * @retval <0 on failure - */ -static inline int set_rss_hash(int fd, const char *name, - uint32_t flow_type, uint64_t options) -{ - struct ifreq ifr; - struct ethtool_rxnfc rsscmd; - - memset(&rsscmd, 0, sizeof(rsscmd)); - - snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name); - - rsscmd.cmd = ETHTOOL_SRXFH; - rsscmd.flow_type = flow_type; - rsscmd.data = options; - - ifr.ifr_data = (caddr_t)&rsscmd; - - if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) - return -1; - - return 0; -} - -int rss_conf_set_fd(int fd, const char *name, - const odp_pktin_hash_proto_t *hash_proto) -{ - uint64_t options; - odp_pktin_hash_proto_t cur_hash; - - /* Compare to currently set hash protocols */ - rss_conf_get_fd(fd, name, &cur_hash); - - if (hash_proto->proto.ipv4_udp && !cur_hash.proto.ipv4_udp) { - options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; - if (set_rss_hash(fd, name, UDP_V4_FLOW, options)) - return -1; - } - if (hash_proto->proto.ipv4_tcp && !cur_hash.proto.ipv4_tcp) { - options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; - if (set_rss_hash(fd, name, TCP_V4_FLOW, options)) - return -1; - } - if (hash_proto->proto.ipv6_udp && !cur_hash.proto.ipv6_udp) { - options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; - if (set_rss_hash(fd, name, UDP_V6_FLOW, options)) - return -1; - } - if (hash_proto->proto.ipv6_tcp && !cur_hash.proto.ipv6_tcp) { - options = RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3; - if (set_rss_hash(fd, name, TCP_V6_FLOW, options)) - return -1; - } - if (hash_proto->proto.ipv4 && !cur_hash.proto.ipv4) { - options = RXH_IP_SRC | RXH_IP_DST; - if (set_rss_hash(fd, name, IPV4_FLOW, options)) - return -1; - } - if (hash_proto->proto.ipv6 && !cur_hash.proto.ipv6) { - options = RXH_IP_SRC | RXH_IP_DST; - if (set_rss_hash(fd, name, IPV6_FLOW, options)) - return -1; - } - return 0; -} - -int rss_conf_get_supported_fd(int fd, const char *name, - odp_pktin_hash_proto_t *hash_proto) -{ - uint64_t options; - int rss_supported = 0; - - memset(hash_proto, 0, sizeof(odp_pktin_hash_proto_t)); - - if (!get_rss_hash_options(fd, name, IPV4_FLOW, &options)) { - if (!set_rss_hash(fd, name, IPV4_FLOW, options)) { - hash_proto->proto.ipv4 = 1; - rss_supported++; - } - } - if (!get_rss_hash_options(fd, name, TCP_V4_FLOW, &options)) { - if (!set_rss_hash(fd, name, TCP_V4_FLOW, options)) { - hash_proto->proto.ipv4_tcp = 1; - rss_supported++; - } - } - if (!get_rss_hash_options(fd, name, UDP_V4_FLOW, &options)) { - if (!set_rss_hash(fd, name, UDP_V4_FLOW, options)) { - hash_proto->proto.ipv4_udp = 1; - rss_supported++; - } - } - if (!get_rss_hash_options(fd, name, IPV6_FLOW, &options)) { - if (!set_rss_hash(fd, name, IPV6_FLOW, options)) { - hash_proto->proto.ipv6 = 1; - rss_supported++; - } - } - if (!get_rss_hash_options(fd, name, TCP_V6_FLOW, &options)) { - if (!set_rss_hash(fd, name, TCP_V6_FLOW, options)) { - hash_proto->proto.ipv6_tcp = 1; - rss_supported++; - } - } - if (!get_rss_hash_options(fd, name, UDP_V6_FLOW, &options)) { - if (!set_rss_hash(fd, name, UDP_V6_FLOW, options)) { - hash_proto->proto.ipv6_udp = 1; - rss_supported++; - } - } - return rss_supported; -} - -void rss_conf_print(const odp_pktin_hash_proto_t *hash_proto) -{ int max_len = 512; - char str[max_len]; - int len = 0; - int n = max_len - 1; - - len += snprintf(&str[len], n - len, " rss conf\n"); - - if (hash_proto->proto.ipv4) - len += snprintf(&str[len], n - len, - " IPV4\n"); - if (hash_proto->proto.ipv4_tcp) - len += snprintf(&str[len], n - len, - " IPV4 TCP\n"); - if (hash_proto->proto.ipv4_udp) - len += snprintf(&str[len], n - len, - " IPV4 UDP\n"); - if (hash_proto->proto.ipv6) - len += snprintf(&str[len], n - len, - " IPV6\n"); - if (hash_proto->proto.ipv6_tcp) - len += snprintf(&str[len], n - len, - " IPV6 TCP\n"); - if (hash_proto->proto.ipv6_udp) - len += snprintf(&str[len], n - len, - " IPV6 UDP\n"); - str[len] = '\0'; - - ODP_PRINT("%s\n", str); -} - -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_close(pktio_entry_t *pktio_entry) { - pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock; + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); + if (pkt_sock->sockfd != -1 && close(pkt_sock->sockfd) != 0) { - __odp_errno = errno; - ODP_ERR("close(sockfd): %s\n", strerror(errno)); + _ODP_ERR("close(sockfd): %s\n", strerror(errno)); return -1; } return 0; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev, odp_pool_t pool) { @@ -477,8 +109,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev, struct ifreq ethreq; struct sockaddr_ll sa_ll; char shm_name[ODP_SHM_NAME_LEN]; - pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock; - odp_pktio_stats_t cur_stats; + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); /* Init pktio entry */ memset(pkt_sock, 0, sizeof(*pkt_sock)); @@ -493,8 +124,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev, sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd == -1) { - __odp_errno = errno; - ODP_ERR("socket(): %s\n", strerror(errno)); + _ODP_ERR("socket(): %s\n", strerror(errno)); goto error; } pkt_sock->sockfd = sockfd; @@ -504,20 +134,21 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev, snprintf(ethreq.ifr_name, IF_NAMESIZE, "%s", netdev); err = ioctl(sockfd, SIOCGIFINDEX, ðreq); if (err != 0) { - __odp_errno = errno; - ODP_ERR("ioctl(SIOCGIFINDEX): %s: \"%s\".\n", strerror(errno), - ethreq.ifr_name); + _ODP_ERR("ioctl(SIOCGIFINDEX): %s: \"%s\".\n", strerror(errno), ethreq.ifr_name); goto error; } if_idx = ethreq.ifr_ifindex; - err = mac_addr_get_fd(sockfd, netdev, pkt_sock->if_mac); + err = _odp_mac_addr_get_fd(sockfd, netdev, pkt_sock->if_mac); if (err != 0) goto error; - pkt_sock->mtu = mtu_get_fd(sockfd, netdev); + pkt_sock->mtu = _odp_mtu_get_fd(sockfd, netdev); if (!pkt_sock->mtu) goto error; + pkt_sock->mtu_max = _ODP_SOCKET_MTU_MAX; + if (pkt_sock->mtu > pkt_sock->mtu_max) + pkt_sock->mtu_max = pkt_sock->mtu; /* bind socket to if */ memset(&sa_ll, 0, sizeof(sa_ll)); @@ -525,31 +156,22 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev, sa_ll.sll_ifindex = if_idx; sa_ll.sll_protocol = htons(ETH_P_ALL); if (bind(sockfd, (struct sockaddr *)&sa_ll, sizeof(sa_ll)) < 0) { - __odp_errno = errno; - ODP_ERR("bind(to IF): %s\n", strerror(errno)); + _ODP_ERR("bind(to IF): %s\n", strerror(errno)); goto error; } - err = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd, - pktio_entry->s.name, - &cur_stats); - if (err != 0) { - err = sysfs_stats(pktio_entry, &cur_stats); - if (err != 0) { - pktio_entry->s.stats_type = STATS_UNSUPPORTED; - ODP_DBG("pktio: %s unsupported stats\n", - pktio_entry->s.name); - } else { - pktio_entry->s.stats_type = STATS_SYSFS; - } - } else { - pktio_entry->s.stats_type = STATS_ETHTOOL; - } + pktio_entry->stats_type = _odp_sock_stats_type_fd(pktio_entry, + pkt_sock->sockfd); + if (pktio_entry->stats_type == STATS_UNSUPPORTED) + _ODP_DBG("pktio: %s unsupported stats\n", pktio_entry->name); err = sock_stats_reset(pktio_entry); if (err != 0) goto error; + odp_ticketlock_init(&pkt_sock->rx_lock); + odp_ticketlock_init(&pkt_sock->tx_lock); + return 0; error: @@ -558,9 +180,6 @@ error: return -1; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_mmsg_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry, const char *devname, odp_pool_t pool) @@ -570,89 +189,133 @@ static int sock_mmsg_open(odp_pktio_t id ODP_UNUSED, return sock_setup_pkt(pktio_entry, devname, pool); } -static uint32_t _rx_pkt_to_iovec(odp_packet_t pkt, - struct iovec iovecs[MAX_SEGS]) +static inline uint32_t _rx_pkt_to_iovec(odp_packet_t pkt, struct iovec *iovecs) { - odp_packet_seg_t seg = odp_packet_first_seg(pkt); + odp_packet_seg_t seg; uint32_t seg_count = odp_packet_num_segs(pkt); - uint32_t seg_id = 0; - uint32_t iov_count = 0; - uint8_t *ptr; - uint32_t seglen; - - for (seg_id = 0; seg_id < seg_count; ++seg_id) { - ptr = odp_packet_seg_data(pkt, seg); - seglen = odp_packet_seg_data_len(pkt, seg); - - if (ptr) { - iovecs[iov_count].iov_base = ptr; - iovecs[iov_count].iov_len = seglen; - iov_count++; - } - seg = odp_packet_next_seg(pkt, seg); + uint32_t i; + + if (odp_likely(seg_count == 1)) { + iovecs[0].iov_base = odp_packet_data(pkt); + iovecs[0].iov_len = odp_packet_len(pkt); + return 1; } - return iov_count; + seg = odp_packet_first_seg(pkt); + + for (i = 0; i < seg_count; i++) { + iovecs[i].iov_base = odp_packet_seg_data(pkt, seg); + iovecs[i].iov_len = odp_packet_seg_data_len(pkt, seg); + seg = odp_packet_next_seg(pkt, seg); + } + return i; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED, - odp_packet_t pkt_table[], int len) + odp_packet_t pkt_table[], int num) { - pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock; + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); odp_pool_t pool = pkt_sock->pool; odp_time_t ts_val; odp_time_t *ts = NULL; const int sockfd = pkt_sock->sockfd; - struct mmsghdr msgvec[len]; - struct iovec iovecs[len][MAX_SEGS]; + struct mmsghdr msgvec[num]; + struct iovec iovecs[num][PKT_MAX_SEGS]; int nb_rx = 0; + int nb_cls = 0; int nb_pkts; int recv_msgs; int i; - - odp_ticketlock_lock(&pktio_entry->s.rxl); - - if (pktio_entry->s.config.pktin.bit.ts_all || - pktio_entry->s.config.pktin.bit.ts_ptp) - ts = &ts_val; + const int cls_enabled = pktio_cls_enabled(pktio_entry); + uint16_t frame_offset = pktio_entry->pktin_frame_offset; + uint32_t alloc_len = pkt_sock->mtu + frame_offset; + const odp_proto_layer_t layer = pktio_entry->parse_layer; + const odp_pktin_config_opt_t opt = pktio_entry->config.pktin; memset(msgvec, 0, sizeof(msgvec)); - nb_pkts = packet_alloc_multi(pool, pkt_sock->mtu, pkt_table, len); + nb_pkts = _odp_packet_alloc_multi(pool, alloc_len, pkt_table, num); for (i = 0; i < nb_pkts; i++) { + if (frame_offset) + pull_head(packet_hdr(pkt_table[i]), frame_offset); msgvec[i].msg_hdr.msg_iovlen = _rx_pkt_to_iovec(pkt_table[i], iovecs[i]); msgvec[i].msg_hdr.msg_iov = iovecs[i]; } + odp_ticketlock_lock(&pkt_sock->rx_lock); recv_msgs = recvmmsg(sockfd, msgvec, nb_pkts, MSG_DONTWAIT, NULL); + odp_ticketlock_unlock(&pkt_sock->rx_lock); - if (ts != NULL) + if (opt.bit.ts_all || opt.bit.ts_ptp) { ts_val = odp_time_global(); + ts = &ts_val; + } for (i = 0; i < recv_msgs; i++) { void *base = msgvec[i].msg_hdr.msg_iov->iov_base; struct ethhdr *eth_hdr = base; odp_packet_t pkt = pkt_table[i]; - odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt); + odp_packet_hdr_t *pkt_hdr = packet_hdr(pkt); uint16_t pkt_len = msgvec[i].msg_len; int ret; - if (pktio_cls_enabled(pktio_entry)) { - uint16_t seg_len = pkt_len; + if (odp_unlikely(msgvec[i].msg_hdr.msg_flags & MSG_TRUNC)) { + odp_packet_free(pkt); + _ODP_DBG("dropped truncated packet\n"); + continue; + } - if (msgvec[i].msg_hdr.msg_iov->iov_len < pkt_len) - seg_len = msgvec[i].msg_hdr.msg_iov->iov_len; + ret = odp_packet_trunc_tail(&pkt, odp_packet_len(pkt) - pkt_len, + NULL, NULL); + if (ret < 0) { + _ODP_ERR("trunc_tail failed"); + odp_packet_free(pkt); + continue; + } + + if (layer) { + uint8_t buf[PARSE_BYTES]; + uint16_t seg_len = msgvec[i].msg_hdr.msg_iov->iov_len; + + /* Make sure there is enough data for the packet + * parser in the case of a segmented packet. */ + if (odp_unlikely(seg_len < PARSE_BYTES && pkt_len > seg_len)) { + seg_len = _ODP_MIN(pkt_len, PARSE_BYTES); + odp_packet_copy_to_mem(pkt, 0, seg_len, buf); + base = buf; + } + + ret = _odp_packet_parse_common(pkt_hdr, base, pkt_len, + seg_len, layer, opt); + if (ret) + odp_atomic_inc_u64(&pktio_entry->stats_extra.in_errors); - if (cls_classify_packet(pktio_entry, base, pkt_len, - seg_len, &pool, pkt_hdr)) { - ODP_ERR("cls_classify_packet failed"); + if (ret < 0) { odp_packet_free(pkt); continue; } + + if (cls_enabled) { + odp_pool_t new_pool; + + ret = _odp_cls_classify_packet(pktio_entry, base, + &new_pool, pkt_hdr); + if (ret < 0) + odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards); + + if (ret) { + odp_packet_free(pkt); + continue; + } + + if (odp_unlikely(_odp_pktio_packet_to_pool( + &pkt, &pkt_hdr, new_pool))) { + odp_packet_free(pkt); + odp_atomic_inc_u64(&pktio_entry->stats_extra.in_discards); + continue; + } + } } /* Don't receive packets sent by ourselves */ @@ -662,85 +325,174 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED, continue; } - ret = odp_packet_trunc_tail(&pkt, odp_packet_len(pkt) - pkt_len, - NULL, NULL); - if (ret < 0) { - ODP_ERR("trunk_tail failed"); - odp_packet_free(pkt); - continue; + pkt_hdr->input = pktio_entry->handle; + packet_set_ts(pkt_hdr, ts); + + if (cls_enabled) { + /* Enqueue packets directly to classifier destination queue */ + pkt_table[nb_cls++] = pkt; + nb_cls = _odp_cls_enq(pkt_table, nb_cls, (i + 1 == recv_msgs)); + } else { + pkt_table[nb_rx++] = pkt; } + } - pkt_hdr->input = pktio_entry->s.handle; + /* Enqueue remaining classified packets */ + if (odp_unlikely(nb_cls)) + _odp_cls_enq(pkt_table, nb_cls, true); - if (!pktio_cls_enabled(pktio_entry)) - packet_parse_layer(pkt_hdr, - pktio_entry->s.config.parser.layer); + /* Free unused pkt buffers */ + if (i < nb_pkts) + odp_packet_free_multi(&pkt_table[i], nb_pkts - i); - pkt_hdr->input = pktio_entry->s.handle; - packet_set_ts(pkt_hdr, ts); + return nb_rx; +} + +static int sock_fd_set(pktio_entry_t *pktio_entry, int index ODP_UNUSED, + fd_set *readfds) +{ + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); + const int sockfd = pkt_sock->sockfd; + + FD_SET(sockfd, readfds); + return sockfd; +} + +static int sock_recv_tmo(pktio_entry_t *pktio_entry, int index, + odp_packet_t pkt_table[], int num, uint64_t usecs) +{ + struct timeval timeout; + int ret; + int maxfd; + fd_set readfds; + + ret = sock_mmsg_recv(pktio_entry, index, pkt_table, num); + if (ret != 0) + return ret; + + timeout.tv_sec = usecs / (1000 * 1000); + timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL); + + FD_ZERO(&readfds); + maxfd = sock_fd_set(pktio_entry, index, &readfds); - pkt_table[nb_rx++] = pkt; + while (1) { + ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout); + if (ret <= 0) + return 0; + + ret = sock_mmsg_recv(pktio_entry, index, pkt_table, num); + if (odp_likely(ret)) + return ret; + + /* If no packets, continue wait until timeout expires */ } +} - /* Free unused pkt buffers */ - for (; i < nb_pkts; i++) - odp_packet_free(pkt_table[i]); +static int sock_recv_mq_tmo(pktio_entry_t *pktio_entry[], int index[], + uint32_t num_q, odp_packet_t pkt_table[], int num, + uint32_t *from, uint64_t usecs) +{ + struct timeval timeout; + uint32_t i; + int ret; + int maxfd = -1, maxfd2; + fd_set readfds; - odp_ticketlock_unlock(&pktio_entry->s.rxl); + for (i = 0; i < num_q; i++) { + ret = sock_mmsg_recv(pktio_entry[i], index[i], pkt_table, num); - return nb_rx; + if (ret > 0 && from) + *from = i; + + if (ret != 0) + return ret; + } + + FD_ZERO(&readfds); + + for (i = 0; i < num_q; i++) { + maxfd2 = sock_fd_set(pktio_entry[i], index[i], &readfds); + if (maxfd2 > maxfd) + maxfd = maxfd2; + } + + timeout.tv_sec = usecs / (1000 * 1000); + timeout.tv_usec = usecs - timeout.tv_sec * (1000ULL * 1000ULL); + + while (1) { + ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout); + if (ret <= 0) + return ret; + + for (i = 0; i < num_q; i++) { + ret = sock_mmsg_recv(pktio_entry[i], index[i], + pkt_table, num); + + if (ret > 0 && from) + *from = i; + + if (ret) + return ret; + } + + /* If no packets, continue wait until timeout expires */ + } } -static uint32_t _tx_pkt_to_iovec(odp_packet_t pkt, - struct iovec iovecs[MAX_SEGS]) +static inline uint32_t _tx_pkt_to_iovec(odp_packet_t pkt, struct iovec *iovecs) { - uint32_t pkt_len = odp_packet_len(pkt); - uint32_t offset = odp_packet_l2_offset(pkt); - uint32_t iov_count = 0; - - while (offset < pkt_len) { - uint32_t seglen; - - iovecs[iov_count].iov_base = odp_packet_offset(pkt, offset, - &seglen, NULL); - iovecs[iov_count].iov_len = seglen; - iov_count++; - offset += seglen; + odp_packet_seg_t seg; + int seg_count = odp_packet_num_segs(pkt); + int i; + + if (odp_likely(seg_count == 1)) { + iovecs[0].iov_base = odp_packet_data(pkt); + iovecs[0].iov_len = odp_packet_len(pkt); + return 1; + } + + seg = odp_packet_first_seg(pkt); + for (i = 0; i < seg_count; i++) { + iovecs[i].iov_base = odp_packet_seg_data(pkt, seg); + iovecs[i].iov_len = odp_packet_seg_data_len(pkt, seg); + seg = odp_packet_next_seg(pkt, seg); } - return iov_count; + return i; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED, - const odp_packet_t pkt_table[], int len) + const odp_packet_t pkt_table[], int num) { - pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock; - struct mmsghdr msgvec[len]; - struct iovec iovecs[len][MAX_SEGS]; + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); + struct mmsghdr msgvec[num]; + struct iovec iovecs[num][PKT_MAX_SEGS]; int ret; - int sockfd; - int n, i; - - odp_ticketlock_lock(&pktio_entry->s.txl); + int sockfd = pkt_sock->sockfd; + int i; + int tx_ts_idx = 0; + uint8_t tx_ts_enabled = _odp_pktio_tx_ts_enabled(pktio_entry); - sockfd = pkt_sock->sockfd; memset(msgvec, 0, sizeof(msgvec)); - for (i = 0; i < len; i++) { + for (i = 0; i < num; i++) { msgvec[i].msg_hdr.msg_iov = iovecs[i]; msgvec[i].msg_hdr.msg_iovlen = _tx_pkt_to_iovec(pkt_table[i], - iovecs[i]); + iovecs[i]); + if (tx_ts_enabled && tx_ts_idx == 0) { + if (odp_unlikely(packet_hdr(pkt_table[i])->p.flags.ts_set)) + tx_ts_idx = i + 1; + } } - for (i = 0; i < len; ) { - ret = sendmmsg(sockfd, &msgvec[i], len - i, MSG_DONTWAIT); + odp_ticketlock_lock(&pkt_sock->tx_lock); + + for (i = 0; i < num; ) { + ret = sendmmsg(sockfd, &msgvec[i], num - i, MSG_DONTWAIT); if (odp_unlikely(ret <= -1)) { if (i == 0 && SOCK_ERR_REPORT(errno)) { - __odp_errno = errno; - ODP_ERR("sendmmsg(): %s\n", strerror(errno)); - odp_ticketlock_unlock(&pktio_entry->s.txl); + _ODP_ERR("sendmmsg(): %s\n", strerror(errno)); + odp_ticketlock_unlock(&pkt_sock->tx_lock); return -1; } break; @@ -749,111 +501,148 @@ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED, i += ret; } - odp_ticketlock_unlock(&pktio_entry->s.txl); + if (odp_unlikely(tx_ts_idx && i >= tx_ts_idx)) + _odp_pktio_tx_ts_set(pktio_entry); - for (n = 0; n < i; ++n) - odp_packet_free(pkt_table[n]); + odp_ticketlock_unlock(&pkt_sock->tx_lock); + + odp_packet_free_multi(pkt_table, i); return i; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static uint32_t sock_mtu_get(pktio_entry_t *pktio_entry) { - return pktio_entry->s.pkt_sock.mtu; + return pkt_priv(pktio_entry)->mtu; +} + +static int sock_mtu_set(pktio_entry_t *pktio_entry, uint32_t maxlen_input, + uint32_t maxlen_output ODP_UNUSED) +{ + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); + int ret; + + ret = _odp_mtu_set_fd(pkt_sock->sockfd, pktio_entry->name, maxlen_input); + if (ret) + return ret; + + pkt_sock->mtu = maxlen_input; + + return 0; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr) { - memcpy(mac_addr, pktio_entry->s.pkt_sock.if_mac, ETH_ALEN); + memcpy(mac_addr, pkt_priv(pktio_entry)->if_mac, ETH_ALEN); return ETH_ALEN; } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_promisc_mode_set(pktio_entry_t *pktio_entry, odp_bool_t enable) { - return promisc_mode_set_fd(pktio_entry->s.pkt_sock.sockfd, - pktio_entry->s.name, enable); + return _odp_promisc_mode_set_fd(pkt_priv(pktio_entry)->sockfd, + pktio_entry->name, enable); } -/* - * ODP_PACKET_SOCKET_MMSG: - */ static int sock_promisc_mode_get(pktio_entry_t *pktio_entry) { - return promisc_mode_get_fd(pktio_entry->s.pkt_sock.sockfd, - pktio_entry->s.name); + return _odp_promisc_mode_get_fd(pkt_priv(pktio_entry)->sockfd, + pktio_entry->name); } static int sock_link_status(pktio_entry_t *pktio_entry) { - return link_status_fd(pktio_entry->s.pkt_sock.sockfd, - pktio_entry->s.name); + return _odp_link_status_fd(pkt_priv(pktio_entry)->sockfd, + pktio_entry->name); +} + +static int sock_link_info(pktio_entry_t *pktio_entry, odp_pktio_link_info_t *info) +{ + return _odp_link_info_fd(pkt_priv(pktio_entry)->sockfd, pktio_entry->name, info); } -static int sock_capability(pktio_entry_t *pktio_entry ODP_UNUSED, +static int sock_capability(pktio_entry_t *pktio_entry, odp_pktio_capability_t *capa) { + pkt_sock_t *pkt_sock = pkt_priv(pktio_entry); + memset(capa, 0, sizeof(odp_pktio_capability_t)); capa->max_input_queues = 1; capa->max_output_queues = 1; capa->set_op.op.promisc_mode = 1; + capa->set_op.op.maxlen = 1; + + capa->maxlen.equal = true; + capa->maxlen.min_input = _ODP_SOCKET_MTU_MIN; + capa->maxlen.max_input = pkt_sock->mtu_max; + capa->maxlen.min_output = _ODP_SOCKET_MTU_MIN; + capa->maxlen.max_output = pkt_sock->mtu_max; odp_pktio_config_init(&capa->config); capa->config.pktin.bit.ts_all = 1; capa->config.pktin.bit.ts_ptp = 1; + + capa->config.pktout.bit.ts_ena = 1; + capa->config.pktout.bit.tx_compl_ena = 1; + capa->tx_compl.mode_all = 1; + capa->tx_compl.mode_event = 1; + capa->tx_compl.mode_poll = 1; + + /* Fill statistics capabilities */ + _odp_sock_stats_capa(pktio_entry, capa); + return 0; } static int sock_stats(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats) { - if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) { - memset(stats, 0, sizeof(*stats)); - return 0; - } - - return sock_stats_fd(pktio_entry, - stats, - pktio_entry->s.pkt_sock.sockfd); + return _odp_sock_stats_fd(pktio_entry, stats, pkt_priv(pktio_entry)->sockfd); } static int sock_stats_reset(pktio_entry_t *pktio_entry) { - if (pktio_entry->s.stats_type == STATS_UNSUPPORTED) { - memset(&pktio_entry->s.stats, 0, - sizeof(odp_pktio_stats_t)); - return 0; - } + return _odp_sock_stats_reset_fd(pktio_entry, pkt_priv(pktio_entry)->sockfd); +} + +static int sock_extra_stat_info(pktio_entry_t *pktio_entry, + odp_pktio_extra_stat_info_t info[], + int num) +{ + return _odp_sock_extra_stat_info(pktio_entry, info, num, + pkt_priv(pktio_entry)->sockfd); +} - return sock_stats_reset_fd(pktio_entry, - pktio_entry->s.pkt_sock.sockfd); +static int sock_extra_stats(pktio_entry_t *pktio_entry, uint64_t stats[], + int num) +{ + return _odp_sock_extra_stats(pktio_entry, stats, num, + pkt_priv(pktio_entry)->sockfd); +} + +static int sock_extra_stat_counter(pktio_entry_t *pktio_entry, uint32_t id, + uint64_t *stat) +{ + return _odp_sock_extra_stat_counter(pktio_entry, id, stat, + pkt_priv(pktio_entry)->sockfd); } static int sock_init_global(void) { if (getenv("ODP_PKTIO_DISABLE_SOCKET_MMSG")) { - ODP_PRINT("PKTIO: socket mmsg skipped," + _ODP_PRINT("PKTIO: socket mmsg skipped," " enabled export ODP_PKTIO_DISABLE_SOCKET_MMSG=1.\n"); disable_pktio = 1; } else { - ODP_PRINT("PKTIO: initialized socket mmsg," - "use export ODP_PKTIO_DISABLE_SOCKET_MMSG=1 to disable.\n"); + _ODP_PRINT("PKTIO: initialized socket mmsg," + " use export ODP_PKTIO_DISABLE_SOCKET_MMSG=1 to disable.\n"); } return 0; } -const pktio_if_ops_t sock_mmsg_pktio_ops = { +const pktio_if_ops_t _odp_sock_mmsg_pktio_ops = { .name = "socket", .print = NULL, .init_global = sock_init_global, @@ -865,16 +654,26 @@ const pktio_if_ops_t sock_mmsg_pktio_ops = { .stop = NULL, .stats = sock_stats, .stats_reset = sock_stats_reset, + .extra_stat_info = sock_extra_stat_info, + .extra_stats = sock_extra_stats, + .extra_stat_counter = sock_extra_stat_counter, .recv = sock_mmsg_recv, + .recv_tmo = sock_recv_tmo, + .recv_mq_tmo = sock_recv_mq_tmo, + .fd_set = sock_fd_set, .send = sock_mmsg_send, - .mtu_get = sock_mtu_get, + .maxlen_get = sock_mtu_get, + .maxlen_set = sock_mtu_set, .promisc_mode_set = sock_promisc_mode_set, .promisc_mode_get = sock_promisc_mode_get, .mac_get = sock_mac_addr_get, + .mac_set = NULL, .link_status = sock_link_status, + .link_info = sock_link_info, .capability = sock_capability, - .pktin_ts_res = NULL, - .pktin_ts_from_ns = NULL, + .pktio_ts_res = NULL, + .pktio_ts_from_ns = NULL, + .pktio_time = NULL, .config = NULL, .input_queues_config = NULL, .output_queues_config = NULL, |